Repository: dan-fritchman/Hdl21 (language: python)

Issue:
Auto-Generated PDK Device Mapping Tables
Feature: automagically generate documentation tables along the lines of: 

---

## MyPDK

### Mos Device Mapping 

| MosType   | MosFamily |    MosVth  |  Device Name |
|-------- | -------- | -------- | -------- | 
| NMOS   | CORE |    STD  |  mypdk_nch |
| PMOS   | CORE |    STD  |  mypdk_pch |
| NMOS   | CORE |    LOW  |  mypdk_nch_lvt |
| PMOS   | CORE |    LOW  |  mypdk_pch_lvt |
| NMOS   | IO |    STD  |  mypdk_nch_io |
| PMOS   | IO |    STD  |  mypdk_pch_io |

(etc)

Maybe this could add some more detail such as valid or default parameter values too. But primarily, tabulate the mappings.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 8359092..76c8cef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,7 +68,7 @@ MANIFEST
 pip-log.txt
 pip-delete-this-directory.txt
 
-# Unit test / coverage reports
+# Unit test / coverage / profiling reports
 htmlcov/
 .tox/
 .nox/
@@ -82,6 +82,7 @@ coverage.xml
 .hypothesis/
 .pytest_cache/
 cover/
+prof/
 
 # Translations
 *.mo
diff --git a/SampleSitePdks/sitepdks.py b/SampleSitePdks/sitepdks.py
index 846272e..c2cce96 100644
--- a/SampleSitePdks/sitepdks.py
+++ b/SampleSitePdks/sitepdks.py
@@ -28,7 +28,9 @@
 import sky130
 
 sky130.install = sky130.Install(
-    model_lib=Path("/usr/local/share/pdk/sky130A/libs.tech/ngspice/sky130.lib.spice")
+    pdk_path=Path("/usr/local/share/pdk/sky130A"),
+    lib_path=Path("libs.tech/ngspice/sky130.lib.spice"),
+    model_ref=Path("libs.ref/sky130_fd_pr/spice"),
 )
 
 # ASAP7
diff --git a/hdl21/external_module.py b/hdl21/external_module.py
index f415054..1008768 100644
--- a/hdl21/external_module.py
+++ b/hdl21/external_module.py
@@ -16,6 +16,9 @@
 from .instance import calls_instantiate
 from .qualname import qualname_magic_methods
 
+# VLSIR Imports
+from vlsir.circuit_pb2 import SpiceType
+from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper
 
 @dataclass
 @qualname_magic_methods
@@ -39,6 +42,7 @@ class ExternalModule:
     paramtype: Type = HasNoParams  # Parameter-type `paramclass`
     desc: Optional[str] = None  # Description
     domain: Optional[str] = None  # Domain name, for references upon export
+    spicetype: Optional[int] = SpiceType.SUBCKT # Spice type, for SPICE export
 
     @property
     def ports(self) -> dict:
diff --git a/hdl21/primitives.py b/hdl21/primitives.py
index 1800f41..cf76ad3 100644
--- a/hdl21/primitives.py
+++ b/hdl21/primitives.py
@@ -407,6 +407,7 @@ class PhysicalCapacitorParams:
     aliases=["Cap3", "PhyCap3", "CapPhy3", "PhyCapacitor3"],
 )
 
+
 @paramclass
 class IdealInductorParams:
     l = Param(dtype=Scalar, desc="Inductance (H)")
diff --git a/hdl21/proto/to_proto.py b/hdl21/proto/to_proto.py
index ef59d7b..2e71490 100644
--- a/hdl21/proto/to_proto.py
+++ b/hdl21/proto/to_proto.py
@@ -23,6 +23,7 @@
 # Proto-definitions
 import vlsir
 import vlsir.circuit_pb2 as vckt
+from vlsirtools.netlist.base import SpicePrefix
 
 # HDL
 from ..params import isparamclass
@@ -161,7 +162,7 @@ def export_external_module(self, emod: ExternalModule) -> vckt.ExternalModule:
 
         # Create the Proto-ExternalModule
         qname = vlsir.utils.QualifiedName(name=emod.name, domain=emod.domain)
-        pmod = vckt.ExternalModule(name=qname)
+        pmod = vckt.ExternalModule(name=qname, spicetype = emod.spicetype)
 
         # Create its Port-objects, which also require Vlsir Signal objects
         for port in emod.port_list:
@@ -223,6 +224,7 @@ def export_instance(self, inst: Instance) -> vckt.Instance:
                     raise ValueError(f"Invalid PrimitiveType {call.prim.primtype}")
 
             elif isinstance(inst._resolved, ExternalModuleCall):
+                
                 self.export_external_module(call.module)
                 pinst.module.external.domain = call.module.domain or ""
                 pinst.module.external.name = call.module.name
