Detecting Swapped Pins

I'm debugging a issue with LVS results where I am seeing an undesired behavior in the LVS compare when the topology matches, but the pin names are incorrect or swapped. I want to detect this case where pins are incorrectly labeled, either as a LVS compare failure, or as some other property that can be accessed during the LVS script execution. I'm at a loss as to where to look for this information and I haven't intentionally enabled swapping in my LVS deck. My KLayout version is a self-built 0.27.11 in case that matters. I can recreate this issue using the Github LVS sample on the NAND gate "ND2X1" layout by taking the following steps:

  1. (at the shell) cp RINGO.cir ND2X1.cir
  2. klayout ringo.gds
  3. Selecting cell ND2X1, "Show As New Top"
  4. Swap locations of the text label "A" with "B" (as shown below)

  1. Run the si4all.lvs script
  2. View the results, expanding the "Pins" section.

The LVS result clearly shows the pins as swapped. How can I make this case fail, or detect swapped pins in the result?


  • edited May 11

    Hi @barlow,

    The LVS is basically name agnostic and does a topological match. It finds - correctly - that the topology matches. What it also finds is, that what is called "A" in the schematic is called "B" in the layout and vice versa.

    By default, LVS does not require name identity of the pins. It will only use names for hints in case of ambiguities (symmetry).

    You can require net names to match using "same_nets!", i.e.

    same_nets!("ND2X1", "A")
    same_nets!("ND2X1", "B")

    or for all (named) nets:

    same_nets!("ND2X1", "*")

    You can also request name matching on all circuits and all (named) nets by using

    same_nets!("*", "*")


  • Thank you for your response!

    I understand topologically everything is correct, but the problem that I run into is when I'm integrating these cells as subcells in a larger schematic/layout. In those cases, my "A" and "B" pins being swapped on a NAND gate will result in an error at the higher level, as then the topology will be wrong. While I haven't tried troubleshooting this type of error in KLayout, I would prefer to be proactive and ensure that the pin names in the layout match the pin names in the schematic.

    I didn't realize the "same_nets!" command could operate on wildcards, that's great to know. However, when I insert the "same_nets!" command on line 113 of the si4all.lvs script I get an error:

    I get the exact same error no matter where in the script I place the command, or if I use the actual cell and pin names. I've included where it is in my version below copied directly. Do you have any ideas on what I might have done wrong?

    # Global connections
    connect_global(ptie, "BULK")
    connect_global(bulk, "BULK")
    same_nets!("*", "*")
    # Actually performs the extraction
  • Two problems (or more) I think.

    First the NAND gate needs to be botom-up correct
    all by itself. Seems that's not the case (initially at least).

    Then if higher level assemblies mis-follow the
    connectivity and call a pin-swap, that must then be
    another class of problem. But NAND has a topological
    difference between A and B legs that should be clean
    to follow.

    I might suggest that LVS debug could always use
    some "assistance". In this case I believe that terminal
    mismatch should be clearly presented in the "top level
    error summary report" along with other classes of
    mismatch and with whatever clues can be delivered
    about what's the nature of the beef.

  • To be clear, the provided design on GitHub is correct as provided. I am changing the labels in the layout to help demonstrate the issue that I'm having, where a bottom-up verification scheme "fails" because the labels aren't treated as a rigid constraint.

    From a verification perspective, I would be happy if I got an error whenever the number of pins in SPICE don't match the labels in GDS, if the pin names aren't identical, and if the circuit topology (independent of labeling) matches but the labels don't match.

  • edited May 14

    Curious whether the klayout LVS understands pin-permute
    and has support for permute rules for pins (which ones
    can swap - like regular MOS would always have
    permuteParallel rule for S and D, but an asymmetric
    (drain-extended / annular / LDMOS) would not allow S/D

    In the NAND2 (and other flat combo logic) case, differences
    in path delay & threshold might be considered "trivial" per
    the library developers and allowed to swap those specific pins.

    Might look to that, in the ND2X1 contexts (instance and
    rules) to see if the swap is flagged for hookup at the low
    level (by actual layout false swapping) but then "blessed"
    by this kind of "overlay" rule that "fixes" legit swaps.

  • Hi @barlow,

    I am afraid 0.27.11 is far too old. Do you have a chance to try on a later version?


  • And regarding the the question of @dick_freebird:

    As you say, a CMOS gate is not symmetric, but if you wire the pins correctly on upper levels, the topology matches and the labels do not matter.

    But it is possible to treat the inputs symmetric and enable swapping to allow more freedom in the wiring. To do you, you have to declare pins equivalent for specific gates where you want to allow this.

    The feature is described here: The keyword is "equivalent_pins".


  • @Matthias I did some playing around with versions to see if it might have been something we did with our build process, and I tested several versions. I think I already tested more than I needed, and I'd probably be better served at this point to dig into the source repository if it was really necessary.

    • Self-built 0.27.11 - fail
    • KLayout 0.27.13 - fail
    • KLayout 0.28.1 - fail
    • KLayout 0.28.10 -PASS
    • KLayout 0.28.17 - PASS
    • KLayout 0.29.1 - PASS

    I think (for me) upgrading is probably the simplest path for getting this to work. Thanks for your help.

  • @Matthias So in trying out the same_nets!("*", "*") function, I have run into a new issue - now all nets must have names matching in both the schematic and layout. I suppose that result is not too surprising considering the wildcards. The desired behavior is to allow any unnamed layout net to match any net in the schematic, while requiring the (named) ports to match.

    The NAND2 gate still works as an example, as there is an unnamed node in the series NFET chain. In the SPICE netlist, this is given as node 1. I added the well/substrate tie to the layout to prevent spurious warnings to the layout:

    M$1 OUT A VDD VDD LVPMOS L=0.25U W=1.5U
    M$2 VDD B OUT VDD LVPMOS L=0.25U W=1.5U
    M$3 VSS A 1 VSS LVNMOS L=0.25U W=0.95U
    M$4 1 B OUT VSS LVNMOS L=0.25U W=0.95U
    .ENDS ND2X1

    The match fails on comparing layout net $I3 to schematic net 1 presumably due to the net name issue. For reference, without the same_nets!("*", "*") function I get a passing result.

    I tried same_nets!("*", "$*", "*", "*") in the hopes that I could form a wildcard net filter that matches all nets that are unlabeled, but this no longer catches incorrect pins. I think either the automatic layout nets that start with a "$" always can be addressed by that name, or I can't run the command after labels have been applied. I'm trying to make this generic to any circuit, but I don't see how to grab the top-level pin names from either the schematic or layout to make the "*" more specific. Any advice on how to proceed?

    I'm running version 0.29.1 now for reference.

  • A brief addendum, I tried seeing if I could iterate through the schematic nets to mark which nets can match "anything" and those that can't. Using the nets_by_name("*") and Net.is_internal? I can iterate through the netlist and differentiate between internal and external nodes. I think I've got a lead on the matching with the following code blurb:

    all_sch_nets = schematic.nets_by_name("*")
    all_sch_nets.each { |x|
      if x.is_internal? 
        same_nets(source.cell_name, "*",

    I have yet to try this on a hierarchal design, but this works on my NAND2 example

  • edited May 23

    Hi @barlow,

    Yes, your solution makes sense.

    However, I just checked my code. I though that * would match named nets only, but that is not strictly the case. It matches the "given" name which is empty in case of unnamed nets. As * also matches empty strings, it will also apply to unnamed nets.

    So I think the solution is simply to use

    same_nets!("*", "?*")

    Then, the match requirement should apply to named nets only.

    I think I can change the behavior internally. Selecting unnamed nets by name does not really make sense.


  • I stand corrected. The solution above isn't working right now as it will either select schematic nets or layout nets - the schematic nets are always named, hence "?*" will still select all nets.

    I have opened a ticket ( to fix the behavior of same_nets!("*", "*") together with other topics.

    Thanks for bringing up this issue.


  • That is an insidious edge case for the wildcards.

    I tried out the same_nets!("*", "?*") bit. I found out that this works roughly as expected, but fails when the schematic net name has only 1 character. In practice, this might not be a big deal but false positives are such a pain to work through that I think I won't use that right now.

    I need to amend my previous segment, I found that the Nets.is_internal? query returns true only if there are no connected pins and the number of terminals is exactly two, which I didn't notice. Going back through the iterate method, I've adjusted the "Check if internal" query to be simply Nets.pin_count == 0.

    all_sch_nets = schematic.nets_by_name("*")
    all_sch_nets.each { |x|
      if x.pin_count == 0 
        # Internal net - no pins
        same_nets(source.cell_name, "*",
        # Named Pin - external net
  • edited May 28


    yes, you're right .. "internal" means it is connecting two device terminals and nothing else. If you wonder what this is good for: internal nets connect devices in a serial fashion, so they can be combined (i.e. two resistors).

    I hope, the fix for will provide a more reasonable and intuitive behavior more compatible with other (and non-agnostic) tools.


Sign In or Register to comment.