L2N log entries with net references

* Log entries on the L2N object can now have net references
  (by expanded name) and the nets will be highlighted
  when the log entry is selected in the netlist browser
  For an application see drcSimpleTests:147.
* New function ("db") in evaluate_nets
* BUGFIX: proper computation of transformations for multiple
  selections of nets in netlist browser
This commit is contained in:
Matthias Koefferlein 2026-01-04 22:46:29 +01:00
parent 7b002a1815
commit 38ddffc645
19 changed files with 471 additions and 40 deletions

View File

@ -188,7 +188,7 @@ namespace db
* scale(<mag>) - magnification (default is 1) [short key: S]
*
* [message-entry]:
* message([severity] [message|message-geometry|message-cell|message-category|any]*) - message entry [short key: H]
* message([severity] [message|message-geometry|message-cell|message-category|message-net|any]*) - message entry [short key: H]
*
* [message]:
* description(<name>) - message text [short key: B]
@ -199,6 +199,9 @@ namespace db
* [message-cell]:
* cell(<name>) - message cell [short key: C]
*
* [message-net]:
* net(<name>) - message net name [short key: N]
*
* [message-category]:
* cat(<name> <name>?) - message category with optional description [short key: X]
*

View File

@ -225,6 +225,18 @@ bool LayoutToNetlistStandardReader::read_message_cell (std::string &cell_name)
}
}
bool LayoutToNetlistStandardReader::read_message_net (std::string &net_name)
{
if (test (skeys::net_key) || test (lkeys::net_key)) {
Brace br (this);
read_word_or_quoted (net_name);
br.done ();
return true;
} else {
return false;
}
}
bool LayoutToNetlistStandardReader::read_message_geometry (db::DPolygon &polygon)
{
if (test (skeys::polygon_key) || test (lkeys::polygon_key)) {
@ -258,7 +270,7 @@ bool LayoutToNetlistStandardReader::read_message_cat (std::string &category_name
void LayoutToNetlistStandardReader::read_message_entry (db::LogEntryData &data)
{
Severity severity (db::NoSeverity);
std::string msg, cell_name, category_name, category_description;
std::string msg, cell_name, net_name, category_name, category_description;
db::DPolygon geometry;
Brace br (this);
@ -269,6 +281,8 @@ void LayoutToNetlistStandardReader::read_message_entry (db::LogEntryData &data)
// continue
} else if (read_message_cell (cell_name)) {
// continue
} else if (read_message_net (net_name)) {
// continue
} else if (read_message_cat (category_name, category_description)) {
// continue
} else if (read_message_geometry (geometry)) {
@ -282,6 +296,7 @@ void LayoutToNetlistStandardReader::read_message_entry (db::LogEntryData &data)
data.set_severity (severity);
data.set_message (msg);
data.set_cell_name (cell_name);
data.set_net_name (net_name);
data.set_category_description (category_description);
data.set_category_name (category_name);
data.set_geometry (geometry);

View File

@ -164,6 +164,7 @@ private:
db::Point read_point ();
void read_message_entry (db::LogEntryData &data);
bool read_message_cell (std::string &cell_name);
bool read_message_net (std::string &net_name);
bool read_message_geometry (db::DPolygon &polygon);
bool read_message_cat (std::string &category_name, std::string &category_description);
};

View File

@ -211,6 +211,10 @@ void std_writer_impl<Keys>::write_log_entry (TokenizedOutput &stream, const LogE
TokenizedOutput (stream, Keys::cell_key, true) << tl::to_word_or_quoted_string (le.cell_name ());
}
if (! le.net_name ().empty ()) {
TokenizedOutput (stream, Keys::net_key, true) << tl::to_word_or_quoted_string (le.net_name ());
}
if (! le.category_name ().empty ()) {
TokenizedOutput o (stream, Keys::cat_key, true);
o << tl::to_word_or_quoted_string (le.category_name ());

View File

@ -81,19 +81,25 @@ static LogEntryStringRepository s_strings;
// LogEntryData implementation
LogEntryData::LogEntryData ()
: m_severity (NoSeverity), m_cell_name (0), m_message (0), m_category_name (0), m_category_description (0)
: m_severity (NoSeverity), m_cell_name (0), m_net_name (0), m_message (0), m_category_name (0), m_category_description (0)
{
// .. nothing yet ..
}
LogEntryData::LogEntryData (Severity s, const std::string &msg)
: m_severity (s), m_cell_name (0), m_message (s_strings.id_for_string (msg)), m_category_name (0), m_category_description (0)
: m_severity (s), m_cell_name (0), m_net_name (0), m_message (s_strings.id_for_string (msg)), m_category_name (0), m_category_description (0)
{
// .. nothing yet ..
}
LogEntryData::LogEntryData (Severity s, const std::string &cell_name, const std::string &msg)
: m_severity (s), m_cell_name (s_strings.id_for_string (cell_name)), m_message (s_strings.id_for_string (msg)), m_category_name (0), m_category_description (0)
: m_severity (s), m_cell_name (s_strings.id_for_string (cell_name)), m_net_name (0), m_message (s_strings.id_for_string (msg)), m_category_name (0), m_category_description (0)
{
// .. nothing yet ..
}
LogEntryData::LogEntryData (Severity s, const std::string &cell_name, const std::string &net_name, const std::string &msg)
: m_severity (s), m_cell_name (s_strings.id_for_string (cell_name)), m_net_name (s_strings.id_for_string (net_name)), m_message (s_strings.id_for_string (msg)), m_category_name (0), m_category_description (0)
{
// .. nothing yet ..
}
@ -104,6 +110,7 @@ LogEntryData::operator== (const LogEntryData &other) const
return m_severity == other.m_severity &&
m_message == other.m_message &&
m_cell_name == other.m_cell_name &&
m_net_name == other.m_net_name &&
m_geometry == other.m_geometry &&
m_category_name == other.m_category_name &&
m_category_description == other.m_category_description;
@ -157,6 +164,18 @@ LogEntryData::set_cell_name (const std::string &n)
m_cell_name = s_strings.id_for_string (n);
}
const std::string &
LogEntryData::net_name () const
{
return s_strings.string_for_id (m_net_name);
}
void
LogEntryData::set_net_name (const std::string &n)
{
m_net_name = s_strings.id_for_string (n);
}
std::string
LogEntryData::to_string (bool with_geometry) const
{
@ -179,10 +198,24 @@ LogEntryData::to_string (bool with_geometry) const
}
}
if (m_cell_name != 0) {
res += tl::to_string (tr ("In cell "));
res += cell_name ();
res += ": ";
if (m_net_name != 0) {
if (m_cell_name != 0) {
res += tl::to_string (tr ("In net "));
res += net_name ();
res += tl::to_string (tr (" in circuit "));
res += cell_name ();
res += ": ";
} else {
res += tl::to_string (tr ("In net "));
res += net_name ();
res += ": ";
}
} else {
if (m_cell_name != 0) {
res += tl::to_string (tr ("In cell "));
res += cell_name ();
res += ": ";
}
}
res += msg;

View File

@ -68,6 +68,11 @@ public:
*/
LogEntryData (Severity s, const std::string &cell_name, const std::string &msg);
/**
* @brief Creates an error with the severity, a cell (circuit) name, a net name and a message
*/
LogEntryData (Severity s, const std::string &cell_name, const std::string &net_name, const std::string &msg);
/**
* @brief Equality
*/
@ -158,6 +163,16 @@ public:
*/
void set_cell_name (const std::string &n);
/**
* @brief Gets the net name the error occurred in
*/
const std::string &net_name () const;
/**
* @brief Sets the net name
*/
void set_net_name (const std::string &n);
/**
* @brief Formats this message for printing
*/
@ -166,6 +181,7 @@ public:
private:
Severity m_severity;
string_id_type m_cell_name;
string_id_type m_net_name;
string_id_type m_message;
db::DPolygon m_geometry;
string_id_type m_category_name, m_category_description;

View File

@ -492,6 +492,28 @@ private:
MeasureNetEval *mp_eval;
};
class NetDbFunction
: public tl::EvalFunction
{
public:
NetDbFunction (MeasureNetEval *eval)
: mp_eval (eval)
{
// .. nothing yet ..
}
virtual void execute (const tl::ExpressionParserContext &context, tl::Variant &out, const std::vector<tl::Variant> &args, const std::map<std::string, tl::Variant> * /*kwargs*/) const
{
if (args.size () != 0) {
throw tl::EvalError (tl::to_string (tr ("'db' function does not take any argument")), context);
}
out = mp_eval->db_func ();
}
private:
MeasureNetEval *mp_eval;
};
class NetFunction
: public tl::EvalFunction
{
@ -558,7 +580,7 @@ private:
MeasureNetEval *mp_eval;
};
MeasureNetEval::MeasureNetEval (const db::LayoutToNetlist *l2n, double dbu)
MeasureNetEval::MeasureNetEval (LayoutToNetlist *l2n, double dbu)
: tl::Eval (), mp_l2n (l2n), m_dbu (dbu)
{
m_copy_merge = false;
@ -588,6 +610,7 @@ MeasureNetEval::init ()
define_function ("area", new NetAreaFunction (this));
define_function ("perimeter", new NetPerimeterFunction (this));
define_function ("net", new NetFunction (this));
define_function ("db", new NetDbFunction (this));
}
void
@ -701,4 +724,10 @@ MeasureNetEval::net_func () const
}
}
tl::Variant
MeasureNetEval::db_func () const
{
return tl::Variant::make_variant_ref (mp_l2n);
}
}

