diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 24a9329ff..ea1359b44 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -546,7 +546,7 @@ std::string Netlist::to_string () const if (p != pd.begin ()) { ps += ","; } - ps += p->name () + "=" + tl::to_string (d->parameter_value (p->id ())); + ps += p->name () + "=" + tl::sprintf ("%.12g", d->parameter_value (p->id ())); } res += std::string (" device ") + tl::to_word_or_quoted_string (d->device_class ()->name ()) + " " + device2string (*d) + " (" + ts + ") (" + ps + ");\n"; } diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index b9e67104d..413e9b949 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -58,7 +58,7 @@ static unsigned int define_layer (db::Layout &ly, db::LayerMap &lmap, int gds_la return lid; } -static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap) +static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters &clusters, db::Layout &ly, const std::map &lmap, const db::CellMapping &cmap, bool with_device_cells = false) { std::set device_cells_seen; @@ -75,7 +75,7 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< any_shapes = ! lc.begin (m->first).at_end (); } - if (any_shapes) { + if (any_shapes || (with_device_cells && n->terminal_count() > 0)) { std::string nn = "NET_" + c->name () + "_" + n->expanded_name (); db::Cell &net_cell = ly.cell (ly.add_cell (nn.c_str ())); @@ -88,40 +88,63 @@ static void dump_nets_to_layout (const db::Netlist &nl, const db::hier_clusters< } } + if (with_device_cells) { + for (db::Net::const_terminal_iterator t = n->begin_terminals (); t != n->end_terminals (); ++t) { + + const db::NetTerminalRef &tref = *t; + db::cell_index_type dci = tref.device ()->device_abstract ()->cell_index (); + db::DCplxTrans dtr = tref.device ()->trans (); + + net_cell.insert (db::CellInstArray (db::CellInst (dci), db::CplxTrans (ly.dbu ()).inverted () * dtr * db::CplxTrans (ly.dbu ()))); + + for (std::vector::const_iterator a = tref.device ()->other_abstracts ().begin (); a != tref.device ()->other_abstracts ().end (); ++a) { + const db::DeviceAbstractRef &aref = *a; + dci = aref.device_abstract->cell_index (); + net_cell.insert (db::CellInstArray (db::CellInst (dci), db::CplxTrans (ly.dbu ()).inverted () * dtr * aref.trans * db::CplxTrans (ly.dbu ()))); + } + + } + } + } } for (db::Circuit::const_device_iterator d = c->begin_devices (); d != c->end_devices (); ++d) { + std::vector original_device_cells; + db::cell_index_type dci = d->device_abstract ()->cell_index (); - - if (device_cells_seen.find (dci) != device_cells_seen.end ()) { - continue; + if (device_cells_seen.find (dci) == device_cells_seen.end ()) { + original_device_cells.push_back (dci); + device_cells_seen.insert (dci); } - db::Cell &device_cell = ly.cell (cmap.cell_mapping (dci)); - device_cells_seen.insert (dci); - - std::string ps; - const std::vector &pd = d->device_class ()->parameter_definitions (); - for (std::vector::const_iterator i = pd.begin (); i != pd.end (); ++i) { - if (! ps.empty ()) { - ps += ","; + for (std::vector::const_iterator a = d->other_abstracts ().begin (); a != d->other_abstracts ().end (); ++a) { + db::cell_index_type dci = a->device_abstract->cell_index (); + if (device_cells_seen.find (dci) == device_cells_seen.end ()) { + original_device_cells.push_back (dci); + device_cells_seen.insert (dci); } - ps += i->name () + "=" + tl::to_string (d->parameter_value (i->id ())); } - const std::vector &td = d->device_class ()->terminal_definitions (); - for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + for (std::vector::const_iterator i = original_device_cells.begin (); i != original_device_cells.end (); ++i) { - const db::local_cluster &dc = clusters.clusters_per_cell (dci).cluster_by_id (d->device_abstract ()->cluster_id_for_terminal (t->id ())); + db::cell_index_type dci = *i; + db::Cell &device_cell = ly.cell (cmap.cell_mapping (dci)); - for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { - db::Shapes &target = device_cell.shapes (m->second); - for (db::local_cluster::shape_iterator s = dc.begin (m->first); !s.at_end (); ++s) { - target.insert (*s); + const std::vector &td = d->device_class ()->terminal_definitions (); + for (std::vector::const_iterator t = td.begin (); t != td.end (); ++t) { + + const db::local_cluster &dc = clusters.clusters_per_cell (dci).cluster_by_id (d->device_abstract ()->cluster_id_for_terminal (t->id ())); + + for (std::map::const_iterator m = lmap.begin (); m != lmap.end (); ++m) { + db::Shapes &target = device_cell.shapes (m->second); + for (db::local_cluster::shape_iterator s = dc.begin (m->first); !s.at_end (); ++s) { + target.insert (*s); + } } + } } @@ -823,3 +846,239 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) db::compare_layouts (_this, ly, au); } +TEST(4_ResAndCapExtraction) +{ + 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_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); + + // derived regions + + db::Region rpoly = rpoly_all - rres; + db::Region rpoly_res = rpoly_all & rres; + + 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; + + // 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::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + db::NetlistDeviceExtractorResistor res_ex ("POLY_RES", 50.0); + db::NetlistDeviceExtractorCapacitor cap_ex ("MIM_CAP", 1.0e-15); + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + // 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; + // 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; + dl["C"] = &rpoly; + // terminal patches + dl["tA"] = &rpoly; + dl["tB"] = &rpoly; + res_ex.extract (dss, 0, dl, nl, cl); + + dl.clear (); + dl["P1"] = &rcap1; + dl["P2"] = &rcap2; + // terminal patches + dl["tA"] = &rmetal1; + dl["tB"] = &rmetal2; + cap_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 + + // 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);\n" + " device PMOS $1 (S=VDD,G=$4,D=OUT) (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) (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) (L=0.4,W=4.6,AS=2.185,AD=2.185,PS=8.8,PD=8.8);\n" + " device MIM_CAP $5 (A=$4,B=VSS) (C=2.622e-14,A=26.22,P=29.8);\n" + " device POLY_RES $7 (A=$3,B=$4) (R=750,A=2.4,P=13.6);\n" + " device POLY_RES $9 (A=$4,B=VSS) (R=1825,A=5.84,P=30);\n" + " device NMOS $10 (S=VSS,G=IN,D=$3) (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_nets.gds"); + + db::compare_layouts (_this, ly, au); +} + diff --git a/testdata/algo/device_extract_capres_nets.gds b/testdata/algo/device_extract_capres_nets.gds new file mode 100644 index 000000000..63b4ef0e1 Binary files /dev/null and b/testdata/algo/device_extract_capres_nets.gds differ diff --git a/testdata/algo/devices_test.oas b/testdata/algo/devices_test.oas new file mode 100644 index 000000000..aa7221ed2 Binary files /dev/null and b/testdata/algo/devices_test.oas differ