diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 8e7e19eca..2bfa0fd70 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1270,6 +1270,11 @@ private: for (db::CellInstArray::iterator ii2 = i2.begin_touching (ib1.transformed (t2i), mp_layout); ! ii2.at_end (); ++ii2) { db::ICplxTrans tt2 = t2 * i2.complex_trans (*ii2); + if (i1.cell_index () == i2.cell_index () && tt1 == tt2) { + // skip interactions between identical instances (duplicate instance removal) + continue; + } + box_type ib2 = bb2.transformed (tt2); box_type common12 = ib1 & ib2 & common; @@ -1653,6 +1658,8 @@ hier_clusters::make_path (const db::Layout &layout, const db::Cell &cell, siz connected_clusters &child_cc = clusters_per_cell (p->inst_cell_index ()); if (child_cc.is_root (id)) { + std::set seen; // to avoid duplicate connections + const db::Cell &child_cell = layout.cell (p->inst_cell_index ()); for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { @@ -1662,7 +1669,7 @@ hier_clusters::make_path (const db::Layout &layout, const db::Cell &cell, siz for (db::CellInstArray::iterator pii = child_inst.begin (); ! pii.at_end (); ++pii) { ClusterInstance ci2 (id, child_inst.cell_index (), child_inst.complex_trans (*pii), child_inst.prop_id ()); - if (cell.cell_index () != pi->parent_cell_index () || ci != ci2) { + if ((cell.cell_index () != pi->parent_cell_index () || ci != ci2) && seen.find (ci2) == seen.end ()) { size_t id_dummy; @@ -1676,6 +1683,7 @@ hier_clusters::make_path (const db::Layout &layout, const db::Cell &cell, siz } parent_cc.add_connection (id_dummy, ci2); + seen.insert (ci2); } @@ -1709,6 +1717,8 @@ hier_clusters::make_path (const db::Layout &layout, const db::Cell &cell, siz connected_clusters &child_cc = clusters_per_cell (p->inst_cell_index ()); if (child_cc.is_root (id)) { + std::set seen; // to avoid duplicate connections + const db::Cell &child_cell = layout.cell (p->inst_cell_index ()); for (db::Cell::parent_inst_iterator pi = child_cell.begin_parent_insts (); ! pi.at_end (); ++pi) { @@ -1717,22 +1727,27 @@ hier_clusters::make_path (const db::Layout &layout, const db::Cell &cell, siz connected_clusters &parent_cc = clusters_per_cell (pi->parent_cell_index ()); for (db::CellInstArray::iterator pii = child_inst.begin (); ! pii.at_end (); ++pii) { - size_t id_dummy; - - const typename db::local_cluster::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets (); - if (gn.empty ()) { - id_dummy = parent_cc.insert_dummy (); - } else { - local_cluster *lc = parent_cc.insert (); - lc->set_global_nets (gn); - id_dummy = lc->id (); - } - ClusterInstance ci2 (id, child_inst.cell_index (), child_inst.complex_trans (*pii), child_inst.prop_id ()); - parent_cc.add_connection (id_dummy, ci2); + if (seen.find (ci2) == seen.end ()) { + + size_t id_dummy; + + const typename db::local_cluster::global_nets &gn = child_cc.cluster_by_id (id).get_global_nets (); + if (gn.empty ()) { + id_dummy = parent_cc.insert_dummy (); + } else { + local_cluster *lc = parent_cc.insert (); + lc->set_global_nets (gn); + id_dummy = lc->id (); + } + + parent_cc.add_connection (id_dummy, ci2); + seen.insert (ci2); + + if (pci == pi->parent_cell_index () && ci == ci2) { + id_new = id_dummy; + } - if (pci == pi->parent_cell_index () && ci == ci2) { - id_new = id_dummy; } } diff --git a/src/db/unit_tests/dbHierProcessorTests.cc b/src/db/unit_tests/dbHierProcessorTests.cc index e5b2724c8..01591c691 100644 --- a/src/db/unit_tests/dbHierProcessorTests.cc +++ b/src/db/unit_tests/dbHierProcessorTests.cc @@ -1095,3 +1095,27 @@ TEST(BasicHierarchyVariantsNot2) run_test_bool (_this, "hlp14.oas", TMNot, 101); } +TEST(RedundantHierarchyAnd1) +{ + // Redundant hierarchy, NOT + run_test_bool2 (_this, "hlp15.oas", TMAnd, 100); +} + +TEST(RedundantHierarchyNot1) +{ + // Redundant hierarchy, NOT + run_test_bool2 (_this, "hlp15.oas", TMNot, 101); +} + +TEST(RedundantHierarchyAnd2) +{ + // Redundant hierarchy, NOT + run_test_bool2 (_this, "hlp16.gds", TMAnd, 100); +} + +TEST(RedundantHierarchyNot2) +{ + // Redundant hierarchy, NOT + run_test_bool2 (_this, "hlp16.gds", TMNot, 101); +} + diff --git a/src/db/unit_tests/dbLayoutToNetlistTests.cc b/src/db/unit_tests/dbLayoutToNetlistTests.cc index 9cf89998a..3fed04252 100644 --- a/src/db/unit_tests/dbLayoutToNetlistTests.cc +++ b/src/db/unit_tests/dbLayoutToNetlistTests.cc @@ -2553,3 +2553,205 @@ TEST(10_Antenna) db::compare_layouts (_this, ly2, au); } +TEST(11_DuplicateInstances) +{ + 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); + + { + 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, "device_extract_l1_dup_inst.gds"); + + tl::InputStream stream (fn); + db::Reader reader (stream); + reader.read (ly, options); + } + + db::Cell &tc = ly.cell (*ly.begin_top_down ()); + db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set ())); + + std::auto_ptr rnwell (l2n.make_layer (nwell, "nwell")); + std::auto_ptr ractive (l2n.make_layer (active, "active")); + std::auto_ptr rpoly (l2n.make_polygon_layer (poly, "poly")); + std::auto_ptr rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl")); + std::auto_ptr rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont")); + std::auto_ptr rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont")); + std::auto_ptr rmetal1 (l2n.make_polygon_layer (metal1, "metal1")); + std::auto_ptr rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl")); + std::auto_ptr rvia1 (l2n.make_polygon_layer (via1, "via1")); + std::auto_ptr rmetal2 (l2n.make_polygon_layer (metal2, "metal2")); + std::auto_ptr rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl")); + + // derived regions + + 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::NetlistDeviceExtractorMOS3Transistor pmos_ex ("PMOS"); + db::NetlistDeviceExtractorMOS3Transistor nmos_ex ("NMOS"); + + // device extraction + + db::NetlistDeviceExtractor::input_layers dl; + + dl["SD"] = &rpsd; + dl["G"] = &rpgate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (pmos_ex, dl); + + dl["SD"] = &rnsd; + dl["G"] = &rngate; + dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes + l2n.extract_devices (nmos_ex, dl); + + // return the computed layers into the original layout and write it for debugging purposes + // NOTE: this will include the device layers too + + 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 = ly.insert_layer (db::LayerProperties (14, 0)); // 14/0 -> Poly with gate terminal + + 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->insert_into (&ly, tc.cell_index (), lpoly); + + // net extraction + + l2n.register_layer (rpsd, "psd"); + l2n.register_layer (rnsd, "nsd"); + + // Intra-layer + l2n.connect (rpsd); + l2n.connect (rnsd); + l2n.connect (*rpoly); + l2n.connect (*rdiff_cont); + l2n.connect (*rpoly_cont); + l2n.connect (*rmetal1); + l2n.connect (*rvia1); + l2n.connect (*rmetal2); + // Inter-layer + l2n.connect (rpsd, *rdiff_cont); + l2n.connect (rnsd, *rdiff_cont); + l2n.connect (*rpoly, *rpoly_cont); + l2n.connect (*rpoly_cont, *rmetal1); + l2n.connect (*rdiff_cont, *rmetal1); + l2n.connect (*rmetal1, *rvia1); + l2n.connect (*rvia1, *rmetal2); + l2n.connect (*rpoly, *rpoly_lbl); // attaches labels + l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels + l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels + + // create some mess - we have to keep references to the layers to make them not disappear + rmetal1_lbl.reset (0); + rmetal2_lbl.reset (0); + rpoly_lbl.reset (0); + + l2n.extract_netlist (); + + // 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 [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0)); + + // write nets to layout + db::CellMapping cm = l2n.cell_mapping_into (ly, tc, true /*with device cells*/); + dump_nets_to_layout (l2n, ly, dump_map, cm); + + dump_map.clear (); + dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0)); + dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0)); + dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0)); + dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0)); + dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0)); + dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0)); + dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0)); + dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0)); + + dump_recursive_nets_to_layout (l2n, ly, dump_map, cm); + + // 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_au1_dup_inst_with_rec_nets.gds"); + + db::compare_layouts (_this, ly, au); + + // compare netlist as string + CHECKPOINT (); + db::compare_netlist (_this, *l2n.netlist (), + "circuit RINGO ();\n" + " subcircuit INV2 $1 (IN=$I12,$2=FB,OUT=OSC,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $2 (IN=FB,$2=$I25,OUT=$I1,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $3 (IN=$I1,$2=$I26,OUT=$I2,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $4 (IN=$I2,$2=$I27,OUT=$I3,$4=VSS,$5=VDD);\n" + " subcircuit INV2 $5 (IN=$I3,$2=$I28,OUT=$I4,$4=VSS,$5=VDD);\n" + " subcircuit BLOCK $6 ($1=$I29,$2=VDD,$3=VSS,$4=$I11,$5=$I5,$6=$I4,$7=$I12);\n" + " subcircuit INV2 $7 (IN=$I11,$2=$I29,OUT=$I5,$4=VSS,$5=VDD);\n" + "end;\n" + "circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5);\n" + " device PMOS $1 (S=$2,G=IN,D=$5) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " device NMOS $3 (S=$2,G=IN,D=$4) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n" + " device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n" + " subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n" + " subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n" + " subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n" + " subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n" + "end;\n" + "circuit TRANS ($1=$1,$2=$2,$3=$3);\n" + "end;\n" + "circuit BLOCK ($1=$I21,$2=$I14,$3=$I10,$4=$I9,$5=$I6,$6=$I5,$7=$I3);\n" + " subcircuit INV2 $1 (IN=$I6,$2=$I22,OUT=$I1,$4=$I10,$5=$I14);\n" + " subcircuit INV2 $2 (IN=$I9,$2=$I21,OUT=$I6,$4=$I10,$5=$I14);\n" + " subcircuit INV2 $3 (IN=$I2,$2=$I20,OUT=$I3,$4=$I10,$5=$I14);\n" + " subcircuit INV2 $4 (IN=$I1,$2=$I19,OUT=$I2,$4=$I10,$5=$I14);\n" + " subcircuit INV2 $5 (IN=$I5,$2=$I18,OUT=$I9,$4=$I10,$5=$I14);\n" + "end;\n" + ); +} diff --git a/testdata/algo/device_extract_au1_dup_inst_with_rec_nets.gds b/testdata/algo/device_extract_au1_dup_inst_with_rec_nets.gds new file mode 100644 index 000000000..639924e76 Binary files /dev/null and b/testdata/algo/device_extract_au1_dup_inst_with_rec_nets.gds differ diff --git a/testdata/algo/device_extract_l1_dup_inst.gds b/testdata/algo/device_extract_l1_dup_inst.gds new file mode 100644 index 000000000..03d71c884 Binary files /dev/null and b/testdata/algo/device_extract_l1_dup_inst.gds differ diff --git a/testdata/algo/hlp15.oas b/testdata/algo/hlp15.oas new file mode 100644 index 000000000..f54b4651d Binary files /dev/null and b/testdata/algo/hlp15.oas differ diff --git a/testdata/algo/hlp16.gds b/testdata/algo/hlp16.gds new file mode 100644 index 000000000..acc57683a Binary files /dev/null and b/testdata/algo/hlp16.gds differ