diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 7f2acce95..5d0568a73 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -524,8 +524,8 @@ class DB_PUBLIC hnp_interaction_receiver public: typedef typename local_cluster::box_type box_type; - hnp_interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans) - : mp_conn (&conn), m_any (false), m_soft_mode (0), m_trans (trans) + hnp_interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans, std::map > *interacting_this, std::map > *interacting_other) + : mp_conn (&conn), m_any (false), m_soft_mode (0), m_trans (trans), mp_interacting_this (interacting_this), mp_interacting_other (interacting_other) { // .. nothing yet .. } @@ -534,6 +534,12 @@ public: { int soft = 0; if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans, soft)) { + if (mp_interacting_this) { + (*mp_interacting_this) [l1].insert (s1); + } + if (mp_interacting_other) { + (*mp_interacting_other) [l2].insert (s2); + } if (soft == 0 || (m_soft_mode != 0 && m_soft_mode != soft)) { m_soft_mode = 0; m_any = true; @@ -543,9 +549,14 @@ public: } } + bool any () const + { + return m_any || m_soft_mode != 0; + } + bool stop () const { - return m_any; + return m_any && ! mp_interacting_other && ! mp_interacting_this; } int soft_mode () const @@ -558,6 +569,7 @@ private: bool m_any; int m_soft_mode; db::ICplxTrans m_trans; + std::map > *mp_interacting_this, *mp_interacting_other; }; template @@ -619,9 +631,31 @@ local_cluster::interacts (const db::Cell &cell, const db::ICplxTrans &trans, return false; } +template +static +void collect_interactions_in_original_order (const std::map::tree_type> &shapes, const std::map > &interacting, std::map > &interacting_out) +{ + for (typename std::map >::const_iterator i = interacting.begin (); i != interacting.end (); ++i) { + + std::vector &t = interacting_out [i->first]; + auto s = shapes.find (i->first); + if (s == shapes.end ()) { + continue; + } + + t.reserve (t.size () + i->second.size ()); + for (auto j = s->second.begin (); j != s->second.end (); ++j) { + if (i->second.find (j.operator-> ()) != i->second.end ()) { + t.push_back (j.operator-> ()); + } + } + + } +} + template bool -local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft) const +local_cluster::interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft, std::map > *interacting_this, std::map > *interacting_other) const { db::box_convert bc; @@ -679,11 +713,26 @@ local_cluster::interacts (const local_cluster &other, const db::ICplxTrans } } - hnp_interaction_receiver rec (conn, trans); + std::map > is_this, is_other; + std::map > *p_is_this = interacting_this ? &is_this : 0; + std::map > *p_is_other = interacting_other ? &is_other : 0; + + hnp_interaction_receiver rec (conn, trans, p_is_this, p_is_other); + scanner.process (rec, 1 /*==touching*/, bc, bc_t); + + if (rec.any ()) { + + if (p_is_this) { + collect_interactions_in_original_order (m_shapes, *p_is_this, *interacting_this); + } + if (p_is_other) { + collect_interactions_in_original_order (other.m_shapes, *p_is_other, *interacting_other); + } - if (! scanner.process (rec, 1 /*==touching*/, bc, bc_t) || rec.soft_mode () != 0) { soft = rec.soft_mode (); + return true; + } else { return false; } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index ad97c7fad..d035075a1 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -326,8 +326,10 @@ public: * * "trans" is the transformation which is applied to the other cluster before * the test. + * If non-null "interacting_this" will receive all interacting shapes from *this in case of success. + * If non-null "interacting_other" will receive all interacting shapes from other in case of success. */ - bool interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft) const; + bool interacts (const local_cluster &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft, std::map > *interacting_this = 0, std::map > *interacting_other = 0) const; /** * @brief Tests whether this cluster interacts with the given cell diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 86b7d50ab..0fc0f40ad 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -1308,6 +1308,102 @@ static bool deliver_shapes_of_net (bool recursive, const db::Netlist *nl, const return true; } +void +LayoutToNetlist::collect_shapes_of_pin (const local_cluster &c, const db::Net *other_net, const db::ICplxTrans &sc_trans, const db::ICplxTrans &trans, std::map &result) const +{ + if (! other_net || ! other_net->circuit ()) { + return; + } + + auto cc_other = m_net_clusters.clusters_per_cell (other_net->circuit ()->cell_index ()); + auto c_other = cc_other.cluster_by_id (other_net->cluster_id ()); + + std::map > interacting; + int soft = 0; + if (c.interacts (c_other, sc_trans, m_conn, soft, 0, &interacting)) { + + auto t = trans * sc_trans; + + for (auto i = interacting.begin (); i != interacting.end (); ++i) { + db::Region &r = result [i->first]; + for (auto s = i->second.begin (); s != i->second.end (); ++s) { + deliver_shape (**s, r, t, 0); + } + } + + } + + double dbu = internal_layout ()->dbu (); + + for (auto p = other_net->begin_subcircuit_pins (); p != other_net->end_subcircuit_pins (); ++p) { + + db::ICplxTrans sc_trans2 = sc_trans * db::CplxTrans (dbu).inverted () * p->subcircuit ()->trans () * db::CplxTrans (dbu); + const db::Net *other_net2 = p->subcircuit ()->circuit_ref ()->net_for_pin (p->pin_id ()); + + collect_shapes_of_pin (c, other_net2, sc_trans2, trans, result); + + } +} + +std::map +LayoutToNetlist::shapes_of_pin (const db::NetSubcircuitPinRef &pin, const db::ICplxTrans &trans) const +{ + std::map result; + + const db::Net *net = pin.net (); + if (! net || ! net->circuit () || ! pin.subcircuit () || ! pin.subcircuit ()->circuit_ref ()) { + return result; + } + + auto cc = m_net_clusters.clusters_per_cell (net->circuit ()->cell_index ()); + auto c = cc.cluster_by_id (net->cluster_id ()); + + double dbu = internal_layout ()->dbu (); + db::ICplxTrans sc_trans = db::CplxTrans (dbu).inverted () * pin.subcircuit ()->trans () * db::CplxTrans (dbu); + const db::Net *other_net = pin.subcircuit ()->circuit_ref ()->net_for_pin (pin.pin_id ()); + + collect_shapes_of_pin (c, other_net, sc_trans, trans, result); + + return result; +} + +std::map +LayoutToNetlist::shapes_of_terminal (const db::NetTerminalRef &terminal, const db::ICplxTrans &trans) const +{ + std::map result; + + const db::Net *net = terminal.net (); + if (! net || ! net->circuit () || ! terminal.device () || ! terminal.device ()->device_abstract ()) { + return result; + } + + auto cc = m_net_clusters.clusters_per_cell (net->circuit ()->cell_index ()); + auto c = cc.cluster_by_id (net->cluster_id ()); + + double dbu = internal_layout ()->dbu (); + db::ICplxTrans d_trans = db::CplxTrans (dbu).inverted () * terminal.device ()->trans () * db::CplxTrans (dbu); + + auto cc_other = m_net_clusters.clusters_per_cell (terminal.device ()->device_abstract ()->cell_index ()); + auto c_other = cc_other.cluster_by_id (terminal.device ()->device_abstract ()->cluster_id_for_terminal (terminal.terminal_id ())); + + std::map > interacting; + int soft = 0; + if (c.interacts (c_other, d_trans, m_conn, soft, 0, &interacting)) { + + auto t = trans * d_trans; + + for (auto i = interacting.begin (); i != interacting.end (); ++i) { + db::Region &r = result [i->first]; + for (auto s = i->second.begin (); s != i->second.end (); ++s) { + deliver_shape (**s, r, t, 0); + } + } + + } + + return result; +} + void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, db::properties_id_type propid, const ICplxTrans &trans) const { unsigned int lid = layer_of (of_layer); diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index e8c78d217..4d1d1ef49 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -828,6 +828,32 @@ public: return m_net_clusters; } + /** + * @brief Gets the shapes that make the subcircuit pin + * + * This method looks up all shapes from the subcircuit that interact with the shapes of the net + * the subcircuit pin lives in. It will return a map of layer index vs. Region with these + * shapes, transformed into the coordinate space of the net. + * + * Note that this method only considers top-down interactions between the shapes of the net + * and subcircuits on any level below, but not between subcircuits. + * It is useful only for certain topologies - i.e. digital nets connecting gate cells. + */ + std::map shapes_of_pin (const db::NetSubcircuitPinRef &pin, const db::ICplxTrans &trans = db::ICplxTrans ()) const; + + /** + * @brief Gets the shapes that make the device terminal + * + * This method looks up all shapes from the device terminals that interact with the shapes of the net + * the device terminal lives in. It will return a map of layer index vs. Region with these + * shapes, transformed into the coordinate space of the net. + * + * Note that this method only considers top-down interactions between the shapes of the net + * and subcircuits on any level below, but not between subcircuits and devices. + * It is useful for flat-extracted netlists for example. + */ + std::map shapes_of_terminal (const db::NetTerminalRef &terminal, const db::ICplxTrans &trans = db::ICplxTrans ()) const; + /** * @brief Returns all shapes of a specific net and layer. * @@ -1099,6 +1125,7 @@ private: void ensure_layout () const; std::string make_new_name (const std::string &stem = std::string ()); db::CellMapping make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector *nets, bool with_device_cells); + void collect_shapes_of_pin (const local_cluster &c, const db::Net *other_net, const db::ICplxTrans &sc_trans, const db::ICplxTrans &trans, std::map &result) const; void connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b); size_t connect_global_impl (const db::ShapeCollection &l, const std::string &gn); void soft_connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b); diff --git a/src/db/db/gsiDeclDbCommonStreamOptions.cc b/src/db/db/gsiDeclDbCommonStreamOptions.cc index 1bc0281ef..f3a889dd4 100644 --- a/src/db/db/gsiDeclDbCommonStreamOptions.cc +++ b/src/db/db/gsiDeclDbCommonStreamOptions.cc @@ -191,16 +191,22 @@ gsi::ClassExt common_reader_options ( gsi::EnumIn decl_dbCommonReader_CellConflictResolution ("db", "CellConflictResolution", gsi::enum_const ("AddToCell", db::AddToCell, "@brief Add content to existing cell\n" - "This is the mode use in before version 0.27. Content of new cells is simply added to existing cells with the same name." + "This is the mode use in before version 0.27. Content of new cells is simply added to existing cells with the same name.\n" + "Before version 0.29.2, this mode also merged instances, rendering it difficult to merge two identical cell hierarchies.\n" + "Since version 0.29.2, no instance duplicates are generated. Instead only new instances are added to existing cells.\n" + "With this feature in place, it is safe to merge two identical cell hierarchies stored in different files using AddToCell mode.\n" + "In that application, the shapes and layers of the layouts are combined, but the cell hierarchy stays identical." ) + gsi::enum_const ("OverwriteCell", db::OverwriteCell, - "@brief The old cell is overwritten entirely (including child cells which are not used otherwise)\n" + "@brief The old cell is overwritten entirely (including child cells which are not used otherwise).\n" ) + gsi::enum_const ("SkipNewCell", db::SkipNewCell, - "@brief The new cell is skipped entirely (including child cells which are not used otherwise)\n" + "@brief The new cell is skipped entirely (including child cells which are not used otherwise).\n" ) + gsi::enum_const ("RenameCell", db::RenameCell, - "@brief The new cell will be renamed to become unique\n" + "@brief The new cell will be renamed to become unique.\n" + "In this mode, two files are are combined rendering independent cell hierarchies coming from the original files.\n" + "Cells may be renamed however. Also, new top cells will appear after merging a file into the layout using RenameCell mode.\n" ), "@brief This enum specifies how cell conflicts are handled if a layout read into another layout and a cell name conflict arises.\n" "Until version 0.26.8 and before, the mode was always 'AddToCell'. On reading, a cell was 'reopened' when encountering a cell name " diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index e041f5ae9..3d11ee518 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -678,6 +678,44 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("netlist", &db::LayoutToNetlist::netlist, "@brief gets the netlist extracted (0 if no extraction happened yet)\n" ) + + gsi::method ("shapes_of_pin", &db::LayoutToNetlist::shapes_of_pin, gsi::arg ("pin"), gsi::arg ("trans", db::ICplxTrans (), "unity"), + "@brief Returns all shapes of the given subcircuit pin that make a connection to the net the pin lives in.\n" + "This will return all shapes from the subcircuit attached by the given pin that interact with the net the pin lives in.\n" + "This method returns a \\Region object with the shapes per layer where interactions are found.\n" + "The layers are given as layer indexes.\n" + "\n" + "The returned shapes are already transformed into the coordinate system of the net (see \\shapes_of_net for example).\n" + "An additional transformation can be applied using the optional \\trans argument.\n" + "\n" + "Note, that this method only considers interations between net shapes and subcircuits on every level below, " + "but not between subcircuits.\n" + "It can be used for example for digital nets connecting gate cells. In the general case however, nets may be formed\n" + "also by touching subcircuits. In that case, the nets do not have shapes of their own and this function cannot detect\n" + "the pin shapes.\n" + "\n" + "The call of this method may not be cheap, specificially if large nets are involved.\n" + "\n" + "This method has been introduced in version 0.29.2." + ) + + gsi::method ("shapes_of_terminal", &db::LayoutToNetlist::shapes_of_terminal, gsi::arg ("terminal"), gsi::arg ("trans", db::ICplxTrans (), "unity"), + "@brief Returns all shapes of the given device terminal that make a connection to the net the terminal lives in.\n" + "This will return all shapes from the device attached by the given terminal that interact with the net the terminal lives in.\n" + "This method returns a \\Region object with the shapes per layer where interactions are found.\n" + "The layers are given as layer indexes.\n" + "\n" + "The returned shapes are already transformed into the coordinate system of the net (see \\shapes_of_net for example).\n" + "An additional transformation can be applied using the optional \\trans argument.\n" + "\n" + "Note, that this method only considers interations between net shapes and the device connected by the terminal, " + "but not between subcircuits on the net and the device.\n" + "It can be used for example for flat-extracted, transistor-level netlists. In the general case however, nets may be formed\n" + "also by subcircuits touching devices. In that case, the nets do not have shapes of their own and this function cannot detect\n" + "the terminal shapes.\n" + "\n" + "The call of this method may not be cheap, specificially if large nets are involved.\n" + "\n" + "This method has been introduced in version 0.29.2." + ) + gsi::factory ("shapes_of_net", (db::Region *(db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive", true), gsi::arg ("trans", db::ICplxTrans (), "unity"), "@brief Returns all shapes of a specific net and layer.\n" "If 'recursive'' is true, the returned region will contain the shapes of\n" diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 6f5e2ddd6..6cc8d2fb5 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -1827,3 +1827,87 @@ TEST(112_Waiving) compare_text_files (report, au_report); } + +TEST(120_ShapesOfPin) +{ + std::string rs = tl::testdata (); + rs += "/drc/drcSimpleTests_120.drc"; + + // apart from that it's a variant of 14b ... + + std::string input = tl::testdata (); + input += "/drc/drcSimpleTests_120.gds"; + + std::string au = tl::testdata (); + au += "/drc/drcSimpleTests_au120.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} + +TEST(121_ShapesOfTerminal) +{ + std::string rs = tl::testdata (); + rs += "/drc/drcSimpleTests_121.drc"; + + // apart from that it's a variant of 14b ... + + std::string input = tl::testdata (); + input += "/drc/drcSimpleTests_121.gds"; + + std::string au = tl::testdata (); + au += "/drc/drcSimpleTests_au121.gds"; + + std::string output = this->tmp_file ("tmp.gds"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + , input, output) + ); + config.set_interpreter (lym::Macro::Ruby); + EXPECT_EQ (config.run (), 0); + } + + lym::Macro drc; + drc.load_from (rs); + EXPECT_EQ (drc.run (), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::Reader reader (stream); + reader.read (layout); + } + + db::compare_layouts (_this, layout, au, db::NoNormalization); +} diff --git a/testdata/drc/drcSimpleTests_120.drc b/testdata/drc/drcSimpleTests_120.drc new file mode 100644 index 000000000..96cb04b97 --- /dev/null +++ b/testdata/drc/drcSimpleTests_120.drc @@ -0,0 +1,52 @@ + +source $drc_test_source +target $drc_test_target + +deep + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +name(l1, "l1") +name(l2, "l2") +name(l3, "l3") + +connect(l1, l2) +connect(l2, l3) + +netlist + +l1_out = polygons +l2_out = polygons +l3_out = polygons + +output_layers = { + "l1" => l1_out, + "l2" => l2_out, + "l3" => l3_out +} + +[ "A", "B", "C" ].each do |n| + + net = l2n_data.netlist.circuit_by_name("TOP").net_by_name(n) + if net + + net.each_subcircuit_pin do |pin| + shapes = l2n_data.shapes_of_pin(pin, RBA::Trans::new(RBA::Vector::new(100, 200))) + shapes.keys.each do |li| + output_layers[l2n_data.layer_name(li)].data.insert(shapes[li]) + end + end + + end + +end + +l1.output(1, 0) +l2.output(2, 0) +l3.output(3, 0) +l1_out.output(101, 0) +l2_out.output(102, 0) +l3_out.output(103, 0) + diff --git a/testdata/drc/drcSimpleTests_120.gds b/testdata/drc/drcSimpleTests_120.gds new file mode 100644 index 000000000..93675cdcd Binary files /dev/null and b/testdata/drc/drcSimpleTests_120.gds differ diff --git a/testdata/drc/drcSimpleTests_121.drc b/testdata/drc/drcSimpleTests_121.drc new file mode 100644 index 000000000..8f043e926 --- /dev/null +++ b/testdata/drc/drcSimpleTests_121.drc @@ -0,0 +1,89 @@ + +source $drc_test_source +target $drc_test_target + +# only works flat: +# deep + +l1 = input(1, 0) +l2 = input(2, 0) +l3 = input(3, 0) + +active = input(10, 0) +poly = input(11, 0) +contact = input(12, 0) + +sd = active - poly +gate = active & poly + +name(l1, "l1") +name(l2, "l2") +name(l3, "l3") +name(sd, "sd") +name(poly, "poly") +name(gate, "gate") +name(contact, "contact") + +mos_ex = RBA::DeviceExtractorMOS3Transistor::new("MOS") +extract_devices(mos_ex, { "SD" => sd, "G" => gate, "P" => poly }) + +connect(contact, poly) +connect(contact, sd) +connect(l1, contact) +connect(l1, l2) +connect(l2, l3) + +netlist + +l1_out = polygons +l2_out = polygons +l3_out = polygons +contact_out = polygons +sd_out = polygons +gate_out = polygons +poly_out = polygons + +output_layers = { + "l1" => l1_out, + "l2" => l2_out, + "l3" => l3_out, + "contact" => contact_out, + "poly" => poly_out, + "gate" => gate_out, + "sd" => sd_out +} + +[ "A", "B", "C" ].each do |n| + + net = l2n_data.netlist.circuit_by_name("TOP").net_by_name(n) + if net + + net.each_terminal do |terminal| + shapes = l2n_data.shapes_of_terminal(terminal, RBA::Trans::new(RBA::Vector::new(100, 200))) + shapes.keys.each do |li| + output_layers[l2n_data.layer_name(li)].data.insert(shapes[li]) + end + end + + end + +end + +l1.output(1, 0) +l2.output(2, 0) +l3.output(3, 0) + +sd.output(10, 0) +poly.output(11, 0) +contact.output(12, 0) +gate.output(13, 0) + +l1_out.output(101, 0) +l2_out.output(102, 0) +l3_out.output(103, 0) + +sd_out.output(110, 0) +poly_out.output(111, 0) +contact_out.output(112, 0) +gate_out.output(113, 0) + diff --git a/testdata/drc/drcSimpleTests_121.gds b/testdata/drc/drcSimpleTests_121.gds new file mode 100644 index 000000000..c70c6c2a8 Binary files /dev/null and b/testdata/drc/drcSimpleTests_121.gds differ diff --git a/testdata/drc/drcSimpleTests_au120.gds b/testdata/drc/drcSimpleTests_au120.gds new file mode 100644 index 000000000..16a682835 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au120.gds differ diff --git a/testdata/drc/drcSimpleTests_au121.gds b/testdata/drc/drcSimpleTests_au121.gds new file mode 100644 index 000000000..5476d6713 Binary files /dev/null and b/testdata/drc/drcSimpleTests_au121.gds differ