diff --git a/hdl21/sim/data.py b/hdl21/sim/data.py
index ee8a303..f7cac0c 100644
--- a/hdl21/sim/data.py
+++ b/hdl21/sim/data.py
@@ -3,7 +3,7 @@
 """
 
 from enum import Enum
-from typing import Union, Any, Optional, List, Awaitable
+from typing import Union, Any, Optional, List, Awaitable, Dict
 from pathlib import Path
 from dataclasses import field
 
@@ -330,20 +330,20 @@ class Lib:
 def is_control(val: Any) -> bool:
     return isinstance(val, Control.__args__)
 
-
+# Define all available option types below
+OptionTypes = Union[
+    bool,
+    Scalar,
+    str,
+    Literal,
+]
 @simattr
 @datatype
 class Options:
     """Simulation Options"""
 
-    temper: Optional[int] = None  # Temperature
-    tnom: Optional[int] = None  # Nominal temperature
-    # FIXME NOTE: these three will in short order become `Scalar`s!
-    gmin: Optional[float] = None
-    reltol: Optional[float] = None
-    iabstol: Optional[float] = None
-
-    name: Optional[str] = None  # Name, used in class-based `Sim` definitions
+    value: OptionTypes
+    name: str
 
 
 # Spice-Sim Attribute-Union
@@ -406,6 +406,8 @@ def run(
 
     from .to_proto import to_proto
 
+    inp.Tb.props.set("simulator", opts.simulator.value)
+
     return vsp.sim(inp=to_proto(inp), opts=opts)
 
 
diff --git a/hdl21/sim/to_proto.py b/hdl21/sim/to_proto.py
index 264fb36..24a1e3f 100644
--- a/hdl21/sim/to_proto.py
+++ b/hdl21/sim/to_proto.py
@@ -274,14 +274,8 @@ def export_sweep(self, sweep: data.Sweep) -> vsp.Sweep:
 
 def export_options(options: data.Options) -> vsp.SimOptions:
     """Export simulation options"""
-    return vsp.SimOptions(
-        temp=options.temper,
-        tnom=options.tnom,
-        gmin=options.gmin,
-        iabstol=options.iabstol,
-        reltol=options.reltol,
-    )
-
+    from ..proto.to_proto import export_param_value
+    return vsp.SimOptions(name=options.name, value=export_param_value(options.value))
 
 def export_control(ctrl: data.Control) -> vsp.Control:
     """Export a `Control` element"""
diff --git a/pdks/Sky130/readme.md b/pdks/Sky130/readme.md
index 355b70c..24f5e87 100644
--- a/pdks/Sky130/readme.md
+++ b/pdks/Sky130/readme.md
@@ -23,21 +23,6 @@ Related Projects:
 `sky130` defines a set of `hdl21.ExternalModule`s comprising the essential devices of the SkyWater 130nm open-source PDK, '
 and an `compile` method for converting process-portable `hdl21.Primitive` elements into these modules. 
 
-The complete 130nm design kit includes hundreds of devices. 
-A subset are targets for conversion from generic Hdl21 `Primitives`. 
-They include: 
-
-* "Core" Mos transistors `sky130_fd_pr__{nfet,pfet}_01v8{,_lvt,_hvt}`
-
-And may in the near future also include: 
-
-* Resistors `sky130_fd_pr__res_*`
-* Capacitors `sky130_fd_pr__cap_*`
-* Bipolar Transistors `sky130_fd_pr__{npn,pnp}_*`
-* Diodes, which the PDK provides as SPICE `.model` statements alone, and will correspondingly need to be `hdl21.Module`s. 
-
-Remaining devices can be added directly to user-projects as `hdl21.ExternalModule`s, or can be added to this package via pull request.  
-
 
 ## Installation
 
@@ -54,7 +39,6 @@ import sky130
 assert sky130.modules.sky130_fd_pr__nfet_01v8 is not None # etc
 ```
 
-
 ## PDK `Install` Data
 
 Silicon process technologies generally require non-Python data to execute simulations and other tasks. Sky130 is no different. *Those files are not distributed as part of this package.* The `sky130` package defines an Hdl21 `PdkInstallation` type `sky130.Install`, which includes references to any such out-of-Python data, generally in the form of filesystem paths. See the [Hdl21 PDK docs](https://github.com/dan-fritchman/Hdl21#process-technologies) for more background. 
@@ -80,9 +64,135 @@ sky130.install = sky130.Install(model_lib=model_lib)
 
 Note the conda-based installation supports simulation solely with [ngspice](https://ngspice.sourceforge.io/). Sky130 models compatible with Sandia Labs' [Xyce](https://xyce.sandia.gov/) have been generated by the community, but are less straightforward to find, install, and revision control.
 
+## List of Available Devices
+
+It is important to emphasize that all PDK units are written in microns (μm). We recommend consulting the [SKY130 Device Detail Documentation](https://skywater-pdk.readthedocs.io/en/main/rules/device-details.html) which can provide additional information on these additional components.
+
+### MOSFETs
+
+MOSFETs can be defined using either width (W), length (L) and number of fingers (NF), the SKY130-HDL21 PDK module offers the following components, all with the junction terminals (d,g,s,b) with the exception of NMOS_ISO_20p0V
+
+| Component Key      | MosType | MosVth | MosFamily | Component Name                 | Description                     |
+|--------------------|---------|--------|-----------|--------------------------------|---------------------------------|
+| NMOS_1p8V_STD      | NMOS    | STD    | CORE      | sky130_fd_pr__nfet_01v8        | Standard 1.8V NMOS transistor   |
+| NMOS_1p8V_LOW      | NMOS    | LOW    | CORE      | sky130_fd_pr__nfet_01v8_lvt    | Low threshold 1.8V NMOS         |
+| PMOS_1p8V_STD      | PMOS    | STD    | CORE      | sky130_fd_pr__pfet_01v8        | Standard 1.8V PMOS transistor   |
+| PMOS_1p8V_HIGH     | PMOS    | HIGH   | CORE      | sky130_fd_pr__pfet_01v8_hvt    | High threshold 1.8V PMOS        |
+| PMOS_1p8V_LOW      | PMOS    | LOW    | CORE      | sky130_fd_pr__pfet_01v8_lvt    | Low threshold 1.8V PMOS         |
+| PMOS_5p5V_D10_STD  | PMOS    | STD    | IO        | sky130_fd_pr__pfet_g5v0d10v5    | Standard 5.5V PMOS with D10     |
+| NMOS_5p5V_D10_STD  | NMOS    | STD    | IO        | sky130_fd_pr__nfet_g5v0d10v5    | Standard 5.5V NMOS with D10     |
+| PMOS_5p5V_D16_STD  | PMOS    | STD    | IO        | sky130_fd_pr__pfet_g5v0d16v0    | Standard 5.5V PMOS with D16     |
+| NMOS_20p0V_STD     | NMOS    | STD    | NONE      | sky130_fd_pr__nfet_20v0        | Standard 20V NMOS transistor    |
+| NMOS_20p0V_LOW     | NMOS    | ZERO   | NONE      | sky130_fd_pr__nfet_20v0_zvt    | Zero threshold 20V NMOS         |
+| NMOS_ISO_20p0V     | NMOS    | STD    | NONE      | sky130_fd_pr__nfet_20v0_iso    | 5-Terminal Isolated 20V NMOS transistor with an extra terminal called "sub"   |
+| PMOS_20p0V         | PMOS    | STD    | NONE      | sky130_fd_pr__pfet_20v0        | Standard 20V PMOS transistor    |
+| NMOS_3p3V_NAT      | NMOS    | NATIVE | NONE      | sky130_fd_pr__nfet_03v3_nvt    | Native 3.3V NMOS transistor     |
+| NMOS_5p0V_NAT      | NMOS    | NATIVE | NONE      | sky130_fd_pr__nfet_05v0_nvt    | Native 5.0V NMOS transistor     |
+| NMOS_20p0V_NAT     | NMOS    | NATIVE | NONE      | sky130_fd_pr__nfet_20v0_nvt    | Native 20V NMOS transistor      |
+| ESD_NMOS_1p8V       | NMOS    | STD      | CORE   | sky130_fd_pr__esd_nfet_01v8         | Native ESD 1.8V NMOS, , standard Vth, core family |
+| ESD_NMOS_5p5V_D10   | NMOS    | STD      | IO     | sky130_fd_pr__esd_nfet_g5v0d10v5     | ESD NMOS, 5.5V, standard Vth, IO family, D10 variant |
+| ESD_NMOS_5p5V_NAT   | NMOS    | NATIVE   | IO     | sky130_fd_pr__esd_nfet_g5v0d10v5_nvt | ESD NMOS, 5.5V, native Vth, IO family, D10 variant |
+| ESD_PMOS_5p5V       | PMOS    | STD      | IO     | sky130_fd_pr__esd_pfet_g5v0d10v5     | ESD PMOS, 5.5V, standard Vth, IO family |
+### Generic Resistors
+
+Generic resistors can be defined using either width (W), length (L), 3-terminal resistors are substrate-based.
+
+| Component Key | Component Name               | Number of Terminals | Description                          |
+|---------------|-------------------------------|---------------------|--------------------------------------|
+| GEN_PO        | sky130_fd_pr__res_generic_po | 2 (p,n)                  | Generic 2-terminal poly resistor     |
+| GEN_L1        | sky130_fd_pr__res_generic_l1 | 2 (p,n)                  | Generic 2-terminal local-interconnect resistor  |
+| GEN_M1        | sky130_fd_pr__res_generic_m1 | 2 (p,n)                  | Generic 2-terminal metal-1 resistor  |
+| GEN_M2        | sky130_fd_pr__res_generic_m2 | 2 (p,n)                  | Generic 2-terminal metal-2 resistor  |
+| GEN_M3        | sky130_fd_pr__res_generic_m3 | 2 (p,n)                  | Generic 2-terminal metal-3 resistor  |
+| GEN_M4        | sky130_fd_pr__res_generic_m4 | 2 (p,n)                  | Generic 2-terminal metal-4 resistor  |
+| GEN_M5        | sky130_fd_pr__res_generic_m5 | 2 (p,n)                  | Generic 2-terminal metal-5 resistor  |
+| GEN_ND        | sky130_fd_pr__res_generic_nd | 3 (p,n,b)                  | Generic 3-terminal N-diff resistor   |
+| GEN_PD        | sky130_fd_pr__res_generic_pd | 3 (p,n,b)                  | Generic 3-terminal P-diff resistor   |
+| GEN_ISO_PW    | sky130_fd_pr__res_iso_pw     | 3 (p,n,b)                  | Generic 3-terminal isolated PW resistor |
+
+### Precision Resistors
+
+Precision resistors have a fixed width in the SKY130 PDK, and can be defined in HDL only using the "L" parameter. All devices have junction terminals (p,n,b).
+
+| Component Key | Component Name                    | Description                 |
+|---------------|------------------------------------|-----------------------------|
+| PP_PREC_0p35  | sky130_fd_pr__res_high_po_0p35    | Precision P+ 0.35μm resistor   |
+| PP_PREC_0p69  | sky130_fd_pr__res_high_po_0p69    | Precision P+ 0.69μm resistor   |
+| PP_PREC_1p41  | sky130_fd_pr__res_high_po_1p41    | Precision P+ 1.41μm resistor   |
+| PP_PREC_2p85  | sky130_fd_pr__res_high_po_2p85    | Precision P+ 2.85μm resistor   |
+| PP_PREC_5p73  | sky130_fd_pr__res_high_po_5p73    | Precision P+ 5.73μm resistor   |
+| PM_PREC_0p35  | sky130_fd_pr__res_xhigh_po_0p35   | Precision P- 0.35μm resistor |
+| PM_PREC_0p69  | sky130_fd_pr__res_xhigh_po_0p69   | Precision P- 0.69μm resistor |
+| PM_PREC_1p41  | sky130_fd_pr__res_xhigh_po_1p41   | Precision P- 1.41μm resistor |
+| PM_PREC_2p85  | sky130_fd_pr__res_xhigh_po_2p85   | Precision P- 2.85μm resistor |
+| PM_PREC_5p73  | sky130_fd_pr__res_xhigh_po_5p73   | Precision P- 5.73μm resistor |
+
+### Diodes
+
+Diodes in HDL21 are defined using width (W) and length (L) which are then converted into area and junction perimeter behind the scenes. All devices have junction terminals (p,n).
+
+| Component Key  | Component Name                             | Description                      |
+|----------------|--------------------------------------------|----------------------------------|
+| PWND_5p5V      | sky130_fd_pr__diode_pw2nd_05v5             | PW2ND diode, 5.5V                |
+| PWND_11p0V     | sky130_fd_pr__diode_pw2nd_11v0             | PW2ND diode, 11.0V               |
+| PWND_5p5V_NAT  | sky130_fd_pr__diode_pw2nd_05v5_nvt         | PW2ND diode, 5.5V, native VT     |
+| PWND_5p5V_LVT  | sky130_fd_pr__diode_pw2nd_05v5_lvt         | PW2ND diode, 5.5V, low VT        |
+| PDNW_5p5V      | sky130_fd_pr__diode_pd2nw_05v5             | PD2NW diode, 5.5V                |
+| PDNW_11p0V     | sky130_fd_pr__diode_pd2nw_11v0             | PD2NW diode, 11.0V               |
+| PDNW_5p5V_HVT  | sky130_fd_pr__diode_pd2nw_05v5_hvt         | PD2NW diode, 5.5V, high VT       |
+| PDNW_5p5V_LVT  | sky130_fd_pr__diode_pd2nw_05v5_lvt         | PD2NW diode, 5.5V, low VT        |
+| PX_RF_PSNW     | sky130_fd_pr__model__parasitic__rf_diode_ps2nw | RF parasitic PS2NW diode      |
+| PX_RF_PWDN     | sky130_fd_pr__model__parasitic__rf_diode_pw2dn | RF parasitic PW2DN diode      |
+| PX_PWDN        | sky130_fd_pr__model__parasitic__diode_pw2dn    | Parasitic PW2DN diode          |
+| PX_PSDN        | sky130_fd_pr__model__parasitic__diode_ps2dn    | Parasitic PS2DN diode          |
+| PX_PSNW        | sky130_fd_pr__model__parasitic__diode_ps2nw    | Parasitic PS2NW diode          |
+
+### Bipolar Junction Transistors
+
+Bipolar Junction Transistors in the SKY130 PDK are defined as static devices and do not yet have parametric cells. As such, no parameters can be passed apart from "m" for parallel multiplicity of components:
+
+| Component Key      | Component Name                     | Number of Terminals | Description                     |
+|--------------------|------------------------------------|---------------------|---------------------------------|
+| NPN_5p0V_1x2       | sky130_fd_pr__npn_05v5_W1p00L2p00  | 4 (c,b,e,s)                   | NPN BJT, 5.0V, 1x2μm       |
+| NPN_11p0V_1x1      | sky130_fd_pr__npn_11v0_W1p00L1p00  | 4 (c,b,e,s)                  | NPN BJT, 11.0V, 1x1μm      |
+| NPN_5p0V_1x1       | sky130_fd_pr__npn_05v5_W1p00L1p00  | 4 (c,b,e,s)                  | NPN BJT, 5.0V, 1x1μm       |
+| PNP_5p0V_0p68x0p68 | sky130_fd_pr__pnp_05v5_W0p68L0p68 | 3 (c,b,e)                  | PNP BJT, 5.0V, 0.68x0.68μm       |
+| PNP_5p0V_3p40x3p40 | sky130_fd_pr__pnp_05v5_W3p40L3p40 | 3 (c,b,e)                  | PNP BJT, 5.0V, 3.4x3.4μm       |
+
+### Capacitors
+
+Capacitors in SKY130 come in 4 flavours, the MiM capacitor, the Varactor, the Vertical Parallel Plate transistor and the Vertical Perpendicular Plate capacitor, the latter two accept no arguments and their dimensions are fixed, whereas the first two allow their width and length to be defined:
+
+| Component Key  | Component Name                           | Number of Terminals | Capacitor Type | Description                             |
+|----------------|------------------------------------------|---------------------|---------------|-----------------------------------------|
+| MIM_M3         | sky130_fd_pr__cap_mim_m3_1               | 2 (p,n)                  | MiM           | MiM capacitor, M3 layer                 |
+| MIM_M4         | sky130_fd_pr__cap_mim_m3_2               | 2 (p,n)                  | MiM           | MiM capacitor, M4 layer                 |
+| VAR_LVT        | sky130_fd_pr__cap_var_lvt                | 3 (p,n,b)                   | Varactor      | Varactor, LVT variant                   |
+| VAR_HVT        | sky130_fd_pr__cap_var_hvt                | 3 (p,n,b)                   | Varactor      | Varactor, HVT variant                   |
+| VPP_PARA_1     | sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o2 | 3         | VPP (Parallel)| VPP parallel capacitor, 4.4x4.6 µm, M1M2 |
+| VPP_PARA_2     | sky130_fd_pr__cap_vpp_02p4x04p6_m1m2_noshield          | 3 (p,n,b)                   | VPP (Parallel)     | VPP parallel capacitor, 2.4x4.6 µm, M1M2                  |
+| VPP_PARA_3     | sky130_fd_pr__cap_vpp_08p6x07p8_m1m2_noshield          | 3 (p,n,b)                   | VPP (Parallel)     | VPP parallel capacitor, 8.6x7.8 µm, M1M2                  |
+| VPP_PARA_4     | sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield          | 3 (p,n,b)                   | VPP (Parallel)     | VPP parallel capacitor, 4.4x4.6 µm, M1M2                  |
+| VPP_PARA_5     | sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_noshield          | 3 (p,n,b)                   | VPP (Parallel)     | VPP parallel capacitor, 11.5x11.7 µm, M1M2                |
+| VPP_PARA_6     | sky130_fd_pr__cap_vpp_44p7x23p1_pol1m1m2m3m4m5_noshield | 3 (p,n,b)                   | VPP (Parallel)     | VPP parallel capacitor, 44.7x23.1 µm, POL1M1M2M3M4M5       |
+| VPP_PARA_7     | sky130_fd_pr__cap_vpp_02p7x06p1_m1m2m3m4_shieldl1_fingercap | 3 (p,n,b)               | VPP (Parallel)     | VPP parallel capacitor, 2.7x6.1 µm, M1M2M3M4, shield L1, finger cap |
+| VPP_PARA_8     | sky130_fd_pr__cap_vpp_02p9x06p1_m1m2m3m4_shieldl1_fingercap2 | 3 (p,n,b)             | VPP (Parallel)     | VPP parallel capacitor, 2.9x6.1 µm, M1M2M3M4, shield L1, finger cap 2 |
+| VPP_PARA_9     | sky130_fd_pr__cap_vpp_02p7x11p1_m1m2m3m4_shieldl1_fingercap | 3 (p,n,b)             | VPP (Parallel)     | VPP parallel capacitor, 2.7x11.1 µm, M1M2M3M4, shield L1, finger cap |
+| VPP_PARA_10    | sky130_fd_pr__cap_vpp_02p7x21p1_m1m2m3m4_shieldl1_fingercap | 3 (p,n,b)             | VPP (Parallel)     | VPP parallel capacitor, 2.7x21.1 µm, M1M2M3M4, shield L1, finger cap |
+| VPP_PARA_11    | sky130_fd_pr__cap_vpp_02p7x41p1_m1m2m3m4_shieldl1_fingercap | 3 (p,n,b)            | VPP (Parallel)     | VPP parallel capacitor, 2.7x41.1 µm, M1M2M3M4, shield L1, finger cap |
+| VPP_PERP_1     | sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldm5    | 4 (p,n,t,b)                   | VPP (Perpendicular)| VPP perpendicular capacitor, 11.5x11.7 µm, L1M1M2M3M4, shield M5 |
+| VPP_PERP_2     | sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5  | 4 (p,n,t,b)                   | VPP (Perpendicular)| VPP perpendicular capacitor, 11.5x11.7 µm, L1M1M2M3M4, shield PoM5 |
+| VPP_PERP_3     | sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5    | 4 (p,n,t,b)                   | VPP (Perpendicular)| VPP perpendicular capacitor, 11.5x11.7 µm, M1M2M3M4, shield L1M5 |
+| VPP_PERP_4     | sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4 | 4 (p,n,t,b)               | VPP (Perpendicular)| VPP perpendicular capacitor, 4.4x4.6 µm, M1M2M3, shield L1M5, floating M4 |
+| VPP_PERP_5     | sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1m5_floatm4 | 4 (p,n,t,b)               | VPP (Perpendicular)| VPP perpendicular capacitor, 8.6x7.8 µm, M1M2M3, shield L1M5, floating M4 |
+| VPP_PERP_6     | sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1m5_floatm4 | 4 (p,n,t,b)               | VPP (Perpendicular)| VPP perpendicular capacitor, 11.5x11.7 µm, M1M2M3, shield L1M5, floating M4 |
+| VPP_PERP_7     | sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3_shieldm4      | 4 (p,n,t,b)                   | VPP (Perpendicular)| VPP perpendicular capacitor, 11.5x11.7 µm, L1M1M2M3, shield M4 |
+| VPP_PERP_8     | sky130_fd_pr__cap_vpp_06p8x06p1_l1m1m2m3_shieldpom4    | 4 (p,n,t,b)                   | VPP (Perpendicular)| VPP perpendicular capacitor, 6.8x6.1 µm, L1M1M2M3, shield PoM4 |
+| VPP_PERP_9     | sky130_fd_pr__cap_vpp_06p8x06p1_m1m2m3_shieldl1m4      | 4 (p,n,t,b)                   | VPP (Perpendicular)| VPP perpendicular capacitor, 6.8x6.1 µm, M1M2M3, shield L1M4 |
+| VPP_PERP_10    | sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhvtop | 4 (p,n,t,b)             | VPP (Perpendicular)| VPP perpendicular capacitor, 11.3x11.8 µm, L1M1M2M3M4, shield M5, NHV top |
 
 ## Development 
 
 ```
 pip install -e ".[dev]"
-```
+```
\ No newline at end of file
diff --git a/pdks/Sky130/sky130/__init__.py b/pdks/Sky130/sky130/__init__.py
index f083558..6661eee 100644
--- a/pdks/Sky130/sky130/__init__.py
+++ b/pdks/Sky130/sky130/__init__.py
@@ -2,12 +2,12 @@
 from hdl21.pdk import register
 
 # Grab our primary PDK-definition module
-from . import pdk
-from .pdk import *
+from . import pdk_logic
+from .pdk_logic import *
 
 # The optional external-data installation.
 # Set by an instantiator of `Install`, if available.
 install: Optional[Install] = None
 
 # And register as a PDK module
-register(pdk)
+register(pdk_logic)
diff --git a/pdks/Sky130/sky130/pdk.py b/pdks/Sky130/sky130/pdk.py
deleted file mode 100644
index ac241a2..0000000
--- a/pdks/Sky130/sky130/pdk.py
+++ /dev/null
@@ -1,832 +0,0 @@
-""" 
-
-# Hdl21 + SkyWater 130nm Open-Source PDK Modules and Transformations 
-
-Defines a set of `hdl21.ExternalModule`s comprising the essential devices of the SkyWater 130nm open-source PDK, '
-and an `hdl21pdk.netlist` method for converting process-portable `hdl21.Primitive` elements into these modules. 
-
-The complete 130nm design kit includes hundreds of devices. A small subset are targets for conversion from `hdl21.Primitive`. 
-They include: 
-
-* "Core" Mos transistors `sky130_fd_pr__{nfet,pfet}_01v8{,_lvt,_hvt}
-
-And may in the near future also include: 
-
-* Resistors `sky130_fd_pr__res_*`
-* Capacitors `sky130_fd_pr__cap_*`
-* Bipolar Transistors `sky130_fd_pr__{npn,pnp}_*`
-* Diodes, which the PDK provides as SPICE `.model` statements alone, and will correspondingly need to be `hdl21.Module`s. 
-
-Many of the latter include a variety of "de-parameterization" steps not yet tested by this package's authors.  
-
-Remaining devices can be added to user-projects as `hdl21.ExternalModule`s, 
-or added to this package via pull request.  
-
-"""
-
-# Std-Lib Imports
-from copy import deepcopy
-from pathlib import Path
-from dataclasses import field
-from typing import Dict, Tuple, Optional, List, Any
-from types import SimpleNamespace
-
-# PyPi Imports
-from pydantic.dataclasses import dataclass
-
-# Hdl21 Imports
-import hdl21 as h
-from hdl21.prefix import  µ, MILLI, TERA, MEGA
-from hdl21.pdk import PdkInstallation, Corner, CmosCorner
-from hdl21.primitives import (
-    Mos,
-    PhysicalResistor,
-    PhysicalCapacitor,
-    Diode,
-    Bipolar,
-    ThreeTerminalResistor,
-    ThreeTerminalCapacitor,
-    MosType,
-    MosVth,
-    MosFamily,
-    BipolarType,
-    MosParams,
-    PhysicalResistorParams,
-    PhysicalCapacitorParams,
-    DiodeParams,
-    BipolarParams,
-)
-
-
-FIXME = None  # FIXME: Replace with real values!
-PDK_NAME = "sky130"
-
-
-@h.paramclass
-class MosParams:
-    """# Sky130 Mos Parameters"""
-
-    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=650 * MILLI)
-    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=150 * MILLI)
-    nf = h.Param(dtype=h.Scalar, desc="Number of Fingers", default=1)
-    mult = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
-
-
-# FIXME: keep this alias as prior versions may have used it
-Sky130MosParams = MosParams
-
-
-@h.paramclass
-class Sky130GenResParams:
-    """# Sky130 Generic Resistor Parameters"""
-
-    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=1000 * MILLI)
-    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
-    m = h.Param(dtype=h.Scalar, desc="Multiplicity", default=1)
-
-
-@h.paramclass
-class Sky130PrecResParams:
-    """# Sky130 Precision Resistor Parameters"""
-
-    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
-    mult = h.Param(dtype=h.Scalar, desc="Multiplicity", default=1)
-    m = h.Param(dtype=h.Scalar, desc="Multiplicity", default=1)
-
-
-@h.paramclass
-class Sky130MimParams:
-    """# Sky130 MiM Capacitor Parameters"""
-
-    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=1000 * MILLI)
-    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
-    mf = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
-
-
-@h.paramclass
-class Sky130VarParams:
-    """# Sky130 Varactor Parameters"""
-
-    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=1000 * MILLI)
-    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
-    vm = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
-
-
-# FIXME: These params will not autoscale arguments, it doesn't really matter that they don't
-# defaults are all that is really valid, but it'd be nice if an error could be thrown with
-# parameters being provided or some kind of scaling in the class.
-@h.paramclass
-class Sky130VPPParams:
-    """# Sky130 VPP Capacitor Parameters"""
-
-    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=1000 * MILLI)
-    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
-    mult = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
-    m = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
-
-
-@h.paramclass
-class Sky130DiodeParams:
-
-    # FIXME: What are these junk units supposed to be for?
-    a = h.Param(
-        dtype=h.Scalar, desc="Area in PDK Units for Diodes (pm²)", default=1 * TERA
-    )
-    pj = h.Param(
-        dtype=h.Scalar,
-        desc="Periphery junction capacitance in aF (?)",
-        default=4 * MEGA,
-    )
-
-
-@h.paramclass
-class Sky130BipolarParams:
-    """Default Sky130 Parameters for Bipolar Transistors"""
-
-    m = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
-
-
-def _xtor_module(modname: str) -> h.ExternalModule:
-    """Transistor module creator, with module-name `name`.
-    If optional `MosKey` `key` is provided, adds an entry in the `xtors` dictionary."""
-
-    mod = h.ExternalModule(
-        domain=PDK_NAME,
-        name=modname,
-        desc=f"{PDK_NAME} PDK Mos {modname}",
-        port_list=deepcopy(h.Mos.port_list),
-        paramtype=MosParams,
-    )
-
-    return mod
-
-
-def _res_module(modname: str, numterminals: int, params=h.Param) -> h.ExternalModule:
-    """Resistor Module creator"""
-
-    num2device = {2: PhysicalResistor, 3: ThreeTerminalResistor}
-
-    mod = h.ExternalModule(
-        domain=PDK_NAME,
-        name=modname,
-        desc=f"{PDK_NAME} PDK Res{numterminals} {modname}",
-        port_list=deepcopy(num2device[numterminals].port_list),
-        paramtype=params,
-    )
-
-    return mod
-
-
-def _diode_module(modname: str) -> h.ExternalModule:
-
-    mod = h.ExternalModule(
-        domain=PDK_NAME,
-        name=modname,
-        desc=f"{PDK_NAME} PDK Diode {modname}",
-        port_list=deepcopy(Diode.port_list),
-        paramtype=Sky130DiodeParams,
-    )
-
-    return mod
-
-
-def _bjt_module(modname: str) -> h.ExternalModule:
-
-    mod = h.ExternalModule(
-        domain=PDK_NAME,
-        name=modname,
-        desc=f"{PDK_NAME} PDK BJT {modname}",
-        port_list=deepcopy(Bipolar.port_list),
-        paramtype=Sky130BipolarParams,
-    )
-
-    return mod
-
-
-def _cap_module(modname: str, numterminals: int, params: h.Param) -> h.ExternalModule:
-
-    num2device = {2: PhysicalCapacitor, 3: ThreeTerminalCapacitor}
-
-    """Capacitor Module creator"""
-    mod = h.ExternalModule(
-        domain=PDK_NAME,
-        name=modname,
-        desc=f"{PDK_NAME} PDK Cap{numterminals} {modname}",
-        port_list=deepcopy(num2device[numterminals].port_list),
-        paramtype=params,
-    )
-
-    return mod
-
-
-PerpVPPPorts = [
-    h.Port(name="p"),
-    h.Port(name="n"),
-    h.Port(name="t", desc="Top Shield"),
-    h.Port(name="b", desc="Bottom Shield"),
-]
-
-
-def _vpp_module(modname: str, num_terminals: int) -> h.ExternalModule:
-    """VPP Creator module"""
-
-    if num_terminals == 3:
-
-        mod = h.ExternalModule(
-            domain=PDK_NAME,
-            name=modname,
-            desc=f"{PDK_NAME} PDK Parallel VPP {num_terminals} {modname}",
-            port_list=deepcopy(h.primitives.ThreeTerminalPorts),
-            paramtype=Sky130VPPParams,
-        )
-
-    elif num_terminals == 4:
-
-        mod = h.ExternalModule(
-            domain=PDK_NAME,
-            name=modname,
-            desc=f"{PDK_NAME} PDK Perpendicular VPP {modname}",
-            port_list=deepcopy(PerpVPPPorts),
-            paramtype=Sky130VPPParams,
-        )
-
-    return mod
-
-
-# Individuate component types
-MosKey = Tuple[str, MosType, MosVth, MosFamily]
-
-mos_corners: Dict[CmosCorner, str] = {
-    CmosCorner.TT: "fet_tt",
-    CmosCorner.FF: "fet_ff",
-    CmosCorner.SS: "fet_ss",
-}
-res_corners: Dict[Corner, str] = {
-    Corner.TYP: "res_nom",
-    Corner.FAST: "res_low",
-    Corner.SLOW: "res_high",
-}
-cap_corners: Dict[Corner, str] = {
-    Corner.TYP: "cap_nom",
-    Corner.FAST: "cap_low",
-    Corner.SLOW: "cap_high",
-}
-
-
-def _corner_section_names(corner: Corner) -> List[str]:
-    """Get the include-section names for `corner`."""
-
-    TYP, FAST, SLOW = Corner.TYP, Corner.FAST, Corner.SLOW
-    TT, FF, SS = CmosCorner.TT, CmosCorner.FF, CmosCorner.SS
-
-    if corner == Corner.TYP:
-        return [mos_corners[TT], res_corners[TYP], cap_corners[TYP]]
-    if corner == Corner.FAST:
-        return [mos_corners[FF], res_corners[FAST], cap_corners[FAST]]
-    if corner == Corner.SLOW:
-        return [mos_corners[SS], res_corners[SLOW], cap_corners[SLOW]]
-
-    raise ValueError(f"Invalid corner: {corner}")
-
-
-@dataclass
-class Install(PdkInstallation):
-    """Pdk Installation Data
-    External data provided by site-specific installations"""
-
-    model_lib: Path  # Path to the transistor models included in this module
-
-    def include(self, corner: h.pdk.Corner) -> h.sim.Lib:
-        """# Get the model include file for process corner `corner`."""
-
-        mos_corners: Dict[h.pdk.Corner, str] = {
-            h.pdk.Corner.TYP: "tt",
-            h.pdk.Corner.FAST: FIXME,
-            h.pdk.Corner.SLOW: FIXME,
-        }
-        if not isinstance(corner, h.pdk.Corner) or corner not in mos_corners:
-            raise ValueError(f"Invalid corner {corner}")
-
-        return h.sim.Lib(path=self.model_lib, section=mos_corners[corner])
-
-
-xtors: Dict[MosKey, h.ExternalModule] = {
-    # Add all generic transistors
-    ("NMOS_1p8V_STD", MosType.NMOS, MosVth.STD, MosFamily.CORE): _xtor_module("sky130_fd_pr__nfet_01v8"),
-    ("NMOS_1p8V_LOW", MosType.NMOS, MosVth.LOW, MosFamily.CORE): _xtor_module("sky130_fd_pr__nfet_01v8_lvt"),
-    ("PMOS_1p8V_STD", MosType.PMOS, MosVth.STD, MosFamily.CORE): _xtor_module("sky130_fd_pr__pfet_01v8"),
-    ("PMOS_1p8V_HIGH", MosType.PMOS, MosVth.HIGH, MosFamily.CORE): _xtor_module("sky130_fd_pr__pfet_01v8_hvt"),
-    ("PMOS_1p8V_LOW", MosType.PMOS, MosVth.LOW, MosFamily.CORE): _xtor_module("sky130_fd_pr__pfet_01v8_lvt"),
-    ("PMOS_5p5V_D10_STD", MosType.PMOS, MosVth.STD, MosFamily.IO): _xtor_module(
-        "sky130_fd_pr__pfet_g5v0d10v5"
-    ),
-    ("NMOS_5p5V_D10_STD", MosType.NMOS, MosVth.STD, MosFamily.IO): _xtor_module(
-        "sky130_fd_pr__nfet_g5v0d10v5"
-    ),
-    ("PMOS_5p5V_D16_STD", MosType.PMOS, MosVth.STD, MosFamily.IO): _xtor_module(
-        "sky130_fd_pr__pfet_g5v0d16v0"
-    ),
-    ("NMOS_20p0V_STD", MosType.NMOS, MosVth.STD, MosFamily.NONE): _xtor_module("sky130_fd_pr__nfet_20v0"),
-    ("NMOS_20p0V_LOW", MosType.NMOS, MosVth.ZERO, MosFamily.NONE): _xtor_module(
-        "sky130_fd_pr__nfet_20v0_zvt"
-    ),
-    ("NMOS_ISO_20p0V", MosType.NMOS, MosVth.STD, MosFamily.NONE): _xtor_module(
-        "sky130_fd_pr__nfet_20v0_iso"
-    ),
-    ("PMOS_20p0V", MosType.PMOS, MosVth.STD, MosFamily.NONE): _xtor_module("sky130_fd_pr__pfet_20v0"),
-    # Note there are no NMOS HVT!
-    # Add Native FET entries
-    ("NMOS_3p3V_NAT", MosType.NMOS, MosVth.NATIVE, MosFamily.NONE): _xtor_module("sky130_fd_pr__nfet_03v3_nvt"),
-    ("NMOS_5p0V_NAT", MosType.NMOS, MosVth.NATIVE, MosFamily.NONE): _xtor_module("sky130_fd_pr__nfet_05v0_nvt"),
-    ("NMOS_20p0V_NAT", MosType.NMOS, MosVth.NATIVE, MosFamily.NONE): _xtor_module(
-        "sky130_fd_pr__nfet_20v0_nvt"
-    ),
-    # Add ESD FET entries
-    ("ESD_NMOS_1p8V", MosType.NMOS, MosVth.STD, MosFamily.CORE): _xtor_module("sky130_fd_pr__esd_nfet_01v8"),
-    ("ESD_NMOS_5p5V_D10", MosType.NMOS, MosVth.STD, MosFamily.IO): _xtor_module(
-        "sky130_fd_pr__esd_nfet_g5v0d10v5"
-    ),
-    ("ESD_NMOS_5p5V_NAT", MosType.NMOS, MosVth.NATIVE, MosFamily.IO): _xtor_module(
-        "sky130_fd_pr__esd_nfet_g5v0d10v5_nvt"
-    ),
-    ("ESD_PMOS_5p5V", MosType.PMOS, MosVth.STD, MosFamily.IO): _xtor_module(
-        "sky130_fd_pr__esd_pfet_g5v0d10v5"
-    ),
-}
-
-ress: Dict[str, h.ExternalModule] = {
-    # 2-terminal generic resistors
-    "GEN_PO": _res_module("sky130_fd_pr__res_generic_po", 2, Sky130GenResParams),
-    "GEN_LI": _res_module("sky130_fd_pr__res_generic_l1", 2, Sky130GenResParams),
-    "GEN_M1": _res_module("sky130_fd_pr__res_generic_m1", 2, Sky130GenResParams),
-    "GEN_M2": _res_module("sky130_fd_pr__res_generic_m2", 2, Sky130GenResParams),
-    "GEN_M3": _res_module("sky130_fd_pr__res_generic_m3", 2, Sky130GenResParams),
-    "GEN_M4": _res_module("sky130_fd_pr__res_generic_m4", 2, Sky130GenResParams),
-    "GEN_M5": _res_module("sky130_fd_pr__res_generic_m5", 2, Sky130GenResParams),
-    # 3-terminal generic resistors
-    "GEN_ND": _res_module("sky130_fd_pr__res_generic_nd", 3, Sky130GenResParams),
-    "GEN_PD": _res_module("sky130_fd_pr__res_generic_pd", 3, Sky130GenResParams),
-    "GEN_ISO_PW": _res_module("sky130_fd_pr__res_iso_pw", 3, Sky130GenResParams),
-    # 3-terminal precision resistors
-    "PP_PREC_0p35": _res_module(
-        "sky130_fd_pr__res_high_po_0p35", 3, Sky130PrecResParams
-    ),
-    "PP_PREC_0p69": _res_module(
-        "sky130_fd_pr__res_high_po_0p69", 3, Sky130PrecResParams
-    ),
-    "PP_PREC_1p41": _res_module(
-        "sky130_fd_pr__res_high_po_1p41", 3, Sky130PrecResParams
-    ),
-    "PP_PREC_2p85": _res_module(
-        "sky130_fd_pr__res_high_po_2p85", 3, Sky130PrecResParams
-    ),
-    "PP_PREC_5p30": _res_module(
-        "sky130_fd_pr__res_high_po_5p3", 3, Sky130PrecResParams
-    ),
-    "PM_PREC_0p35": _res_module(
-        "sky130_fd_pr__res_xhigh_po_0p35", 3, Sky130PrecResParams
-    ),
-    "PM_PREC_0p69": _res_module(
-        "sky130_fd_pr__res_xhigh_po_0p69", 3, Sky130PrecResParams
-    ),
-    "PM_PREC_1p41": _res_module(
-        "sky130_fd_pr__res_xhigh_po_1p41", 3, Sky130PrecResParams
-    ),
-    "PM_PREC_2p85": _res_module(
-        "sky130_fd_pr__res_xhigh_po_2p85", 3, Sky130PrecResParams
-    ),
-    "PM_PREC_5p30": _res_module(
-        "sky130_fd_pr__res_xhigh_po_5p3", 3, Sky130PrecResParams
-    ),
-}
-
-diodes: Dict[str, h.ExternalModule] = {
-    # Add diodes
-    "PWND_5p5V": _diode_module("sky130_fd_pr__diode_pw2nd_05v5"),
-    "PWND_11p0V": _diode_module("sky130_fd_pr__diode_pw2nd_11v0"),
-    "PWND_5p5V_NAT": _diode_module("sky130_fd_pr__diode_pw2nd_05v5_nvt"),
-    "PWND_5p5V_LVT": _diode_module("sky130_fd_pr__diode_pw2nd_05v5_lvt"),
-    "PDNW_5p5V": _diode_module("sky130_fd_pr__diode_pd2nw_05v5"),
-    "PDNW_11p0V": _diode_module("sky130_fd_pr__diode_pd2nw_11v0"),
-    "PDNW_5p5V_HVT": _diode_module("sky130_fd_pr__diode_pd2nw_05v5_hvt"),
-    "PDNW_5p5V_LVT": _diode_module("sky130_fd_pr__diode_pd2nw_05v5_lvt"),
-    "PX_RF_PSNW": _diode_module("sky130_fd_pr__model__parasitic__rf_diode_ps2nw"),
-    "PX_RF_PWDN": _diode_module("sky130_fd_pr__model__parasitic__rf_diode_pw2dn"),
-    "PX_PWDN": _diode_module("sky130_fd_pr__model__parasitic__diode_pw2dn"),
-    "PX_PSDN": _diode_module("sky130_fd_pr__model__parasitic__diode_ps2dn"),
-    "PX_PSNW": _diode_module("sky130_fd_pr__model__parasitic__diode_ps2nw"),
-}
-
-bjts: Dict[str, h.ExternalModule] = {
-    # Add BJTs
-    "NPN_5p0V" : _bjt_module("sky130_fd_pr__npn_05v5"),
-    "NPN_11p0V" : _bjt_module("sky130_fd_pr__npn_11v0"),
-    "PNP_5p0V" : _bjt_module("sky130_fd_pr__pnp_05v5"),
-}
-
-caps: Dict[str, h.ExternalModule] = {
-    # List all MiM capacitors
-    "MIM_M3": _cap_module("sky130_fd_pr__cap_mim_m3__base", 2, Sky130MimParams),
-    "MIM_M4": _cap_module("sky130_fd_pr__cap_mim_m4__base", 2, Sky130MimParams),
-    # List available Varactors
-    "VAR_LVT": _cap_module("sky130_fd_pr__cap_var_lvt", 3, Sky130VarParams),
-    "VAR_HVT": _cap_module("sky130_fd_pr__cap_var_hvt", 3, Sky130VarParams),
-}
-
-vpps: Dict[str, h.ExternalModule] = {
-    # List Parallel VPP capacitors
-    "VPP_PARA_1": _vpp_module("sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o2", 3),
-    "VPP_PARA_2": _vpp_module("sky130_fd_pr__cap_vpp_02p4x04p6_m1m2_noshield", 3),
-    "VPP_PARA_3": _vpp_module("sky130_fd_pr__cap_vpp_08p6x07p8_m1m2_noshield", 3),
-    "VPP_PARA_4": _vpp_module("sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield", 3),
-    "VPP_PARA_5": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_noshield", 3),
-    "VPP_PARA_6": _vpp_module("sky130_fd_pr__cap_vpp_44p7x23p1_pol1m1m2m3m4m5_noshield", 3),
-    "VPP_PARA_7": _vpp_module("sky130_fd_pr__cap_vpp_02p7x06p1_m1m2m3m4_shieldl1_fingercap",3),
-    "VPP_PARA_8": _vpp_module("sky130_fd_pr__cap_vpp_02p9x06p1_m1m2m3m4_shieldl1_fingercap2",3),
-    "VPP_PARA_9": _vpp_module("sky130_fd_pr__cap_vpp_02p7x11p1_m1m2m3m4_shieldl1_fingercap",3),
-    "VPP_PARA_10": _vpp_module("sky130_fd_pr__cap_vpp_02p7x21p1_m1m2m3m4_shieldl1_fingercap",3),
-    "VPP_PARA_11": _vpp_module("sky130_fd_pr__cap_vpp_02p7x41p1_m1m2m3m4_shieldl1_fingercap",3),
-    # List Perpendicular VPP capacitors
-    "VPP_PERP_1": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldm5", 4),
-    "VPP_PERP_2": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5", 4),
-    "VPP_PERP_3": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5", 4),
-    "VPP_PERP_4": _vpp_module("sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4",4),
-    "VPP_PERP_5": _vpp_module("sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1m5_floatm4",4),
-    "VPP_PERP_6": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1m5_floatm4",4),
-    "VPP_PERP_7": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3_shieldm4", 4),
-    "VPP_PERP_8": _vpp_module("sky130_fd_pr__cap_vpp_06p8x06p1_l1m1m2m3_shieldpom4", 4),
-    "VPP_PERP_9": _vpp_module("sky130_fd_pr__cap_vpp_06p8x06p1_m1m2m3_shieldl1m4", 4),
-    "VPP_PERP_10": _vpp_module("sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5", 4),
-}
-
-# Collected `ExternalModule`s are stored in the `modules` namespace
-modules = SimpleNamespace()
-
-# Add each to the `modules` namespace
-for name, mod in xtors.items():
-    setattr(modules, name[0], mod)
-for name, mod in ress.items():
-    setattr(modules, name, mod)
-for name, mod in caps.items():
-    setattr(modules, name, mod)
-for name, mod in diodes.items():
-    setattr(modules, name, mod)
-for name, mod in bjts.items():
-    setattr(modules, name, mod)
-for name, mod in vpps.items():
-    setattr(modules, name, mod)
-
-
-@dataclass
-class Cache:
-    """# Module-Scope Cache(s)"""
-
-    mos_modcalls: Dict[MosParams, h.ExternalModuleCall] = field(default_factory=dict)
-
-    res_modcalls: Dict[PhysicalResistorParams, h.ExternalModuleCall] = field(
-        default_factory=dict
-    )
-
-    cap_modcalls: Dict[PhysicalCapacitorParams, h.ExternalModuleCall] = field(
-        default_factory=dict
-    )
-
-    diode_modcalls: Dict[DiodeParams, h.ExternalModule] = field(default_factory=dict)
-
-    bjt_modcalls: Dict[BipolarParams, h.ExternalModule] = field(default_factory=dict)
-
-
-CACHE = Cache()
-
-# Default param dicts
-default_xtor_size = {
-    "sky130_fd_pr__nfet_01v8": (h.Scalar(inner=0.420 * µ), h.Scalar(inner=0.150 * µ)),
-    "sky130_fd_pr__nfet_01v8_lvt": (h.Scalar(inner=0.420 * µ), h.Scalar(inner=0.150 * µ)),
-    "sky130_fd_pr__pfet_01v8": (h.Scalar(inner=0.550 * µ), h.Scalar(inner=0.150 * µ)),
-    "sky130_fd_pr__pfet_01v8_hvt": (h.Scalar(inner=0.550 * µ), h.Scalar(inner=0.150 * µ)),
-    "sky130_fd_pr__pfet_01v8_lvt": (h.Scalar(inner=0.550 * µ), h.Scalar(inner=0.350 * µ)),
-    "sky130_fd_pr__pfet_g5v0d10v5": (h.Scalar(inner=0.420 * µ), h.Scalar(inner=0.500 * µ)),
-    "sky130_fd_pr__nfet_g5v0d10v5": (h.Scalar(inner=0.420 * µ), h.Scalar(inner=0.500 * µ)),
-    "sky130_fd_pr__pfet_g5v0d16v0": (h.Scalar(inner=5.000 * µ), h.Scalar(inner=0.660 * µ)),
-    "sky130_fd_pr__nfet_20v0": (h.Scalar(inner=29.410 * µ), h.Scalar(inner=2.950 * µ)),
-    "sky130_fd_pr__nfet_20v0_zvt": (h.Scalar(inner=30.000 * µ), h.Scalar(inner=1.500 * µ)),
-    "sky130_fd_pr__nfet_20v0_iso": (h.Scalar(inner=30.000 * µ), h.Scalar(inner=1.500 * µ)),
-    "sky130_fd_pr__pfet_20v0": (h.Scalar(inner=30.000 * µ), h.Scalar(inner=1.000 * µ)),
-    "sky130_fd_pr__nfet_03v3_nvt": (h.Scalar(inner=0.700 * µ), h.Scalar(inner=0.500 * µ)),
-    "sky130_fd_pr__nfet_05v0_nvt": (h.Scalar(inner=0.700 * µ), h.Scalar(inner=0.900 * µ)),
-    "sky130_fd_pr__nfet_20v0_nvt": (h.Scalar(inner=30.000 * µ), h.Scalar(inner=1.000 * µ)),
-    "sky130_fd_pr__esd_nfet_01v8": (h.Scalar(inner=20.350 * µ), h.Scalar(inner=0.165 * µ)),
-    "sky130_fd_pr__esd_nfet_g5v0d10v5": (h.Scalar(inner=14.500 * µ), h.Scalar(inner=0.550 * µ)),
-    "sky130_fd_pr__esd_nfet_g5v0d10v5_nvt": (
-        h.Scalar(inner=10.000 * µ),
-        h.Scalar(inner=0.900 * µ),
-    ),
-    "sky130_fd_pr__esd_pfet_g5v0d10v5": (h.Scalar(inner=14.500 * µ), h.Scalar(inner=0.550 * µ)),
-}
-
-default_gen_res_size = {
-    "sky130_fd_pr__res_generic_po": (h.Scalar(inner=0.720 * µ), h.Scalar(inner=0.290 * µ)),
-    "sky130_fd_pr__res_generic_l1": (h.Scalar(inner=0.720 * µ), h.Scalar(inner=0.290 * µ)),
-    "sky130_fd_pr__res_generic_m1": (h.Scalar(inner=0.720 * µ), h.Scalar(inner=0.290 * µ)),
-    "sky130_fd_pr__res_generic_m2": (h.Scalar(inner=0.720 * µ), h.Scalar(inner=0.290 * µ)),
-    "sky130_fd_pr__res_generic_m3": (h.Scalar(inner=0.720 * µ), h.Scalar(inner=0.290 * µ)),
-    "sky130_fd_pr__res_generic_m4": (h.Scalar(inner=0.720 * µ), h.Scalar(inner=0.290 * µ)),
-    "sky130_fd_pr__res_generic_m5": (h.Scalar(inner=0.720 * µ), h.Scalar(inner=0.290 * µ)),
-    "sky130_fd_pr__res_generic_nd": (h.Scalar(inner=0.150 * µ), h.Scalar(inner=0.270 * µ)),
-    "sky130_fd_pr__res_generic_pd": (h.Scalar(inner=0.150 * µ), h.Scalar(inner=0.270 * µ)),
-    # FIXME: This value is lifted from xschem but can't be found in documentation
-    "sky130_fd_pr__res_iso_pw": (h.Scalar(inner=2.650 * µ), h.Scalar(inner=2.650 * µ)),
-}
-
-"These have to be left in microns for parsing reasons"
-default_prec_res_L = {
-    "sky130_fd_pr__res_high_po_0p35": h.Scalar(inner=0.350),
-    "sky130_fd_pr__res_high_po_0p69": h.Scalar(inner=0.690),
-    "sky130_fd_pr__res_high_po_1p41": h.Scalar(inner=1.410),
-    "sky130_fd_pr__res_high_po_2p85": h.Scalar(inner=2.850),
-    "sky130_fd_pr__res_high_po_5p3": h.Scalar(inner=5.300),
-    "sky130_fd_pr__res_xhigh_po_0p35": h.Scalar(inner=0.350),
-    "sky130_fd_pr__res_xhigh_po_0p69": h.Scalar(inner=0.690),
-    "sky130_fd_pr__res_xhigh_po_1p41": h.Scalar(inner=1.410),
-    "sky130_fd_pr__res_xhigh_po_2p85": h.Scalar(inner=2.850),
-    "sky130_fd_pr__res_xhigh_po_5p3": h.Scalar(inner=5.300),
-}
-
-default_cap_sizes = {
-    # FIXME: Using documentation minimum sizing not sure of correct answer
-    "sky130_fd_pr__cap_mim_m3__base": (h.Scalar(inner=2.000 * µ), h.Scalar(inner=2.000 * µ)),
-    "sky130_fd_pr__cap_mim_m4__base": (h.Scalar(inner=2.000 * µ), h.Scalar(inner=2.000 * µ)),
-    "sky130_fd_pr__cap_var_lvt": (h.Scalar(inner=0.180 * µ), h.Scalar(inner=0.180 * µ)),
-    "sky130_fd_pr__cap_var_hvt": (h.Scalar(inner=0.180 * µ), h.Scalar(inner=0.180 * µ)),
-}
-
-
-class Sky130Walker(h.HierarchyWalker):
-    """Hierarchical Walker, converting `h.Primitive` instances to process-defined `ExternalModule`s."""
-
-    def __init__(self):
-        super().__init__()
-        # Caches of our external module calls
-        self.mos_modcalls = dict()
-        self.res_modcalls = dict()
-        self.cap_modcalls = dict()
-        self.diode_modcalls = dict()
-        self.bjt_modcalls = dict()
-
-    def visit_primitive_call(self, call: h.PrimitiveCall) -> h.Instantiable:
-        """Replace instances of `h.primitive.Mos` with our `ExternalModule`s"""
-        # Replace transistors
-        if call.prim is Mos:
-            return self.mos_module_call(call.params)
-
-        elif call.prim is PhysicalResistor or call.prim is ThreeTerminalResistor:
-            return self.res_module_call(call.params)
-
-        elif call.prim is PhysicalCapacitor or call.prim is ThreeTerminalCapacitor:
-            return self.cap_module_call(call.params)
-
-        elif call.prim is Diode:
-            return self.diode_module_call(call.params)
-
-        elif call.prim is Bipolar:
-            return self.bjt_module_call(call.params)
-
-        else:
-            raise RuntimeError(f"{call.prim} is not legitimate primitive")
-
-        # Return everything else as-is
-        return call
-
-    def mos_module(self, params: MosParams) -> h.ExternalModule:
-        """Retrieve or create an `ExternalModule` for a MOS of parameters `params`."""
-        if params.model is not None:
-
-            try:
-                return [v for k, v in xtors.items() if params.model in k][0]
-            except IndexError:
-                msg = f"No Mos module for model name {(params.model)}"
-                raise RuntimeError(msg)
-            
-        """
-        Here we wittle down our list of potential candidates
-
-        General parameters we're concerned with are:
-
-        1) h.MosType - Most important decision is it a PMOS or an NMOS, default = NMOS
-        2) h.MosFamily - Detemines what class of transistor, CORE, IO, HVT, default = CORE
-        3) h.MosVth - Mos Threshold, HIGH, STD, LOW, ULTRA_LOW, default = STD
-        """
-
-        # Map none to default, otherwise leave alone
-        mostype = h.MosType.NMOS if params.tp is None else params.tp
-        mosfam = h.MosFamily.CORE if params.family is None else params.family
-        mosvth = h.MosVth.STD if params.vth is None else params.vth
-        args = (mostype,mosfam,mosvth)
-
-        # Filter the xtors by a dictionary by partial match
-        subset = {key: value for key, value in xtors.items() if any(a in key for a in args)}
-
-        # More than one answer? You weren't specific enough.
-        if len(subset) != 1:
-            msg = f"Mos module choice not well-defined given parameters {args}"
-            raise RuntimeError(msg)
-
-        return subset.values()[0]
-
-    def mos_module_call(self, params: MosParams) -> h.ExternalModuleCall:
-        """Retrieve or create a `Call` for MOS parameters `params`."""
-        # First check our cache
-        if params in CACHE.mos_modcalls:
-            return CACHE.mos_modcalls[params]
-
-        # Not found; create a new `ExternalModuleCall`.
-        # First retrieve the `ExternalModule`.
-        mod = self.mos_module(params)
-
-        w, l = self.use_defaults(params, mod.name, default_xtor_size)
-
-        modparams = Sky130MosParams(
-            w=w,
-            l=l,
-            nf=params.npar,  # FIXME: renaming
-            mult=params.mult,
-        )
-
-        # Combine the two into a call, cache and return it
-        modcall = mod(modparams)
-        CACHE.mos_modcalls[params] = modcall
-        return modcall
-
-    def res_module(self, params: PhysicalResistorParams):
-        """Retrieve or create an `ExternalModule` for a Resistor of parameters `params`."""
-        mod = ress.get((params.model), None)
-
-        if mod is None:
-            msg = f"No Res module for model {params.model}"
-            raise RuntimeError(msg)
-
-        return mod
-
-    def res_module_call(self, params: PhysicalResistorParams):
-
-        # First check our cache
-        if params in CACHE.res_modcalls:
-            return CACHE.res_modcalls[params]
-
-        mod = self.res_module(params)
-
-        if mod.paramtype == Sky130GenResParams:
-
-            w, l = self.use_defaults(params, mod.name, default_gen_res_size)
-
-            modparams = Sky130GenResParams(w=w, l=l)
-
-        elif mod.paramtype == Sky130PrecResParams:
-
-            l = default_prec_res_L[mod.name]
-
-            modparams = Sky130PrecResParams(l=l)
-
-        modcall = mod(modparams)
-        CACHE.res_modcalls[params] = modcall
-        return modcall
-
-    def cap_module(self, params: Any):
-        """Retrieve or create an `ExternalModule` for a Capacitor of parameters `params`."""
-        mod = caps.get((params.model), None)
-
-        if mod is None:
-            msg = f"No Capacitor module for model {params.model}"
-            raise RuntimeError(msg)
-
-        return mod
-
-    def cap_module_call(self, params: PhysicalCapacitorParams):
-
-        if params in CACHE.cap_modcalls:
-            return CACHE.cap_modcalls[params]
-
-        mod = self.cap_module(params)
-
-        m = 1
-
-        if params.mult is not None:
-
-            m = int(params.mult)
-
-        if mod.paramtype == Sky130MimParams:
-
-            w, l = self.use_defaults(params, mod.name, default_cap_sizes)
-            modparams = Sky130MimParams(w=w, l=l, mf=m)
-
-        elif mod.paramtype == Sky130VarParams:
-
-            w, l = self.use_defaults(params, mod.name, default_cap_sizes)
-            modparams = Sky130VarParams(w=w, l=l, vm=m)
-
-        modcall = mod(modparams)
-        CACHE.cap_modcalls[params] = modcall
-        return modcall
-
-    def diode_module(self, params: DiodeParams):
-        """Retrieve or create an `ExternalModule` for a Diode of parameters `params`."""
-        mod = diodes.get((params.model), None)
-            
-        if mod is None:
-            msg = f"No Diode module for model {params.model}"
-            raise RuntimeError(msg)
-
-        return mod
-
-    def diode_module_call(self, params: DiodeParams):
-
-        if params in CACHE.diode_modcalls:
-            return CACHE.diode_modcalls[params]
-
-        mod = self.diode_module(params)
-
-        if params.w is not None and params.w is not None:
-
-            # This scaling is a quirk of SKY130
-            a = params.w * params.l * 1 * TERA
-            pj = 2 * (params.w + params.l) * MEGA
-            modparams = Sky130DiodeParams(a=a,pj=pj)
-
-        else:
-
-            modparams = Sky130DiodeParams()
-
-        modcall = mod(modparams)
-        CACHE.diode_modcalls[params] = modcall
-        return modcall
-
-    def bjt_module(self, params: BipolarParams):
-        """Retrieve or create an `ExternalModule` for a Bipolar of parameters `params`."""
-        mod = bjts.get(params.model, None)
-
-        if mod is None:
-            msg = f"No Bipolar module for model {params.mparamsodel}"
-            raise RuntimeError(msg)
-
-        return mod
-
-    def bjt_module_call(self, params: BipolarParams):
-
-        if params in CACHE.bjt_modcalls:
-            return CACHE.bjt_modcalls[params]
-
-        mod = self.bjt_module(params)
-
-        if params.mult is not None:
-            mult = int(params.mult)
-        else:
-            mult = 1
-
-        modparams = Sky130BipolarParams(m=mult)
-
-        modcall = mod(modparams)
-        CACHE.bjt_modcalls[params] = modcall
-        return modcall
-
-    def scale_param(self, orig: Optional[h.Scalar], default: h.Prefixed) -> h.Scalar:
-        """Replace device size parameter-value `orig`.
-        Primarily type-dispatches across the need to scale to microns for this PDK."""
-        if orig is None:
-            return default
-        if not isinstance(orig, h.Scalar):
-            raise TypeError(f"Invalid Scalar parameter {orig}")
-        inner = orig.inner
-        if isinstance(inner, h.Prefixed):
-            return h.Scalar(inner=inner * MEGA)
-        if isinstance(inner, h.Literal):
-            return h.Scalar(inner=h.Literal(f"({inner} * 1e6)"))
-        raise TypeError(f"Param Value {inner}")
-
-    def use_defaults(self, params: h.paramclass, modname: str, defaults: dict):
-
-        w, l = None, None
-
-        if params.w is None:
-
-            w = defaults[modname][0]
-
-        else:
-
-            w = params.w
-
-        if params.l is None:
-
-            l = defaults[modname][1]
-
-        else:
-
-            l = params.l
-
-        w = self.scale_param(w, 1000 * MILLI)
-        l = self.scale_param(l, 1000 * MILLI)
-
-        return w, l
-
-def compile(src: h.Elaboratables) -> None:
-    """Compile `src` to the Sample technology"""
-    Sky130Walker().walk(src)
diff --git a/pdks/Sky130/sky130/pdk_data.py b/pdks/Sky130/sky130/pdk_data.py
new file mode 100644
index 0000000..0395298
--- /dev/null
+++ b/pdks/Sky130/sky130/pdk_data.py
@@ -0,0 +1,889 @@
+# Std-Lib Imports
+from copy import deepcopy
+from dataclasses import field
+from typing import Dict, Tuple
+from types import SimpleNamespace
+from pathlib import Path
+
+# PyPi Imports
+from pydantic.dataclasses import dataclass
+
+# Hdl21 Imports
+import hdl21 as h
+from hdl21.prefix import (
+    MILLI,
+    µ,
+    MEGA,
+    TERA,
+)
+from hdl21.primitives import (
+    MosType,
+    MosVth,
+    MosFamily,
+    PhysicalResistor,
+    PhysicalCapacitor,
+    Diode,
+    Bipolar,
+    ThreeTerminalResistor,
+    ThreeTerminalCapacitor,
+    MosParams,
+    PhysicalResistorParams,
+    PhysicalCapacitorParams,
+    DiodeParams,
+    BipolarParams,
+)
+from hdl21.pdk import Corner, CmosCorner
+from hdl21.props import Properties
+
+# Vlsirtool Types to ease downstream parsing
+from vlsir.circuit_pb2 import SpiceType
+
+FIXME = None  # FIXME: Replace with real values!
+PDK_NAME = "sky130"
+
+"""
+The following section contains all the necessary @paramclasses required to describe
+parameters of devices in the Sky130 Open PDK. It contains params:
+
+- MosParams (aka. Sky130MosParams) - standard MOS device parameters
+- Sky130Mos20VParams - special parameters for 20V MOS devices
+- Sky130GenResParams - parameters for generic resistors
+- Sky130PrecResParams - parameters for Sky130's precision resistors
+- Sky130MimParams - parameters for  Metal-insulator-Metal (MiM) capacitors
+- Sky130VarParams - paramaters for variable capacitors (ie. varactors)
+- Sky130VPPParams - parameters for both Vertical Perpendicular Plate (VPP) and Vertical Parallel Plate (VPP) capacitors
+- Sky130DiodeParams - parameters for diodes
+- Sky130BipolarParams - parameters for bipolar devices
+"""
+
+
+@h.paramclass
+class MosParams:
+    """
+    A parameter class representing the MOSFET parameters for the Sky130 technology.
+    These parameters include various geometrical and electrical props of the MOSFET device,
+    such as width, length, number of fingers, drain and source areas, drain and source perimeters,
+    resistive values, spacings, and multipliers.
+
+    Attributes:
+    w (h.Scalar): Width of the MOSFET in PDK Units (µm). Default is 650 * MILLI.
+    l (h.Scalar): Length of the MOSFET in PDK Units (µm). Default is 150 * MILLI.
+    nf (h.Scalar): Number of fingers in the MOSFET. Default is 1.
+    m (h.Scalar): Multiplier for the MOSFET (alias for mult). Default is 1.
+
+    #! CAUTION: The following parameters are not recommended for design use.
+
+    ad (h.Literal): Drain area of the MOSFET. Default is 'int((nf+1)/2) * w/nf * 0.29'.
+    As (h.Literal): Source area of the MOSFET. Default is 'int((nf+2)/2) * w/nf * 0.29'.
+    pd (h.Literal): Drain perimeter of the MOSFET. Default is '2int((nf+1)/2) * (w/nf + 0.29)'.
+    ps (h.Literal): Source perimeter of the MOSFET. Default is '2int((nf+2)/2) * (w/nf + 0.29)'.
+    nrd (h.Literal): Drain resistive value of the MOSFET. Default is '0.29 / w'.
+    nrs (h.Literal): Source resistive value of the MOSFET. Default is '0.29 / w'.
+    sa (h.Scalar): Spacing between adjacent gate to drain. Default is 0.
+    sb (h.Scalar): Spacing between adjacent gate to source. Default is 0.
+    sd (h.Scalar): Spacing between adjacent drain to source. Default is 0.
+    mult (h.Scalar): Multiplier for the MOSFET. Default is 1.
+    """
+
+    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=650 * MILLI)
+    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=150 * MILLI)
+    nf = h.Param(dtype=h.Scalar, desc="Number of Fingers", default=1)
+    ad = h.Param(
+        dtype=h.Literal,
+        desc="Drain Area",
+        default=h.Literal("int((nf+1)/2) * w/nf * 0.29"),
+    )
+
+    # This unfortunate naming is to prevent conflicts with base python.
+    As = h.Param(
+        dtype=h.Literal,
+        desc="Source Area",
+        default=h.Literal("int((nf+2)/2) * w/nf * 0.29"),
+    )
+
+    pd = h.Param(
+        dtype=h.Literal,
+        desc="Drain Perimeter",
+        default=h.Literal("2*int((nf+1)/2) * (w/nf + 0.29)"),
+    )
+    ps = h.Param(
+        dtype=h.Literal,
+        desc="Source Perimeter",
+        default=h.Literal("2*int((nf+2)/2) * (w/nf + 0.29)"),
+    )
+    nrd = h.Param(
+        dtype=h.Literal, desc="Drain Resistive Value", default=h.Literal("0.29 / w")
+    )
+    nrs = h.Param(
+        dtype=h.Literal, desc="Source Resistive Value", default=h.Literal("0.29 / w")
+    )
+    sa = h.Param(
+        dtype=h.Scalar,
+        desc="Spacing between Adjacent Gate to Drain",
+        default=h.Literal(0),
+    )
+    sb = h.Param(
+        dtype=h.Scalar,
+        desc="Spacing between Adjacent Gate to Source",
+        default=h.Literal(0),
+    )
+    sd = h.Param(
+        dtype=h.Scalar,
+        desc="Spacing between Adjacent Drain to Source",
+        default=h.Literal(0),
+    )
+    mult = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
+    m = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
+
+
+# FIXME: keep this alias as prior versions may have used it
+Sky130MosParams = MosParams
+
+
+@h.paramclass
+class Sky130Mos20VParams:
+    """
+    A parameter class representing the MOSFET parameters for the Sky130 technology with a 20V rating.
+    These parameters include the width, length, and multiplier of the MOSFET device.
+
+    Attributes:
+    w (h.Scalar): Width of the MOSFET in PDK Units (µm). Default is 30.
+    l (h.Scalar): Length of the MOSFET in PDK Units (µm). Default is 1.
+    m (h.Scalar): Multiplier for the MOSFET. Default is 1.
+    """
+
+    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=30)
+    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1)
+    m = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
+
+
+@h.paramclass
+class Sky130GenResParams:
+    """
+    A parameter class representing the generic resistor parameters for the Sky130 technology. These parameters include the width, length, and multiplicity of the resistor device.
+
+    Attributes:
+    w (h.Scalar): Width of the resistor in PDK Units (µm). Default is 1000 * MILLI.
+    l (h.Scalar): Length of the resistor in PDK Units (µm). Default is 1000 * MILLI.
+    m (h.Scalar): Multiplicity of the resistor. Default is 1.
+    """
+
+    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=1000 * MILLI)
+    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
+    m = h.Param(dtype=h.Scalar, desc="Multiplicity", default=1)
+
+
+@h.paramclass
+class Sky130PrecResParams:
+    """
+    A parameter class representing the precision resistor parameters for the Sky130 technology.
+    These parameters include the length and multiplicity of the precision resistor device.
+
+    Attributes:
+    l (h.Scalar): Length of the precision resistor in PDK Units (µm). Default is 1000 * MILLI.
+    mult (h.Scalar): Multiplicity of the precision resistor. Default is 1.
+    m (h.Scalar): Multiplicity of the precision resistor (alias for mult). Default is 1.
+    """
+
+    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
+    mult = h.Param(dtype=h.Scalar, desc="Multiplicity", default=1)
+    m = h.Param(dtype=h.Scalar, desc="Multiplicity", default=1)
+
+
+@h.paramclass
+class Sky130MimParams:
+    """
+    A parameter class representing the MiM (Metal-insulator-Metal) capacitor parameters for the Sky130 technology.
+    These parameters include the width, length, and multiplier of the MiM capacitor device.
+
+    Attributes:
+    w (h.Scalar): Width of the MiM capacitor in PDK Units (µm). Default is 1000 * MILLI.
+    l (h.Scalar): Length of the MiM capacitor in PDK Units (µm). Default is 1000 * MILLI.
+    mf (h.Scalar): Multiplier for the MiM capacitor. Default is 1.
+    """
+
+    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=1000 * MILLI)
+    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
+    mf = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
+
+
+@h.paramclass
+class Sky130VarParams:
+    """
+    A parameter class representing the varactor parameters for the Sky130 technology.
+    These parameters include the width, length, and multiplier of the varactor device.
+
+    Attributes:
+    w (h.Scalar): Width of the varactor in PDK Units (µm). Default is 1000 * MILLI.
+    l (h.Scalar): Length of the varactor in PDK Units (µm). Default is 1000 * MILLI.
+    vm (h.Scalar): Multiplier for the varactor. Default is 1.
+    """
+
+    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=1000 * MILLI)
+    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
+    vm = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
+
+
+# FIXME: These params will not autoscale arguments, it doesn't really matter that they don't
+# defaults are all that is really valid, but it'd be nice if an error could be thrown with
+# parameters being provided or some kind of scaling in the class.
+@h.paramclass
+class Sky130VPPParams:
+    """
+    A parameter class representing the Vertical Parallel/Perpendicular Plate (VPP) capacitor parameters for the Sky130 technology.
+    These parameters include the width, length, and multiplier of the VPP capacitor device.
+
+    Attributes:
+    w (h.Scalar): Width of the VPP capacitor in PDK Units (µm). Default is 1000 * MILLI.
+    l (h.Scalar): Length of the VPP capacitor in PDK Units (µm). Default is 1000 * MILLI.
+    mult (h.Scalar): Multiplier for the VPP capacitor. Default is 1.
+    m (h.Scalar): Multiplier for the VPP capacitor (alias for mult). Default is 1.
+    """
+
+    w = h.Param(dtype=h.Scalar, desc="Width in PDK Units (µm)", default=1000 * MILLI)
+    l = h.Param(dtype=h.Scalar, desc="Length in PDK Units (µm)", default=1000 * MILLI)
+    mult = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
+    m = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
+
+
+@h.paramclass
+class Sky130DiodeParams:
+    """
+    A parameter class representing the diode parameters for the Sky130 technology.
+    These parameters include the area and periphery junction capacitance of the diode device.
+
+    Attributes:
+    a (h.Scalar): Area of the diode in PDK Units (pm²). Default is 1 * TERA.
+    pj (h.Scalar): Periphery junction capacitance of the diode in attofarads (aF). Default is 4 * MEGA.
+    """
+
+    area = h.Param(
+        dtype=h.Scalar, desc="Area in PDK Units for Diodes (pm²)", default=1 * TERA
+    )
+    pj = h.Param(
+        dtype=h.Scalar,
+        desc="Periphery junction capacitance in aF (?)",
+        default=4 * MEGA,
+    )
+
+
+@h.paramclass
+class Sky130BipolarParams:
+    """
+    A parameter class representing the default bipolar transistor parameters for the Sky130 technology. These parameters include the multiplier for the bipolar transistor device.
+
+    Attributes:
+    m (h.Scalar): Multiplier for the bipolar transistor. Default is 1.
+    """
+
+    m = h.Param(dtype=h.Scalar, desc="Multiplier", default=1)
+
+
+"""
+This subsection of code defines a set of module creator functions for various electronic components in the Sky130 technology.
+Each function is designed to create instances of external modules with the appropriate props and parameter types
+for the specified component.
+
+#! An important note is that `ExternalModule` props is used here to specify the spice prefix for the component used in
+#! in different simulators. The introduction of props into the `ExternalModule` class is more generic than this, but this
+#! is how we are using it for now. Future work will likely expand on this to make it more general.
+"""
+
+Mos5TPortList = [
+    h.Port(name="g", desc="Gate"),
+    h.Port(name="d", desc="Drain"),
+    h.Port(name="s", desc="Source"),
+    h.Port(name="b", desc="Bulk"),
+    h.Port(name="sub", desc="Substrate"),
+]
+
+
+def _xtor_module(
+    modname: str,
+    params: h.Param = Sky130MosParams,
+    num_terminals: int = 4,
+) -> h.ExternalModule:
+
+    """Transistor module creator, with module-name `name`.
+    If `MoKey` `key` is provided, adds an entry in the `xtors` dictionary."""
+
+    num2device = {4: h.Mos.port_list, 5: Mos5TPortList}
+
+    mod = h.ExternalModule(
+        domain=PDK_NAME,
+        name=modname,
+        desc=f"{PDK_NAME} PDK Mos {modname}",
+        port_list=deepcopy(num2device[num_terminals]),
+        paramtype=params,
+        spicetype=SpiceType.SUBCKT
+    )
+
+    return mod
+
+
+def _res_module(
+    modname: str,
+    numterminals: int,
+    params: h.Param,
+    spicetype=SpiceType.SUBCKT,
+) -> h.ExternalModule:
+
+    """Resistor Module creator"""
+
+    num2device = {2: PhysicalResistor, 3: ThreeTerminalResistor}
+
+
+    mod = h.ExternalModule(
+        domain=PDK_NAME,
+        name=modname,
+        desc=f"{PDK_NAME} PDK Res{numterminals} {modname}",
+        port_list=deepcopy(num2device[numterminals].port_list),
+        paramtype=params,
+        spicetype=spicetype,
+    )
+
+    return mod
+
+
+def _diode_module(
+    modname: str,
+) -> h.ExternalModule:
+
+    """Diode Module creator"""
+
+    mod = h.ExternalModule(
+        domain=PDK_NAME,
+        name=modname,
+        desc=f"{PDK_NAME} PDK Diode {modname}",
+        port_list=deepcopy(Diode.port_list),
+        paramtype=Sky130DiodeParams,
+        spicetype=SpiceType.DIODE,
+    )
+
+    return mod
+
+
+BJT4TPortList = [
+    h.Port(name="c", desc="Collector"),
+    h.Port(name="b", desc="Base"),
+    h.Port(name="e", desc="Emitter"),
+    h.Port(name="s", desc="Substrate"),
+]
+
+
+def _bjt_module(
+    modname: str,
+    numterminals: int = 3,
+) -> h.ExternalModule:
+
+    num2device = {3: Bipolar.port_list, 4: BJT4TPortList}
+
+    mod = h.ExternalModule(
+        domain=PDK_NAME,
+        name=modname,
+        desc=f"{PDK_NAME} PDK BJT {modname}",
+        port_list=deepcopy(num2device[numterminals]),
+        paramtype=Sky130BipolarParams,
+    )
+
+    return mod
+
+
+def _cap_module(
+    modname: str,
+    numterminals: int,
+    params: h.Param,
+) -> h.ExternalModule:
+
+    num2device = {2: PhysicalCapacitor, 3: ThreeTerminalCapacitor}
+
+    """Capacitor Module creator"""
+    mod = h.ExternalModule(
+        domain=PDK_NAME,
+        name=modname,
+        desc=f"{PDK_NAME} PDK Cap{numterminals} {modname}",
+        port_list=deepcopy(num2device[numterminals].port_list),
+        paramtype=params,
+    )
+
+    return mod
+
+
+PerpVPPPorts = [
+    h.Port(name="p"),
+    h.Port(name="n"),
+    h.Port(name="t", desc="Top Shield"),
+    h.Port(name="b", desc="Bottom Shield"),
+]
+
+
+def _vpp_module(
+    modname: str,
+    num_terminals: int,
+) -> h.ExternalModule:
+    """VPP Creator module"""
+
+    if num_terminals == 3:
+
+        mod = h.ExternalModule(
+            domain=PDK_NAME,
+            name=modname,
+            desc=f"{PDK_NAME} PDK Parallel VPP {num_terminals} {modname}",
+            port_list=deepcopy(h.primitives.ThreeTerminalPorts),
+            paramtype=Sky130VPPParams,
+        )
+
+    elif num_terminals == 4:
+
+        mod = h.ExternalModule(
+            domain=PDK_NAME,
+            name=modname,
+            desc=f"{PDK_NAME} PDK Perpendicular VPP {modname}",
+            port_list=deepcopy(PerpVPPPorts),
+            paramtype=Sky130VPPParams,
+        )
+
+    return mod
+
+
+# Individuate component types
+MosKey = Tuple[str, MosType, MosVth, MosFamily]
+
+"""
+These dictionaries are used to map all of the devices of the Sky130 technology
+to their corresponding caller functions above. Keys and names are used to 
+differentiate individual components and populate a namespace which can be used
+to find and determine the correct internal device to use.
+"""
+
+xtors: Dict[MosKey, h.ExternalModule] = {
+    # Add all generic transistors
+    ("NMOS_1p8V_STD", MosType.NMOS, MosVth.STD, MosFamily.CORE): _xtor_module(
+        "sky130_fd_pr__nfet_01v8"),
+    ("NMOS_1p8V_LOW", MosType.NMOS, MosVth.LOW, MosFamily.CORE): _xtor_module(
+        "sky130_fd_pr__nfet_01v8_lvt"
+    ),
+    ("PMOS_1p8V_STD", MosType.PMOS, MosVth.STD, MosFamily.CORE): _xtor_module(
+        "sky130_fd_pr__pfet_01v8"
+    ),
+    ("PMOS_1p8V_HIGH", MosType.PMOS, MosVth.HIGH, MosFamily.CORE): _xtor_module(
+        "sky130_fd_pr__pfet_01v8_hvt"
+    ),
+    ("PMOS_1p8V_LOW", MosType.PMOS, MosVth.LOW, MosFamily.CORE): _xtor_module(
+        "sky130_fd_pr__pfet_01v8_lvt"
+    ),
+    ("PMOS_5p5V_D10_STD", MosType.PMOS, MosVth.STD, MosFamily.IO): _xtor_module(
+        "sky130_fd_pr__pfet_g5v0d10v5"
+    ),
+    ("NMOS_5p5V_D10_STD", MosType.NMOS, MosVth.STD, MosFamily.IO): _xtor_module(
+        "sky130_fd_pr__nfet_g5v0d10v5"
+    ),
+    ("PMOS_5p5V_D16_STD", MosType.PMOS, MosVth.STD, MosFamily.IO): _xtor_module(
+        "sky130_fd_pr__pfet_g5v0d16v0"),
+    ("NMOS_20p0V_STD", MosType.NMOS, MosVth.STD, MosFamily.NONE): _xtor_module(
+        "sky130_fd_pr__nfet_20v0", params=Sky130Mos20VParams
+    ),
+    ("NMOS_20p0V_LOW", MosType.NMOS, MosVth.ZERO, MosFamily.NONE): _xtor_module(
+        "sky130_fd_pr__nfet_20v0_zvt", params=Sky130Mos20VParams
+    ),
+    ("NMOS_ISO_20p0V", MosType.NMOS, MosVth.STD, MosFamily.NONE): _xtor_module(
+        "sky130_fd_pr__nfet_20v0_iso", params=Sky130Mos20VParams, num_terminals=5
+    ),
+    ("PMOS_20p0V", MosType.PMOS, MosVth.STD, MosFamily.NONE): _xtor_module(
+        "sky130_fd_pr__pfet_20v0",
+        params=Sky130Mos20VParams
+    ),
+    # Note there are no NMOS HVT!
+    # Add Native FET entries
+    ("NMOS_3p3V_NAT", MosType.NMOS, MosVth.NATIVE, MosFamily.NONE): _xtor_module(
+        "sky130_fd_pr__nfet_03v3_nvt"
+    ),
+    ("NMOS_5p0V_NAT", MosType.NMOS, MosVth.NATIVE, MosFamily.NONE): _xtor_module(
+        "sky130_fd_pr__nfet_05v0_nvt"
+    ),
+    ("NMOS_20p0V_NAT", MosType.NMOS, MosVth.NATIVE, MosFamily.NONE): _xtor_module(
+        "sky130_fd_pr__nfet_20v0_nvt",
+        params=Sky130Mos20VParams
+    ),
+    # Add ESD FET entries
+    ("ESD_NMOS_1p8V", MosType.NMOS, MosVth.STD, MosFamily.CORE): _xtor_module(
+        "sky130_fd_pr__esd_nfet_01v8"
+    ),
+    ("ESD_NMOS_5p5V_D10", MosType.NMOS, MosVth.STD, MosFamily.IO): _xtor_module(
+        "sky130_fd_pr__esd_nfet_g5v0d10v5"
+    ),
+    ("ESD_NMOS_5p5V_NAT", MosType.NMOS, MosVth.NATIVE, MosFamily.IO): _xtor_module(
+        "sky130_fd_pr__esd_nfet_g5v0d10v5_nvt"
+    ),
+    ("ESD_PMOS_5p5V", MosType.PMOS, MosVth.STD, MosFamily.IO): _xtor_module(
+        "sky130_fd_pr__esd_pfet_g5v0d10v5"
+    ),
+}
+
+ress: Dict[str, h.ExternalModule] = {
+    # 2-terminal generic resistors
+    "GEN_PO": _res_module("sky130_fd_pr__res_generic_po", 2, Sky130GenResParams,
+    spicetype=SpiceType.RESISTOR),
+    "GEN_L1": _res_module(
+        "sky130_fd_pr__res_generic_l1",
+        2,
+        Sky130GenResParams,
+        spicetype=SpiceType.RESISTOR),
+    "GEN_M1": _res_module(
+        "sky130_fd_pr__res_generic_m1",
+        2,
+        Sky130GenResParams,
+        spicetype=SpiceType.RESISTOR),
+    "GEN_M2": _res_module(
+        "sky130_fd_pr__res_generic_m2",
+        2,
+        Sky130GenResParams,
+        spicetype=SpiceType.RESISTOR),
+    "GEN_M3": _res_module(
+        "sky130_fd_pr__res_generic_m3",
+        2,
+        Sky130GenResParams,
+        spicetype=SpiceType.RESISTOR),
+    "GEN_M4": _res_module(
+        "sky130_fd_pr__res_generic_m4",
+        2,
+        Sky130GenResParams,
+        spicetype=SpiceType.RESISTOR),
+    "GEN_M5": _res_module(
+        "sky130_fd_pr__res_generic_m5",
+        2,
+        Sky130GenResParams,
+        spicetype=SpiceType.RESISTOR),
+    # 3-terminal generic resistors
+    "GEN_ND": _res_module("sky130_fd_pr__res_generic_nd", 3, Sky130GenResParams),
+    "GEN_PD": _res_module("sky130_fd_pr__res_generic_pd", 3, Sky130GenResParams),
+    "GEN_ISO_PW": _res_module(
+        "sky130_fd_pr__res_iso_pw",
+        3,
+        Sky130GenResParams,
+    ),
+    # 3-terminal precision resistors
+    "PP_PREC_0p35": _res_module(
+        "sky130_fd_pr__res_high_po_0p35", 3, Sky130PrecResParams
+    ),
+    "PP_PREC_0p69": _res_module(
+        "sky130_fd_pr__res_high_po_0p69", 3, Sky130PrecResParams
+    ),
+    "PP_PREC_1p41": _res_module(
+        "sky130_fd_pr__res_high_po_1p41", 3, Sky130PrecResParams
+    ),
+    "PP_PREC_2p85": _res_module(
+        "sky130_fd_pr__res_high_po_2p85", 3, Sky130PrecResParams
+    ),
+    "PP_PREC_5p73": _res_module(
+        "sky130_fd_pr__res_high_po_5p73", 3, Sky130PrecResParams
+    ),
+    "PM_PREC_0p35": _res_module(
+        "sky130_fd_pr__res_xhigh_po_0p35", 3, Sky130PrecResParams
+    ),
+    "PM_PREC_0p69": _res_module(
+        "sky130_fd_pr__res_xhigh_po_0p69", 3, Sky130PrecResParams
+    ),
+    "PM_PREC_1p41": _res_module(
+        "sky130_fd_pr__res_xhigh_po_1p41", 3, Sky130PrecResParams
+    ),
+    "PM_PREC_2p85": _res_module(
+        "sky130_fd_pr__res_xhigh_po_2p85", 3, Sky130PrecResParams
+    ),
+    "PM_PREC_5p73": _res_module(
+        "sky130_fd_pr__res_xhigh_po_5p73", 3, Sky130PrecResParams
+    ),
+}
+
+diodes: Dict[str, h.ExternalModule] = {
+    # Add diodes
+    "PWND_5p5V": _diode_module("sky130_fd_pr__diode_pw2nd_05v5"),
+    "PWND_11p0V": _diode_module("sky130_fd_pr__diode_pw2nd_11v0"),
+    "PWND_5p5V_NAT": _diode_module("sky130_fd_pr__diode_pw2nd_05v5_nvt"),
+    "PWND_5p5V_LVT": _diode_module("sky130_fd_pr__diode_pw2nd_05v5_lvt"),
+    "PDNW_5p5V": _diode_module("sky130_fd_pr__diode_pd2nw_05v5"),
+    "PDNW_11p0V": _diode_module("sky130_fd_pr__diode_pd2nw_11v0"),
+    "PDNW_5p5V_HVT": _diode_module("sky130_fd_pr__diode_pd2nw_05v5_hvt"),
+    "PDNW_5p5V_LVT": _diode_module("sky130_fd_pr__diode_pd2nw_05v5_lvt"),
+    "PX_RF_PSNW": _diode_module("sky130_fd_pr__model__parasitic__rf_diode_ps2nw"),
+    "PX_RF_PWDN": _diode_module("sky130_fd_pr__model__parasitic__rf_diode_pw2dn"),
+    "PX_PWDN": _diode_module("sky130_fd_pr__model__parasitic__diode_pw2dn"),
+    "PX_PSDN": _diode_module("sky130_fd_pr__model__parasitic__diode_ps2dn"),
+    "PX_PSNW": _diode_module("sky130_fd_pr__model__parasitic__diode_ps2nw"),
+}
+
+"""
+BJTs in this PDK are all subcircuits but are distributed in a way that is quite unusual
+and can make it particularly difficult to access them without a PR to the PDK itself.
+
+As noted here there is no functional difference between rf and non-rf BJTs in SKY130:
+
+https://open-source-silicon.slack.com/archives/C016HUV935L/p1650549447460139?thread_ts=1650545374.248099&cid=C016HUV935L
+"""
+bjts: Dict[str, h.ExternalModule] = {
+    # Add BJTs
+    "NPN_5p0V_1x2": _bjt_module("sky130_fd_pr__npn_05v5_W1p00L2p00", numterminals=4),
+    "NPN_11p0V_1x1": _bjt_module("sky130_fd_pr__npn_11v0_W1p00L1p00", numterminals=4),
+    "NPN_5p0V_1x1": _bjt_module("sky130_fd_pr__npn_05v5_W1p00L1p00", numterminals=4),
+    "PNP_5p0V_0p68x0p68": _bjt_module("sky130_fd_pr__pnp_05v5_W0p68L0p68"),
+    "PNP_5p0V_3p40x3p40": _bjt_module("sky130_fd_pr__pnp_05v5_W3p40L3p40"),
+}
+
+caps: Dict[str, h.ExternalModule] = {
+    # List all MiM capacitors
+    # https://open-source-silicon.slack.com/archives/C016HUV935L/p1618923323152300?thread_ts=1618887703.151600&cid=C016HUV935L
+    "MIM_M3": _cap_module(
+        "sky130_fd_pr__cap_mim_m3_1",
+        numterminals=2,
+        params=Sky130MimParams,
+    ),
+    "MIM_M4": _cap_module(
+        "sky130_fd_pr__cap_mim_m3_2",
+        numterminals=2,
+        params=Sky130MimParams,
+    ),
+    # List available Varactors
+    "VAR_LVT": _cap_module(
+        "sky130_fd_pr__cap_var_lvt",
+        numterminals=3,
+        params=Sky130VarParams,
+    ),
+    "VAR_HVT": _cap_module(
+        "sky130_fd_pr__cap_var_hvt",
+        numterminals=3,
+        params=Sky130VarParams,
+    ),
+}
+
+vpps: Dict[str, h.ExternalModule] = {
+    # List Parallel VPP capacitors
+    "VPP_PARA_1": _vpp_module("sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o2", 3),
+    "VPP_PARA_2": _vpp_module("sky130_fd_pr__cap_vpp_02p4x04p6_m1m2_noshield", 3),
+    "VPP_PARA_3": _vpp_module("sky130_fd_pr__cap_vpp_08p6x07p8_m1m2_noshield", 3),
+    "VPP_PARA_4": _vpp_module("sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield", 3),
+    "VPP_PARA_5": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_noshield", 3),
+    "VPP_PARA_6": _vpp_module(
+        "sky130_fd_pr__cap_vpp_44p7x23p1_pol1m1m2m3m4m5_noshield", 3
+    ),
+    "VPP_PARA_7": _vpp_module(
+        "sky130_fd_pr__cap_vpp_02p7x06p1_m1m2m3m4_shieldl1_fingercap", 3
+    ),
+    "VPP_PARA_8": _vpp_module(
+        "sky130_fd_pr__cap_vpp_02p9x06p1_m1m2m3m4_shieldl1_fingercap2", 3
+    ),
+    "VPP_PARA_9": _vpp_module(
+        "sky130_fd_pr__cap_vpp_02p7x11p1_m1m2m3m4_shieldl1_fingercap", 3
+    ),
+    "VPP_PARA_10": _vpp_module(
+        "sky130_fd_pr__cap_vpp_02p7x21p1_m1m2m3m4_shieldl1_fingercap", 3
+    ),
+    "VPP_PARA_11": _vpp_module(
+        "sky130_fd_pr__cap_vpp_02p7x41p1_m1m2m3m4_shieldl1_fingercap", 3
+    ),
+    # List Perpendicular VPP capacitors
+    "VPP_PERP_1": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldm5", 4),
+    "VPP_PERP_2": _vpp_module(
+        "sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3m4_shieldpom5", 4
+    ),
+    "VPP_PERP_3": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3m4_shieldl1m5", 4),
+    "VPP_PERP_4": _vpp_module(
+        "sky130_fd_pr__cap_vpp_04p4x04p6_m1m2m3_shieldl1m5_floatm4", 4
+    ),
+    "VPP_PERP_5": _vpp_module(
+        "sky130_fd_pr__cap_vpp_08p6x07p8_m1m2m3_shieldl1m5_floatm4", 4
+    ),
+    "VPP_PERP_6": _vpp_module(
+        "sky130_fd_pr__cap_vpp_11p5x11p7_m1m2m3_shieldl1m5_floatm4", 4
+    ),
+    "VPP_PERP_7": _vpp_module("sky130_fd_pr__cap_vpp_11p5x11p7_l1m1m2m3_shieldm4", 4),
+    "VPP_PERP_8": _vpp_module("sky130_fd_pr__cap_vpp_06p8x06p1_l1m1m2m3_shieldpom4", 4),
+    "VPP_PERP_9": _vpp_module("sky130_fd_pr__cap_vpp_06p8x06p1_m1m2m3_shieldl1m4", 4),
+    "VPP_PERP_10": _vpp_module(
+        "sky130_fd_pr__cap_vpp_11p3x11p8_l1m1m2m3m4_shieldm5_nhvtop", 4
+    ),
+}
+
+# Collected `ExternalModule`s are stored in the `modules` namespace
+modules = SimpleNamespace()
+
+# Add each to the `modules` namespace
+for name, mod in xtors.items():
+    setattr(modules, name[0], mod)
+for name, mod in ress.items():
+    setattr(modules, name, mod)
+for name, mod in caps.items():
+    setattr(modules, name, mod)
+for name, mod in diodes.items():
+    setattr(modules, name, mod)
+for name, mod in bjts.items():
+    setattr(modules, name, mod)
+for name, mod in vpps.items():
+    setattr(modules, name, mod)
+
+
+@dataclass
+class Cache:
+    """# Module-Scope Cache(s)"""
+
+    mos_modcalls: Dict[MosParams, h.ExternalModuleCall] = field(default_factory=dict)
+
+    res_modcalls: Dict[PhysicalResistorParams, h.ExternalModuleCall] = field(
+        default_factory=dict
+    )
+
+    cap_modcalls: Dict[PhysicalCapacitorParams, h.ExternalModuleCall] = field(
+        default_factory=dict
+    )
+
+    diode_modcalls: Dict[DiodeParams, h.ExternalModule] = field(default_factory=dict)
+
+    bjt_modcalls: Dict[BipolarParams, h.ExternalModule] = field(default_factory=dict)
+
+
+CACHE = Cache()
+
+"""
+This section of code defines default sizes for various electronic components in the Sky130 technology,
+including transistors, resistors, and capacitors. Default dimensions are provided in microns or PDK units,
+with the sizes stored in dictionaries: default_xtor_size for transistors, default_gen_res_size for generic resistors,
+default_prec_res_L for precise resistors, and default_cap_sizes for capacitors.
+These default sizes are important for creating instances of the components with proper dimensions,
+ensuring correct layout and performance in the circuit designs.
+"""
+
+# Default param dicts
+default_xtor_size = {
+    "sky130_fd_pr__nfet_01v8": (h.Scalar(inner=0.420 * µ), h.Scalar(inner=0.150 * µ)),
+    "sky130_fd_pr__nfet_01v8_lvt": (
+        h.Scalar(inner=0.420 * µ),
+        h.Scalar(inner=0.150 * µ),
+    ),
+    "sky130_fd_pr__pfet_01v8": (h.Scalar(inner=0.550 * µ), h.Scalar(inner=0.150 * µ)),
+    "sky130_fd_pr__pfet_01v8_hvt": (
+        h.Scalar(inner=0.550 * µ),
+        h.Scalar(inner=0.150 * µ),
+    ),
+    "sky130_fd_pr__pfet_01v8_lvt": (
+        h.Scalar(inner=0.550 * µ),
+        h.Scalar(inner=0.350 * µ),
+    ),
+    "sky130_fd_pr__pfet_g5v0d10v5": (
+        h.Scalar(inner=0.420 * µ),
+        h.Scalar(inner=0.500 * µ),
+    ),
+    "sky130_fd_pr__nfet_g5v0d10v5": (
+        h.Scalar(inner=0.420 * µ),
+        h.Scalar(inner=0.500 * µ),
+    ),
+    "sky130_fd_pr__pfet_g5v0d16v0": (
+        h.Scalar(inner=5.000 * µ),
+        h.Scalar(inner=0.660 * µ),
+    ),
+    "sky130_fd_pr__nfet_20v0": (h.Scalar(inner=29.410 * µ), h.Scalar(inner=2.950 * µ)),
+    "sky130_fd_pr__nfet_20v0_zvt": (
+        h.Scalar(inner=30.000 * µ),
+        h.Scalar(inner=1.500 * µ),
+    ),
+    "sky130_fd_pr__nfet_20v0_iso": (
+        h.Scalar(inner=30.000 * µ),
+        h.Scalar(inner=1.500 * µ),
+    ),
+    "sky130_fd_pr__pfet_20v0": (h.Scalar(inner=30.000 * µ), h.Scalar(inner=1.000 * µ)),
+    "sky130_fd_pr__nfet_03v3_nvt": (
+        h.Scalar(inner=0.700 * µ),
+        h.Scalar(inner=0.500 * µ),
+    ),
+    "sky130_fd_pr__nfet_05v0_nvt": (
+        h.Scalar(inner=0.700 * µ),
+        h.Scalar(inner=0.900 * µ),
+    ),
+    "sky130_fd_pr__nfet_20v0_nvt": (
+        h.Scalar(inner=30.000 * µ),
+        h.Scalar(inner=1.000 * µ),
+    ),
+    "sky130_fd_pr__esd_nfet_01v8": (
+        h.Scalar(inner=20.350 * µ),
+        h.Scalar(inner=0.165 * µ),
+    ),
+    "sky130_fd_pr__esd_nfet_g5v0d10v5": (
+        h.Scalar(inner=14.500 * µ),
+        h.Scalar(inner=0.550 * µ),
+    ),
+    "sky130_fd_pr__esd_nfet_g5v0d10v5_nvt": (
+        h.Scalar(inner=10.000 * µ),
+        h.Scalar(inner=0.900 * µ),
+    ),
+    "sky130_fd_pr__esd_pfet_g5v0d10v5": (
+        h.Scalar(inner=14.500 * µ),
+        h.Scalar(inner=0.550 * µ),
+    ),
+}
+
+default_gen_res_size = {
+    "sky130_fd_pr__res_generic_po": (
+        h.Scalar(inner=0.720 * µ),
+        h.Scalar(inner=0.290 * µ),
+    ),
+    "sky130_fd_pr__res_generic_l1": (
+        h.Scalar(inner=0.720 * µ),
+        h.Scalar(inner=0.290 * µ),
+    ),
+    "sky130_fd_pr__res_generic_m1": (
+        h.Scalar(inner=0.720 * µ),
+        h.Scalar(inner=0.290 * µ),
+    ),
+    "sky130_fd_pr__res_generic_m2": (
+        h.Scalar(inner=0.720 * µ),
+        h.Scalar(inner=0.290 * µ),
+    ),
+    "sky130_fd_pr__res_generic_m3": (
+        h.Scalar(inner=0.720 * µ),
+        h.Scalar(inner=0.290 * µ),
+    ),
+    "sky130_fd_pr__res_generic_m4": (
+        h.Scalar(inner=0.720 * µ),
+        h.Scalar(inner=0.290 * µ),
+    ),
+    "sky130_fd_pr__res_generic_m5": (
+        h.Scalar(inner=0.720 * µ),
+        h.Scalar(inner=0.290 * µ),
+    ),
+    "sky130_fd_pr__res_generic_nd": (
+        h.Scalar(inner=0.150 * µ),
+        h.Scalar(inner=0.270 * µ),
+    ),
+    "sky130_fd_pr__res_generic_pd": (
+        h.Scalar(inner=0.150 * µ),
+        h.Scalar(inner=0.270 * µ),
+    ),
+    # FIXME: This value is lifted from xschem but can't be found in documentation
+    "sky130_fd_pr__res_iso_pw": (h.Scalar(inner=2.650 * µ), h.Scalar(inner=2.650 * µ)),
+}
+
+# These have to be left in microns for parsing reasons
+default_prec_res_L = {
+    "sky130_fd_pr__res_high_po_0p35": h.Scalar(inner=0.350),
+    "sky130_fd_pr__res_high_po_0p69": h.Scalar(inner=0.690),
+    "sky130_fd_pr__res_high_po_1p41": h.Scalar(inner=1.410),
+    "sky130_fd_pr__res_high_po_2p85": h.Scalar(inner=2.850),
+    "sky130_fd_pr__res_high_po_5p73": h.Scalar(inner=5.300),
+    "sky130_fd_pr__res_xhigh_po_0p35": h.Scalar(inner=0.350),
+    "sky130_fd_pr__res_xhigh_po_0p69": h.Scalar(inner=0.690),
+    "sky130_fd_pr__res_xhigh_po_1p41": h.Scalar(inner=1.410),
+    "sky130_fd_pr__res_xhigh_po_2p85": h.Scalar(inner=2.850),
+    "sky130_fd_pr__res_xhigh_po_5p73": h.Scalar(inner=5.300),
+}
+
+default_cap_sizes = {
+    # FIXME: Using documentation minimum sizing not sure of correct answer
+    "sky130_fd_pr__cap_mim_m3_1": (
+        h.Scalar(inner=2.000 * µ),
+        h.Scalar(inner=2.000 * µ),
+    ),
+    "sky130_fd_pr__cap_mim_m3_2": (
+        h.Scalar(inner=2.000 * µ),
+        h.Scalar(inner=2.000 * µ),
+    ),
+    "sky130_fd_pr__cap_var_lvt": (h.Scalar(inner=0.180 * µ), h.Scalar(inner=0.180 * µ)),
+    "sky130_fd_pr__cap_var_hvt": (h.Scalar(inner=0.180 * µ), h.Scalar(inner=0.180 * µ)),
+}
\ No newline at end of file
diff --git a/pdks/Sky130/sky130/pdk_logic.py b/pdks/Sky130/sky130/pdk_logic.py
new file mode 100644
index 0000000..b2cb20d
--- /dev/null
+++ b/pdks/Sky130/sky130/pdk_logic.py
@@ -0,0 +1,423 @@
+""" 
+
+# Hdl21 + SkyWater 130nm Open-Source PDK Modules and Transformations 
+
+Defines a set of `hdl21.ExternalModule`s comprising the essential devices of the SkyWater 130nm open-source PDK, '
+and an `hdl21pdk.netlist` method for converting process-portable `hdl21.Primitive` elements into these modules. 
+
+The complete 130nm design kit includes hundreds of devices. A small subset are targets for conversion from `hdl21.Primitive`. 
+They include: 
+
+* "Core" Mos transistors `sky130_fd_pr__{nfet,pfet}_01v8{,_lvt,_hvt}
+
+And may in the near future also include: 
+
+* Resistors `sky130_fd_pr__res_*`
+* Capacitors `sky130_fd_pr__cap_*`
+* Bipolar Transistors `sky130_fd_pr__{npn,pnp}_*`
+* Diodes, which the PDK provides as SPICE `.model` statements alone, and will correspondingly need to be `hdl21.Module`s. 
+
+Many of the latter include a variety of "de-parameterization" steps not yet tested by this package's authors.  
+
+Remaining devices can be added to user-projects as `hdl21.ExternalModule`s, 
+or added to this package via pull request.  
+
+"""
+
+# Std-Lib Imports
+from pathlib import Path
+from typing import Dict, Optional, Any
+
+# PyPi Imports
+from pydantic.dataclasses import dataclass
+
+# Hdl21 Imports
+import hdl21 as h
+from hdl21.prefix import MILLI, TERA, MEGA
+from hdl21.pdk import PdkInstallation
+from hdl21.primitives import (
+    Mos,
+    PhysicalResistor,
+    PhysicalCapacitor,
+    Diode,
+    Bipolar,
+    ThreeTerminalResistor,
+    ThreeTerminalCapacitor,
+    MosParams,
+    PhysicalResistorParams,
+    PhysicalCapacitorParams,
+    DiodeParams,
+    BipolarParams,
+)
+
+# Import relevant data from the PDK's data module
+from .pdk_data import *
+
+@dataclass
+class Install(PdkInstallation):
+    """
+    PDK Installation Data for the Sky130 process.
+
+    This class represents site-specific PDK installation information, including paths to
+    the PDK installation, library files, and SPICE models reference. It also provides a
+    method to include the appropriate model files for a given process corner.
+
+    Attributes:
+        pdk_path (Path): Path to the PDK installation.
+        lib_path (Path): Relative path to the PDK ngspice library file.
+        model_ref (Path): Relative path to the PDK SPICE models reference.
+    """
+
+    pdk_path: Path  # Path to PDK installation
+    lib_path: Path  # Relative path to PDK ngpisce library file
+    model_ref: Path  # Relative path to PDK SPICE models reference
+
+    def include(self, corner: h.pdk.Corner) -> h.sim.Lib:
+        """
+        Get the model include file for the specified process corner.
+
+        This method returns the appropriate model include file for the given process corner.
+        It checks if the provided corner is a valid h.pdk.Corner instance and raises a
+        ValueError if it is not.
+
+        Args:
+            corner (h.pdk.Corner): The process corner for which to retrieve the model include file.
+
+        Returns:
+            h.sim.Lib: The model include file for the specified process corner.
+
+        Raises:
+            ValueError: If an invalid process corner is provided.
+        """
+
+        mos_corners: Dict[h.pdk.Corner, str] = {
+            h.pdk.Corner.TYP: "tt",
+            h.pdk.Corner.FAST: "ff",
+            h.pdk.Corner.SLOW: "ss",
+        }
+        if not isinstance(corner, h.pdk.Corner) or corner not in mos_corners:
+            raise ValueError(f"Invalid corner {corner}")
+
+        return h.sim.Lib(
+            path=self.pdk_path / self.lib_path, section=mos_corners[corner]
+        )
+
+    singleton: Optional["Install"] = None
+
+    @classmethod
+    def instance(cls) -> "Install":
+        if cls.singleton is None:
+            raise ValueError("The Install instance has not been initialized yet.")
+        return cls.singleton
+
+    def __post_init__(self):
+        if Install.singleton is not None:
+            raise ValueError("The Install instance has already been initialized.")
+        Install.singleton = self
+
+
+class Sky130Walker(h.HierarchyWalker):
+    """Hierarchical Walker, converting `h.Primitive` instances to process-defined `ExternalModule`s."""
+
+    def __init__(self):
+        super().__init__()
+        # Caches of our external module calls
+        self.mos_modcalls = dict()
+        self.res_modcalls = dict()
+        self.cap_modcalls = dict()
+        self.diode_modcalls = dict()
+        self.bjt_modcalls = dict()
+
+    def visit_primitive_call(self, call: h.PrimitiveCall) -> h.Instantiable:
+        """Replace instances of `h.primitive.Mos` with our `ExternalModule`s"""
+        # Replace transistors
+        if call.prim is Mos:
+            return self.mos_module_call(call.params)
+
+        elif call.prim is PhysicalResistor or call.prim is ThreeTerminalResistor:
+            return self.res_module_call(call.params)
+
+        elif call.prim is PhysicalCapacitor or call.prim is ThreeTerminalCapacitor:
+            return self.cap_module_call(call.params)
+
+        elif call.prim is Diode:
+            return self.diode_module_call(call.params)
+
+        elif call.prim is Bipolar:
+            return self.bjt_module_call(call.params)
+
+        else:
+            raise RuntimeError(f"{call.prim} is not legitimate primitive")
+
+        # Return everything else as-is
+        return call
+
+    def mos_module(self, params: MosParams) -> h.ExternalModule:
+        """Retrieve or create an `ExternalModule` for a MOS of parameters `params`."""
+        if params.model is not None:
+
+            try:
+                return [v for k, v in xtors.items() if params.model in k][0]
+            except IndexError:
+                msg = f"No Mos module for model name {(params.model)}"
+                raise RuntimeError(msg)
+
+        """
+        Here we wittle down our list of potential candidates
+
+        General parameters we're concerned with are:
+
+        1) h.MosType - Most important decision is it a PMOS or an NMOS, default = NMOS
+        2) h.MosFamily - Detemines what class of transistor, CORE, IO, HVT, default = CORE
+        3) h.MosVth - Mos Threshold, HIGH, STD, LOW, ULTRA_LOW, default = STD
+        """
+
+        # Map none to default, otherwise leave alone
+        mostype = h.MosType.NMOS if params.tp is None else params.tp
+        mosfam = h.MosFamily.CORE if params.family is None else params.family
+        mosvth = h.MosVth.STD if params.vth is None else params.vth
+        args = (mostype, mosfam, mosvth)
+
+        # Find all the xtors that match the args
+        subset = {}
+        for k,v in xtors.items():
+            
+            match = False
+            for a in args:
+                if a not in k:
+                    break
+            else:
+                match = True
+            
+            if match:
+                subset[k] = v
+
+        # FIXME: Probably should be an error...
+        # if len(subset) > 2:
+        #     msg = f"Mos module choice not well-defined given parameters {args}"
+        #     raise RuntimeError(msg)
+
+        # Return the first one (supported as of 3.7)
+        return next(iter(subset.values()))
+
+    def mos_module_call(self, params: MosParams) -> h.ExternalModuleCall:
+        """Retrieve or create a `Call` for MOS parameters `params`."""
+        # First check our cache
+        if params in CACHE.mos_modcalls:
+            return CACHE.mos_modcalls[params]
+
+        # Not found; create a new `ExternalModuleCall`.
+        # First retrieve the `ExternalModule`.
+        mod = self.mos_module(params)
+
+        w, l = self.use_defaults(params, mod.name, default_xtor_size)
+
+        # Select appropriate parameters for 20V/ESD-G5V0D10V5 mosfets
+        if "20v" in mod.name:
+
+            modparams = Sky130Mos20VParams(w=w, l=l, m=params.mult)
+
+        else:
+
+            modparams = Sky130MosParams(
+                w=w,
+                l=l,
+                nf=params.npar,  # FIXME: renaming
+                mult=params.mult,
+            )
+
+        # Combine the two into a call, cache and return it
+        modcall = mod(modparams)
+        CACHE.mos_modcalls[params] = modcall
+        return modcall
+
+    def res_module(self, params: PhysicalResistorParams):
+        """Retrieve or create an `ExternalModule` for a Resistor of parameters `params`."""
+        mod = ress.get((params.model), None)
+
+        if mod is None:
+            msg = f"No Res module for model {params.model}"
+            raise RuntimeError(msg)
+
+        return mod
+
+    def res_module_call(self, params: PhysicalResistorParams):
+
+        # First check our cache
+        if params in CACHE.res_modcalls:
+            return CACHE.res_modcalls[params]
+
+        mod = self.res_module(params)
+
+        if mod.paramtype == Sky130GenResParams:
+
+            w, l = self.use_defaults(params, mod.name, default_gen_res_size)
+
+            modparams = Sky130GenResParams(w=w, l=l)
+
+        elif mod.paramtype == Sky130PrecResParams:
+
+            l = default_prec_res_L[mod.name]
+
+            modparams = Sky130PrecResParams(l=l)
+
+        modcall = mod(modparams)
+        CACHE.res_modcalls[params] = modcall
+        return modcall
+
+    def cap_module(self, params: Any):
+        """Retrieve or create an `ExternalModule` for a Capacitor of parameters `params`."""
+        mod = caps.get((params.model), None)
+
+        if mod is None:
+            msg = f"No Capacitor module for model {params.model}"
+            raise RuntimeError(msg)
+
+        return mod
+
+    def cap_module_call(self, params: PhysicalCapacitorParams):
+
+        if params in CACHE.cap_modcalls:
+            return CACHE.cap_modcalls[params]
+
+        mod = self.cap_module(params)
+
+        m = 1
+
+        if params.mult is not None:
+
+            m = int(params.mult)
+
+        if mod.paramtype == Sky130MimParams:
+
+            w, l = self.use_defaults(params, mod.name, default_cap_sizes)
+            modparams = Sky130MimParams(w=w, l=l, mf=m)
+
+        elif mod.paramtype == Sky130VarParams:
+
+            w, l = self.use_defaults(params, mod.name, default_cap_sizes)
+            modparams = Sky130VarParams(w=w, l=l, vm=m)
+
+        modcall = mod(modparams)
+        CACHE.cap_modcalls[params] = modcall
+        return modcall
+
+    def diode_module(self, params: DiodeParams):
+        """Retrieve or create an `ExternalModule` for a Diode of parameters `params`."""
+        mod = diodes.get((params.model), None)
+
+        if mod is None:
+            msg = f"No Diode module for model {params.model}"
+            raise RuntimeError(msg)
+
+        return mod
+
+    def diode_module_call(self, params: DiodeParams):
+
+        if params in CACHE.diode_modcalls:
+            return CACHE.diode_modcalls[params]
+
+        mod = self.diode_module(params)
+
+        if params.w is not None and params.w is not None:
+
+            # This scaling is a quirk of SKY130
+            a = params.w * params.l * 1 * TERA
+            pj = 2 * (params.w + params.l) * MEGA
+            modparams = Sky130DiodeParams(area=a, pj=pj)
+
+        else:
+
+            modparams = Sky130DiodeParams()
+
+        modcall = mod(modparams)
+        CACHE.diode_modcalls[params] = modcall
+        return modcall
+
+    def bjt_module(self, params: BipolarParams):
+        """Retrieve or create an `ExternalModule` for a Bipolar of parameters `params`."""
+        mod = bjts.get(params.model, None)
+
+        if mod is None:
+            msg = f"No Bipolar module for model {params.model}"
+            raise RuntimeError(msg)
+
+        return mod
+
+    def bjt_module_call(self, params: BipolarParams):
+
+        if params in CACHE.bjt_modcalls:
+            return CACHE.bjt_modcalls[params]
+
+        mod = self.bjt_module(params)
+
+        if params.mult is not None:
+            mult = int(params.mult)
+        else:
+            mult = 1
+
+        modparams = Sky130BipolarParams(m=mult)
+
+        modcall = mod(modparams)
+        CACHE.bjt_modcalls[params] = modcall
+        return modcall
+
+    def scale_param(self, orig: Optional[h.Scalar], default: h.Prefixed) -> h.Scalar:
+        """Replace device size parameter-value `orig`.
+        Primarily type-dispatches across the need to scale to microns for this PDK."""
+        if orig is None:
+            return default
+        if not isinstance(orig, h.Scalar):
+            raise TypeError(f"Invalid Scalar parameter {orig}")
+        inner = orig.inner
+        if isinstance(inner, h.Prefixed):
+            return h.Scalar(inner=inner * MEGA)
+        if isinstance(inner, h.Literal):
+            return h.Scalar(inner=h.Literal(f"({inner} * 1e6)"))
+        raise TypeError(f"Param Value {inner}")
+
+    def use_defaults(self, params: h.paramclass, modname: str, defaults: dict):
+
+        w, l = None, None
+
+        if params.w is None:
+
+            w = defaults[modname][0]
+
+        else:
+
+            w = params.w
+
+        if params.l is None:
+
+            l = defaults[modname][1]
+
+        else:
+
+            l = params.l
+
+        w = self.scale_param(w, 1000 * MILLI)
+        l = self.scale_param(l, 1000 * MILLI)
+
+        return w, l
+
+
+def compile(src: h.Elaboratables) -> None:
+    """
+    Compile the given `src` into the Sample technology using the Sky130 process.
+
+    This function uses the Sky130Walker class to walk through the hierarchy of the input `src`
+    and replace instances of h.Primitive with process-defined ExternalModules. The Sky130Walker
+    class takes care of replacing different primitive components such as MOS transistors, resistors,
+    capacitors, diodes, and bipolar junction transistors with their respective Sky130 technology
+    counterparts.
+
+    Args:
+        src (h.Elaboratables): The input source representing the circuit to be compiled
+            into the Sample technology using the Sky130 process.
+
+    Returns:
+        None
+    """
+
+    Sky130Walker().walk(src)
\ No newline at end of file


Test patch:
diff --git a/hdl21/sim/tests/test_sim.py b/hdl21/sim/tests/test_sim.py
index c58ad62..c8d3ca0 100644
--- a/hdl21/sim/tests/test_sim.py
+++ b/hdl21/sim/tests/test_sim.py
@@ -56,7 +56,7 @@ def test_sim2():
             Meas(analysis="mytr", name="a_delay", expr="trig_targ_something"),
             Include("/home/models"),
             Lib(path="/home/models", section="fast"),
-            Options(reltol=1e-9),
+            Options(1e-9,name="reltol"),
         ],
     )
     to_proto(s)
@@ -90,7 +90,8 @@ def test_simattrs():
     s.meas(analysis=tr, name="a_delay", expr="trig_targ_something")
     s.include("/home/models")
     s.lib(path="/home/models", section="fast")
-    s.options(reltol=1e-9)
+    s.options(1e-9,name="reltol")
+
 
     to_proto(s)
 
@@ -124,7 +125,7 @@ class MySim:
             npts=11,
         )
         a_delay = Meas(analysis=mytran, expr="trig_targ_something")
-        opts = Options(reltol=1e-9)
+        opts = Options(1e-9,name="reltol")
 
         # Attributes whose names don't really matter can be called anything,
         # but must be *assigned* into the class, not just constructed.
@@ -183,7 +184,7 @@ def test_proto1():
             Meas(analysis="mytr", name="a_delay", expr="trig_targ_something"),
             Include("/home/models"),
             Lib(path="/home/models", section="fast"),
-            Options(reltol=1e-9),
+            Options(1e-9,name="reltol"),
         ],
     )
 
diff --git a/pdks/Sky130/sky130/test_netlists.py b/pdks/Sky130/sky130/test_netlists.py
index 24c6fdf..1952e63 100644
--- a/pdks/Sky130/sky130/test_netlists.py
+++ b/pdks/Sky130/sky130/test_netlists.py
@@ -13,7 +13,8 @@
 
 import hdl21 as h
 from hdl21.prefix import µ
-import sky130# No weird or illegal parameters...
+import sky130  # No weird or illegal parameters...
+
 
 def test_xtor_netlists():
     """
