Merge pull request #1139 from KLayout/issue-1138
Issue 1138 (switch to suppress warnings on DXF reader - and other readers)
10
README.md
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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> ();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ()));
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 ();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"/>
|
||||
|
|
|
|||
|
|
@ -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) & 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>
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.1 KiB |