Process Design Kit

A process design kit (PDK) defines process-specific tools used to assemble designs for fabrication. For example, a typical PDK defines a set of layer names along with the GDS layer and datatype for each of those names. It also defines a set of components using those layers.

In DeviceLayout.jl terms, a PDK would define a layer record mapping names to GDS metadata, as well as a ProcessTechnology and Target for each desired method of creating artwork and simulation files from schematics:

module MyPDK
using DeviceLayout, DeviceLayout.SchematicDrivenLayout, DeviceLayout.PreferredUnits

const my_layer_record = (;
    metal=GDSMeta(0, 0),
    dielectric=GDSMeta(1, 0),
    chip_area=GDSMeta(100, 0),
    simulated_area=GDSMeta(200, 0)
    # ...
)

const my_process_params = (; # From ExamplePDK: Unrealistic thicknesses for clearer visualizations
    chip_thicknesses=[100μm, 100μm], # [Bottom chip, top chip] (for calculating z height by level)
    flipchip_gaps=[80μm], # Space between chip surfaces (for calculating z height by level)
    height=(; simulated_area=-1mm), # z height at the bottom of simulation volume
    thickness=(; # Extrusion distances for various layers
        simulated_area=2mm,
        dielectric=[2μm, 5μm], # For levelwise layers, specify thickness for each level
        chip_area=[100μm, 100μm]
        # ...
    )
    # ...
)

const MyFlipchipProcess = ProcessTechnology(my_layer_record, my_process_params)

const BottomChipTarget = ArtworkTarget(MyFlipchipProcess, levels=[1])
const TopChipTarget = ArtworkTarget(MyFlipchipProcess, levels=[2])
const SimulationTarget = SolidModelTarget(
    MyFlipchipProcess;
    bounding_layers=[:simulated_area],
    substrate_layers=[:chip_area],
    levelwise_layers=[:chip_area],
    simulation=true
)
end

A DeviceLayout.jl PDK would also define any Components to be used in building schematics. We provide ExamplePDK as a module within DeviceLayout.jl as an example.

One way we recommend organizing your own PDK for use with DeviceLayout.jl is as a single version-controlled repository, with a MyPDK.jl package at the top level, defining a module like the above snippet, and independently versioned component packages in a subdirectory. For example:

MyPDK/
├─ Project.toml
├─ README.md
├─ ...
├─ components/
│  ├─ MyInterdigitalCapacitors/
│  │  ├─ Project.toml
│  │  ├─ README.md
│  │  ├─ src/
│  │  │  └─ MyInterdigitalCapacitors.jl
│  │  ├─ examples/
│  │  │  └─ example.jl
│  │  └─ test/
│  ├─ MyMeanderInductors/
│  ├─ MySpiralInductors/
│  └─ ...
├─ src/
│  └─ MyPDK.jl
└─ test/

MyPDK.jl would have DeviceLayout.jl as a dependency, and each component package would depend on MyPDK.jl (and possibly other components) for layer names and other information or functionality held in common.

Here, component packages have their own Project.toml file distinct from MyPDKPackage because they are separate packages despite living in the same repository. Like with all Julia packages it is not advised to actually commit Manifest.toml files, although it is useful to commit them to one-off "projects" like analyses and layout scripts to enable fully reproducible environments.

This structure can be combined with LocalRegistry to make the PDK and component packages available from a private registry. Doing so allows you to use the full power of the Julia package manager by versioning physical designs using semantic versioning, seamlessly tracking and switching between versions as needed.

PDK tools

DeviceLayout.jl provides some utilities for generating packages and files for PDKs and components from templates:

DeviceLayout.SchematicDrivenLayout.generate_component_definitionFunction
generate_component_definition(compname, pdk::Module, filepath; composite=false,
    template=get_template(composite ? "CompositeComponent.jlt" : "Component.jlt", pdk=pdk))

Generates a file defining the component type compname at filepath based on template.

Uses a template at the file path template for standard components or for composite components depending on the keyword argument composite. If the template keyword is not explicitly used, then if the PDK defines a Component.jlt or CompositeComponent.jlt template in a templates folder at the package root, that will be used; otherwise, the built-in DeviceLayout templates are used.

For generating a new component package, see generate_component_package. Closely related components that should always be versioned together can be defined in the same package, in which case this method can be used to generate only the file defining a component. That file can then be included from the file defining the root package module.

The built-in template defines a module because it's also used for package generation, but it is not necessary for every component in a package to be in its own module.

source
DeviceLayout.SchematicDrivenLayout.generate_component_packageFunction
generate_component_package(name::AbstractString, pdk::Module, compname="MyComp";
    composite=false,
    template=get_template(composite ? "CompositeComponent.jlt" : "Component.jlt", pdk=pdk)
    docs_template=get_template("Component.mdt", pdk=pdk),
    kwargs...
)

Generates a new component package named name in the components directory of pdk.

Adds pdk and DeviceLayout as dependencies and sets non-inclusive upper bounds of the next major versions. Creates a definition for a Component type named compname in the main module file, using a template for standard components or for composite components depending on the keyword argument composite. If the template keyword is not explicitly used, then if the PDK defines a Component.jlt or CompositeComponent.jlt template in a templates folder at the package root, that will be used; otherwise, the built-in DeviceLayout templates are used. The source file generated in this way should not be included from the PDK source files, since it is an independent package even if it is tracked in the same Git repository.

Also generates documentation based on docs_template.

The component package can be registered in your private registry MyRegistry as follows using the LocalRegistry package. First, make sure you are on a branch of the MyRegistry registry in ~/.julia/registries/MyRegistry. Then add the LocalRegistry package to your active environment and run:

using LocalRegistry
register(
    name;
    registry="MyRegistry",
    push=false,
    repo="git@ssh.example.com:path/to/MyPDK.jl.git" # or however you usually get your repo
)

You will need to push the changes and make a pull request for your branch.

For more information about creating and using a local registry, see the LocalRegistry README.

source
DeviceLayout.SchematicDrivenLayout.generate_pdkFunction
generate_pdk(name="MyPDK"; dir=pwd(), template=get_template("PDK.jlt"), kwargs...)

Generates a PDK package named name in the parent directory dir based on template.

Additional keyword arguments are forwarded to PkgTemplates.Template.

The PDK package can be registered in your private registry MyRegistry as follows using the LocalRegistry package. First, make sure you are on a branch of the MyRegistry registry in ~/.julia/registries/MyRegistry. Then add the LocalRegistry package to your active environment and run:

using LocalRegistry
register(
    "MyPDK";
    registry="MyRegistry",
    push=false,
    repo="git@ssh.example.com:path/to/MyPDK.jl.git" # or however you usually get your repo
)

You will need to push the changes and make a pull request for your branch.

For more information about creating and using a local registry, see the LocalRegistry README.

source