@@ -43,14 +44,39 @@ class SingleXtor:
 
         # Netlist and compare
         s = StringIO()
-        h.netlist(mod, dest=s, fmt="spice")
-        s = s.getvalue().split("\n")
 
-        assert s[9] == "+ a b c d "  # Correctly maps ports to their places...
-        assert s[10] == "+ " + sky130.xtors[x].name + " "  # Has correct PDK name...
-        assert (
-            s[11] == "+ w='30' l='30' nf='1' mult='1' "
-        )  # No weird or illegal parameters...
+        # Ignore the iso model, its not used in the test
+        if sky130.xtors[x].name != "sky130_fd_pr__nfet_20v0_iso":
+
+            h.netlist(mod, dest=s, fmt="spice")
+            s = s.getvalue().split("\n")
+
+            assert s[9] == "+ a b c d "  # Correctly maps ports to their places...
+            assert s[10] == "+ " + sky130.xtors[x].name + " "  # Has correct PDK name...
+
+            if "20v" not in sky130.xtors[x].name:
+
+                assert (
+                    s[11]
+                    == "+ w='30' \
+l='30' \
+nf='1' \
+ad='int((nf+1)/2) * w/nf * 0.29' \
+As='int((nf+2)/2) * w/nf * 0.29' \
+pd='2*int((nf+1)/2) * (w/nf + 0.29)' \
+ps='2*int((nf+2)/2) * (w/nf + 0.29)' \
+nrd='0.29 / w' \
+nrs='0.29 / w' \
+sa='0' \
+sb='0' \
+sd='0' \
+mult='1' \
+m='1' "
+                )  # No weird or illegal parameters...
+
+            else:
+
+                assert s[11] == "+ w='30' l='30' m='1' "
 
 
 def test_2_term_res_netlists():
