Tried to implement MINWIDTH .. WRONGDIRECTION in LEF58_MINWIDTH property

This commit is contained in:
Matthias Koefferlein 2020-04-04 00:40:01 +02:00
parent bcaf28c94c
commit 4a24b727d5
10 changed files with 623 additions and 367 deletions

View File

@ -136,10 +136,10 @@ struct Group
std::vector<tl::GlobPattern> comp_match;
};
db::Coord
std::pair<db::Coord, db::Coord>
DEFImporter::get_wire_width_for_rule (const std::string &rulename, const std::string &ln, double dbu)
{
double w = db::coord_traits<db::Coord>::rounded (m_lef_importer.layer_width (ln, rulename, 0.0) / dbu);
db::Coord w = db::coord_traits<db::Coord>::rounded (m_lef_importer.layer_width (ln, rulename, 0.0) / dbu);
// try to find local nondefault rule
if (! rulename.empty ()) {
@ -152,7 +152,34 @@ DEFImporter::get_wire_width_for_rule (const std::string &rulename, const std::st
}
}
return w;
std::pair<double, double> min_wxy = m_lef_importer.min_layer_width (ln);
db::Coord min_wx = db::coord_traits<db::Coord>::rounded (min_wxy.first / dbu);
db::Coord min_wy = db::coord_traits<db::Coord>::rounded (min_wxy.second / dbu);
return std::make_pair (std::max (w, min_wx), std::max (w, min_wy));
}
std::pair<db::Coord, db::Coord>
DEFImporter::get_def_ext (const std::string &ln, const std::pair<db::Coord, db::Coord> &wxy, double dbu)
{
if (wxy.first == wxy.second) {
db::Coord de = db::coord_traits<db::Coord>::rounded (m_lef_importer.layer_ext (ln, wxy.first * 0.5 * dbu) / dbu);
return std::make_pair (de, de);
} else {
#if 0
// This implementation picks the default extension according to the real width
// NOTE: the swapping of x and y for the default extension is intended. For horizontal lines, the
// extension is in x direction but corresponds to a wire width of a vertical wire and vice versa.
db::Coord dex = db::coord_traits<db::Coord>::rounded (m_lef_importer.layer_ext (ln, wxy.second * 0.5 * dbu) / dbu);
db::Coord dey = db::coord_traits<db::Coord>::rounded (m_lef_importer.layer_ext (ln, wxy.first * 0.5 * dbu) / dbu);
return std::make_pair (dex, dey);
#else
// This implementation picks the default extension according to the specified wire width (which is the minimum
// of wx and wy)
db::Coord de = db::coord_traits<db::Coord>::rounded (m_lef_importer.layer_ext (ln, std::min (wxy.first, wxy.second) * 0.5 * dbu) / dbu);
return std::make_pair (de, de);
#endif
}
}
void
@ -501,9 +528,10 @@ DEFImporter::do_read (db::Layout &layout)
taperrule.clear ();
const std::string *rulename = 0;
db::Coord w = 0;
std::pair<db::Coord, db::Coord> w (0, 0);
if (specialnets) {
w = db::coord_traits<db::Coord>::rounded (get_double () * scale);
db::Coord n = db::coord_traits<db::Coord>::rounded (get_double () * scale);
w = std::make_pair (n, n);
}
const db::Polygon *style = 0;
@ -544,11 +572,11 @@ DEFImporter::do_read (db::Layout &layout)
rulename = &nondefaultrule;
}
db::Coord def_ext = 0;
std::pair<db::Coord, db::Coord> def_ext (0, 0);
if (! specialnets) {
w = get_wire_width_for_rule (*rulename, ln, layout.dbu ());
def_ext = db::coord_traits<db::Coord>::rounded (m_lef_importer.layer_ext (ln, w * 0.5 * layout.dbu ()) / layout.dbu ());
def_ext = get_def_ext (ln, w, layout.dbu ());
}
std::map<int, db::Polygon>::const_iterator s = styles.find (sn);
@ -556,7 +584,7 @@ DEFImporter::do_read (db::Layout &layout)
style = &s->second;
}
std::vector<db::Coord> ext;
std::vector<std::pair<db::Coord, db::Coord> > ext;
std::vector<db::Point> pts;
double x = 0.0, y = 0.0;
@ -629,11 +657,12 @@ DEFImporter::do_read (db::Layout &layout)
y = get_double ();
}
pts.push_back (db::Point (db::DPoint (x * scale, y * scale)));
db::Coord e = def_ext;
std::pair<db::Coord, db::Coord> ee = def_ext;
if (! peek (")")) {
e = db::coord_traits<db::Coord>::rounded (get_double () * scale);
db::Coord e = db::coord_traits<db::Coord>::rounded (get_double () * scale);
ee.first = ee.second = e;
}
ext.push_back (e);
ext.push_back (ee);
test (")");
@ -648,8 +677,11 @@ DEFImporter::do_read (db::Layout &layout)
// Use the default style (octagon "pen" for non-manhattan segments, paths for
// horizontal/vertical segments).
// Manhattan paths are stitched together from two-point paths if they
db::Coord e = std::max (ext.front (), ext.back ());
std::pair<db::Coord, db::Coord> e = std::max (ext.front (), ext.back ());
bool is_isotropic = (e.first == e.second && w.first == w.second);
bool was_path = false;
std::vector<db::Point>::const_iterator pt = pts.begin ();
while (pt != pts.end ()) {
@ -657,11 +689,27 @@ DEFImporter::do_read (db::Layout &layout)
std::vector<db::Point>::const_iterator pt0 = pt;
do {
++pt;
} while (pt != pts.end () && (pt[-1].x () == pt[0].x () || pt[-1].y () == pt[0].y()));
} while (pt != pts.end () && is_isotropic && (pt[-1].x () == pt[0].x () || pt[-1].y () == pt[0].y()));
if (pt - pt0 > 1) {
if (pt - pt0 > 1 || pt0->x () == pt0[1].x () || pt0->y () == pt0[0].y()) {
db::Path p (pt0, pt, w, pt0 == pts.begin () ? e : 0, pt == pts.end () ? e : 0, false);
if (pt - pt0 == 1) {
++pt;
}
db::Coord wxy, wxy_perp, exy;
if (pt0->x () == pt0 [1].x ()) {
wxy = w.second;
wxy_perp = w.first;
exy = e.second;
} else {
wxy = w.first;
wxy_perp = w.second;
exy = e.first;
}
db::Path p (pt0, pt, wxy, pt0 == pts.begin () ? exy : (was_path ? wxy_perp / 2 : 0), pt == pts.end () ? exy : 0, false);
if (prop_id != 0) {
design.shapes (dl.second).insert (db::object_with_properties<db::Path> (p, prop_id));
} else {
@ -674,10 +722,16 @@ DEFImporter::do_read (db::Layout &layout)
--pt;
} else if (pt != pts.end ()) {
was_path = true;
db::Coord s = (w + 1) / 2;
db::Coord t = db::Coord (ceil (w * (M_SQRT2 - 1) / 2));
} else {
if (! is_isotropic) {
warn("Anisotropic wire widths not supported for diagonal wires");
}
db::Coord s = (w.first + 1) / 2;
db::Coord t = db::Coord (ceil (w.first * (M_SQRT2 - 1) / 2));
db::Point octagon[8] = {
db::Point (-s, t),
@ -700,6 +754,8 @@ DEFImporter::do_read (db::Layout &layout)
design.shapes (dl.second).insert (p);
}
was_path = false;
}
}
@ -766,7 +822,7 @@ DEFImporter::do_read (db::Layout &layout)
if (! specialnets) {
w = get_wire_width_for_rule (*rulename, ln, layout.dbu ());
def_ext = db::coord_traits<db::Coord>::rounded (m_lef_importer.layer_ext (ln, w * 0.5 * layout.dbu ()) / layout.dbu ());
def_ext = get_def_ext (ln, w, layout.dbu ());
}
// continue a segment with the current point and the new layer

View File

@ -66,7 +66,8 @@ private:
db::FTrans get_orient (bool optional);
void read_polygon (db::Polygon &poly, double scale);
void read_rect (db::Polygon &poly, double scale);
db::Coord get_wire_width_for_rule(const std::string &rule, const std::string &ln, double dbu);
std::pair<Coord, Coord> get_wire_width_for_rule(const std::string &rule, const std::string &ln, double dbu);
std::pair<db::Coord, db::Coord> get_def_ext (const std::string &ln, const std::pair<db::Coord, db::Coord> &wxy, double dbu);
};
}

