From 36ee1efe167245722f8f71a1bf06f65f4d3f89e8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 21 Oct 2019 22:14:36 +0200 Subject: [PATCH] WIP: speedup LVS 'align' by flattening top-down --- src/db/db/dbNetlist.cc | 24 +++++++++++++++++++ src/db/db/dbNetlist.h | 6 +++++ src/db/db/gsiDeclDbNetlist.cc | 14 ++++++----- src/lvs/lvs/built-in-macros/_lvs_netter.rb | 7 +++--- testdata/ruby/dbNetlist.rb | 27 +++++++++++++++------- 5 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/db/db/dbNetlist.cc b/src/db/db/dbNetlist.cc index 98e8a089e..62056542c 100644 --- a/src/db/db/dbNetlist.cc +++ b/src/db/db/dbNetlist.cc @@ -380,6 +380,30 @@ void Netlist::purge_circuit (Circuit *circuit) remove_circuit (circuit); } +void Netlist::flatten_circuits (const std::vector &circuits) +{ + if (circuits.empty ()) { + return; + } + + std::set circuits_set (circuits.begin (), circuits.end ()); + + std::vector to_flatten; + to_flatten.reserve (circuits.size ()); + + // Before flatten, we sort top-down. This optimizes for the case of flattening away + // some hierarchy above a certain circuit. + for (top_down_circuit_iterator c = begin_top_down (); c != end_top_down (); ++c) { + if (circuits_set.find (c.operator-> ()) != circuits_set.end ()) { + to_flatten.push_back (c.operator-> ()); + } + } + + for (std::vector::const_iterator c = to_flatten.begin (); c != to_flatten.end (); ++c) { + flatten_circuit (*c); + } +} + void Netlist::flatten_circuit (Circuit *circuit) { tl_assert (circuit != 0); diff --git a/src/db/db/dbNetlist.h b/src/db/db/dbNetlist.h index 760fa381f..ff961036b 100644 --- a/src/db/db/dbNetlist.h +++ b/src/db/db/dbNetlist.h @@ -174,6 +174,12 @@ public: */ void flatten_circuit (Circuit *circuit); + /** + * @brief Flattens the given circuits + * This is basically equivalent to calling flatten on all given circuits, but more efficient. + */ + void flatten_circuits (const std::vector &circuits); + /** * @brief Flattens the netlist */ diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index 899925c45..85f985571 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -1345,7 +1345,7 @@ static void read_netlist (db::Netlist *nl, const std::string &file, db::NetlistR static void flatten_circuit_by_name (db::Netlist *nl, const std::string &name_pattern) { - std::list > circuits_to_flatten; + std::vector circuits_to_flatten; tl::GlobPattern pat (name_pattern); for (db::Netlist::circuit_iterator c = nl->begin_circuits (); c != nl->end_circuits (); ++c) { if (pat.match (c->name ())) { @@ -1353,11 +1353,7 @@ static void flatten_circuit_by_name (db::Netlist *nl, const std::string &name_pa } } - for (std::list >::iterator c = circuits_to_flatten.begin (); c != circuits_to_flatten.end (); ++c) { - if (c->get ()) { - nl->flatten_circuit (c->get ()); - } - } + nl->flatten_circuits (circuits_to_flatten); } static void blank_circuit_by_name (db::Netlist *nl, const std::string &name_pattern) @@ -1399,6 +1395,12 @@ Class decl_dbNetlist ("db", "Netlist", "@brief Flattens all circuits of the netlist\n" "After calling this method, only the top circuits will remain." ) + + gsi::method ("flatten_circuits", &db::Netlist::flatten_circuits, + "@brief Flattens all given circuits of the netlist\n" + "This method is equivalent to calling \\flatten_circuit for all given circuits, but more efficient.\n" + "\n" + "This method has been introduced in version 0.26.1" + ) + gsi::method ("flatten_circuit", &db::Netlist::flatten_circuit, gsi::arg ("circuit"), "@brief Flattens a subcircuit\n" "This method will substitute all instances (subcircuits) of the given circuit by it's " diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index f04df659b..41c639a04 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -137,14 +137,15 @@ module LVS # flatten layout cells for which there is no corresponding schematic circuit unmatched_a.each do |c| @engine.info("Flatten layout cell (no schematic): #{c.name}") - nl[0].flatten_circuit(c) end + nl[0].flatten_circuits(unmatched_a) # flatten schematic circuits for which there is no corresponding layout cell - comparer.unmatched_circuits_b(*nl).each do |c| + unmatched_b = comparer.unmatched_circuits_b(*nl) + unmatched_b.each do |c| @engine.info("Flatten schematic circuit (no layout): #{c.name}") - nl[1].flatten_circuit(c) end + nl[1].flatten_circuits(unmatched_b) end diff --git a/testdata/ruby/dbNetlist.rb b/testdata/ruby/dbNetlist.rb index 3609096b1..1a391a0ba 100644 --- a/testdata/ruby/dbNetlist.rb +++ b/testdata/ruby/dbNetlist.rb @@ -841,25 +841,36 @@ end; END nl3 = nl2.dup - nl2.flatten_circuit(nl2.circuit_by_name("PTRANS")) - nl2.flatten_circuit(nl2.circuit_by_name("NTRANS")) + nl3.flatten_circuit(nl3.circuit_by_name("NTRANS")) + nl3.flatten_circuit(nl3.circuit_by_name("PTRANS")) - assert_equal(nl2.to_s, <<"END") + assert_equal(nl3.to_s, <<"END") circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5); device PMOS $1 (S=$5,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); - device PMOS $2 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); - device NMOS $3 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); - device NMOS $4 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + device NMOS $2 (S=$4,G=IN,D=$2) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + device NMOS $3 (S=$4,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); + device PMOS $4 (S=$5,G=$2,D=OUT) (L=0.25,W=0.95,AS=0,AD=0,PS=0,PD=0); end; END - nl3.flatten_circuit("{N,P}TRANS") - assert_equal(nl3.to_s, nl2.to_s) + nl4 = nl2.dup + nl4.flatten_circuit("{P,N}TRANS") + assert_equal(nl4.to_s, nl3.to_s) + + nl4 = nl2.dup + nl4.flatten_circuits([ nl4.circuit_by_name("PTRANS"), nl4.circuit_by_name("NTRANS") ]) + assert_equal(nl4.to_s, nl3.to_s) nl2 = nl.dup nl2.flatten_circuit("*") # smoke test assert_equal(nl2.to_s, "") + nl2 = nl.dup + cc = [] + nl2.each_circuit { |c| cc << c } + nl2.flatten_circuits(cc) + assert_equal(nl2.to_s, "") + end def test_12_BlankAndPurgeCircuits