@@ -131,7 +157,7 @@ class SingleRes:
 
             # Are you a generic resistor??
             if x.startswith("GEN"):
-              
+
                 assert s[9] == "+ x y z "  # Correctly maps ports to their places
                 assert s[10] == "+ " + name + " "  # Has correct PDK name
                 assert (
@@ -139,7 +165,7 @@ class SingleRes:
                 )  # No weird or illegal parameters...
 
             else:
-                
+
                 fixed_length = float(sky130.default_prec_res_L[name].inner)
 
                 assert s[9] == "+ x y z "  # Correctly maps ports to their places
@@ -183,10 +209,10 @@ class SingleDiode:
 
         assert s[9] == "+ x y "  # Correctly maps ports to their places...
         assert s[10] == "+ " + sky130.diodes[x].name + " "  # Has correct PDK name...
-        assert s[11] == "+ a='9T' pj='12M' "  # No weird or illegal parameters...
+        assert s[11] == "+ area='9T' pj='12M' "  # No weird or illegal parameters...
 
 
-def test_bjt_netlists():
+def test_pnp_netlists():
     @h.generator
     def GenBipolar(params: h.BipolarParams) -> h.Module:
         @h.module
@@ -199,21 +225,55 @@ class SingleBipolar:
 
     for x in sky130.bjts.keys():
 