View File

@ -122,7 +122,7 @@ class DB_PUBLIC MeasureNetEval
: public tl::Eval
{
public:
MeasureNetEval (const db::LayoutToNetlist *l2n, double dbu);
MeasureNetEval (db::LayoutToNetlist *l2n, double dbu);
void set_primary_layer (unsigned int layer_index);
void set_secondary_layer (const std::string &name, unsigned int layer_index);
@ -144,6 +144,7 @@ private:
friend class NetAreaFunction;
friend class NetPerimeterFunction;
friend class NetFunction;
friend class NetDbFunction;
friend class NetSkipFunction;
friend class NetCopyFunction;
@ -153,7 +154,7 @@ private:
double area, perimeter;
};
const db::LayoutToNetlist *mp_l2n;
db::LayoutToNetlist *mp_l2n;
double m_dbu;
std::vector<unsigned int> m_layers;
mutable std::vector<unsigned int> m_copy_layers;
@ -173,6 +174,7 @@ private:
tl::Variant perimeter_func (int layer_index) const;
void copy_func (const std::vector<unsigned int> &layer_indexes, bool merge, size_t max_polygons) const;
tl::Variant net_func () const;
tl::Variant db_func () const;
};
}

View File

@ -1251,6 +1251,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"@li 'skip' or 'skip(flag)': will skip the primary shapes of that net when called with a true value or without one. See also 'copy'. @/li\n"
"@li 'copy(...)': see below for details @/li\n"
"@li 'net': the \\Net object of the current net @/li\n"
"@li 'db': the \\LayoutToDatabase object the netlist lives in @/li\n"
"@/ul\n"
"\n"
"If given, the 'dbu' argument gives the database unit to use for converting shape dimensions into micrometer units. "
@ -1294,7 +1295,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"where the second expression establishes 'skip' as the default and conditionally executes 'copy',\n"
"overriding 'skip'.\n"
"\n"
"The 'copy' function was added and the 'skip' argument was made optional in version 0.30.6."
"The 'copy' and 'db' functions were added and the 'skip' argument was made optional in version 0.30.6."
) +
// test API
gsi::method ("make_soft_connection_diodes=", &db::LayoutToNetlist::set_make_soft_connection_diodes, gsi::arg ("flag"), "@hide") +

