LVS of CMOS standard-cells: Ignore fourth terminal of 4-terminal MOS?

I try to add an automated LVS check into the librecell standard-cell layout generator. Netlist extraction works fine but I don't manage to compare the reference netlist and extracted netlist the way I want.

For now I use this function to make the comparison. It is obviously not yet what I want.

def compare_netlist(extracted: db.Netlist, reference: db.Netlist) -> bool:
    Check if two netlists are equal.
    :param extracted:
    :param reference:
    :return: Returns True iff the two netlists are equivalent.
    cmp = db.NetlistComparer()
    cmp.same_device_classes(db.DeviceClassMOS3Transistor(), db.DeviceClassMOS4Transistor())
    compare_result =, reference)"Netlist comparision result: {}".format(compare_result))

    if not compare_result:
        logger.warning("Netlists don't match.")
        print('extracted netlist:')
        print('reference netlist:')

    return compare_result

Now I get this output:

2020-02-20 11:40:42              lvs     INFO: Netlist comparision result: False
2020-02-20 11:40:42              lvs  WARNING: Netlists don't match.
extracted netlist:
circuit INVX1 (A=A,gnd=gnd,Y=Y,vdd=vdd);
  device PMOS $1 (S=Y,G=A,D=vdd) (L=0.05,W=0.5,AS=0.1125,AD=0.115,PS=1.45,PD=1.5);
  device NMOS $2 (S=gnd,G=A,D=Y) (L=0.05,W=0.25,AS=0.05875,AD=0.05875,PS=1,PD=1);

reference netlist:
circuit INVX1 (A=A,Y=Y,VDD=VDD,GND=GND);
  device PMOS '0' (S=Y,G=A,D=VDD,B=VDD) (L=0.05,W=0.5,AS=0,AD=0,PS=0,PD=0);
  device NMOS '1' (S=Y,G=A,D=GND,B=GND) (L=0.05,W=0.25,AS=0,AD=0,PS=0,PD=0);

The two netlists have the same topology of transistors but are not exactly the same. The reference is made up of 4-terminal MOS transistors whereas the layout actually only has 3-terminal transistors. The forth terminal comes from the well-tap cells and is therefore not visible in the layout.

So in the above case the comparison says "not the same netlist" but I want it to treat this netlists as equivalent.

My question: Can I tell the netlist comparer to just ignore the fourth terminal?

Thanks a lot in advance!


  • edited February 21

    Hi Thomas,

    unfortunately not directly as MOS3 and MOS4 are different devices and basically the forth terminal is part of the functional description. Imagine it's tied to the wrong potential - in this case the transistor would not work.

    The "same device classes" feature is to map different incarnations of MOS3 or MOS4 between schematic and layout, but cannot map MOS3 to MOS4 as these are different fundamental types.

    But it's possible to tell the Spice reader to read M devices as MOS3. The key concept is the "Spice reader delegate". It's explained here:

    This is the code in your case:

    class MOS4To3ConvertingSpiceReader < RBA::NetlistSpiceReaderDelegate
      def element(circuit, el, name, model, value, nets, params)
        if el != "M" || nets.size != 4
          # all other elements are left to the standard implementation
          return super
        # provide a device class
        cls = circuit.netlist.device_class_by_name(model)
        if ! cls
          cls = RBA::DeviceClassMOS3Transistor::new
 = model
        # create a device
        device = circuit.create_device(cls, name)
        # and configure the device
        [ "S", "G", "D" ].each_with_index do |t,index|
          device.connect_terminal(t, nets[index])
        # parameters in the model are given in micrometer units, so 
        # we need to translate the parameter values from SI to um values:
        device.set_parameter("W", (params["W"] || 0.0) * 1e6)
        device.set_parameter("L", (params["L"] || 0.0) * 1e6)
        # TODO: if required, add AS,AD,PS and PD ...
        return true
    # Instantiate a reader using the new delegate
    reader = RBA::NetlistSpiceReader::new(MOS4To3ConvertingSpiceReader::new)
    # Import the schematic with this reader
    schematic("your_schematic.cir", reader)

    Best regards,


  • Thank you so much Matthias! Worked out of the box.

    In case somebody also needs it for Python:

    class MOS4To3NetlistSpiceReader(db.NetlistSpiceReaderDelegate): """ Read SPICE netlists and convert 4-terminal MOS into 3-terminal MOS by dropping the body net. This is required for the LVS step when the standard cells are lacking well taps and therefore the body terminal of the transistors is unconnected. """ def element(self, circuit: db.Circuit, el: str, name: str, model: str, value, nets: List[db.Net], params: Dict[str, float]): """ Process a SPICE element. All elements except 4-terminal MOS transistors are left unchanged. :return: True iff the device has not been ignored and put into the netlist. """ if el != 'M' or len(nets) != 4: # All other elements are left to the standard implementation. return super().element(circuit, el, name, model, value, nets, params) else: # Provide a device class. cls = circuit.netlist().device_class_by_name(model) if not cls: # Create MOS3Transistor device class if it does not yet exist. cls = db.DeviceClassMOS3Transistor() = model circuit.netlist().add(cls) # Create MOS3 device. device: db.Device = circuit.create_device(cls, name) # Configure the MOS3 device. for terminal_name, net in zip(['S', 'G', 'D'], nets): device.connect_terminal(terminal_name, net) # Parameters in the model are given in micrometer units, so # we need to translate the parameter values from SI to um values. device.set_parameter('W', params.get('W', 0) * 1e6) device.set_parameter('L', params.get('L', 0) * 1e6) return True
  • edited February 24

    Thanks! But I still love Ruby :)

    Just one remark regarding this solution: the code above does not take the "M" parameter into account. For a fully compatible Spice reader, the code should read "M" as well and multiply W with M. Not a big change, but sometimes this may be important.

    Internally, KLayout does not use "M". Instead, only the total width of the transistor is used.

    Best regards,


  • For analog circuits where matching is an interest, it would be better to check explicitly all three params (W, L, M) because edge effects make W=2 M=1 different than W=1 M=2 depending on lithography. There are also capacitance concerns for the RF folks (Cdb varies by factor of 2 or so). While gross function / connectivity may be satisfied, LVS has additional roles (like enforcing schematic intent beyond just the hookup).

  • Hi Jim,

    true ... I just don't know how to tell apart digital from analog designers in my code (popup: "are you a digital guy?", answers: "yes", "no" and "don't care") :)

    I just introduced "tolerances". Maybe it's possible to extract "M" and give it a 100% tolerance by default. So by default it's not checked. And if you want, you could set the tolerance.

    BTW: KLayout also extracts AD,SD,PD and PS, but I can't imagine it makes sense to check this too. The idea was originally that having these values would allow a simulator to pick a somewhat better model, but before this happens, I think parasitics need to be included in the extraction.

    Best regards,


Sign In or Register to comment.