-        # Relevant param
-        p = h.BipolarParams(model=x)
+        if x.startswith("PNP"):
+            # Relevant param
+            p = h.BipolarParams(model=x)
 
-        # Generate and compile
-        mod = h.elaborate(GenBipolar(p))
-        sky130.compile(mod)
+            # Generate and compile
+            mod = h.elaborate(GenBipolar(p))
+            sky130.compile(mod)
 
-        # Netlist and compare
-        s = StringIO()
-        h.netlist(mod, dest=s, fmt="spice")
-        s = s.getvalue().split("\n")
+            # Netlist and compare
+            s = StringIO()
+            h.netlist(mod, dest=s, fmt="spice")
+            s = s.getvalue().split("\n")
+
+            assert s[9] == "+ x y z "  # Correctly maps ports to their places...
+            assert s[10] == "+ " + sky130.bjts[x].name + " "  # Has correct PDK name...
+            assert s[11] == "+ m='1' "  # No weird or illegal parameters...
+
+
+def test_npn_netlists():
+
+    p = sky130.Sky130BipolarParams()
 
-        assert s[9] == "+ x y z "  # Correctly maps ports to their places...
-        assert s[10] == "+ " + sky130.bjts[x].name + " "  # Has correct PDK name...
-        assert s[11] == "+ m='1' "  # No weird or illegal parameters...
+    @h.module
+    class Bipolar1:
+
+        w, x, y, z = 4 * h.Signal()
+        genBipolar = sky130.modules.NPN_5p0V_1x2(p)(c=w, b=x, e=y, s=z)
+
+    @h.module
+    class Bipolar2:
+
+        w, x, y, z = 4 * h.Signal()
+        genBipolar = sky130.modules.NPN_5p0V_1x1(p)(c=w, b=x, e=y, s=z)
+
+    s = StringIO()
+    h.netlist(Bipolar1, dest=s, fmt="spice")
+    s = s.getvalue().split("\n")
+
+    assert s[9] == "+ w x y z "  # Correctly maps ports to their places...
+    assert s[10] == "+ sky130_fd_pr__npn_05v5_W1p00L2p00 "  # Has correct PDK name...
+    assert s[11] == "+ m='1' "  # No weird or illegal parameters...
+
+    s = StringIO()
+    h.netlist(Bipolar2, dest=s, fmt="spice")
+    s = s.getvalue().split("\n")
+
+    assert s[9] == "+ w x y z "  # Correctly maps ports to their places...
+    assert s[10] == "+ sky130_fd_pr__npn_05v5_W1p00L1p00 "  # Has correct PDK name...
+    assert s[11] == "+ m='1' "  # No weird or illegal parameters...
 
 
 def test_mim_cap_netlists():
