From 4212a783a5f2b1a07f52bee8886414093408607f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 14 Jun 2019 21:21:11 +0200 Subject: [PATCH] WIP: test cases for device extractors R/C with bulk --- src/db/db/dbNetlistDeviceExtractorClasses.cc | 16 +- src/db/unit_tests/dbNetlistExtractorTests.cc | 273 ++++++++++++++++++ .../device_extract_capres_with_bulk_nets.gds | Bin 0 -> 15288 bytes testdata/algo/devices_with_bulk_test.oas | Bin 0 -> 1298 bytes 4 files changed, 281 insertions(+), 8 deletions(-) create mode 100644 testdata/algo/device_extract_capres_with_bulk_nets.gds create mode 100644 testdata/algo/devices_with_bulk_test.oas diff --git a/src/db/db/dbNetlistDeviceExtractorClasses.cc b/src/db/db/dbNetlistDeviceExtractorClasses.cc index 3f6d25b2e..3714ea201 100644 --- a/src/db/db/dbNetlistDeviceExtractorClasses.cc +++ b/src/db/db/dbNetlistDeviceExtractorClasses.cc @@ -293,10 +293,10 @@ void NetlistDeviceExtractorResistorWithBulk::setup () { define_layer ("R", "Resistor"); // #0 define_layer ("C", "Contacts"); // #1 - define_layer ("W", "Well/Bulk"); // #2 - define_layer ("tA", 1, "A terminal output"); // #3 -> C - define_layer ("tB", 1, "B terminal output"); // #4 -> C - define_layer ("tW", 2, "W terminal output"); // #5 -> W + define_layer ("tA", 1, "A terminal output"); // #2 -> C + define_layer ("tB", 1, "B terminal output"); // #3 -> C + define_layer ("W", "Well/Bulk"); // #4 + define_layer ("tW", 4, "W terminal output"); // #5 -> W register_device_class (new db::DeviceClassResistorWithBulk ()); } @@ -395,10 +395,10 @@ void NetlistDeviceExtractorCapacitorWithBulk::setup () { define_layer ("P1", "Plate 1"); // #0 define_layer ("P2", "Plate 2"); // #1 - define_layer ("W", "Well/Bulk"); // #2 - define_layer ("tA", 0, "A terminal output"); // #3 -> P1 - define_layer ("tB", 1, "B terminal output"); // #4 -> P2 - define_layer ("tW", 2, "W terminal output"); // #5 -> W + define_layer ("tA", 0, "A terminal output"); // #2 -> P1 + define_layer ("tB", 1, "B terminal output"); // #3 -> P2 + define_layer ("W", "Well/Bulk"); // #4 + define_layer ("tW", 4, "W terminal output"); // #5 -> W register_device_class (new db::DeviceClassCapacitorWithBulk ()); } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index 413e9b949..7c66997fa 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -1082,3 +1082,276 @@ TEST(4_ResAndCapExtraction) db::compare_layouts (_this, ly, au); } +TEST(5_ResAndCapWithBulkExtraction) +{ + 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 cap = define_layer (ly, lmap, 10); + unsigned int res = define_layer (ly, lmap, 11); + + { + 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, "devices_with_bulk_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_all (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 rcap (db::RecursiveShapeIterator (ly, tc, cap), dss); + db::Region rres (db::RecursiveShapeIterator (ly, tc, res), dss); + db::Region rbulk (new db::DeepRegion (dss.empty_layer ())); + + // derived regions + + db::Region rpoly = rpoly_all - rres; + db::Region rpoly_res = rpoly_all & rres; + db::Region rpoly_res_nw = rpoly_res & rnwell; + db::Region rpoly_res_sub = rpoly_res - rnwell; + + db::Region rpactive = ractive & rnwell; + db::Region rpgate = rpactive & rpoly; + db::Region rpsd = rpactive - rpgate; + + db::Region rnactive = ractive - rnwell; + db::Region rngate = rnactive & rpoly; + db::Region rnsd = rnactive - rngate; + + db::Region rcap1 = rmetal1 & rcap; + db::Region rcap2 = rmetal2 & rcap; + + db::Region rcap1_nw = rcap1 & rnwell; + db::Region rcap2_nw = rcap2 & rnwell; + + db::Region rcap1_sub = rcap1 - rnwell; + db::Region rcap2_sub = rcap2 - rnwell; + + // 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 lpoly_res = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Resistor Poly + unsigned int lcap1 = ly.insert_layer (db::LayerProperties (15, 0)); // 15/0 -> Cap 1 + unsigned int lcap2 = ly.insert_layer (db::LayerProperties (16, 0)); // 16/0 -> Cap 2 + + 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); + rpoly_res.insert_into (&ly, tc.cell_index (), lpoly_res); + rcap1.insert_into (&ly, tc.cell_index (), lcap1); + rcap2.insert_into (&ly, tc.cell_index (), lcap2); + + // perform the extraction + + db::Netlist nl; + db::hier_clusters cl; + + db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS"); + db::NetlistDeviceExtractorResistorWithBulk res_substrate_ex ("POLY_RES_SUBSTRATE", 50.0); + db::NetlistDeviceExtractorResistorWithBulk res_nwell_ex ("POLY_RES_NWELL", 50.0); + db::NetlistDeviceExtractorCapacitorWithBulk cap_substrate_ex ("MIM_CAP_SUBSTRATE", 1.0e-15); + db::NetlistDeviceExtractorCapacitorWithBulk cap_nwell_ex ("MIM_CAP_NWELL", 1.0e-15); + + 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["R"] = &rpoly_res_sub; + dl["C"] = &rpoly; + dl["W"] = &rbulk; + // terminal patches + dl["tA"] = &rpoly; + dl["tB"] = &rpoly; + res_substrate_ex.extract (dss, 0, dl, nl, cl); + + dl.clear (); + dl["R"] = &rpoly_res_nw; + dl["C"] = &rpoly; + dl["W"] = &rnwell; + // terminal patches + dl["tA"] = &rpoly; + dl["tB"] = &rpoly; + res_nwell_ex.extract (dss, 0, dl, nl, cl); + + dl.clear (); + dl["P1"] = &rcap1_sub; + dl["P2"] = &rcap2_sub; + dl["W"] = &rbulk; + // terminal patches + dl["tA"] = &rmetal1; + dl["tB"] = &rmetal2; + cap_substrate_ex.extract (dss, 0, dl, nl, cl); + + dl.clear (); + dl["P1"] = &rcap1_nw; + dl["P2"] = &rcap2_nw; + dl["W"] = &rnwell; + // terminal patches + dl["tA"] = &rmetal1; + dl["tB"] = &rmetal2; + cap_nwell_ex.extract (dss, 0, dl, nl, cl); + + + // perform the net extraction + + db::NetlistExtractor net_ex; + + db::Connectivity conn; + // Intra-layer + conn.connect (rpsd); + conn.connect (rnsd); + conn.connect (rpoly); + conn.connect (rdiff_cont); + conn.connect (rpoly_cont); + conn.connect (rmetal1); + conn.connect (rvia1); + conn.connect (rmetal2); + // Inter-layer + 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"); + conn.connect_global (rnwell, "NWELL"); + + // 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 ("RES"); + circuits_to_flatten.push_back ("RES_MEANDER"); + 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 + // 202/0 -> Active + // 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 + 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 (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,NWELL=NWELL);\n" + " device PMOS $1 (S=VDD,G=$4,D=OUT,B=NWELL) (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=IN,D=$3,B=NWELL) (L=0.4,W=2.3,AS=1.38,AD=1.38,PS=5.8,PD=5.8);\n" + " device NMOS $3 (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 MIM_CAP_SUBSTRATE $5 (A=$4,B=VSS,W=BULK) (C=1.334e-14,A=13.34,P=15);\n" + " device MIM_CAP_NWELL $6 (A=$4,B=VSS,W=NWELL) (C=1.288e-14,A=12.88,P=14.8);\n" + " device POLY_RES_NWELL $7 (A=$3,B=$4,W=NWELL) (R=750,A=2.4,P=13.6);\n" + " device POLY_RES_SUBSTRATE $9 (A=$4,B=VSS,W=BULK) (R=1825,A=5.84,P=30);\n" + " device NMOS $10 (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, "device_extract_capres_with_bulk_nets.gds"); + + db::compare_layouts (_this, ly, au); +} + diff --git a/testdata/algo/device_extract_capres_with_bulk_nets.gds b/testdata/algo/device_extract_capres_with_bulk_nets.gds new file mode 100644 index 0000000000000000000000000000000000000000..42331829d10f8ea10a87b63c4b0433a17ed50fa8 GIT binary patch literal 15288 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 aT$-w04J1HZZC>;TCb_Lyj!XW}2ch4_MVyiV literal 0 HcmV?d00001