Merge pull request #1139 from KLayout/issue-1138

Issue 1138 (switch to suppress warnings on DXF reader - and other readers)
This commit is contained in:
Matthias Köfferlein 2022-09-04 09:07:51 +02:00 committed by GitHub
commit e343939aaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
296 changed files with 6166 additions and 1268 deletions

View File

@ -11,12 +11,12 @@ For more details see http://www.klayout.org.
Building on Linux:
* Qt 4.7 or later (4.6 with some restrictions) or Qt 5
* Qt 4.7 or later (4.6 with some restrictions), Qt 5 or Qt 6
* gcc 4.6 or later or clang 3.8 or later
Building on Windows with MSYS2:
* MSYS2 with gcc, Qt4 or 5, zlib, ruby and python packages installed
* MSYS2 with gcc, Qt4, 5 or 6, zlib, ruby and python packages installed
Building on Windows with MSVC 2017:
@ -34,14 +34,10 @@ For more build instructions see http://www.klayout.de/build.html.
## Building instructions (Linux)
### Plain building for Qt4
### Plain building for Qt4, Qt5 and Qt6 (one Qt version installed)
./build.sh
### Plain building for Qt5
./build.sh -qt5
### Building without Qt binding
./build.sh -without-qtbinding

View File

@ -556,6 +556,7 @@ CommonReader::read (db::Layout &layout)
void
CommonReader::init (const LoadLayoutOptions &options)
{
ReaderBase::init (options);
CommonReaderBase::init ();
db::CommonReaderOptions common_options = options.get_options<db::CommonReaderOptions> ();

View File

@ -237,7 +237,7 @@ protected:
friend class CommonReaderLayerMapping;
virtual void common_reader_error (const std::string &msg) = 0;
virtual void common_reader_warn (const std::string &msg) = 0;
virtual void common_reader_warn (const std::string &msg, int warn_level = 1) = 0;
/**
* @brief Merge (and delete) the src_cell into target_cell

View File

@ -1366,9 +1366,10 @@ compute_area_and_perimeter_of_net_shapes (const db::hier_clusters<db::NetShape>
perimeter = ap_collector.perimeter ();
}
static void
static db::Point
get_merged_shapes_of_net (const db::hier_clusters<db::NetShape> &clusters, db::cell_index_type ci, size_t cid, unsigned int layer_id, db::Shapes &shapes)
{
db::Point ref;
db::EdgeProcessor ep;
// count vertices and reserve space
@ -1380,16 +1381,76 @@ get_merged_shapes_of_net (const db::hier_clusters<db::NetShape> &clusters, db::c
size_t p = 0;
for (db::recursive_cluster_shape_iterator<db::NetShape> rci (clusters, layer_id, ci, cid); !rci.at_end (); ++rci) {
ep.insert (rci.trans () * rci->polygon_ref (), ++p);
if (p == 0) {
db::PolygonRef pr = (rci.trans () * rci->polygon_ref ());
db::PolygonRef::polygon_edge_iterator e = pr.begin_edge ();
if (! e.at_end ()) {
// pick one reference point for the label
ref = (*e).p1 ();
ep.insert (pr, ++p);
}
} else {
ep.insert (rci.trans () * rci->polygon_ref (), ++p);
}
}
db::ShapeGenerator sg (shapes);
db::PolygonGenerator pg (sg, false);
db::SimpleMerge op;
ep.process (pg, op);
return ref;
}
db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes)
static std::string
create_antenna_msg (double agate, db::Polygon::area_type agate_int, double gate_area_factor, db::Polygon::perimeter_type pgate_int, double gate_perimeter_factor,
double ametal, db::Polygon::area_type ametal_int, double metal_area_factor, db::Polygon::perimeter_type pmetal_int, double metal_perimeter_factor,
const std::vector<std::pair<const db::Region *, double> > &diodes,
const std::vector<db::Polygon::area_type> &adiodes_int,
double r, double ratio, double dbu)
{
std::string msg;
msg += tl::sprintf ("agate_eff: %.12g, ", agate);
if (fabs (gate_area_factor) > 1e-6) {
msg += tl::sprintf ("agate: %.12g, agate_factor: %.12g, ", agate_int * dbu * dbu, gate_area_factor);
}
if (fabs (gate_perimeter_factor) > 1e-6) {
msg += tl::sprintf ("pgate: %.12g, pgate_factor: %.12g, ", pgate_int * dbu * dbu, gate_perimeter_factor);
}
msg += tl::sprintf ("ametal_eff: %.12g, ", ametal);
if (fabs (metal_area_factor) > 1e-6) {
msg += tl::sprintf ("ametal: %.12g, ametal_factor: %.12g, ", ametal_int * dbu * dbu, metal_area_factor);
}
if (fabs (metal_perimeter_factor) > 1e-6) {
msg += tl::sprintf ("pmetal: %.12g, pmetal_factor: %.12g, ", pmetal_int * dbu * dbu, metal_perimeter_factor);
}
if (! adiodes_int.empty ()) {
msg += "adiodes: [";
for (auto d = adiodes_int.begin (); d != adiodes_int.end (); ++d) {
if (d != adiodes_int.begin ()) {
msg += ", ";
}
msg += tl::sprintf ("%.12g", *d * dbu * dbu);
}
msg += "], ";
}
if (! diodes.empty ()) {
msg += "diode_factors: [";
for (auto d = diodes.begin (); d != diodes.end (); ++d) {
if (d != diodes.begin ()) {
msg += ", ";
}
msg += tl::sprintf ("%.12g", d->second);
}
msg += "], ";
}
msg += tl::sprintf ("ratio: %.12g, ", ametal / agate);
msg += tl::sprintf ("max_ratio_eff: %.12g, ", r);
msg += tl::sprintf ("max_ratio: %.12g", ratio);
return msg;
}
db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes, db::Texts *values)
{
// TODO: that's basically too much .. we only need the clusters
if (! m_netlist_extracted) {
@ -1401,6 +1462,11 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
db::DeepLayer dl (&dss (), m_layout_index, ly.insert_layer ());
db::DeepLayer dlv;
if (values) {
dlv = db::DeepLayer (&dss (), m_layout_index, ly.insert_layer ());
}
for (db::Layout::bottom_up_const_iterator cid = ly.begin_bottom_up (); cid != ly.end_bottom_up (); ++cid) {
const connected_clusters<db::NetShape> &clusters = m_net_clusters.clusters_per_cell (*cid);
@ -1408,6 +1474,8 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
continue;
}
std::vector<db::Polygon::area_type> adiodes_int;
for (connected_clusters<db::NetShape>::all_iterator c = clusters.begin_all (); ! c.at_end (); ++c) {
if (! clusters.is_root (*c)) {
@ -1417,13 +1485,18 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
double r = ratio;
bool skip = false;
for (std::vector<std::pair<const db::Region *, double> >::const_iterator d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
adiodes_int.clear ();
adiodes_int.reserve (diodes.size ());
for (auto d = diodes.begin (); d != diodes.end () && ! skip; ++d) {
db::Polygon::area_type adiode_int = 0;
db::Polygon::perimeter_type pdiode_int = 0;
compute_area_and_perimeter_of_net_shapes (m_net_clusters, *cid, *c, layer_of (*d->first), adiode_int, pdiode_int);
adiodes_int.push_back (adiode_int);
if (fabs (d->second) < db::epsilon) {
if (adiode_int > 0) {
skip = true;
@ -1465,12 +1538,29 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
}
if (tl::verbosity () >= 50) {
tl::info << "cell [" << ly.cell_name (*cid) << "]: agate=" << tl::to_string (agate) << ", ametal=" << tl::to_string (ametal) << ", r=" << tl::sprintf ("%.12g", r);
tl::info << "cell [" << ly.cell_name (*cid) << "]: " <<
create_antenna_msg (agate, agate_int, gate_area_factor, pgate_int, gate_perimeter_factor,
ametal, ametal_int, metal_area_factor, pmetal_int, metal_perimeter_factor,
diodes, adiodes_int, r, ratio, dbu);
}
if (ametal / agate > r + db::epsilon) {
db::Shapes &shapes = ly.cell (*cid).shapes (dl.layer ());
get_merged_shapes_of_net (m_net_clusters, *cid, *c, layer_of (metal), shapes);
db::Point ref = get_merged_shapes_of_net (m_net_clusters, *cid, *c, layer_of (metal), shapes);
if (values) {
// generate a data string with the details of the antenna computation (intentionally like JSON)
std::string msg = create_antenna_msg (agate, agate_int, gate_area_factor, pgate_int, gate_perimeter_factor,
ametal, ametal_int, metal_area_factor, pmetal_int, metal_perimeter_factor,
diodes, adiodes_int, r, ratio, dbu);
db::Shapes &shapesv = ly.cell (*cid).shapes (dlv.layer ());
shapesv.insert (db::Text (msg, db::Trans (ref - db::Point ())));
}
}
}
@ -1481,6 +1571,10 @@ db::Region LayoutToNetlist::antenna_check (const db::Region &gate, double gate_a
}
if (values) {
*values = db::Texts (new db::DeepTexts (dlv));
}
return db::Region (new db::DeepRegion (dl));
}

View File

@ -856,18 +856,18 @@ public:
* regardless of the diode's area.
* In other words: any diode will make the net safe against antenna discharge.
*/
db::Region antenna_check (const db::Region &gate, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ())
db::Region antenna_check (const db::Region &gate, double gate_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > (), db::Texts *values = 0)
{
return antenna_check (gate, 1.0, gate_perimeter_factor, metal, 1.0, metal_perimeter_factor, ratio, diodes);
return antenna_check (gate, 1.0, gate_perimeter_factor, metal, 1.0, metal_perimeter_factor, ratio, diodes, values);
}
/**
* @brief Variant of the antenna check not using the perimeter
* This version uses 0 for the perimeter factor hence not taking into account the perimeter at all.
*/
db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ())
db::Region antenna_check (const db::Region &gate, const db::Region &metal, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > (), db::Texts *values = 0)
{
return antenna_check (gate, 1.0, 0.0, metal, 1.0, 0.0, ratio, diodes);
return antenna_check (gate, 1.0, 0.0, metal, 1.0, 0.0, ratio, diodes, values);
}
/**
@ -879,8 +879,10 @@ public:
*
* where f is the area scale factor and t the perimeter scale factor. This version allows to ignore the
* area contribution entirely and switch to a perimeter-based antenna check by setting f to zero.
*
* If values is non-null, texts explaining the violations are placed there.
*/
db::Region antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > ());
db::Region antenna_check (const db::Region &gate, double gate_area_factor, double gate_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<std::pair<const db::Region *, double> > &diodes = std::vector<std::pair<const db::Region *, double> > (), Texts *values = 0);
/**
* @brief Saves the database to the given path

View File

@ -46,55 +46,76 @@ namespace db
* The file follows the declaration-before-use principle
* (circuits before subcircuits, nets before use ...)
*
* Global statements:
* Main body:
* [version|description|unit|top|layer|connect|global|circuit|class|device|any]*
*
* [version]:
* version(<number>) - file format version [short key: V]
*
* [description]:
* description(<text>) - an arbitrary description text [short key: B]
*
* [unit]:
* unit(<unit>) - specifies the database unit [short key: U]
*
* [top]:
* top(<circuit>) - specifies the name of the top circuit [short key: W]
*
* [layer]:
* layer(<name> <source-spec>?) - define a layer [short key: L]
*
* [connect]:
* connect(<layer1> <name> ...) - connects layer1 with the following layers [short key: C]
*
* [global]:
* global(<layer> <net-name> ...)
* - connects the shapes of the layer with the given global
* nets [short key: G]
*
* [circuit]:
* circuit(<name> [circuit-def]) - circuit (cell) [short key: X]
*
* [class]:
* class(<name> <template> [template-def]) - a device class definition (template: RES,CAP,...) [short key: K]
* device(<name> <class> [device-abstract-def])
*
* [device]:
* device(<name> <class> [device-abstract-terminal|any]*)
* - device abstract [short key: D]
*
* [circuit-def]:
* [boundary|property|circuit-net|circuit-pin|circuit-device|subcircuit|any]*
*
* [boundary-def]
*
* [property-def]*
*
* net(<id> [name]? [property-def]* [geometry-def]*)
* [circuit-net]:
* net(<id> [name]? [geometries-def])
* - net geometry [short key: N]
* A net declaration shall be there also if no geometry
* is present. The ID is a numerical shortcut for the net.
*
* [circuit-pin]:
* pin(<net-id> [name]?) - outgoing pin connection [short key: P]
* Statement order specifies pin order.
* device(<id> <abstract-or-class> [name]? [combined-device]* [terminal-route]* [device-def])
*
* [circuit-device]:
* device(<id> <abstract-or-class> [name|trans|combined-device|terminal-route|param|device-terminal|any]*)
* - device with connections [short key: D]
* circuit(<id> [name]? [subcircuit-def])
*
* [subcircuit]:
* circuit(<id> [name]? [property|trans|subcircuit-pin|any])
* - subcircuit with connections [short key: X]
*
* [boundary-def]:
*
* polygon([coord] ...) - defines a polygon [short key: Q]
* [boundary]:
* polygon([coord] ...) | - defines a polygon [short key: Q]
* "*" for <x> or <y> means take previous
* rect([coord] [coord]) - defines a rectangle [short key: R]
* coordinates are bottom/left and top/right
*
* [combined-device]:
*
* device(<abstract> [trans-def])
* device(<abstract> [trans])
* - specifies an additional device component
* (for combined devices) with abstract <abstract>
* and offset dx, dy.
*
* [terminal-route]:
*
* connect(<device-index> <outer-terminal-name> <inner-terminal-name>)
* - connects the outer terminal with the terminal
* of the device component with <device-index>:
@ -102,68 +123,70 @@ namespace db
* device etc.
*
* [name]:
*
* name(<name>) - specify net name [short key: I]
*
* [property-def]:
* [geometries-def]:
* [property|polygon|rect|text|any]*
*
* [property]:
* property(<prop-name> <prop-value>)
* - specifies a property value/key pair [short key: F]
* prop-name and prop-value are variant specifications
* in klayout notation: #x is an integer, ##y a floating-point
* value, a word or quoted literal is a string.
*
* [geometry-def]:
*
* [polygon]:
* polygon(<layer> [coord] ...) - defines a polygon [short key: Q]
* "*" for <x> or <y> means take previous
*
* [rect]:
* rect(<layer> [coord] [coord]) - defines a rectangle [short key: R]
* coordinates are bottom/left and top/right
*
* [text]:
* text(<layer> [text] [coord]) - defines a rectangle [short key: J]
*
* [coord]
*
* [coord]:
* <x> <y> - absolute coordinates
* (<x> <y>) - relative coordinates (reference is reset to 0,0
* for each net or terminal in device abstract)
*
* [template-def]:
* [template-param|template-terminal|any]*
*
* [template-param]:
* param(<name> <primary>? <default-value>*) - defines a template parameter [short key: E]
* ('primary' is a value: 0 or 1)
*
* [template-terminal]:
* terminal(<name>) - defines a terminal [short key: T]
*
* [device-abstract-def]:
*
* [device-abstract-terminal-def]*
*
* [device-abstract-terminal-def]:
*
* terminal(<terminal-name> [geometry-def]*)
* [device-abstract-terminal]:
* terminal(<terminal-name> [geometries-def])
* - specifies the terminal geometry [short key: T]
*
* [device-def]:
*
* [property-def]* - user properties
* [trans-def] - location of the device
* must be before terminal
* [param]:
* param(<name> <value>) - defines a parameter [short key: E]
*
* [device-terminal]:
* terminal(<terminal-name> <net-id>)
* - specifies connection of the terminal with
* a net (short key: T)
* - specifies connection of the terminal with a net (short key: T)
*
* [subcircuit-def]:
*
* [property-def]* - user properties
* [trans-def] - location of the subcircuit
* [subcircuit-pin]:
* pin(<pin-id> <net-id>) - specifies connection of the pin with a net [short key: P]
*
* [trans-def]:
*
* [trans]:
* location(<x> <y>) - location of the instance [short key: Y]
* rotation(<angle>) - rotation angle (in degree, default is 0) [short key: O]
* mirror - if specified, the instance is mirrored before rotation [short key: M]
* scale(<mag>) - magnification (default is 1) [short key: S]
*
* [any]:
* * |
* <token> |
* <token> ( [any]* ) |
* <float> |
* <quoted-string>
*/
namespace l2n_std_format

View File