@@ -281,7 +341,6 @@ class SingleCap:
 
 
 def test_vpp_cap_netlists():
-
     @h.generator
     def T3VPPCap(params: h.PhysicalCapacitorParams) -> h.Module:
         @h.module
@@ -296,41 +355,38 @@ class SingleCap:
 
         if x.startswith("VPP_PARA"):
 
-            p = sky130.Sky130VPPParams(w=3,l=3)
+            p = sky130.Sky130VPPParams(w=3, l=3)
 
             @h.module
             class SingleCap:
 
                 a, b, c = 3 * h.Signal()
                 exec(f"genCap = sky130.modules.{x}")
-                Cap = genCap(p)(p=a,n=b,b=c)
+                Cap = genCap(p)(p=a, n=b, b=c)
 
             # Generate
             mod = SingleCap
             sky130.compile(mod)
 
-
             # Netlist and compare
             s = StringIO()
             h.netlist(mod, dest=s, fmt="spice")
             s = s.getvalue().split("\n")
 
             assert s[9] == "+ a b c "  # Correctly maps ports to their places...
-            assert (
-                s[10] == "+ " + sky130.vpps[x].name + " "
-            )  # Has correct PDK name...
+            assert s[10] == "+ " + sky130.vpps[x].name + " "  # Has correct PDK name...
             assert s[11] == "+ w='3' l='3' mult='1' m='1' "
 
         if x.startswith("VPP_PERP"):
 
-            p = sky130.Sky130VPPParams(w=3,l=3)
+            p = sky130.Sky130VPPParams(w=3, l=3)
 
             @h.module
             class SingleCap:
 
                 a, b, c, d = 4 * h.Signal()
                 exec(f"genCap = sky130.modules.{x}")
-                Cap = genCap(p)(p=a,n=b,t=c,b=d)
+                Cap = genCap(p)(p=a, n=b, t=c, b=d)
 
             # Generate
             mod = SingleCap
