DRC issue

edited July 30 in Verification

Hi,
I am having an issue with DRC. I have already used all the projection commands over different metals to eliminate corner distances, but I am still getting errors. I have attached a screenshot of the issue.
My DRC code is like this:

# Define the number of threads to use
threads('all')

# Run DRC script in hierarchical mode
deep

# Define layers
layers = {
  "BG" => input(1, 0), # Base Gate layer
  "SD" => input(3, 0), # Source Drain layer
  "GM" => input(4, 0), # Gate Metal layer
  "PV1" => input(6, 0), # Via layer
  "GC1" => input(7, 0),  # Gate Contact layer
  "PV2" => input(9, 0), # Via layer
  "GC2" => input(10, 0),  # Gate Contact layer
}

# Define minimum width and space for metal layers
min_width = 3.5 # in microns
min_space = 2.5 # in microns

# Define minimum width and overlap for the via layer
min_via_width = 2.47 # in microns
min_via_space = 5 # in microns

# Define a helper method to check width and space violations for a given layer and output a message
def check_width_and_space(layer, message, min_width, min_space)
  # Use the drc method to perform a geometric operation on the layer
  # Use the width method to check the width of the projection of the layer
  # Use the output method to print a message if there are any violations
  layer.drc(width(projection) < min_width).output("ERROR_w: Width violations on #{message} layer")
  # Use the space method to check the space between the projection of the layer
  layer.drc(space(projection) < min_space).output("ERROR_s: Space violations on #{message} layer") 
end

# Check width and space violations for metal layers
# Use the each method to iterate over the layers hash
layers.each do |layer_name, layer|
  # Skip the via layer as it has different rules
  next if layer_name == "PV1" || layer_name == "PV2"
  # Call the helper method with the layer and its name
  check_width_and_space(layer, layer_name, min_width, min_space)
end

# Check width and space violations for via layer
# Call the helper method with the via layer and its name
check_width_and_space(layers["PV1"], "PV1", min_via_width, min_via_space)
check_width_and_space(layers["PV2"], "PV2", min_via_width, min_via_space)
# Check that GC overlap on PV must be at least 2.5 microns
# Assign the layer objects to variables for convenience
bg, sd, gm, pv1, gc1, pv2, gc2 = layers.values_at("BG", "SD", "GM", "PV1", "GC1", "PV2", "GC2")

# Define the enclosure value for the overlap
enc_val         = 2.49.um # in microns
# Use the sized method to create a new layer that is larger than the original layer by the enclosure value
ext_pv1         = pv1.sized(enc_val, enc_val)

# Use the split_inside method to divide the extended PV layer into two parts: one that is inside the GC layer and one that is not
(gc1_pv1_none, gc1_pv1_err) = ext_pv1.split_inside(gc1)

# Use the inside method to check if the non-overlapping part of the PV layer is inside the GM layer
gc1_pv1_gm      = gc1_pv1_none.inside(gm)
# Use the interacting and not_interacting methods to check if there are any errors in the overlap between the PV and GM layers
gc1_pv1_gm_err  = gc1_pv1_none.interacting(gm).not_interacting(gc1_pv1_gm)
# Use the not_inside method to remove the part of the PV layer that is inside the GM layer
gc1_pv1_none    = gc1_pv1_none.not_inside(gm)

# Repeat the same process for the SD layer
gc1_pv1_sd      = gc1_pv1_none.inside(sd)
gc1_pv1_sd_err  = gc1_pv1_none.interacting(sd).not_interacting(gc1_pv1_sd)
gc1_pv1_none    = gc1_pv1_none.not_inside(sd)

# Repeat the same process for the BG layer
gc1_pv1_bg      = gc1_pv1_none.inside(bg)
gc1_pv1_bg_err  = gc1_pv1_none.interacting(bg).not_interacting(gc1_pv1_bg)
gc1_pv1_none    = gc1_pv1_none.not_inside(bg)

# Use the sized method to create a new layer that is larger than the original layer by the enclosure value
ext_pv2         = pv2.sized(enc_val, enc_val)

# Use the split_inside method to divide the extended PV layer into two parts: one that is inside the GC layer and one that is not
(gc1_pv2_none, gc1_pv2_err) = ext_pv2.split_inside(gc1)

