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 Component
s 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_definition
— Functiongenerate_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 include
d 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.
DeviceLayout.SchematicDrivenLayout.generate_component_package
— Functiongenerate_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 include
d 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.
DeviceLayout.SchematicDrivenLayout.generate_pdk
— Functiongenerate_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.