From eabf5581865040e6d635262b9a240f6ccc59c71d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 15 Apr 2019 23:24:27 +0200 Subject: [PATCH] netlist exaction: selective net joining with labels Now, a glob pattern can be used to identify the labels which implicitly join nets. Also, net joining now only happens on top level. --- src/db/db/dbCircuit.cc | 5 +- src/db/db/dbDeviceClass.cc | 7 ++ src/db/db/dbDeviceClass.h | 26 ++++++- src/db/db/dbHierNetworkProcessor.cc | 2 +- src/db/db/dbLayoutToNetlist.cc | 4 +- src/db/db/dbLayoutToNetlist.h | 2 +- src/db/db/dbNetlistDeviceClasses.cc | 8 +-- src/db/db/dbNetlistExtractor.cc | 14 ++-- src/db/db/dbNetlistExtractor.h | 2 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 19 +++++- src/db/db/gsiDeclDbNetlist.cc | 9 +++ src/db/unit_tests/dbNetlistCompareTests.cc | 17 ++++- src/db/unit_tests/dbNetlistExtractorTests.cc | 14 +++- src/db/unit_tests/dbNetlistTests.cc | 4 +- src/drc/drc/built-in-macros/drc.lym | 37 +++++++++- src/drc/unit_tests/drcSimpleTests.cc | 38 +++++++++++ testdata/drc/drcSimpleTests_12.drc | 66 ++++++++++++++++++ testdata/drc/drcSimpleTests_au12a.cir | 68 +++++++++++++++++++ testdata/drc/implicit_nets.gds | Bin 0 -> 3276 bytes testdata/ruby/dbNetlist.rb | 4 ++ 20 files changed, 316 insertions(+), 30 deletions(-) create mode 100644 testdata/drc/drcSimpleTests_12.drc create mode 100644 testdata/drc/drcSimpleTests_au12a.cir create mode 100644 testdata/drc/implicit_nets.gds diff --git a/src/db/db/dbCircuit.cc b/src/db/db/dbCircuit.cc index 54c6f909a..2b5b6a09d 100644 --- a/src/db/db/dbCircuit.cc +++ b/src/db/db/dbCircuit.cc @@ -30,7 +30,7 @@ namespace db // Circuit class implementation Circuit::Circuit () - : mp_netlist (0), + : m_cell_index (0), mp_netlist (0), m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), @@ -45,7 +45,7 @@ Circuit::Circuit () } Circuit::Circuit (const Circuit &other) - : gsi::ObjectBase (other), tl::Object (other), mp_netlist (0), + : gsi::ObjectBase (other), tl::Object (other), m_cell_index (0), mp_netlist (0), m_device_by_id (this, &Circuit::begin_devices, &Circuit::end_devices), m_subcircuit_by_id (this, &Circuit::begin_subcircuits, &Circuit::end_subcircuits), m_net_by_cluster_id (this, &Circuit::begin_nets, &Circuit::end_nets), @@ -80,6 +80,7 @@ Circuit &Circuit::operator= (const Circuit &other) clear (); m_name = other.m_name; + m_cell_index = other.m_cell_index; m_pins = other.m_pins; std::map device_table; diff --git a/src/db/db/dbDeviceClass.cc b/src/db/db/dbDeviceClass.cc index 0d5401cb6..b42ddfcd0 100644 --- a/src/db/db/dbDeviceClass.cc +++ b/src/db/db/dbDeviceClass.cc @@ -118,6 +118,7 @@ DeviceClass &DeviceClass::operator= (const DeviceClass &other) m_parameter_definitions = other.m_parameter_definitions; m_name = other.m_name; m_description = other.m_description; + mp_pc_delegate.reset (const_cast (other.mp_pc_delegate.get ())); } return *this; } @@ -228,6 +229,9 @@ bool DeviceClass::less (const db::Device &a, const db::Device &b) const std::vector &pd = a.device_class ()->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (! p->is_primary ()) { + continue; + } int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, relative_tolerance); if (cmp != 0) { return cmp < 0; @@ -255,6 +259,9 @@ bool DeviceClass::equal (const db::Device &a, const db::Device &b) const std::vector &pd = a.device_class ()->parameter_definitions (); for (std::vector::const_iterator p = pd.begin (); p != pd.end (); ++p) { + if (! p->is_primary ()) { + continue; + } int cmp = compare_parameters (a.parameter_value (p->id ()), b.parameter_value (p->id ()), 0.0, relative_tolerance); if (cmp != 0) { return false; diff --git a/src/db/db/dbDeviceClass.h b/src/db/db/dbDeviceClass.h index bece69f4d..6b0388a79 100644 --- a/src/db/db/dbDeviceClass.h +++ b/src/db/db/dbDeviceClass.h @@ -124,7 +124,7 @@ public: * @brief Creates an empty device parameter definition */ DeviceParameterDefinition () - : m_name (), m_description (), m_default_value (0.0), m_id (0) + : m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true) { // .. nothing yet .. } @@ -132,8 +132,8 @@ public: /** * @brief Creates a device parameter definition with the given name and description */ - DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0) - : m_name (name), m_description (description), m_default_value (default_value), m_id (0) + DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true) + : m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary) { // .. nothing yet .. } @@ -186,6 +186,25 @@ public: m_default_value = d; } + /** + * @brief Sets a value indicating whether the parameter is a primary parameter + * + * If this flag is set to true (the default), the parameter is considered a primary parameter. + * Only primary parameters are compared by default. + */ + void set_is_primary (bool p) + { + m_is_primary = p; + } + + /** + * @brief Gets a value indicating whether the parameter is a primary parameter + */ + bool is_primary () const + { + return m_is_primary; + } + /** * @brief Gets the parameter ID */ @@ -200,6 +219,7 @@ private: std::string m_name, m_description; double m_default_value; size_t m_id; + bool m_is_primary; void set_id (size_t id) { diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index a1016b750..593f031c2 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1769,7 +1769,7 @@ hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layou tl::RelativeProgress progress (tl::to_string (tr ("Computing local clusters")), called.size (), 1); for (std::set::const_iterator c = called.begin (); c != called.end (); ++c) { - build_local_cluster (layout, layout.cell (*c), shape_flags, conn, attr_equivalence); + build_local_cluster (layout, layout.cell (*c), shape_flags, conn, *c == cell.cell_index () ? attr_equivalence : 0); ++progress; } } diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 6b610fefa..b9bb376ca 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -245,7 +245,7 @@ size_t LayoutToNetlist::global_net_id (const std::string &name) return m_conn.global_net_id (name); } -void LayoutToNetlist::extract_netlist (bool join_nets_by_label) +void LayoutToNetlist::extract_netlist (const std::string &joined_net_names) { if (m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); @@ -255,7 +255,7 @@ void LayoutToNetlist::extract_netlist (bool join_nets_by_label) } db::NetlistExtractor netex; - netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters, join_nets_by_label); + netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters, joined_net_names); m_netlist_extracted = true; } diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index e3f9436fe..5003deb03 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -296,7 +296,7 @@ public: * @brief Runs the netlist extraction * See the class description for more details. */ - void extract_netlist (bool join_nets_by_label = true); + void extract_netlist (const std::string &joined_net_names = std::string ()); /** * @brief Marks the netlist as extracted diff --git a/src/db/db/dbNetlistDeviceClasses.cc b/src/db/db/dbNetlistDeviceClasses.cc index 05a1a59c8..1a15a0df3 100644 --- a/src/db/db/dbNetlistDeviceClasses.cc +++ b/src/db/db/dbNetlistDeviceClasses.cc @@ -214,10 +214,10 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor () add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0)); add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0)); - add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0)); + add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false)); + add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false)); + add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false)); + add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false)); } bool DeviceClassMOS3Transistor::combine_devices (Device *a, Device *b) const diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 40cd6439f..4ae5449da 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -23,6 +23,7 @@ #include "dbNetlistExtractor.h" #include "dbDeepShapeStore.h" #include "dbNetlistDeviceExtractor.h" +#include "tlGlobPattern.h" namespace db { @@ -34,15 +35,18 @@ NetlistExtractor::NetlistExtractor () } static void -build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, tl::equivalence_clusters &eq) +build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, const std::string &joined_net_names, tl::equivalence_clusters &eq) { std::map > prop_by_name; + tl::GlobPattern jn_pattern (joined_net_names); for (db::PropertiesRepository::iterator i = layout->properties_repository ().begin (); i != layout->properties_repository ().end (); ++i) { for (db::PropertiesRepository::properties_set::const_iterator p = i->second.begin (); p != i->second.end (); ++p) { if (p->first == net_name_id) { std::string nn = p->second.to_string (); - prop_by_name [nn].insert (i->first); + if (jn_pattern.match (nn)) { + prop_by_name [nn].insert (i->first); + } } } } @@ -58,7 +62,7 @@ build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type } void -NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label) +NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, const std::string &joined_net_names) { mp_clusters = &clusters; mp_layout = &dss.const_layout (layout_index); @@ -77,8 +81,8 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo // the big part: actually extract the nets tl::equivalence_clusters net_name_equivalence; - if (m_text_annot_name_id.first && join_nets_by_label) { - build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, net_name_equivalence); + if (m_text_annot_name_id.first && ! joined_net_names.empty ()) { + build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, joined_net_names, net_name_equivalence); } mp_clusters->build (*mp_layout, *mp_cell, db::ShapeIterator::Polygons, conn, &net_name_equivalence); diff --git a/src/db/db/dbNetlistExtractor.h b/src/db/db/dbNetlistExtractor.h index edbe6a1c6..6eba650f2 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -82,7 +82,7 @@ public: * @brief Extract the nets * See the class description for more details. */ - void extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, bool join_nets_by_label = true); + void extract_nets (const db::DeepShapeStore &dss, unsigned int layout_index, const db::Connectivity &conn, db::Netlist &nl, hier_clusters_type &clusters, const std::string &joined_net_names = std::string ()); private: hier_clusters_type *mp_clusters; diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 78860e07c..070980970 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -288,10 +288,23 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", gsi::method ("global_net_name", &db::LayoutToNetlist::global_net_name, gsi::arg ("global_net_id"), "@brief Gets the global net name for the given global net ID." ) + - gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_nets_by_label", true), + gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_net_names", std::string ()), "@brief Runs the netlist extraction\n" - "If join_nets_by_label is true, nets on the same hierarchy level carrying the same label will be connected " - "implicitly even if there is no physical connection.\n" + "'join_net_names' is a glob expression for labels. Nets on top level carrying the same label which matches this glob " + "expression will be connected implicitly even if there is no physical connection. This feature is useful to simulate a connection " + "which will be made later when integrating the component.\n" + "\n" + "Valid glob expressions are:\n" + "@ul\n" + "@li \"\" no implicit connections.@/li\n" + "@li \"*\" to make all labels candidates for implicit connections.@/li\n" + "@li \"VDD\" to make all 'VDD'' nets candidates for implicit connections.@/li\n" + "@li \"VDD\" to make all 'VDD'+suffix nets candidates for implicit connections.@/li\n" + "@li \"{VDD,VSS}\" to all VDD and VSS nets candidates for implicit connections.@/li\n" + "@/ul\n" + "\n" + "Label matching is case sensitive.\n" + "\n" "See the class description for more details.\n" ) + gsi::method_ext ("internal_layout", &l2n_internal_layout, diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index e65c03e04..244f87168 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -462,6 +462,15 @@ Class decl_dbDeviceParameterDefinition ("db", "De "@brief Sets the default value of the parameter.\n" "The default value is used to initialize parameters of \\Device objects." ) + + gsi::method ("is_primary?", &db::DeviceParameterDefinition::is_primary, + "@brief Gets a value indicating whether the parameter is a primary parameter\n" + "See \\is_primary= for details about this predicate." + ) + + gsi::method ("is_primary=", &db::DeviceParameterDefinition::set_is_primary, gsi::arg ("primary"), + "@brief Sets a value indicating whether the parameter is a primary parameter\n" + "If this flag is set to true (the default), the parameter is considered a primary parameter.\n" + "Only primary parameters are compared by default.\n" + ) + gsi::method ("id", &db::DeviceParameterDefinition::id, "@brief Gets the ID of the parameter.\n" "The ID of the parameter is used in some places to refer to a specific parameter (e.g. in " diff --git a/src/db/unit_tests/dbNetlistCompareTests.cc b/src/db/unit_tests/dbNetlistCompareTests.cc index 98792743f..289bec5c5 100644 --- a/src/db/unit_tests/dbNetlistCompareTests.cc +++ b/src/db/unit_tests/dbNetlistCompareTests.cc @@ -228,6 +228,17 @@ TEST(0_EqualDeviceParameters) EXPECT_EQ (dc.less (d1, d2), false); EXPECT_EQ (dc.less (d2, d1), false); + // AD, AS, PD and PS aren't a primary parameter, so we don't compare it. + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 1.0); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 1.0); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_AD, 1.0); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_PS, 1.0); + + EXPECT_EQ (dc.equal (d1, d2), true); + EXPECT_EQ (dc.equal (d2, d1), true); + EXPECT_EQ (dc.less (d1, d2), false); + EXPECT_EQ (dc.less (d2, d1), false); + d2.set_parameter_value (db::DeviceClassMOS3Transistor::param_id_L, 41.0); EXPECT_EQ (dc.equal (d1, d2), false); @@ -1837,10 +1848,10 @@ TEST(15_EmptySubCircuitTest) "end_circuit TRANS TRANS MATCH\n" "begin_circuit INV2 INV2\n" "match_nets $5 $5\n" - "match_nets OUT OUT\n" "match_nets $2 $2\n" "match_nets IN IN\n" "match_nets $4 $4\n" + "match_nets OUT OUT\n" "match_pins IN IN\n" "match_pins $1 $1\n" "match_pins OUT OUT\n" @@ -1912,10 +1923,10 @@ TEST(15_EmptySubCircuitWithoutPinNames) "end_circuit TRANS TRANS MATCH\n" "begin_circuit INV2 INV2\n" "match_nets $5 $5\n" - "match_nets OUT OUT\n" "match_nets $2 $2\n" "match_nets IN IN\n" "match_nets $4 $4\n" + "match_nets OUT OUT\n" "match_pins IN IN\n" "match_pins $1 $1\n" "match_pins OUT OUT\n" @@ -1988,8 +1999,8 @@ TEST(16_UniqueSubCircuitMatching) "match_nets VDD VDD\n" "match_nets OUT OUT\n" "match_nets $3 $3\n" - "match_nets $1 $1\n" "match_nets IN IN\n" + "match_nets $1 $1\n" "match_nets VSS VSS\n" "match_nets BULK BULK\n" "match_pins $0 $0\n" diff --git a/src/db/unit_tests/dbNetlistExtractorTests.cc b/src/db/unit_tests/dbNetlistExtractorTests.cc index f3a8cd1f7..b9e67104d 100644 --- a/src/db/unit_tests/dbNetlistExtractorTests.cc +++ b/src/db/unit_tests/dbNetlistExtractorTests.cc @@ -483,7 +483,7 @@ TEST(2_DeviceAndNetExtractionFlat) // extract the nets // don't use "join_nets_by_label" because the flattened texts will spoil everything - net_ex.extract_nets (dss, 0, conn, nl, cl, false); + net_ex.extract_nets (dss, 0, conn, nl, cl); // debug layers produced for nets // 202/0 -> Active @@ -716,7 +716,17 @@ TEST(3_DeviceAndNetExtractionWithImplicitConnections) // extract the nets - net_ex.extract_nets (dss, 0, conn, nl, cl); + db::Netlist nl2 = nl; + net_ex.extract_nets (dss, 0, conn, nl2, cl, "{VDDZ,VSSZ,NEXT,FB}"); + + EXPECT_EQ (all_net_names_unique (nl2), true); + + nl2 = nl; + net_ex.extract_nets (dss, 0, conn, nl2, cl, "{VDDZ,VSSZ,NEXT}"); + + EXPECT_EQ (all_net_names_unique (nl2), false); + + net_ex.extract_nets (dss, 0, conn, nl, cl, "*"); EXPECT_EQ (all_net_names_unique (nl), true); diff --git a/src/db/unit_tests/dbNetlistTests.cc b/src/db/unit_tests/dbNetlistTests.cc index 3ab084247..4df092672 100644 --- a/src/db/unit_tests/dbNetlistTests.cc +++ b/src/db/unit_tests/dbNetlistTests.cc @@ -259,11 +259,13 @@ TEST(1_DeviceTerminalDefinition) dc.clear_terminal_definitions (); EXPECT_EQ (dc.terminal_definitions ().empty (), true); - db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0); + db::DeviceParameterDefinition ppd ("P1", "Parameter 1", 1.0, false); dc.add_parameter_definition (ppd); + EXPECT_EQ (ppd.is_primary (), false); db::DeviceParameterDefinition ppd2 ("P2", "Parameter 2"); dc.add_parameter_definition (ppd2); + EXPECT_EQ (ppd.is_primary (), true); EXPECT_EQ (pd2string (dc.parameter_definitions ()[0]), "P1(Parameter 1)=1 #0"); EXPECT_EQ (pd2string (dc.parameter_definitions ()[1]), "P2(Parameter 2)=0 #1"); diff --git a/src/drc/drc/built-in-macros/drc.lym b/src/drc/drc/built-in-macros/drc.lym index bb82ea4f4..e869a76c4 100644 --- a/src/drc/drc/built-in-macros/drc.lym +++ b/src/drc/drc/built-in-macros/drc.lym @@ -3804,9 +3804,36 @@ CODE @connections = [] @global_connections = [] @layers = {} + @join_nets = "" modified end + # %DRC% + # @name join_nets + # @brief Specifies a search pattern for labels which create implicit net connections + # @synopsis join_nets(label_pattern) + # Use this method to supply a glob pattern for labels which create implicit net connections + # on the top level circuit. This feature is useful to connect identically labelled nets + # while a component isn't integrated yet. If the component is integrated, net may be connected + # on a higher hierarchy level - e.g. by a power mesh. Inside the component this net consists + # of individual islands. To properly perform netlist extraction and comparison, these islands + # need to be connected even though there isn't a physical connection. "join_nets" can + # achive this if these islands are labelled with the same text on the top level of the + # component. + # + # Glob pattern are used which resemble shell file pattern: "*" is for all labels, "VDD" + # for all "VDD" labels (pattern act case sensitive). "VDD*" is for all labels beginning + # with "VDD" (still different labels will be connected to different nets!). "{VDD,VSS}" + # is either "VDD" or "VSS". + # + # The search pattern is applied on the next net extraction. The search pattern is cleared + # on "clear_connections". + + def join_nets(arg) + @join_nets = arg + modified + end + # %DRC% # @brief Performs an antenna check # @name antenna_check @@ -3957,7 +3984,7 @@ CODE @global_connections.each { |l,n| @l2n.connect_global(@layers[l], n) } # run extraction in a timed environment - @engine._cmd(@l2n, :extract_netlist) + @engine._cmd(@l2n, :extract_netlist, @join_nets) @l2n end @@ -4922,6 +4949,12 @@ CODE # @synopsis clear_connections # See \Netter#clear_connections for a description of that function + # %DRC% + # @name join_nets + # @brief Specifies a label pattern for implicit net connections + # @synopsis join_nets(label_pattern) + # See \Netter#join_nets for a description of that function + # %DRC% # @name antenna_check # @brief Performs an antenna check @@ -4940,7 +4973,7 @@ CODE # @synopsis extract_devices(extractor, layer_hash) # See \Netter#extract_devices for a description of that function - %w(connect connect_global clear_connections antenna_check l2n_data extract_devices).each do |f| + %w(connect connect_global clear_connections join_nets antenna_check l2n_data extract_devices).each do |f| eval <<"CODE" def #{f}(*args) _netter.#{f}(*args) diff --git a/src/drc/unit_tests/drcSimpleTests.cc b/src/drc/unit_tests/drcSimpleTests.cc index 6d27bd9ae..c3a0a68c5 100644 --- a/src/drc/unit_tests/drcSimpleTests.cc +++ b/src/drc/unit_tests/drcSimpleTests.cc @@ -352,11 +352,13 @@ static void compare_netlists (tl::TestBase *_this, const std::string &cir, const db::NetlistSpiceReader reader; { + tl::info << "Output: " << cir; tl::InputStream is (cir); reader.read (is, nl); } { + tl::info << "Golden: " << cir_au; tl::InputStream is (cir_au); reader.read (is, nl_au); } @@ -495,3 +497,39 @@ TEST(11_CustomDevices) CHECKPOINT (); compare_netlists (_this, output_simplified, au_simplified); } + +TEST(12_NetlistJoinLabels) +{ + std::string rs = tl::testsrc (); + rs += "/testdata/drc/drcSimpleTests_12.drc"; + + std::string input = tl::testsrc (); + input += "/testdata/drc/implicit_nets.gds"; + + std::string au = tl::testsrc (); + au += "/testdata/drc/drcSimpleTests_au12a.cir"; + + std::string output = this->tmp_file ("tmp.cir"); + + { + // Set some variables + lym::Macro config; + config.set_text (tl::sprintf ( + "$drc_test_source = '%s'\n" + "$drc_test_target = '%s'\n" + "$drc_test_target_simplified = nil\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); + + // verify + + CHECKPOINT (); + compare_netlists (_this, output, au); +} diff --git a/testdata/drc/drcSimpleTests_12.drc b/testdata/drc/drcSimpleTests_12.drc new file mode 100644 index 000000000..0792169a5 --- /dev/null +++ b/testdata/drc/drcSimpleTests_12.drc @@ -0,0 +1,66 @@ +# Flat extraction + +source($drc_test_source) + +deep + +# Drawing layers + +nwell = input(1, 0) +active = input(2, 0) +poly = input(3, 0) +poly_lbl = input(3, 1) +diff_cont = input(4, 0) +poly_cont = input(5, 0) +metal1 = input(6, 0) +metal1_lbl = input(6, 1) +via1 = input(7, 0) +metal2 = input(8, 0) +metal2_lbl = input(8, 1) + +# Computed layers + +pactive = active & nwell +pgate = active & poly +psd = active - pgate + +nactive = active - nwell +ngate = nactive & poly +nsd = nactive - ngate + +# PMOS transistor device extraction + +pmos_ex = RBA::DeviceExtractorMOS3Transistor::new("PMOS") +extract_devices(pmos_ex, { "SD" => psd, "G" => pgate, "P" => poly }) + +# NMOS transistor device extraction + +nmos_ex = RBA::DeviceExtractorMOS3Transistor::new("NMOS") +extract_devices(nmos_ex, { "SD" => nsd, "G" => ngate, "P" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, diff_cont) +connect(nsd, diff_cont) +connect(poly, poly_cont) +connect(poly_cont, metal1) +connect(diff_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Actually performs the extraction + +join_nets("{VDDZ,VSSZ,NEXT,FB}") + +netlist = l2n_data.netlist + +# Writes the netlist + +writer = RBA::NetlistSpiceWriter::new + +netlist.write($drc_test_target, writer, "RINGO netlist") + diff --git a/testdata/drc/drcSimpleTests_au12a.cir b/testdata/drc/drcSimpleTests_au12a.cir new file mode 100644 index 000000000..b459b2a77 --- /dev/null +++ b/testdata/drc/drcSimpleTests_au12a.cir @@ -0,0 +1,68 @@ +* RINGO netlist + +* cell RINGO +.SUBCKT RINGO +* net 1 FB +* net 2 OSC +* net 3 NEXT +* net 4 VSSZ,VSS +* net 5 VDDZ,VDD +* cell instance $1 r180 *1 -0.24,9.18 +X$1 16 1 2 4 5 INV2 +* cell instance $2 r0 *1 0,0 +X$2 1 14 15 4 5 INV2 +* cell instance $3 r180 *1 10.32,9.18 +X$3 3 9 19 4 5 INV2 +* cell instance $4 r0 *1 10.56,0 +X$4 20 10 3 4 5 INV2 +* cell instance $5 r180 *1 7.68,9.18 +X$5 19 8 18 4 5 INV2 +* cell instance $6 r180 *1 5.04,9.18 +X$6 18 7 17 4 5 INV2 +* cell instance $7 r180 *1 2.4,9.18 +X$7 17 6 16 4 5 INV2 +* cell instance $8 r0 *1 2.64,0 +X$8 15 13 22 4 5 INV2 +* cell instance $9 r0 *1 5.28,0 +X$9 22 12 21 4 5 INV2 +* cell instance $10 r0 *1 7.92,0 +X$10 21 11 20 4 5 INV2 +.ENDS RINGO + +* cell INV2 +* pin IN +* pin +* pin OUT +* pin +* pin +.SUBCKT INV2 1 2 3 4 5 +* net 1 IN +* net 3 OUT +* cell instance $1 r0 *1 -0.4,0 +X$1 2 4 1 TRANS +* cell instance $2 r0 *1 -0.4,2.8 +X$2 2 5 1 TRANS +* cell instance $3 m0 *1 0.4,2.8 +X$3 5 3 2 TRANS +* cell instance $4 m0 *1 0.4,0 +X$4 4 3 2 TRANS +* device instance $1 -0.4,0 PMOS +M$1 2 1 4 2 PMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U +* device instance $2 0.4,0 PMOS +M$2 4 2 3 4 PMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U +* device instance $3 -0.4,2.8 PMOS +M$3 2 1 5 2 PMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U +* device instance $4 0.4,2.8 PMOS +M$4 5 2 3 5 PMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U +* device instance $5 -0.4,0 NMOS +M$5 2 1 4 2 NMOS L=0.25U W=0.95U AS=0.49875P AD=0.26125P PS=2.95U PD=1.5U +* device instance $6 0.4,0 NMOS +M$6 4 2 3 4 NMOS L=0.25U W=0.95U AS=0.26125P AD=0.49875P PS=1.5U PD=2.95U +.ENDS INV2 + +* cell TRANS +* pin +* pin +* pin +.SUBCKT TRANS 1 2 3 +.ENDS TRANS diff --git a/testdata/drc/implicit_nets.gds b/testdata/drc/implicit_nets.gds new file mode 100644 index 0000000000000000000000000000000000000000..0b8e3faf949ab06c07f009651db362b27729fa00 GIT binary patch literal 3276 zcmbtWOK2295Uriv?fqocs8NX~!A}$gjUaxIfM|?Lh$Nbk9 zF5zUpl0!*aM0r{h{!dxQ>~Cu!s!67I?7F_K{oSMS?X4pl2k-S2sV*JO%NtYd+SSob zl*$sBIS%$Wa~&dDU!8Q_sWYxyUd>39_(bH~P*gi3rRE3xRG43Ous(_}&>aGP2~h3j z(KtRb>0>_PZe@OeYNx&^K73b#Gb-o~0m~d;Q0>w2naA-36I+>wua9c4h;PE@pL&h> zS5Uv#;GIY8Mn$zVTKR|gi2d1H2Qm-!Z&g%#z|Tqll74)%g1eZVm&9+0YN{E@b$^aA z5+#>Q)C|SWN7k6Oj-E}F$`j=m=&TfYHp-{GjlAAZznUMH7}ly;nz%%S9dMG{5j#}) z=sx}lwL+m2CQk6YdZ!|Kk6}EDoqfz;uyI;a%=Jgr8`W8la~)wM%JrjfcNNvn7{eb! ztj$v8)P3ofqS}+2Exz_k?%M=r#31ejTF3Y1yQ10|qxh)kLT??&Jj9=)sP=e1&l&Rb z=1ucfRC`?hKaOAsi>}L<;n~NGD5~8Qt^Bj$`%=0F&JNctUBg-45T(y5s+}>8kA5`? zzwk`>ifYd_S^R_@pXWsQb3Y1SQSF$cu->HuK5|`&`kfO@^#Gw4ifRW&@KLu1sNaBK z2HIP?hvF^)v~;x8S3?04YIYS^H32D z;-}XPI*8|FS6j!vPIyAivI|5NJrzTi{81gW*766U*J=i%*BZv7*VgTvb`6>dd!a7f z^1@s9HFhmBSoQgj>>|7yK%Kq!!l!@ZojW>vZ%jaS;0*!iHxt!;Pbn*vru0^nhOts< z{7I1+!19s=;nqsMmZf*DIqrRAf=YNo%LMIhnP!4w+0ffwqK30PfB(YXFo-dFN|?)z zJ-xn4T2nql^rk0xn18eRe(fxkom>mQg6lHY-^Z;P2~h2I1?%+S z-Tqsdpa1OmsC_MN7x=BM|G=NbtXCEPk59+!{Qde_D*NLO3UL!E4a{7{xo-uic5_qe z_a`5U`W*E?ELeXKQ}Y=0_fhS@@KeHyKY5z@=pV5DAo_PQK(#C1sz07})2Re@8pn