@@ -342,7 +398,5 @@ class SingleCap:
             s = s.getvalue().split("\n")
 
             assert s[9] == "+ a b c d "  # Correctly maps ports to their places...
-            assert (
-                s[10] == "+ " + sky130.vpps[x].name + " "
-            )  # Has correct PDK name...
+            assert s[10] == "+ " + sky130.vpps[x].name + " "  # Has correct PDK name...
             assert s[11] == "+ w='3' l='3' mult='1' m='1' "
diff --git a/pdks/Sky130/sky130/test_pdk.py b/pdks/Sky130/sky130/test_pdk.py
index 75986c0..7bcb6ee 100644
--- a/pdks/Sky130/sky130/test_pdk.py
+++ b/pdks/Sky130/sky130/test_pdk.py
@@ -6,8 +6,8 @@
 
 from io import StringIO
 import hdl21 as h
-from . import pdk as sky130
-from .pdk import modules as s
+from . import pdk_logic as sky130
+from .pdk_logic import modules as s
 from hdl21.prefix import µ
 from hdl21.primitives import *
 
@@ -23,30 +23,41 @@ def mos_primitives_module():
 
     @h.module
     class Primitives:
-        """Module with all the primitives supported by the PDK package"""
+        """Module with all the non-20v FETs supported by the PDK package"""
 
         z = h.Signal(desc="Sole signal connected to everything")
 
         # Add all generic transistors
-        nfet_01v8 = h.Mos(model="NMOS_1p8V_STD")(d=z, g=z, s=z, b=z)
-        nfet_01v8_lvt = h.Mos(model="NMOS_1p8V_LOW")(d=z, g=z, s=z, b=z)
-        pfet_01v8 = h.Mos(model="PMOS_1p8V_STD")(d=z, g=z, s=z, b=z)
-        pfet_01v8_hvt = h.Mos(model="PMOS_1p8V_HIGH")(d=z, g=z, s=z, b=z)
-        pfet_01v8_lvt = h.Mos(model="PMOS_1p8V_LOW")(d=z, g=z, s=z, b=z)
-        pfet_g5v0d10v5 = h.Mos(model="PMOS_5p5V_D10_STD")(d=z, g=z, s=z, b=z)
-        nfet_g5v0d10v5 = h.Mos(model="NMOS_5p5V_D10_STD")(d=z, g=z, s=z, b=z)
-        pfet_g5v0d16v0 = h.Mos(model="PMOS_5p5V_D16_STD")(d=z, g=z, s=z, b=z)
+        nfet_01v8 = h.Mos(tp=MosType.NMOS, vth=MosVth.STD, family=MosFamily.CORE)(d=z, g=z, s=z, b=z)
+        nfet_01v8_lvt = h.Mos(tp=MosType.NMOS, vth=MosVth.LOW, family=MosFamily.CORE)(d=z, g=z, s=z, b=z)
+        pfet_01v8 = h.Mos(tp=MosType.PMOS, vth=MosVth.STD, family=MosFamily.CORE)(d=z, g=z, s=z, b=z)
+        pfet_01v8_hvt = h.Mos(tp=MosType.PMOS, vth=MosVth.HIGH, family=MosFamily.CORE)(d=z, g=z, s=z, b=z)
+        pfet_01v8_lvt = h.Mos(tp=MosType.PMOS, vth=MosVth.LOW, family=MosFamily.CORE)(d=z, g=z, s=z, b=z)
+        pfet_g5v0d10v5 = h.Mos(tp=MosType.PMOS, vth=MosVth.STD, family=MosFamily.IO)(d=z, g=z, s=z, b=z)
+        nfet_g5v0d10v5 = h.Mos(tp=MosType.NMOS, vth=MosVth.STD, family=MosFamily.IO)(d=z, g=z, s=z, b=z)
+        pfet_g5v0d16v0 = h.Mos(tp=MosType.PMOS, vth=MosVth.STD, family=MosFamily.IO)(d=z, g=z, s=z, b=z)
+        nfet_03v3_nvt = h.Mos(tp=MosType.NMOS, vth=MosVth.NATIVE, family=MosFamily.NONE)(d=z, g=z, s=z, b=z)
+        nfet_05v0_nvt = h.Mos(tp=MosType.NMOS, vth=MosVth.NATIVE, family=MosFamily.NONE)(d=z, g=z, s=z, b=z)
+        esd_nfet_01v8 = h.Mos(tp=MosType.NMOS, vth=MosVth.STD, family=MosFamily.CORE)(d=z, g=z, s=z, b=z)
+        esd_nfet_g5v0d10v5 = h.Mos(tp=MosType.NMOS, vth=MosVth.STD, family=MosFamily.IO)(d=z, g=z, s=z, b=z)
+        esd_nfet_g5v0d10v5_nvt = h.Mos(tp=MosType.NMOS, vth=MosVth.NATIVE, family=MosFamily.IO)(d=z, g=z, s=z, b=z)
+        esd_pfet_g5v0d10v5 = h.Mos(tp=MosType.PMOS, vth=MosVth.STD, family=MosFamily.IO)(d=z, g=z, s=z, b=z)
+
+    return Primitives
+
+
+def mos20v_primitives_module():
+    @h.module
+    class Primitives:
+        """Module with all the 20v FETs supported by the PDK package"""
+
+        z = h.Signal(desc="Sole signal connected to everything")
+
         nfet_20v0 = h.Mos(model="NMOS_20p0V_STD")(d=z, g=z, s=z, b=z)
         nfet_20v0_zvt = h.Mos(model="NMOS_20p0V_LOW")(d=z, g=z, s=z, b=z)
-        nfet_20v0_iso = h.Mos(model="NMOS_ISO_20p0V")(d=z, g=z, s=z, b=z)
+        # nfet_20v0_iso = h.Mos(model="NMOS_ISO_20p0V")(d=z, g=z, s=z, b=z) <-- can't be compiled.
         pfet_20v0 = h.Mos(model="PMOS_20p0V")(d=z, g=z, s=z, b=z)
-        nfet_03v3_nvt = h.Mos(model="NMOS_3p3V_NAT")(d=z, g=z, s=z, b=z)
-        nfet_05v0_nvt = h.Mos(model="NMOS_5p0V_NAT")(d=z, g=z, s=z, b=z)
         nfet_20v0_nvt = h.Mos(model="NMOS_20p0V_NAT")(d=z, g=z, s=z, b=z)
-        esd_nfet_01v8 = h.Mos(model="ESD_NMOS_1p8V")(d=z, g=z, s=z, b=z)
-        esd_nfet_g5v0d10v5 = h.Mos(model="ESD_NMOS_5p5V_D10")(d=z, g=z, s=z, b=z)
-        esd_nfet_g5v0d10v5_nvt = h.Mos(model="ESD_NMOS_5p5V_NAT")(d=z, g=z, s=z, b=z)
-        esd_pfet_g5v0d10v5 = h.Mos(model="ESD_PMOS_5p5V")(d=z, g=z, s=z, b=z)
 
     return Primitives
 
@@ -59,7 +70,7 @@ class ResistorPrimitives:
         z = h.Signal(desc="Sole signal connected to everything")
 
         res_gen_po = h.PhysicalResistor(model="GEN_PO")(p=z, n=z)
-        res_gen_li = h.PhysicalResistor(model="GEN_LI")(p=z, n=z)
+        res_gen_li = h.PhysicalResistor(model="GEN_L1")(p=z, n=z)
         res_gen_m1 = h.PhysicalResistor(model="GEN_M1")(p=z, n=z)
         res_gen_m2 = h.PhysicalResistor(model="GEN_M2")(p=z, n=z)
         res_gen_m3 = h.PhysicalResistor(model="GEN_M3")(p=z, n=z)
@@ -84,7 +95,7 @@ class ResistorPrimitives:
         res_p_prec_069 = h.ThreeTerminalResistor(model="PP_PREC_0p69")(p=z, n=z, b=z)
         res_p_prec_141 = h.ThreeTerminalResistor(model="PP_PREC_1p41")(p=z, n=z, b=z)
         res_p_prec_285 = h.ThreeTerminalResistor(model="PP_PREC_2p85")(p=z, n=z, b=z)
