diff --git a/src/db/db/dbHierNetworkProcessor.cc b/src/db/db/dbHierNetworkProcessor.cc index 721d17aac..c19f12dc1 100644 --- a/src/db/db/dbHierNetworkProcessor.cc +++ b/src/db/db/dbHierNetworkProcessor.cc @@ -1103,6 +1103,9 @@ private: // ------------------------------------------------------------------------------ // hier_clusters implementation +template +const db::cell_index_type hier_clusters::top_cell_index = std::numeric_limits::max (); + template hier_clusters::hier_clusters () : m_base_verbosity (20) @@ -1124,7 +1127,7 @@ void hier_clusters::clear () template void -hier_clusters::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence, const std::set *breakout_cells) +hier_clusters::build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const std::map > *attr_equivalence, const std::set *breakout_cells) { clear (); cell_clusters_box_converter cbc (layout, *this); @@ -1903,7 +1906,7 @@ hier_clusters::propagate_cluster_inst (const db::Layout &layout, const db::Ce template void -hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence, const std::set *breakout_cells) +hier_clusters::do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const std::map > *attr_equivalence, const std::set *breakout_cells) { tl::SelfTimer timer (tl::verbosity () > m_base_verbosity, tl::to_string (tr ("Computing shape clusters"))); @@ -1918,8 +1921,30 @@ 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, *c == cell.cell_index () ? attr_equivalence : 0); + + // look for the net label joining spec - for the top cell the "top_cell_index" entry is looked for. + // If there is no such entry or the cell is not the top cell, look for the entry by cell index. + std::map >::const_iterator ae; + const tl::equivalence_clusters *ec = 0; + if (attr_equivalence) { + if (*c == cell.cell_index ()) { + ae = attr_equivalence->find (top_cell_index); + if (ae != attr_equivalence->end ()) { + ec = &ae->second; + } + } + if (! ec) { + ae = attr_equivalence->find (*c); + if (ae != attr_equivalence->end ()) { + ec = &ae->second; + } + } + } + + build_local_cluster (layout, layout.cell (*c), shape_flags, conn, ec); + ++progress; + } } diff --git a/src/db/db/dbHierNetworkProcessor.h b/src/db/db/dbHierNetworkProcessor.h index fda6e07e8..057e2e649 100644 --- a/src/db/db/dbHierNetworkProcessor.h +++ b/src/db/db/dbHierNetworkProcessor.h @@ -986,10 +986,15 @@ public: */ void set_base_verbosity (int bv); + /** + * @brief A constant indicating the top cell for the equivalence cluster key + */ + static const db::cell_index_type top_cell_index; + /** * @brief Builds a hierarchy of clusters from a cell hierarchy and given connectivity */ - void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence = 0, const std::set *breakout_cells = 0); + void build (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const std::map > *attr_equivalence = 0, const std::set *breakout_cells = 0); /** * @brief Gets the connected clusters for a given cell @@ -1030,7 +1035,7 @@ private: void build_local_cluster (const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence); void build_hier_connections (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, const db::Connectivity &conn, const std::set *breakout_cells, instance_interaction_cache_type &instance_interaction_cache); void build_hier_connections_for_cells (cell_clusters_box_converter &cbc, const db::Layout &layout, const std::vector &cells, const db::Connectivity &conn, const std::set *breakout_cells, tl::RelativeProgress &progress, instance_interaction_cache_type &instance_interaction_cache); - void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const tl::equivalence_clusters *attr_equivalence, const std::set *breakout_cells); + void do_build (cell_clusters_box_converter &cbc, const db::Layout &layout, const db::Cell &cell, db::ShapeIterator::flags_type shape_flags, const db::Connectivity &conn, const std::map > *attr_equivalence, const std::set *breakout_cells); std::map > m_per_cell_clusters; int m_base_verbosity; diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 21a08732e..3ee07e8c7 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -31,6 +31,7 @@ #include "dbLayoutVsSchematic.h" #include "dbLayoutToNetlistFormatDefs.h" #include "dbLayoutVsSchematicFormatDefs.h" +#include "tlGlobPattern.h" namespace db { @@ -295,6 +296,11 @@ size_t LayoutToNetlist::global_net_id (const std::string &name) } void LayoutToNetlist::extract_netlist (const std::string &joined_net_names, bool include_floating_subcircuits) +{ + extract_netlist (joined_net_names, std::map (), include_floating_subcircuits); +} + +void LayoutToNetlist::extract_netlist (const std::string &joined_net_names, const std::map &joined_net_names_per_cell, bool include_floating_subcircuits) { if (m_netlist_extracted) { throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted"))); @@ -302,7 +308,23 @@ void LayoutToNetlist::extract_netlist (const std::string &joined_net_names, bool ensure_netlist (); db::NetlistExtractor netex; + netex.set_joined_net_names (joined_net_names); + + const db::Layout &layout = dss ().layout (m_layout_index); + for (std::map::const_iterator j = joined_net_names_per_cell.begin (); j != joined_net_names_per_cell.end (); ++j) { + tl::GlobPattern pat (j->first); + if (pat.is_const ()) { + netex.set_joined_net_names (j->first, j->second); + } else { + for (db::Layout::const_iterator c = layout.begin (); c != layout.end (); ++c) { + if (pat.match (layout.cell_name (c->cell_index ()))) { + netex.set_joined_net_names (layout.cell_name (c->cell_index ()), j->second); + } + } + } + } + netex.set_include_floating_subcircuits (include_floating_subcircuits); netex.extract_nets (dss (), m_layout_index, m_conn, *mp_netlist, m_net_clusters); diff --git a/src/db/db/dbLayoutToNetlist.h b/src/db/db/dbLayoutToNetlist.h index 57cc2bdee..6bf48322f 100644 --- a/src/db/db/dbLayoutToNetlist.h +++ b/src/db/db/dbLayoutToNetlist.h @@ -391,6 +391,16 @@ public: */ void extract_netlist (const std::string &joined_net_names = std::string (), bool include_floating_subcircuits = false); + /** + * @brief Runs the netlist extraction + * In addition to the previous version, this extraction method allows specification of a per-cell list of + * joined (labelled) net names. + * The key of the "joined_net_names_per_cell" is a cell name or a glob expression for cells. On all matching cells, + * the value is applied as a label selector for labels that are joined together. The "joined_net_names" expressions + * is only applied to the top cell. + */ + void extract_netlist (const std::string &joined_net_names, const std::map &joined_net_names_per_cell, bool include_floating_subcircuits = false); + /** * @brief Marks the netlist as extracted * NOTE: this method is provided for special cases such as netlist readers. Don't diff --git a/src/db/db/dbNetlistExtractor.cc b/src/db/db/dbNetlistExtractor.cc index 44f1eac29..15a40ab24 100644 --- a/src/db/db/dbNetlistExtractor.cc +++ b/src/db/db/dbNetlistExtractor.cc @@ -39,6 +39,11 @@ void NetlistExtractor::set_joined_net_names (const std::string &jnn) m_joined_net_names = jnn; } +void NetlistExtractor::set_joined_net_names (const std::string &cellname, const std::string &jnn) +{ + m_joined_net_names_per_cell.push_back (std::make_pair (cellname, jnn)); +} + void NetlistExtractor::set_include_floating_subcircuits (bool f) { m_include_floating_subcircuits = f; @@ -90,9 +95,17 @@ 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 && ! m_joined_net_names.empty ()) { - build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, m_joined_net_names, net_name_equivalence); + std::map > net_name_equivalence; + if (m_text_annot_name_id.first) { + if (! m_joined_net_names.empty ()) { + build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, m_joined_net_names, net_name_equivalence [hier_clusters_type::top_cell_index]); + } + for (std::list >::const_iterator m = m_joined_net_names_per_cell.begin (); m != m_joined_net_names_per_cell.end (); ++m) { + std::pair cp = mp_layout->cell_by_name (m->first.c_str ()); + if (cp.first) { + build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, m->second, net_name_equivalence [cp.second]); + } + } } 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 8cd0a64dc..ab3c1eb25 100644 --- a/src/db/db/dbNetlistExtractor.h +++ b/src/db/db/dbNetlistExtractor.h @@ -106,6 +106,13 @@ public: */ void set_joined_net_names (const std::string &jnn); + /** + * @brief Sets the joined net names attribute for a given cell name + * While the single-parameter set_joined_net_names only acts on the top cell, this + * version will act on the cell with the given name. + */ + void set_joined_net_names (const std::string &cell_name, const std::string &jnn); + /** * @brief Gets the joined net names expression */ @@ -128,6 +135,7 @@ private: std::pair m_device_annot_name_id; std::pair m_terminal_annot_name_id; std::string m_joined_net_names; + std::list > m_joined_net_names_per_cell; bool m_include_floating_subcircuits; bool instance_is_device (db::properties_id_type prop_id) const; diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index 5f53b5898..eab15bca5 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -355,6 +355,17 @@ Class decl_dbLayoutToNetlist ("db", "LayoutToNetlist", "\n" "The 'include_floating_subcircuits' argument has been introduced in version 0.26.2." ) + + gsi::method ("extract_netlist", &db::LayoutToNetlist::extract_netlist, gsi::arg ("join_net_names"), gsi::arg ("join_net_names_per_cell"), gsi::arg ("include_floating_subcircuits", false), + "@brief Runs the netlist extraction\n" + "This method runs the netlist extraction like the two-parameter version. In addition to the latter, this method " + "can be given a per-cell net label joining specification in 'join_net_names_per_cell'. The keys of this array " + "are cell names or cell names or cell name match expressions (glob style). The values are lable match expressions.\n" + "\n" + "If not an empty string, the 'join_net_names' label match expression is applied to the top cell. For all non-top cells " + "the per-cell label match expression is applied and determines what labels are joined into single nets. " + "As the keys of 'join_net_names_per_cell' are glob expressions, a single cell may fall into more than one category. In this " + "case, the label match pattern are combined. In any case, the 'join_net_names' has priority for the top cell." + ) + gsi::method_ext ("internal_layout", &l2n_internal_layout, "@brief Gets the internal layout\n" "Usually it should not be required to obtain the internal layout. If you need to do so, make sure not to modify the layout as\n" diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index acf00645c..b9c49e41f 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -66,6 +66,7 @@ module DRC @engine = engine @netlisted = false @connect_implicit = [] + @connect_implicit_per_cell = {} @l2n = nil @lnum = 0 @device_scaling = 1.0 @@ -203,6 +204,7 @@ module DRC def clear_connections @netlisted = false @connect_implicit = [] + @connect_implicit_per_cell = {} _clear_data end @@ -210,8 +212,9 @@ module DRC # @name connect_implicit # @brief Specifies a search pattern for labels which create implicit net connections # @synopsis connect_implicit(label_pattern) + # @synopsis connect_implicit(cell_pattern, label_pattern) # Use this method to supply label strings which create implicit net connections - # on the top level circuit. This feature is useful to connect identically labelled nets + # on the top level circuit in the first version. This feature is useful to connect identically labelled nets # while a component isn't integrated yet. If the component is integrated, nets 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 @@ -219,12 +222,26 @@ module DRC # achive this if these islands are labelled with the same text on the top level of the # component. # + # In the second version, the pattern can be specified for a cell range (given by a cell name pattern or a + # single cell name). These pattern are applied to non-top cells. The unspecific pattern + # has priority over the cell-specific ones. As the cell selector is a pattern itself, a + # single cell may fall into more than one category. In this case, the label filters are + # combined. + # # The implicit connections are applied on the next net extraction and cleared # on "clear_connections". - def connect_implicit(arg) + def connect_implicit(arg1, arg2 = nil) cleanup - @connect_implicit << arg + if arg2 + (arg2.is_a?(String) && arg2 != "") || raise("The second argument of 'connect_implicit' has to be a non-empty string") + arg1.is_a?(String) || raise("The first argument of 'connect_implicit' has to be a string") + @connect_implicit_per_cell[arg1] ||= [] + @connect_implicit_per_cell[arg1] << arg2 + else + arg1.is_a?(String) || raise("The argument of 'connect_implicit' has to be a string") + @connect_implicit << arg1 + end end # %DRC% @@ -341,15 +358,19 @@ module DRC # run extraction in a timed environment if ! @netlisted + # build a glob expression from the parts - exprs = @connect_implicit.collect { |c| c.gsub(/\?\*\[\]\{\},\(\)\\/) { |x| "\\" + x } } - if exprs.size > 1 - expr = "{" + exprs.join(",") + "}" - else - expr = exprs[0] || "" + expr = _join_glob_pattern(@connect_implicit) + + # build cell-pattern specific glob expressions from the parts + per_cell_expr = {} + @connect_implicit_per_cell.each do |cell_pattern,label_pattern| + per_cell_expr[cell_pattern] = _join_glob_pattern(label_pattern) end - @engine._cmd(@l2n, :extract_netlist, expr) + + @engine._cmd(@l2n, :extract_netlist, expr, per_cell_expr) @netlisted = true + end @l2n @@ -401,6 +422,19 @@ module DRC end end + def _join_glob_pattern(pattern) + + exprs = pattern.collect { |c| c.gsub(/\?\*\[\]\{\},\(\)\\/) { |x| "\\" + x } } + if exprs.size > 1 + expr = "{" + exprs.join(",") + "}" + else + expr = exprs[0] || "" + end + + expr + + end + def _make_data if @engine._dss diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml index 11f97782c..5d22e240b 100644 --- a/src/lay/lay/doc/about/drc_ref_global.xml +++ b/src/lay/lay/doc/about/drc_ref_global.xml @@ -234,6 +234,7 @@ See Netter#connect_global