View File

@ -531,6 +531,22 @@ LEFDEFImporter::expect (const std::string &token)
}
}
void
LEFDEFImporter::expect (const std::string &token1, const std::string &token2)
{
if (! test (token1) && ! test (token2)) {
error ("Expected token: " + token1 + " or " + token2);
}
}
void
LEFDEFImporter::expect (const std::string &token1, const std::string &token2, const std::string &token3)
{
if (! test (token1) && ! test (token2) && ! test (token3)) {
error ("Expected token: " + token1 + ", " + token2 + " or " + token3);
}
}
double
LEFDEFImporter::get_double ()
{

View File

@ -642,6 +642,16 @@ protected:
*/
void expect (const std::string &token);
/**
* @brief Test whether the next token matches one of the given ones and raise an error if it does not
*/
void expect (const std::string &token1, const std::string &token2);
/**
* @brief Test whether the next token matches one of the given ones and raise an error if it does not
*/
void expect (const std::string &token, const std::string &token2, const std::string &token3);
/**
* @brief Gets the next token
*/

View File

@ -65,6 +65,17 @@ LEFImporter::layer_ext (const std::string &layer, double def_ext) const
}
}
std::pair<double, double>
LEFImporter::min_layer_width (const std::string &layer) const
{
std::map<std::string, std::pair<double, double> >::const_iterator l = m_min_widths.find (layer);
if (l != m_min_widths.end ()) {
return l->second;
} else {
return std::make_pair (0.0, 0.0);
}
}
double
LEFImporter::layer_width (const std::string &layer, const std::string &nondefaultrule, double def_width) const
{
@ -361,59 +372,8 @@ LEFImporter::read_geometries (db::Layout &layout, db::Cell &cell, LayerPurpose p
}
void
LEFImporter::do_read (db::Layout &layout)
LEFImporter::read_nondefaultrule (db::Layout & /*layout*/)
{
// TODO: what to do with that value?
// double dbu_mic = 1000;
while (! at_end ()) {
if (test ("END")) {
// END LIBRARY terminates the file
expect ("LIBRARY");
break;
} else if (test ("VERSION")) {
// ignore VERSION statement currently
take ();
expect (";");
} else if (test ("UNITS")) {
// read over SPACING sections
while (! test ("END")) {
if (test ("DATABASE")) {
expect ("MICRONS");
// TODO: what to do with that value
/* dbu_mic = */ get_double ();
expect (";");
} else {
while (! test (";")) {
take ();
}
}
}
expect ("UNITS");
} else if (test ("SPACING")) {
// read over SPACING sections
while (! test ("END") || ! test ("SPACING")) {
take ();
}
} else if (test ("PROPERTYDEFINITIONS")) {
// read over PROPERTYDEFINITIONS sections
while (! test ("END") || ! test ("PROPERTYDEFINITIONS")) {
take ();
}
} else if (test ("NONDEFAULTRULE")) {
// read NONDEFAULTRULE sections
std::string n = get ();
@ -453,40 +413,11 @@ LEFImporter::do_read (db::Layout &layout)
}
}
} else if (test ("SITE")) {
// read over SITE sections
std::string n = get ();
while (! test ("END") || ! test (n)) {
take ();
}
} else if (test ("VIARULE")) {
// read over VIARULE sections
std::string n = get ();
while (! test ("END") || ! test (n)) {
take ();
}
} else if (test ("VIA")) {
std::string n = get ();
// produce a cell for vias
std::string cellname = "VIA_" + n;
db::Cell &cell = layout.cell (layout.add_cell (cellname.c_str ()));
ViaDesc &via_desc = m_vias[n];
via_desc.cell = &cell;
while (test ("DEFAULT") || test ("TOPOFSTACKONLY"))
;
test (";");
if (test ("VIARULE")) {
void
LEFImporter::read_viadef_by_rule (Layout &layout, db::Cell &cell, ViaDesc &via_desc, const std::string & /*n*/)
{
db::Vector cutsize, cutspacing;
db::Vector be, te;
db::Vector bo, to;
@ -593,9 +524,11 @@ LEFImporter::do_read (db::Layout &layout)
}
}
}
}
} else {
void
LEFImporter::read_viadef_by_geometry (Layout &layout, db::Cell &cell, ViaDesc &via_desc, const std::string &n)
{
// ignore resistance spec
if (test ("RESISTANCE")) {
get_double ();
@ -624,31 +557,53 @@ LEFImporter::do_read (db::Layout &layout)
reset_cellname ();
expect ("END");
}
void
LEFImporter::read_viadef (Layout &layout)
{
std::string n = get ();
// produce a cell for vias
std::string cellname = "VIA_" + n;
db::Cell &cell = layout.cell (layout.add_cell (cellname.c_str ()));
ViaDesc &via_desc = m_vias[n];
via_desc.cell = &cell;
while (test ("DEFAULT") || test ("TOPOFSTACKONLY"))
;
test (";");
if (test ("VIARULE")) {
read_viadef_by_rule (layout, cell, via_desc, n);
} else {
read_viadef_by_geometry (layout, cell, via_desc, n);
}
test ("VIA");
expect (n);
} else if (test ("BEGINEXT")) {
// read over BEGINEXT sections
while (! test ("ENDEXT")) {
take ();
}
} else if (test ("LAYER")) {
void
LEFImporter::read_layer (Layout & /*layout*/)
{
std::string ln = get ();
double wmin = 0.0, wmin_wrongdir = 0.0;
bool is_horizontal = true;
register_layer (ln);
// just extract the width from the layer - we need that as the default width for paths
while (! at_end ()) {
if (test ("END")) {
expect (ln);
break;
} else if (test ("TYPE")) {
if (test ("ROUTING")) {
m_routing_layers.insert (ln);
} else if (test ("CUT")) {
@ -657,15 +612,29 @@ LEFImporter::do_read (db::Layout &layout)
get ();
}
expect (";");
} else if (test ("WIDTH")) {
double w = get_double ();
m_default_widths.insert (std::make_pair (ln, w));
expect (";");
} else if (test ("DIRECTION")) {
if (test ("HORIZONTAL")) {
is_horizontal = true;
} else {
expect ("VERTICAL", "DIAG45", "DIAG135");
}
} else if (test ("WIREEXTENSION")) {
double w = get_double ();
m_default_ext.insert (std::make_pair (ln, w));
expect (";");
} else if (test ("ACCURRENTDENSITY")) {
// ACCURRENTDENSITY needs some special attention because it can contain nested WIDTH
// blocks following a semicolon
take ();
@ -677,15 +646,50 @@ LEFImporter::do_read (db::Layout &layout)
while (! test (";")) {
take ();
}
} else if (test ("PROPERTY")) {
std::string name = get ();
tl::Variant value = get ();
if (name == "LEF58_MINWIDTH") {
// Cadence extension
tl::Extractor ex (value.to_string ());
double mw = 0.0;
if (ex.test ("MINWIDTH") && ex.try_read (mw)) {
if (ex.test ("WRONGDIRECTION")) {
wmin_wrongdir = mw;
} else {
wmin = mw;
}
}
}
} else {
while (! test (";")) {
take ();
}
}
}
} else if (test ("MACRO")) {
if (wmin > 0.0 || wmin_wrongdir > 0.0) {
if (! is_horizontal) {
std::swap (wmin, wmin_wrongdir);
}
m_min_widths.insert (std::make_pair (ln, std::make_pair (wmin, wmin_wrongdir)));
}
}
void
LEFImporter::read_macro (Layout &layout)
{
std::string mn = get ();
set_cellname (mn);
@ -790,6 +794,98 @@ LEFImporter::do_read (db::Layout &layout)
m_macro_bboxes_by_name.insert (std::make_pair (mn, db::Box (-origin, -origin + size)));
reset_cellname ();
}
void
LEFImporter::do_read (db::Layout &layout)
{
// TODO: what to do with that value?
// double dbu_mic = 1000;
while (! at_end ()) {
if (test ("END")) {
// END LIBRARY terminates the file
expect ("LIBRARY");
break;
} else if (test ("VERSION")) {
// ignore VERSION statement currently
take ();
expect (";");
} else if (test ("UNITS")) {
// read over SPACING sections
while (! test ("END")) {
if (test ("DATABASE")) {
expect ("MICRONS");
// TODO: what to do with that value
/* dbu_mic = */ get_double ();
expect (";");
} else {
while (! test (";")) {
take ();
}
}
}
expect ("UNITS");
} else if (test ("SPACING")) {
// read over SPACING sections
while (! test ("END") || ! test ("SPACING")) {
take ();
}
} else if (test ("PROPERTYDEFINITIONS")) {
// read over PROPERTYDEFINITIONS sections
while (! test ("END") || ! test ("PROPERTYDEFINITIONS")) {
take ();
}
} else if (test ("NONDEFAULTRULE")) {
read_nondefaultrule (layout);
} else if (test ("SITE")) {
// read over SITE sections
std::string n = get ();
while (! test ("END") || ! test (n)) {
take ();
}
} else if (test ("VIARULE")) {
// read over VIARULE sections
std::string n = get ();
while (! test ("END") || ! test (n)) {
take ();
}
} else if (test ("VIA")) {
read_viadef (layout);
} else if (test ("BEGINEXT")) {
// read over BEGINEXT sections
while (! test ("ENDEXT")) {
take ();
}
} else if (test ("LAYER")) {
read_layer (layout);
} else if (test ("MACRO")) {
read_macro (layout);
} else {
while (! test (";")) {

View File

@ -86,6 +86,11 @@ public:
*/
double layer_ext (const std::string &layer, double def_ext = 0.0) const;
/**
* @brief Gets the minimum wire width in x and y direction for the given layer name
*/
std::pair<double, double> min_layer_width (const std::string &layer) const;
/**
* @brief Returns true if the given layer is a routing layer
*/
@ -119,6 +124,7 @@ private:
std::map<std::string, std::map<std::string, double> > m_nondefault_widths;
std::map<std::string, double> m_default_widths;
std::map<std::string, double> m_default_ext;
std::map<std::string, std::pair<double, double> > m_min_widths;
std::map<std::string, db::Cell *> m_macros_by_name;
std::map<std::string, db::Box> m_macro_bboxes_by_name;
std::map<std::string, ViaDesc> m_vias;
@ -126,6 +132,12 @@ private:
std::vector <db::Trans> get_iteration (db::Layout &layout);
void read_geometries (db::Layout &layout, db::Cell &cell, LayerPurpose purpose, std::map<std::string, db::Box> *collect_bboxes = 0, properties_id_type prop_id = 0);
void read_nondefaultrule (Layout &layout);
void read_viadef (Layout &layout);
void read_viadef_by_rule (Layout &layout, db::Cell &cell, ViaDesc &desc, const std::string &n);
void read_viadef_by_geometry (Layout &layout, db::Cell &cell, ViaDesc &desc, const std::string &n);
void read_layer (Layout &layout);
void read_macro (Layout &layout);
};
}

View File

@ -270,12 +270,17 @@ TEST(103)
run_test (_this, "issue-517", "def:in.def", "au.oas.gz", default_options (), false);
}
TEST(104)
TEST(104_doxy_vias)
{
run_test (_this, "doxy_vias", "def:test.def", "au.oas.gz", default_options (), false);
}
TEST(105)
TEST(105_specialnets_geo)
{
run_test (_this, "specialnets_geo", "lef:test.lef+def:test.def", "au.oas.gz", default_options (), false);
}
TEST(106_wrongdirection)
{
run_test (_this, "wrongdirection", "lef:test.lef+def:test.def", "au.oas.gz", default_options (), false);
}

BIN
testdata/lefdef/wrongdirection/au.oas.gz vendored Normal file

Binary file not shown.

19
testdata/lefdef/wrongdirection/test.def vendored Normal file
View File

@ -0,0 +1,19 @@
VERSION 5.8 ;
DIVIDERCHAR "/" ;
BUSBITCHARS "[]" ;
DESIGN test ;
UNITS DISTANCE MICRONS 1000 ;
DIEAREA ( 0 0 ) ( 10000 10000 ) ;
NONDEFAULTRULES 1 ;
- ndr
+ HARDSPACING
+ LAYER M3 WIDTH 500 SPACING 500
;
END NONDEFAULTRULES
NETS 1 ;
- net_b
+ NONDEFAULTRULE ndr
+ ROUTED M3 ( 0 0 ) ( 2000 0 ) via34 ( * 2000 ) ( 4000 * ) ( * 4000 )
;
END NETS
END DESIGN

41
testdata/lefdef/wrongdirection/test.lef vendored Normal file
View File

@ -0,0 +1,41 @@
VERSION 5.8 ;
BUSBITCHARS "[]" ;
DIVIDERCHAR "/" ;
UNITS
DATABASE MICRONS 1000 ;
END UNITS
MANUFACTURINGGRID 0.001 ;
PROPERTYDEFINITIONS
LAYER LEF58_MINWIDTH STRING ;
END PROPERTYDEFINITIONS
LAYER M3
TYPE ROUTING ;
DIRECTION VERTICAL ;
WIDTH 0.6 ;
END M3
LAYER VIA3
TYPE CUT ;
END VIA3
LAYER M4
TYPE ROUTING ;
DIRECTION HORIZONTAL ;
WIDTH 0.5 ;
PROPERTY LEF58_MINWIDTH "MINWIDTH 1.0 WRONGDIRECTION ; " ;
END M4
VIA via34
LAYER M3 ;
RECT -0.4 -0.3 0.4 0.3 ;
LAYER VIA3 ;
RECT -0.3 -0.25 0.3 0.25 ;
LAYER M4 ;
RECT -1.00 -0.5 1.00 0.5 ;
END via34
END LIBRARY