Fixed #807 - now supporting incremental connect and clear_connections in DRC/LVS

This commit is contained in:
Matthias Koefferlein 2021-05-24 21:56:57 +02:00
parent 58afc47071
commit 1c8442f485
10 changed files with 241 additions and 18 deletions

View File

@ -246,11 +246,21 @@ void LayoutToNetlist::extract_devices (db::NetlistDeviceExtractor &extractor, co
extractor.extract (dss (), m_layout_index, layers, *mp_netlist, m_net_clusters, m_device_scaling);
}
void LayoutToNetlist::connect (const db::Region &l)
void LayoutToNetlist::reset_extracted ()
{
if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
m_net_clusters.clear ();
mp_netlist.reset (0);
m_netlist_extracted = false;
}
}
void LayoutToNetlist::connect (const db::Region &l)
{
reset_extracted ();
if (! is_persisted (l)) {
register_layer (l, make_new_name ());
@ -265,9 +275,8 @@ void LayoutToNetlist::connect (const db::Region &l)
void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b)
{
if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
reset_extracted ();
if (! is_persisted (a)) {
register_layer (a, make_new_name ());
}
@ -286,9 +295,8 @@ void LayoutToNetlist::connect_impl (const db::ShapeCollection &a, const db::Shap
size_t LayoutToNetlist::connect_global_impl (const db::ShapeCollection &l, const std::string &gn)
{
if (m_netlist_extracted) {
throw tl::Exception (tl::to_string (tr ("The netlist has already been extracted")));
}
reset_extracted ();
if (! is_persisted (l)) {
register_layer (l, make_new_name ());
}

View File

@ -359,6 +359,14 @@ public:
*/
void extract_devices (db::NetlistDeviceExtractor &extractor, const std::map<std::string, db::ShapeCollection *> &layers);
/**
* @brief Resets the extracted netlist
*
* This method will invalidate the netlist and extraction. It is called automatically when
* cone of the connect methods is called.
*/
void reset_extracted ();
/**
* @brief Defines an intra-layer connection for the given layer.
* The layer is either an original layer created with "make_layer" and it's variants or
@ -532,6 +540,14 @@ public:
*/
void set_netlist_extracted ();
/**
* @brief Gets a value indicating whether the netlist has been extracted
*/
bool is_netlist_extracted () const
{
return m_netlist_extracted;
}
/**
* @brief Gets the internal DeepShapeStore object
*

View File

@ -394,6 +394,18 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"If errors occur, the device extractor will contain theses errors.\n"
) +
gsi::method ("reset_extracted", &db::LayoutToNetlist::reset_extracted,
"@brief Resets the extracted netlist and enables re-extraction\n"
"This method is implicitly called when using \\connect or \\connect_global after a netlist has been extracted.\n"
"This enables incremental connect with re-extraction.\n"
"\n"
"This method has been introduced in version 0.27.1.\n"
) +
gsi::method ("is_extracted?", &db::LayoutToNetlist::is_netlist_extracted,
"@brief Gets a value indicating whether the netlist has been extracted\n"
"\n"
"This method has been introduced in version 0.27.1.\n"
) +
gsi::method ("connect", (void (db::LayoutToNetlist::*) (const db::Region &)) &db::LayoutToNetlist::connect, gsi::arg ("l"),
"@brief Defines an intra-layer connection for the given layer.\n"
"The layer is either an original layer created with \\make_incluidelayer and it's variants or\n"

View File

@ -64,7 +64,6 @@ module DRC
def initialize(engine)
@engine = engine
@netlisted = false
@connect_implicit = []
@connect_implicit_per_cell = {}
@connect_explicit = []
@ -240,7 +239,6 @@ module DRC
# See \connect for more details.
def clear_connections
@netlisted = false
@connect_implicit = []
@connect_implicit_per_cell = {}
@connect_explicit = []
@ -544,7 +542,7 @@ module DRC
ensure_data
# run extraction in a timed environment
if ! @netlisted
if ! @l2n.is_extracted?
# configure implicit net connections
@l2n.clear_join_net_names
@ -569,7 +567,6 @@ module DRC
end
@engine._cmd(@l2n, :extract_netlist)
@netlisted = true
end
@ -609,13 +606,13 @@ module DRC
end
def _l2n_data
@netlisted && self.l2n_data
@l2n && @l2n.is_extracted? && self.l2n_data
end
private
def cleanup
@netlisted && clear_connections
@l2n && @l2n.is_extracted? && clear_connections
end
def ensure_data
@ -644,14 +641,13 @@ module DRC
def register_layer(data)
id = data.data_id
ensure_data
if @layers && @layers[id]
# already registered
return
end
ensure_data
@layers[id] = data
@lnum += 1

View File

@ -225,6 +225,46 @@ TEST(5_FlatAntenna)
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
TEST(5_FlatAntennaIncremental)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_5i.drc";
std::string input = tl::testdata ();
input += "/drc/antenna_l1.gds";
std::string au = tl::testdata ();
au += "/drc/drcSimpleTests_au5.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(6_HierarchicalAntenna)
{
std::string rs = tl::testdata ();

View File

@ -1,5 +1,5 @@
# Hierarchical antenna check
# Flat antenna check
source($drc_test_source, "RINGO")
target($drc_test_target)
@ -14,6 +14,17 @@ metal2 = input(8, 0)
gate = diff & poly
connect(gate, poly)
connect(poly, poly_cont)
connect(poly_cont, metal1)
antenna_check(gate, metal1, 1.0).output(201)
antenna_check(gate, metal1, 2.0).output(202)
antenna_check(gate, metal1, 3.0).output(203)
antenna_check(gate, metal1, 4.0).output(204)
clear_connections
connect(gate, poly)
connect(poly, poly_cont)
connect(poly_cont, metal1)
@ -24,3 +35,4 @@ antenna_check(gate, metal2, 1.0).output(101)
antenna_check(gate, metal2, 5.0).output(105)
antenna_check(gate, metal2, 10.0).output(110)
antenna_check(gate, metal2, 50.0).output(150)

33
testdata/drc/drcSimpleTests_5i.drc vendored Normal file
View File

@ -0,0 +1,33 @@
# Flat antenna check
source($drc_test_source, "RINGO")
target($drc_test_target)
diff = input(2, 0)
poly = input(3, 0)
contact = input(4, 0)
poly_cont = input(5, 0)
metal1 = input(6, 0)
via1 = input(7, 0)
metal2 = input(8, 0)
gate = diff & poly
connect(gate, poly)
connect(poly, poly_cont)
connect(poly_cont, metal1)
antenna_check(gate, metal1, 1.0).output(201)
antenna_check(gate, metal1, 2.0).output(202)
antenna_check(gate, metal1, 3.0).output(203)
antenna_check(gate, metal1, 4.0).output(204)
connect(metal1, via1)
connect(via1, metal2)
antenna_check(gate, metal2, 1.0).output(101)
antenna_check(gate, metal2, 5.0).output(105)
antenna_check(gate, metal2, 10.0).output(110)
antenna_check(gate, metal2, 50.0).output(150)

Binary file not shown.

View File

@ -576,6 +576,54 @@ end;
self.assertEqual(str(a1_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(101, 0)))), "")
self.assertEqual(str(a1_30.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(102, 0)))), "")
# --- simple incremental antenna check with metal1 + metal2
l2n._destroy()
l2n = pya.LayoutToNetlist(dss)
l2n.register(rdiode, "diode")
l2n.register(rpoly, "poly")
l2n.register(rcont, "cont")
l2n.register(rmetal1, "metal1")
l2n.register(rvia1, "via1")
l2n.register(rmetal2, "metal2")
l2n.connect(rpoly)
l2n.connect(rcont)
l2n.connect(rmetal1)
l2n.connect(rmetal2)
l2n.connect(rpoly, rcont)
l2n.connect(rcont, rmetal1)
self.assertEqual(l2n.is_extracted(), False)
l2n.extract_netlist()
self.assertEqual(l2n.is_extracted(), True)
a1_3 = l2n.antenna_check(rpoly, rmetal1, 3)
a1_10 = l2n.antenna_check(rpoly, rmetal1, 10)
a1_30 = l2n.antenna_check(rpoly, rmetal1, 30)
# Note: flatten.merged performs some normalization
self.assertEqual(str(a1_3.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(100, 0)))), "")
self.assertEqual(str(a1_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(101, 0)))), "")
self.assertEqual(str(a1_30.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(102, 0)))), "")
l2n.connect(rmetal1, rvia1)
l2n.connect(rvia1, rmetal2)
self.assertEqual(l2n.is_extracted(), False)
l2n.extract_netlist()
self.assertEqual(l2n.is_extracted(), True)
a2_5 = l2n.antenna_check(rpoly, rmetal2, 5)
a2_10 = l2n.antenna_check(rpoly, rmetal2, 10)
a2_17 = l2n.antenna_check(rpoly, rmetal2, 17)
# Note: flatten.merged performs some normalization
self.assertEqual(str(a2_5.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(200, 0)))), "")
self.assertEqual(str(a2_10.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(201, 0)))), "")
self.assertEqual(str(a2_17.flatten() ^ pya.Region(ly_au.top_cell().begin_shapes_rec(ly_au.layer(202, 0)))), "")
# --- simple antenna check with metal2
l2n._destroy()

View File

@ -170,7 +170,8 @@ class DBLayoutToNetlist_TestClass < TestBase
# Perform netlist extraction
l2n.extract_netlist
assert_equal(l2n.netlist.to_s, <<END)
nl_string = l2n.netlist.to_s
assert_equal(nl_string, <<END)
circuit TRANS ($1=$1,$2=$2);
end;
circuit INV2 (OUT=OUT,$2=$3,$3=$4);
@ -218,6 +219,15 @@ END
assert_equal(r.to_s,
"(-980,-420;-980,2420;-620,2420;-620,-420);(-800,820;-800,1180;580,1180;580,820);(-980,2420;-980,3180;-620,3180;-620,2420);(-980,-380;-980,380;-620,380;-620,-380)")
assert_equal(l2n.is_extracted?, true)
l2n.reset_extracted
assert_equal(l2n.is_extracted?, false)
assert_equal(l2n.netlist.inspect, "nil")
l2n.extract_netlist
assert_equal(l2n.is_extracted?, true)
assert_equal(l2n.netlist.to_s, nl_string)
end
def test_10_LayoutToNetlistExtractionWithoutDevices
@ -755,6 +765,54 @@ END
assert_equal((a2_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(201, 0)))).to_s, "")
assert_equal((a2_17.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(202, 0)))).to_s, "")
# --- simple incremental antenna check with metal1 + metal2
l2n._destroy
l2n = RBA::LayoutToNetlist::new(dss)
l2n.register(rdiode, "diode")
l2n.register(rpoly, "poly")
l2n.register(rcont, "cont")
l2n.register(rmetal1, "metal1")
l2n.register(rvia1, "via1")
l2n.register(rmetal2, "metal2")
l2n.connect(rpoly)
l2n.connect(rcont)
l2n.connect(rmetal1)
l2n.connect(rmetal2)
l2n.connect(rpoly, rcont)
l2n.connect(rcont, rmetal1)
assert_equal(l2n.is_extracted?, false)
l2n.extract_netlist
assert_equal(l2n.is_extracted?, true)
a1_3 = l2n.antenna_check(rpoly, rmetal1, 3)
a1_10 = l2n.antenna_check(rpoly, rmetal1, 10)
a1_30 = l2n.antenna_check(rpoly, rmetal1, 30)
# Note: flatten.merged performs some normalization
assert_equal((a1_3.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(100, 0)))).to_s, "")
assert_equal((a1_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(101, 0)))).to_s, "")
assert_equal((a1_30.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(102, 0)))).to_s, "")
l2n.connect(rmetal1, rvia1)
l2n.connect(rvia1, rmetal2)
assert_equal(l2n.is_extracted?, false)
l2n.extract_netlist
assert_equal(l2n.is_extracted?, true)
a2_5 = l2n.antenna_check(rpoly, rmetal2, 5)
a2_10 = l2n.antenna_check(rpoly, rmetal2, 10)
a2_17 = l2n.antenna_check(rpoly, rmetal2, 17)
# Note: flatten.merged performs some normalization
assert_equal((a2_5.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(200, 0)))).to_s, "")
assert_equal((a2_10.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(201, 0)))).to_s, "")
assert_equal((a2_17.flatten ^ RBA::Region::new(ly_au.top_cell.begin_shapes_rec(ly_au.layer(202, 0)))).to_s, "")
# --- antenna check with diodes and antenna effect reduction
l2n._destroy