Usage:

  • connect_implicit(label_pattern)
  • +
  • connect_implicit(cell_pattern, label_pattern)

See Netter#connect_implicit for a description of that function. diff --git a/src/lay/lay/doc/about/drc_ref_netter.xml b/src/lay/lay/doc/about/drc_ref_netter.xml index a131bad28..406d7eb91 100644 --- a/src/lay/lay/doc/about/drc_ref_netter.xml +++ b/src/lay/lay/doc/about/drc_ref_netter.xml @@ -184,17 +184,24 @@ to shapes belonging to tie-down diodes.

Usage:

  • connect_implicit(label_pattern)
  • +
  • connect_implicit(cell_pattern, label_pattern)

-Use this method to supply label strings which create implicit net connections +Use the first version of this method to supply label strings 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, nets 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. "connect_implicit" can -achive this if these islands are labelled with the same text on the top level of the +achieve this if these islands are labelled with the same text on the top level of the component.

+In the second version, the pattern can be specified for a cell range (given by a cell glob-style +name pattern or a single cell name). These pattern are applied to non-top cells. An unspecific pattern +has priority over the cell-specific ones. As the cell selector is a pattern itself, a +single cell may fall into more than one category. In this case, the label filters are +combined. +

The implicit connections are applied on the next net extraction and cleared on "clear_connections".