# Use the inside method to check if the non-overlapping part of the PV2 layer is inside the GC2 layer
gc1_pv2_gc2      = gc1_pv2_none.inside(gc2)
# Use the interacting and not_interacting methods to check if there are any errors in the overlap between the PV2 and GC2 layers
gc1_pv2_gc2_err  = gc1_pv2_none.interacting(gc2).not_interacting(gc1_pv2_gc2)
# Use the not_inside method to remove the part of the PV2 layer that is inside the GC2 layer
gc1_pv2_none    = gc1_pv2_none.not_inside(gc2)

# Use the sized method to create a new layer that is smaller than the original layer by the enclosure value
# Use the output method to print a message if there are any errors in the overlap between the PV and GC layers
(gc1_pv1_err                  ).sized(-enc_val, -enc_val).output("ERROR_gc1_pv1"    )
# Use the output method to print a message if there are any errors in the overlap between the PV and GM layers
(gc1_pv1_gm_err               ).sized(-enc_val, -enc_val).output("ERROR_gc1_pv1_gm" )
# Use the output method to print a message if there are any errors in the overlap between the PV and SD layers
(gc1_pv1_sd_err               ).sized(-enc_val, -enc_val).output("ERROR_gc1_pv1_sd" )
# Use the output method to print a message if there are any errors in the overlap between the PV and BG layers or if there are any parts of the PV layer that are not inside any of the other layers
(gc1_pv1_bg_err + gc1_pv1_none).sized(-enc_val, -enc_val).output("ERROR_gc1_pv1_bg" )
# Use the output method to print a message if there are any errors in the overlap between the PV2 and GC2 layers
(gc1_pv2_gc2_err              ).sized(-enc_val, -enc_val).output("ERROR_gc1_pv2_gc2")

Comments

  • Hi @ashkan,

    if I understand correctly, essentially you are using

    layer.drc(space(projection) < min_space).output("ERROR_s: Space violations on #{message} layer") 
    

    on the "GC1" layer.

    "projection" is not filtering the acute angles if this is what you want. Instead it is just a certain way to measure distances (see here: https://www.klayout.de/doc-qt5/about/drc_ref_layer.html#h2-3505).

    Acute angle filtering is difficult as this configuration is actually a severe space violation. If you really want to exclude this case, you can consider using an intra-polygon check ("isolation", see here: https://www.klayout.de/doc-qt5/about/drc_ref_layer.html#isolated). This will not check space violations between edges from the same polygon.

    Matthias

  • Hi @Matthias,
    The problem is that these are not the same polygon.
    These curve shapes are IN/OUT, but since we are using openroad, so these spaces are between the IN/OUT curve and a path.

  • But they are on the same layer, right? Otherwise "drc(space)" does not make sense.

    By "polygons" I mean "merged polygons". As soon as they touch, they become one polygon and the space becomes a notch case.

    If you want them to be treated separately, you have to have them on different layers. Is that the problem - that you cannot separate them?

    Matthias

  • Hi @Matthias,
    Thank you for your answer.
    So you are saying instead of something like this:
    layer.drc(space(projection) < min_space).output("ERROR_s: Space violations on #{message} layer")

    Put this:
    layers["GC1"].isolated(min_isolation_gc1).output("ERROR_isolated: Isolated polygons on GC1 layer")

    Which works but the problem is that the designs and connections are paths and not polygons.
    If I change the paths to Polygon manually, it will work. But I need to do it inside a process. (An OpenROAD flow)

  • Paths should be converted to polygons automatically. When I say "polygons" in DRC context that does not refer to the shape type - paths, boxes and polygons are "polygons" or can be converted to one. That is different for edges, edge pairs or texts which are lines, pairs of lines and (labelled) dots. These less-than-2-dimensional objects cannot be converted to polygons. Everything else can be converted and is so.

    You mean it only works when you turn paths into polygons? That is weird. It should not make any difference whether you use polygons or paths.

    Matthias

  • Yes, It only works when I change all the paths to polygons.
    I can send you the case, but I cannot upload the gds file since it has been limited on the comments.

  • edited August 31

    Hi @ashkan,

    you can zip a file to upload any kind of file here. If you don't want to make your layout public, you can send it to the support mail - contact "at" klayout.de. I will keep it private then.

    Regards,

    Matthias

  • Hi @Matthias,
    Thank you for your answer. I think the problem was because of my Klayout version? I don't know. But when I update the Klayout, it works.

  • Hi @ashkan,

    That must be a very odd version you have used then :) ... anyway, thanks for letting me know. Good to hear it works now.

    Kind regards,

    Matthias

Sign In or Register to comment.