-        res_p_prec_530 = h.ThreeTerminalResistor(model="PP_PREC_5p30")(p=z, n=z, b=z)
+        res_p_prec_573 = h.ThreeTerminalResistor(model="PP_PREC_5p73")(p=z, n=z, b=z)
 
         res_p_minus_prec_035 = h.ThreeTerminalResistor(model="PM_PREC_0p35")(
             p=z, n=z, b=z
@@ -98,7 +109,7 @@ class ResistorPrimitives:
         res_p_minus_prec_285 = h.ThreeTerminalResistor(model="PM_PREC_2p85")(
             p=z, n=z, b=z
         )
-        res_p_minus_prec_530 = h.ThreeTerminalResistor(model="PM_PREC_5p30")(
+        res_p_minus_prec_573 = h.ThreeTerminalResistor(model="PM_PREC_5p73")(
             p=z, n=z, b=z
         )
 
@@ -136,9 +147,12 @@ class BjtPrimitives:
 
         z = h.Signal(desc="Sole signal connected to everything")
 
-        npn_50v = h.Npn(model="NPN_5p0V")
-        npn_110v = h.Npn(model="NPN_11p0V")
-        pnp_50v = h.Pnp(model="PNP_5p0V")
+        # No compilation support for these yet
+        # npn_5_1x2  = h.Npn(model = "NPN_5p0V_1x2")(e=z, b=z, c=z, s=z)
+        # npn_11_1x1 = h.Npn(model = "NPN_11p0V_1x1")(e=z, b=z, c=z, s=z)
+        # npn_5_1x1  = h.Npn(model = "NPN_5p0V_1x1")(e=z, b=z, c=z, s=z)
+        pnp_5_1x1 = h.Pnp(model="PNP_5p0V_0p68x0p68")(e=z, b=z, c=z)
+        pnp_5_3x3 = h.Pnp(model="PNP_5p0V_3p40x3p40")(e=z, b=z, c=z)
 
     return BjtPrimitives
 
@@ -167,6 +181,7 @@ class CapacitorPrimitives:
 
     return CapacitorPrimitives
 
+
 def _compile_and_test(prims: h.Module, paramtype: h.paramclass):
 
     # Compile
@@ -184,6 +199,7 @@ def _compile_and_test(prims: h.Module, paramtype: h.paramclass):
 def test_compile():
 
     _compile_and_test(mos_primitives_module(), sky130.Sky130MosParams)
+    _compile_and_test(mos20v_primitives_module(), sky130.Sky130Mos20VParams)
     _compile_and_test(genres_primitives_module(), sky130.Sky130GenResParams)
     _compile_and_test(precres_primitives_module(), sky130.Sky130PrecResParams)
     _compile_and_test(diode_primitives_module(), sky130.Sky130DiodeParams)
@@ -197,13 +213,16 @@ def _netlist(prims):
     # Netlist it for the PDK
     sky130.compile(prims)
     h.netlist(prims, StringIO(), fmt="spice")
-    h.netlist(prims, StringIO(), fmt="spectre")
-    h.netlist(prims, StringIO(), fmt="verilog")
+
+    # FIXME: The following netlist formats are not yet supported
+    # h.netlist(prims, StringIO(), fmt="spectre")
+    # h.netlist(prims, StringIO(), fmt="verilog")
 
 
 def test_netlist():
 
     _netlist(mos_primitives_module())
+    _netlist(mos20v_primitives_module())
     _netlist(genres_primitives_module())
     _netlist(precres_primitives_module())
     _netlist(diode_primitives_module())
@@ -215,6 +234,7 @@ def test_netlist():
 def test_mos_module():
 
     p = sky130.Sky130MosParams()
+    q = sky130.Sky130Mos20VParams()
 
     @h.module
     class HasXtors:
@@ -226,13 +246,13 @@ class HasXtors:
         pfet_g5v0d10v5 = s.PMOS_5p5V_D10_STD(p)()
         nfet_g5v0d10v5 = s.NMOS_5p5V_D10_STD(p)()
         pfet_g5v0d16v0 = s.PMOS_5p5V_D16_STD(p)()
-        nfet_20v0 = s.NMOS_20p0V_STD(p)()
-        nfet_20v0_zvt = s.NMOS_20p0V_LOW(p)()
-        nfet_20v0_iso = s.NMOS_ISO_20p0V(p)()
-        pfet_20v0 = s.PMOS_20p0V(p)()
+        nfet_20v0 = s.NMOS_20p0V_STD(q)()
+        nfet_20v0_zvt = s.NMOS_20p0V_LOW(q)()
+        nfet_20v0_iso = s.NMOS_ISO_20p0V(q)()
+        pfet_20v0 = s.PMOS_20p0V(q)()
         nfet_03v3_nvt = s.NMOS_3p3V_NAT(p)()
         nfet_05v0_nvt = s.NMOS_5p0V_NAT(p)()
-        nfet_20v0_nvt = s.NMOS_20p0V_NAT(p)()
+        nfet_20v0_nvt = s.NMOS_20p0V_NAT(q)()
         esd_nfet_01v8 = s.ESD_NMOS_1p8V(p)()
         esd_nfet_g5v0d10v5 = s.ESD_NMOS_5p5V_D10(p)()
         esd_nfet_g5v0d10v5_nvt = s.ESD_NMOS_5p5V_NAT(p)()
@@ -246,7 +266,7 @@ def test_genres_module():
     @h.module
     class HasGenRes:
         res_gen_po = s.GEN_PO(p)()
-        res_gen_li = s.GEN_LI(p)()
+        res_gen_l1 = s.GEN_L1(p)()
         res_gen_m1 = s.GEN_M1(p)()
         res_gen_m2 = s.GEN_M2(p)()
         res_gen_m3 = s.GEN_M3(p)()
@@ -267,12 +287,12 @@ class HasPrecRes:
         res_p_prec_069 = s.PP_PREC_0p69(p)()
         res_p_prec_141 = s.PP_PREC_1p41(p)()
         res_p_prec_285 = s.PP_PREC_2p85(p)()
-        res_p_prec_530 = s.PP_PREC_5p30(p)()
+        res_p_prec_573 = s.PP_PREC_5p73(p)()
         res_p_minus_prec_035 = s.PM_PREC_0p35(p)()
         res_p_minus_prec_069 = s.PM_PREC_0p69(p)()
         res_p_minus_prec_141 = s.PM_PREC_1p41(p)()
         res_p_minus_prec_285 = s.PM_PREC_2p85(p)()
-        res_p_minus_prec_530 = s.PM_PREC_5p30(p)()
+        res_p_minus_prec_573 = s.PM_PREC_5p73(p)()
 
 
 def test_diode_module():
@@ -303,9 +323,11 @@ def test_bjt_module():
     @h.module
     class HasBJTs:
 
-        npn_50v = s.NPN_5p0V(p)()
-        npn_110v = s.NPN_11p0V(p)()
-        pnp_50v = s.PNP_5p0V(p)()
+        npn_5_1x2 = s.NPN_5p0V_1x2(p)()
+        npn_11_1x1 = s.NPN_11p0V_1x1(p)()
+        npn_5_1x1 = s.NPN_5p0V_1x1(p)()
+        pnp_5_1x1 = s.PNP_5p0V_0p68x0p68(p)()
+        pnp_5_3x3 = s.PNP_5p0V_3p40x3p40(p)()
 
 
 def test_gencap_module():
diff --git a/pdks/Sky130/sky130/test_site.py b/pdks/Sky130/sky130/test_site.py
deleted file mode 100644
index d9b8eaa..0000000
--- a/pdks/Sky130/sky130/test_site.py
+++ /dev/null
@@ -1,133 +0,0 @@
-"""
-# Test Site
-
-Tests which are only valid with a PDK `install` configured, 
-generally via a `sitepdks` package. 
-"""
-
-import pytest
-
-# Import the site PDKs, or skip all these tests if not available.
-sitepdks = pytest.importorskip("sitepdks")
-
-# If that succeeded, import the PDK we want to test.
-# It should have a valid `install` attribute.
-import sky130
-import hdl21 as h
-import vlsirtools.spice as vsp
-from .pdk import modules as s
-
-
-def test_installed():
-    assert sky130.install is not None
-    assert isinstance(sky130.install, sky130.Install)
-
-
-def test_sim():
-    """# Test invoking simulation"""
-
-    @h.sim.sim
-    class Sim:
-        """Single transistor simulation"""
-
-        @h.module
-        class Tb:  # Testbench
-            VSS = h.Port()
-            vdd = h.Signal()
-            v = h.Vdc(dc=1)(p=vdd, n=VSS)
-            n = sky130.modules.NMOS_1p8V_STD()(d=vdd, g=vdd, s=VSS, b=VSS)
-
-        # Simulation Controls
-        op = h.sim.Op()
-        inc = sky130.install.include(h.pdk.Corner.TYP)
-
-    opts = vsp.SimOptions(
-        simulator=vsp.SupportedSimulators.NGSPICE,
-        fmt=vsp.ResultFormat.SIM_DATA,
-        rundir="./scratch",
-    )
-    rv = Sim.run(opts)
-    assert isinstance(rv, vsp.sim_data.SimResult)
-
-    op = rv[vsp.sim_data.AnalysisType.OP]
-    assert isinstance(op, vsp.sim_data.OpResult)
-    id = abs(op.data["i(v.xtop.vv)"])
-    assert id > 5e-5
-    assert id < 6e-5
-
-
-def test_sim_inv():
-    """# Test invoking simulation on a default inverter"""
-
-    @h.module
-    class Inv:  # Default-sized inverter
-        i, o, VDD, VSS = 4 * h.Port()
-        n = sky130.modules.NMOS_1p8V_STD()(d=o, g=i, s=VSS, b=VSS)
-        p = sky130.modules.PMOS_1p8V_STD()(d=o, g=i, s=VDD, b=VDD)
-
-    @h.sim.sim
-    class Sim:
-        """# Inverter DC OP Simulation"""
-
-        @h.module
-        class Tb:  # Testbench
-            VSS = h.Port()
-            s, VDD = 2 * h.Signal()
-            vvdd = h.Vdc(dc=1)(p=VDD, n=VSS)
-            inv = Inv(i=s, o=s, VDD=VDD, VSS=VSS)
-
-        # Simulation Controls
-        op = h.sim.Op()
-        inc = h.sim.Lib(path=sky130.install.model_lib, section="tt")
-
-    opts = vsp.SimOptions(
-        simulator=vsp.SupportedSimulators.NGSPICE,
-        fmt=vsp.ResultFormat.SIM_DATA,
-        rundir="./scratch",
-    )
-    rv = Sim.run(opts)
-    assert isinstance(rv, vsp.sim_data.SimResult)
-
-    op = rv[vsp.sim_data.AnalysisType.OP]
-    assert isinstance(op, vsp.sim_data.OpResult)
-    vs = op.data["v(xtop.s)"]
-    assert vs > 0.5
-    assert vs < 0.55
-
-
-def test_sim_components():
-    @h.sim.sim
-    class Sim:
-        @h.module
-        class Tb:
-
-            VSS = h.Port()
-            vdd = h.Signal()
-            v = h.Vdc(dc=1)(p=vdd, n=VSS)
-
-            for idx, val in enumerate(vars(s).values()):
-
-                exec("comp_" + str(idx) + "=val()")
-
-                for jdx, p in enumerate(val().ports):
-                    if jdx == 0:
-                        exec("comp_" + str(idx) + "." + p + "=vdd")
-                    else:
-                        exec("comp_" + str(idx) + "." + p + "=VSS")
-
-            # Simulation Controls
-
-        op = h.sim.Op()
-        inc = h.sim.Lib(path=sky130.install.model_lib, section="tt")
-
-    opts = vsp.SimOptions(
-        simulator=vsp.SupportedSimulators.NGSPICE,
-        fmt=vsp.ResultFormat.SIM_DATA,
-        rundir="./scratch",
-    )
-
-    rv = Sim.run(opts)
-    assert isinstance(rv, vsp.sim_data.SimResult)
-
-    op = rv[vsp.sim_data.AnalysisType.OP]
-    assert isinstance(op, vsp.sim_data.OpResult)
diff --git a/pdks/Sky130/sky130/test_site_sims.py b/pdks/Sky130/sky130/test_site_sims.py
new file mode 100644
index 0000000..0acdf8b
--- /dev/null
+++ b/pdks/Sky130/sky130/test_site_sims.py
@@ -0,0 +1,664 @@
+"""
+# Test Site Simulations
+
+Tests all PDK components with default include statements
+to ensure simulators are functioning as intended.
+"""
+
+import pytest
+
+# Import the site PDKs, or skip all these tests if not available.
+sitepdks = pytest.importorskip("sitepdks")
+
+# If that succeeded, import the PDK we want to test.
+# It should have a valid `install` attribute.
+import sky130
+import hdl21 as h
+import vlsirtools.spice as vsp
+from sky130 import modules as s
+
+
+def test_installed():
+    """
+    Test if the PDK is installed and properly configured.
+
+    This test checks if the PDK `sky130.install` is not None and if its type
+    is `sky130.Install`. If both conditions are met, the test passes.
+    """
+    assert sky130.install is not None
+    assert isinstance(sky130.install, sky130.Install)
+
+
+def test_sim():
+    """
+    Test the DC operating point simulation of a single transistor.
+
+    This test creates a simple testbench with a single NMOS transistor, runs a DC operating point
+    simulation, and checks if the output current falls within a specific range. If the output current
+    is within the specified range, the test passes.
+    """
+
+    @h.sim.sim
+    class Sim:
+        """Single transistor simulation"""
+
+        @h.module
+        class Tb:  # Testbench
+            VSS = h.Port()
+            vdd = h.Signal()
+            v = h.Vdc(dc=1)(p=vdd, n=VSS)
+            n = sky130.modules.NMOS_1p8V_STD()(d=vdd, g=vdd, s=VSS, b=VSS)
+
+        # Simulation Controls
+        op = h.sim.Op()
+        inc = sky130.install.include(h.pdk.Corner.TYP)
+
+    opts = vsp.SimOptions(
+        simulator=vsp.SupportedSimulators.NGSPICE,
+        fmt=vsp.ResultFormat.SIM_DATA,
+        rundir="./scratch",
+    )
+    rv = Sim.run(opts)
+    assert isinstance(rv, vsp.sim_data.SimResult)
+
+    op = rv[vsp.sim_data.AnalysisType.OP]
+    assert isinstance(op, vsp.sim_data.OpResult)
+    id = abs(op.data["i(v.xtop.vv)"])
+    assert id > 5e-5
+    assert id < 6e-5
+
+
+def test_sim_inv():
+    """
+    Test the DC operating point simulation of a default-sized inverter.
+
+    This test creates a testbench with a default-sized inverter, runs a DC operating point
+    simulation, and checks if the output voltage falls within a specific range. If the output voltage
+    is within the specified range, the test passes.
+    """
+
+    @h.module
+    class Inv:  # Default-sized inverter
+        i, o, VDD, VSS = 4 * h.Port()
+        n = sky130.modules.NMOS_1p8V_STD()(d=o, g=i, s=VSS, b=VSS)
+        p = sky130.modules.PMOS_1p8V_STD()(d=o, g=i, s=VDD, b=VDD)
+
+    @h.sim.sim
+    class Sim:
+        """# Inverter DC OP Simulation"""
+
+        @h.module
+        class Tb:  # Testbench
+            VSS = h.Port()
+            s, VDD = 2 * h.Signal()
+            vvdd = h.Vdc(dc=1)(p=VDD, n=VSS)
+            inv = Inv(i=s, o=s, VDD=VDD, VSS=VSS)
+
+        # Simulation Controls
+        op = h.sim.Op()
+        inc = sky130.install.include(h.pdk.Corner.TYP)
+
+    opts = vsp.SimOptions(
+        simulator=vsp.SupportedSimulators.NGSPICE,
+        fmt=vsp.ResultFormat.SIM_DATA,
+        rundir="./scratch",
+    )
+    rv = Sim.run(opts)
+    assert isinstance(rv, vsp.sim_data.SimResult)
+
+    op = rv[vsp.sim_data.AnalysisType.OP]
+    assert isinstance(op, vsp.sim_data.OpResult)
+    vs = op.data["v(xtop.s)"]
+    assert vs > 0.5
+    assert vs < 0.55
+
+
+def test_sim_mosfets():
+    @h.sim.sim
+    class Sim:
+        @h.module
+        class Tb:
+
+            VSS = h.Port()
+            vdd = h.Signal()
+            v = h.Vdc(dc=1)(p=vdd, n=VSS)
+
+            p = sky130.Sky130MosParams
+            q = sky130.Sky130Mos20VParams
+
+            nfet_01v8 = s.NMOS_1p8V_STD(p(w=0.420, l=0.150))(d=vdd, g=vdd, s=VSS, b=VSS)
+            nfet_01v8_lvt = s.NMOS_1p8V_LOW(p(w=0.420, l=0.150))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+            pfet_01v8 = s.PMOS_1p8V_STD(p(w=0.550, l=0.150))(d=vdd, g=vdd, s=VSS, b=VSS)
+            pfet_01v8_hvt = s.PMOS_1p8V_HIGH(p(w=0.550, l=0.150))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+            pfet_01v8_lvt = s.PMOS_1p8V_LOW(p(w=0.550, l=0.350))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+            pfet_g5v0d10v5 = s.PMOS_5p5V_D10_STD(p(w=0.420, l=0.500))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+            nfet_g5v0d10v5 = s.NMOS_5p5V_D10_STD(p(w=0.420, l=0.500))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+            pfet_g5v0d16v0 = s.PMOS_5p5V_D16_STD(p(w=5.000, l=0.660))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+
+            # This weirdo needs 5 terminals
+            nfet_20v0_iso = s.NMOS_ISO_20p0V(q(w=30.000, l=1.500))(
+                d=vdd, g=vdd, s=VSS, b=VSS, sub=VSS
+            )
+
+            # 20V series only accepts W/L/m
+            pfet_20v0 = s.PMOS_20p0V(q(w=30, l=1, m=1))(d=vdd, g=vdd, s=VSS, b=VSS)
+            nfet_20v0_zvt = s.NMOS_20p0V_LOW(q(w=30.000, l=1.500))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+            nfet_20v0 = s.NMOS_20p0V_STD(q(w=29.410, l=2.950))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+            nfet_20v0_nvt = s.NMOS_20p0V_NAT(q(w=30.000, l=1.000))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+
+            nfet_03v3_nvt = s.NMOS_3p3V_NAT(p(w=0.700, l=0.500))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+            nfet_05v0_nvt = s.NMOS_5p0V_NAT(p(w=0.700, l=0.900))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+            esd_nfet_01v8 = s.ESD_NMOS_1p8V(p(w=20.350, l=0.165))(
+                d=vdd, g=vdd, s=VSS, b=VSS
+            )
+
+            # ESD G5V0D10V5's are simply unavailable in the high-level SPICE libraries in the PDK!
+            # esd_nfet_g5v0d10v5_nvt = s.ESD_NMOS_5p5V_NAT(p(w = 10.000, l = 0.900))(d=vdd, g=vdd, s=VSS, b=VSS)
+            # esd_nfet_g5v0d10v5 = s.ESD_NMOS_5p5V_D10(p(w = 14.500, l = 0.550))(d=vdd, g=vdd, s=VSS, b=VSS)
+            # esd_pfet_g5v0d10v5 = s.ESD_PMOS_5p5V(p(w = 14.500, l = 0.550))(d=vdd, g=vdd, s=VSS, b=VSS)
+
+        # Simulation Controls
+        op = h.sim.Op()
+        # wnf = h.sim.Options(wnflag=1)
+        mc_mm_switch = h.sim.Param(0)
+        mc_pr_switch = h.sim.Param(0)
+        i1 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt.spice"
+        )
+        i2 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical.spice"
+        )
+        i3 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical__lin.spice"
+        )
+        i4 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt/specialized_cells.spice"
+        )
+        # inc = sky130.install.include(h.pdk.Corner.TYP)
+
+    opts = vsp.SimOptions(
+        simulator=vsp.SupportedSimulators.NGSPICE,
+        fmt=vsp.ResultFormat.SIM_DATA,
+        rundir="./scratch",
+    )
+
+    rv = Sim.run(opts)
+    assert isinstance(rv, vsp.sim_data.SimResult)
+
+    op = rv[vsp.sim_data.AnalysisType.OP]
+    assert isinstance(op, vsp.sim_data.OpResult)
+
+
+def test_sim_genres():
+    @h.sim.sim
+    class Sim:
+        @h.module
+        class Tb:
+
+            VSS = h.Port()
+            vdd = h.Signal()
+            v = h.Vdc(dc=1)(p=vdd, n=VSS)
+
+            p = sky130.Sky130GenResParams()
+
+            res_gen_po = s.GEN_PO(p)(p=VSS, n=vdd)
+
+            # FIXME: These models will only simulate in ngspice
+            # with a name starting with "R" (why? why not!)
+
+            res_gen_l1 = s.GEN_L1(p)(p=VSS, n=vdd)
+            res_gen_m1 = s.GEN_M1(p)(p=VSS, n=vdd)
+            res_gen_m2 = s.GEN_M2(p)(p=VSS, n=vdd)
+            res_gen_m3 = s.GEN_M3(p)(p=VSS, n=vdd)
+            res_gen_m4 = s.GEN_M4(p)(p=VSS, n=vdd)
+            res_gen_m5 = s.GEN_M5(p)(p=VSS, n=vdd)
+
+            res_gen_nd = s.GEN_ND(p)(p=VSS, n=vdd, b=VSS)
+            res_gen_pd = s.GEN_PD(p)(p=VSS, n=vdd, b=VSS)
+            res_gen_iso_pw = s.GEN_ISO_PW(p)(p=VSS, n=vdd, b=VSS)
+
+        # Simulation Controls
+        op = h.sim.Op()
+        # wnf = h.sim.Options(wnflag=1)
+
+        mc_mm_switch = h.sim.Param(0)
+        mc_pr_switch = h.sim.Param(0)
+
+        i1 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt.spice"
+        )
+        i2 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical.spice"
+        )
+        i3 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical__lin.spice"
+        )
+        i4 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt/specialized_cells.spice"
+        )
+        # inc = sky130.install.include(h.pdk.Corner.TYP)
+
+    opts = vsp.SimOptions(
+        simulator=vsp.SupportedSimulators.NGSPICE,
+        fmt=vsp.ResultFormat.SIM_DATA,
+        rundir="./scratch",
+    )
+
+    rv = Sim.run(opts)
+    assert isinstance(rv, vsp.sim_data.SimResult)
+
+    op = rv[vsp.sim_data.AnalysisType.OP]
+    assert isinstance(op, vsp.sim_data.OpResult)
+
+
+def test_sim_precres():
+    @h.sim.sim
+    class Sim:
+        @h.module
+        class Tb:
+
+            VSS = h.Port()
+            vdd = h.Signal()
+            v = h.Vdc(dc=1)(p=vdd, n=VSS)
+
+            p = sky130.Sky130PrecResParams()
+
+            res_p_prec_035 = s.PP_PREC_0p35(p)(p=VSS, n=vdd, b=VSS)
+            res_p_prec_069 = s.PP_PREC_0p69(p)(p=VSS, n=vdd, b=VSS)
+            res_p_prec_141 = s.PP_PREC_1p41(p)(p=VSS, n=vdd, b=VSS)
+            res_p_prec_285 = s.PP_PREC_2p85(p)(p=VSS, n=vdd, b=VSS)
+            res_p_prec_573 = s.PP_PREC_5p73(p)(p=VSS, n=vdd, b=VSS)
+            res_p_minus_prec_035 = s.PM_PREC_0p35(p)(p=VSS, n=vdd, b=VSS)
+            res_p_minus_prec_069 = s.PM_PREC_0p69(p)(p=VSS, n=vdd, b=VSS)
+            res_p_minus_prec_141 = s.PM_PREC_1p41(p)(p=VSS, n=vdd, b=VSS)
+            res_p_minus_prec_285 = s.PM_PREC_2p85(p)(p=VSS, n=vdd, b=VSS)
+            res_p_minus_prec_573 = s.PM_PREC_5p73(p)(p=VSS, n=vdd, b=VSS)
+
+        # Simulation Controls
+        op = h.sim.Op()
+        # wnf = h.sim.Options(wnflag=1)
+        mc_mm_switch = h.sim.Param(0)
+        mc_pr_switch = h.sim.Param(0)
+        i1 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt.spice"
+        )
+        i2 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical.spice"
+        )
+        i3 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical__lin.spice"
+        )
+        i4 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt/specialized_cells.spice"
+        )
+        # inc = sky130.install.include(h.pdk.Corner.TYP)
+
+    opts = vsp.SimOptions(
+        simulator=vsp.SupportedSimulators.NGSPICE,
+        fmt=vsp.ResultFormat.SIM_DATA,
+        rundir="./scratch",
+    )
+
+    rv = Sim.run(opts)
+    assert isinstance(rv, vsp.sim_data.SimResult)
+
+    op = rv[vsp.sim_data.AnalysisType.OP]
+    assert isinstance(op, vsp.sim_data.OpResult)
+
+
+def test_sim_bjt():
+    @h.sim.sim
+    class Sim:
+        @h.module
+        class Tb:
+
+            VSS = h.Port()
+            vdd = h.Signal()
+            v = h.Vdc(dc=1)(p=vdd, n=VSS)
+
+            p = sky130.Sky130BipolarParams()
+
+            # FIXME: NPN Transistors currently require a hack
+            npn_5_1x2 = s.NPN_5p0V_1x2(p)(c=VSS, b=vdd, e=VSS, s=VSS)
+            # npn_11_1x1 = s.NPN_11p0V_1x1(p)(c=VSS,b=vdd,e=VSS,s=VSS) # This one just won't work...
+            npn_5_1x1 = s.NPN_5p0V_1x1(p)(c=VSS, b=vdd, e=VSS, s=VSS)
+
+            # These function without the hack below
+            pnp_5_1x1 = s.PNP_5p0V_0p68x0p68(p)(c=VSS, b=vdd, e=VSS)
+            pnp_5_3x3 = s.PNP_5p0V_3p40x3p40(p)(c=VSS, b=vdd, e=VSS)
+
+        # Simulation Controls
+        op = h.sim.Op()
+        # wnf = h.sim.Options(wnflag=1)
+
+        # Hack: https://open-source-silicon.slack.com/archives/C016UL7AQ73/p1680254109624109?thread_ts=1680112037.990359&cid=C016UL7AQ73
+        dkisnpn1x1 = h.sim.Param(8.7913e-01)
+        dkbfnpn1x1 = h.sim.Param(9.8501e-01)
+        dkisnpn1x2 = h.sim.Param(9.0950e-01)
+        dkbfnpn1x2 = h.sim.Param(9.6759e-01)
+        dkisnpnpolyhv = h.sim.Param(1.0)
+        dkbfnpnpolyhv = h.sim.Param(1.0)
+
+        mc_mm_switch = h.sim.Param(0)
+        mc_pr_switch = h.sim.Param(0)
+        i1 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt.spice"
+        )
+        i2 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical.spice"
+        )
+        i3 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical__lin.spice"
+        )
+        i4 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt/specialized_cells.spice"
+        )
+
+    opts = vsp.SimOptions(
+        simulator=vsp.SupportedSimulators.NGSPICE,
+        fmt=vsp.ResultFormat.SIM_DATA,
+        rundir="./scratch",
+    )
+
+    rv = Sim.run(opts)
+    assert isinstance(rv, vsp.sim_data.SimResult)
+
+    op = rv[vsp.sim_data.AnalysisType.OP]
+    assert isinstance(op, vsp.sim_data.OpResult)
+
+
+def test_sim_diode():
+    @h.sim.sim
+    class Sim:
+        @h.module
+        class Tb:
+
+            VSS = h.Port()
+            vdd = h.Signal()
+            v = h.Vdc(dc=1)(p=vdd, n=VSS)
+
+            p = sky130.Sky130DiodeParams()
+
+            # FIXME: These are not working
+
+            # All require a name starting with D
+
+            pwnd_55v = s.PWND_5p5V(p)(p=vdd, n=VSS)
+            pwnd_110v = s.PWND_11p0V(p)(p=vdd, n=VSS)
+            nat_pwnd_55v = s.PWND_5p5V_NAT(p)(p=vdd, n=VSS)
+            lvt_pwnd_55v = s.PWND_5p5V_LVT(p)(p=vdd, n=VSS)
+            pdnw_55v = s.PDNW_5p5V(p)(p=vdd, n=VSS)
+            pdnw_110v = s.PDNW_11p0V(p)(p=vdd, n=VSS)
+            hvt_pdnw_55v = s.PDNW_5p5V_HVT(p)(p=vdd, n=VSS)
+            lvt_pdnw_55v = s.PDNW_5p5V_LVT(p)(p=vdd, n=VSS)
+            px_pwdn = s.PX_PWDN(p)(p=vdd, n=VSS)
+            px_psdn = s.PX_PSDN(p)(p=vdd, n=VSS)
+            px_psnw = s.PX_PSNW(p)(p=vdd, n=VSS)
+
+            # RF diodes are just strange
+
+            # px_rf_psnw = s.PX_RF_PSNW(p)(p=vdd, n=VSS)
+            # px_rf_pwdn = s.PX_RF_PWDN(p)(p=vdd, n=VSS)
+
+        # Simulation Controls
+        op = h.sim.Op()
+        # wnf = h.sim.Options(wnflag=1)
+        mc_mm_switch = h.sim.Param(0)
+        mc_pr_switch = h.sim.Param(0)
+        i1 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt.spice"
+        )
+        i2 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical.spice"
+        )
+        i3 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical__lin.spice"
+        )
+        i4 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt/specialized_cells.spice"
+        )
+        i5 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt/rf.spice"
+        )
+        # inc = sky130.install.include(h.pdk.Corner.TYP)
+
+    opts = vsp.SimOptions(
+        simulator=vsp.SupportedSimulators.NGSPICE,
+        fmt=vsp.ResultFormat.SIM_DATA,
+        rundir="./scratch",
+    )
+
+    rv = Sim.run(opts)
+    assert isinstance(rv, vsp.sim_data.SimResult)
+
+    op = rv[vsp.sim_data.AnalysisType.OP]
+    assert isinstance(op, vsp.sim_data.OpResult)
+
+
+def test_sim_gencap():
+    @h.sim.sim
+    class Sim:
+        @h.module
+        class Tb:
+
+            VSS = h.Port()
+            vdd = h.Signal()
+            v = h.Vdc(dc=1)(p=vdd, n=VSS)
+
+            p = sky130.Sky130MimParams()
+            q = sky130.Sky130VarParams()
+
+            MIM_M3 = s.MIM_M3(p)(p=vdd, n=VSS)
+            MIM_M4 = s.MIM_M4(p)(p=vdd, n=VSS)
+            VAR_LVT = s.VAR_LVT(q)(p=vdd, n=VSS, b=VSS)
+            VAR_HVT = s.VAR_HVT(q)(p=vdd, n=VSS, b=VSS)
+
+        # Simulation Controls
+        op = h.sim.Op()
+        # wnf = h.sim.Options(wnflag=1)
+        mc_mm_switch = h.sim.Param(0)
+        mc_pr_switch = h.sim.Param(0)
+        i1 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt.spice"
+        )
+        i2 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical.spice"
+        )
+        i3 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical__lin.spice"
+        )
+        i4 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt/specialized_cells.spice"
+        )
+        # inc = sky130.install.include(h.pdk.Corner.TYP)
+
+    opts = vsp.SimOptions(
+        simulator=vsp.SupportedSimulators.NGSPICE,
+        fmt=vsp.ResultFormat.SIM_DATA,
+        rundir="./scratch",
+    )
+
+    rv = Sim.run(opts)
+    assert isinstance(rv, vsp.sim_data.SimResult)
+
+    op = rv[vsp.sim_data.AnalysisType.OP]
+    assert isinstance(op, vsp.sim_data.OpResult)
+
+
+def test_sim_devcap():
+    @h.sim.sim
+    class Sim:
+        @h.module
+        class Tb:
+
+            VSS = h.Port()
+            vdd = h.Signal()
+            v = h.Vdc(dc=1)(p=vdd, n=VSS)
+
+            p = sky130.Sky130VPPParams()
+
+            # These work but you must include parameters from the
+            # the first 5 which parameterize all the others
+
+            cap_vpp_1 = s.VPP_PARA_1(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_2 = s.VPP_PARA_2(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_3 = s.VPP_PARA_3(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_4 = s.VPP_PARA_4(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_5 = s.VPP_PARA_5(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_6 = s.VPP_PARA_6(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_7 = s.VPP_PARA_7(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_8 = s.VPP_PARA_8(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_9 = s.VPP_PARA_9(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_10 = s.VPP_PARA_10(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_11 = s.VPP_PARA_11(p)(p=vdd, n=VSS, b=VSS)
+            cap_vpp_12 = s.VPP_PERP_1(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+            cap_vpp_13 = s.VPP_PERP_2(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+            cap_vpp_14 = s.VPP_PERP_3(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+            cap_vpp_15 = s.VPP_PERP_4(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+            cap_vpp_16 = s.VPP_PERP_5(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+            cap_vpp_17 = s.VPP_PERP_6(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+            cap_vpp_18 = s.VPP_PERP_7(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+            cap_vpp_19 = s.VPP_PERP_8(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+            cap_vpp_20 = s.VPP_PERP_9(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+            # cap_vpp_21 = s.VPP_PERP_10(p)(p=vdd, n=VSS, b=VSS, t=VSS)
+
+        # Simulation Controls
+        op = h.sim.Op()
+        # wnf = h.sim.Options(wnflag=1)
+        mc_mm_switch = h.sim.Param(0)
+        mc_pr_switch = h.sim.Param(0)
+        j1 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.model_ref
+            / "sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield_o2.spice"
+        )
+
+        j2 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.model_ref
+            / "sky130_fd_pr__cap_vpp_02p4x04p6_m1m2_noshield.spice"
+        )
+
+        j3 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.model_ref
+            / "sky130_fd_pr__cap_vpp_08p6x07p8_m1m2_noshield.spice"
+        )
+
+        j4 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.model_ref
+            / "sky130_fd_pr__cap_vpp_04p4x04p6_m1m2_noshield.spice"
+        )
+
+        j5 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.model_ref
+            / "sky130_fd_pr__cap_vpp_11p5x11p7_m1m2_noshield.spice"
+        )
+
+        i1 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt.spice"
+        )
+        i2 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical.spice"
+        )
+        i3 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "r+c/res_typical__cap_typical__lin.spice"
+        )
+        i4 = h.sim.Include(
+            sky130.install.pdk_path
+            / sky130.install.lib_path.parent
+            / "corners/tt/specialized_cells.spice"
+        )
+        # inc = sky130.install.include(h.pdk.Corner.TYP)
+
+    opts = vsp.SimOptions(
+        simulator=vsp.SupportedSimulators.NGSPICE,
+        fmt=vsp.ResultFormat.SIM_DATA,
+        rundir="./scratch",
+    )
+
+    rv = Sim.run(opts)
+    assert isinstance(rv, vsp.sim_data.SimResult)
+
+    op = rv[vsp.sim_data.AnalysisType.OP]
+    assert isinstance(op, vsp.sim_data.OpResult)