@ -97,6 +97,13 @@ LayoutToNetlistStandardReader::read_int ()
return i;
}
bool
LayoutToNetlistStandardReader::try_read_int (int &i)
{
i = 0;
return m_ex.try_read (i);
}
db::Coord
LayoutToNetlistStandardReader::read_coord ()
{
@ -133,6 +140,50 @@ LayoutToNetlistStandardReader::skip ()
}
}
void LayoutToNetlistStandardReader::skip_element ()
{
std::string s;
double f;
if (m_ex.try_read_word (s)) {
// skip bracket elements after token key
Brace br (this);
while (br) {
skip_element ();
}
br.done ();
} else if (m_ex.test ("*")) {
// asterisk is allowed as element (e.g. inside point)
} else if (m_ex.try_read_quoted (s)) {
// skip string
} else if (m_ex.try_read (f)) {
// skip numeric value
} else {
Brace br (this);
if (br) {
// skip bracket elements without token
while (br) {
skip_element ();
}
br.done ();
} else {
throw tl::Exception (tl::to_string (tr ("Unexpected token")));
}
}
}
void LayoutToNetlistStandardReader::do_read (db::LayoutToNetlist *l2n)
{
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("File read: ")) + m_path);
@ -153,7 +204,7 @@ static db::Region &layer_by_name (db::LayoutToNetlist *l2n, const std::string &n
return *l;
}
void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::LayoutToNetlist *l2n, bool nested, std::map<const db::Circuit *, ObjectMap> *map_per_circuit)
void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::LayoutToNetlist *l2n, LayoutToNetlistStandardReader::Brace *nested, std::map<const db::Circuit *, ObjectMap> *map_per_circuit)
{
m_dbu = 0.001;
int version = 0;
@ -179,7 +230,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
db::LayoutLocker layout_locker (l2n ? l2n->internal_layout () : 0);
while (! at_end ()) {
while (nested ? *nested : ! at_end ()) {
if (test (skeys::version_key) || test (lkeys::version_key)) {
@ -289,7 +340,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
br.done ();
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword")));
skip_element ();
}
}
@ -369,7 +420,7 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (rect, polygon, net, pin, device or circuit expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (rect, polygon, net, pin, device or circuit expected)")));
skip_element ();
}
}
@ -430,19 +481,17 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside device abstract definition (terminal expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device abstract definition (terminal expected)")));
skip_element ();
}
}
br.done ();
} else if (nested) {
break;
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file")));
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside device abstract definition (terminal expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword")));
skip_element ();
}
}
@ -450,6 +499,10 @@ void LayoutToNetlistStandardReader::read_netlist (db::Netlist *netlist, db::Layo
if (l2n) {
l2n->set_netlist_extracted ();
}
if (version > 1) {
throw tl::Exception (tl::to_string (tr ("This program version only supports version 1 of the L2N DB format. File version is: ")) + tl::to_string (version));
}
}
db::Point
@ -490,65 +543,6 @@ LayoutToNetlistStandardReader::read_property (db::NetlistObject *obj)
br.done ();
}
std::pair<unsigned int, NetShape> LayoutToNetlistStandardReader::read_geometry(db::LayoutToNetlist *l2n)
{
std::string lname;
if (test (skeys::rect_key) || test (lkeys::rect_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
db::Point lb = read_point ();
db::Point rt = read_point ();
db::Box box (lb, rt);
br.done ();
return std::make_pair (lid, db::PolygonRef (db::Polygon (box), l2n->internal_layout ()->shape_repository ()));
} else if (test (skeys::polygon_key) || test (lkeys::polygon_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
std::vector<db::Point> pt;
while (br) {
pt.push_back (read_point ());
}
br.done ();
db::Polygon poly;
poly.assign_hull (pt.begin (), pt.end ());
return std::make_pair (lid, db::PolygonRef (poly, l2n->internal_layout ()->shape_repository ()));
} else if (test (skeys::text_key) || test (lkeys::text_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
std::string text;
read_word_or_quoted (text);
db::Point pt = read_point ();
br.done ();
return std::make_pair (lid, db::TextRef (db::Text (text, db::Trans (pt - db::Point ())), l2n->internal_layout ()->shape_repository ()));
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file (polygon or rect expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword (polygon or rect expected)")));
}
}
db::Box
LayoutToNetlistStandardReader::read_rect ()
{
@ -587,14 +581,75 @@ void
LayoutToNetlistStandardReader::read_geometries (db::NetlistObject *obj, Brace &br, db::LayoutToNetlist *l2n, db::local_cluster<db::NetShape> &lc, db::Cell &cell)
{
m_ref = db::Point ();
std::string lname;
while (br) {
if (test (skeys::property_key) || test (lkeys::property_key)) {
read_property (obj);
} else if (test (skeys::rect_key) || test (lkeys::rect_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
db::Point lb = read_point ();
db::Point rt = read_point ();
db::Box box (lb, rt);
br.done ();
NetShape n (db::PolygonRef (db::Polygon (box), l2n->internal_layout ()->shape_repository ()));
lc.add (n, lid);
n.insert_into (cell.shapes (lid));
} else if (test (skeys::polygon_key) || test (lkeys::polygon_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
std::vector<db::Point> pt;
while (br) {
pt.push_back (read_point ());
}
br.done ();
db::Polygon poly;
poly.assign_hull (pt.begin (), pt.end ());
NetShape n (db::PolygonRef (poly, l2n->internal_layout ()->shape_repository ()));
lc.add (n, lid);
n.insert_into (cell.shapes (lid));
} else if (test (skeys::text_key) || test (lkeys::text_key)) {
Brace br (this);
read_word_or_quoted (lname);
unsigned int lid = l2n->layer_of (layer_by_name (l2n, lname));
std::string text;
read_word_or_quoted (text);
db::Point pt = read_point ();
br.done ();
NetShape n (db::TextRef (db::Text (text, db::Trans (pt - db::Point ())), l2n->internal_layout ()->shape_repository ()));
lc.add (n, lid);
n.insert_into (cell.shapes (lid));
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file (polygon, text or rect expected)")));
} else {
std::pair<unsigned int, db::NetShape> pr = read_geometry (l2n);
lc.add (pr.second, pr.first);
pr.second.insert_into (cell.shapes (pr.first));
skip_element ();
}
}
}
@ -641,6 +696,7 @@ LayoutToNetlistStandardReader::read_pin (db::Netlist * /*netlist*/, db::LayoutTo
db::Net *net = 0;
db::Pin pin;
int netid = 0;
while (br) {
@ -660,18 +716,19 @@ LayoutToNetlistStandardReader::read_pin (db::Netlist * /*netlist*/, db::LayoutTo
read_property (&pin);
} else {
} else if (try_read_int (netid)) {
if (net) {
throw tl::Exception (tl::to_string (tr ("Duplicate net ID")));
}
unsigned int netid = (unsigned int) read_int ();
net = map.id2net [netid];
net = map.id2net [(unsigned int) netid];
if (!net) {
throw tl::Exception (tl::to_string (tr ("Not a valid net ID: ")) + tl::to_string (netid));
}
} else {
skip_element ();
}
}
@ -851,7 +908,7 @@ LayoutToNetlistStandardReader::read_device (db::Netlist *netlist, db::LayoutToNe
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside device definition (location, scale, mirror, rotation, param or terminal expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside device definition (location, scale, mirror, rotation, param or terminal expected)")));
skip_element ();
}
}
@ -1032,7 +1089,7 @@ LayoutToNetlistStandardReader::read_subcircuit (db::Netlist *netlist, db::Layout
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside subcircuit definition (location, rotation, mirror, scale or pin expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside subcircuit definition (location, rotation, mirror, scale or pin expected)")));
skip_element ();
}
}

View File

@ -44,6 +44,11 @@ namespace l2n_std_reader {
operator bool ();
void done ();
bool has_brace () const
{
return m_has_brace;
}
private:
db::LayoutToNetlistStandardReader *mp_reader;
bool m_checked;
@ -101,7 +106,7 @@ protected:
friend class l2n_std_reader::Brace;
typedef l2n_std_reader::Brace Brace;
void read_netlist (Netlist *netlist, db::LayoutToNetlist *l2n, bool nested = false, std::map<const db::Circuit *, ObjectMap> *map_per_circuit = 0);
void read_netlist (Netlist *netlist, db::LayoutToNetlist *l2n, Brace *nested = 0, std::map<const db::Circuit *, ObjectMap> *map_per_circuit = 0);
static size_t terminal_id (const db::DeviceClass *device_class, const std::string &tname);
static std::pair<db::DeviceAbstract *, const db::DeviceClass *> device_model_by_name (db::Netlist *netlist, const std::string &dmname);
@ -126,12 +131,14 @@ protected:
bool test (const std::string &token);
void expect (const std::string &token);
void read_word_or_quoted(std::string &s);
void read_word_or_quoted (std::string &s);
int read_int ();
bool try_read_int (int &i);
db::Coord read_coord ();
double read_double ();
bool at_end ();
void skip ();
void skip_element ();
void read_net (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map);
void read_pin (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map);
@ -139,7 +146,6 @@ protected:
void read_subcircuit (Netlist *netlist, db::LayoutToNetlist *l2n, db::Circuit *circuit, ObjectMap &map, std::map<db::CellInstArray, std::list<Connections> > &connections);
bool read_trans_part (db::DCplxTrans &tr);
void read_abstract_terminal (db::LayoutToNetlist *l2n, db::DeviceAbstract *dm, db::DeviceClass *dc);
std::pair<unsigned int, db::NetShape> read_geometry(db::LayoutToNetlist *l2n);
void read_property (db::NetlistObject *obj);
db::Polygon read_polygon ();
db::Box read_rect ();

View File

@ -29,6 +29,9 @@
namespace db
{
static const std::string endl ("\n");
static const std::string indent1 (" ");
// -------------------------------------------------------------------------------------------
// LayoutToNetlistWriterBase implementation
@ -47,15 +50,89 @@ void LayoutToNetlistWriterBase::write (const db::LayoutToNetlist *l2n)
do_write (l2n);
}
// -------------------------------------------------------------------------------------------
// TokenizedOutput implementation
TokenizedOutput::TokenizedOutput (tl::OutputStream &s)
: mp_stream (&s), mp_parent (0), m_first (true), m_inline (false), m_newline (false), m_indent (-1)
{
// .. nothing yet ..
}
TokenizedOutput::TokenizedOutput (tl::OutputStream &s, const std::string &token)
: mp_stream (&s), mp_parent (0), m_first (true), m_inline (false), m_newline (false), m_indent (0)
{
stream () << token << "(";
}
TokenizedOutput::TokenizedOutput (tl::OutputStream &s, int indent, const std::string &token)
: mp_stream (&s), mp_parent (0), m_first (true), m_inline (false), m_newline (false)
{
m_indent = indent;
for (int i = 0; i < m_indent; ++i) {
stream () << indent1;
}
stream () << token << "(";
}
TokenizedOutput::TokenizedOutput (TokenizedOutput &output, const std::string &token, bool inl)
: mp_stream (&output.stream ()), mp_parent (&output), m_first (true), m_inline (inl), m_newline (false)
{
m_indent = output.indent () + 1;
output.emit_sep ();
stream () << token << "(";
}
TokenizedOutput::~TokenizedOutput ()
{
if (m_newline) {
for (int i = 0; i < m_indent; ++i) {
stream () << indent1;
}
}
if (m_indent >= 0) {
stream () << ")";
if (! m_inline) {
if (mp_parent) {
*mp_parent << endl;
} else {
stream () << endl;
}
}
}
}
void TokenizedOutput::emit_sep ()
{
if (m_newline) {
for (int i = 0; i <= m_indent; ++i) {
stream () << indent1;
}
m_newline = false;
} else if (! m_first) {
stream () << " ";
}
m_first = false;
}
TokenizedOutput &TokenizedOutput::operator<< (const std::string &s)
{
if (s == endl) {
m_newline = true;
stream () << s;
} else if (! s.empty ()) {
emit_sep ();
stream () << s;
}
return *this;
}
// -------------------------------------------------------------------------------------------
namespace l2n_std_format
{
static const std::string endl ("\n");
static const std::string indent1 (" ");
static const std::string indent2 (" ");
template <class Keys>
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description)
: mp_stream (&stream), m_dbu (dbu), mp_netlist (0),
@ -82,7 +159,10 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n)
mp_netlist = l2n->netlist ();
mp_l2n = l2n;
write (false, 0);
{
TokenizedOutput stream (*mp_stream);
write (false, stream, 0);
}
mp_netlist = 0;
mp_l2n = 0;
@ -95,14 +175,14 @@ void std_writer_impl<Keys>::write (const db::LayoutToNetlist *l2n)
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, bool nested, const db::Netlist *netlist, const db::LayoutToNetlist *l2n, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
{
try {
mp_netlist = netlist;
mp_l2n = l2n;
write (nested, net2id_per_circuit);
write (nested, stream, net2id_per_circuit);
mp_netlist = 0;
mp_l2n = 0;
@ -126,16 +206,20 @@ static bool same_parameter (const DeviceParameterDefinition &a, const DevicePara
}
template <class Keys>
void std_writer_impl<Keys>::write_device_class (const std::string &indent, const db::DeviceClass *cls, const std::string &temp_name, const db::DeviceClass *temp_class)
void std_writer_impl<Keys>::write_device_class (TokenizedOutput &stream, const db::DeviceClass *cls, const std::string &temp_name, const db::DeviceClass *temp_class)
{
*mp_stream << indent << Keys::class_key << "(" << tl::to_word_or_quoted_string (cls->name ()) << " " << tl::to_word_or_quoted_string (temp_name);
TokenizedOutput out (stream, Keys::class_key);
out << tl::to_word_or_quoted_string (cls->name ()) << tl::to_word_or_quoted_string (temp_name);
bool any_def = false;
const std::vector<DeviceParameterDefinition> &pd = cls->parameter_definitions ();
for (std::vector<DeviceParameterDefinition>::const_iterator p = pd.begin (); p != pd.end (); ++p) {
if (! temp_class->has_parameter_with_name (p->name ()) || !same_parameter (*p, *temp_class->parameter_definition (temp_class->parameter_id_for_name (p->name ())))) {
*mp_stream << endl << indent << indent1 << Keys::param_key << "(" << tl::to_word_or_quoted_string (p->name ()) << " " << tl::to_string (p->is_primary () ? 1 : 0) << " " << tl::to_string (p->default_value ()) << ")";
if (! any_def) {
out << endl;
}
TokenizedOutput (out, Keys::param_key) << tl::to_word_or_quoted_string (p->name ()) << tl::to_string (p->is_primary () ? 1 : 0) << tl::to_string (p->default_value ());
any_def = true;
}
}
@ -143,73 +227,70 @@ void std_writer_impl<Keys>::write_device_class (const std::string &indent, const
const std::vector<DeviceTerminalDefinition> &td = cls->terminal_definitions ();
for (std::vector<DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
if (! temp_class->has_terminal_with_name (t->name ())) {
*mp_stream << endl << indent << indent1 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (t->name ()) << ")";
if (! any_def) {
out << endl;
}
TokenizedOutput (out, Keys::terminal_key) << tl::to_word_or_quoted_string (t->name ());
any_def = true;
}
}
if (any_def) {
*mp_stream << endl << indent << ")" << endl;
} else {
*mp_stream << ")" << endl;
}
}
template <class Keys>
void std_writer_impl<Keys>::write (bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
void std_writer_impl<Keys>::write (bool nested, TokenizedOutput &stream, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
{
bool any = false;
const int version = 0;
const db::Layout *ly = mp_l2n ? mp_l2n->internal_layout () : 0;
const std::string indent (nested ? indent1 : "");
if (! nested) {
*mp_stream << Keys::l2n_magic_string << endl;
stream << Keys::l2n_magic_string << endl;
}
if (version > 0) {
*mp_stream << indent << Keys::version_key << "(" << version << ")" << endl;
TokenizedOutput (stream, Keys::version_key) << tl::to_string (version);
stream << endl;
}
if (ly) {
*mp_stream << indent << Keys::top_key << "(" << tl::to_word_or_quoted_string (ly->cell_name (mp_l2n->internal_top_cell ()->cell_index ())) << ")" << endl;
*mp_stream << indent << Keys::unit_key << "(" << m_dbu << ")" << endl;
TokenizedOutput (stream, Keys::top_key) << tl::to_word_or_quoted_string (ly->cell_name (mp_l2n->internal_top_cell ()->cell_index ()));
TokenizedOutput (stream, Keys::unit_key) << tl::to_string (m_dbu);
}
bool any = false;
if (mp_l2n) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Layer section" << endl;
*mp_stream << indent << "# This section lists the mask layers (drawing or derived) and their connections." << endl;
stream << endl << "# Layer section" << endl;
stream << "# This section lists the mask layers (drawing or derived) and their connections." << endl;
}
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Mask layers" << endl;
stream << endl << "# Mask layers" << endl;
}
for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
*mp_stream << indent << Keys::layer_key << "(" << name_for_layer (mp_l2n, *l);
TokenizedOutput out (stream, Keys::layer_key);
out << name_for_layer (mp_l2n, *l);
db::LayerProperties lp = ly->get_properties (*l);
if (! lp.is_null ()) {
*mp_stream << " " << tl::to_word_or_quoted_string (lp.to_string ());
out << tl::to_word_or_quoted_string (lp.to_string ());
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Mask layer connectivity" << endl;
stream << endl << "# Mask layer connectivity" << endl;
}
for (db::Connectivity::layer_iterator l = mp_l2n->connectivity ().begin_layers (); l != mp_l2n->connectivity ().end_layers (); ++l) {
db::Connectivity::layer_iterator ce = mp_l2n->connectivity ().end_connected (*l);
db::Connectivity::layer_iterator cb = mp_l2n->connectivity ().begin_connected (*l);
if (cb != ce) {
*mp_stream << indent << Keys::connect_key << "(" << name_for_layer (mp_l2n, *l);
TokenizedOutput out (stream, Keys::connect_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::layer_iterator c = mp_l2n->connectivity ().begin_connected (*l); c != ce; ++c) {
*mp_stream << " " << name_for_layer (mp_l2n, *c);
out << name_for_layer (mp_l2n, *c);
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
@ -223,15 +304,15 @@ void std_writer_impl<Keys>::write (bool nested, std::map<const db::Circuit *, st
if (gb != ge) {
if (! any) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Global nets and connectivity" << endl;
stream << endl << "# Global nets and connectivity" << endl;
}
any = true;
}
*mp_stream << indent << Keys::global_key << "(" << name_for_layer (mp_l2n, *l);
TokenizedOutput out (stream, Keys::global_key);
out << name_for_layer (mp_l2n, *l);
for (db::Connectivity::global_nets_iterator g = gb; g != ge; ++g) {
*mp_stream << " " << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (*g));
out << tl::to_word_or_quoted_string (mp_l2n->connectivity ().global_net_name (*g));
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
@ -240,68 +321,64 @@ void std_writer_impl<Keys>::write (bool nested, std::map<const db::Circuit *, st
}
if (mp_netlist->begin_device_classes () != mp_netlist->end_device_classes () && ! Keys::is_short ()) {
*mp_stream << endl << indent << "# Device class section" << endl;
stream << endl << "# Device class section" << endl;
}
for (db::Netlist::const_device_class_iterator c = mp_netlist->begin_device_classes (); c != mp_netlist->end_device_classes (); ++c) {
db::DeviceClassTemplateBase *temp = db::DeviceClassTemplateBase::is_a (c.operator-> ());
if (temp) {
std::unique_ptr<db::DeviceClass> temp_class (temp->create ());
write_device_class (indent, c.operator-> (), temp->name (), temp_class.get ());
write_device_class (stream, c.operator-> (), temp->name (), temp_class.get ());
} else {
db::DeviceClass empty;
write_device_class (indent, c.operator-> (), std::string (), &empty);
write_device_class (stream, c.operator-> (), std::string (), &empty);
}
m_progress.set (mp_stream->pos ());
}
if (mp_netlist->begin_device_abstracts () != mp_netlist->end_device_abstracts () && ! Keys::is_short ()) {
*mp_stream << endl << indent << "# Device abstracts section" << endl;
*mp_stream << indent << "# Device abstracts list the pin shapes of the devices." << endl;
stream << endl << "# Device abstracts section" << endl;
stream << "# Device abstracts list the pin shapes of the devices." << endl;
}
for (db::Netlist::const_abstract_model_iterator m = mp_netlist->begin_device_abstracts (); m != mp_netlist->end_device_abstracts (); ++m) {
if (m->device_class ()) {
*mp_stream << indent << Keys::device_key << "(" << tl::to_word_or_quoted_string (m->name ()) << " " << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl;
write (*m, indent);
*mp_stream << indent << ")" << endl;
TokenizedOutput out (stream, Keys::device_key);
out << tl::to_word_or_quoted_string (m->name ()) << tl::to_word_or_quoted_string (m->device_class ()->name ()) << endl;
write (out, *m);
m_progress.set (mp_stream->pos ());
}
}
if (! Keys::is_short ()) {
*mp_stream << endl << indent << "# Circuit section" << endl;
*mp_stream << indent << "# Circuits are the hierarchical building blocks of the netlist." << endl;
stream << endl << "# Circuit section" << endl;
stream << "# Circuits are the hierarchical building blocks of the netlist." << endl;
}
for (db::Netlist::const_bottom_up_circuit_iterator i = mp_netlist->begin_bottom_up (); i != mp_netlist->end_bottom_up (); ++i) {
const db::Circuit *x = i.operator-> ();
*mp_stream << indent << Keys::circuit_key << "(" << tl::to_word_or_quoted_string (x->name ()) << endl;
write (*x, indent, net2id_per_circuit);
*mp_stream << indent << ")" << endl;
TokenizedOutput out (stream, Keys::circuit_key);
out << tl::to_word_or_quoted_string (x->name ()) << endl;
write (out, *x, net2id_per_circuit);
m_progress.set (mp_stream->pos ());
}
}
void write_point (tl::OutputStream &stream, const db::Point &pt, db::Point &ref, bool relative)
static void write_point (TokenizedOutput &out, const db::Point &pt, db::Point &ref, bool relative)
{
if (relative) {
stream << "(";
stream << pt.x () - ref.x ();
stream << " ";
stream << pt.y () - ref.y ();
stream << ")";
TokenizedOutput (out, std::string (), true) << tl::to_string (pt.x () - ref.x ()) << tl::to_string (pt.y () - ref.y ());
} else {
if (pt.x () == 0 || pt.x () != ref.x ()) {
stream << pt.x ();
out << tl::to_string (pt.x ());
} else {
stream << "*";
out << "*";
}
if (pt.y () == 0 || pt.y () != ref.y ()) {
stream << pt.y ();
out << tl::to_string (pt.y ());
} else {
stream << "*";
out << "*";
}
}
@ -310,25 +387,20 @@ void write_point (tl::OutputStream &stream, const db::Point &pt, db::Point &ref,
}
template <class T, class Tr>
void write_points (tl::OutputStream &stream, const T &poly, const Tr &tr, db::Point &ref, bool relative)
static void write_points (TokenizedOutput &out, const T &poly, const Tr &tr, db::Point &ref, bool relative)
{
for (typename T::polygon_contour_iterator c = poly.begin_hull (); c != poly.end_hull (); ++c) {
typename T::point_type pt = tr * *c;
stream << " ";
write_point (stream, pt, ref, relative);
write_point (out, tr * *c, ref, relative);
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::Circuit &circuit, const std::string &indent, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::Circuit &circuit, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit)
{
if (circuit.boundary ().vertices () > 0) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Circuit boundary" << endl;
stream << endl << "# Circuit boundary" << endl;
}
reset_geometry_ref ();
@ -337,22 +409,20 @@ void std_writer_impl<Keys>::write (const db::Circuit &circuit, const std::string
if (poly.is_box ()) {
db::Box box = poly.box ();
*mp_stream << indent << indent1 << Keys::rect_key << "(";
write_point (*mp_stream, box.p1 (), m_ref, true);
*mp_stream << " ";
write_point (*mp_stream, box.p2 (), m_ref, true);
*mp_stream << ")" << endl;
TokenizedOutput out (stream, Keys::rect_key);
write_point (out, box.p1 (), m_ref, true);
write_point (out, box.p2 (), m_ref, true);
} else {
*mp_stream << indent << indent1 << Keys::polygon_key << "(";
TokenizedOutput out (stream, Keys::polygon_key);
if (poly.holes () > 0) {
db::SimplePolygon sp = db::polygon_to_simple_polygon (poly);
write_points (*mp_stream, sp, db::UnitTrans (), m_ref, true);
write_points (out, sp, db::UnitTrans (), m_ref, true);
} else {
write_points (*mp_stream, poly, db::UnitTrans (), m_ref, true);
write_points (out, poly, db::UnitTrans (), m_ref, true);
}
*mp_stream << ")" << endl;
}
@ -360,9 +430,9 @@ void std_writer_impl<Keys>::write (const db::Circuit &circuit, const std::string
for (db::NetlistObject::property_iterator p = circuit.begin_properties (); p != circuit.end_properties (); ++p) {
if (p == circuit.begin_properties() && ! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Properties" << endl;
stream << endl << "# Properties" << endl;
}
*mp_stream << indent << indent1 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (stream, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
std::map<const db::Net *, unsigned int> net2id_local;
@ -379,60 +449,56 @@ void std_writer_impl<Keys>::write (const db::Circuit &circuit, const std::string
if (circuit.begin_nets () != circuit.end_nets ()) {
if (! Keys::is_short ()) {
if (mp_l2n) {
*mp_stream << endl << indent << indent1 << "# Nets with their geometries" << endl;
stream << endl << "# Nets with their geometries" << endl;
} else {
*mp_stream << endl << indent << indent1 << "# Nets" << endl;
stream << endl << "# Nets" << endl;
}
}
for (db::Circuit::const_net_iterator n = circuit.begin_nets (); n != circuit.end_nets (); ++n) {
write (*n, (*net2id) [n.operator-> ()], indent);
write (stream, *n, (*net2id) [n.operator-> ()]);
m_progress.set (mp_stream->pos ());
}
}
if (circuit.begin_pins () != circuit.end_pins ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Outgoing pins and their connections to nets" << endl;
stream << endl << "# Outgoing pins and their connections to nets" << endl;
}
for (db::Circuit::const_pin_iterator p = circuit.begin_pins (); p != circuit.end_pins (); ++p) {
*mp_stream << indent << indent1 << Keys::pin_key << "(";
TokenizedOutput out (stream, Keys::pin_key);
const db::Net *net = circuit.net_for_pin (p->id ());
if (net) {
*mp_stream << (*net2id) [net];
out << tl::to_string ((*net2id) [net]);
}
if (! p->name ().empty ()) {
if (net) {
*mp_stream << " ";
}
*mp_stream << Keys::name_key << "(" << tl::to_word_or_quoted_string (p->name ()) << ")";
TokenizedOutput (out, Keys::name_key, true) << tl::to_word_or_quoted_string (p->name ());
}
*mp_stream << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
if (circuit.begin_devices () != circuit.end_devices ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Devices and their connections" << endl;
stream << endl << "# Devices and their connections" << endl;
}
for (db::Circuit::const_device_iterator d = circuit.begin_devices (); d != circuit.end_devices (); ++d) {
write (*d, *net2id, indent);
write (stream, *d, *net2id);
m_progress.set (mp_stream->pos ());
}
}
if (circuit.begin_subcircuits () != circuit.end_subcircuits ()) {
if (! Keys::is_short ()) {
*mp_stream << endl << indent << indent1 << "# Subcircuits and their connections" << endl;
stream << endl << "# Subcircuits and their connections" << endl;
}
for (db::Circuit::const_subcircuit_iterator x = circuit.begin_subcircuits (); x != circuit.end_subcircuits (); ++x) {
write (*x, *net2id, indent);
write (stream, *x, *net2id);
m_progress.set (mp_stream->pos ());
}
}
if (! Keys::is_short ()) {
*mp_stream << endl;
stream << endl;
}
}
@ -443,7 +509,7 @@ void std_writer_impl<Keys>::reset_geometry_ref ()
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative)
{
if (s->type () == db::NetShape::Polygon) {
@ -454,39 +520,36 @@ void std_writer_impl<Keys>::write (const db::NetShape *s, const db::ICplxTrans &
if (poly.is_box ()) {
db::Box box = t * poly.box ();
*mp_stream << Keys::rect_key << "(" << lname;
*mp_stream << " ";
write_point (*mp_stream, box.p1 (), m_ref, relative);
*mp_stream << " ";
write_point (*mp_stream, box.p2 (), m_ref, relative);
*mp_stream << ")";
TokenizedOutput out (stream, Keys::rect_key);
out << lname;
write_point (out, box.p1 (), m_ref, relative);
write_point (out, box.p2 (), m_ref, relative);
} else {
*mp_stream << Keys::polygon_key << "(" << lname;
TokenizedOutput out (stream, Keys::polygon_key);
out << lname;
if (poly.holes () > 0) {
db::SimplePolygon sp = db::polygon_to_simple_polygon (poly);
write_points (*mp_stream, sp, t, m_ref, relative);
write_points (out, sp, t, m_ref, relative);
} else {
write_points (*mp_stream, poly, t, m_ref, relative);
write_points (out, poly, t, m_ref, relative);
}
*mp_stream << ")";
}
} else if (s->type () == db::NetShape::Text) {
*mp_stream << Keys::text_key << "(" << lname;
TokenizedOutput out (stream, Keys::text_key);
out << lname;
db::TextRef txtr = s->text_ref ();
db::ICplxTrans t = tr * db::ICplxTrans (txtr.trans ());
*mp_stream << " " << tl::to_word_or_quoted_string (txtr.obj ().string ()) << " ";
out << tl::to_word_or_quoted_string (txtr.obj ().string ());
db::Point pt = t * (db::Point () + txtr.obj ().trans ().disp ());
write_point (*mp_stream, pt, m_ref, relative);
*mp_stream << ")";
write_point (out, pt, m_ref, relative);
}
}
@ -498,13 +561,13 @@ bool std_writer_impl<Keys>::new_cell (cell_index_type ci) const
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::Net &net, unsigned int id, const std::string &indent)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::Net &net, unsigned int id)
{
const db::hier_clusters<db::NetShape> &clusters = mp_l2n->net_clusters ();
const db::Circuit *circuit = net.circuit ();
const db::Connectivity &conn = mp_l2n->connectivity ();
bool any = false;
std::unique_ptr<TokenizedOutput> outp;
if (mp_l2n) {
@ -527,25 +590,24 @@ void std_writer_impl<Keys>::write (const db::Net &net, unsigned int id, const st
} else {
if (! any) {
if (! outp) {
*mp_stream << indent << indent1 << Keys::net_key << "(" << id;
outp.reset (new TokenizedOutput (stream, Keys::net_key));
*outp << tl::to_string (id);
if (! net.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")";
TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.name ());
}
*mp_stream << endl;
*outp << endl;
for (db::NetlistObject::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) {
*mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (*outp, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
any = true;
}
*mp_stream << indent << indent2;
write (si.operator-> (), si.trans (), name_for_layer (mp_l2n, *l), true);
*mp_stream << endl;
write (*outp, si.operator-> (), si.trans (), name_for_layer (mp_l2n, *l), true);
m_progress.set (mp_stream->pos ());
prev_ci = ci;
@ -560,80 +622,66 @@ void std_writer_impl<Keys>::write (const db::Net &net, unsigned int id, const st
}
if (any) {
*mp_stream << indent << indent1 << ")" << endl;
} else {
if (! outp) {
outp.reset (new TokenizedOutput (stream, Keys::net_key));
*outp << tl::to_string (id);
*mp_stream << indent << indent1 << Keys::net_key << "(" << id;
if (! net.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (net.name ()) << ")";
TokenizedOutput (*outp, Keys::name_key, true) << tl::to_word_or_quoted_string (net.name ());
}
if (net.begin_properties () != net.end_properties ()) {
*mp_stream << endl;
*outp << endl;
for (db::NetlistObject::property_iterator p = net.begin_properties (); p != net.end_properties (); ++p) {
*mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (*outp, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
*mp_stream << indent << ")" << endl;
} else {
*mp_stream << ")" << endl;
}
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::SubCircuit &subcircuit, std::map<const db::Net *, unsigned int> &net2id, const std::string &indent)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::SubCircuit &subcircuit, std::map<const db::Net *, unsigned int> &net2id)
{
*mp_stream << indent << indent1 << Keys::circuit_key << "(" << tl::to_string (subcircuit.id ());
*mp_stream << " " << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ());
TokenizedOutput out (stream, Keys::circuit_key);
out << tl::to_string (subcircuit.id ());
out << tl::to_word_or_quoted_string (subcircuit.circuit_ref ()->name ());
if (! subcircuit.name ().empty ()) {
*mp_stream << " " << Keys::name_key << "(" << tl::to_word_or_quoted_string (subcircuit.name ()) << ")";
TokenizedOutput (out, Keys::name_key, true) << tl::to_word_or_quoted_string (subcircuit.name ());
}
if (mp_l2n) {
*mp_stream << " ";
write (subcircuit.trans ());
write (out, subcircuit.trans ());
}
// each pin in one line for more than a few pins
bool separate_lines = (subcircuit.circuit_ref ()->pin_count () > 1) || subcircuit.begin_properties () != subcircuit.end_properties ();
if (separate_lines) {
*mp_stream << endl;
out << endl;
}
for (db::NetlistObject::property_iterator p = subcircuit.begin_properties (); p != subcircuit.end_properties (); ++p) {
*mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (out, Keys::property_key, ! separate_lines) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
unsigned int pin_id = 0;
for (db::Circuit::const_pin_iterator p = subcircuit.circuit_ref ()->begin_pins (); p != subcircuit.circuit_ref ()->end_pins (); ++p, ++pin_id) {
const db::Net *net = subcircuit.net_for_pin (p->id ());
if (net) {
if (separate_lines) {
*mp_stream << indent << indent2;
} else {
*mp_stream << " ";
}
*mp_stream << Keys::pin_key << "(" << tl::to_string (pin_id) << " " << net2id [net] << ")";
if (separate_lines) {
*mp_stream << endl;
}
TokenizedOutput (out, Keys::pin_key, ! separate_lines) << tl::to_string (pin_id) << tl::to_string (net2id [net]);
m_progress.set (mp_stream->pos ());
}
}
if (separate_lines) {
*mp_stream << indent << indent1;
}
*mp_stream << ")" << endl;
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::DeviceAbstract &device_abstract, const std::string &indent)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::DeviceAbstract &device_abstract)
{
tl_assert (mp_l2n);
const std::vector<db::DeviceTerminalDefinition> &td = device_abstract.device_class ()->terminal_definitions ();
const db::hier_clusters<db::NetShape> &clusters = mp_l2n->net_clusters ();
@ -641,10 +689,13 @@ void std_writer_impl<Keys>::write (const db::DeviceAbstract &device_abstract, co
for (std::vector<db::DeviceTerminalDefinition>::const_iterator t = td.begin (); t != td.end (); ++t) {
*mp_stream << indent << indent1 << Keys::terminal_key << "(" << t->name () << endl;
TokenizedOutput out (stream, Keys::terminal_key);
out << t->name ();
reset_geometry_ref ();
bool any = false;
for (db::Connectivity::layer_iterator l = conn.begin_layers (); l != conn.end_layers (); ++l) {
size_t cid = device_abstract.cluster_id_for_terminal (t->id ());
@ -656,72 +707,62 @@ void std_writer_impl<Keys>::write (const db::DeviceAbstract &device_abstract, co
const db::local_cluster<db::NetShape> &lc = clusters.clusters_per_cell (device_abstract.cell_index ()).cluster_by_id (cid);
for (db::local_cluster<db::NetShape>::shape_iterator s = lc.begin (*l); ! s.at_end (); ++s) {
*mp_stream << indent << indent2;
write (s.operator-> (), db::ICplxTrans (), name_for_layer (mp_l2n, *l), true);
*mp_stream << endl;
if (! any) {
out << endl;
}
write (out, s.operator-> (), db::ICplxTrans (), name_for_layer (mp_l2n, *l), true);
m_progress.set (mp_stream->pos ());
any = true;
}
}
*mp_stream << indent << indent1 << ")" << endl;
m_progress.set (mp_stream->pos ());
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::DCplxTrans &tr)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::DCplxTrans &tr)
{
bool first = true;
if (tr.is_mag ()) {
*mp_stream << Keys::scale_key << "(" << tr.mag () << ")";
first = false;
TokenizedOutput (stream, Keys::scale_key, true) << tl::to_string (tr.mag ());
}
if (tr.is_mirror ()) {
if (! first) {
*mp_stream << " ";
}
*mp_stream << Keys::mirror_key;
first = false;
stream << Keys::mirror_key;
}
if (fabs (tr.angle ()) > 1e-6) {
if (! first) {
*mp_stream << " ";
}
*mp_stream << Keys::rotation_key << "(" << tr.angle () << ")";
first = false;
TokenizedOutput (stream, Keys::rotation_key, true) << tl::to_string (tr.angle ());
}
if (! first) {
*mp_stream << " ";
}
*mp_stream << Keys::location_key << "(" << floor (0.5 + tr.disp ().x () / m_dbu) << " " << floor (0.5 + tr.disp ().y () / m_dbu) << ")";
TokenizedOutput (stream, Keys::location_key, true) << tl::to_string (floor (0.5 + tr.disp ().x () / m_dbu)) << tl::to_string (floor (0.5 + tr.disp ().y () / m_dbu));
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::Device &device, std::map<const Net *, unsigned int> &net2id, const std::string &indent)
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::Device &device, std::map<const Net *, unsigned int> &net2id)
{
tl_assert (device.device_class () != 0);
const std::vector<DeviceTerminalDefinition> &td = device.device_class ()->terminal_definitions ();
const std::vector<DeviceParameterDefinition> &pd = device.device_class ()->parameter_definitions ();
*mp_stream << indent << indent1 << Keys::device_key << "(" << tl::to_string (device.id ());
TokenizedOutput out (stream, Keys::device_key);
out << tl::to_string (device.id ());
if (device.device_abstract ()) {
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl;
out << tl::to_word_or_quoted_string (device.device_abstract ()->name ()) << endl;
const std::vector<db::DeviceAbstractRef> &other_abstracts = device.other_abstracts ();
for (std::vector<db::DeviceAbstractRef>::const_iterator a = other_abstracts.begin (); a != other_abstracts.end (); ++a) {
*mp_stream << indent << indent2 << Keys::device_key << "(" << tl::to_word_or_quoted_string (a->device_abstract->name ()) << " ";
write (a->trans);
*mp_stream << ")" << endl;
TokenizedOutput o (out, Keys::device_key);
o << tl::to_word_or_quoted_string (a->device_abstract->name ());
write (o, a->trans);
}
@ -729,41 +770,38 @@ void std_writer_impl<Keys>::write (const db::Device &device, std::map<const Net
for (std::map<unsigned int, std::vector<db::DeviceReconnectedTerminal> >::const_iterator t = reconnected_terminals.begin (); t != reconnected_terminals.end (); ++t) {
for (std::vector<db::DeviceReconnectedTerminal>::const_iterator c = t->second.begin (); c != t->second.end (); ++c) {
*mp_stream << indent << indent2 << Keys::connect_key << "(" << c->device_index << " " << tl::to_word_or_quoted_string (td [t->first].name ()) << " " << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ()) << ")" << endl;
TokenizedOutput (out, Keys::connect_key) << tl::to_string (c->device_index) << tl::to_word_or_quoted_string (td [t->first].name ()) << tl::to_word_or_quoted_string (td [c->other_terminal_id].name ());
}
}
*mp_stream << indent << indent2;
write (device.trans ());
*mp_stream << endl;
write (out, device.trans ());
out << endl;
} else {
*mp_stream << " " << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl;
out << tl::to_word_or_quoted_string (device.device_class ()->name ()) << endl;
}
if (! device.name ().empty ()) {
*mp_stream << indent << indent2 << Keys::name_key << "(" << tl::to_word_or_quoted_string (device.name ()) << ")" << endl;
TokenizedOutput (out, Keys::name_key) << tl::to_word_or_quoted_string (device.name ());
}
for (db::NetlistObject::property_iterator p = device.begin_properties (); p != device.end_properties (); ++p) {
*mp_stream << indent << indent2 << Keys::property_key << "(" << p->first.to_parsable_string () << " " << p->second.to_parsable_string () << ")" << endl;
TokenizedOutput (out, Keys::property_key) << p->first.to_parsable_string () << p->second.to_parsable_string ();
}
for (std::vector<DeviceParameterDefinition>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
*mp_stream << indent << indent2 << Keys::param_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << tl::sprintf ("%.12g", device.parameter_value (i->id ())) << ")" << endl;
TokenizedOutput (out, Keys::param_key) << tl::to_word_or_quoted_string (i->name ()) << tl::sprintf ("%.12g", device.parameter_value (i->id ()));
}
for (std::vector<DeviceTerminalDefinition>::const_iterator i = td.begin (); i != td.end (); ++i) {
const db::Net *net = device.net_for_terminal (i->id ());
TokenizedOutput o (out, Keys::terminal_key);
o << tl::to_word_or_quoted_string (i->name ());
if (net) {
*mp_stream << indent << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << " " << net2id [net] << ")" << endl;
} else {
*mp_stream << indent << indent2 << Keys::terminal_key << "(" << tl::to_word_or_quoted_string (i->name ()) << ")" << endl;
o << tl::to_string (net2id [net]);
}
}
*mp_stream << indent << indent1 << ")" << endl;
}
// explicit instantiation

View File

@ -44,6 +44,35 @@ class Netlist;
class LayoutToNetlist;
class NetShape;
/**
* @brief A helper class to produce token/list lines
* Such lines are like:
* token(a b c)
* This class takes care of properly handling separation blanks
*/
class TokenizedOutput
{
public:
TokenizedOutput (tl::OutputStream &stream);
TokenizedOutput (tl::OutputStream &stream, const std::string &token);
TokenizedOutput (tl::OutputStream &stream, int indent, const std::string &token);
TokenizedOutput (TokenizedOutput &output, const std::string &token, bool inl = false);
~TokenizedOutput ();
TokenizedOutput &operator<< (const std::string &s);
tl::OutputStream &stream () { return *mp_stream; }
private:
tl::OutputStream *mp_stream;
TokenizedOutput *mp_parent;
bool m_first, m_inline, m_newline;
int m_indent;
void emit_sep ();
int indent () const { return m_indent; }
};
namespace l2n_std_format
{
@ -55,7 +84,7 @@ public:
std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ());
void write (const db::LayoutToNetlist *l2n);
void write (const db::Netlist *netlist, const db::LayoutToNetlist *l2n, bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (TokenizedOutput &stream, bool nested, const db::Netlist *netlist, const db::LayoutToNetlist *l2n, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
protected:
tl::OutputStream &stream ()
@ -71,15 +100,15 @@ private:
const db::LayoutToNetlist *mp_l2n;
tl::AbsoluteProgress m_progress;
void write (bool nested, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (const db::Circuit &circuit, const std::string &indent, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (const db::Net &net, unsigned int id, const std::string &indent);
void write (const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &net2id, const std::string &indent);
void write (const db::Device &device, std::map<const Net *, unsigned int> &net2id, const std::string &indent);
void write (const db::DeviceAbstract &device_abstract, const std::string &indent);
void write (const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative);
void write (const db::DCplxTrans &trans);
void write_device_class (const std::string &indent, const db::DeviceClass *cls, const std::string &name, const db::DeviceClass *temp_class);
void write (bool nested, TokenizedOutput &stream, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (TokenizedOutput &stream, const db::Circuit &circuit, std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > *net2id_per_circuit);
void write (TokenizedOutput &stream, const db::Net &net, unsigned int id);
void write (TokenizedOutput &stream, const db::SubCircuit &subcircuit, std::map<const Net *, unsigned int> &net2id);
void write (TokenizedOutput &stream, const db::Device &device, std::map<const Net *, unsigned int> &net2id);
void write (TokenizedOutput &stream, const db::DeviceAbstract &device_abstract);
void write (TokenizedOutput &stream, const db::NetShape *s, const db::ICplxTrans &tr, const std::string &lname, bool relative);
void write (TokenizedOutput &stream, const db::DCplxTrans &trans);
void write_device_class (TokenizedOutput &stream, const db::DeviceClass *cls, const std::string &name, const db::DeviceClass *temp_class);
void reset_geometry_ref ();
// implementation of CircuitCallback

View File

@ -34,6 +34,8 @@ namespace lvs_std_format
DB_PUBLIC std::string LongKeys::reference_key ("reference");
DB_PUBLIC std::string LongKeys::layout_key ("layout");
DB_PUBLIC std::string LongKeys::xref_key ("xref");
DB_PUBLIC std::string LongKeys::log_key ("log");
DB_PUBLIC std::string LongKeys::log_entry_key ("entry");
DB_PUBLIC std::string LongKeys::mismatch_key ("mismatch");
DB_PUBLIC std::string LongKeys::match_key ("match");
@ -41,15 +43,27 @@ namespace lvs_std_format
DB_PUBLIC std::string LongKeys::warning_key ("warning");
DB_PUBLIC std::string LongKeys::skipped_key ("skipped");
DB_PUBLIC std::string LongKeys::info_severity_key ("info");
DB_PUBLIC std::string LongKeys::warning_severity_key ("warning");
DB_PUBLIC std::string LongKeys::error_severity_key ("error");
// E, H, I, J, L, M, S, W, X, Z, 0, 1
DB_PUBLIC std::string ShortKeys::reference_key ("H");
DB_PUBLIC std::string ShortKeys::layout_key ("J");
DB_PUBLIC std::string ShortKeys::xref_key ("Z");
DB_PUBLIC std::string ShortKeys::log_key ("L");
DB_PUBLIC std::string ShortKeys::log_entry_key ("M");
DB_PUBLIC std::string ShortKeys::mismatch_key ("0");
DB_PUBLIC std::string ShortKeys::match_key ("1");
DB_PUBLIC std::string ShortKeys::nomatch_key ("X");
DB_PUBLIC std::string ShortKeys::warning_key ("W");
DB_PUBLIC std::string ShortKeys::skipped_key ("S");
DB_PUBLIC std::string ShortKeys::info_severity_key ("I");
DB_PUBLIC std::string ShortKeys::warning_severity_key ("W");
DB_PUBLIC std::string ShortKeys::error_severity_key ("E");
}
}

View File

@ -47,80 +47,85 @@ namespace db
* The file follows the declaration-before-use principle
* (circuits before subcircuits, nets before use ...)
*
* Global statements:
*
* Main body:
* #%lvsdb-klayout - header line identifies format
* [version|description|layout-netlist|reference-netlist|xrefs|any]*
*
* [version]:
* version(<number>) - file format version [short key: V]
*
* [description]:
* description(<text>) - an arbitrary description text [short key: B]
* layout([layout]) - layout part [short key: J]
* reference([reference-def]*) - reference netlist part [short key: H]
* xref([xref-def]*) - cross-reference part [short key: Z]
*
* [layout]:
* [layout-netlist]:
* layout(...) - layout netlist part [short key: J]
* Content is the LayoutToNetlist dump without version and description
*
* ... - the LayoutToNetlist dump without version and description
* [reference-netlist]:
* reference(...)
* - reference netlist part [short key: H]
* Content is the Netlist dump (reduced version of LayoutToNetlist)
*
* [reference-def]:
* [xrefs]:
* xref([xref|any]*) - cross-reference part [short key: Z]
*
* circuit(<name> [netlist-circuit-def]*)
* - circuit [short key: X]
* [netlist-circuit-def]:
*
* net(<id> [net-name]?) - a net declaration [short key: N]
* pin(<name> <net-id>) - outgoing pin connection [short key: P]
* device(<name> [device-def]*) - device with connections [short key: D]
* circuit(<name> [subcircuit-def]*)
* - subcircuit with connections [short key: X]
*
* [net-name]:
*
* name(<net-name>) - specify net name [short key: I]
*
* [device-def]:
*
* terminal(<terminal-name> <net-id>)
* - specifies connection of the terminal with
* a net [short key: T]
*
* [subcircuit-def]:
*
* pin(<pin-name> <net-id>) - specifies connection of the pin with a net [short key: P]
*
* [xref-def]:
*
* circuit([non] [non] [status]? [message]? [circuit-xrefs])
* [xref]:
* circuit([non] [non] [status|message|log|circuit-xrefs|any]*)
* - circuit pair [short key: X]
*
* [circuit-xrefs]:
*
* xref([pair]*) - circuit cross-reference part [short key: Z]
*
* [pair]
*
* pin([ion] [ion] [status]? [message]?) - a pin pair [short key: P]
* device([ion] [ion] [status]? [message]?) - a device pair [short key: D]
* circuit([ion] [ion] [status]? [message]?) - a subcircuit pair [short key: X]
* net([ion] [ion] [status]? [message]?) - a net pair [short key: N]
*
* [non]
*
* <name> | ()
*
* [ion]
* [log]:
* log([log-entry]*) - log entries [short key: L]
*
* [log-entry]:
* entry([severity] [message|any]*) - log entry [short key: M]
*
* [severity]:
* info | - [short key: I]
* warning | - [short key: W]
* error - [short key: E]
*
* [circuit-xrefs]:
* xref([xref-pin|xref-device|xref-circuit|xref-net|any]*)
* - circuit cross-reference part [short key: Z]
*
* [xref-pin]:
* pin([ion] [ion] [status]? [message]? [any]*)
* - a pin pair [short key: P]
*
* [xref-device]:
* device([ion] [ion] [status]? [message]? [any]*)
* - a device pair [short key: D]
*
* [xref-circuit]:
* circuit([ion] [ion] [status]? [message]? [any]*)
* - a subcircuit pair [short key: X]
*
* [xref-net]:
* net([ion] [ion] [status]? [message]? [any]*)
* - a net pair [short key: N]
*
* [ion]:
* <id> | ()
*
* [message]
*
* [message]:
* description(<name>) - error description [short key: B]
*
* [status]
*
* [status]:
* mismatch | - [short key: 0]
* match | - [short key: 1]
* nomatch | - [short key: X]
* warning | - [short key: W]
* skipped - [short key: S]
*
* [any]:
* * |
* <token> |
* <token> ( [any]* ) |
* <float> |
* <quoted-string>
*/
namespace lvs_std_format
@ -132,12 +137,18 @@ namespace lvs_std_format
static std::string reference_key;
static std::string layout_key;
static std::string xref_key;
static std::string log_key;
static std::string log_entry_key;
static std::string mismatch_key;
static std::string match_key;
static std::string nomatch_key;
static std::string warning_key;
static std::string skipped_key;
static std::string info_severity_key;
static std::string warning_severity_key;
static std::string error_severity_key;
};
struct DB_PUBLIC LongKeys
@ -147,12 +158,18 @@ namespace lvs_std_format
static std::string reference_key;
static std::string layout_key;
static std::string xref_key;
static std::string log_key;
static std::string log_entry_key;
static std::string mismatch_key;
static std::string match_key;
static std::string nomatch_key;
static std::string warning_key;
static std::string skipped_key;
static std::string info_severity_key;
static std::string warning_severity_key;
static std::string error_severity_key;
};
template <bool Short> struct DB_PUBLIC keys;

View File

@ -38,10 +38,10 @@ LayoutVsSchematicStandardReader::LayoutVsSchematicStandardReader (tl::InputStrea
// .. nothing yet ..
}
void LayoutVsSchematicStandardReader::do_read_lvs (db::LayoutVsSchematic *l2n)
void LayoutVsSchematicStandardReader::do_read_lvs (db::LayoutVsSchematic *lvs)
{
try {
read_netlist (l2n);
read_netlist (lvs);
} catch (tl::Exception &ex) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("%s in line: %d of %s")), ex.msg (), stream ().line_number (), path ()));
}
@ -81,14 +81,14 @@ void LayoutVsSchematicStandardReader::read_netlist (db::LayoutVsSchematic *lvs)
} else if (test (skeys::layout_key) || test (lkeys::layout_key)) {
Brace br (this);
LayoutToNetlistStandardReader::read_netlist (0, lvs, true /*nested*/, &m_map_per_circuit_a);
LayoutToNetlistStandardReader::read_netlist (0, lvs, &br, &m_map_per_circuit_a);
br.done ();
} else if (test (skeys::reference_key) || test (lkeys::reference_key)) {
Brace br (this);
std::unique_ptr<db::Netlist> netlist (new db::Netlist ());
LayoutToNetlistStandardReader::read_netlist (netlist.get (), 0, true /*nested*/, &m_map_per_circuit_b);
LayoutToNetlistStandardReader::read_netlist (netlist.get (), 0, &br, &m_map_per_circuit_b);
lvs->set_reference_netlist (netlist.release ());
br.done ();
@ -106,10 +106,14 @@ void LayoutVsSchematicStandardReader::read_netlist (db::LayoutVsSchematic *lvs)
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword")));
skip_element ();
}
}
if (version > 1) {
throw tl::Exception (tl::to_string (tr ("This program version only supports version 1 of the LVS DB format. File version is: ")) + tl::to_string (version));
}
}
bool LayoutVsSchematicStandardReader::read_message (std::string &msg)
@ -146,6 +150,59 @@ bool LayoutVsSchematicStandardReader::read_status (db::NetlistCrossReference::St
}
}
bool LayoutVsSchematicStandardReader::read_severity (db::NetlistCrossReference::Severity &severity)
{
if (test (skeys::info_severity_key) || test (lkeys::info_severity_key)) {
severity = db::NetlistCrossReference::Info;
return true;
} else if (test (skeys::warning_severity_key) || test (lkeys::warning_severity_key)) {
severity = db::NetlistCrossReference::Warning;
return true;
} else if (test (skeys::error_severity_key) || test (lkeys::error_severity_key)) {
severity = db::NetlistCrossReference::Error;
return true;
} else {
return false;
}
}
void LayoutVsSchematicStandardReader::read_log_entry (db::NetlistCrossReference *xref)
{
db::NetlistCrossReference::Severity severity = db::NetlistCrossReference::NoSeverity;
std::string msg;
Brace br (this);
while (br) {
if (read_severity (severity)) {
// continue
} else if (read_message (msg)) {
// continue
} else {
skip_element ();
}
}
br.done ();
xref->log_entry (severity, msg);
}
void LayoutVsSchematicStandardReader::read_logs_for_circuits (db::NetlistCrossReference *xref)
{
Brace br (this);
while (br) {
if (test (skeys::log_entry_key) || test (lkeys::log_entry_key)) {
read_log_entry (xref);
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (net, pin, device or circuit expected)")));
} else {
skip_element ();
}
}
br.done ();
}
void LayoutVsSchematicStandardReader::read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b)
{
Brace br (this);
@ -162,7 +219,7 @@ void LayoutVsSchematicStandardReader::read_xrefs_for_circuits (db::NetlistCrossR
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (net, pin, device or circuit expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (net, pin, device or circuit expected)")));
skip_element ();
}
}
@ -211,10 +268,12 @@ void LayoutVsSchematicStandardReader::read_xref (db::NetlistCrossReference *xref
// continue
} else if (test (skeys::xref_key) || test (lkeys::xref_key)) {
read_xrefs_for_circuits (xref, circuit_a, circuit_b);
} else if (test (skeys::log_key) || test (lkeys::log_key)) {
read_logs_for_circuits (xref);
} else if (at_end ()) {
throw tl::Exception (tl::to_string (tr ("Unexpected end of file inside circuit definition (status keyword of xrefs expected)")));
} else {
throw tl::Exception (tl::to_string (tr ("Invalid keyword inside circuit definition (status keyword of xrefs expected)")));
skip_element ();
}
}
@ -223,6 +282,8 @@ void LayoutVsSchematicStandardReader::read_xref (db::NetlistCrossReference *xref
br.done ();
} else {
skip_element ();
}
}
@ -344,6 +405,10 @@ void LayoutVsSchematicStandardReader::read_net_pair (db::NetlistCrossReference *
read_status (status);
read_message (msg);
while (br) {
skip_element ();
}
br.done ();
xref->gen_nets (net_by_numerical_id (circuit_a, ion_a, m_map_per_circuit_a), net_by_numerical_id (circuit_b, ion_b, m_map_per_circuit_b), status, msg);
@ -362,6 +427,10 @@ void LayoutVsSchematicStandardReader::read_pin_pair (db::NetlistCrossReference *
read_status (status);
read_message (msg);
while (br) {
skip_element ();
}
br.done ();
xref->gen_pins (pin_by_numerical_id (circuit_a, ion_a), pin_by_numerical_id (circuit_b, ion_b), status, msg);
@ -380,6 +449,10 @@ void LayoutVsSchematicStandardReader::read_device_pair (db::NetlistCrossReferenc
read_status (status);
read_message (msg);
while (br) {
skip_element ();
}
br.done ();
xref->gen_devices (device_by_numerical_id (circuit_a, ion_a, m_map_per_circuit_a), device_by_numerical_id (circuit_b, ion_b, m_map_per_circuit_b), status, msg);
@ -398,6 +471,10 @@ void LayoutVsSchematicStandardReader::read_subcircuit_pair (db::NetlistCrossRefe
read_status (status);
read_message (msg);
while (br) {
skip_element ();
}
br.done ();
xref->gen_subcircuits (subcircuit_by_numerical_id (circuit_a, ion_a, m_map_per_circuit_a), subcircuit_by_numerical_id (circuit_b, ion_b, m_map_per_circuit_b), status, msg);

View File

@ -79,6 +79,9 @@ private:
bool read_status (db::NetlistCrossReference::Status &status);
bool read_message (std::string &msg);
void read_log_entry (db::NetlistCrossReference *xref);
void read_logs_for_circuits (db::NetlistCrossReference *xref);
bool read_severity (db::NetlistCrossReference::Severity &severity);
void read_xref (db::NetlistCrossReference *xref);
void read_xrefs_for_circuits (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b);
void read_net_pair (db::NetlistCrossReference *xref, const db::Circuit *circuit_a, const db::Circuit *circuit_b);

View File

@ -28,6 +28,8 @@
namespace db
{
static const std::string endl ("\n");
// -------------------------------------------------------------------------------------------
// LayoutVsSchematicWriterBase implementation
@ -61,25 +63,22 @@ class std_writer_impl
public:
std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description = std::string ());
void write (const db::LayoutVsSchematic *l2n);
void write (const db::LayoutVsSchematic *lvs);
private:
tl::OutputStream &stream ()
tl::OutputStream &ostream ()
{
return l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::stream ();
}
std::string status_to_s (const db::NetlistCrossReference::Status status);
std::string severity_to_s (const db::NetlistCrossReference::Severity severity);
std::string message_to_s (const std::string &msg);
void write (const db::NetlistCrossReference *xref);
void write (TokenizedOutput &stream, const db::NetlistCrossReference *xref);
std::map<const db::Circuit *, std::map<const db::Net *, unsigned int> > m_net2id_per_circuit_a, m_net2id_per_circuit_b;
};
static const std::string endl ("\n");
static const std::string indent1 (" ");
static const std::string indent2 (" ");
template <class Keys>
std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, const std::string &progress_description)
: l2n_std_format::std_writer_impl<typename Keys::l2n_keys> (stream, dbu, progress_description.empty () ? tl::to_string (tr ("Writing LVS database")) : progress_description)
@ -90,39 +89,40 @@ std_writer_impl<Keys>::std_writer_impl (tl::OutputStream &stream, double dbu, co
template <class Keys>
void std_writer_impl<Keys>::write (const db::LayoutVsSchematic *lvs)
{
TokenizedOutput out (ostream ());
out << Keys::lvs_magic_string << endl;
const int version = 0;
stream () << Keys::lvs_magic_string << endl;
if (version > 0) {
stream () << Keys::version_key << "(" << version << ")" << endl;
TokenizedOutput (out, Keys::version_key) << tl::to_string (version);
}
if (lvs->netlist ()) {
if (! Keys::is_short ()) {
stream () << endl << "# Layout" << endl;
out << endl << "# Layout" << endl;
}
stream () << Keys::layout_key << "(" << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (lvs->netlist (), lvs, true, &m_net2id_per_circuit_a);
stream () << ")" << endl;
TokenizedOutput o (out, Keys::layout_key);
o << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (o, true, lvs->netlist (), lvs, &m_net2id_per_circuit_a);
}
if (lvs->reference_netlist ()) {
if (! Keys::is_short ()) {
stream () << endl << "# Reference netlist" << endl;
out << endl << "# Reference netlist" << endl;
}
stream () << Keys::reference_key << "(" << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (lvs->reference_netlist (), 0, true, &m_net2id_per_circuit_b);
stream () << ")" << endl;
TokenizedOutput o (out, Keys::reference_key);
o << endl;
l2n_std_format::std_writer_impl<typename Keys::l2n_keys>::write (o, true, lvs->reference_netlist (), 0, &m_net2id_per_circuit_b);
}
if (lvs->cross_ref ()) {
if (! Keys::is_short ()) {
stream () << endl << "# Cross reference" << endl;
out << endl << "# Cross reference" << endl;
}
stream () << Keys::xref_key << "(" << endl;
write (lvs->cross_ref ());
stream () << ")" << endl;
TokenizedOutput o (out, Keys::xref_key);
o << endl;
write (o, lvs->cross_ref ());
}
}
@ -184,7 +184,7 @@ std::string std_writer_impl<Keys>::message_to_s (const std::string &msg)
if (msg.empty ()) {
return std::string ();
} else {
return " " + Keys::description_key + "(" + tl::to_word_or_quoted_string (msg) + ")";
return Keys::description_key + "(" + tl::to_word_or_quoted_string (msg) + ")";
}
}
@ -192,54 +192,83 @@ template <class Keys>
std::string std_writer_impl<Keys>::status_to_s (const db::NetlistCrossReference::Status status)
{
if (status == db::NetlistCrossReference::Match) {
return " " + Keys::match_key;
return Keys::match_key;
} else if (status == db::NetlistCrossReference::NoMatch) {
return " " + Keys::nomatch_key;
return Keys::nomatch_key;
} else if (status == db::NetlistCrossReference::Mismatch) {
return " " + Keys::mismatch_key;
return Keys::mismatch_key;
} else if (status == db::NetlistCrossReference::MatchWithWarning) {
return " " + Keys::warning_key;
return Keys::warning_key;
} else if (status == db::NetlistCrossReference::Skipped) {
return " " + Keys::skipped_key;
return Keys::skipped_key;
} else {
return std::string ();
}
}
template <class Keys>
void std_writer_impl<Keys>::write (const db::NetlistCrossReference *xref)
std::string std_writer_impl<Keys>::severity_to_s (const db::NetlistCrossReference::Severity severity)
{
if (severity == db::NetlistCrossReference::Info) {
return Keys::info_severity_key;
} else if (severity == db::NetlistCrossReference::Warning) {
return Keys::warning_severity_key;
} else if (severity == db::NetlistCrossReference::Error) {
return Keys::error_severity_key;
} else {
return std::string ();
}
}
template <class Keys>
void std_writer_impl<Keys>::write (TokenizedOutput &stream, const db::NetlistCrossReference *xref)
{
for (db::NetlistCrossReference::circuits_iterator c = xref->begin_circuits (); c != xref->end_circuits (); ++c) {
const db::NetlistCrossReference::PerCircuitData *pcd = xref->per_circuit_data_for (*c);
tl_assert (pcd != 0);
stream () << indent1 << Keys::circuit_key << "(" << name_to_s (c->first) << " " << name_to_s (c->second) << status_to_s (pcd->status) << message_to_s (pcd->msg) << endl;
stream () << indent2 << Keys::xref_key << "(" << endl;
TokenizedOutput out (stream, Keys::circuit_key);
out << name_to_s (c->first) << name_to_s (c->second) << status_to_s (pcd->status) << message_to_s (pcd->msg);
out << endl;
if (! pcd->log_entries.empty ()) {
TokenizedOutput o (out, Keys::log_key);
o << endl;
for (db::NetlistCrossReference::PerCircuitData::log_entries_const_iterator l = pcd->log_entries.begin (); l != pcd->log_entries.end (); ++l) {
TokenizedOutput (o, Keys::log_entry_key, true) << severity_to_s (l->severity) << message_to_s (l->msg);
o << endl;
}
for (db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator n = pcd->nets.begin (); n != pcd->nets.end (); ++n) {
stream () << indent1 << indent2 << Keys::net_key << "(" << net_id_to_s (n->pair.first, m_net2id_per_circuit_a [c->first]) << " " << net_id_to_s (n->pair.second, m_net2id_per_circuit_b [c->second]) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl;
}
std::map<const db::Pin *, unsigned int> pin2index_a, pin2index_b;
build_pin_index_map (c->first, pin2index_a);
build_pin_index_map (c->second, pin2index_b);
{
TokenizedOutput o (out, Keys::xref_key);
o << endl;
for (db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator n = pcd->pins.begin (); n != pcd->pins.end (); ++n) {
stream () << indent1 << indent2 << Keys::pin_key << "(" << pin_id_to_s (n->pair.first, pin2index_a) << " " << pin_id_to_s (n->pair.second, pin2index_b) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl;
for (db::NetlistCrossReference::PerCircuitData::net_pairs_const_iterator n = pcd->nets.begin (); n != pcd->nets.end (); ++n) {
TokenizedOutput (o, Keys::net_key) << net_id_to_s (n->pair.first, m_net2id_per_circuit_a [c->first]) << net_id_to_s (n->pair.second, m_net2id_per_circuit_b [c->second]) << status_to_s (n->status) << message_to_s (n->msg);
}
std::map<const db::Pin *, unsigned int> pin2index_a, pin2index_b;
build_pin_index_map (c->first, pin2index_a);
build_pin_index_map (c->second, pin2index_b);
for (db::NetlistCrossReference::PerCircuitData::pin_pairs_const_iterator n = pcd->pins.begin (); n != pcd->pins.end (); ++n) {
TokenizedOutput (o, Keys::pin_key) << pin_id_to_s (n->pair.first, pin2index_a) << pin_id_to_s (n->pair.second, pin2index_b) << status_to_s (n->status) << message_to_s (n->msg);
}
for (db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator n = pcd->devices.begin (); n != pcd->devices.end (); ++n) {
TokenizedOutput (o, Keys::device_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg);
}
for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator n = pcd->subcircuits.begin (); n != pcd->subcircuits.end (); ++n) {
TokenizedOutput (o, Keys::circuit_key) << ion_to_s (n->pair.first) << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg);
}
}
for (db::NetlistCrossReference::PerCircuitData::device_pairs_const_iterator n = pcd->devices.begin (); n != pcd->devices.end (); ++n) {
stream () << indent1 << indent2 << Keys::device_key << "(" << ion_to_s (n->pair.first) << " " << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl;
}
for (db::NetlistCrossReference::PerCircuitData::subcircuit_pairs_const_iterator n = pcd->subcircuits.begin (); n != pcd->subcircuits.end (); ++n) {
stream () << indent1 << indent2 << Keys::circuit_key << "(" << ion_to_s (n->pair.first) << " " << ion_to_s (n->pair.second) << status_to_s (n->status) << message_to_s (n->msg) << ")" << endl;
}
stream () << indent2 << ")" << endl;
stream () << indent1 << ")" << endl;
}
}

View File

@ -30,6 +30,7 @@
namespace db
{
LoadLayoutOptions::LoadLayoutOptions ()
: m_warn_level (1)
{
// .. nothing yet ..
}
@ -44,6 +45,8 @@ namespace db
{
if (&d != this) {
m_warn_level = d.m_warn_level;
release ();
for (std::map <std::string, FormatSpecificReaderOptions *>::const_iterator o = d.m_options.begin (); o != d.m_options.end (); ++o) {
m_options.insert (std::make_pair (o->first, o->second->clone ()));

View File

@ -79,6 +79,26 @@ public:
*/
~LoadLayoutOptions ();
/**
* @brief Gets the warning level
*
* The warning level is a reader-specific setting which enables or disables warnings
* on specific levels. Level 0 is always "warnings off". The default level is 1
* which means "reasonable warnings emitted".
*/
int warn_level () const
{
return m_warn_level;
}
/**
* @brief Sets the warning level
*/
void set_warn_level (int w)
{
m_warn_level = w;
}
/**
* @brief Sets specific options for the given format
*
@ -217,6 +237,7 @@ public:
private:
std::map <std::string, FormatSpecificReaderOptions *> m_options;
int m_warn_level;
void release ();
};

View File

@ -62,6 +62,8 @@ NetlistComparer::NetlistComparer (NetlistCompareLogger *logger)
m_dont_consider_net_names = false;
m_case_sensitive = false;
m_with_log = true;
}
NetlistComparer::~NetlistComparer ()
@ -379,8 +381,16 @@ NetlistComparer::compare_impl (const db::Netlist *a, const db::Netlist *b) const
} else {
if (mp_logger) {
mp_logger->circuit_skipped (ca, cb, generate_subcircuits_not_verified_warning (ca, verified_circuits_a, cb, verified_circuits_b));
std::string msg = generate_subcircuits_not_verified_warning (ca, verified_circuits_a, cb, verified_circuits_b);
if (m_with_log) {
mp_logger->log_entry (db::NetlistCompareLogger::Error, msg);
}
mp_logger->circuit_skipped (ca, cb, msg);
good = false;
}
}
@ -442,7 +452,6 @@ NetlistComparer::all_subcircuits_verified (const db::Circuit *c, const std::set<
return false;
}
}
return true;
}
@ -889,6 +898,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
// in must_match mode, check if the nets are identical
if (mp_logger) {
if (p->second && ! exact_match) {
if (m_with_log) {
mp_logger->log_entry (db::NetlistCompareLogger::Error,
tl::sprintf (tl::to_string (tr ("Nets %s are paired explicitly, but are not identical topologically")), nets2string (p->first)));
}
mp_logger->net_mismatch (p->first.first, p->first.second);
} else {
mp_logger->match_nets (p->first.first, p->first.second);
@ -944,7 +957,8 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
// Three passes: one without ambiguities, the second one with ambiguities and names (optional) and a third with ambiguities with topology
for (int pass = 0; pass < 3 && ! good; ++pass) {
int num_passes = 3;
for (int pass = 0; pass < num_passes && ! good; ++pass) {
if (pass == 1 && m_dont_consider_net_names) {
// skip the named pass in "don't consider net names" mode
@ -971,8 +985,11 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
compare.subcircuit_equivalence = &subcircuit_equivalence;
compare.device_equivalence = &device_equivalence;
compare.logger = mp_logger;
compare.with_log = m_with_log;
compare.progress = &progress;
std::vector<NodeEdgePair> nodes, other_nodes;
good = true;
while (true) {
@ -1015,7 +1032,8 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
// derive new identities through topology: first collect all nets with the same topological signature
std::vector<NodeEdgePair> nodes, other_nodes;
nodes.clear ();
other_nodes.clear ();
std::vector<NetGraphNode::edge_type> no_edges;
no_edges.push_back (NetGraphNode::edge_type ());
@ -1070,6 +1088,10 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
}
if (pass + 1 == num_passes && ! good && mp_logger && m_with_log) {
compare.analyze_failed_matches ();
}
}
// Report missing net assignment
@ -1119,6 +1141,33 @@ NetlistComparer::compare_circuits (const db::Circuit *c1, const db::Circuit *c2,
return good;
}
static void
analyze_pin_mismatch (const db::Pin *pin1, const db::Circuit *c1, const db::Pin *pin2, const db::Circuit * /*c2*/, db::NetlistCompareLogger *logger)
{
if (! pin1) {
logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("No equivalent pin %s from reference netlist found in netlist.\nThis is an indication that a physical connection is not made to the subcircuit.")), pin2->expanded_name ()));
}
if (! pin2) {
logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("No equivalent pin %s from netlist found in reference netlist.\nThis is an indication that additional physical connections are made to the subcircuit cell.")), pin1->expanded_name ()));
// attempt to identify pins which are creating invalid connections
for (auto p = c1->begin_parents (); p != c1->end_parents (); ++p) {
for (auto c = p->begin_subcircuits (); c != p->end_subcircuits (); ++c) {
const db::SubCircuit &sc = *c;
if (sc.circuit_ref () == c1) {
const db::Net *net = sc.net_for_pin (pin1->id ());
if (net && (net->subcircuit_pin_count () > 1 || net->terminal_count () > 0 || net->pin_count () > 0)) {
logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (tl::to_string (tr ("Potential invalid connection in circuit %s, subcircuit cell reference at %s")), p->name (), sc.trans ().to_string ()));
}
}
}
}
}
}
bool
NetlistComparer::handle_pin_mismatch (const db::NetGraph &g1, const db::Circuit *c1, const db::Pin *pin1, const db::NetGraph &g2, const db::Circuit *c2, const db::Pin *pin2) const
{
@ -1163,7 +1212,11 @@ NetlistComparer::handle_pin_mismatch (const db::NetGraph &g1, const db::Circuit
}
return true;
} else {
if (mp_logger) {
if (m_with_log) {
analyze_pin_mismatch (pin1, c1, pin2, c2, mp_logger);
}
mp_logger->pin_mismatch (pin1, pin2);
}
return false;

View File

@ -50,6 +50,16 @@ public:
NetlistCompareLogger () { }
virtual ~NetlistCompareLogger () { }
/**
* @brief An enum describing the severity for the log_entry function
*/
enum Severity {
NoSeverity = 0, // unspecific
Info = 1, // information only
Warning = 2, // a warning
Error = 3 // an error
};
/**
* @brief Begin logging for netlist a and b
*/
@ -88,6 +98,11 @@ public:
*/
virtual void circuit_mismatch (const db::Circuit * /*a*/, const db::Circuit * /*b*/, const std::string & /*msg*/ = std::string ()) { }
/**
* @brief Receives log entries for the current circuit pair
*/
virtual void log_entry (Severity /*level*/, const std::string & /*msg*/) { }
/**
* @brief Nets a and b match exactly
*/
@ -257,6 +272,24 @@ public:
return m_max_depth;
}
/**
* @brief Sets a value indicating that log messages are generated
* Log messages may be expensive to compute, hence they can be turned off.
* By default, log messages are generated.
*/
void set_with_log (bool f)
{
m_with_log = f;
}
/**
* @brief Gets a value indicating that log messages are generated
*/
bool with_log () const
{
return m_with_log;
}
/**
* @brief Sets a value indicating whether not to consider net names
* This feature is mainly intended for testing.
@ -365,6 +398,7 @@ protected:
bool handle_pin_mismatch (const NetGraph &g1, const db::Circuit *c1, const db::Pin *pin1, const NetGraph &g2, const db::Circuit *c2, const db::Pin *p2) const;
mutable NetlistCompareLogger *mp_logger;
bool m_with_log;
std::map<std::pair<const db::Circuit *, const db::Circuit *>, std::vector<std::pair<std::pair<const Net *, const Net *>, bool> > > m_same_nets;
std::unique_ptr<CircuitPinCategorizer> mp_circuit_pin_categorizer;
std::unique_ptr<DeviceCategorizer> mp_device_categorizer;

View File

@ -31,6 +31,7 @@
#include "tlAssert.h"
#include "tlLog.h"
#include "tlInternational.h"
namespace db
{
@ -402,6 +403,7 @@ NetlistCompareCore::NetlistCompareCore (NetGraph *graph, NetGraph *other_graph)
dont_consider_net_names (false),
with_ambiguous (false),
logger (0),
with_log (true),
circuit_pin_mapper (0),
subcircuit_equivalence (0),
device_equivalence (0),
@ -1049,6 +1051,10 @@ NetlistCompareCore::derive_node_identities_from_ambiguity_group (const NodeRange
if (ambiguous) {
if (logger) {
if (with_log) {
logger->log_entry (db::NetlistCompareLogger::Warning,
tl::sprintf (tl::to_string (tr ("Matching nets %s from an ambiguous group of nets")), nets2string (p->first->net (), p->second->net ())));
}
logger->match_ambiguous_nets (p->first->net (), p->second->net ());
}
for (db::Net::const_pin_iterator i = p->first->net ()->begin_pins (); i != p->first->net ()->end_pins (); ++i) {
@ -1070,9 +1076,11 @@ NetlistCompareCore::derive_node_identities_from_ambiguity_group (const NodeRange
std::vector<std::pair<const NetGraphNode *, const NetGraphNode *> >::const_iterator p = pairs.begin ();
for (std::list<TentativeNodeMapping>::iterator tn_of_pair = tn_for_pairs.begin (); tn_of_pair != tn_for_pairs.end (); ++tn_of_pair, ++p) {
bool was_ambiguous = equivalent_other_nodes.has_attribute (p->second);
// Note: this would propagate ambiguities to all *derived* mappings. But this probably goes too far:
// bool ambiguous = equivalent_other_nodes.has_attribute (p->second);
// Instead we ignore propagated ambiguities for now:
// bool ambiguous = was_ambiguous;
// Instead we ignore propagated ambiguitied for now:
bool ambiguous = false;
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
@ -1100,13 +1108,18 @@ NetlistCompareCore::derive_node_identities_from_ambiguity_group (const NodeRange
NetGraphNode *n_other = & mp_other_graph->node (other_net_index);
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare || tl::verbosity () >= 40) {
if (ambiguous) {
tl::info << indent_s << "deduced ambiguous match: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
if (was_ambiguous) {
tl::info << indent_s << "deduced from ambiguous match: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
} else {
tl::info << indent_s << "deduced match: " << n->net ()->expanded_name () << " vs. " << n_other->net ()->expanded_name ();
}
}
if (logger && with_log && was_ambiguous) {
logger->log_entry (db::NetlistCompareLogger::Info,
tl::sprintf (tl::to_string (tr ("Matching nets %s following an ambiguous match")), nets2string (n->net (), n_other->net ())));
}
if (ambiguous) {
if (logger) {
logger->match_ambiguous_nets (n->net (), n_other->net ());
@ -1244,6 +1257,263 @@ NetlistCompareCore::derive_node_identities_from_singular_match (const NetGraphNo
}
}
static size_t distance (const NetGraphNode &a, const NetGraphNode &b)
{
auto i = a.begin ();
auto j = b.begin ();
size_t fuzz = 0;
while (i != a.end () || j != b.end ()) {
if (j == b.end ()) {
++fuzz;
++i;
continue;
}
if (i == a.end ()) {
++fuzz;
++j;
continue;
}
if (i->first < j->first) {
++fuzz;
++i;
continue;
} else if (j->first < i->first) {
++fuzz;
++j;
continue;
}
++i;
++j;
}
return fuzz;
}
static size_t distance3 (const NetGraphNode &a, const NetGraphNode &b1, const NetGraphNode &b2, const NetGraph &gb)
{
bool needs_join = false;
for (auto e = b1.begin (); e != b1.end () && ! needs_join; ++e) {
needs_join = (e->second.second == b1.net () || e->second.second == b2.net ());
}
for (auto e = b2.begin (); e != b2.end () && ! needs_join; ++e) {
needs_join = (e->second.second == b1.net () || e->second.second == b2.net ());
}
if (needs_join) {
return distance (a, gb.joined (b1, b2));
}
auto i = a.begin ();
auto j1 = b1.begin ();
auto j2 = b2.begin ();
size_t fuzz = 0;
while (i != a.end () || j1 != b1.end () || j2 != b2.end ()) {
if (j1 == b1.end () && j2 == b2.end ()) {
++fuzz;
++i;
continue;
}
bool use_j1 = j2 == b2.end () || (j1 != b1.end () && *j1 < *j2);
auto &j = use_j1 ? j1 : j2;
if (i == a.end ()) {
++fuzz;
++j;
continue;
}
if (i->first < j->first) {
++fuzz;
++i;
continue;
} else if (j->first < i->first) {
++fuzz;
++j;
continue;
}
++i;
++j;
}
return fuzz;
}
static void
analyze_nodes_for_close_matches (const std::multimap<size_t, const NetGraphNode *> &nodes_by_edges1, const std::multimap<size_t, const NetGraphNode *> &nodes_by_edges2, bool layout2ref, db::NetlistCompareLogger *logger, const db::NetGraph &g2)
{
size_t max_search = 100;
double max_fuzz_factor = 0.25;
size_t max_fuzz_count = 3;
size_t max_edges_split = 3; // by how many edges joining will reduce the edge count at max
size_t min_edges = 2;
std::string msg;
if (layout2ref) {
msg = tl::to_string (tr ("Net %s may be shorting nets %s and %s from reference netlist (fuzziness %d nodes)"));
} else {
msg = tl::to_string (tr ("Connecting nets %s and %s is making a better match to net %s from reference netlist (fuzziness %d nodes)"));
}
for (auto i = nodes_by_edges1.begin (); i != nodes_by_edges1.end (); ++i) {
if (i->first < min_edges) {
continue;
}
std::set<const db::NetGraphNode *> seen;
for (auto j = nodes_by_edges2.begin (); j != nodes_by_edges2.end (); ++j) {
seen.insert (j->second);
if (j->first > i->first + max_fuzz_count - 1) {
break;
}
size_t ne = i->first > j->first ? i->first - j->first : 0;
if (ne > max_fuzz_count) {
ne -= max_fuzz_count;
}
if (ne == 0 && layout2ref) {
// analyze nets for similarities (only layout -> ref as the other case is symmetric)
size_t fuzz = distance (*i->second, *j->second);
double fuzz_factor = double (fuzz) / ne;
if (fuzz_factor < max_fuzz_factor) {
std::string msg = tl::to_string (tr ("Net %s from netlist approximately matches net %s from reference netlist (fuzziness %d nodes)"));
logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (msg,
i->second->net ()->expanded_name (),
j->second->net ()->expanded_name (),
int (fuzz)));
}
}
auto k = nodes_by_edges2.lower_bound (ne);
size_t tries = max_search;
for ( ; k != nodes_by_edges2.end () && j->first + k->first < i->first + max_fuzz_count + max_edges_split && tries > 0; ++k) {
if (seen.find (k->second) != seen.end ()) {
continue;
}
size_t fuzz = distance3 (*i->second, *j->second, *k->second, g2);
double fuzz_factor = double (fuzz) / i->first;
if (fuzz_factor < max_fuzz_factor) {
logger->log_entry (db::NetlistCompareLogger::Info, tl::sprintf (msg,
(layout2ref ? i : j)->second->net ()->expanded_name (),
(layout2ref ? j : k)->second->net ()->expanded_name (),
(layout2ref ? k : i)->second->net ()->expanded_name (),
int (fuzz)));
}
--tries;
}
}
}
}
void
NetlistCompareCore::analyze_failed_matches () const
{
// Determine the range of nodes with same identity
std::vector<NetGraphNode::edge_type> no_edges;
no_edges.push_back (NetGraphNode::edge_type ());
std::vector<NodeEdgePair> nodes, other_nodes;
nodes.reserve (mp_graph->end () - mp_graph->begin ());
for (db::NetGraph::node_iterator i1 = mp_graph->begin (); i1 != mp_graph->end (); ++i1) {
if (i1->net ()) {
nodes.push_back (NodeEdgePair (i1.operator-> (), no_edges.begin ()));
}
}
other_nodes.reserve (mp_other_graph->end () - mp_other_graph->begin ());
for (db::NetGraph::node_iterator i2 = mp_other_graph->begin (); i2 != mp_other_graph->end (); ++i2) {
if (i2->net ()) {
other_nodes.push_back (NodeEdgePair (i2.operator-> (), no_edges.begin ()));
}
}
std::sort (nodes.begin (), nodes.end (), CompareNodeEdgePair ());
std::sort (other_nodes.begin (), other_nodes.end (), CompareNodeEdgePair ());
auto n1 = nodes.begin ();
auto n2 = other_nodes.begin ();
std::vector<const db::NetGraphNode *> singular1, singular2;
while (n1 != nodes.end () || n2 != other_nodes.end ()) {
if (n2 == other_nodes.end ()) {
singular1.push_back (n1->node);
++n1;
continue;
} else if (n1 == nodes.end ()) {
singular2.push_back (n2->node);
++n2;
continue;
}
if (*n1->node < *n2->node) {
singular1.push_back (n1->node);
++n1;
continue;
} else if (*n2->node < *n1->node) {
singular2.push_back (n2->node);
++n2;
continue;
}
++n1;
++n2;
}
for (auto i = singular1.begin (); i != singular1.end (); ++i) {
logger->log_entry (db::NetlistCompareLogger::Error, tl::sprintf (tl::to_string (tr ("Net %s is not matching any net from reference netlist")), (*i)->net ()->expanded_name ()));
}
// attempt some analysis for close matches (including shorts / opens)
std::multimap<size_t, const NetGraphNode *> nodes_by_edges1, nodes_by_edges2;
for (auto i = singular1.begin (); i != singular1.end (); ++i) {
const NetGraphNode *n = *i;
nodes_by_edges1.insert (std::make_pair (n->end () - n->begin (), n));
}
for (auto i = singular2.begin (); i != singular2.end (); ++i) {
const NetGraphNode *n = *i;
nodes_by_edges2.insert (std::make_pair (n->end () - n->begin (), n));
}
analyze_nodes_for_close_matches (nodes_by_edges1, nodes_by_edges2, true, logger, *mp_other_graph);
analyze_nodes_for_close_matches (nodes_by_edges2, nodes_by_edges1, false, logger, *mp_graph);
}
size_t
NetlistCompareCore::derive_node_identities_from_node_set (std::vector<NodeEdgePair> &nodes, std::vector<NodeEdgePair> &other_nodes) const
{
@ -1281,6 +1551,9 @@ NetlistCompareCore::derive_node_identities_from_node_set (std::vector<NodeEdgePa
}
if (max_depth != std::numeric_limits<size_t>::max() && depth > max_depth) {
if (with_log) {
logger->log_entry (db::NetlistCompareLogger::Warning, tl::sprintf (tl::to_string (tr ("Maximum depth exhausted (max depth is %d)")), int (max_depth)));
}
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "max. depth exhausted (" << depth << ">" << max_depth << ")";
}
@ -1396,6 +1669,9 @@ NetlistCompareCore::derive_node_identities_from_node_set (std::vector<NodeEdgePa
} else if (max_n_branch != std::numeric_limits<size_t>::max () && double (std::max (nr->num1, nr->num2)) * double (n_branch) > double (max_n_branch)) {
if (with_log) {
logger->log_entry (db::NetlistCompareLogger::Warning, tl::sprintf (tl::to_string (tr ("Maximum complexity exhausted (max complexity is %s, needs at least %s)")), tl::to_string (max_n_branch), tl::to_string (std::max (nr->num1, nr->num2) * n_branch)));
}
if (db::NetlistCompareGlobalOptions::options ()->debug_netcompare) {
tl::info << indent_s << "max. complexity exhausted (" << std::max (nr->num1, nr->num2) << "*" << n_branch << ">" << max_n_branch << ") - mismatch.";
}

View File

@ -85,12 +85,18 @@ public:
*/
size_t derive_node_identities_from_node_set (std::vector<NodeEdgePair> &nodes, std::vector<NodeEdgePair> &other_nodes) const;
/**
* @brief Analyzes the non-matched remaining nodes and produces log output
*/
void analyze_failed_matches () const;
size_t max_depth;
size_t max_n_branch;
bool depth_first;
bool dont_consider_net_names;
bool with_ambiguous;
NetlistCompareLogger *logger;
bool with_log;
CircuitPinCategorizer *circuit_pin_mapper;
SubCircuitEquivalenceTracker *subcircuit_equivalence;
DeviceEquivalenceTracker *device_equivalence;

View File

@ -624,4 +624,48 @@ NetGraph::build (const db::Circuit *c, DeviceCategorizer &device_categorizer, Ci
}
}
NetGraphNode
NetGraph::joined (const NetGraphNode &a, const NetGraphNode &b) const
{
NetGraphNode nj = a;
nj.edges ().clear ();
nj.edges ().reserve ((a.end () - a.begin ()) + (b.end () - b.begin ()));
std::map<const db::Net *, NetGraphNode::edge_type> joined;
for (int m = 0; m < 2; ++m) {
const NetGraphNode &n = (m == 0 ? a : b);
for (auto i = n.begin (); i != n.end (); ++i) {
if (i->second.second) {
const db::Net *net = i->second.second == b.net () ? a.net () : i->second.second;
auto j = joined.find (net);
if (j != joined.end ()) {
j->second.first.insert (j->second.first.end (), i->first.begin (), i->first.end ());
} else {
j = joined.insert (std::make_pair (net, *i)).first;
j->second.second.second = net;
}
} else {
nj.edges ().push_back (*i);
}
}
}
for (auto i = joined.begin (); i != joined.end (); ++i) {
nj.edges ().push_back (i->second);
}
nj.apply_net_index (m_net_index);
return nj;
}
}

View File

@ -272,6 +272,11 @@ public:
}
}
std::vector<edge_type> &edges ()
{
return m_edges;
}
private:
const db::Net *mp_net;
size_t m_other_net_index;
@ -414,6 +419,11 @@ public:
return const_cast<db::NetGraphNode &> (((const NetGraph *) this)->virtual_node (sc));
}
/**
* @brief Creates a new node representing two joined nodes
*/
NetGraphNode joined (const NetGraphNode &a, const NetGraphNode &b) const;
/**
* @brief Gets the net for a given node index
*/

View File

@ -61,7 +61,8 @@ NetlistCompareGlobalOptions::options ()
// --------------------------------------------------------------------------------------------------------------------
// Some utilities
std::string nl_compare_debug_indent (size_t depth)
std::string
nl_compare_debug_indent (size_t depth)
{
std::string s;
for (size_t d = 0; d < depth; ++d) {
@ -70,6 +71,38 @@ std::string nl_compare_debug_indent (size_t depth)
return s;
}
const std::string var_sep = tl::to_string (tr (" vs. "));
static std::string
expanded_name (const db::Net *a)
{
if (a == 0) {
return tl::to_string (tr ("(not connected)"));
} else {
return a->expanded_name ();
}
}
std::string
nets2string (const db::Net *a, const db::Net *b)
{
std::string na = expanded_name (a);
std::string nb = expanded_name (b);
if (na != nb) {
return na + var_sep + nb;
} else {
return nb;
}
}
std::string
nets2string (const std::pair<const db::Net *, const db::Net *> &np)
{
return nets2string (np.first, np.second);
}
// --------------------------------------------------------------------------------------------------------------------
// Some functions

View File

@ -83,6 +83,8 @@ const size_t unknown_id = std::numeric_limits<size_t>::max () - 1;
// Some utilities
std::string nl_compare_debug_indent (size_t depth);
std::string nets2string (const db::Net *a, const db::Net *b);
std::string nets2string (const std::pair<const db::Net *, const db::Net *> &np);
// --------------------------------------------------------------------------------------------------------------------
// Net name compare

View File

@ -416,6 +416,16 @@ NetlistCrossReference::gen_end_circuit (const db::Circuit *, const db::Circuit *
mp_per_circuit_data = 0;
}
void
NetlistCrossReference::gen_log_entry (Severity severity, const std::string &msg)
{
if (mp_per_circuit_data) {
mp_per_circuit_data->log_entries.push_back (LogEntryData (severity, msg));
} else {
m_other_log_entries.push_back (LogEntryData (severity, msg));
}
}
void
NetlistCrossReference::gen_nets (const db::Net *a, const db::Net *b, Status status, const std::string &msg)
{

View File

@ -105,6 +105,15 @@ public:
std::string msg;
};
struct LogEntryData
{
LogEntryData (Severity s, const std::string &m) : severity (s), msg (m) { }
LogEntryData () : severity (NoSeverity) { }
Severity severity;
std::string msg;
};
struct PerCircuitData
{
PerCircuitData () : status (None) { }
@ -117,6 +126,8 @@ public:
typedef pin_pairs_type::const_iterator pin_pairs_const_iterator;
typedef std::vector<SubCircuitPairData> subcircuit_pairs_type;
typedef subcircuit_pairs_type::const_iterator subcircuit_pairs_const_iterator;
typedef std::vector<LogEntryData> log_entries_type;
typedef log_entries_type::const_iterator log_entries_const_iterator;
Status status;
std::string msg;
@ -124,6 +135,7 @@ public:
device_pairs_type devices;
pin_pairs_type pins;
subcircuit_pairs_type subcircuits;
log_entries_type log_entries;
};
struct PerNetData
@ -145,6 +157,7 @@ public:
void gen_end_netlist (const db::Netlist *a, const db::Netlist *b);
void gen_begin_circuit (const db::Circuit *a, const db::Circuit *b);
void gen_end_circuit (const db::Circuit *a, const db::Circuit *b, Status status, const std::string &msg);
void gen_log_entry (Severity severity, const std::string &msg);
void gen_nets (const db::Net *a, const db::Net *b, Status status, const std::string &msg);
void gen_devices (const db::Device *a, const db::Device *b, Status status, const std::string &msg);
void gen_pins (const db::Pin *a, const db::Pin *b, Status status, const std::string &msg);
@ -185,6 +198,11 @@ public:
gen_end_circuit (a, b, Mismatch, msg);
}
virtual void log_entry (Severity severity, const std::string &msg)
{
gen_log_entry (severity, msg);
}
virtual void match_nets (const db::Net *a, const db::Net *b)
{
gen_nets (a, b, Match, std::string ());
@ -261,6 +279,11 @@ public:
return m_circuits.end ();
}
const PerCircuitData::log_entries_type &other_log_entries () const
{
return m_other_log_entries;
}
const db::Pin *other_pin_for (const db::Pin *pin) const;
const db::Device *other_device_for (const db::Device *device) const;
const db::SubCircuit *other_subcircuit_for (const db::SubCircuit *subcircuit) const;
@ -295,6 +318,7 @@ private:
std::map<const db::SubCircuit *, const db::SubCircuit *> m_other_subcircuit;
std::pair<const db::Circuit *, const db::Circuit *> m_current_circuits;
PerCircuitData *mp_per_circuit_data;
PerCircuitData::log_entries_type m_other_log_entries;
void establish_pair (const db::Circuit *a, const db::Circuit *b);
void establish_pair (const db::Net *a, const db::Net *b, Status status, const std::string &msg);

View File

@ -61,7 +61,7 @@ join_layer_names (std::string &s, const std::string &n)
// ReaderBase implementation
ReaderBase::ReaderBase ()
: m_warnings_as_errors (false)
: m_warnings_as_errors (false), m_warn_level (1)
{
}
@ -75,6 +75,12 @@ ReaderBase::set_warnings_as_errors (bool f)
m_warnings_as_errors = f;
}
void
ReaderBase::init (const db::LoadLayoutOptions &options)
{
m_warn_level = options.warn_level ();
}
// ---------------------------------------------------------------
// Reader implementation

View File

@ -89,8 +89,20 @@ public:
return m_warnings_as_errors;
}
/**
* @brief Gets the warning level
*/
int warn_level () const
{
return m_warn_level;
}
protected:
virtual void init (const db::LoadLayoutOptions &options);
private:
bool m_warnings_as_errors;
int m_warn_level;
};
/**

View File

@ -998,6 +998,8 @@ Shapes::clear ()
{
if (!m_layers.empty ()) {
invalidate_state (); // HINT: must come before the change is done!
for (tl::vector<LayerBase *>::const_iterator l = m_layers.end (); l != m_layers.begin (); ) {
// because the undo stack will do a push, we need to remove layers from the back (this is the last undo
// element to be executed)
@ -1010,7 +1012,6 @@ Shapes::clear ()
}
}
invalidate_state (); // HINT: must come before the change is done!
m_layers.clear ();
}

View File

@ -141,6 +141,55 @@ struct cell_inst_array_defs
}
}
// Cell-based constructors
static C *
new_cell_inst_vector2 (const db::Cell *cell, const vector_type &v)
{
tl_assert (cell != 0);
return new_cell_inst_vector (cell->cell_index (), v);
}
static C *
new_cell_inst2 (const db::Cell *cell, const trans_type &t)
{
tl_assert (cell != 0);
return new_cell_inst (cell->cell_index (), t);
}
static C *
new_cell_inst_cplx2 (const db::Cell *cell, const complex_trans_type &t)
{
tl_assert (cell != 0);
return new_cell_inst_cplx (cell->cell_index (), t);
}
static C *
new_cell_inst_array_vector2 (const db::Cell *cell, const vector_type &v,
const vector_type &a, const vector_type &b, unsigned long na, unsigned long nb)
{
tl_assert (cell != 0);
return new_cell_inst_array_vector (cell->cell_index (), v, a, b, na, nb);
}
static C *
new_cell_inst_array2 (const db::Cell *cell, const trans_type &t,
const vector_type &a, const vector_type &b, unsigned long na, unsigned long nb)
{
tl_assert (cell != 0);
return new_cell_inst_array (cell->cell_index (), t, a, b, na, nb);
}
static C *
new_cell_inst_array_cplx2 (const db::Cell *cell, const complex_trans_type &t,
const vector_type &a, const vector_type &b, unsigned long na, unsigned long nb)
{
tl_assert (cell != 0);
return new_cell_inst_array_cplx (cell->cell_index (), t, a, b, na, nb);
}
// Methods
static db::cell_index_type cell_index (const C *a)
{
return a->object ().cell_index ();
@ -151,6 +200,12 @@ struct cell_inst_array_defs
a->object ().cell_index (cell_index);
}
static void set_cell (C *a, db::Cell *cell)
{
tl_assert (cell != 0);
a->object ().cell_index (cell->cell_index ());
}
static C transformed_simple (const C *arr, const coord_trans_type &t)
{
return arr->transformed (t);
@ -421,17 +476,41 @@ struct cell_inst_array_defs
"@param cell_index The cell to instantiate\n"
"@param trans The transformation by which to instantiate the cell\n"
) +
gsi::constructor ("new", &new_cell_inst2, gsi::arg ("cell"), gsi::arg ("trans"),
"@brief Creates a single cell instance\n"
"@param cell The cell to instantiate\n"
"@param trans The transformation by which to instantiate the cell\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_vector, gsi::arg ("cell_index"), gsi::arg ("disp"),
"@brief Creates a single cell instance\n"
"@param cell_index The cell to instantiate\n"
"@param disp The displacement\n"
"This convenience initializer has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_vector2, gsi::arg ("cell"), gsi::arg ("disp"),
"@brief Creates a single cell instance\n"
"@param cell The cell to instantiate\n"
"@param disp The displacement\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_cplx, gsi::arg ("cell_index"), gsi::arg ("trans"),
"@brief Creates a single cell instance with a complex transformation\n"
"@param cell_index The cell to instantiate\n"
"@param trans The complex transformation by which to instantiate the cell\n"
) +
gsi::constructor ("new", &new_cell_inst_cplx2, gsi::arg ("cell"), gsi::arg ("trans"),
"@brief Creates a single cell instance with a complex transformation\n"
"@param cell The cell to instantiate\n"
"@param trans The complex transformation by which to instantiate the cell\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_array, gsi::arg ("cell_index"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance\n"
"@param cell_index The cell to instantiate\n"
@ -445,6 +524,18 @@ struct cell_inst_array_defs
"Starting with version 0.25 the displacements are of vector type."
)
) +
gsi::constructor ("new", &new_cell_inst_array2, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance\n"
"@param cell The cell to instantiate\n"
"@param trans The transformation by which to instantiate the cell\n"
"@param a The displacement vector of the array in the 'a' axis\n"
"@param b The displacement vector of the array in the 'b' axis\n"
"@param na The number of placements in the 'a' axis\n"
"@param nb The number of placements in the 'b' axis\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_array_vector, gsi::arg ("cell_index"), gsi::arg ("disp"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance\n"
"@param cell_index The cell to instantiate\n"
@ -456,6 +547,18 @@ struct cell_inst_array_defs
"\n"
"This convenience initializer has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_array_vector2, gsi::arg ("cell"), gsi::arg ("disp"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance\n"
"@param cell The cell to instantiate\n"
"@param disp The basic displacement of the first instance\n"
"@param a The displacement vector of the array in the 'a' axis\n"
"@param b The displacement vector of the array in the 'b' axis\n"
"@param na The number of placements in the 'a' axis\n"
"@param nb The number of placements in the 'b' axis\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::constructor ("new", &new_cell_inst_array_cplx, gsi::arg ("cell_index"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance with a complex transformation\n"
"@param cell_index The cell to instantiate\n"
@ -469,6 +572,18 @@ struct cell_inst_array_defs
"Starting with version 0.25 the displacements are of vector type."
)
) +
gsi::constructor ("new", &new_cell_inst_array_cplx2, gsi::arg ("cell"), gsi::arg ("trans"), gsi::arg ("a"), gsi::arg ("b"), gsi::arg ("na"), gsi::arg ("nb"),
"@brief Creates a single cell instance with a complex transformation\n"
"@param cell The cell to instantiate\n"
"@param trans The complex transformation by which to instantiate the cell\n"
"@param a The displacement vector of the array in the 'a' axis\n"
"@param b The displacement vector of the array in the 'b' axis\n"
"@param na The number of placements in the 'a' axis\n"
"@param nb The number of placements in the 'b' axis\n"
"\n"
"This convenience variant takes a \\Cell pointer and is equivalent to using 'cell.cell_index()'. It "
"has been introduced in version 0.28."
) +
gsi::iterator ("each_trans", (typename C::iterator (C::*) () const) &C::begin,
"@brief Gets the simple transformations represented by this instance\n"
"For a single instance, this iterator will deliver the single, simple transformation. "
@ -500,10 +615,18 @@ struct cell_inst_array_defs
) +
gsi::method_ext ("cell_index", &cell_index,
"@brief Gets the cell index of the cell instantiated \n"
"Use \\Layout#cell to get the \\Cell object from the cell index."
) +
method_ext ("cell_index=", &set_cell_index, gsi::arg ("index"),
"@brief Sets the index of the cell this instance refers to\n"
) +
method_ext ("cell=", &set_cell, gsi::arg ("cell"),
"@brief Sets the cell this instance refers to\n"
"This is a convenience method and equivalent to 'cell_index = cell.cell_index()'. There is no getter for "
"the cell pointer because the \\CellInstArray object only knows about cell indexes.\n"
"\n"
"This convenience method has been introduced in version 0.28.\n"
) +
gsi::method ("cplx_trans", (complex_trans_type (C::*) () const) &C::complex_trans,
"@brief Gets the complex transformation of the first instance in the array\n"
"This method is always applicable, compared to \\trans, since simple transformations can be expressed as complex transformations as well."

View File

@ -98,7 +98,7 @@ static std::vector<std::string> l2n_layer_names (const db::LayoutToNetlist *l2n)
return ln;
}
static db::Region antenna_check3 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_area_factor, double poly_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<tl::Variant> &diodes)
static db::Region antenna_check3 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_area_factor, double poly_perimeter_factor, const db::Region &metal, double metal_area_factor, double metal_perimeter_factor, double ratio, const std::vector<tl::Variant> &diodes, db::Texts *texts)
{
std::vector<std::pair<const db::Region *, double> > diode_pairs;
@ -127,17 +127,17 @@ static db::Region antenna_check3 (db::LayoutToNetlist *l2n, const db::Region &po
}
return l2n->antenna_check (poly, poly_area_factor, poly_perimeter_factor, metal, metal_area_factor, metal_perimeter_factor, ratio, diode_pairs);
return l2n->antenna_check (poly, poly_area_factor, poly_perimeter_factor, metal, metal_area_factor, metal_perimeter_factor, ratio, diode_pairs, texts);
}
static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector<tl::Variant> &diodes)
static db::Region antenna_check2 (db::LayoutToNetlist *l2n, const db::Region &poly, double poly_perimeter_factor, const db::Region &metal, double metal_perimeter_factor, double ratio, const std::vector<tl::Variant> &diodes, db::Texts *texts)
{
return antenna_check3 (l2n, poly, 1, poly_perimeter_factor, metal, 1, metal_perimeter_factor, ratio, diodes);
return antenna_check3 (l2n, poly, 1, poly_perimeter_factor, metal, 1, metal_perimeter_factor, ratio, diodes, texts);
}
static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &poly, const db::Region &metal, double ratio, const std::vector<tl::Variant> &diodes)
static db::Region antenna_check (db::LayoutToNetlist *l2n, const db::Region &poly, const db::Region &metal, double ratio, const std::vector<tl::Variant> &diodes, db::Texts *texts)
{
return antenna_check3 (l2n, poly, 1, 0, metal, 1, 0, ratio, diodes);
return antenna_check3 (l2n, poly, 1, 0, metal, 1, 0, ratio, diodes, texts);
}
static void join_net_names (db::LayoutToNetlist *l2n, const std::string &s)
@ -701,7 +701,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"@brief Reads the extracted netlist from the file.\n"
"This method employs the native format of KLayout.\n"
) +
gsi::method_ext ("antenna_check", &antenna_check, gsi::arg ("gate"), gsi::arg ("metal"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"),
gsi::method_ext ("antenna_check", &antenna_check, gsi::arg ("gate"), gsi::arg ("metal"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"), gsi::arg ("texts", (db::Texts *) 0, "nil"),
"@brief Runs an antenna check on the extracted clusters\n"
"\n"
"The antenna check will traverse all clusters and run an antenna check\n"
@ -741,8 +741,13 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"# diode_layer1 increases the ratio by 50 per square micrometer area:\n"
"errors = l2n.antenna(poly, metal, 10.0 [ [ diode_layer, 50.0 ] ])\n"
"@/code\n"
"\n"
"If 'texts' is non-nil, this text collection will receive labels explaining the error in "
"terms of area values and relevant ratio.\n"
"\n"
"The 'texts' parameter has been added in version 0.27.11."
) +
gsi::method_ext ("antenna_check", &antenna_check2, gsi::arg ("gate"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"),
gsi::method_ext ("antenna_check", &antenna_check2, gsi::arg ("gate"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"), gsi::arg ("texts", (db::Texts *) 0, "nil"),
"@brief Runs an antenna check on the extracted clusters taking the perimeter into account\n"
"\n"
"This version of the \\antenna_check method allows taking the perimeter of gate or metal into account. "
@ -759,7 +764,7 @@ Class<db::LayoutToNetlist> decl_dbLayoutToNetlist ("db", "LayoutToNetlist",
"\n"
"This variant has been introduced in version 0.26.6.\n"
) +
gsi::method_ext ("antenna_check", &antenna_check3, gsi::arg ("gate"), gsi::arg ("gate_area_factor"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_area_factor"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"),
gsi::method_ext ("antenna_check", &antenna_check3, gsi::arg ("gate"), gsi::arg ("gate_area_factor"), gsi::arg ("gate_perimeter_factor"), gsi::arg ("metal"), gsi::arg ("metal_area_factor"), gsi::arg ("metal_perimeter_factor"), gsi::arg ("ratio"), gsi::arg ("diodes", std::vector<tl::Variant> (), "[]"), gsi::arg ("texts", (db::Texts *) 0, "nil"),
"@brief Runs an antenna check on the extracted clusters taking the perimeter into account and providing an area factor\n"
"\n"
"This (most generic) version of the \\antenna_check method allows taking the perimeter of gate or metal into account and also "

View File

@ -21,6 +21,7 @@
*/
#include "gsiDecl.h"
#include "gsiEnums.h"
#include "dbNetlistCompare.h"
namespace {
@ -127,7 +128,7 @@ public:
if (cb_circuit_mismatch.can_issue ()) {
cb_circuit_mismatch.issue<GenericNetlistCompareLogger, const db::Circuit *, const db::Circuit *, const std::string &> (&GenericNetlistCompareLogger::circuit_mismatch_fb, a, b, msg);
} else {
db::NetlistCompareLogger::circuit_mismatch (a, b);
db::NetlistCompareLogger::circuit_mismatch (a, b, msg);
}
}
@ -136,6 +137,20 @@ public:
db::NetlistCompareLogger::circuit_mismatch (a, b, msg);
}
virtual void log_entry (db::NetlistCompareLogger::Severity severity, const std::string &msg)
{
if (cb_log_entry.can_issue ()) {
cb_log_entry.issue<GenericNetlistCompareLogger, db::NetlistCompareLogger::Severity, const std::string &> (&GenericNetlistCompareLogger::log_entry, severity, msg);
} else {
db::NetlistCompareLogger::log_entry (severity, msg);
}
}
void log_entry_fb (db::NetlistCompareLogger::Severity severity, const std::string &msg)
{
db::NetlistCompareLogger::log_entry (severity, msg);
}
virtual void match_nets (const db::Net *a, const db::Net *b)
{
if (cb_match_nets.can_issue ()) {
@ -297,8 +312,9 @@ public:
gsi::Callback cb_end_circuit;
gsi::Callback cb_circuit_skipped;
gsi::Callback cb_match_nets;
gsi::Callback cb_net_mismatch;
gsi::Callback cb_circuit_mismatch;
gsi::Callback cb_log_entry;
gsi::Callback cb_net_mismatch;
gsi::Callback cb_match_ambiguous_nets;
gsi::Callback cb_match_devices;
gsi::Callback cb_match_devices_with_different_parameters;
@ -368,6 +384,13 @@ Class<GenericNetlistCompareLogger> decl_GenericNetlistCompareLogger (decl_dbNetl
"\n"
"This method is called instead of \\begin_circuit and \\end_circuit."
) +
gsi::callback ("log_entry", &GenericNetlistCompareLogger::log_entry, &GenericNetlistCompareLogger::cb_log_entry, gsi::arg ("level"), gsi::arg ("msg"),
"@brief Issues an entry for the compare log.\n"
"This method delivers a log message generated during the compare of two circuits.\n"
"It is called between of \\begin_circuit and \\end_circuit.\n"
"\n"
"This method has been added in version 0.28."
) +
gsi::callback ("match_nets", &GenericNetlistCompareLogger::match_nets, &GenericNetlistCompareLogger::cb_match_nets, gsi::arg ("a"), gsi::arg ("b"),
"@brief This function is called when two nets are identified.\n"
"If two nets are identified as a corresponding pair, this method will be called with both nets.\n"
@ -471,6 +494,19 @@ Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
"The logger is a delegate or event receiver which the comparer will send compare events to. "
"See the class description for more details."
) +
gsi::method ("with_log=", &db::NetlistComparer::set_with_log, gsi::arg ("flag"),
"@brief Sets a value indicating that log messages are generated.\n"
"Log messages may be expensive to compute, hence they can be turned off.\n"
"By default, log messages are generated.\n"
"\n"
"This attribute have been introduced in version 0.28.\n"
) +
gsi::method ("with_log", &db::NetlistComparer::with_log,
"@brief Gets a value indicating that log messages are generated.\n"
"See \\with_log= for details about this flag.\n"
"\n"
"This attribute have been introduced in version 0.28.\n"
) +
gsi::method ("same_nets", (void (db::NetlistComparer::*) (const db::Net *, const db::Net *, bool)) &db::NetlistComparer::same_nets, gsi::arg ("net_a"), gsi::arg ("net_b"), gsi::arg ("must_match", false),
"@brief Marks two nets as identical.\n"
"This makes a net net_a in netlist a identical to the corresponding\n"
@ -621,4 +657,21 @@ Class<db::NetlistComparer> decl_dbNetlistComparer ("db", "NetlistComparer",
"This class has been introduced in version 0.26."
);
gsi::EnumIn<GenericNetlistCompareLogger, db::NetlistCompareLogger::Severity> decl_CompareLoggerSeverity ("db", "Severity",
gsi::enum_const ("NoSeverity", db::NetlistCompareLogger::NoSeverity,
"@brief Unspecific severity\n"
) +
gsi::enum_const ("Info", db::NetlistCompareLogger::Info,
"@brief Information only\n"
) +
gsi::enum_const ("Warning", db::NetlistCompareLogger::Warning,
"@brief A warning\n"
) +
gsi::enum_const ("Error", db::NetlistCompareLogger::Error,
"@brief An error\n"
),
"@brief This class represents the log severity level for \\GenericNetlistCompareLogger#log_entry.\n"
"This enum has been introduced in version 0.28."
);
}

View File

@ -384,7 +384,20 @@ namespace gsi
// NOTE: the contribution comes from format specific extensions.
Class<db::LoadLayoutOptions> decl_LoadLayoutOptions ("db", "LoadLayoutOptions",
gsi::Methods (),
gsi::method ("warn_level=", &db::LoadLayoutOptions::set_warn_level, gsi::arg ("level"),
"@brief Sets the warning level.\n"
"The warning level is a reader-specific setting which enables or disables warnings\n"
"on specific levels. Level 0 is always \"warnings off\". The default level is 1\n"
"which means \"reasonable warnings emitted\".\n"
"\n"
"This attribute has been added in version 0.28."
) +
gsi::method ("warn_level", &db::LoadLayoutOptions::warn_level,
"@brief Sets the warning level.\n"
"See \\warn_level= for details about this attribute.\n"
"\n"
"This attribute has been added in version 0.28."
),
"@brief Layout reader options\n"
"\n"
"This object describes various layer reader options used for loading layouts.\n"

View File

@ -473,3 +473,27 @@ TEST(4_ReaderCombinedDevices)
}
}
TEST(5_ReaderFuture)
{
db::LayoutToNetlist l2n;
std::string in_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_5.l2n");
tl::InputStream is_in (in_path);
db::LayoutToNetlistStandardReader reader (is_in);
reader.read (&l2n);
// verify against the input
std::string path = tmp_file ("tmp.txt");
{
tl::OutputStream stream (path);
db::LayoutToNetlistStandardWriter writer (stream, false);
writer.write (&l2n);
}
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "l2n_reader_au_5.l2n");
compare_text_files (path, au_path);
}

View File

@ -443,3 +443,19 @@ TEST(2_FlowWithErrors)
compare_lvsdbs (_this, path2, au_path2);
}
TEST(3_ReaderFuture)
{
db::LayoutVsSchematic lvs;
std::string in_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "lvs_test3.lvsdb");
lvs.load (in_path);
// verify against the input
std::string path = tmp_file ("tmp.txt");
lvs.save (path, false);
std::string au_path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "lvs_test3_au.lvsdb");
compare_text_files (path, au_path);
}

View File

@ -351,7 +351,7 @@ module DRC
# %DRC%
# @brief Performs an antenna check
# @name antenna_check
# @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ])
# @synopsis antenna_check(gate, metal, ratio, [ diode_specs ... ] [, texts ])
#
# The antenna check is used to avoid plasma induced damage. Physically,
# the damage happes if during the manufacturing of a metal layer with
@ -486,8 +486,13 @@ module DRC
# The error shapes produced by the antenna check are copies
# of the metal shapes on the metal layers of each network
# violating the antenna rule.
#
# You can specify a text layer (use "labels" to create one). It will receive
# error labels describing the measured values and computation parameters for debugging
# the layout. This option has been introduced in version 0.27.11.
#
def antenna_check(agate, ametal, ratio, *diodes)
def antenna_check(agate, ametal, ratio, *args)
@engine._context("antenna_check") do
@ -529,18 +534,31 @@ module DRC
raise("Ratio argument is not a number")
end
dl = diodes.collect do |d|
if d.is_a?(Array)
d.size == 2 || raise("Diode specification pair expects two elements")
d[0].requires_region
[ d[0].data, d[1].to_f ]
else
d.requires_region
[ d.data, 0.0 ]
dl = []
texts = nil
n = 3
args.each do |a|
if a.is_a?(Array)
a.size == 2 || raise("Diode specification pair expects two elements for argument #{n + 1}")
if ! a[0].is_a?(DRC::DRCLayer)
raise("Diode specification pair needs a layer for the first argument of argument #{n + 1}")
end
a[0].requires_region
dl << [ a[0].data, a[1].to_f ]
elsif ! a.is_a?(DRC::DRCLayer)
raise("Argument #{n + 1} has to be a layer")
else
a.requires_texts_or_region
if a.data.is_a?(RBA::Region)
dl << [ a.data, 0.0 ]
else
texts = a.data
end
end
n += 1
end
DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_area_factor, gate_perimeter_factor, metal.data, metal_area_factor, metal_perimeter_factor, ratio, dl))
DRC::DRCLayer::new(@engine, @engine._cmd(l2n_data, :antenna_check, gate.data, gate_area_factor, gate_perimeter_factor, metal.data, metal_area_factor, metal_perimeter_factor, ratio, dl, texts))
end

View File

@ -367,6 +367,7 @@ CODE
# %DRC%
# @name input
# @brief Specifies input from a source
# @synopsis source.input
# @synopsis source.input(layer)
# @synopsis source.input(layer, datatype)
# @synopsis source.input(layer_into)
@ -405,6 +406,8 @@ CODE
# True text layers should be preferred over mixed polygon/text layers if text object processing
# is required.
#
# "input" without any arguments will create a new, empty original layer.
#
# Use the global version of "input" without a source object to address the default source.
def input(*args)
@ -417,6 +420,7 @@ CODE
# %DRC%
# @name labels
# @brief Gets the labels (texts) from an input layer
# @synopsis source.labels
# @synopsis source.labels(layer)
# @synopsis source.labels(layer, datatype)
# @synopsis source.labels(layer_into)
@ -429,6 +433,8 @@ CODE
# to provide text support but a layer type which is provided for carrying text objects
# explicitly.
#
# "labels" without any arguments will create a new, empty original layer.
#
# Use the global version of "labels" without a source object to address the default source.
def labels(*args)
@ -441,6 +447,7 @@ CODE
# %DRC%
# @name polygons
# @brief Gets the polygon shapes (or shapes that can be converted polygons) from an input layer
# @synopsis source.polygons
# @synopsis source.polygons(layer)
# @synopsis source.polygons(layer, datatype)
# @synopsis source.polygons(layer_into)
@ -452,6 +459,8 @@ CODE
#
# This method is identical to \input with respect to the options supported.
#
# "polygons" without any arguments will create a new, empty original layer.
#
# Use the global version of "polygons" without a source object to address the default source.
def polygons(*args)
@ -464,6 +473,7 @@ CODE
# %DRC%
# @name edges
# @brief Gets the edge shapes (or shapes that can be converted edges) from an input layer
# @synopsis source.edges
# @synopsis source.edges(layer)
# @synopsis source.edges(layer, datatype)
# @synopsis source.edges(layer_into)
@ -478,6 +488,8 @@ CODE
#
# Use the global version of "edges" without a source object to address the default source.
#
# "edges" without any arguments will create a new, empty original layer.
#
# This method has been introduced in version 0.27.
def edges(*args)
@ -490,6 +502,7 @@ CODE
# %DRC%
# @name edge_pairs
# @brief Gets the edge pairs from an input layer
# @synopsis source.edge_pairs
# @synopsis source.edge_pairs(layer)
# @synopsis source.edge_pairs(layer, datatype)
# @synopsis source.edge_pairs(layer_into)
@ -504,6 +517,8 @@ CODE
#
# Use the global version of "edge_pairs" without a source object to address the default source.
#
# "edge_pairs" without any arguments will create a new, empty original layer.
#
# This method has been introduced in version 0.27.
def edge_pairs(*args)
@ -517,7 +532,8 @@ CODE
# @name make_layer
# @brief Creates an empty polygon layer based on the hierarchy of the layout
# @synopsis make_layer
# This method delivers a new empty original layer.
# This method delivers a new empty original layer. It is provided to keep old code working.
# Use "input" without arguments instead.
def make_layer
layers = []

View File

@ -1117,18 +1117,18 @@ TEST(24_enclosing)
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
static void run_test (tl::TestBase *_this, const std::string &number, bool deep)
static void run_test (tl::TestBase *_this, const std::string &number, bool deep, bool oasis = false)
{
std::string rs = tl::testdata ();
rs += "/drc/drcSimpleTests_" + number + ".drc";
std::string input = tl::testdata ();
input += "/drc/drcSimpleTests_" + number + ".gds";
input += "/drc/drcSimpleTests_" + number + "." + (oasis ? "oas" : "gds");
std::string au = tl::testdata ();
au += "/drc/drcSimpleTests_au" + number + std::string (deep ? "d" : "") + ".gds";
au += "/drc/drcSimpleTests_au" + number + std::string (deep ? "d" : "") + "." + (oasis ? "oas" : "gds");
std::string output = _this->tmp_file ("tmp.gds");
std::string output = _this->tmp_file (oasis ? "tmp.oas" : "tmp.gds");
{
// Set some variables
@ -1295,7 +1295,7 @@ TEST(49d_epAngle)
TEST(50_issue826)
{
run_test (_this, "50", false);
run_test (_this, "50", false, true /*OASIS*/);
}
TEST(51_epInternalAngle)

View File

@ -85,7 +85,7 @@ void MethodBase::parse_name (const std::string &name)
{
const char *n = name.c_str ();
if (*n == '*' && n[1]) {
if (*n == '*' && n[1] && n[1] != '*' && n[1] != '=') {
m_protected = true;
++n;
}

View File

@ -3037,6 +3037,12 @@ The effect of the operation is shown in these examples:
</tr>
</table>
</p>
<a name="texts?"/><h2>"texts?" - Returns true, if the layer is a text collection</h2>
<keyword name="texts?"/>
<p>Usage:</p>
<ul>
<li><tt>layer.texts?</tt></li>
</ul>
<a name="texts_not"/><h2>"texts_not" - Selects texts from an original layer not matching a specific selection</h2>
<keyword name="texts_not"/>
<p>Usage:</p>

View File

@ -65,7 +65,7 @@ More methods will be added in the future to support network-related features.
<keyword name="antenna_check"/>
<p>Usage:</p>
<ul>
<li><tt>antenna_check(gate, metal, ratio, [ diode_specs ... ])</tt></li>
<li><tt>antenna_check(gate, metal, ratio, [ diode_specs ... ] [, texts ])</tt></li>
</ul>
<p>
The antenna check is used to avoid plasma induced damage. Physically,
@ -201,6 +201,10 @@ errors = antenna_check(perimeter_only(gate, 0.5), ...)
The error shapes produced by the antenna check are copies
of the metal shapes on the metal layers of each network
violating the antenna rule.
</p><p>
You can specify a text layer (use "labels" to create one). It will receive
error labels describing the measured values and computation parameters for debugging
the layout. This option has been introduced in version 0.27.11.
</p>
<a name="clear_connections"/><h2>"clear_connections" - Clears all connections stored so far</h2>
<keyword name="clear_connections"/>

View File

@ -58,6 +58,7 @@ same but without clipping is <a href="#touching">touching</a> or <a href="#overl
<keyword name="edge_pairs"/>
<p>Usage:</p>
<ul>
<li><tt>source.edge_pairs</tt></li>
<li><tt>source.edge_pairs(layer)</tt></li>
<li><tt>source.edge_pairs(layer, datatype)</tt></li>
<li><tt>source.edge_pairs(layer_into)</tt></li>
@ -73,12 +74,15 @@ This method is identical to <a href="#input">input</a> with respect to the optio
</p><p>
Use the global version of "edge_pairs" without a source object to address the default source.
</p><p>
"edge_pairs" without any arguments will create a new, empty original layer.
</p><p>
This method has been introduced in version 0.27.
</p>
<a name="edges"/><h2>"edges" - Gets the edge shapes (or shapes that can be converted edges) from an input layer</h2>
<keyword name="edges"/>
<p>Usage:</p>
<ul>
<li><tt>source.edges</tt></li>
<li><tt>source.edges(layer)</tt></li>
<li><tt>source.edges(layer, datatype)</tt></li>
<li><tt>source.edges(layer_into)</tt></li>
@ -94,6 +98,8 @@ This method is identical to <a href="#input">input</a> with respect to the optio
</p><p>
Use the global version of "edges" without a source object to address the default source.
</p><p>
"edges" without any arguments will create a new, empty original layer.
</p><p>
This method has been introduced in version 0.27.
</p>
<a name="extent"/><h2>"extent" - Returns a layer with the bounding box of the selected layout or cells</h2>
@ -160,6 +166,7 @@ source.global_transform(shift(0, 100.um), rotate(90.0))
<keyword name="input"/>
<p>Usage:</p>
<ul>
<li><tt>source.input</tt></li>
<li><tt>source.input(layer)</tt></li>
<li><tt>source.input(layer, datatype)</tt></li>
<li><tt>source.input(layer_into)</tt></li>
@ -200,12 +207,15 @@ operations is available for these objects, such as boolean "and" and "not" with
True text layers should be preferred over mixed polygon/text layers if text object processing
is required.
</p><p>
"input" without any arguments will create a new, empty original layer.
</p><p>
Use the global version of "input" without a source object to address the default source.
</p>
<a name="labels"/><h2>"labels" - Gets the labels (texts) from an input layer</h2>
<keyword name="labels"/>
<p>Usage:</p>
<ul>
<li><tt>source.labels</tt></li>
<li><tt>source.labels(layer)</tt></li>
<li><tt>source.labels(layer, datatype)</tt></li>
<li><tt>source.labels(layer_into)</tt></li>
@ -219,6 +229,8 @@ layer. Starting with version 0.27, the result is no longer a polygon layer that
to provide text support but a layer type which is provided for carrying text objects
explicitly.
</p><p>
"labels" without any arguments will create a new, empty original layer.
</p><p>
Use the global version of "labels" without a source object to address the default source.
</p>
<a name="layers"/><h2>"layers" - Gets the layers the source contains</h2>
@ -258,7 +270,8 @@ layers.each { |l| (input(l) &amp; clip_box).output(l) }
<li><tt>make_layer</tt></li>
</ul>
<p>
This method delivers a new empty original layer.
This method delivers a new empty original layer. It is provided to keep old code working.
Use "input" without arguments instead.
</p>
<a name="overlapping"/><h2>"overlapping" - Specifies input selected from a region in overlapping mode</h2>
<keyword name="overlapping"/>
@ -286,6 +299,7 @@ the search region with their bounding box (without the requirement to overlap)
<keyword name="polygons"/>
<p>Usage:</p>
<ul>
<li><tt>source.polygons</tt></li>
<li><tt>source.polygons(layer)</tt></li>
<li><tt>source.polygons(layer, datatype)</tt></li>
<li><tt>source.polygons(layer_into)</tt></li>
@ -298,6 +312,8 @@ Those are boxes, paths and real polygons.
</p><p>
This method is identical to <a href="#input">input</a> with respect to the options supported.
</p><p>
"polygons" without any arguments will create a new, empty original layer.
</p><p>
Use the global version of "polygons" without a source object to address the default source.
</p>
<a name="select"/><h2>"select" - Adds cell name expressions to the cell filters</h2>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Some files were not shown because too many files have changed in this diff Show More