diff --git a/src/lay/lay/doc/manual/lvs_connect.xml b/src/lay/lay/doc/manual/lvs_connect.xml index e47efff0e..2fb0ff02c 100644 --- a/src/lay/lay/doc/manual/lvs_connect.xml +++ b/src/lay/lay/doc/manual/lvs_connect.xml @@ -131,4 +131,56 @@ connect(metal2, metal2_labels) opens may pass unnoticed.

+

+ You can include labels of a certain class in a "connect_implicit" statement + using glob-style pattern: +

+ +
connect_implicit("VDD*")
+ +

+ This will connect all nets labelled with "VDD1" for example or those labelled + with "VDD_5V". However, this statement will only connect "VDD1" with "VDD1", + not nets with different labels. I.e. it will not connect "VDD1" with "VDD2" + labels. +

+ +

+ "connect_implicit" can be present multiple times. Each statement extends the + choice of labels which will be connected. +

+ +

+ The standard method "connect_implicit" will only act on top-level cells. + However, sometimes the construction of certain library cells requires + connecting nets inside subcells. For example, memory cells are often made + in a way that their common rails are exposed on different sides but + not connected internally. Formally, those cells need to be described by + circuits with multiple pins in the schematic. As the cells are only used + in certain contexts where these rails are connected, it's sufficient to + specify a single pin and connect the rails inside the subcells if labelled + properly. The following statement will connect all nets labelled with "VDD" + from the "MEMCELL" subcell: +