View File

@ -23,11 +23,53 @@
#include "gsiDecl.h"
#include "gsiEnums.h"
#include "dbLog.h"
#include "dbNet.h"
#include "dbCircuit.h"
namespace gsi
{
db::LogEntryData *new_le1 (db::Severity severity, const std::string &msg)
{
return new db::LogEntryData (severity, msg);
}
db::LogEntryData *new_le2 (db::Severity severity, const std::string &cell_name, const std::string &msg)
{
return new db::LogEntryData (severity, cell_name, msg);
}
db::LogEntryData *new_le3 (db::Severity severity, const std::string &cell_name, const std::string &net_name, const std::string &msg)
{
return new db::LogEntryData (severity, cell_name, net_name, msg);
}
db::LogEntryData *new_le4 (db::Severity severity, const db::Net *net, const std::string &msg)
{
if (! net || ! net->circuit ()) {
return new db::LogEntryData (severity, msg);
} else {
return new db::LogEntryData (severity, net->circuit ()->name (), net->expanded_name (), msg);
}
}
Class<db::LogEntryData> decl_dbNetlistDeviceExtractorError ("db", "LogEntryData",
gsi::constructor ("new", &new_le1, gsi::arg ("severity"), gsi::arg ("msg"),
"@brief Creates a new LogEntry object with the given severity and message\n"
"This convenience constructor has been added in version 0.30.6\n"
) +
gsi::constructor ("new", &new_le2, gsi::arg ("severity"), gsi::arg ("cell_name"), gsi::arg ("msg"),
"@brief Creates a new LogEntry object with the given severity, cell or circuit name and message\n"
"This convenience constructor has been added in version 0.30.6\n"
) +
gsi::constructor ("new", &new_le3, gsi::arg ("severity"), gsi::arg ("cell_name"), gsi::arg ("new_name"), gsi::arg ("msg"),
"@brief Creates a new LogEntry object with the given severity, cell or circuit name, net name and message\n"
"This convenience constructor has been added in version 0.30.6\n"
) +
gsi::constructor ("new", &new_le4, gsi::arg ("severity"), gsi::arg ("net"), gsi::arg ("msg"),
"@brief Creates a new LogEntry object with the given severity and message and circuit and net name taken from the given \\Net object\n"
"This convenience constructor has been added in version 0.30.6\n"
) +
gsi::method ("severity", &db::LogEntryData::severity,
"@brief Gets the severity attribute.\n"
) +
@ -51,6 +93,21 @@ Class<db::LogEntryData> decl_dbNetlistDeviceExtractorError ("db", "LogEntryData"
"warning generated during device extraction, the cell name is "
"the circuit the device should have appeared in."
) +
gsi::method ("net_name", &db::LogEntryData::net_name,
"@brief Gets the net name.\n"
"See \\net_name= for details about this attribute."
"\n"
"The net_name attribute has been introduced in version 0.30.6.\n"
) +
gsi::method ("net_name=", &db::LogEntryData::set_net_name, gsi::arg ("net_name"),
"@brief Sets the net name.\n"
"The net (or circuit) name specifies the net the "
"log entry is related to.\n"
"\n"
"By convention, the net name is the expanded net name (see \\Net#expanded_name).\n"
"\n"
"The net_name attribute has been introduced in version 0.30.6.\n"
) +
gsi::method ("geometry", &db::LogEntryData::geometry,
"@brief Gets the geometry.\n"
"See \\geometry= for more details."

View File

@ -807,6 +807,7 @@ module DRC
#
# @ul
# @li "net" - the RBA::Net object of the current net @/li
# @li "db" - the RBA::LayoutToNetlist object the netlist lives in @/li
# @li "skip" or "skip(flag)" - if called with a 'true' argument (the default), the primary layer's shapes are not copied for this net @/li
# @li "copy(...)" - configures polygon output in a more elaborate way than "skip" (see below) @/li
# @li "put(name, value)" - places the value as a property with name 'name' (this must be a string) on the output shapes @/li

View File

@ -2076,3 +2076,35 @@ TEST(146d_edges_and_corners)
run_test (_this, "146", true);
}
TEST(147_MeasureNetsWithL2N)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_147.drc";
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_147.gds";
std::string au_output = tl::testdata ();
au_output += "/drc/drcSimpleTests_au147.l2n";
std::string output = this->tmp_file ("tmp.l2n");
{
// 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);
compare_text_files (output, au_output);
}

View File

@ -75,6 +75,24 @@ struct test_arg_func<gsi::VariantType>
}
};
template <>
struct test_arg_func<gsi::StringType>
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/)
{
*ret = arg.is_a_string ();
}
};
template <>
struct test_arg_func<gsi::ByteArrayType>
{
void operator () (bool *ret, const tl::Variant &arg, const gsi::ArgType & /*atype*/, bool /*loose*/, bool /*object_substitution*/)
{
*ret = arg.is_a_bytearray ();
}
};
template <>
struct test_arg_func<gsi::ObjectType>
{

View File

@ -816,15 +816,31 @@ NetlistBrowserPage::log_selection_changed ()
QModelIndexList selection = log_view->selectionModel ()->selectedIndexes ();
for (QModelIndexList::const_iterator i = selection.begin (); i != selection.end (); ++i) {
if (i->column () == 0) {
const db::LogEntryData *le = model->log_entry (*i);
if (le && le->geometry () != db::DPolygon () && ! le->cell_name ().empty ()) {
const db::Circuit *c = mp_database->netlist ()->circuit_by_name (le->cell_name ());
if (c) {
m_markers.push_back (std::make_pair (c, le->geometry ()));
}
if (i->column () != 0) {
continue;
}
const db::LogEntryData *le = model->log_entry (*i);
const db::Circuit *c = 0;
if (le && ! le->cell_name ().empty ()) {
c = mp_database->netlist ()->circuit_by_name (le->cell_name ());
}
// highlight geometries
if (c && le->geometry () != db::DPolygon ()) {
m_markers.push_back (std::make_pair (c, le->geometry ()));
}
// highlight nets
if (c && ! le->net_name ().empty ()) {
const db::Net *net = c->net_by_name (le->net_name ());
if (net) {
m_net_markers.push_back (std::make_pair (c, net));
}
}
}
update_highlights ();
@ -1291,6 +1307,7 @@ NetlistBrowserPage::clear_highlights ()
m_current_path = lay::NetlistObjectsPath ();
m_selected_paths.clear ();
m_markers.clear ();
m_net_markers.clear ();
update_highlights ();
}
@ -1350,6 +1367,31 @@ bbox_for_circuit (const db::Layout *layout, const db::Circuit *circuit)
return layout->cell (circuit->cell_index ()).bbox ();
}
static db::Box
bbox_for_net (const db::LayoutToNetlist *db, const db::Circuit *circuit, const db::Net *net)
{
db::Box bbox;
db::cell_index_type cell_index = circuit->cell_index ();
size_t cluster_id = net->cluster_id ();
const db::Connectivity &conn = db->connectivity ();
for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::Box layer_bbox;
db::recursive_cluster_shape_iterator<db::NetShape> shapes (db->net_clusters (), *layer, cell_index, cluster_id);
while (! shapes.at_end ()) {
layer_bbox += shapes->bbox ().transformed (shapes.trans ());
++shapes;
}
bbox += layer_bbox;
}
return bbox;
}
void
NetlistBrowserPage::adjust_view ()
{
@ -1425,22 +1467,7 @@ NetlistBrowserPage::adjust_view ()
} else if (net) {
db::cell_index_type cell_index = net->circuit ()->cell_index ();
size_t cluster_id = net->cluster_id ();
const db::Connectivity &conn = mp_database->connectivity ();
for (db::Connectivity::all_layer_iterator layer = conn.begin_layers (); layer != conn.end_layers (); ++layer) {
db::Box layer_bbox;
db::recursive_cluster_shape_iterator<db::NetShape> shapes (mp_database->net_clusters (), *layer, cell_index, cluster_id);
while (! shapes.at_end ()) {
layer_bbox += shapes->bbox ().transformed (shapes.trans ());
++shapes;
}
ebox += layer_bbox;
}
ebox += bbox_for_net (mp_database.get (), circuit, net);
} else if (circuit) {
ebox += bbox_for_circuit (layout, circuit);
@ -1461,6 +1488,17 @@ NetlistBrowserPage::adjust_view ()
}
// add net markers boxes
for (auto marker = m_net_markers.begin (); marker != m_net_markers.end (); ++marker) {
std::pair<bool, db::DCplxTrans> tr = trans_for (marker->first, *layout, *cell, m_cell_context_cache, cv.context_dtrans ());
if (tr.first) {
bbox += tr.second * db::CplxTrans (layout->dbu ()) * bbox_for_net (mp_database.get (), marker->first, marker->second);
}
}
if (! bbox.empty ()) {
std::vector<db::DCplxTrans> tv = mp_view->cv_transform_variants (m_cv_index);
@ -1739,26 +1777,52 @@ NetlistBrowserPage::update_highlights ()
// a map of display properties vs. layer properties
// correct DBU differences between the storage layout and the original layout
for (std::vector<db::DCplxTrans>::iterator t = tv.begin (); t != tv.end (); ++t) {
std::vector<db::DCplxTrans> tvt = tv;
for (std::vector<db::DCplxTrans>::iterator t = tvt.begin (); t != tvt.end (); ++t) {
*t = *t * trans * db::DCplxTrans (layout->dbu () / original_layout.dbu ());
}
if (path->net.first) {
if (produce_highlights_for_net (path->net.first, n_markers, display_by_lp, tv)) {
if (produce_highlights_for_net (path->net.first, n_markers, display_by_lp, tvt)) {
not_all_shapes_are_shown = true;
}
} else if (path->device.first) {
if (produce_highlights_for_device (path->device.first, n_markers, tv)) {
if (produce_highlights_for_device (path->device.first, n_markers, tvt)) {
not_all_shapes_are_shown = true;
}
} else if (circuit) {
if (produce_highlights_for_circuit (circuit, n_markers, tv)) {
if (produce_highlights_for_circuit (circuit, n_markers, tvt)) {
not_all_shapes_are_shown = true;
}
}
}
for (auto marker = m_net_markers.begin (); marker != m_net_markers.end (); ++marker) {
// computes the transformation supplied by the path
std::pair<bool, db::DCplxTrans> tr = trans_for (marker->first, *layout, *cell, m_cell_context_cache, cv.context_dtrans ());
if (! tr.first) {
continue;
}
db::DCplxTrans trans = tr.second;
// a map of display properties vs. layer properties
// correct DBU differences between the storage layout and the original layout
std::vector<db::DCplxTrans> tvt = tv;
for (std::vector<db::DCplxTrans>::iterator t = tvt.begin (); t != tvt.end (); ++t) {
*t = *t * trans * db::DCplxTrans (layout->dbu () / original_layout.dbu ());
}
if (produce_highlights_for_net (marker->second, n_markers, display_by_lp, tvt)) {
not_all_shapes_are_shown = true;
}
}
for (auto marker = m_markers.begin (); marker != m_markers.end (); ++marker) {
// computes the transformation supplied by the path

View File

@ -246,6 +246,7 @@ private:
lay::NetlistObjectsPath m_current_path;
std::vector<lay::NetlistObjectsPath> m_selected_paths;
std::vector<std::pair<const db::Circuit *, db::DPolygon> > m_markers;
std::vector<std::pair<const db::Circuit *, const db::Net *> > m_net_markers;
lay::NetInfoDialog *mp_info_dialog;
tl::DeferredMethod<NetlistBrowserPage> dm_update_highlights;
tl::DeferredMethod<NetlistBrowserPage> dm_rerun_macro;

37
testdata/drc/drcSimpleTests_147.drc vendored Normal file
View File

@ -0,0 +1,37 @@
source $drc_test_source
report_netlist $drc_test_target
deep
l1 = input(1, 0)
l2 = input(2, 0)
l3 = input(3, 0)
l4 = input(4, 0)
l5 = input(5, 0)
connect(l1, l2)
connect(l2, l3)
connect(l3, l4)
connect(l4, l5)
l1.output(1, 0)
l2.output(2, 0)
l3.output(3, 0)
l4.output(4, 0)
l5.output(5, 0)
netlist
sec = { "l2" => l2, "l3" => l3, "l4" => l4, "l5" => l5 }
script = <<"END"
skip;
var ar=area(l5)/area;
db.add_log_entry(LogEntryData.new(Severity.Info, net, 'AR='+to_s(ar)))
END
evaluate_nets(l1, sec, script, { "n" => "NET1" })
netlist

BIN
testdata/drc/drcSimpleTests_147.gds vendored Normal file

Binary file not shown.

74
testdata/drc/drcSimpleTests_au147.l2n vendored Normal file
View File

@ -0,0 +1,74 @@
#%l2n-klayout
W(TOP)
U(0.001)
L(l1 '1/0')
L(l2 '2/0')
L(l3 '3/0')
L(l4 '4/0')
L(l5 '5/0')
C(l1 l1 l2)
C(l2 l1 l2 l3)
C(l3 l2 l3 l4)
C(l4 l3 l4 l5)
C(l5 l4 l5)
H(I B('AR=5') C(B) N(NET1))
H(I B('AR=0') C(B) N($4))
H(I B('AR=9.875') C(TOP) N($1))
H(I B('AR=9.5') C(TOP) N($I2))
X(A
R((0 0) (38000 12000))
N(1 I(NET2)
R(l1 (0 8000) (4000 4000))
R(l2 (-3000 -3000) (2000 2000))
R(l3 (-3000 -11000) (4000 12000))
R(l4 (-3000 -11000) (2000 2000))
R(l5 (-3000 -3000) (38000 4000))
R(l5 (-22000 -2000) (0 0))
)
P(1 I(NET2))
)
X(B
R((-8000 -14000) (48000 34000))
N(1 I(NET1)
R(l1 (12000 8000) (4000 4000))
R(l1 (-10000 -14000) (4000 4000))
R(l2 (3000 7000) (2000 2000))
R(l2 (-8000 -12000) (2000 2000))
R(l3 (3000 -3000) (4000 14000))
R(l3 (-10000 -14000) (10000 4000))
R(l4 (-3000 -3000) (2000 2000))
R(l5 (-3000 -15000) (4000 16000))
R(l5 (14000 -16000) (10000 4000))
R(l5 (-6000 -2000) (0 0))
)
N(2 I($2)
R(l3 (25000 -5000) (4000 7000))
R(l4 (-3000 -3000) (2000 2000))
R(l5 (-3000 -3000) (15000 4000))
)
N(3 I($4)
R(l1 (25000 -5000) (4000 18000))
)
N(4 I($5)
R(l3 (25000 6000) (4000 7000))
)
N(5 I($6)
R(l5 (25000 6000) (15000 4000))
)
N(6 I(NET2)
R(l5 (16000 18000) (0 0))
)
P(6 I(NET2))
X(1 A Y(-8000 -14000) P(0 1))
X(2 A M Y(-8000 20000) P(0 6))
)
X(TOP
R((-18000 -51000) (58000 108000))
N(1 I($1)
R(l5 (0 16000) (4000 11000))
)
N(2 I($I2))
X(1 B Y(0 0) P(0 1))
X(2 B O(180) Y(22000 43000) P(0 1))
X(3 B O(180) Y(22000 -31000) P(0 2))
)

View File

@ -35,6 +35,11 @@ class DBLog_TestClass < TestBase
le = RBA::LogEntryData::new
le.severity = RBA::LogEntryData::Error
assert_equal(le.severity.to_s, "Error")
le.severity = RBA::LogEntryData::NoSeverity
assert_equal(le.severity.to_s, "NoSeverity")
le.message = "message"
assert_equal(le.message, "message")
@ -52,6 +57,44 @@ class DBLog_TestClass < TestBase
assert_equal(le.to_s, "[the answer] In cell TOP: message, shape: (1,2;1,4;3,4;3,2)")
le.net_name = "NET"
assert_equal(le.net_name, "NET")
assert_equal(le.to_s, "[the answer] In net NET in circuit TOP: message, shape: (1,2;1,4;3,4;3,2)")
end
def test_2_LogConstructors
le = RBA::LogEntryData::new(RBA::LogEntryData::Error, "a message")
assert_equal(le.to_s, "a message")
assert_equal(le.severity.to_s, "Error")
le = RBA::LogEntryData::new(RBA::LogEntryData::Info, "CELL", "a message")
assert_equal(le.to_s, "In cell CELL: a message")
assert_equal(le.severity.to_s, "Info")
le = RBA::LogEntryData::new(RBA::LogEntryData::Warning, "CELL", "NET", "a message")
assert_equal(le.to_s, "In net NET in circuit CELL: a message")
assert_equal(le.severity.to_s, "Warning")
# Create a LogEntry from a Net object:
nl = RBA::Netlist::new
c = RBA::Circuit::new
c.name = "CIRCUIT"
nl.add(c)
# NOTE: no explicit name, but ID 0
net = c.create_net
le = RBA::LogEntryData::new(RBA::LogEntryData::Error, net, "a message")
assert_equal(le.to_s, "In net $0 in circuit CIRCUIT: a message")
assert_equal(le.severity.to_s, "Error")
end
end