KLayout / klayout

KLayout Main Sources
http://www.klayout.org
GNU General Public License v3.0
745 stars 192 forks source link

Deep Vs. Flat for LVS Test #1715

Closed FaragElsayed2 closed 1 month ago

FaragElsayed2 commented 1 month ago

Deep vs. Flat Mode for LVS Test

Setup

Issue Description

We are encountering an issue with passing LVS checks in deep mode for tests involving nFETs and pFETs. Below are the details of the layout and netlist, along with the results from both flat and deep runs.

Layout

Layout Image

Netlist

* Gates Test

.subckt gates_test_simple
X1 in1 VDD VSS inv_and sg13g2_inv_2
X2 inv_and in2 VDD VSS out sg13g2_and2_1
.ends

.SUBCKT sg13g2_inv_2 A VDD VSS Y
*.PININFO A:I Y:O VDD:B VSS:B
MX1 Y A VSS VSS sg13_lv_nmos m=2 w=740.00n l=130.00n ng=1
MX0 Y A VDD VDD sg13_lv_pmos m=2 w=1.12u l=130.00n ng=1
.ENDS

.SUBCKT sg13g2_and2_1 A B VDD VSS X
*.PININFO A:I B:I X:O VDD:B VSS:B
MX0 net4 A net2 VSS sg13_lv_nmos m=1 w=640.00n l=130.00n ng=1
MX2 X net4 VSS VSS sg13_lv_nmos m=1 w=740.00n l=130.00n ng=1
MX3 net2 B VSS VSS sg13_lv_nmos m=1 w=640.00n l=130.00n ng=1
MX1 net4 B VDD VDD sg13_lv_pmos m=1 w=840.00n l=130.00n ng=1
MX4 VDD net4 X VDD sg13_lv_pmos m=1 w=1.12e-06 l=130.00n ng=1
MX5 net4 A VDD VDD sg13_lv_pmos m=1 w=840.00n l=130.00n ng=1
.ENDS

Flat Run

Command:

python3 run_lvs.py --layout=test_deep_flat_gates/gates_test_simple.gds --netlist=test_deep_flat_gates/gates_test_simple.cdl --run_mode=flat --run_dir=flat_test

image

Deep Run

Command:

python3 run_lvs.py --layout=test_deep_flat_gates/gates_test_simple.gds --netlist=test_deep_flat_gates/gates_test_simple.cdl --run_mode=deep --run_dir=deep_test

image

Additional Notes

We have ensured that all connections are added at the top level to establish connections to the sub-cells. Despite this, the LVS check fails in deep mode but passes in flat mode.

Steps to Reproduce

  1. Set up the environment with KLayout version 0.29.0 and the IHP-130nm technology.
  2. Use the provided layout and netlist in Attachments.
  3. Run the LVS check in flat/deep mode using the command provided above and open the results.

Expected Behavior

The LVS check should pass in both flat and deep modes.

Actual Behavior

The LVS check passes in flat mode but fails in deep mode.

Attachments

We would appreciate any insights or suggestions to resolve this issue. Thank you!

FaragElsayed2 commented 1 month ago

Cc @atorkmabrains

atorkmabrains commented 1 month ago

@klayoutmatthias @KrzysztofHerman

klayoutmatthias commented 1 month ago

Hi @FaragElsayed2,

thanks for this nicely prepared test case.

The problem is the following

The body connections are made to "pwell" which is a flat top-level layer, because it is derived from the chip boundary. The way it is defined now, it acts like a flat top-level conductor layer over all gate cells. This creates pins from the device body terminals as you can see here:

image

Usually, you would use a global connection for this purpose (see https://www.klayout.de/doc-qt5/manual/lvs_connect.html#h2-80). Instead of computing pwell as anything "not nwell" you can include "not well" in the nmos recognition booleans, so you do not need to worry about nwell and can simply connect their bulks to the respective global net.

If you really need to use a pcell layer of some kind, you should try to derive it in some hierarchical way. Remember, that the first operand of a boolean operation determines where the results end up in the hierarchy. So if you do

not_nwell = CHIP.not(nwell_drw.join(pwell_block).join(digisub_drw)))

