mirror of https://github.com/KLayout/klayout.git
Attempt to auto-import .map files for LEF/DEF reader. Probably needs adjustment.
This commit is contained in:
parent
df11ff9b85
commit
44503facea
|
|
@ -147,18 +147,25 @@ LEFDEFLayerDelegate::open_layer (db::Layout &layout, const std::string &n, Layer
|
|||
{
|
||||
if (purpose == Outline || purpose == PlacementBlockage || purpose == Region) {
|
||||
|
||||
// NOTE: the canonical name is independent from the tech component's settings
|
||||
// as is "(name)". It's used for implementing the automatic map file import
|
||||
// feature.
|
||||
std::string ld;
|
||||
std::string canonical_name;
|
||||
bool produce;
|
||||
|
||||
if (purpose == Outline) {
|
||||
produce = mp_tech_comp->produce_cell_outlines ();
|
||||
ld = mp_tech_comp->cell_outline_layer ();
|
||||
canonical_name = "(OUTLINE)";
|
||||
} else if (purpose == Region) {
|
||||
produce = mp_tech_comp->produce_regions ();
|
||||
ld = mp_tech_comp->region_layer ();
|
||||
canonical_name = "(REGION)";
|
||||
} else {
|
||||
produce = mp_tech_comp->produce_placement_blockages ();
|
||||
ld = mp_tech_comp->placement_blockage_layer ();
|
||||
canonical_name = "(BLK)";
|
||||
}
|
||||
|
||||
if (! produce) {
|
||||
|
|
@ -178,10 +185,9 @@ LEFDEFLayerDelegate::open_layer (db::Layout &layout, const std::string &n, Layer
|
|||
|
||||
if (ll.first) {
|
||||
|
||||
// create the layer if it is not part of the layout yet.
|
||||
if (! layout.is_valid_layer (ll.second)) {
|
||||
layout.insert_layer (ll.second, m_layer_map.mapping (ll.second));
|
||||
}
|
||||
return ll;
|
||||
|
||||
} else if ((ll = m_layer_map.logical (db::LayerProperties (canonical_name), layout)).first) {
|
||||
|
||||
return ll;
|
||||
|
||||
|
|
@ -199,67 +205,99 @@ LEFDEFLayerDelegate::open_layer (db::Layout &layout, const std::string &n, Layer
|
|||
|
||||
} else {
|
||||
|
||||
if (mp_tech_comp) {
|
||||
bool produce = true;
|
||||
switch (purpose) {
|
||||
case Routing:
|
||||
default:
|
||||
produce = mp_tech_comp->produce_routing ();
|
||||
break;
|
||||
case ViaGeometry:
|
||||
produce = mp_tech_comp->produce_via_geometry ();
|
||||
break;
|
||||
case Label:
|
||||
produce = mp_tech_comp->produce_labels ();
|
||||
break;
|
||||
case Pins:
|
||||
produce = mp_tech_comp->produce_pins ();
|
||||
break;
|
||||
case Obstructions:
|
||||
produce = mp_tech_comp->produce_obstructions ();
|
||||
break;
|
||||
case Blockage:
|
||||
produce = mp_tech_comp->produce_blockages ();
|
||||
break;
|
||||
}
|
||||
if (! produce) {
|
||||
return std::make_pair (false, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: "name" is the decorated name as provided by the tech component's
|
||||
// x_suffix specifications. As this is a variable entity, we also provide
|
||||
// a canonical name of the form "(layer,purpose)" where purpose is a
|
||||
// predefined suffix. The canonical name is the last fallback. Hence this
|
||||
// allows importing layer mapping files as canonical name mapping.
|
||||
std::string name (n);
|
||||
bool produce = true;
|
||||
int dt = 0;
|
||||
|
||||
std::string canonical_purpose;
|
||||
|
||||
if (mp_tech_comp) {
|
||||
switch (purpose) {
|
||||
case Routing:
|
||||
default:
|
||||
produce = mp_tech_comp->produce_routing ();
|
||||
name += mp_tech_comp->routing_suffix ();
|
||||
canonical_purpose = "NET";
|
||||
dt += mp_tech_comp->routing_datatype ();
|
||||
break;
|
||||
case ViaGeometry:
|
||||
produce = mp_tech_comp->produce_via_geometry ();
|
||||
name += mp_tech_comp->via_geometry_suffix ();
|
||||
dt += mp_tech_comp->via_geometry_datatype ();
|
||||
canonical_purpose = "VIA";
|
||||
break;
|
||||
case Label:
|
||||
produce = mp_tech_comp->produce_labels ();
|
||||
name += mp_tech_comp->labels_suffix ();
|
||||
dt += mp_tech_comp->labels_datatype ();
|
||||
canonical_purpose = "LABEL";
|
||||
break;
|
||||
case Pins:
|
||||
produce = mp_tech_comp->produce_pins ();
|
||||
name += mp_tech_comp->pins_suffix ();
|
||||
dt += mp_tech_comp->pins_datatype ();
|
||||
canonical_purpose = "PIN";
|
||||
break;
|
||||
case Obstructions:
|
||||
produce = mp_tech_comp->produce_obstructions ();
|
||||
name += mp_tech_comp->obstructions_suffix ();
|
||||
dt += mp_tech_comp->obstructions_datatype ();
|
||||
canonical_purpose = "OBS";
|
||||
break;
|
||||
case Blockage:
|
||||
produce = mp_tech_comp->produce_blockages ();
|
||||
name += mp_tech_comp->blockages_suffix ();
|
||||
dt += mp_tech_comp->blockages_datatype ();
|
||||
canonical_purpose = "BLK";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! produce) {
|
||||
return std::make_pair (false, 0);
|
||||
}
|
||||
std::string canonical_name = std::string ("(") + n + "," + canonical_purpose + ")";
|
||||
|
||||
std::pair<bool, unsigned int> ll = m_layer_map.logical (name, layout);
|
||||
|
||||
if (ll.first) {
|
||||
|
||||
// create the layer if it is not part of the layout yet.
|
||||
if (! layout.is_valid_layer (ll.second)) {
|
||||
layout.insert_layer (ll.second, m_layer_map.mapping (ll.second));
|
||||
}
|
||||
return ll;
|
||||
|
||||
} else if ((ll = m_layer_map.logical (db::LayerProperties (canonical_name), layout)).first) {
|
||||
|
||||
// final fallback: try canonical name
|
||||
return ll;
|
||||
|
||||
} else {
|
||||
|
||||
std::pair<bool, unsigned int> ll_raw = m_layer_map.logical (n, layout);
|
||||
ll = m_layer_map.logical (n, layout);
|
||||
int ln = -1;
|
||||
|
||||
if (ll_raw.first && (ln = layout.get_properties (ll_raw.second).layer) >= 0) {
|
||||
if (ll.first && (ln = layout.get_properties (ll.second).layer) >= 0) {
|
||||
|
||||
m_layer_map.map (db::LayerProperties (name), layout.layers (), db::LayerProperties (ln, dt, name));
|
||||
m_layer_map.prepare (layout);
|
||||
|
|
|
|||
|
|
@ -518,6 +518,14 @@ public:
|
|||
return m_layer_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the layer map (non-const version)
|
||||
*/
|
||||
db::LayerMap &layer_map ()
|
||||
{
|
||||
return m_layer_map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a new layer or return the index of the given layer
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -77,6 +77,124 @@ static bool is_def_format (const std::string &fn)
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads a map file
|
||||
*
|
||||
* NOTE: this is rather experimental ... no idea what is the specification of
|
||||
* the map file.
|
||||
*/
|
||||
static void
|
||||
read_map_file (const std::string &path, db::LEFDEFLayerDelegate &layers)
|
||||
{
|
||||
tl::log << tl::to_string (tr ("Reading LEF/DEF map file")) << " " << path;
|
||||
|
||||
db::LayerMap &lm = layers.layer_map ();
|
||||
unsigned int n = lm.next_index ();
|
||||
|
||||
tl::InputFile file (path);
|
||||
tl::InputStream file_stream (file);
|
||||
tl::TextInputStream ts (file_stream);
|
||||
|
||||
std::map<std::string, std::string> purpose_translation;
|
||||
purpose_translation ["LEFPIN"] = "PIN";
|
||||
purpose_translation ["LEFOBS"] = "OBS";
|
||||
purpose_translation ["SPNET"] = "NET";
|
||||
purpose_translation ["NET"] = "NET";
|
||||
purpose_translation ["VIA"] = "VIA";
|
||||
|
||||
while (! ts.at_end ()) {
|
||||
|
||||
const std::string &l = ts.get_line ();
|
||||
|
||||
tl::Extractor ex (l.c_str ());
|
||||
if (ex.at_end () || ex.test ("#")) {
|
||||
|
||||
// ignore empty of comment lines
|
||||
|
||||
} else {
|
||||
|
||||
std::string w1, w2;
|
||||
int layer = 0, datatype = 0;
|
||||
|
||||
if (ex.try_read_word (w1) && ex.try_read_word (w2, "._$,/:") && ex.try_read (layer) && ex.try_read (datatype)) {
|
||||
|
||||
if (w1 == "DIEAREA") {
|
||||
|
||||
std::string canonical_name = "(OUTLINE)";
|
||||
lm.map (db::LayerProperties (canonical_name), n++, db::LayerProperties (layer, datatype));
|
||||
|
||||
} else if (w1 == "NAME") {
|
||||
|
||||
std::vector<std::string> purposes = tl::split (w2, ",");
|
||||
for (std::vector<std::string>::const_iterator p = purposes.begin (); p != purposes.end (); ++p) {
|
||||
std::string canonical_name = std::string ("(") + tl::split (*p, "/").front () + ",LABEL)";
|
||||
lm.map (db::LayerProperties (canonical_name), n++, db::LayerProperties (layer, datatype));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
std::vector<std::string> purposes = tl::split (w2, ",");
|
||||
for (std::vector<std::string>::const_iterator p = purposes.begin (); p != purposes.end (); ++p) {
|
||||
std::map<std::string, std::string>::const_iterator i = purpose_translation.find (*p);
|
||||
if (i != purpose_translation.end ()) {
|
||||
std::string canonical_name = std::string ("(") + w1 + "," + i->second + ")";
|
||||
lm.map (db::LayerProperties (canonical_name), n++, db::LayerProperties (layer, datatype));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Imports a .map file present next to the input files
|
||||
*/
|
||||
static void
|
||||
import_map_file_heuristics (const std::string &main_path, db::LEFDEFLayerDelegate &layers)
|
||||
{
|
||||
std::string input_dir = tl::absolute_path (main_path);
|
||||
if (! tl::file_exists (input_dir)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string bn = tl::basename (tl::filename (main_path));
|
||||
std::vector<std::string> map_files;
|
||||
std::string map_file_exact;
|
||||
|
||||
std::vector<std::string> entries = tl::dir_entries (input_dir);
|
||||
for (std::vector<std::string>::const_iterator e = entries.begin (); e != entries.end (); ++e) {
|
||||
|
||||
if (tl::to_lower_case (tl::extension (*e)) == "map") {
|
||||
|
||||
if (tl::basename (*e) == bn) {
|
||||
map_file_exact = *e;
|
||||
} else {
|
||||
map_files.push_back (*e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
if (! map_file_exact.empty ()) {
|
||||
read_map_file (tl::combine_path (input_dir, map_file_exact), layers);
|
||||
tl::log << layers.layer_map ().to_string_file_format (); // @@@
|
||||
} else if (map_files.size () == 1) {
|
||||
read_map_file (tl::combine_path (input_dir, map_files.front ()), layers);
|
||||
tl::log << layers.layer_map ().to_string_file_format (); // @@@
|
||||
}
|
||||
} catch (tl::Exception &ex) {
|
||||
// ignore read errors on map file (this is a heuristics!)
|
||||
tl::error << ex.msg ();
|
||||
}
|
||||
}
|
||||
|
||||
class LEFDEFReader
|
||||
: public db::ReaderBase
|
||||
{
|
||||
|
|
@ -125,6 +243,9 @@ private:
|
|||
|
||||
// Take the layer map and the "read all layers" flag from the reader options - hence we override the
|
||||
db::LEFDEFLayerDelegate layers (lefdef_options);
|
||||
|
||||
import_map_file_heuristics (m_stream.absolute_path (), layers);
|
||||
|
||||
layers.prepare (layout);
|
||||
layout.dbu (lefdef_options->dbu ());
|
||||
|
||||
|
|
@ -163,9 +284,11 @@ private:
|
|||
|
||||
}
|
||||
|
||||
// Additionally read all LEF files next to the DEF file
|
||||
// Additionally read all LEF files next to the DEF file and if there is a single .map file
|
||||
// or one with the same name than the input file with ".map" suffix, try to read this one too.
|
||||
|
||||
std::string input_dir = tl::absolute_path (m_stream.absolute_path ());
|
||||
|
||||
if (tl::file_exists (input_dir)) {
|
||||
|
||||
std::vector<std::string> entries = tl::dir_entries (input_dir);
|
||||
|
|
|
|||
Loading…
Reference in New Issue