From da88973df91a76e370b2987e9bb434e56f09350c Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 9 Mar 2025 22:40:17 +0100 Subject: [PATCH] A number of new methods and variants in LayoutToNetlist The goal is to support texts as native objects in LayoutToNetlist. Primarily this means, that instead of using a Region to identify a layer, it is encouraged to use a layer index instead. Also, Region is not longer the only way to represent a layer, Texts is available too. It is possible now to retrieve the texts of a net using "texts_of_net". Also, the "lmap" parameter of "build_nets" became optional. If "nil", a layer map is created based on the layer info attribute of the layers. --- src/db/db/dbLayoutToNetlist.cc | 8 +- src/db/db/gsiDeclDbLayoutToNetlist.cc | 15 +- testdata/algo/l2n_lmap_au.gds | Bin 0 -> 5886 bytes testdata/algo/l2n_lmap_au_2.gds | Bin 0 -> 3008 bytes testdata/ruby/dbLayoutToNetlist.rb | 258 ++++++++++++++++++++++++-- 5 files changed, 253 insertions(+), 28 deletions(-) create mode 100644 testdata/algo/l2n_lmap_au.gds create mode 100644 testdata/algo/l2n_lmap_au_2.gds diff --git a/src/db/db/dbLayoutToNetlist.cc b/src/db/db/dbLayoutToNetlist.cc index 3c5e06d0c..ecc43f376 100644 --- a/src/db/db/dbLayoutToNetlist.cc +++ b/src/db/db/dbLayoutToNetlist.cc @@ -2360,9 +2360,7 @@ NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, con StopOnFirst sof; std::map sof_lmap; for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { - if (l->second) { - sof_lmap.insert (std::make_pair (l->second, &sof)); - } + sof_lmap.insert (std::make_pair (l->second, &sof)); } bool consider_cell = ! deliver_shapes_of_net (m_hier_mode == BNH_Flatten, mp_source->netlist (), mp_source->net_clusters (), ci, cid, sof_lmap, tr, 0); @@ -2382,9 +2380,7 @@ NetBuilder::build_net_rec (db::cell_index_type ci, size_t cid, db::Cell &tc, con std::map target_lmap; for (std::map::const_iterator l = lmap.begin (); l != lmap.end (); ++l) { - if (l->second) { - target_lmap.insert (std::make_pair (l->second, &target_cell->shapes (l->first))); - } + target_lmap.insert (std::make_pair (l->second, &target_cell->shapes (l->first))); } deliver_shapes_of_net (m_hier_mode == BNH_Flatten, mp_source->netlist (), mp_source->net_clusters (), ci, cid, target_lmap, tr, netname_propid); diff --git a/src/db/db/gsiDeclDbLayoutToNetlist.cc b/src/db/db/gsiDeclDbLayoutToNetlist.cc index facc04627..470d0a133 100644 --- a/src/db/db/gsiDeclDbLayoutToNetlist.cc +++ b/src/db/db/gsiDeclDbLayoutToNetlist.cc @@ -94,11 +94,14 @@ static std::map layer_map_from_var (const db::Layout std::map res; for (auto kv = lmap.begin_array (); kv != lmap.end_array (); ++kv) { + const tl::Variant &k = kv->first; const tl::Variant &v = kv->second; + unsigned int ki = k.to_uint (); - unsigned int vi = 0; if (v.is_user ()) { + + unsigned int vi = 0; if (dynamic_cast *> (v.user_cls ()) != 0) { vi = l2n->layer_of (v.to_user ()); } else if (dynamic_cast *> (v.user_cls ()) != 0) { @@ -106,10 +109,14 @@ static std::map layer_map_from_var (const db::Layout } else { throw tl::Exception (tl::to_string (tr ("'lmap' argument hash values need to be ints, Region or Texts objects"))); } - } else { - vi = v.to_uint (); + res.insert (std::make_pair (ki, vi)); + + } else if (! v.is_nil ()) { + + res.insert (std::make_pair (ki, v.to_uint ())); + } - res.insert (std::make_pair (ki, vi)); + } return res; diff --git a/testdata/algo/l2n_lmap_au.gds b/testdata/algo/l2n_lmap_au.gds new file mode 100644 index 0000000000000000000000000000000000000000..01cbacf518129934d5bb110a85cd94b4b50a85aa GIT binary patch literal 5886 zcmcgw&ud&&6h8AKZ+_%8#BmH1iWD7-g;;HVRJ2%XJCUZTNoiuxA&~w7wTKoGX+^LF zMJz?7i|V2X)3LTS4aagmh^cXg4i3nAdj66g7yd+wQc-;G(-v7a6I*22 zTkS_K%0$7AJZcq;{BTft_b#RC{|ZarZ9mQ;YfNlLO3;QSH>)PX3nTPiYVQi-;fE&7DX5(5|4` z858k8EI*|^oPP)Y@db;1g8vIKs=a+D;XlO(XO8)ZdxrTjs-5N%{FHX#|Bz@m_p@kM zQ00wfdbHNWVmR^c!ZQL7tg~%OF}*yMJ~H53||u zdG;}1QSJWOEj;Mk@LB)NS5&)S|I9;v*95u$3YSq!i)v5xpXzUgzfFbRsCiwGbzAse z^vf8a5P8gFMa*C-{LZsluyhyot`jNhIY2X6?7UEamB zhj^&nfl5um1F-lt_{|vA z4)pTbZ|OJjKQb!*71a*R;78Hsy70Mz9KWF24Z8fqS7!5Vs17eI#_H85`gcA?wfnnN zc>I!Lez-^YifZ>y3Fe`i_)0QHH<%xz+EZQH6+eFs*?;bU_*YbWYDx^nFAQ*=K)&7= zEG`4vsGmi(1Ks>NT?@a+ePK$lyZ*YK!5_!{SFmrKyC3m?si=0wEWYe#et1UsihQ1& zo5jcZ_)7D>+7=82-(G21i!Z?22~!fx>|$Q76Ppo|PYcfIgOMuo4a zcI|pQt8ssw)$9X&yf$_QoNb=ecD6a%#_hhrEw2fd-o;Dg1F$TrJ-8BCo)djy&8);XORQCKn2%S#?q7R+!@suo wuzzjxihu2;b^qG2=lpA@K1{6H>_QQ}Dx;O-4$N3LQzvVkr(xGRs#h%1f78LkB>(^b literal 0 HcmV?d00001 diff --git a/testdata/algo/l2n_lmap_au_2.gds b/testdata/algo/l2n_lmap_au_2.gds new file mode 100644 index 0000000000000000000000000000000000000000..80ed5a2b8d38e53e4b66302285db10ee933563e3 GIT binary patch literal 3008 zcmcIm&uddb5T3leY~D+r5Y+DLkoM-mLr?w*UcKe)A@SR+)0drWd{0XQVZzM!?ab~svl~+6c@JnR!#6zr5c3_Mvy9_T|FX+fIw7a+7%#rE2wdqgwCQrkC$6 ztxjJe^7HgpPFZl08KYSi|09d$BxgZg)y7KoexucGEVeIWD}I5fT();e6qT}rLH{vP zp-28PtP4~{p!7KaN0fOL;{qae#3P^7QICku>2(ILiNYOY3vb|E1OAU?f4F1vOyU>k z4L>?#_-xrKrEj zpX(Rh2mW)^kM7MpMg8brW7)5*RsTWxqWiG^F7j{U`x*H+V$1&iu9aW#!D(tf>aJ^k zY}rpui!Ztt`R7dcX1<#4HJ1I_8h%3eMoKN#Titf8W^Pp6yW%z;cI3nFs50){38*0^ z5mi`DvsPs*4eC@c4eIQzG^i_hGw|W*oUq56KhmJ?UQB~pc^0E0^GZSI6CP#EE2g7( z?Shla=%Zh~lzOh}mlrQ&$=1`XE7WvihLy#q;lv0fofx5`6C+e~VuXfHj8M>t5&AhX zLOmxYjdnOgt=&+I6xsz!>3=|p8Uxo_!VqbFgRVb2K%I03vXFog}v44*1dv7d`8S{Pm4GXU>fB*mh literal 0 HcmV?d00001 diff --git a/testdata/ruby/dbLayoutToNetlist.rb b/testdata/ruby/dbLayoutToNetlist.rb index b643d4b73..30408b611 100644 --- a/testdata/ruby/dbLayoutToNetlist.rb +++ b/testdata/ruby/dbLayoutToNetlist.rb @@ -558,6 +558,58 @@ END end + def shape_to_s(s) + if s.is_box || s.is_polygon + s.polygon.to_s + elsif s.is_text + s.text.to_s + else + "nn" + end + end + + def compare_layouts(ly, au_file, tmp_file = nil) + + ret = true + + begin + ly_au = RBA::Layout::new + ly_au.read(au_file) + rescue + ly.write(tmp_file) + puts "Actual layout written to: #{tmp_file}" + raise + end + + lmap = {} + + [ ly, ly_au ].each_with_index do |l,i| + l.layer_indexes.each do |li| + info = l.get_info(li) + lmap[info] ||= [ nil, nil ] + lmap[info][i] = li + end + end + + lmap.each do |info,lis| + s = !lis[0] ? "" : ly .top_cell.begin_shapes_rec(lis[0]).each.collect { |i| shape_to_s(i.shape) }.sort.join("\n") + s_au = !lis[1] ? "" : ly_au.top_cell.begin_shapes_rec(lis[1]).each.collect { |i| shape_to_s(i.shape) }.sort.join("\n") + if s != s_au + puts "Layer #{info}:\nActual shapes:\n#{s}\nGolden layout shapes:\n#{s_au}" + ret = false + end + end + + if !ret && tmp_file + ly.write(tmp_file) + puts "Golden: #{au_file}" + puts "Actual: #{tmp_file}" + end + + return ret + + end + def test_14_BuildNets l2n = RBA::LayoutToNetlist::new @@ -585,17 +637,49 @@ END l2n.build_all_nets(cm, ly, lmap, "NET_", nil, RBA::LayoutToNetlist::BNH_Disconnected, nil, "DEVICE_") - ly_au = RBA::Layout::new au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_reader_au_1.gds") - ly_au.read(au_file) + tmp_file = File.join($ut_testtmp, "l2n_reader_1.gds") - lmap.each do |li,v| - li_au = ly_au.layer(ly.get_info(li)) - ly_region = RBA::Region::new(ly.top_cell.begin_shapes_rec(li)) - ly_au_region = RBA::Region::new(ly_au.top_cell.begin_shapes_rec(li_au)) - info = ly.get_info(li).to_s + ":" - assert_equal(info + (ly_region ^ ly_au_region).to_s, info) - end + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) + + # build_all_nets with int-to-int layer map + + ly = RBA::Layout::new + ly.create_cell("TOP") + + cm = l2n.cell_mapping_into(ly, ly.top_cell) + + lmap = { + ly.insert_layer(RBA::LayerInfo::new(10, 0)) => l2n.layer_index("psd"), + ly.insert_layer(RBA::LayerInfo::new(11, 0)) => l2n.layer_index("nsd"), + ly.insert_layer(RBA::LayerInfo::new(3, 0)) => l2n.layer_index("poly"), + ly.insert_layer(RBA::LayerInfo::new(4, 0)) => l2n.layer_index("diff_cont"), + ly.insert_layer(RBA::LayerInfo::new(5, 0)) => l2n.layer_index("poly_cont"), + ly.insert_layer(RBA::LayerInfo::new(6, 0)) => l2n.layer_index("metal1"), + ly.insert_layer(RBA::LayerInfo::new(7, 0)) => l2n.layer_index("via1"), + ly.insert_layer(RBA::LayerInfo::new(8, 0)) => l2n.layer_index("metal2") + } + + l2n.build_all_nets(cm, ly, lmap, "NET_", nil, RBA::LayoutToNetlist::BNH_Disconnected, nil, "DEVICE_") + + au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_reader_au_1.gds") + tmp_file = File.join($ut_testtmp, "l2n_reader_1.gds") + + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) + + # build_all_nets with auto layer map + + ly = RBA::Layout::new + ly.create_cell("TOP") + + cm = l2n.cell_mapping_into(ly, ly.top_cell) + + l2n.build_all_nets(cm, ly, nil, "NET_", nil, RBA::LayoutToNetlist::BNH_Disconnected, nil, "DEVICE_") + + au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_lmap_au.gds") + tmp_file = File.join($ut_testtmp, "l2n_lmap.gds") + + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) # build_nets @@ -622,17 +706,29 @@ END l2n.build_nets(nets, cm, ly, lmap, "NET_", nil, RBA::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_") - ly_au = RBA::Layout::new au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_reader_au_1d.gds") - ly_au.read(au_file) + tmp_file = File.join($ut_testtmp, "l2n_reader_1d.gds") - lmap.each do |li,v| - li_au = ly_au.layer(ly.get_info(li)) - ly_region = RBA::Region::new(ly.top_cell.begin_shapes_rec(li)) - ly_au_region = RBA::Region::new(ly_au.top_cell.begin_shapes_rec(li_au)) - info = ly.get_info(li).to_s + ":" - assert_equal(info + (ly_region ^ ly_au_region).to_s, info) - end + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) + + # build_nets + + ly = RBA::Layout::new + ly.create_cell("TOP") + + cm = l2n.cell_mapping_into(ly, ly.top_cell) + + nets = [ + l2n.netlist.circuit_by_name("RINGO").net_by_name("VSS"), + l2n.netlist.circuit_by_name("RINGO").net_by_name("VDD") + ] + + l2n.build_nets(nets, cm, ly, nil, "NET_", nil, RBA::LayoutToNetlist::BNH_SubcircuitCells, "CIRCUIT_", "DEVICE_") + + au_file = File.join($ut_testsrc, "testdata", "algo", "l2n_lmap_au_2.gds") + tmp_file = File.join($ut_testtmp, "l2n_lmap_2.gds") + + assert_equal(true, compare_layouts(ly, au_file, tmp_file)) end @@ -1027,6 +1123,132 @@ END end + def test_22_Layers + + ly = RBA::Layout::new + ly.read(File.join($ut_testsrc, "testdata", "algo", "device_extract_l1.gds")) + + l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, ly.top_cell, [])) + assert_equal(l2n.original_layout.object_id, ly.object_id) + assert_equal(l2n.original_top_cell.cell_index, ly.top_cell.cell_index) + + # only plain connectivity + + ractive = l2n.make_layer( ly.layer(2, 0), "active" ) + rpoly = l2n.make_polygon_layer( ly.layer(3, 0), "poly" ) + rlabels = l2n.make_text_layer( ly.layer(6, 1), "labels" ) + rptemp = l2n.make_polygon_layer( "poly_temp" ) + rltemp = l2n.make_text_layer ( "labels_temp" ) + + rsd = ractive - rpoly + l2n.register(rsd, "sd") + + li_ptemp = l2n.layer_index(rptemp) + assert_equal(l2n.layer_name(li_ptemp), "poly_temp") + assert_equal(l2n.polygons_by_index(li_ptemp).to_s, "") + + li_ltemp = l2n.layer_index(rltemp) + assert_equal(l2n.layer_name(li_ltemp), "labels_temp") + assert_equal(l2n.texts_by_index(li_ltemp).to_s, "") + + assert_equal(l2n.layer_name(ractive), "active") + assert_equal(l2n.layer_name(rpoly), "poly") + assert_equal(l2n.layer_name(rsd), "sd") + assert_equal(l2n.layer_name(rlabels), "labels") + + li_active = l2n.layer_index("active") + li_labels = l2n.layer_index("labels") + assert_equal(l2n.layer_index(ractive), li_active) + + ractive2 = l2n.layer_by_index(li_active) + assert_equal(ractive.to_s, ractive2.to_s) + + ractive2 = l2n.layer_by_name("active") + assert_equal(ractive.to_s, ractive2.to_s) + + ractive2 = l2n.polygons_by_index(li_active) + assert_equal(ractive.to_s, ractive2.to_s) + + ractive2 = l2n.polygons_by_name("active") + assert_equal(ractive.to_s, ractive2.to_s) + + rlabels2 = l2n.texts_by_index(li_labels) + assert_equal(rlabels.to_s, rlabels2.to_s) + + rlabels2 = l2n.texts_by_name("labels") + assert_equal(rlabels.to_s, rlabels2.to_s) + + end + + def test_23_ShapesOfNet + + ly = RBA::Layout::new + top = ly.create_cell("TOP") + + l1 = ly.layer(1, 0) + l2 = ly.layer(2, 0) + l3 = ly.layer(3, 0) + + top.shapes(l1).insert(RBA::DBox::new(0, 0, 1000, 10)) + top.shapes(l2).insert(RBA::DBox::new(0, 0, 10, 10)) + top.shapes(l2).insert(RBA::DBox::new(990, 0, 1000, 10)) + top.shapes(l3).insert(RBA::DText::new("A", RBA::DTrans::new(5, 5))) + top.shapes(l3).insert(RBA::DText::new("B", RBA::DTrans::new(995, 5))) + + l2n = RBA::LayoutToNetlist::new(RBA::RecursiveShapeIterator::new(ly, top, [])) + l1r = l2n.make_polygon_layer(l1, "L1") + l2r = l2n.make_polygon_layer(l2, "L1.pin") + l3r = l2n.make_text_layer(l3, "L1.label") + + l2n.connect(l1r) + l2n.connect(l1r, l2r) + l2n.connect(l2r) + l2n.connect(l2r, l3r) + + l2n.extract_netlist + + nl = l2n.netlist + net = nl.top_circuit.net_by_name("A,B") + + assert_equal(l2n.shapes_of_net(net, l1r).to_s, "(0,0;0,10000;1000000,10000;1000000,0)") + assert_equal(l2n.shapes_of_net(net, l2r).to_s, "(0,0;0,10000;10000,10000;10000,0);(990000,0;990000,10000;1000000,10000;1000000,0)") + assert_equal(l2n.shapes_of_net(net, l3r).to_s, "('A',m45 0,0);('B',m135 0,0)") + + assert_equal(l2n.polygons_of_net(net, l2n.layer_index(l1r)).to_s, "(0,0;0,10000;1000000,10000;1000000,0)") + assert_equal(l2n.polygons_of_net(net, l2n.layer_index(l2r)).to_s, "(0,0;0,10000;10000,10000;10000,0);(990000,0;990000,10000;1000000,10000;1000000,0)") + assert_equal(l2n.polygons_of_net(net, l2n.layer_index(l3r)).to_s, "") + + assert_equal(l2n.texts_of_net(net, l2n.layer_index(l1r)).to_s, "") + assert_equal(l2n.texts_of_net(net, l2n.layer_index(l2r)).to_s, "") + assert_equal(l2n.texts_of_net(net, l2n.layer_index(l3r)).to_s, "('A',m45 0,0);('B',m135 0,0)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l1r, true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "box (0,0;1000000,10000)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l2r, true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "box (0,0;10000,10000)/box (990000,0;1000000,10000)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l2n.layer_index(l2r), true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "box (0,0;10000,10000)/box (990000,0;1000000,10000)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l3r, true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "text ('A',m45 0,0)/text ('B',m135 0,0)") + + shapes = RBA::Shapes::new + # wrong container type, correct content (layer_by_index gives a Region) + l2n.shapes_of_net(net, l2n.layer_by_index(l2n.layer_index(l3r)), true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "text ('A',m45 0,0)/text ('B',m135 0,0)") + + shapes = RBA::Shapes::new + l2n.shapes_of_net(net, l2n.layer_index(l3r), true, shapes) + assert_equal(shapes.each.collect(&:to_s).join("/"), "text ('A',m45 0,0)/text ('B',m135 0,0)") + + end + end load("test_epilogue.rb")