the results will be flat, because CHIP is a single flat rectangle. But if you use

cell_boundary = input(189, 4)   # I am guessing
not_nwell = cell_boundary.not(nwell_drw.join(pwell_block).join(digisub_drw)))

the result will be hierarchical as the cell boundary is drawn in every cell. BTW: with this patch, the LVS passes in my case :)

You can basically also replace "CHIP" by

extent("*")

which places bounding boxes into every cell. This creates a hierarchical layer, but also creates a lot of overlaps. I think this will add a performance penalty.

Matthias

atorkmabrains commented 1 month ago

@klayoutmatthias We have adopted the following change:

extent("*")

Thanks for all the help.

FaragElsayed2 commented 1 month ago

@klayoutmatthias Thanks for your help.

I have used this derivation:

CHIP =   extent('*')

This derivation passes LVS when using the pwell as the bulk. However, when I tested it with the digisub layer at the top level, which is a marker layer used to isolate the substrate and acts as a separate net, the LVS fails. It only passes if I add this digisub layer in the subcells as well. I believe this issue arises because the substrate net differs between the top cell and the subcells.

image

Below is the derivation I used for the substrate:

# === CHIP ===
CHIP = case $run_mode
when 'deep'
  extent('*')
else
  #=== FLAT MODE ===
  extent.sized(0.0)
end

# === General Derivations ===
# pwell
pwell_pre_init = CHIP.not(pwell_block).not(digisub_drw)
pwell_pre = pwell_pre_init.not(nwell_drw)
digisub_pre = digisub_drw.sized(-1.nm).not(nwell_drw).not(pwell_block)
pwell = pwell_pre.join(digisub_pre)
atorkmabrains commented 1 month ago

@klayoutmatthias Please keep in mind it's a valid use case to have digisub as single polygon on a large array of standard cells for example.

Happy to discuss.

klayoutmatthias commented 1 month ago

Hi @FaragElsayed2,

well, by adding a flat shape on digisub_drw ("adding" is what "join" does), you have re-introduced the problem we just solved with extent("*").

One solution is to rewrite the pwell expression:

pwell_pre_init = CHIP.not(pwell_block).not(digisub_drw)
pwell_pre = pwell_pre_init.not(nwell_drw)
digisub_pre = digisub_drw.sized(-1.nm).not(nwell_drw).not(pwell_block)
pwell = pwell_pre.join(digisub_pre)

into

pwell_allowed = CHIP.not(pwell_block)
pwell_pre_init = pwell_allowed.not(digisub_drw) # not needed here, but somewhere else?

digisub_gap = digisub_drw.not(digisub_drw.sized(-1.nm))
pwell = pwell_allowed.not(nwell_drw).not(digisub_gap)

This way, we maintain the hierarchical nature of CHIP as this layer forms the first operand of the boolean "not" chain.

Some hints in general:

Bottom-up completeness

KLayout's LVS compare and LVS netlist extraction is bottom-up and there needs to be a strict correspondence between layout and schematic cells. This means that a cell needs to be complete in itself - including all marker layers and potential connectivity layers. If that is not given in terms of drawing layers, you need to utilize the behavior of the deep mode booleans to make sure this condition is fulfilled.

A practical hint: you can output debug layers in you LVS script like in DRC, for example using:

target("debug.gds")

# write pwell to 100/0 of "debug.gds"
pwell.output(100, 0)

this may help you understanding how the deep mode functions work.

No marker variants

KLayout does not create marker layer variants for cells. This means, you cannot simply switch one gate's transistors to high-Vt type by drawing some high-Vt marker over this gate. I don't think, this makes sense as models would change etc. But if you really want to do that, you will need to create a copy of the cell and put the marker layer into that cell. In the schematic, this copy would use high-Vt models while the original subcircuit would not.

The same is true for example, if you draw "digisub" over the half of a cell while you do not do so for other instances. This will not create a cell variant with two substrate pins. The solution is again to copy the cell and create a variant with a specific "digisub" shape inside.

Matthias

FaragElsayed2 commented 1 month ago

Perfect @klayoutmatthias

Thank you for your time and assistance. I now understand how to maintain the derived layer hierarchically.