How to display the results of comparator in the netlist browser and generate a customized netlist

edited July 2021 in Verification

I want to extract the width and length of the resistor, and then generate a netlist containing the length and width after the comparison (the default output netlist by "target_netlist" only contains the resistance value, but I need the length and width of the resistor more).

However, it has the following problems:

1. "mismatch" in Netlist Database Browse

Even if the device topology and parameters match, the result in the netlist browser is still mismatch.

I used the same custom comparator as @cgelinek_radlogic in "".

Here is a comparison of the two netlists:

I have also tested it through the default comparator, the result turns out to be even stranger. Even if the extracted netlist does not change, the browser confirms that both vdd and gnd are connected to two nodes, and the topology does not match:

So how should I get the result of device match (by length and width instead of resistance value) in the browser?

2. more than the actual device in the variable device is puts

Too many device parameters were compared during the comparison, actually there is only one resistor in the gds file. This is no problem when there are only few devices in Layout, but if the number of devices to be verified is large, it will not be able to accurately determine the information of the mismatched device during the comparison process :

How should deal with this problem?

3. customized netlist generate

The default extracted netlist (by "target_netlist") only contains the resistance value, but the length and width of the resistance are more meaningful. Because if the length and width of the resistor are very small, the usual resistance value estimation methods have large tolerances.

How can I get the extractes netlist in the specified format?


  • edited July 2021

    Hier are the test file

  • edited July 2021


    Thanks for the testcase and the nice description of the problem.

    First of all there is a very simply explanation for the mismatch of the resistors: the models do not match.

    This line from the schematic:

    RR0 vdd! gnd! 10 $[RR1] $W=600n $L=6u M=1

    will be read as model "$[RR1]" while the layout extracts model "RR1".

    Is the "$[...]" notation some kind of standard and if so which? How do I read that?

    Same is true for "$W" which for KLayout's Spice reader is not the parameter "W".

    If I change the Spice line in the schematic to

    RR0 vdd! gnd! 10 RR1 W=600n L=6u M=1

    I'll get a match. Still, the custom compare approach will fail, because as a matter of fact there is a bug too in KLayout ( which means W and L is extracted 2 times too large by the default extractor. I'll fix that in the next release as well.

    The "puts" method in the comparer is just for debugging - it's not intended to present the match result. As explained in the post you're referring to, the problem is that W and L are considered "hidden parameters" by the Resistor device class and are not taken into account for matching nor are displayed.

    Hence, you do not see W and L in the browser, it's not netlisted and not compared. Right now, it's not possible to override the device class behaviour that disables W and L because KLayout's API does not provide the methods to customize device classes to this level. I'll see that I can fix that for the next release. Currently, only tedious workarounds exist (Comparer, netlist reader and writer delegates) and I'd rather not like to advertise these.

    The code that has evolved in the discussions before is actually not correct. I'll show the corrected code here (see "FIXED:" comments for the changes I applied):

    # Resistor extraction
    class ResistorExtractor < RBA::GenericDeviceExtractor
      def initialize(name, sheet_rho) = name
        @sheet_rho = sheet_rho
      def setup
        define_layer("C", "Conductor")
        define_layer("R", "Resistor")
      # FIXED: was missing
      def get_connectivity(layout, layers)
        # this "connectivity" forms the shape clusters that make up the device
        conn = RBA::Connectivity::new
        conn.connect(layers[0], layers[1])   # collect touching contacts
        conn.connect(layers[1], layers[1])   # combine resistor shapes into one area
      def extract_devices(layer_geometry)
        # layer_geometry provides the input layers in the order they are defined with "define_layer"
        conductor = layer_geometry[0]
        resistor  = layer_geometry[1]
        resistor_regions = resistor.merged
        resistor_regions.each do |r|
          terminals = conductor.interacting(resistor)
          if terminals.size != 2
            error("Resistor shape does not touch marker border in exactly two places", r)
            double_width = 0
            (terminals.edges & resistor.edges).merged.each do |e|
              double_width += e.length
            # A = L*W
            # -> L = A/W
            a = r.area*dbu*dbu
            w = (double_width / 2.0)*dbu
            l = a / w
            device = create_device
            device.set_parameter(RBA::DeviceClassResistor::PARAM_R, @sheet_rho * l / w);
            device.set_parameter(RBA::DeviceClassResistor::PARAM_A, a)
            device.set_parameter(RBA::DeviceClassResistor::PARAM_L, l)
            device.set_parameter(RBA::DeviceClassResistor::PARAM_P, 2*l+2*w)
            device.set_parameter(RBA::DeviceClassResistor::PARAM_W, w)
            define_terminal(device, RBA::DeviceClassResistor::TERMINAL_A, 0, terminals[0]);
            define_terminal(device, RBA::DeviceClassResistor::TERMINAL_B, 0, terminals[1]);
    class ResistorComparator < RBA::GenericDeviceParameterCompare
      def less(device_a, device_b)
          [RBA::DeviceClassResistor::PARAM_L, "L"],  
          [RBA::DeviceClassResistor::PARAM_W, "W"],
        ].each do |param_id, param|
          # FIXED: use a 0.01 nm tolerance at least
          result = device_a.parameter(param_id) < device_b.parameter(param_id) - 1e-5
          return true if result
        #puts "false" 
        return false
    # Resistor Definition
    # FIXED: use the new extractor
    extract_devices(ResistorExtractor::new("RR1", 1), 
      { "C" => metal1_not_res, "R" => res })

    With this code, the resistor's length and width are extracted correctly and the comparer will compare W and L rather than R. Still, W and L are not shown in the browser nor netlisted. As mentioned, the leaner way is for me to enable customization of device classes. That'll be much easier. I have tried to briefly summarize the requirements here:

    I'll keep you updated.


  • @Matthias
    The original netlist is in pspice format from cadence, there are "$" before variable or modell name.
    So I am also very interested in a another question, which format of netlist can klayout-netlist-reader read? Are there any dokumentation for it?

    Thanks for your answer and help, and looking forward to next release!

  • edited July 2021

    @Bian Unfortunately there are too many Spice flavours. I recall even Cadence wasn't consistent within their own tools.

    I took my knowledge from the ngspice user manual:

    Actually KLayout's netlist is an object and the API for building that netlist is exposed to Python or Ruby. This essentially enables building any kind of netlist reader in Python. So if one likes to implement CDL or HSPICE readers, please go ahead.

    BTW: KLayout internally uses it's own format to represent netlists, geometry-annotated netlists and cross-reference information (l2n and lvsdb databases).


  • @Bian
    Hi! I use the pspice format from cadence too. So I am interested in the same question as you.
    Could you please share how you deal with this problem if you have found the solution?

Sign In or Register to comment.