diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 7c66997fa..3faf281bc 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -1355,3 +1355,245 @@ TEST(5_ResAndCapWithBulkExtraction) db::compare_layouts (_this, ly, au); } +TEST(6_BipolarTransistorExtraction) +{ + db::Layout ly; + db::LayerMap lmap; + + unsigned int nwell = define_layer (ly, lmap, 1); + unsigned int active = define_layer (ly, lmap, 2); + unsigned int poly = define_layer (ly, lmap, 3); + unsigned int poly_lbl = define_layer (ly, lmap, 3, 1); + unsigned int diff_cont = define_layer (ly, lmap, 4); + unsigned int poly_cont = define_layer (ly, lmap, 5); + unsigned int metal1 = define_layer (ly, lmap, 6); + unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1); + unsigned int via1 = define_layer (ly, lmap, 7); + unsigned int metal2 = define_layer (ly, lmap, 8); + unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1); + unsigned int pplus = define_layer (ly, lmap, 9); + unsigned int nplus = define_layer (ly, lmap, 10); + + { + db::LoadLayoutOptions options; + options.get_options ().layer_map = lmap; + options.get_options ().create_other_layers = false; + + std::string fn (tl::testsrc ()); + fn = tl::combine_path (fn, "testdata"); + fn = tl::combine_path (fn, "algo"); + fn = tl::combine_path (fn, "bipolar_devices_test.oas"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + + db::DeepShapeStore dss; + dss.set_text_enlargement (1); + dss.set_text_property_name (tl::Variant ("LABEL")); + + // original layers + db::Region rnwell (db::RecursiveShapeIterator (ly, tc, nwell), dss); + db::Region ractive (db::RecursiveShapeIterator (ly, tc, active), dss); + db::Region rpoly (db::RecursiveShapeIterator (ly, tc, poly), dss); + db::Region rpoly_lbl (db::RecursiveShapeIterator (ly, tc, poly_lbl), dss); + db::Region rdiff_cont (db::RecursiveShapeIterator (ly, tc, diff_cont), dss); + db::Region rpoly_cont (db::RecursiveShapeIterator (ly, tc, poly_cont), dss); + db::Region rmetal1 (db::RecursiveShapeIterator (ly, tc, metal1), dss); + db::Region rmetal1_lbl (db::RecursiveShapeIterator (ly, tc, metal1_lbl), dss); + db::Region rvia1 (db::RecursiveShapeIterator (ly, tc, via1), dss); + db::Region rmetal2 (db::RecursiveShapeIterator (ly, tc, metal2), dss); + db::Region rmetal2_lbl (db::RecursiveShapeIterator (ly, tc, metal2_lbl), dss); + db::Region rpplus (db::RecursiveShapeIterator (ly, tc, pplus), dss); + db::Region rnplus (db::RecursiveShapeIterator (ly, tc, nplus), dss); + db::Region rbulk (new db::DeepRegion (dss.empty_layer ())); + + // derived regions + + db::Region rpactive = ractive & rnwell; + db::Region rbase = rpactive.selected_not_interacting (rpoly).selected_interacting (rpplus); + db::Region rpactive_mos = rpactive - rbase; + + db::Region rpgate = rpactive_mos & rpoly; + db::Region rpsd = rpactive_mos - rpgate; + + db::Region rnactive = ractive - rnwell; + db::Region rngate = rnactive & rpoly; + db::Region rnsd = rnactive - rngate; + + db::Region rntie = rnwell & rnplus; + db::Region remitter = rpplus & rbase; + + // return the computed layers into the original layout and write it for debugging purposes + + unsigned int lgate = ly.insert_layer (db::LayerProperties (10, 0)); // 10/0 -> Gate + unsigned int lsd = ly.insert_layer (db::LayerProperties (11, 0)); // 11/0 -> Source/Drain + unsigned int lpdiff = ly.insert_layer (db::LayerProperties (12, 0)); // 12/0 -> P Diffusion + unsigned int lndiff = ly.insert_layer (db::LayerProperties (13, 0)); // 13/0 -> N Diffusion + unsigned int lbase = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Base + unsigned int lemitter = ly.insert_layer (db::LayerProperties (15, 0)); // 15/0 -> Base + unsigned int lntie = ly.insert_layer (db::LayerProperties (16, 0)); // 16/0 -> N Tiedown + + rpgate.insert_into (&ly, tc.cell_index (), lgate); + rngate.insert_into (&ly, tc.cell_index (), lgate); + rpsd.insert_into (&ly, tc.cell_index (), lsd); + rnsd.insert_into (&ly, tc.cell_index (), lsd); + rpsd.insert_into (&ly, tc.cell_index (), lpdiff); + rnsd.insert_into (&ly, tc.cell_index (), lndiff); + rbase.insert_into (&ly, tc.cell_index (), lbase); + remitter.insert_into (&ly, tc.cell_index (), lemitter); + rntie.insert_into (&ly, tc.cell_index (), lntie); + + // perform the extraction + + db::Netlist nl; + db::hier_clusters cl; + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + db::NetlistDeviceExtractorBipolarTransistor bjt_ex ("PNP"); + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["W"] = &rnwell; + // terminal patches + dl["tG"] = &rpoly; + dl["tS"] = &rpsd; + dl["tD"] = &rpsd; + pmos_ex.extract (dss, 0, dl, nl, cl); + + dl.clear (); + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["W"] = &rbulk; + // terminal patches + dl["tG"] = &rpoly; + dl["tS"] = &rnsd; + dl["tD"] = &rnsd; + nmos_ex.extract (dss, 0, dl, nl, cl); + + dl.clear (); + dl["E"] = &remitter; + dl["B"] = &rbase; + dl["C"] = &rbulk; + // terminal patches + dl["tB"] = &rnwell; + bjt_ex.extract (dss, 0, dl, nl, cl); + + + // perform the net extraction + + db::NetlistExtractor net_ex; + + db::Connectivity conn; + // Intra-layer + conn.connect (rnwell); + conn.connect (rpsd); + conn.connect (rnsd); + conn.connect (rbase); + conn.connect (remitter); + conn.connect (rntie); + conn.connect (rpoly); + conn.connect (rdiff_cont); + conn.connect (rpoly_cont); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); + // Inter-layer + conn.connect (rntie, rnwell); + conn.connect (rntie, rdiff_cont); + conn.connect (remitter, rdiff_cont); + conn.connect (rpsd, rdiff_cont); + conn.connect (rnsd, rdiff_cont); + conn.connect (rpoly, rpoly_cont); + conn.connect (rpoly_cont, rmetal1); + conn.connect (rdiff_cont, rmetal1); + conn.connect (rmetal1, rvia1); + conn.connect (rvia1, rmetal2); + conn.connect (rpoly, rpoly_lbl); // attaches labels + conn.connect (rmetal1, rmetal1_lbl); // attaches labels + conn.connect (rmetal2, rmetal2_lbl); // attaches labels + // Global nets + conn.connect_global (rbulk, "BULK"); + + // extract the nets + + net_ex.extract_nets (dss, 0, conn, nl, cl, "*"); + + // Flatten device circuits + + std::vector circuits_to_flatten; + circuits_to_flatten.push_back ("TRANS"); + circuits_to_flatten.push_back ("TRANS2"); + + for (std::vector::const_iterator i = circuits_to_flatten.begin (); i != circuits_to_flatten.end (); ++i) { + db::Circuit *c = nl.circuit_by_name (*i); + tl_assert (c != 0); + nl.flatten_circuit (c); + } + + // cleanup + completion + nl.combine_devices (); + nl.make_top_level_pins (); + nl.purge (); + + EXPECT_EQ (all_net_names_unique (nl), true); + + // debug layers produced for nets + // 201/0 -> n well + // 203/0 -> Poly + // 204/0 -> Diffusion contacts + // 205/0 -> Poly contacts + // 206/0 -> Metal1 + // 207/0 -> Via1 + // 208/0 -> Metal2 + // 210/0 -> N source/drain + // 211/0 -> P source/drain + // 212/0 -> Emitter + // 213/0 -> N tiedown + std::map dump_map; + dump_map [layer_of (rpsd) ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [layer_of (rnsd) ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [layer_of (remitter) ] = ly.insert_layer (db::LayerProperties (212, 0)); + dump_map [layer_of (rntie) ] = ly.insert_layer (db::LayerProperties (213, 0)); + dump_map [layer_of (rnwell) ] = ly.insert_layer (db::LayerProperties (201, 0)); + dump_map [layer_of (rpoly) ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [layer_of (rdiff_cont)] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [layer_of (rpoly_cont)] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [layer_of (rmetal1) ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [layer_of (rvia1) ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [layer_of (rmetal2) ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = dss.cell_mapping_to_original (0, &ly, tc.cell_index ()); + dump_nets_to_layout (nl, cl, ly, dump_map, cm, true /*with device cells*/); + + // compare netlist as string + CHECKPOINT (); + db::compare_netlist (_this, nl, + "circuit TOP (VSS=VSS,IN=IN,OUT=OUT,VDD=VDD,BULK=BULK);\n" + " device PMOS $1 (S=$4,G=VSS,D=VDD,B=VDD) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n" + " device PMOS $2 (S=VDD,G=$4,D=OUT,B=VDD) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n" + " device PMOS $3 (S=VDD,G=IN,D=$3,B=VDD) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n" + " device NMOS $4 (S=VSS,G=$4,D=OUT,B=BULK) (L=0.4,W=4.6,AS=2.185,AD=2.185,PS=8.8,PD=8.8);\n" + " device PNP $6 (C=BULK,B=$3,E=$3) (AE=3.06,PE=7);\n" + " device PNP $7 (C=BULK,B=$3,E=$4) (AE=6.12,PE=14);\n" + " device NMOS $9 (S=VSS,G=IN,D=$3,B=BULK) (L=0.4,W=3.1,AS=1.86,AD=1.86,PS=7.4,PD=7.4);\n" + "end;\n" + ); + + // compare the collected test data + + std::string au = tl::testsrc (); + au = tl::combine_path (au, "testdata"); + au = tl::combine_path (au, "algo"); + au = tl::combine_path (au, "bipolar_devices_nets.gds"); + + db::compare_layouts (_this, ly, au); +} + diff --git a/testdata/algo/bipolar_devices_nets.gds b/testdata/algo/bipolar_devices_nets.gds new file mode 100644 index 000000000..f27a72859 Binary files /dev/null and b/testdata/algo/bipolar_devices_nets.gds differ diff --git a/testdata/algo/bipolar_devices_test.oas b/testdata/algo/bipolar_devices_test.oas new file mode 100644 index 000000000..c60878984 Binary files /dev/null and b/testdata/algo/bipolar_devices_test.oas differ