diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index e0f573282..4cc77b69d 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -1060,14 +1060,85 @@ LEFDEFReaderState::open_layer (db::Layout &layout, const std::string &n, LayerPu } } +static std::string purpose_to_name (LayerPurpose purpose) +{ + switch (purpose) { + case Outline: + return "OUTLINE"; + case Regions: + return "REGION"; + case PlacementBlockage: + return "BLOCKAGE"; + case Routing: + return "NET"; + case SpecialRouting: + return "SPNET"; + case ViaGeometry: + return "VIA"; + case Label: + return "LABEL"; + case Pins: + return "PIN"; + case LEFPins: + return "LEFPIN"; + case Obstructions: + return "LEFOBS"; + case Blockage: + return "BLK"; + case All: + return "ALL"; + } + + return std::string (); +} + +/** + * @brief Implements implicit layer mapping + * + * This is how Implicit layer mapping works: + * + * 1. For named layers (e.g. routing, pin, etc. + * + * A decorated name is formed from the basic name and the purpose string (e.g. "M1" -> "M1.PIN"). + * With the example of "M1" and purpose Pin (decorated name "M1.PIN") and with a tech component datatype specification + * of "5" for "Pin", the layer map entries have the following effect: + * + * Layer map Result + * + * (nothing) M1.PIN (default/5) (only if "create_all_layers" is ON, "default" is a default number assigned by the reader) + * M1.PIN : 1/0 M1.PIN (1/0) + * M1.PIN : 1/17 M1.PIN (1/17) + * M1 : 1/0 M1.PIN (1/5) + * M1 : 1/2 M1.PIN (1/7) (datatypes will add) + * M1 M1.PIN (default/5) + * M1 : METAL1 METAL1.PIN (default/5) (name is taken from layer map and decorated) + * M1 : METAL1 (1/2) METAL1.PIN (1/7) + * M1.PIN : METAL1_PIN METAL1_PIN (default/5) (specific name is used without decoration) + * M1.PIN : METAL1_PIN (1/17) METAL1_PIN (1/17) (full and specific mapping) + * + * 2. For general layers (e.g. outline) + * + * By default, the name, layer and datatype are taken from the tech component's specification. The specification may + * lack the layer and datatype and even the name. If the name is missing, it is generated from the purpose. + * + * Here are some examples for the mapping of "OUTLINE": + * + * Tech component Layer map Result + * + * (nothing) (nothing) OUTLINE (only if "create_all_layers" is ON) + * OUTL (nothing) OUTL (default/0) ("default" is a default number assigned by the reader) + * OUTL (4/17) (nothing) OUTL (4/17) + * OUTL OUTL : 5/1 OUTL (5/1) + * OUTL (4/17) OUTL : 4/11 OUTL 4/11 + * OUTL (4/17) 4/17 : 4/11 OUTL 4/11 + * 4/17 4/17 : 4/11 OUTLINE 4/11 + */ + std::pair LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n, LayerPurpose purpose, unsigned int mask) { if (n.empty ()) { - // 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; bool produce = false; @@ -1095,6 +1166,42 @@ LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n lp.datatype = 0; } + // if no name is given, derive one from the purpose + if (lp.name.empty ()) { + lp.name = purpose_to_name (purpose); + } + + if (lp.layer < 0) { + std::map::const_iterator ldef = m_default_number.find (lp.name); + if (ldef != m_default_number.end ()) { + lp.layer = ldef->second; + lp.datatype = 0; + } + } + + // employ the layer map to find the target layer + std::pair ll = m_layer_map.logical (lp, layout); + + if (ll.first) { + + // If the layer map provides a target, use that one for the layer + const db::LayerProperties *lpp = m_layer_map.target (ll.second); + if (lpp) { + if (! lpp->name.empty ()) { + lp.name = lpp->name; + } + if (lpp->datatype >= 0) { + lp.datatype = lpp->datatype; + } + if (lpp->layer >= 0) { + lp.layer = lpp->layer; + } + } + + } else if (! m_create_layers) { + return std::make_pair (false, 0); + } + for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { if ((*l).second->log_equal (lp)) { return std::make_pair (true, (*l).first); @@ -1139,8 +1246,6 @@ LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n } } - // Note: "name" is the decorated name as provided by the tech component's - // x_suffix specifications. std::string name_suffix; int dt = 0; @@ -1182,8 +1287,10 @@ LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n } } + // "name" is the decorated name as provided by the tech component's x_suffix specifications. std::string name = n + name_suffix; + // Assign a layer number (a default one for now) and the datatype from the tech component's x_datatype specification. db::LayerProperties lp (name); lp.datatype = dt; std::map::const_iterator ldef = m_default_number.find (n); @@ -1191,21 +1298,31 @@ LEFDEFReaderState::open_layer_uncached (db::Layout &layout, const std::string &n lp.layer = ldef->second; } + // Route the layer through the layer map, first the decorated name and if there is no mapping, the + // undecorated one. std::pair ll = m_layer_map.logical (name, layout); + bool generic_match = false; if (! ll.first) { ll = m_layer_map.logical (n, layout); + generic_match = true; } if (ll.first) { + // If the layer map provides a target, use that one for the layer + // Datatypes from the target and the tech component's x_datatype specification are additive const db::LayerProperties *lpp = m_layer_map.target (ll.second); if (lpp) { lp = *lpp; - if (lp.datatype >= 0) { + if (lp.datatype < 0) { + lp.datatype = dt; + } else if (generic_match) { lp.datatype += dt; } if (lp.name.empty ()) { lp.name = name; + } else if (generic_match) { + lp.name += name_suffix; } } @@ -1254,44 +1371,7 @@ LEFDEFReaderState::finish (db::Layout &layout) continue; } - std::string ps; - - switch (l->first.second.first) { - case Outline: - ps = "OUTLINE"; - break; - case Regions: - ps = "REGION"; - break; - case PlacementBlockage: - ps = "PLACEMENT_BLK"; - break; - case Routing: - default: - ps = "NET"; - break; - case SpecialRouting: - ps = "SPNET"; - break; - case ViaGeometry: - ps = "VIA"; - break; - case Label: - ps = "LABEL"; - break; - case Pins: - ps = "PIN"; - break; - case LEFPins: - ps = "LEFPIN"; - break; - case Obstructions: - ps = "OBS"; - break; - case Blockage: - ps = "BLK"; - break; - } + std::string ps = purpose_to_name (l->first.second.first); unsigned int layer_index = l->second.second; db::LayerProperties lp = layout.get_properties (layer_index); @@ -1336,7 +1416,7 @@ LEFDEFReaderState::finish (db::Layout &layout) } - // On return we deliver the "canonical" map + // On return we deliver the "canonical" map which lists the decorated name vs. the real ones. m_layer_map = lm; } diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index 72332bedf..fa446436a 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -50,16 +50,13 @@ static db::LEFDEFReaderOptions default_options () return tc; } -static db::LayerMap run_test (tl::TestBase *_this, const char *lef_dir, const char *filename, const char *au, const db::LEFDEFReaderOptions &options, bool priv = true) +static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *filename, const db::LEFDEFReaderOptions &options, bool priv = true) { std::string fn_path (priv ? tl::testsrc_private () : tl::testsrc ()); fn_path += "/testdata/lefdef/"; fn_path += lef_dir; fn_path += "/"; - db::Manager m (false); - db::Layout layout (&m), layout2 (&m), layout_au (&m); - tl::Extractor ex (filename); db::LEFDEFReaderState ld (&options, layout, fn_path); @@ -142,6 +139,16 @@ static db::LayerMap run_test (tl::TestBase *_this, const char *lef_dir, const ch ld.finish (layout); + return ld.layer_map (); +} + +static db::LayerMap run_test (tl::TestBase *_this, const char *lef_dir, const char *filename, const char *au, const db::LEFDEFReaderOptions &options, bool priv = true) +{ + db::Manager m (false); + db::Layout layout (&m), layout2 (&m), layout_au (&m); + + db::LayerMap lm = read (layout, lef_dir, filename, options, priv); + // normalize the layout by writing to OASIS and reading from .. // generate a "unique" name ... @@ -200,7 +207,7 @@ static db::LayerMap run_test (tl::TestBase *_this, const char *lef_dir, const ch } - return ld.layer_map (); + return lm; } TEST(1) @@ -549,16 +556,151 @@ TEST(115_componentmaskshift) run_test (_this, "masks-2", "lef:in_tech.lef+lef:in.lef+def:in.def", "au.oas.gz", options, false); } -TEST(116_name_to_ld_target_mapping) +TEST(116_layer_mapping) { db::LEFDEFReaderOptions options = default_options (); db::LayerMap lm = db::LayerMap::from_string_file_format ("metal1: 1\nvia1: 2\nmetal2: 3\nOUTLINE: 42/17"); options.set_layer_map (lm); - db::LayerMap lm_read = run_test (_this, "via_properties", "lef:in.lef+def:in.def", "au.oas.gz", options, false); - EXPECT_EQ (lm_read.to_string (), - "layer_map('OUTLINE : OUTLINE (4/0)';'metal1.VIA : metal1 (1/0)';'metal2.VIA : metal2 (3/0)';'via1.VIA : via1 (2/0)')" - ) + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTLINE (42/17)';'metal1.VIA : metal1 (1/0)';'metal2.VIA : metal2 (3/0)';'via1.VIA : via1 (2/0)')" + ) + } + + options.set_layer_map (db::LayerMap ()); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTLINE (4/0)';'metal1.VIA : metal1 (1/0)';'metal2.VIA : metal2 (3/0)';'via1.VIA : via1 (2/0)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("metal1: M1\nmetal1.V: M1_V\nvia1: V1\nmetal2: M2\nOUTLINE: OUTL"); + options.set_layer_map (lm); + options.set_via_geometry_suffix ("V"); + options.set_via_geometry_datatype (42); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTL (4/0)';'metal1.VIA : M1V (1/42)';'metal2.VIA : M2V (3/42)';'via1.VIA : V1V (2/42)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("metal1: M1\nmetal1.V: M1_V\nvia1: V1\nmetal2: M2"); + options.set_layer_map (lm); + options.set_via_geometry_suffix ("V"); + options.set_via_geometry_datatype (42); + options.set_read_all_layers (false); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('metal1.VIA : M1V (1/42)';'metal2.VIA : M2V (3/42)';'via1.VIA : V1V (2/42)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("metal2: M2 (17/1)"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('metal2.VIA : M2V (17/43)')" + ) + } + + options.set_produce_via_geometry (false); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map()" + ) + } + + options.set_produce_via_geometry (true); + options.set_via_geometry_suffix (".V"); + lm = db::LayerMap::from_string_file_format ("metal2.V: 17/1"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('metal2.VIA : metal2.V (17/1)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("metal2.V: m2v (17/5)"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('metal2.VIA : m2v (17/5)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("OUTLINE: OUTL"); + options.set_layer_map (lm); + options.set_cell_outline_layer ("OUTLINE (42/17)"); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTL (42/17)')" + ) + } + + lm = db::LayerMap::from_string_file_format ("OUTLINE: OUTL (18/1)"); + options.set_layer_map (lm); + options.set_cell_outline_layer ("OUTLINE (42/17)"); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTL (18/1)')" + ) + } + + options.set_cell_outline_layer ("OUTLINE (42/17)"); + lm = db::LayerMap::from_string_file_format ("42/17: OUTL (18/1)"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTL (18/1)')" + ) + } + + options.set_cell_outline_layer ("42/17"); + lm = db::LayerMap::from_string_file_format ("42/17: 18/1"); + options.set_layer_map (lm); + + { + db::Layout layout; + db::LayerMap lm_read = read (layout, "via_properties", "lef:in.lef+def:in.def", options, false); + EXPECT_EQ (lm_read.to_string (), + "layer_map('OUTLINE : OUTLINE (18/1)')" + ) + } + + } TEST(200_lefdef_plugin)