NPN transistor extraction issue

Hi,
I'm writing a LVS script to check my layout and I want to see if someone can help with this issue.
I try to extract a vertical NPN transistor in the layout. The following is the structure of the NPN transistor

I'm using the following script to extract the NPN transistor

emitter = active_outside_nwell & nplus
base = active_outside_nwell & bury_n
collector = bury_n
collector_contact = bury_n & nwell
base_contact = base & pplus

npn_model_name = "NPN"
extract_devices(bjt3(npn_model_name), { "C" => collector, "B" => base, "E" => emitter,
"tC" => collector_contact, "tB" => base_contact, "tE" => emitter})

NPN connect ======================

connect(emitter, contact)
connect(collector_contact, contact)
connect(base_contact, contact)

metal connection

connect(contact, metal1)
connect(metal1, via1)
connect(via1, metal2)

But I can't extract the transistor and it always show me the error message as follows:

Please give me some advise to handle the above issue! Thanks!

Comments

  • Please format your code using Markdown: a line with three backticks before and after the code will format it

    like this
    

    Matthias

  • Your collector contact should be the stack of contact, Nplus, Nwell, buriedN. You do not have the N+ and contact in your logic. It's possible that contact to NWell could be a Schottky contact depending on surface doping unless you impose N+. Also (can't see) the connect chain from metal to the "contacts" should include the contact layer as a stepping-stone, how else can metal connect to the "contact regions" as-defined?

  • Thank you very much for all your support.
    Hi Matthias,
    I can't see the format look like. I use ~~~ before and after the code. Please help to take a look. Many thanks! :)

    Here is my code for NPN extraction


    # Read about LVS scripts in the User Manual in "Layout vs. Schematic (LVS)" # Enable hierarchical mode deep # Produce LVS report report_lvs # ------------------------------------------------------------------- # Layers # Drawing layers nwell = input(1, 0) #(TB) active = input(2, 0) #(TO) poly1 = input(3, 0) pplus = input(4, 0) #(P+) nplus = input(5, 0) #(N+) poly2 = input(14, 0) contact = input(6, 0) metal1 = input(7, 0) # includes labels via1 = input(93, 0) metal2 = input(86, 0) # includes labels bury_n = input(20, 0) # Bulk layer for terminal provisioning bulk = polygon_layer # Computed layers active_in_nwell = active & nwell active_outside_nwell = active - nwell emitter = active_outside_nwell & nplus base = active_outside_nwell & bury_n collector = bury_n collector_contact = bury_n & nwell base_contact = base & pplus emitter.output(181, 0) base_contact.output(182, 0) base.output(185, 0) collector_contact.output(183, 0) collector.output(184, 0) #Device Extaction npn_model_name = "NPN" extract_devices(bjt3(npn_model_name), { "C" => collector, "B" => base, "E" => emitter, "tC" => collector_contact, "tB" => base_contact, "tE" => emitter}) #Connection Define #NPN connect ====================== connect(emitter, contact) connect(collector_contact, nwell) connect(nwell, nplus) connect(nplus, contact) connect(base_contact, contact) ###metal connection connect(contact, metal1) connect(metal1, via1) connect(via1, metal2) # ------------------------------------------------------------------- # Netlist and compare # Netlist normalization netlist.simplify target_netlist("layout_simply.cir", write_spice, "Extracted by KLayout") # Hierarchy alignment (flatten out unmatched cells) align # Netlist vs. netlist compare
  • Hi dick_freebird,
    Thanks for your information. I have tried different connection methods. But it is fail to extract the NPN device also. Could you please take a look of the above coding and give me more advises. Thanks!

    Terry

  • @eeterry11 I think you should think simple. Without a sample, it's quite difficult to debug, but I'd propose to change the collector connection to simply:

    # instead of:
    connect(emitter, contact)
    connect(collector_contact, nwell)
    connect(nwell, nplus)
    connect(nplus, contact)
    connect(base_contact, contact)
    
    # use
    connect(emitter, contact)
    connect(collector_contact, contact)
    connect(base_contact, contact)
    

    This should eventually give you the terminals. You have correctly identified non-overlapping regions fro collector_contact and base_contact, so these three connects should give your the three terminals separately.

    But as mentioned, an example layout will be helpful.

    Matthias

  • @Matthias the following link can download the layout gds file. Please help to take a look! Thank you very much!
    https://www.dropbox.com/s/9dputxuhrlwbdm6/P200241_sample_layout_test.gds?dl=0

    Terry

  • Can you somehow display each of those logical
    layers that you are outputting, and see whether
    any of them are not giving you what you think?

    Maybe one is "malformed" and breaking the
    device extraction?

  • edited October 2021

    Another thought - I see the extraction of
    connectivity, but isn't there some need to
    "recognize" the device as such? I don't see
    logic for that (what makes the NPN an
    NPN, to the extraction?).

    In more elaborate kits and tools I'm used
    to seeing both type-extraction and
    connectivity-extraction to that type's
    terminals. Of course I could easily be
    confused.

  • @dick_freebird I did output the logical layers and it is exactly what I want! The following code output the logical layer

    # Computed layers
    active_in_nwell       = active & nwell 
    active_outside_nwell  = active - nwell
    
    
    emitter = active_outside_nwell & nplus
    base = active_outside_nwell & bury_n
    collector = bury_n 
    collector_contact = bury_n & nwell 
    base_contact = base & pplus
    
    emitter.output(181, 0)
    base_contact.output(182, 0)
    base.output(185, 0)
    collector_contact.output(183, 0)
    collector.output(184, 0)
    

    But now, I don't know how to create a contact to the collector , base and emitter which the extractor can recognize the device! Thanks!
    Terry

  • edited October 2021

    Hi @eeterry11,

    I'm afraid there seems to be a bug related to collector region. Looks like my test base is too weak on this spot.

    I need to debug the problem.

    But there is a workaround. I think it's not required to include the collector into the device recognition as base and emitter should be specific enough.

    "collector" is optional, so you can replace it by an empty "polygon_layer". This does not give you that errors.

    However, the "collector" terminal then will not be the collector region but the base region only. You can make the connection to "collector_contact" via the original collector region though:

    # NOTE: "C" is empty, "tC" puts the terminal on the original collector region
    extract_devices(bjt3(npn_model_name), { "C" => polygon_layer, "B" => base, "E" => emitter, 
                                            "tC" => collector, "tB" => base_contact, "tE" => emitter})
    
    #Connection Define
    #NPN connect ======================
    connect(emitter, contact)
    # NOTE: collector connects to collector_contact outside base
    connect(collector_contact, collector)
    connect(collector_contact, nwell)
    connect(nwell, nplus)
    connect(nplus, contact)
    connect(base_contact, contact)
    

    I have created an issue for this with an artificial test case: https://github.com/KLayout/klayout/issues/921

    Matthias

  • Hi @Matthias,

    I have tried to replace the collector to an "empty" polygon_layer, but the error is here. The following is my updated code. Could you please help to take a look. Do I misunderstanding your meaning? Thank your very much !!


    # Read about LVS scripts in the User Manual in "Layout vs. Schematic (LVS)" # Enable hierarchical mode deep # Produce LVS report report_lvs # ------------------------------------------------------------------- # Layers # Drawing layers nwell = input(1, 0) #(TB) active = input(2, 0) #(TO) poly1 = input(3, 0) pplus = input(4, 0) #(P+) nplus = input(5, 0) #(N+) poly2 = input(14, 0) contact = input(6, 0) metal1 = input(7, 0) # includes labels via1 = input(93, 0) metal2 = input(86, 0) # includes labels bury_n = input(20, 0) # Bulk layer for terminal provisioning bulk = polygon_layer bulk.output(198, 0) # Computed layers active_in_nwell = active & nwell active_outside_nwell = active - nwell emitter = active_outside_nwell & nplus base = active_outside_nwell & bury_n collector = bury_n collector_contact = bury_n & nwell base_contact = base & pplus emitter.output(181, 0) base_contact.output(182, 0) base.output(185, 0) collector_contact.output(183, 0) collector.output(184, 0) npn_model_name = "NPN" # NOTE: "C" is empty, "tC" puts the terminal on the original collector region extract_devices(bjt3(npn_model_name), { "C" => polygon_layer, "B" => base, "E" => emitter, "tC" => collector, "tB" => base_contact, "tE" => emitter}) #Connection Define #NPN connect ====================== connect(emitter, contact) # NOTE: collector connects to collector_contact outside base connect(collector_contact, collector) connect(collector_contact, nwell) connect(nwell, nplus) connect(nplus, contact) connect(base_contact, contact) ###metal connection connect(contact, metal1) connect(metal1, via1) connect(via1, metal2) # ------------------------------------------------------------------- # Netlist and compare # Netlist normalization netlist.simplify target_netlist("layout_simply.cir", write_spice, "Extracted by KLayout") # Hierarchy alignment (flatten out unmatched cells) align # Netlist vs. netlist compare
  • It still bothers me that there is no logic to recognize the device itself. The terminals, OK. But terminals of what? I see where you assign the model name "arbitrarily".

    Maybe it's just that I'm used to working in bipolar technologies where there were PCells for a variety of emitter, base, collector combinations - from simple single (1B 1E 1C) through permutations of dual base, dual emitter, ring collector, etc. - 6 each of NPN and PNP in one flow I used a lot. Distinguishing the geometries to point the extraction at the appropriate model, was the first step of the logic, to derive that modelName.

    Perhaps in this case there's only one style and only one model so no thinking is required. But maybe that logic would have yielded a finite collector region, or the terms for creating it logically?

  • edited October 2021

    @eeterry11 I tried the code of yours with the sample layout and it works for my 0.27.3 version - until it cannot read the netlist. But no error :(

    BTW: I have fixed the issue and the extraction script you proposed initially should work in 0.27.5. Sorry for the confusion.

  • edited October 2021

    @dick_freebird Well, there is some logic, but it's built-in.

    It's basically possible to code a device extractor, but that's slightly tedious, so some standard device extractors are provided. The "bjt3" device extractor works this way:

    • Consider all shapes (=merged regions) on the "B" layer. These are the "seeds", or "device recognition shapes"
    • Skip all base regions which do not interact with polygons on the "E" layer. So the presence of "E" inside or overlapping with "B" is another device recognition feature.
    • Find any regions on the "C" layer overlapping with "B"
    • If "C" overlaps "B" regions entirely, a vertical BJT is assumed. Compute the collector area "tC" as the "C" region minus "B" (typically a donut).
    • If no "C" is overlapping "B", assume the base area forms the collector area "tC" - in this case, it is assume that the substrate forms the collector
    • If "C" partially overlaps "B", a lateral BJT is assumed. Compute the base area "tB" as "B" minus "C" (a donut) and "tC" as the part of "C" overlapping "B"
    • (if this is weird, take a look at the cross sections here: https://www.klayout.de/doc-qt5/manual/lvs_device_extractors.html#h2-213)
    • Finally punch out "E" polygons from "tB" and "tC" and use the "E" polygons as emitter area "tE".
    • Generate a new device of the given class
    • Place the "tC" regions (forming the connectable collector areas) on the layer specified with "tC" and connect them with the new devices "C" terminal.
    • Do the same with "tB" and "tE" regions for base and emitter respectively.
    • (I skipped the measurements here)
    • (the whole code isn't secret, you can read that here: https://github.com/KLayout/klayout/blob/master/src/db/db/dbNetlistDeviceExtractorClasses.cc, line 665++)
    • (a custom extractor basically does the same, but is typically implemented in Ruby inside the LVS script)

    Bottom line is: the device recognition happens through the base region mainly. So if you want to differentiate different device types, you first have to select the specific base regions - by means of texts, marker layers, geometry filters or similar. With these base layers - one per device - you go into different extract_devices statements with different classes for the different device types.

    I hope this makes some sense.

    Best regards,

    Matthias

  • edited October 2021

    @ Matthias Thank you very much! It doesn't work if there are others devices. I added a marker layer. I can extract the NPN device successfully!

  • I see. Thank you for this feedback.

    This basically means you have other places which are incorrectly recognized as NPN bases (with fake "emitters"). I hope the previous explanation helped with that - a marker layer is definitely helpful as a disambiguator.

    Kind regards,

    Matthias

  • I personally favor an all-markers approach where every
    device that's placed intentionally, incorporates a unique
    marker in its PCell which the extract deck uses to make
    the "recognition call" (backed up by a "recognizer" for
    device structures containing no such marker, to flag
    as error / risk).

    eeterry11, I'd like to see your example of marker layer
    use, that worked, as it's where my plans headed.

Sign In or Register to comment.