From 1123f733bd7b3c399a92a715bdf7d59aed23cc90 Mon Sep 17 00:00:00 2001 From: ThVerg Date: Tue, 11 Aug 2026 14:56:03 +0200 Subject: [PATCH] sky130: use special_nfet_01v8 model for sub-0.42um nfets The sky130 magic techfile splits nfet extraction on device width: device msubcircuit sky130_fd_pr__nfet_01v8 nfet,scnfet ... w>=0.42 device msubcircuit sky130_fd_pr__special_nfet_01v8 scnfet ... w<0.42 ptx always emitted spice["nmos"], so every gate built from a minimum-width device (drc["minwidth_tx"] = 0.36) was netlisted as sky130_fd_pr__nfet_01v8 while magic extracted it as sky130_fd_pr__special_nfet_01v8. netgen then reported a device class mismatch in each such cell: Circuit 1: sky130_fd_pr__special_nfet_01v8 (1) | Circuit 2: (no matching element) Circuit 1: (no matching element) | Circuit 2: sky130_fd_pr__nfet_01v8 (1) The signed-off macros shipped in sky130A/libs.ref/sky130_sram_macros use special_nfet_01v8 at this geometry, so this restores the naming those were built with. ptx now asks the technology for a narrow-device model, keyed by width. Technologies that declare no such model are unaffected, and there is no equivalent split for pfets so only the nmos is declared. After this change pinv and pinv_dec report "Netlists match uniquely" where they previously mismatched. Verified with ngspice that special_nfet_01v8 resolves through sky130.lib.spice, so characterization is unaffected. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FcKSuKSchDEd7MqT8AffpN --- compiler/modules/ptx.py | 28 ++++++++++++++++++++++++---- technology/sky130/tech/tech.py | 8 ++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/compiler/modules/ptx.py b/compiler/modules/ptx.py index 8e65aed0c..75af26291 100644 --- a/compiler/modules/ptx.py +++ b/compiler/modules/ptx.py @@ -116,12 +116,32 @@ def create_layout(self): # but this may be uncommented for debug purposes # self.DRC() + def get_model_name(self): + """ + Return the device model name for this transistor's width. + + Some technologies model narrow devices with a separate model. In + sky130, an nfet narrower than 0.42um is extracted as + sky130_fd_pr__special_nfet_01v8 rather than sky130_fd_pr__nfet_01v8 + (see the "device msubcircuit" rules in sky130A.tech), so the netlist + must use the same name or LVS reports a device class mismatch on + every gate built from a minimum-width device. + """ + narrow_model = spice.get("{}_narrow".format(self.tx_type)) + narrow_width = spice.get("{}_narrow_max_width".format(self.tx_type)) + if narrow_model and narrow_width and self.tx_width < narrow_width: + return narrow_model + + return spice[self.tx_type] + def create_netlist(self): pin_list = ["D", "G", "S", "B"] if self.tx_type == "nmos": body_dir = "GROUND" else: body_dir = "POWER" + + self.model_name = self.get_model_name() dir_list = ["INOUT", "INPUT", "INOUT", body_dir] self.add_pin_list(pin_list, dir_list) @@ -133,7 +153,7 @@ def create_netlist(self): self.channel_length = drc("minlength_channel") if cell_props.ptx.model_is_subckt: # sky130 - main_str = "X{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], + main_str = "X{{0}} {{1}} {0} m={1} w={2} l={3} ".format(self.model_name, self.mults, self.tx_width, self.channel_length) @@ -142,7 +162,7 @@ def create_netlist(self): area_str = "pd={0:.2f} ps={0:.2f} as={1:.2f}u ad={1:.2f}u".format(perimeter_sd, area_sd) else: - main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(self.model_name, self.mults, self.tx_width, self.channel_length) @@ -164,12 +184,12 @@ def create_netlist(self): self.tx_width, self.channel_length) elif cell_props.ptx.model_is_subckt: - self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2}u l={3}u".format(spice[self.tx_type], + self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2}u l={3}u".format(self.model_name, self.mults, self.tx_width, self.channel_length) else: - self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], + self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(self.model_name, self.mults, self.tx_width, self.channel_length) diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py index 8ee49f402..5d7a5514c 100755 --- a/technology/sky130/tech/tech.py +++ b/technology/sky130/tech/tech.py @@ -711,6 +711,14 @@ spice = {} spice["nmos"] = "sky130_fd_pr__nfet_01v8" spice["pmos"] = "sky130_fd_pr__pfet_01v8" + +# An nfet narrower than 0.42um is a different device in sky130: magic +# extracts it as sky130_fd_pr__special_nfet_01v8, so the netlist has to use +# that name below the threshold or netgen reports a device class mismatch. +# See the "device msubcircuit" lines in sky130A.tech. There is no equivalent +# split for pfets, so only the nmos is declared here. +spice["nmos_narrow"] = "sky130_fd_pr__special_nfet_01v8" +spice["nmos_narrow_max_width"] = 0.42 spice["power"]="vccd1" spice["ground"]="vssd1"