+ +
connect_implicit("MEMCELL", "VDD")
+ +

+ If MEMCELL is the top cell, the single-argument, unspecific "connect_implicit" + rule is applied, unless no such rule is given. In other words: the unspecific + rule has priority for the top cell. +

+ +

+ The cell argument can be a glob-style pattern. In this case, the rule is + applied to all matching cells. Again, the "connect_implicit" rule may be + given multiple times. In this case, all matching occurances act together. +

+ +

+ The "connect_implicit" statements must be given before the netlist is + extracted. Typically this happens before or shortly after "connect" + statements. +

+ diff --git a/src/lvs/unit_tests/lvsSimpleTests.cc b/src/lvs/unit_tests/lvsSimpleTests.cc index 278369787..f8e53abe0 100644 --- a/src/lvs/unit_tests/lvsSimpleTests.cc +++ b/src/lvs/unit_tests/lvsSimpleTests.cc @@ -165,3 +165,15 @@ TEST(18_cheats) run_test (_this, "invchain_cheat", "invchain_for_cheat.gds"); } +// testing cell specific net joining for VSS of the double-height inverter standard cell +TEST(19_double_height_inv) +{ + run_test (_this, "double_height", "double_height_inv.gds"); +} + +// testing cell specific net joining for VSS of the double-height inverter standard cell +TEST(20_double_height2_inv) +{ + run_test (_this, "double_height2", "double_height2_inv.gds"); +} + diff --git a/testdata/lvs/double_height.cir b/testdata/lvs/double_height.cir new file mode 100644 index 000000000..f2def2326 --- /dev/null +++ b/testdata/lvs/double_height.cir @@ -0,0 +1,17 @@ +* Extracted by KLayout + +.SUBCKT INVCHAIN IN OUT VSS VDD +X$1 VDD IN \$1 \$1 OUT VSS INV2 +.ENDS INVCHAIN + +.SUBCKT INV2 VDD A1 A2 Q1 Q2 VSS +X$1 VSS VDD A2 Q2 INV +X$2 VSS VDD A1 Q1 INV +.ENDS INV2 + +.SUBCKT INV \$1 \$2 \$3 \$4 +M$1 \$4 \$3 \$2 \$4 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 \$4 \$3 \$1 \$4 NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS INV diff --git a/testdata/lvs/double_height.lvs b/testdata/lvs/double_height.lvs new file mode 100644 index 000000000..ec154d59f --- /dev/null +++ b/testdata/lvs/double_height.lvs @@ -0,0 +1,130 @@ + +source($lvs_test_source) + +report_lvs($lvs_test_target_lvsdb) + +writer = write_spice(true, false) +target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") + +# needs this delegate because we use MOS3 which is not available in Spice +class SpiceReaderDelegate < RBA::NetlistSpiceReaderDelegate + + # says we want to catch these subcircuits as devices + def wants_subcircuit(name) + name == "HVNMOS" || name == "HVPMOS" + end + + # translate the element + def element(circuit, el, name, model, value, nets, params) + + if el != "M" + # all other elements are left to the standard implementation + return super + end + + if nets.size != 4 + error("Device #{model} needs four nodes") + end + + # provide a device class + cls = circuit.netlist.device_class_by_name(model) + if ! cls + cls = RBA::DeviceClassMOS3Transistor::new + cls.name = model + circuit.netlist.add(cls) + end + + # create a device + device = circuit.create_device(cls, name) + + # and configure the device + [ "S", "G", "D" ].each_with_index do |t,index| + device.connect_terminal(t, nets[index]) + end + device.set_parameter("W", params["W"] * 1e6) + device.set_parameter("L", params["L"] * 1e6) + + device + + end + +end + +reader = RBA::NetlistSpiceReader::new(SpiceReaderDelegate::new) +schematic("double_height_inv.cir", reader) + +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) + +# Bulk layer for terminal provisioning + +bulk = polygon_layer + +psd = nil +nsd = nil + +# Computed layers + +active_in_nwell = active & nwell +pactive = active_in_nwell +pgate = pactive & poly +psd = pactive - pgate + +active_outside_nwell = active - nwell +nactive = active_outside_nwell +ngate = nactive & poly +nsd = nactive - ngate + +# Device extraction + +# PMOS transistor device extraction +extract_devices(mos3("PMOS"), { "SD" => psd, "G" => pgate, + "tS" => psd, "tD" => psd, "tG" => poly }) + +# NMOS transistor device extraction +extract_devices(mos3("NMOS"), { "SD" => nsd, "G" => ngate, + "tS" => nsd, "tD" => nsd, "tG" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, diff_cont) +connect(nsd, diff_cont) +connect(poly, poly_cont) +connect(diff_cont, metal1) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# attach labels +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Global +connect_global(bulk, "SUBSTRATE") + +# Implicit connection of the INV2 +# VSS nets +connect_implicit("INV2", "VSS") + +# Compare section + +netlist.simplify +align + +compare + diff --git a/testdata/lvs/double_height.lvsdb b/testdata/lvs/double_height.lvsdb new file mode 100644 index 000000000..0d2517e42 --- /dev/null +++ b/testdata/lvs/double_height.lvsdb @@ -0,0 +1,326 @@ +#%lvsdb-klayout +J( + W(INVCHAIN) + U(0.001) + L(l3 '3/0') + L(l11 '3/1') + L(l6 '4/0') + L(l7 '5/0') + L(l8 '6/0') + L(l12 '6/1') + L(l9 '7/0') + L(l10 '8/0') + L(l13 '8/1') + L(l14) + L(l2) + L(l5) + C(l3 l3 l11 l7) + C(l11 l3 l11) + C(l6 l6 l8 l2 l5) + C(l7 l3 l7 l8) + C(l8 l6 l7 l8 l12 l9) + C(l12 l8 l12) + C(l9 l8 l9 l10) + C(l10 l9 l10 l13) + C(l13 l10 l13) + C(l14 l14) + C(l2 l6 l2) + C(l5 l6 l5) + G(l14 SUBSTRATE) + K(PMOS MOS3) + K(NMOS MOS3) + D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + ) + D(D$NMOS NMOS + T(S + R(l5 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l5 (125 -475) (775 950)) + ) + ) + X(INV + R((-1500 -800) (3000 4600)) + N(1 + R(l6 (290 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l5 (-1375 -925) (775 950)) + ) + N(2 + R(l6 (290 2490) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l2 (-1375 -925) (775 950)) + ) + N(3 + R(l3 (-125 -250) (250 2500)) + R(l3 (-250 -3050) (250 1600)) + R(l3 (-250 1200) (250 1600)) + ) + N(4 + R(l6 (-510 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l6 (-220 2180) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -3530) (360 2840)) + R(l8 (-360 -2800) (360 760)) + R(l8 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l5 (-775 -3750) (775 950)) + ) + P(1) + P(2) + P(3) + P(4) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 2) + ) + D(2 D$NMOS + Y(0 0) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 1) + ) + ) + X(INV2 + R((0 0) (3000 9200)) + N(1 I(VDD) + R(l10 (0 3150) (3000 2900)) + R(l13 (-1891 -1451) (2 2)) + ) + N(2 I(A1) + R(l11 (1479 7109) (2 2)) + ) + N(3 I(A2) + R(l11 (1519 1949) (2 2)) + ) + N(4 I(Q1) + R(l12 (1919 7069) (2 2)) + ) + N(5 I(Q2) + R(l12 (1939 1949) (2 2)) + ) + N(6 I(VSS) + R(l13 (2679 8389) (2 2)) + R(l13 (-32 -7642) (2 2)) + ) + P(1 I(VDD)) + P(2 I(A1)) + P(3 I(A2)) + P(4 I(Q1)) + P(5 I(Q2)) + P(6 I(VSS)) + X(1 INV M O(180) Y(1500 800) + P(0 6) + P(1 1) + P(2 3) + P(3 5) + ) + X(2 INV O(180) Y(1500 8400) + P(0 6) + P(1 1) + P(2 2) + P(3 4) + ) + ) + X(INVCHAIN + R((-90 0) (4475 9200)) + N(1 + R(l3 (1625 1835) (2160 250)) + R(l3 (-250 0) (250 4990)) + R(l3 (-1605 0) (1605 250)) + R(l7 (-1545 -250) (240 250)) + R(l8 (-560 -375) (690 510)) + ) + N(2 I(IN) + R(l3 (-90 6850) (1590 650)) + R(l11 (-701 -351) (2 2)) + ) + N(3 I(OUT) + R(l8 (-90 1720) (1890 470)) + R(l12 (-1171 -231) (2 2)) + ) + N(4 I(VSS) + R(l10 (3000 655) (1385 250)) + R(l10 (-250 0) (250 7220)) + R(l10 (-1385 0) (1385 250)) + R(l13 (-686 -126) (2 2)) + ) + N(5 I(VDD) + R(l13 (2299 4599) (2 2)) + ) + P(2 I(IN)) + P(3 I(OUT)) + P(4 I(VSS)) + P(5 I(VDD)) + X(1 INV2 Y(0 0) + P(0 5) + P(1 2) + P(2 1) + P(3 1) + P(4 3) + P(5 4) + ) + ) +) +H( + K(PMOS MOS3) + K(NMOS MOS3) + X(INV + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I(Q)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A)) + P(4 I(Q)) + D(1 PMOS + I($1) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 1) + T(G 3) + T(D 4) + ) + D(2 NMOS + I($3) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 2) + T(G 3) + T(D 4) + ) + ) + X(INV2 + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + ) + X(2 INV I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + ) + ) + X(INVCHAIN + N(1 I('1')) + N(2 I('2')) + N(3 I('3')) + N(4 I('4')) + N(5 I('5')) + X(1 INV2 I($2) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + P(4 4) + P(5 5) + ) + ) +) +Z( + X(INV INV 1 + Z( + N(3 3 1) + N(4 4 1) + N(2 1 1) + N(1 2 1) + P(2 2 1) + P(3 3 1) + P(1 0 1) + P(0 1 1) + D(2 2 1) + D(1 1 1) + ) + ) + X(INV2 INV2 1 + Z( + N(2 3 1) + N(3 5 1) + N(4 4 W) + N(5 6 W) + N(1 1 1) + N(6 2 1) + P(1 2 1) + P(2 4 1) + P(3 3 1) + P(4 5 1) + P(0 0 1) + P(5 1 1) + X(2 1 1) + X(1 2 1) + ) + ) + X(INVCHAIN INVCHAIN 1 + Z( + N(1 4 1) + N(2 3 1) + N(3 5 1) + N(5 1 1) + N(4 2 1) + P(0 () 1) + P(1 () 1) + P(3 () 1) + P(2 () 1) + X(1 1 1) + ) + ) +) diff --git a/testdata/lvs/double_height2.cir b/testdata/lvs/double_height2.cir new file mode 100644 index 000000000..882e59444 --- /dev/null +++ b/testdata/lvs/double_height2.cir @@ -0,0 +1,17 @@ +* Extracted by KLayout + +.SUBCKT INVCHAIN ANY R ANY$1 PWR GND +X$1 ANY R R ANY$1 PWR GND INV2 +.ENDS INVCHAIN + +.SUBCKT INV2 A1 A2 Q1 Q2 R VSS +X$1 VSS R A1 Q1 INV +X$2 VSS R A2 Q2 INV +.ENDS INV2 + +.SUBCKT INV \$1 \$2 \$3 \$4 +M$1 \$4 \$3 \$2 \$4 PMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +M$2 \$4 \$3 \$1 \$4 NMOS L=0.25U W=0.95U AS=0.73625P AD=0.73625P PS=3.45U ++ PD=3.45U +.ENDS INV diff --git a/testdata/lvs/double_height2.lvs b/testdata/lvs/double_height2.lvs new file mode 100644 index 000000000..23edbf991 --- /dev/null +++ b/testdata/lvs/double_height2.lvs @@ -0,0 +1,135 @@ + +source($lvs_test_source) + +report_lvs($lvs_test_target_lvsdb) + +writer = write_spice(true, false) +target_netlist($lvs_test_target_cir, writer, "Extracted by KLayout") + +# needs this delegate because we use MOS3 which is not available in Spice +class SpiceReaderDelegate < RBA::NetlistSpiceReaderDelegate + + # says we want to catch these subcircuits as devices + def wants_subcircuit(name) + name == "HVNMOS" || name == "HVPMOS" + end + + # translate the element + def element(circuit, el, name, model, value, nets, params) + + if el != "M" + # all other elements are left to the standard implementation + return super + end + + if nets.size != 4 + error("Device #{model} needs four nodes") + end + + # provide a device class + cls = circuit.netlist.device_class_by_name(model) + if ! cls + cls = RBA::DeviceClassMOS3Transistor::new + cls.name = model + circuit.netlist.add(cls) + end + + # create a device + device = circuit.create_device(cls, name) + + # and configure the device + [ "S", "G", "D" ].each_with_index do |t,index| + device.connect_terminal(t, nets[index]) + end + device.set_parameter("W", params["W"] * 1e6) + device.set_parameter("L", params["L"] * 1e6) + + device + + end + +end + +reader = RBA::NetlistSpiceReader::new(SpiceReaderDelegate::new) +schematic("double_height_inv.cir", reader) + +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) + +# Bulk layer for terminal provisioning + +bulk = polygon_layer + +psd = nil +nsd = nil + +# Computed layers + +active_in_nwell = active & nwell +pactive = active_in_nwell +pgate = pactive & poly +psd = pactive - pgate + +active_outside_nwell = active - nwell +nactive = active_outside_nwell +ngate = nactive & poly +nsd = nactive - ngate + +# Device extraction + +# PMOS transistor device extraction +extract_devices(mos3("PMOS"), { "SD" => psd, "G" => pgate, + "tS" => psd, "tD" => psd, "tG" => poly }) + +# NMOS transistor device extraction +extract_devices(mos3("NMOS"), { "SD" => nsd, "G" => ngate, + "tS" => nsd, "tD" => nsd, "tG" => poly }) + +# Define connectivity for netlist extraction + +# Inter-layer +connect(psd, diff_cont) +connect(nsd, diff_cont) +connect(poly, poly_cont) +connect(diff_cont, metal1) +connect(poly_cont, metal1) +connect(metal1, via1) +connect(via1, metal2) + +# attach labels +connect(poly, poly_lbl) +connect(metal1, metal1_lbl) +connect(metal2, metal2_lbl) + +# Global +connect_global(bulk, "SUBSTRATE") + +# Implicit connection of the INV2 +# VSS nets +connect_implicit("GND") +connect_implicit("?") # "R" +connect_implicit("DOESNOTEXIST") +connect_implicit("*2", "*SS") +connect_implicit("*", "R") +connect_implicit("DOESNOTEXIST", "DOESNOTEXIST") + +# Compare section + +netlist.simplify +align + +compare + diff --git a/testdata/lvs/double_height2.lvsdb b/testdata/lvs/double_height2.lvsdb new file mode 100644 index 000000000..f9d933d9c --- /dev/null +++ b/testdata/lvs/double_height2.lvsdb @@ -0,0 +1,325 @@ +#%lvsdb-klayout +J( + W(INVCHAIN) + U(0.001) + L(l3 '3/0') + L(l11 '3/1') + L(l6 '4/0') + L(l7 '2/0') + L(l8 '6/0') + L(l12 '6/1') + L(l9 '7/0') + L(l10 '8/0') + L(l13 '8/1') + L(l14) + L(l2) + L(l5) + C(l3 l3 l11 l7) + C(l11 l3 l11) + C(l6 l6 l8 l2 l5) + C(l7 l3 l7 l8) + C(l8 l6 l7 l8 l12 l9) + C(l12 l8 l12) + C(l9 l8 l9 l10) + C(l10 l9 l10 l13) + C(l13 l10 l13) + C(l14 l14) + C(l2 l6 l2) + C(l5 l6 l5) + G(l14 SUBSTRATE) + K(PMOS MOS3) + K(NMOS MOS3) + D(D$PMOS PMOS + T(S + R(l2 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l2 (125 -475) (775 950)) + ) + ) + D(D$NMOS NMOS + T(S + R(l5 (-900 -475) (775 950)) + ) + T(G + R(l3 (-125 -475) (250 950)) + ) + T(D + R(l5 (125 -475) (775 950)) + ) + ) + X(INV + R((-1500 -800) (3000 4600)) + N(1 + R(l6 (290 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l5 (-1375 -925) (775 950)) + ) + N(2 + R(l6 (290 2490) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -690) (360 760)) + R(l9 (-305 -705) (250 250)) + R(l9 (-250 150) (250 250)) + R(l10 (-2025 -775) (3000 900)) + R(l2 (-1375 -925) (775 950)) + ) + N(3 + R(l3 (-125 -250) (250 2500)) + R(l3 (-250 -3050) (250 1600)) + R(l3 (-250 1200) (250 1600)) + ) + N(4 + R(l6 (-510 -310) (220 220)) + R(l6 (-220 180) (220 220)) + R(l6 (-220 2180) (220 220)) + R(l6 (-220 180) (220 220)) + R(l8 (-290 -3530) (360 2840)) + R(l8 (-360 -2800) (360 760)) + R(l8 (-360 2040) (360 760)) + R(l2 (-680 -855) (775 950)) + R(l5 (-775 -3750) (775 950)) + ) + P(1) + P(2) + P(3) + P(4) + D(1 D$PMOS + Y(0 2800) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 2) + ) + D(2 D$NMOS + Y(0 0) + E(L 0.25) + E(W 0.95) + E(AS 0.73625) + E(AD 0.73625) + E(PS 3.45) + E(PD 3.45) + T(S 4) + T(G 3) + T(D 1) + ) + ) + X(INV2 + R((0 0) (3000 9200)) + N(1 I(A1) + R(l11 (1479 7109) (2 2)) + ) + N(2 I(A2) + R(l11 (1519 1949) (2 2)) + ) + N(3 I(Q1) + R(l12 (1919 7069) (2 2)) + ) + N(4 I(Q2) + R(l12 (1939 1949) (2 2)) + ) + N(5 I(R) + R(l13 (2719 5559) (2 2)) + R(l13 (-92 -1942) (2 2)) + ) + N(6 I(VSS) + R(l13 (2679 8389) (2 2)) + R(l13 (-32 -7642) (2 2)) + ) + P(1 I(A1)) + P(2 I(A2)) + P(3 I(Q1)) + P(4 I(Q2)) + P(5 I(R)) + P(6 I(VSS)) + X(1 INV O(180) Y(1500 8400) + P(0 6) + P(1 5) + P(2 1) + P(3 3) + ) + X(2 INV M O(180) Y(1500 800) + P(0 6) + P(1 5) + P(2 2) + P(3 4) + ) + ) + X(INVCHAIN + R((-90 0) (3090 9200)) + N(1 I(ANY) + R(l3 (-90 6850) (1590 650)) + R(l11 (-701 -351) (2 2)) + ) + N(2 I(R) + R(l11 (1479 2369) (2 2)) + R(l8 (439 4579) (690 510)) + R(l12 (-301 -291) (2 2)) + ) + N(3 I(ANY) + R(l8 (-90 1720) (1890 470)) + R(l12 (-1171 -231) (2 2)) + ) + N(4 I(PWR) + R(l13 (299 5549) (2 2)) + R(l13 (-12 -1992) (2 2)) + ) + N(5 I(GND) + R(l13 (319 8399) (2 2)) + R(l13 (-52 -7602) (2 2)) + ) + P(1 I(ANY)) + P(2 I(R)) + P(3 I(ANY)) + P(4 I(PWR)) + P(5 I(GND)) + X(1 INV2 Y(0 0) + P(0 1) + P(1 2) + P(2 2) + P(3 3) + P(4 4) + P(5 5) + ) + ) +) +H( + K(PMOS MOS3) + K(NMOS MOS3) + X(INV + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A)) + N(4 I(Q)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A)) + P(4 I(Q)) + D(1 PMOS + I($1) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 1) + T(G 3) + T(D 4) + ) + D(2 NMOS + I($3) + E(L 0.25) + E(W 0.95) + E(AS 0) + E(AD 0) + E(PS 0) + E(PD 0) + T(S 2) + T(G 3) + T(D 4) + ) + ) + X(INV2 + N(1 I(VDD)) + N(2 I(VSS)) + N(3 I(A1)) + N(4 I(Q1)) + N(5 I(A2)) + N(6 I(Q2)) + P(1 I(VDD)) + P(2 I(VSS)) + P(3 I(A1)) + P(4 I(Q1)) + P(5 I(A2)) + P(6 I(Q2)) + X(1 INV I($1) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + ) + X(2 INV I($2) + P(0 1) + P(1 2) + P(2 5) + P(3 6) + ) + ) + X(INVCHAIN + N(1 I('1')) + N(2 I('2')) + N(3 I('3')) + N(4 I('4')) + N(5 I('5')) + X(1 INV2 I($2) + P(0 1) + P(1 2) + P(2 3) + P(3 4) + P(4 4) + P(5 5) + ) + ) +) +Z( + X(INV INV 1 + Z( + N(3 3 1) + N(4 4 1) + N(2 1 1) + N(1 2 1) + P(2 2 1) + P(3 3 1) + P(1 0 1) + P(0 1 1) + D(2 2 1) + D(1 1 1) + ) + ) + X(INV2 INV2 1 + Z( + N(1 3 1) + N(2 5 1) + N(3 4 W) + N(4 6 W) + N(5 1 1) + N(6 2 1) + P(0 2 1) + P(1 4 1) + P(2 3 1) + P(3 5 1) + P(4 0 1) + P(5 1 1) + X(1 1 1) + X(2 2 1) + ) + ) + X(INVCHAIN INVCHAIN 1 + Z( + N(1 3 1) + N(3 5 1) + N(5 2 1) + N(4 1 1) + N(2 4 1) + P(2 () 1) + P(0 () 1) + P(4 () 1) + P(3 () 1) + P(1 () 1) + X(1 1 1) + ) + ) +) diff --git a/testdata/lvs/double_height2_inv.cir b/testdata/lvs/double_height2_inv.cir new file mode 100644 index 000000000..c41d87966 --- /dev/null +++ b/testdata/lvs/double_height2_inv.cir @@ -0,0 +1,18 @@ + +* cell INVCHAIN +.SUBCKT INVCHAIN +X$2 1 2 3 4 4 5 INV2 +.ENDS INVCHAIN + +* cell INV2 +.SUBCKT INV2 VDD VSS A1 Q1 A2 Q2 +X$1 VDD VSS A1 Q1 INV +X$2 VDD VSS A2 Q2 INV +.ENDS INV2 + +* cell INV +.SUBCKT INV VDD VSS A Q +M$1 VDD A Q VDD PMOS L=0.25U W=0.95U +M$3 VSS A Q VSS NMOS L=0.25U W=0.95U +.ENDS INV + diff --git a/testdata/lvs/double_height2_inv.gds b/testdata/lvs/double_height2_inv.gds new file mode 100644 index 000000000..bd62bf8c8 Binary files /dev/null and b/testdata/lvs/double_height2_inv.gds differ diff --git a/testdata/lvs/double_height_inv.cir b/testdata/lvs/double_height_inv.cir new file mode 100644 index 000000000..c41d87966 --- /dev/null +++ b/testdata/lvs/double_height_inv.cir @@ -0,0 +1,18 @@ + +* cell INVCHAIN +.SUBCKT INVCHAIN +X$2 1 2 3 4 4 5 INV2 +.ENDS INVCHAIN + +* cell INV2 +.SUBCKT INV2 VDD VSS A1 Q1 A2 Q2 +X$1 VDD VSS A1 Q1 INV +X$2 VDD VSS A2 Q2 INV +.ENDS INV2 + +* cell INV +.SUBCKT INV VDD VSS A Q +M$1 VDD A Q VDD PMOS L=0.25U W=0.95U +M$3 VSS A Q VSS NMOS L=0.25U W=0.95U +.ENDS INV + diff --git a/testdata/lvs/double_height_inv.gds b/testdata/lvs/double_height_inv.gds new file mode 100644 index 000000000..0f2ea846d Binary files /dev/null and b/testdata/lvs/double_height_inv.gds differ