Merge pull request #1732 from KLayout/wip

Wip
This commit is contained in:
Matthias Köfferlein 2024-06-07 14:42:44 +02:00 committed by GitHub
commit 9d8a438d1d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 454 additions and 11 deletions

View File

@ -524,8 +524,8 @@ class DB_PUBLIC hnp_interaction_receiver
public:
typedef typename local_cluster<T>::box_type box_type;
hnp_interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans)
: mp_conn (&conn), m_any (false), m_soft_mode (0), m_trans (trans)
hnp_interaction_receiver (const Connectivity &conn, const db::ICplxTrans &trans, std::map<unsigned int, std::set<const T *> > *interacting_this, std::map<unsigned int, std::set<const T *> > *interacting_other)
: mp_conn (&conn), m_any (false), m_soft_mode (0), m_trans (trans), mp_interacting_this (interacting_this), mp_interacting_other (interacting_other)
{
// .. nothing yet ..
}
@ -534,6 +534,12 @@ public:
{
int soft = 0;
if (mp_conn->interacts (*s1, l1, *s2, l2, m_trans, soft)) {
if (mp_interacting_this) {
(*mp_interacting_this) [l1].insert (s1);
}
if (mp_interacting_other) {
(*mp_interacting_other) [l2].insert (s2);
}
if (soft == 0 || (m_soft_mode != 0 && m_soft_mode != soft)) {
m_soft_mode = 0;
m_any = true;
@ -543,9 +549,14 @@ public:
}
}
bool any () const
{
return m_any || m_soft_mode != 0;
}
bool stop () const
{
return m_any;
return m_any && ! mp_interacting_other && ! mp_interacting_this;
}
int soft_mode () const
@ -558,6 +569,7 @@ private:
bool m_any;
int m_soft_mode;
db::ICplxTrans m_trans;
std::map<unsigned int, std::set<const T *> > *mp_interacting_this, *mp_interacting_other;
};
template <class T, class Trans>
@ -619,9 +631,31 @@ local_cluster<T>::interacts (const db::Cell &cell, const db::ICplxTrans &trans,
return false;
}
template <class T>
static
void collect_interactions_in_original_order (const std::map<unsigned int, typename local_cluster<T>::tree_type> &shapes, const std::map<unsigned int, std::set<const T *> > &interacting, std::map<unsigned int, std::vector<const T *> > &interacting_out)
{
for (typename std::map<unsigned int, std::set<const T *> >::const_iterator i = interacting.begin (); i != interacting.end (); ++i) {
std::vector<const T *> &t = interacting_out [i->first];
auto s = shapes.find (i->first);
if (s == shapes.end ()) {
continue;
}
t.reserve (t.size () + i->second.size ());
for (auto j = s->second.begin (); j != s->second.end (); ++j) {
if (i->second.find (j.operator-> ()) != i->second.end ()) {
t.push_back (j.operator-> ());
}
}
}
}
template <class T>
bool
local_cluster<T>::interacts (const local_cluster<T> &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft) const
local_cluster<T>::interacts (const local_cluster<T> &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft, std::map<unsigned int, std::vector<const T *> > *interacting_this, std::map<unsigned int, std::vector<const T *> > *interacting_other) const
{
db::box_convert<T> bc;
@ -679,11 +713,26 @@ local_cluster<T>::interacts (const local_cluster<T> &other, const db::ICplxTrans
}
}
hnp_interaction_receiver<T> rec (conn, trans);
std::map<unsigned int, std::set<const T *> > is_this, is_other;
std::map<unsigned int, std::set<const T *> > *p_is_this = interacting_this ? &is_this : 0;
std::map<unsigned int, std::set<const T *> > *p_is_other = interacting_other ? &is_other : 0;
hnp_interaction_receiver<T> rec (conn, trans, p_is_this, p_is_other);
scanner.process (rec, 1 /*==touching*/, bc, bc_t);
if (rec.any ()) {
if (p_is_this) {
collect_interactions_in_original_order (m_shapes, *p_is_this, *interacting_this);
}
if (p_is_other) {
collect_interactions_in_original_order (other.m_shapes, *p_is_other, *interacting_other);
}
if (! scanner.process (rec, 1 /*==touching*/, bc, bc_t) || rec.soft_mode () != 0) {
soft = rec.soft_mode ();
return true;
} else {
return false;
}

View File

@ -326,8 +326,10 @@ public:
*
* "trans" is the transformation which is applied to the other cluster before
* the test.
* If non-null "interacting_this" will receive all interacting shapes from *this in case of success.
* If non-null "interacting_other" will receive all interacting shapes from other in case of success.
*/
bool interacts (const local_cluster<T> &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft) const;
bool interacts (const local_cluster<T> &other, const db::ICplxTrans &trans, const Connectivity &conn, int &soft, std::map<unsigned int, std::vector<const T *> > *interacting_this = 0, std::map<unsigned int, std::vector<const T *> > *interacting_other = 0) const;
/**
* @brief Tests whether this cluster interacts with the given cell

View File

@ -1308,6 +1308,102 @@ static bool deliver_shapes_of_net (bool recursive, const db::Netlist *nl, const
return true;
}
void
LayoutToNetlist::collect_shapes_of_pin (const local_cluster<db::NetShape> &c, const db::Net *other_net, const db::ICplxTrans &sc_trans, const db::ICplxTrans &trans, std::map<unsigned int, db::Region> &result) const
{
if (! other_net || ! other_net->circuit ()) {
return;
}
auto cc_other = m_net_clusters.clusters_per_cell (other_net->circuit ()->cell_index ());
auto c_other = cc_other.cluster_by_id (other_net->cluster_id ());
std::map<unsigned int, std::vector<const db::NetShape *> > interacting;
int soft = 0;
if (c.interacts (c_other, sc_trans, m_conn, soft, 0, &interacting)) {
auto t = trans * sc_trans;
for (auto i = interacting.begin (); i != interacting.end (); ++i) {
db::Region &r = result [i->first];
for (auto s = i->second.begin (); s != i->second.end (); ++s) {
deliver_shape (**s, r, t, 0);
}
}
}
double dbu = internal_layout ()->dbu ();
for (auto p = other_net->begin_subcircuit_pins (); p != other_net->end_subcircuit_pins (); ++p) {
db::ICplxTrans sc_trans2 = sc_trans * db::CplxTrans (dbu).inverted () * p->subcircuit ()->trans () * db::CplxTrans (dbu);
const db::Net *other_net2 = p->subcircuit ()->circuit_ref ()->net_for_pin (p->pin_id ());
collect_shapes_of_pin (c, other_net2, sc_trans2, trans, result);
}
}
std::map<unsigned int, db::Region>
LayoutToNetlist::shapes_of_pin (const db::NetSubcircuitPinRef &pin, const db::ICplxTrans &trans) const
{
std::map<unsigned int, db::Region> result;
const db::Net *net = pin.net ();
if (! net || ! net->circuit () || ! pin.subcircuit () || ! pin.subcircuit ()->circuit_ref ()) {
return result;
}
auto cc = m_net_clusters.clusters_per_cell (net->circuit ()->cell_index ());
auto c = cc.cluster_by_id (net->cluster_id ());
double dbu = internal_layout ()->dbu ();
db::ICplxTrans sc_trans = db::CplxTrans (dbu).inverted () * pin.subcircuit ()->trans () * db::CplxTrans (dbu);
const db::Net *other_net = pin.subcircuit ()->circuit_ref ()->net_for_pin (pin.pin_id ());
collect_shapes_of_pin (c, other_net, sc_trans, trans, result);
return result;
}
std::map<unsigned int, db::Region>
LayoutToNetlist::shapes_of_terminal (const db::NetTerminalRef &terminal, const db::ICplxTrans &trans) const
{
std::map<unsigned int, db::Region> result;
const db::Net *net = terminal.net ();
if (! net || ! net->circuit () || ! terminal.device () || ! terminal.device ()->device_abstract ()) {
return result;
}
auto cc = m_net_clusters.clusters_per_cell (net->circuit ()->cell_index ());
auto c = cc.cluster_by_id (net->cluster_id ());
double dbu = internal_layout ()->dbu ();
db::ICplxTrans d_trans = db::CplxTrans (dbu).inverted () * terminal.device ()->trans () * db::CplxTrans (dbu);
auto cc_other = m_net_clusters.clusters_per_cell (terminal.device ()->device_abstract ()->cell_index ());
auto c_other = cc_other.cluster_by_id (terminal.device ()->device_abstract ()->cluster_id_for_terminal (terminal.terminal_id ()));
std::map<unsigned int, std::vector<const db::NetShape *> > interacting;
int soft = 0;
if (c.interacts (c_other, d_trans, m_conn, soft, 0, &interacting)) {
auto t = trans * d_trans;
for (auto i = interacting.begin (); i != interacting.end (); ++i) {
db::Region &r = result [i->first];
for (auto s = i->second.begin (); s != i->second.end (); ++s) {
deliver_shape (**s, r, t, 0);
}
}
}
return result;
}
void LayoutToNetlist::shapes_of_net (const db::Net &net, const db::Region &of_layer, bool recursive, db::Shapes &to, db::properties_id_type propid, const ICplxTrans &trans) const
{
unsigned int lid = layer_of (of_layer);

View File

@ -828,6 +828,32 @@ public:
return m_net_clusters;
}
/**
* @brief Gets the shapes that make the subcircuit pin
*
* This method looks up all shapes from the subcircuit that interact with the shapes of the net
* the subcircuit pin lives in. It will return a map of layer index vs. Region with these
* shapes, transformed into the coordinate space of the net.
*
* Note that this method only considers top-down interactions between the shapes of the net
* and subcircuits on any level below, but not between subcircuits.
* It is useful only for certain topologies - i.e. digital nets connecting gate cells.
*/
std::map<unsigned int, db::Region> shapes_of_pin (const db::NetSubcircuitPinRef &pin, const db::ICplxTrans &trans = db::ICplxTrans ()) const;
/**
* @brief Gets the shapes that make the device terminal
*
* This method looks up all shapes from the device terminals that interact with the shapes of the net
* the device terminal lives in. It will return a map of layer index vs. Region with these
* shapes, transformed into the coordinate space of the net.
*
* Note that this method only considers top-down interactions between the shapes of the net
* and subcircuits on any level below, but not between subcircuits and devices.
* It is useful for flat-extracted netlists for example.
*/
std::map<unsigned int, db::Region> shapes_of_terminal (const db::NetTerminalRef &terminal, const db::ICplxTrans &trans = db::ICplxTrans ()) const;
/**
* @brief Returns all shapes of a specific net and layer.
*
@ -1099,6 +1125,7 @@ private:
void ensure_layout () const;
std::string make_new_name (const std::string &stem = std::string ());
db::CellMapping make_cell_mapping_into (db::Layout &layout, db::Cell &cell, const std::vector<const db::Net *> *nets, bool with_device_cells);
void collect_shapes_of_pin (const local_cluster<db::NetShape> &c, const db::Net *other_net, const db::ICplxTrans &sc_trans, const db::ICplxTrans &trans, std::map<unsigned int, db::Region> &result) const;
void connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b);
size_t connect_global_impl (const db::ShapeCollection &l, const std::string &gn);
void soft_connect_impl (const db::ShapeCollection &a, const db::ShapeCollection &b);

View File

@ -191,16 +191,22 @@ gsi::ClassExt<db::LoadLayoutOptions> common_reader_options (
gsi::EnumIn<db::LoadLayoutOptions, db::CellConflictResolution> decl_dbCommonReader_CellConflictResolution ("db", "CellConflictResolution",
gsi::enum_const ("AddToCell", db::AddToCell,
"@brief Add content to existing cell\n"
"This is the mode use in before version 0.27. Content of new cells is simply added to existing cells with the same name."
"This is the mode use in before version 0.27. Content of new cells is simply added to existing cells with the same name.\n"
"Before version 0.29.2, this mode also merged instances, rendering it difficult to merge two identical cell hierarchies.\n"
"Since version 0.29.2, no instance duplicates are generated. Instead only new instances are added to existing cells.\n"
"With this feature in place, it is safe to merge two identical cell hierarchies stored in different files using AddToCell mode.\n"
"In that application, the shapes and layers of the layouts are combined, but the cell hierarchy stays identical."
) +
gsi::enum_const ("OverwriteCell", db::OverwriteCell,
"@brief The old cell is overwritten entirely (including child cells which are not used otherwise)\n"
"@brief The old cell is overwritten entirely (including child cells which are not used otherwise).\n"
) +
gsi::enum_const ("SkipNewCell", db::SkipNewCell,
"@brief The new cell is skipped entirely (including child cells which are not used otherwise)\n"
"@brief The new cell is skipped entirely (including child cells which are not used otherwise).\n"
) +
gsi::enum_const ("RenameCell", db::RenameCell,
"@brief The new cell will be renamed to become unique\n"
"@brief The new cell will be renamed to become unique.\n"
"In this mode, two files are are combined rendering independent cell hierarchies coming from the original files.\n"
"Cells may be renamed however. Also, new top cells will appear after merging a file into the layout using RenameCell mode.\n"
),
"@brief This enum specifies how cell conflicts are handled if a layout read into another layout and a cell name conflict arises.\n"
"Until version 0.26.8 and before, the mode was always 'AddToCell'. On reading, a cell was 'reopened' when encountering a cell name "

View File

@ -678,6 +678,44 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
gsi::method ("netlist", &db::LayoutToNetlist::netlist,
"@brief gets the netlist extracted (0 if no extraction happened yet)\n"
) +
gsi::method ("shapes_of_pin", &db::LayoutToNetlist::shapes_of_pin, gsi::arg ("pin"), gsi::arg ("trans", db::ICplxTrans (), "unity"),
"@brief Returns all shapes of the given subcircuit pin that make a connection to the net the pin lives in.\n"
"This will return all shapes from the subcircuit attached by the given pin that interact with the net the pin lives in.\n"
"This method returns a \\Region object with the shapes per layer where interactions are found.\n"
"The layers are given as layer indexes.\n"
"\n"
"The returned shapes are already transformed into the coordinate system of the net (see \\shapes_of_net for example).\n"
"An additional transformation can be applied using the optional \\trans argument.\n"
"\n"
"Note, that this method only considers interations between net shapes and subcircuits on every level below, "
"but not between subcircuits.\n"
"It can be used for example for digital nets connecting gate cells. In the general case however, nets may be formed\n"
"also by touching subcircuits. In that case, the nets do not have shapes of their own and this function cannot detect\n"
"the pin shapes.\n"
"\n"
"The call of this method may not be cheap, specificially if large nets are involved.\n"
"\n"
"This method has been introduced in version 0.29.2."
) +
gsi::method ("shapes_of_terminal", &db::LayoutToNetlist::shapes_of_terminal, gsi::arg ("terminal"), gsi::arg ("trans", db::ICplxTrans (), "unity"),
"@brief Returns all shapes of the given device terminal that make a connection to the net the terminal lives in.\n"
"This will return all shapes from the device attached by the given terminal that interact with the net the terminal lives in.\n"
"This method returns a \\Region object with the shapes per layer where interactions are found.\n"
"The layers are given as layer indexes.\n"
"\n"
"The returned shapes are already transformed into the coordinate system of the net (see \\shapes_of_net for example).\n"
"An additional transformation can be applied using the optional \\trans argument.\n"
"\n"
"Note, that this method only considers interations between net shapes and the device connected by the terminal, "
"but not between subcircuits on the net and the device.\n"
"It can be used for example for flat-extracted, transistor-level netlists. In the general case however, nets may be formed\n"
"also by subcircuits touching devices. In that case, the nets do not have shapes of their own and this function cannot detect\n"
"the terminal shapes.\n"
"\n"
"The call of this method may not be cheap, specificially if large nets are involved.\n"
"\n"
"This method has been introduced in version 0.29.2."
) +
gsi::factory ("shapes_of_net", (db::Region *(db::LayoutToNetlist::*) (const db::Net &, const db::Region &, bool, const db::ICplxTrans &) const) &db::LayoutToNetlist::shapes_of_net, gsi::arg ("net"), gsi::arg ("of_layer"), gsi::arg ("recursive", true), gsi::arg ("trans", db::ICplxTrans (), "unity"),
"@brief Returns all shapes of a specific net and layer.\n"
"If 'recursive'' is true, the returned region will contain the shapes of\n"

View File

@ -1827,3 +1827,87 @@ TEST(112_Waiving)
compare_text_files (report, au_report);
}
TEST(120_ShapesOfPin)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_120.drc";
// apart from that it's a variant of 14b ...
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_120.gds";
std::string au = tl::testdata ();
au += "/drc/drcSimpleTests_au120.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(121_ShapesOfTerminal)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_121.drc";
// apart from that it's a variant of 14b ...
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_121.gds";
std::string au = tl::testdata ();
au += "/drc/drcSimpleTests_au121.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);
}

52
testdata/drc/drcSimpleTests_120.drc vendored Normal file
View File

@ -0,0 +1,52 @@
source $drc_test_source
target $drc_test_target
deep
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
name(l1, "l1")
name(l2, "l2")
name(l3, "l3")
connect(l1, l2)
connect(l2, l3)
netlist
l1_out = polygons
l2_out = polygons
l3_out = polygons
output_layers = {
"l1" => l1_out,
"l2" => l2_out,
"l3" => l3_out
}
[ "A", "B", "C" ].each do |n|
net = l2n_data.netlist.circuit_by_name("TOP").net_by_name(n)
if net
net.each_subcircuit_pin do |pin|
shapes = l2n_data.shapes_of_pin(pin, RBA::Trans::new(RBA::Vector::new(100, 200)))
shapes.keys.each do |li|
output_layers[l2n_data.layer_name(li)].data.insert(shapes[li])
end
end
end
end
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
l1_out.output(101, 0)
l2_out.output(102, 0)
l3_out.output(103, 0)

BIN
testdata/drc/drcSimpleTests_120.gds vendored Normal file

Binary file not shown.

89
testdata/drc/drcSimpleTests_121.drc vendored Normal file
View File

@ -0,0 +1,89 @@
source $drc_test_source
target $drc_test_target
# only works flat:
# deep
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
active = input(10, 0)
poly = input(11, 0)
contact = input(12, 0)
sd = active - poly
gate = active & poly
name(l1, "l1")
name(l2, "l2")
name(l3, "l3")
name(sd, "sd")
name(poly, "poly")
name(gate, "gate")
name(contact, "contact")
mos_ex = RBA::DeviceExtractorMOS3Transistor::new("MOS")
extract_devices(mos_ex, { "SD" => sd, "G" => gate, "P" => poly })
connect(contact, poly)
connect(contact, sd)
connect(l1, contact)
connect(l1, l2)
connect(l2, l3)
netlist
l1_out = polygons
l2_out = polygons
l3_out = polygons
contact_out = polygons
sd_out = polygons
gate_out = polygons
poly_out = polygons
output_layers = {
"l1" => l1_out,
"l2" => l2_out,
"l3" => l3_out,
"contact" => contact_out,
"poly" => poly_out,
"gate" => gate_out,
"sd" => sd_out
}
[ "A", "B", "C" ].each do |n|
net = l2n_data.netlist.circuit_by_name("TOP").net_by_name(n)
if net
net.each_terminal do |terminal|
shapes = l2n_data.shapes_of_terminal(terminal, RBA::Trans::new(RBA::Vector::new(100, 200)))
shapes.keys.each do |li|
output_layers[l2n_data.layer_name(li)].data.insert(shapes[li])
end
end
end
end
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
sd.output(10, 0)
poly.output(11, 0)
contact.output(12, 0)
gate.output(13, 0)
l1_out.output(101, 0)
l2_out.output(102, 0)
l3_out.output(103, 0)
sd_out.output(110, 0)
poly_out.output(111, 0)
contact_out.output(112, 0)
gate_out.output(113, 0)

BIN
testdata/drc/drcSimpleTests_121.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au120.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au121.gds vendored Normal file

Binary file not shown.