diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc index 752d20edd..958bdb723 100644 --- a/src/buddies/src/bd/bdReaderOptions.cc +++ b/src/buddies/src/bd/bdReaderOptions.cc @@ -846,12 +846,20 @@ static std::vector split_file_list (const std::string &infile) void read_files (db::Layout &layout, const std::string &infile, const db::LoadLayoutOptions &options) { + // We may do this: + // db::LayoutLocker locker (&layout); + // but there are yet unknown side effects + + // enter a LEF caching context for chaining multiple DEF with the same LEF + db::LoadLayoutOptions local_options (options); + local_options.set_option_by_name ("lefdef_config.lef_context_enabled", true); + std::vector files = split_file_list (infile); for (std::vector::const_iterator f = files.begin (); f != files.end (); ++f) { tl::InputStream stream (*f); db::Reader reader (stream); - reader.read (layout, options); + reader.read (layout, local_options); } } diff --git a/src/buddies/unit_tests/bdConverterTests.cc b/src/buddies/unit_tests/bdConverterTests.cc index eb8eef30d..4dfd7d20e 100644 --- a/src/buddies/unit_tests/bdConverterTests.cc +++ b/src/buddies/unit_tests/bdConverterTests.cc @@ -186,3 +186,327 @@ TEST(6) db::compare_layouts (this, layout, input_au, db::WriteGDS2); } + +// Large LEF/DEF to OAS converter test +TEST(10) +{ + test_is_long_runner (); + + std::string input_dir = tl::testdata_private (); + input_dir += "/lefdef/strm2oas/"; + + std::string lef_dir = input_dir + "/lef"; + std::string def_dir = input_dir + "/def"; + std::string gds_dir = input_dir + "/gds"; + + std::string input_au = input_dir + "/strm2oas_au.oas"; + + std::string output = this->tmp_file ("strm2oas.oas"); + std::string map_arg = "--lefdef-map=" + input_dir + "/sky130.map"; + + const char *lef_files[] = { + "sky130_fd_sc_hd.tlef", + "sky130_fd_sc_hd_merged.lef", + "sky130_fd_sc_hs_merged.lef", + "sky130_ef_sc_hd__decap_20_12.lef", + "sky130_ef_sc_hd__decap_80_12.lef", + "sky130_ef_sc_hd__fill_4.lef", + "sky130_ef_sc_hd__decap_40_12.lef", + "sky130_ef_sc_hd__decap_60_12.lef", + "sky130_ef_io__analog_esd_pad.lef", + "sky130_ef_io__analog_noesd_pad.lef", + "sky130_ef_io__analog_pad.lef", + "sky130_ef_io__bare_pad.lef", + "sky130_ef_io__com_bus_slice_10um.lef", + "sky130_ef_io__com_bus_slice_1um.lef", + "sky130_ef_io__com_bus_slice_20um.lef", + "sky130_ef_io__com_bus_slice_5um.lef", + "sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um.lef", + "sky130_ef_io__corner_pad.lef", + "sky130_ef_io__disconnect_vccd_slice_5um.lef", + "sky130_ef_io__disconnect_vdda_slice_5um.lef", + "sky130_ef_io__gpiov2_pad.lef", + "sky130_ef_io__gpiov2_pad_wrapped.lef", + "sky130_ef_io__top_power_hvc.lef", + "sky130_ef_io__vccd_hvc_pad.lef", + "sky130_ef_io__vccd_lvc_clamped2_pad.lef", + "sky130_ef_io__vccd_lvc_clamped3_pad.lef", + "sky130_ef_io__vccd_lvc_clamped_pad.lef", + "sky130_ef_io__vccd_lvc_pad.lef", + "sky130_ef_io__vdda_hvc_clamped_pad.lef", + "sky130_ef_io__vdda_hvc_pad.lef", + "sky130_ef_io__vdda_lvc_pad.lef", + "sky130_ef_io__vddio_hvc_clamped_pad.lef", + "sky130_ef_io__vddio_hvc_pad.lef", + "sky130_ef_io__vddio_lvc_pad.lef", + "sky130_ef_io__vssa_hvc_clamped_pad.lef", + "sky130_ef_io__vssa_hvc_pad.lef", + "sky130_ef_io__vssa_lvc_pad.lef", + "sky130_ef_io__vssd_hvc_pad.lef", + "sky130_ef_io__vssd_lvc_clamped2_pad.lef", + "sky130_ef_io__vssd_lvc_clamped3_pad.lef", + "sky130_ef_io__vssd_lvc_clamped_pad.lef", + "sky130_ef_io__vssd_lvc_pad.lef", + "sky130_ef_io__vssio_hvc_clamped_pad.lef", + "sky130_ef_io__vssio_hvc_pad.lef", + "sky130_ef_io__vssio_lvc_pad.lef", + "sky130_fd_io__signal_5_sym_hv_local_5term.lef", + "sky130_fd_io__top_gpiov2.lef", + "sky130_fd_io__top_power_hvc_wpadv2.lef", + "sky130_fd_sc_hvl__a21o_1.lef", + "sky130_fd_sc_hvl__a21oi_1.lef", + "sky130_fd_sc_hvl__a22o_1.lef", + "sky130_fd_sc_hvl__a22oi_1.lef", + "sky130_fd_sc_hvl__and2_1.lef", + "sky130_fd_sc_hvl__and3_1.lef", + "sky130_fd_sc_hvl__buf_1.lef", + "sky130_fd_sc_hvl__buf_16.lef", + "sky130_fd_sc_hvl__buf_2.lef", + "sky130_fd_sc_hvl__buf_32.lef", + "sky130_fd_sc_hvl__buf_4.lef", + "sky130_fd_sc_hvl__buf_8.lef", + "sky130_fd_sc_hvl__conb_1.lef", + "sky130_fd_sc_hvl__decap_4.lef", + "sky130_fd_sc_hvl__decap_8.lef", + "sky130_fd_sc_hvl__dfrbp_1.lef", + "sky130_fd_sc_hvl__dfrtp_1.lef", + "sky130_fd_sc_hvl__dfsbp_1.lef", + "sky130_fd_sc_hvl__dfstp_1.lef", + "sky130_fd_sc_hvl__dfxbp_1.lef", + "sky130_fd_sc_hvl__dfxtp_1.lef", + "sky130_fd_sc_hvl__diode_2.lef", + "sky130_fd_sc_hvl__dlclkp_1.lef", + "sky130_fd_sc_hvl__dlrtp_1.lef", + "sky130_fd_sc_hvl__dlxtp_1.lef", + "sky130_fd_sc_hvl__einvn_1.lef", + "sky130_fd_sc_hvl__einvp_1.lef", + "sky130_fd_sc_hvl__fill_1.lef", + "sky130_fd_sc_hvl__fill_2.lef", + "sky130_fd_sc_hvl__fill_4.lef", + "sky130_fd_sc_hvl__fill_8.lef", + "sky130_fd_sc_hvl__inv_1.lef", + "sky130_fd_sc_hvl__inv_16.lef", + "sky130_fd_sc_hvl__inv_2.lef", + "sky130_fd_sc_hvl__inv_4.lef", + "sky130_fd_sc_hvl__inv_8.lef", + "sky130_fd_sc_hvl__lsbufhv2hv_hl_1.lef", + "sky130_fd_sc_hvl__lsbufhv2hv_lh_1.lef", + "sky130_fd_sc_hvl__lsbufhv2lv_1.lef", + "sky130_fd_sc_hvl__lsbufhv2lv_simple_1.lef", + "sky130_fd_sc_hvl__lsbuflv2hv_1.lef", + "sky130_fd_sc_hvl__lsbuflv2hv_clkiso_hlkg_3.lef", + "sky130_fd_sc_hvl__lsbuflv2hv_isosrchvaon_1.lef", + "sky130_fd_sc_hvl__lsbuflv2hv_symmetric_1.lef", + "sky130_fd_sc_hvl__mux2_1.lef", + "sky130_fd_sc_hvl__mux4_1.lef", + "sky130_fd_sc_hvl__nand2_1.lef", + "sky130_fd_sc_hvl__nand3_1.lef", + "sky130_fd_sc_hvl__nor2_1.lef", + "sky130_fd_sc_hvl__nor3_1.lef", + "sky130_fd_sc_hvl__o21a_1.lef", + "sky130_fd_sc_hvl__o21ai_1.lef", + "sky130_fd_sc_hvl__o22a_1.lef", + "sky130_fd_sc_hvl__o22ai_1.lef", + "sky130_fd_sc_hvl__or2_1.lef", + "sky130_fd_sc_hvl__or3_1.lef", + "sky130_fd_sc_hvl__probe_p_8.lef", + "sky130_fd_sc_hvl__probec_p_8.lef", + "sky130_fd_sc_hvl__schmittbuf_1.lef", + "sky130_fd_sc_hvl__sdfrbp_1.lef", + "sky130_fd_sc_hvl__sdfrtp_1.lef", + "sky130_fd_sc_hvl__sdfsbp_1.lef", + "sky130_fd_sc_hvl__sdfstp_1.lef", + "sky130_fd_sc_hvl__sdfxbp_1.lef", + "sky130_fd_sc_hvl__sdfxtp_1.lef", + "sky130_fd_sc_hvl__sdlclkp_1.lef", + "sky130_fd_sc_hvl__sdlxtp_1.lef", + "sky130_fd_sc_hvl__xnor2_1.lef", + "sky130_fd_sc_hvl__xor2_1.lef", + "caravel.lef", + "caravel_clocking.lef", + "caravel_core.lef", + "gpio_defaults_block.lef", + "gpio_logic_high.lef", + "housekeeping.lef", + "mgmt_protect_hv.lef", + "mprj2_logic_high.lef", + "mprj_io_buffer.lef", + "mprj_logic_high.lef", + "spare_logic_block.lef", + "user_project_wrapper.lef", + "xres_buf.lef", + "caravel_logo-stub.lef", + "caravel_motto-stub.lef", + "chip_io.lef", + "copyright_block-stub.lef", + "empty_macro.lef", + "manual_power_connections.lef", + "open_source-stub.lef", + "simple_por.lef", + "user_id_programming.lef", + "user_id_textblock-stub.lef", + "RAM128.lef" + }; + + std::string lefs_arg = "--lefdef-lefs="; + for (size_t i = 0; i < sizeof (lef_files) / sizeof (lef_files[0]); ++i) { + if (i > 0) { + lefs_arg += ","; + } + lefs_arg += lef_dir + "/" + lef_files[i]; + } + + const char *lefdef_layout_files[] = { + "sky130_fd_sc_hd.gds", + "sky130_fd_sc_hvl__sdlxtp_1.gds", + "sky130_fd_sc_hvl__decap_8.gds", + "sky130_fd_sc_hvl__decap_4.gds", + "sky130_fd_sc_hvl__nand3_1.gds", + "sky130_fd_sc_hvl__sdfxbp_1.gds", + "sky130_fd_sc_hvl__lsbufhv2hv_hl_1.gds", + "sky130_fd_sc_hvl__sdfrbp_1.gds", + "sky130_fd_sc_hvl__a21o_1.gds", + "sky130_fd_sc_hvl__inv_2.gds", + "sky130_fd_sc_hvl__inv_16.gds", + "sky130_fd_sc_hvl__inv_1.gds", + "sky130_fd_sc_hvl__inv_4.gds", + "sky130_fd_sc_hvl__inv_8.gds", + "sky130_fd_sc_hvl__nand2_1.gds", + "sky130_fd_sc_hvl__dfstp_1.gds", + "sky130_fd_sc_hvl__a22o_1.gds", + "sky130_fd_sc_hvl__schmittbuf_1.gds", + "sky130_fd_sc_hvl__a22oi_1.gds", + "sky130_fd_sc_hvl__lsbuflv2hv_1.gds", + "sky130_fd_sc_hvl__fill_4.gds", + "sky130_fd_sc_hvl__fill_1.gds", + "sky130_fd_sc_hvl__fill_2.gds", + "sky130_fd_sc_hvl__fill_8.gds", + "sky130_fd_sc_hvl__sdfrtp_1.gds", + "sky130_fd_sc_hvl__sdfxtp_1.gds", + "sky130_fd_sc_hvl__o22a_1.gds", + "sky130_fd_sc_hvl__dfsbp_1.gds", + "sky130_fd_sc_hvl__o21a_1.gds", + "sky130_fd_sc_hvl__a21oi_1.gds", + "sky130_fd_sc_hvl__buf_1.gds", + "sky130_fd_sc_hvl__buf_2.gds", + "sky130_fd_sc_hvl__buf_4.gds", + "sky130_fd_sc_hvl__buf_32.gds", + "sky130_fd_sc_hvl__buf_16.gds", + "sky130_fd_sc_hvl__buf_8.gds", + "sky130_fd_sc_hvl__einvp_1.gds", + "sky130_fd_sc_hvl__conb_1.gds", + "sky130_fd_sc_hvl__and3_1.gds", + "sky130_fd_sc_hvl__lsbufhv2lv_1.gds", + "sky130_fd_sc_hvl__and2_1.gds", + "sky130_fd_sc_hvl__nor3_1.gds", + "sky130_fd_sc_hvl__dlclkp_1.gds", + "sky130_fd_sc_hvl__lsbuflv2hv_symmetric_1.gds", + "sky130_fd_sc_hvl__sdfstp_1.gds", + "sky130_fd_sc_hvl__dfrbp_1.gds", + "sky130_fd_sc_hvl__dfxbp_1.gds", + "sky130_fd_sc_hvl__nor2_1.gds", + "sky130_fd_sc_hvl__diode_2.gds", + "sky130_fd_sc_hvl__dlrtp_1.gds", + "sky130_fd_sc_hvl__dlxtp_1.gds", + "sky130_fd_sc_hvl__lsbufhv2lv_simple_1.gds", + "sky130_fd_sc_hvl__lsbuflv2hv_clkiso_hlkg_3.gds", + "sky130_fd_sc_hvl__sdlclkp_1.gds", + "sky130_fd_sc_hvl__o22ai_1.gds", + "sky130_fd_sc_hvl__or3_1.gds", + "sky130_fd_sc_hvl__sdfsbp_1.gds", + "sky130_fd_sc_hvl__xor2_1.gds", + "sky130_fd_sc_hvl__mux4_1.gds", + "sky130_fd_sc_hvl__or2_1.gds", + "sky130_fd_sc_hvl__probe_p_8.gds", + "sky130_fd_sc_hvl__dfxtp_1.gds", + "sky130_fd_sc_hvl__mux2_1.gds", + "sky130_fd_sc_hvl__dfrtp_1.gds", + "sky130_fd_sc_hvl__lsbuflv2hv_isosrchvaon_1.gds", + "sky130_fd_sc_hvl__probec_p_8.gds", + "sky130_fd_sc_hvl__xnor2_1.gds", + "sky130_fd_sc_hvl__einvn_1.gds", + "sky130_fd_sc_hvl__o21ai_1.gds", + "sky130_fd_sc_hvl__lsbufhv2hv_lh_1.gds", + "sky130_ef_io__analog.gds", + "sky130_ef_io__bare_pad.gds", + "sky130_ef_io__connect_vcchib_vccd_and_vswitch_vddio_slice_20um.gds", + "sky130_ef_io__disconnect_vccd_slice_5um.gds", + "sky130_ef_io__disconnect_vdda_slice_5um.gds", + "sky130_ef_io__gpiov2_pad_wrapped.gds", + "sky130_ef_sc_hd__decap_12.gds", + "sky130_ef_sc_hd__decap_20_12.gds", + "sky130_ef_sc_hd__decap_40_12.gds", + "sky130_ef_sc_hd__decap_60_12.gds", + "sky130_ef_sc_hd__decap_80_12.gds", + "sky130_ef_sc_hd__fill_12.gds", + "sky130_ef_sc_hd__fill_2.gds", + "sky130_ef_sc_hd__fill_4.gds", + "sky130_ef_sc_hd__fill_8.gds", + "sky130_ef_sc_hvl__fill_8.gds", + "caravel_logo.gds.gz", + "caravel_motto.gds.gz", + "chip_io.gds.gz", + "copyright_block.gds.gz", + "empty_macro.gds.gz", + "manual_power_connections.gds.gz", + "open_source.gds.gz", + "simple_por.gds.gz", + "user_id_programming.gds.gz", + "user_id_textblock.gds.gz", + "RAM128.gds.gz" + }; + + std::string lefdef_layouts_arg = "--lefdef-lef-layouts="; + for (size_t i = 0; i < sizeof (lefdef_layout_files) / sizeof (lefdef_layout_files[0]); ++i) { + if (i > 0) { + lefdef_layouts_arg += ","; + } + lefdef_layouts_arg += gds_dir + "/" + lefdef_layout_files[i]; + } + + const char *def_files[] = { + "caravel.def", + "caravel_clocking.def", + "caravel_core.def.gz", + "gpio_defaults_block.def", + "gpio_logic_high.def", + "housekeeping.def", + "mgmt_protect_hv.def", + "mprj2_logic_high.def", + "mprj_io_buffer.def", + "mprj_logic_high.def", + "spare_logic_block.def", + "user_project_wrapper.def", + "xres_buf.def" + }; + + std::string input; + for (size_t i = 0; i < sizeof (def_files) / sizeof (def_files[0]); ++i) { + if (i > 0) { + input += ","; + } + input += def_dir + "/" + def_files[i]; + } + + const char *argv[] = { "x", + "--lefdef-no-implicit-lef", + map_arg.c_str (), + lefs_arg.c_str (), + lefdef_layouts_arg.c_str (), + input.c_str (), + output.c_str () + }; + + EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::oasis_format_name), 0); + + db::Layout layout; + + { + tl::InputStream stream (output); + db::LoadLayoutOptions options; + db::Reader reader (stream); + reader.read (layout, options); + } + + db::compare_layouts (this, layout, input_au, db::WriteOAS); +} diff --git a/src/db/db/dbCommonReader.cc b/src/db/db/dbCommonReader.cc index bf21fa5fc..b1becdcfb 100644 --- a/src/db/db/dbCommonReader.cc +++ b/src/db/db/dbCommonReader.cc @@ -314,8 +314,13 @@ CommonReaderBase::merge_cell_without_instances (db::Layout &layout, db::cell_ind } void -CommonReaderBase::init () +CommonReaderBase::start () { + m_id_map.clear (); + m_name_map.clear (); + m_temp_cells.clear (); + m_name_for_id.clear (); + m_layer_map_out.clear (); m_multi_mapping_placeholders.clear (); m_layer_cache.clear (); @@ -621,7 +626,7 @@ void CommonReader::init (const LoadLayoutOptions &options) { ReaderBase::init (options); - CommonReaderBase::init (); + CommonReaderBase::start (); db::CommonReaderOptions common_options = options.get_options (); set_conflict_resolution_mode (common_options.cell_conflict_resolution); diff --git a/src/db/db/dbCommonReader.h b/src/db/db/dbCommonReader.h index 7433cafe3..1a7dafcdf 100644 --- a/src/db/db/dbCommonReader.h +++ b/src/db/db/dbCommonReader.h @@ -207,7 +207,7 @@ public: /** * @brief Re-initialize: clears the tables and caches */ - void init (); + void start (); /** * @brief Sets a value indicating whether to create layers diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc index b91bd458f..ed3856e6e 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.cc @@ -53,24 +53,11 @@ struct DEFImporterGroup }; DEFImporter::DEFImporter (int warn_level) - : LEFDEFImporter (warn_level), - m_lef_importer (warn_level) + : LEFDEFImporter (warn_level) { // .. nothing yet .. } -void -DEFImporter::read_lef (tl::InputStream &stream, db::Layout &layout, LEFDEFReaderState &state) -{ - m_lef_importer.read (stream, layout, state); -} - -void -DEFImporter::finish_lef (db::Layout &layout) -{ - m_lef_importer.finish_lef (layout); -} - void DEFImporter::read_polygon (db::Polygon &poly, double scale) { @@ -112,7 +99,7 @@ DEFImporter::read_rect (db::Polygon &poly, double scale) std::pair DEFImporter::get_wire_width_for_rule (const std::string &rulename, const std::string &ln, double dbu) { - std::pair wxy = m_lef_importer.layer_width (ln, rulename); + std::pair wxy = reader_state ()->lef_importer ().layer_width (ln, rulename); db::Coord wx = db::coord_traits::rounded (wxy.first / dbu); db::Coord wy = db::coord_traits::rounded (wxy.second / dbu); @@ -127,7 +114,7 @@ DEFImporter::get_wire_width_for_rule (const std::string &rulename, const std::st } } - std::pair min_wxy = m_lef_importer.min_layer_width (ln); + std::pair min_wxy = reader_state ()->lef_importer ().min_layer_width (ln); db::Coord min_wx = db::coord_traits::rounded (min_wxy.first / dbu); db::Coord min_wy = db::coord_traits::rounded (min_wxy.second / dbu); @@ -762,14 +749,19 @@ DEFImporter::read_single_net (std::string &nondefaultrule, Layout &layout, db::C } std::map::const_iterator vd = m_via_desc.find (vn); - if (vd != m_via_desc.end () && ! pts.empty ()) { + + if (vd == m_via_desc.end ()) { + + warn (tl::to_string (tr ("Invalid via name: ")) + vn); + + } else if (! pts.empty ()) { // For the via, the masks are encoded in a three-digit number ( ) unsigned int mask_top = (mask / 100) % 10; unsigned int mask_cut = (mask / 10) % 10; unsigned int mask_bottom = mask % 10; - db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer); + db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &reader_state ()->lef_importer ()); if (cell) { if (nx <= 1 && ny <= 1) { design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (ft.rot (), db::Vector (pts.back ())))); @@ -953,7 +945,7 @@ DEFImporter::read_nets (db::Layout &layout, db::Cell &design, double scale, bool std::map::const_iterator vd = m_via_desc.find (vn); if (vd != m_via_desc.end ()) { - db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer); + db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &reader_state ()->lef_importer ()); if (cell) { design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (ft.rot (), pt))); } @@ -1130,7 +1122,7 @@ DEFImporter::read_vias (db::Layout &layout, db::Cell & /*design*/, double scale) std::string ln = get (); - if (m_lef_importer.is_routing_layer (ln)) { + if (reader_state ()->lef_importer ().is_routing_layer (ln)) { if (seen_layers.find (ln) == seen_layers.end ()) { @@ -1145,7 +1137,7 @@ DEFImporter::read_vias (db::Layout &layout, db::Cell & /*design*/, double scale) } - } else if (m_lef_importer.is_cut_layer (ln)) { + } else if (reader_state ()->lef_importer ().is_cut_layer (ln)) { geo_based_vg->set_maskshift_layer (1, ln); has_cut_geometry = true; @@ -1195,8 +1187,10 @@ DEFImporter::read_vias (db::Layout &layout, db::Cell & /*design*/, double scale) if (rule_based_vg.get () && geo_based_vg.get ()) { error (tl::to_string (tr ("A via can only be defined through a VIARULE or geometry, not both ways"))); } else if (rule_based_vg.get ()) { + rule_based_vg->def_local = true; reader_state ()->register_via_cell (n, std::string (), rule_based_vg.release ()); } else if (geo_based_vg.get ()) { + geo_based_vg->def_local = true; reader_state ()->register_via_cell (n, std::string (), geo_based_vg.release ()); } else { error (tl::to_string (tr ("Too little information to generate a via"))); @@ -1336,7 +1330,7 @@ DEFImporter::read_pins (db::Layout &layout, db::Cell &design, double scale) std::map::const_iterator vd = m_via_desc.find (vn); if (vd != m_via_desc.end ()) { std::string nondefaultrule; - db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer); + db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &reader_state ()->lef_importer ()); if (cell) { design.insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (pt))); } @@ -1549,7 +1543,7 @@ DEFImporter::read_fills (db::Layout &layout, db::Cell &design, double scale) std::map::const_iterator vd = m_via_desc.find (vn); if (vd != m_via_desc.end ()) { std::string nondefaultrule; - db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &m_lef_importer); + db::Cell *cell = reader_state ()->via_cell (vn, nondefaultrule, layout, mask_bottom, mask_cut, mask_top, &reader_state ()->lef_importer ()); if (cell) { ensure_fill_cell (layout, design, fill_cell).insert (db::CellInstArray (db::CellInst (cell->cell_index ()), db::Trans (pt))); } @@ -1600,6 +1594,7 @@ DEFImporter::read_styles (double scale) } } + void DEFImporter::read_components (db::Layout &layout, std::list > &instances, double scale) { @@ -1613,9 +1608,26 @@ DEFImporter::read_components (db::Layout &layout, std::list::const_iterator m = m_lef_importer.macros ().find (model); - if (m == m_lef_importer.macros ().end ()) { - error (tl::to_string (tr ("Macro not found in LEF file: ")) + model); + const MacroDesc *m = 0; + + std::map::const_iterator im = reader_state ()->lef_importer ().macros ().find (model); + if (im == reader_state ()->lef_importer ().macros ().end ()) { + + warn (tl::sprintf (tl::to_string (tr ("Macro not found in LEF file: %s - creating dummy macro")), model)); + + // create a dummy macro definition (no FOREIGN, size 0x0 etc.) + GeometryBasedLayoutGenerator *mg = new GeometryBasedLayoutGenerator (); + reader_state ()->register_macro_cell (model, mg); + + MacroDesc macro_desc; + macro_desc.bbox = db::Box (db::Point (), db::Point ()); + + m = reader_state ()->lef_importer ().insert_macro (model, macro_desc); + + } else { + + m = &im->second; + } while (test ("+")) { @@ -1627,7 +1639,7 @@ DEFImporter::read_components (db::Layout &layout, std::listsecond.bbox.transformed (ft).lower_left (); + d = pt - m->bbox.transformed (ft).lower_left (); is_placed = true; } else if (test ("UNPLACED")) { @@ -1639,7 +1651,7 @@ DEFImporter::read_components (db::Layout &layout, std::listsecond.bbox.transformed (ft).lower_left (); + d = pt - m->bbox.transformed (ft).lower_left (); is_placed = true; } @@ -1660,7 +1672,7 @@ DEFImporter::read_components (db::Layout &layout, std::list ct = reader_state ()->macro_cell (model, layout, m_component_maskshift, string2masks (maskshift), m->second, &m_lef_importer); + std::pair ct = reader_state ()->macro_cell (model, layout, m_component_maskshift, string2masks (maskshift), *m, &reader_state ()->lef_importer ()); if (ct.first) { db::CellInstArray inst (db::CellInst (ct.first->cell_index ()), db::Trans (ft.rot (), d) * ct.second); instances.push_back (std::make_pair (inst_name, inst)); @@ -1684,7 +1696,7 @@ DEFImporter::do_read (db::Layout &layout) std::list groups; std::list > instances; - m_via_desc = m_lef_importer.vias (); + m_via_desc = reader_state ()->lef_importer ().vias (); m_styles.clear (); m_design_name.clear (); diff --git a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h index 0b824e403..56e7568f8 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbDEFImporter.h @@ -48,26 +48,12 @@ public: /** * @brief Default constructor */ - DEFImporter (int warn_level = 1); - - /** - * @brief Read the given LEF file prior to the DEF file - * - * This method reads the layout specified into the given layout. - * Multiple LEF files can be read. - */ - void read_lef (tl::InputStream &stream, db::Layout &layout, LEFDEFReaderState &state); - - /** - * @brief Provided for test purposes - */ - void finish_lef (Layout &layout); + DEFImporter (int warn_level); protected: void do_read (db::Layout &layout); private: - LEFImporter m_lef_importer; std::map > m_nondefault_widths; std::map m_via_desc; std::map m_styles; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc index d95144c75..dbbe55cf1 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.cc @@ -22,9 +22,11 @@ #include "dbLEFDEFImporter.h" +#include "dbLEFImporter.h" #include "dbLayoutUtils.h" #include "dbTechnology.h" #include "dbShapeProcessor.h" +#include "dbCellMapping.h" #include "tlStream.h" #include "tlProgress.h" @@ -598,13 +600,15 @@ LEFDEFReaderOptions::LEFDEFReaderOptions () m_map_file (), m_macro_resolution_mode (0), m_read_lef_with_def (true), - m_paths_relative_to_cwd (false) + m_paths_relative_to_cwd (false), + m_lef_context_enabled (false) { // .. nothing yet .. } LEFDEFReaderOptions::LEFDEFReaderOptions (const LEFDEFReaderOptions &d) - : db::FormatSpecificReaderOptions () + : db::FormatSpecificReaderOptions (), + m_lef_context_enabled (false) { operator= (d); } @@ -951,26 +955,33 @@ LEFDEFReaderOptions::special_routing_datatype_str () const return get_datatypes (this, &LEFDEFReaderOptions::special_routing_datatype, &LEFDEFReaderOptions::special_routing_datatype_per_mask, max_mask_number ()); } +void +LEFDEFReaderOptions::set_lef_context_enabled (bool f) +{ + if (f != m_lef_context_enabled) { + mp_reader_state.reset (0); + m_lef_context_enabled = f; + } +} + +db::LEFDEFReaderState * +LEFDEFReaderOptions::reader_state (db::Layout &layout, const std::string &base_path, const db::LoadLayoutOptions &options) const +{ + if (m_lef_context_enabled && ! mp_reader_state.get ()) { + mp_reader_state.reset (new db::LEFDEFReaderState (this)); + mp_reader_state->init (layout, base_path, options); + } + + return mp_reader_state.get (); +} + // ----------------------------------------------------------------------------------- // LEFDEFLayerDelegate implementation -LEFDEFReaderState::LEFDEFReaderState (const LEFDEFReaderOptions *tc, db::Layout &layout, const std::string &base_path) +LEFDEFReaderState::LEFDEFReaderState (const LEFDEFReaderOptions *tc) : mp_importer (0), m_create_layers (true), m_has_explicit_layer_mapping (false), m_laynum (1), mp_tech_comp (tc) { - if (! tc) { - - // use default options - - } else if (! tc->map_file ().empty ()) { - - read_map_file (tc->map_file (), layout, base_path); - - } else { - - m_layer_map = tc->layer_map (); - m_create_layers = tc->read_all_layers (); - - } + // .. nothing yet .. } LEFDEFReaderState::~LEFDEFReaderState () @@ -988,6 +999,57 @@ LEFDEFReaderState::~LEFDEFReaderState () m_macro_generators.clear (); } +void +LEFDEFReaderState::init (Layout &layout, const std::string &base_path, const LoadLayoutOptions &options) +{ + if (! mp_tech_comp) { + + // use default options + + } else if (! mp_tech_comp->map_file ().empty ()) { + + read_map_file (mp_tech_comp->map_file (), layout, base_path); + + } else { + + m_layer_map = mp_tech_comp->layer_map (); + m_create_layers = mp_tech_comp->read_all_layers (); + + } + + if (mp_tech_comp) { + + m_macro_layouts = mp_tech_comp->macro_layouts (); + + // Additionally read the layouts from the given paths + for (std::vector::const_iterator l = mp_tech_comp->begin_macro_layout_files (); l != mp_tech_comp->end_macro_layout_files (); ++l) { + + auto paths = correct_path (*l, layout, base_path, true); + for (auto lp = paths.begin (); lp != paths.end (); ++lp) { + + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + *lp); + + tl::InputStream macro_layout_stream (*lp); + tl::log << tl::to_string (tr ("Reading")) << " " << *lp; + db::Layout *new_layout = new db::Layout (false); + m_macro_layout_object_holder.push_back (new_layout); + m_macro_layouts.push_back (new_layout); + + db::Reader reader (macro_layout_stream); + reader.read (*new_layout, options); + + if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { + warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), + *lp, new_layout->dbu (), layout.dbu ())); + } + + } + + } + + } +} + void LEFDEFReaderState::error (const std::string &msg) { @@ -1004,6 +1066,36 @@ LEFDEFReaderState::warn (const std::string &msg, int warn_level) } } +void +LEFDEFReaderState::ensure_lef_importer (int warn_level) +{ + if (! mp_lef_importer.get ()) { + mp_lef_importer.reset (new db::LEFImporter (warn_level)); + } +} + +db::LEFImporter & +LEFDEFReaderState::lef_importer () +{ + tl_assert (mp_lef_importer.get () != 0); + return *mp_lef_importer; +} + +void +LEFDEFReaderState::read_lef (const std::string &fn, db::Layout &layout) +{ + tl::InputStream stream (fn); + lef_importer ().read (stream, layout, *this); + + m_lef_files_read.insert (fn); +} + +void +LEFDEFReaderState::finish_lef (db::Layout &layout) +{ + lef_importer ().finish_lef (layout); +} + void LEFDEFReaderState::register_layer (const std::string &ln) { @@ -1715,11 +1807,76 @@ std::set LEFDEFReaderState::open_layer_uncached(db::Layout &layout } } +void +LEFDEFReaderState::start () +{ + // Start over for a new DEF file - this function is used in LEF context mode + // i.e. when LEFs are cached during multiple DEF reads. It is called when a new DEF is read. + + CommonReaderBase::start (); + + m_foreign_cells.clear (); + + // Remove the via generators that were added by DEF + // TODO: there is no concept for "local LEFs" currently. Even LEFs stored along + // with DEFs are considered "global". + for (auto vg = m_via_generators.begin (); vg != m_via_generators.end (); ) { + auto vg_here = vg; + ++vg; + if (vg_here->second->def_local) { + delete vg_here->second; + m_via_generators.erase (vg_here); + } + } + + // We always create fresh via cells for different DEFs to avoid potential + // content conflicts. Problem is: vias can be generated by both LEF (global) + // and DEF (local) + m_via_cells.clear (); +} + void LEFDEFReaderState::finish (db::Layout &layout) { CommonReaderBase::finish (layout); + // Resolve unresolved COMPONENT cells + + db::cell_index_type seen = std::numeric_limits::max (); + + for (std::vector::const_iterator m = macro_layouts ().begin (); m != macro_layouts ().end (); ++m) { + + std::vector target_cells, source_cells; + + // collect the cells to pull in + for (std::map::iterator f = m_foreign_cells.begin (); f != m_foreign_cells.end (); ++f) { + if (f->second != seen) { + std::pair cp = (*m)->cell_by_name (f->first.c_str ()); + if (cp.first) { + target_cells.push_back (f->second); + source_cells.push_back (cp.second); + layout.cell (f->second).set_ghost_cell (false); + f->second = seen; + } + } + } + + db::CellMapping cm; + cm.create_multi_mapping_full (layout, target_cells, **m, source_cells); + layout.copy_tree_shapes (**m, cm); + + } + + // Warn about cells that could not be resolved + for (std::map::iterator f = m_foreign_cells.begin (); f != m_foreign_cells.end (); ++f) { + if (f->second != seen && layout.cell (f->second).is_ghost_cell ()) { + warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")), + f->first)); + } + } + + // Create the layers + int lnum = 0; std::set used_numbers; @@ -1790,48 +1947,52 @@ LEFDEFReaderState::finish (db::Layout &layout) void LEFDEFReaderState::register_via_cell (const std::string &vn, const std::string &nondefaultrule, LEFDEFLayoutGenerator *generator) { - if (m_via_generators.find (std::make_pair (vn, nondefaultrule)) != m_via_generators.end ()) { - delete m_via_generators [std::make_pair (vn, nondefaultrule)]; - } - m_via_generators [std::make_pair (vn, nondefaultrule)] = generator; + // inserts at the end of the range + m_via_generators.insert (std::make_pair (std::make_pair (vn, nondefaultrule), generator)); } LEFDEFLayoutGenerator * LEFDEFReaderState::via_generator (const std::string &vn, const std::string &nondefaultrule) { - std::map, LEFDEFLayoutGenerator *>::const_iterator g = m_via_generators.find (std::make_pair (vn, nondefaultrule)); - if (g == m_via_generators.end () && ! nondefaultrule.empty ()) { - // default rule is fallback - g = m_via_generators.find (std::make_pair (vn, std::string ())); + return via_generator_and_rule (vn, nondefaultrule).first; +} + +std::pair +LEFDEFReaderState::via_generator_and_rule (const std::string &vn, const std::string &nondefaultrule) +{ + auto key = std::make_pair (vn, nondefaultrule); + + auto g = m_via_generators.upper_bound (key); + if (g != m_via_generators.begin ()) { + --g; } - if (g != m_via_generators.end ()) { - return g->second; + + if (g == m_via_generators.end () || g->first != key) { + if (nondefaultrule.empty ()) { + return std::pair (0, std::string ()); + } else { + // default rule is fallback + return via_generator_and_rule (vn, std::string ()); + } } else { - return 0; + return std::make_pair (g->second, nondefaultrule); } } db::Cell * LEFDEFReaderState::via_cell (const std::string &vn, const std::string &nondefaultrule, db::Layout &layout, unsigned int mask_bottom, unsigned int mask_cut, unsigned int mask_top, const LEFDEFNumberOfMasks *nm) { - ViaKey vk (vn, nondefaultrule, mask_bottom, mask_cut, mask_top); + auto gr = via_generator_and_rule (vn, nondefaultrule); + LEFDEFLayoutGenerator *vg = gr.first; - std::map, LEFDEFLayoutGenerator *>::const_iterator g = m_via_generators.find (std::make_pair (vn, nondefaultrule)); - - if (g == m_via_generators.end () && ! vk.nondefaultrule.empty ()) { - // default rule is fallback - g = m_via_generators.find (std::make_pair (vn, std::string ())); - vk.nondefaultrule.clear (); - } + ViaKey vk (vn, gr.second, mask_bottom, mask_cut, mask_top); std::map::const_iterator i = m_via_cells.find (vk); if (i == m_via_cells.end ()) { db::Cell *cell = 0; - if (g != m_via_generators.end ()) { - - LEFDEFLayoutGenerator *vg = g->second; + if (vg) { std::string n = vn; @@ -1860,6 +2021,14 @@ LEFDEFReaderState::via_cell (const std::string &vn, const std::string &nondefaul vg->create_cell (*this, layout, *cell, 0, masks, nm); + } else { + + std::string details; + if (! nondefaultrule.empty () && nondefaultrule != vk.nondefaultrule) { + details = tl::sprintf (tl::to_string (tr (" (trying with NONDEFAULTRULE '%s' and without)")), nondefaultrule); + } + error (tl::sprintf (tl::to_string (tr ("Could not find a via specification with name '%s'")) + details, vn)); + } m_via_cells[vk] = cell; diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h index 2a5736f0e..d801c04b2 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFImporter.h @@ -46,6 +46,7 @@ namespace db class LEFDEFReaderState; class LEFDEFImporter; +class LEFImporter; struct MacroDesc; /** @@ -960,6 +961,25 @@ public: m_macro_layout_files = lf; } + /** + * @brief A hidden attribute to enable a LEF context + * LEF context are used for chaining DEF readers. This method must be + * called on local copy of the importer options. With this attribute + * set to "true", the client code can store a LEFDEFReaderState object + * in this object. + * Initially, this attribute is set to false. + * Note that this attribute is not copied. + */ + void set_lef_context_enabled (bool context); + + bool lef_context_enabled () const + { + return m_lef_context_enabled; + } + + // makes the reader state object if required + db::LEFDEFReaderState *reader_state (db::Layout &layout, const std::string &base_path, const LoadLayoutOptions &options) const; + private: bool m_read_all_layers; db::LayerMap m_layer_map; @@ -1028,6 +1048,8 @@ private: tl::weak_collection m_macro_layouts; std::vector m_macro_layout_files; bool m_paths_relative_to_cwd; + bool m_lef_context_enabled; + mutable std::unique_ptr mp_reader_state; }; /** @@ -1119,12 +1141,14 @@ public: class DB_PLUGIN_PUBLIC LEFDEFLayoutGenerator { public: - LEFDEFLayoutGenerator () { } + LEFDEFLayoutGenerator () : def_local (false) { } virtual ~LEFDEFLayoutGenerator () { } virtual void create_cell (LEFDEFReaderState &reader, db::Layout &layout, db::Cell &cell, const std::vector *maskshift_layers, const std::vector &masks, const LEFDEFNumberOfMasks *nm) = 0; virtual std::vector maskshift_layers () const = 0; virtual bool is_fixedmask () const = 0; + + bool def_local; }; /** @@ -1247,13 +1271,18 @@ public: /** * @brief Constructor */ - LEFDEFReaderState (const LEFDEFReaderOptions *tc, db::Layout &layout, const std::string &base_path = std::string ()); + LEFDEFReaderState (const LEFDEFReaderOptions *tc); /** * @brief Destructor */ ~LEFDEFReaderState (); + /** + * @brief Initialize with the layout and base path + */ + void init (db::Layout &layout, const std::string &base_path, const LoadLayoutOptions &options); + /** * @brief Attaches to or detaches from an importer */ @@ -1299,8 +1328,15 @@ public: */ void register_layer (const std::string &l); + /** + * @brief Start reading a file + * After the file is read, "finish" needs to be called. + */ + void start (); + /** * @brief Finish, i.e. assign GDS layer numbers to the layers + * This is the counterpart for "start". */ void finish (db::Layout &layout); @@ -1364,6 +1400,44 @@ public: */ void warn (const std::string &msg, int warn_level = 1); + /** + * @brief Ensures the LEF importer for DEF reading is available + */ + void ensure_lef_importer (int warn_level); + + /** + * @brief Gets the LEF importer for DEF reading + */ + db::LEFImporter &lef_importer (); + + /** + * @brief Reads a LEF file into the LEF importer + * + * Multiple LEF files can be read. + */ + void read_lef (const std::string &fn, db::Layout &layout); + + /** + * @brief Provided for test purposes + */ + void finish_lef (db::Layout &layout); + + /** + * @brief Gets a value indicating whether the given LEF file was already read + */ + bool lef_file_already_read (const std::string &fn) + { + return m_lef_files_read.find (fn) != m_lef_files_read.end (); + } + + /** + * @brief Gets the stored macro layouts + */ + const std::vector ¯o_layouts () const + { + return m_macro_layouts; + } + protected: virtual void common_reader_error (const std::string &msg) { error (msg); } virtual void common_reader_warn (const std::string &msg, int warn_level) { warn (msg, warn_level); } @@ -1452,14 +1526,19 @@ private: std::map m_default_number; const LEFDEFReaderOptions *mp_tech_comp; std::map m_via_cells; - std::map, LEFDEFLayoutGenerator *> m_via_generators; + std::multimap, LEFDEFLayoutGenerator *> m_via_generators; std::map > m_macro_cells; std::map m_macro_generators; std::map m_foreign_cells; + std::set m_lef_files_read; + std::vector m_macro_layouts; + tl::shared_collection m_macro_layout_object_holder; + std::unique_ptr mp_lef_importer; std::set open_layer_uncached (db::Layout &layout, const std::string &name, LayerPurpose purpose, unsigned int mask); db::cell_index_type foreign_cell(Layout &layout, const std::string &name); void read_single_map_file (const std::string &path, std::map, std::vector > &layer_map); + std::pair via_generator_and_rule (const std::string &vn, const std::string &nondefaultrule); }; /** diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc index 06e42a0c7..e4e7dfb80 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFDEFPlugin.cc @@ -113,23 +113,39 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti init (options); const db::LEFDEFReaderOptions *lefdef_options = dynamic_cast (options.get_options (format ())); + db::LEFDEFReaderOptions effective_options; if (lefdef_options) { effective_options = *lefdef_options; } + layout.dbu (effective_options.dbu ()); + std::string base_path; if (! effective_options.paths_relative_to_cwd ()) { base_path = tl::dirname (m_stream.absolute_file_path ()); } - db::LEFDEFReaderState state (&effective_options, layout, base_path); + // If the LEF reader context (LEF caching) is enabled on the options, + // pull the state from there, otherwise create a local state. + db::LEFDEFReaderState local_state (&effective_options); + db::LEFDEFReaderState *state = 0; + if (lefdef_options) { + state = lefdef_options->reader_state (layout, base_path, options); + } + if (! state) { + local_state.init (layout, base_path, options); + state = &local_state; + } + state->ensure_lef_importer (warn_level ()); + state->start (); + + // Configure the conflict resolution mode db::CommonReaderOptions common_options = options.get_options (); - state.set_conflict_resolution_mode (common_options.cell_conflict_resolution); - - layout.dbu (effective_options.dbu ()); + state->set_conflict_resolution_mode (common_options.cell_conflict_resolution); + // Import LEF if (import_lef) { // Always produce LEF geometry when reading LEF @@ -145,13 +161,13 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti for (auto lp = paths.begin (); lp != paths.end (); ++lp) { tl::InputStream lef_stream (*lp); tl::log << tl::to_string (tr ("Reading")) << " " << *lp; - importer.read (lef_stream, layout, state); + importer.read (lef_stream, layout, *state); } } tl::log << tl::to_string (tr ("Reading")) << " " << m_stream.source (); - importer.read (m_stream, layout, state); + importer.read (m_stream, layout, *state); importer.finish_lef (layout); @@ -161,20 +177,18 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti DEFImporter importer (warn_level ()); - std::set lef_files_read; - for (std::vector::const_iterator l = effective_options.begin_lef_files (); l != effective_options.end_lef_files (); ++l) { auto paths = correct_path (*l, layout, base_path, true); for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - if (lef_files_read.insert (tl::normalize_path (*lp)).second) { + std::string norm_lp = tl::normalize_path (*lp); + if (! state->lef_file_already_read (norm_lp)) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + *lp); - tl::InputStream lef_stream (*lp); tl::log << tl::to_string (tr ("Reading")) << " " << *lp; - importer.read_lef (lef_stream, layout, state); + state->read_lef (norm_lp, layout); } @@ -198,13 +212,13 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti std::string lp = tl::combine_path (input_dir, *e); // skip if already read (issue-1724) - if (lef_files_read.find (tl::normalize_path (lp)) == lef_files_read.end ()) { + std::string norm_lp = tl::normalize_path (lp); + if (! state->lef_file_already_read (norm_lp)) { tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF file: ")) + lp); - tl::InputStream lef_stream (lp); tl::log << tl::to_string (tr ("Reading")) << " " << lp; - importer.read_lef (lef_stream, layout, state); + state->read_lef (norm_lp, layout); } @@ -217,78 +231,13 @@ LEFDEFReader::read_lefdef (db::Layout &layout, const db::LoadLayoutOptions &opti } tl::log << tl::to_string (tr ("Reading")) << " " << m_stream.source (); - importer.read (m_stream, layout, state); - - // Resolve unresolved COMPONENT cells - - std::map foreign_cells = state.foreign_cells (); - db::cell_index_type seen = std::numeric_limits::max (); - - std::vector macro_layouts = effective_options.macro_layouts (); - - // Additionally read the layouts from the given paths - tl::shared_collection macro_layout_object_holder; - for (std::vector::const_iterator l = effective_options.begin_macro_layout_files (); l != effective_options.end_macro_layout_files (); ++l) { - - auto paths = correct_path (*l, layout, base_path, true); - for (auto lp = paths.begin (); lp != paths.end (); ++lp) { - - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading LEF macro layout file: ")) + *lp); - - tl::InputStream macro_layout_stream (*lp); - tl::log << tl::to_string (tr ("Reading")) << " " << *lp; - db::Layout *new_layout = new db::Layout (false); - macro_layout_object_holder.push_back (new_layout); - macro_layouts.push_back (new_layout); - - db::Reader reader (macro_layout_stream); - reader.read (*new_layout, options); - - if (fabs (new_layout->dbu () / layout.dbu () - 1.0) > db::epsilon) { - importer.warn (tl::sprintf (tl::to_string (tr ("DBU of macro layout file '%s' does not match reader DBU (layout DBU is %.12g, reader DBU is set to %.12g)")), - *lp, new_layout->dbu (), layout.dbu ())); - } - - } - - } - - for (std::vector::const_iterator m = macro_layouts.begin (); m != macro_layouts.end (); ++m) { - - std::vector target_cells, source_cells; - - // collect the cells to pull in - for (std::map::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) { - if (f->second != seen) { - std::pair cp = (*m)->cell_by_name (f->first.c_str ()); - if (cp.first) { - target_cells.push_back (f->second); - source_cells.push_back (cp.second); - layout.cell (f->second).set_ghost_cell (false); - f->second = seen; - } - } - } - - db::CellMapping cm; - cm.create_multi_mapping_full (layout, target_cells, **m, source_cells); - layout.copy_tree_shapes (**m, cm); - - } - - // Warn about cells that could not be resolved - for (std::map::iterator f = foreign_cells.begin (); f != foreign_cells.end (); ++f) { - if (f->second != seen && layout.cell (f->second).is_ghost_cell ()) { - importer.warn (tl::sprintf (tl::to_string (tr ("Could not find a substitution layout for foreign cell '%s'")), - f->first)); - } - } + importer.read (m_stream, layout, *state); } - state.finish (layout); + state->finish (layout); - m_layer_map = state.layer_map (); + m_layer_map = state->layer_map (); return m_layer_map; } diff --git a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h index 94721a5fe..38c953194 100644 --- a/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h +++ b/src/plugins/streamers/lefdef/db_plugin/dbLEFImporter.h @@ -122,7 +122,7 @@ public: } /** - * @brief Gets the + * @brief Gets the macros map * * The map maps the macro name to the macro description. */ @@ -131,6 +131,14 @@ public: return m_macros; } + /** + * @brief Inserts a macro description for a name + */ + const MacroDesc *insert_macro (const std::string &mn, const MacroDesc &m) + { + return &m_macros.insert (std::make_pair (mn, m)).first->second; + } + /** * @brief Finishes reading a LEF file * diff --git a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc index 032ad31eb..b2dfae449 100644 --- a/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc +++ b/src/plugins/streamers/lefdef/db_plugin/gsiDeclDbLEFDEF.cc @@ -997,6 +997,10 @@ gsi::Class decl_lefdef_config ("db", "LEFDEFReaderConfi "See \\read_lef_with_def for details about this property.\n" "\n" "This property has been added in version 0.27.\n" + ) + + // special attribute to implement LEF caching + gsi::method ("lef_context_enabled=", &db::LEFDEFReaderOptions::set_lef_context_enabled, + "@hide\n" ), "@brief Detailed LEF/DEF reader options\n" "This class is a aggregate belonging to the \\LoadLayoutOptions class. It provides options for the LEF/DEF reader. " diff --git a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc index 7e128f9c7..8f27111a6 100644 --- a/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc +++ b/src/plugins/streamers/lefdef/unit_tests/dbLEFDEFImportTests.cc @@ -25,6 +25,7 @@ #include "dbWriter.h" #include "dbDEFImporter.h" #include "dbLEFImporter.h" +#include "dbCommonReader.h" #include "tlUnitTest.h" #include "dbTestSupport.h" @@ -59,10 +60,13 @@ static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *f tl::Extractor ex (filename); - db::LEFDEFReaderState ld (&options, layout, fn_path); + db::LoadLayoutOptions other_options; + db::LEFDEFReaderState ld (&options); + ld.ensure_lef_importer (1); + ld.init (layout, fn_path, other_options); ld.set_conflict_resolution_mode (cc_mode); - db::DEFImporter imp; + db::DEFImporter imp (1); bool any_def = false; bool any_lef = false; @@ -92,8 +96,7 @@ static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *f ex.read_word_or_quoted (f); fn += f; - tl::InputStream stream (fn); - imp.read_lef (stream, layout, ld); + ld.read_lef (fn, layout); any_lef = true; @@ -134,7 +137,7 @@ static db::LayerMap read (db::Layout &layout, const char *lef_dir, const char *f } if (! any_def && any_lef) { - imp.finish_lef (layout); + ld.finish_lef (layout); } ld.finish (layout); @@ -518,6 +521,9 @@ TEST(109_foreigncell) options.set_macro_resolution_mode (2); run_test (_this, "foreigncell", "gds:foreign.gds+lef:in_tech.lef+lef:in.lef+def:in.def", "au_always_foreign.oas.gz", options, false); + + // no macros -> warning + run_test (_this, "foreigncell", "gds:macros.gds+lef:in_tech.lef+def:in.def", "au_no_macros.oas.gz", options, false); } TEST(110_lefpins) @@ -895,6 +901,21 @@ TEST(132_issue1307_pin_names) run_test (_this, "issue-1307c", "lef:in.lef+def:in.def", "au.oas", opt, false); } +/* +TODO: need to clarify first, if invalid via specs should be errors +TEST(133_unknown_vias_are_errors) +{ + db::LEFDEFReaderOptions opt = default_options (); + + try { + run_test (_this, "invalid_via", "lef:tech.lef+def:comp_invalid_via.def", "au.oas", opt, false); + EXPECT_EQ (true, false); + } catch (db::LEFDEFReaderException &ex) { + EXPECT_EQ (ex.msg ().find ("Invalid via name"), size_t (0)); + } +} +*/ + TEST(200_lefdef_plugin) { db::Layout ly; @@ -1100,3 +1121,35 @@ TEST(214_issue1877) db::compare_layouts (_this, ly, fn_path + "au.oas", db::WriteOAS); } +// multi-DEF reader support (issue-2014) +TEST(215_multiDEF) +{ + std::string fn_path (tl::testdata ()); + fn_path += "/lefdef/multi_def/"; + + db::Layout ly; + + db::LoadLayoutOptions opt; + // anything else will not make much sense + opt.get_options ().cell_conflict_resolution = db::CellConflictResolution::RenameCell; + + // Test "set_option_by_name" + opt.set_option_by_name ("lefdef_config.lef_context_enabled", true); + opt.set_option_by_name ("lefdef_config.map_file", "layers.map"); + opt.set_option_by_name ("lefdef_config.read_lef_with_def", true); + + const char *files[] = { + "main.def", + "comp_a.def", + "comp_b.def", + "comp_c.def" + }; + + for (const char **fn = files; fn != files + sizeof (files) / sizeof (files[0]); ++fn) { + tl::InputStream is (fn_path + *fn); + db::Reader reader (is); + reader.read (ly, opt); + } + + db::compare_layouts (_this, ly, fn_path + "au.oas", db::WriteOAS); +} diff --git a/testdata/lefdef/foreigncell/au_no_macros.oas.gz b/testdata/lefdef/foreigncell/au_no_macros.oas.gz new file mode 100644 index 000000000..98458d212 Binary files /dev/null and b/testdata/lefdef/foreigncell/au_no_macros.oas.gz differ diff --git a/testdata/lefdef/foreigncell/macros.gds b/testdata/lefdef/foreigncell/macros.gds new file mode 100644 index 000000000..6bf27d072 Binary files /dev/null and b/testdata/lefdef/foreigncell/macros.gds differ diff --git a/testdata/lefdef/invalid_via/comp_invalid_via.def b/testdata/lefdef/invalid_via/comp_invalid_via.def new file mode 100644 index 000000000..1eb475daf --- /dev/null +++ b/testdata/lefdef/invalid_via/comp_invalid_via.def @@ -0,0 +1,16 @@ +VERSION 5.8 ; +DIVIDERCHAR "/" ; +BUSBITCHARS "[]" ; +DESIGN comp_invalid_via ; +UNITS DISTANCE MICRONS 1000 ; +DIEAREA ( 0 0 ) ( 10000 2000 ) ; + +SPECIALNETS 2 ; + - VGND ( PIN VGND ) ( * VNB ) ( * VGND ) + USE GROUND + + ROUTED met1 480 + SHAPE FOLLOWPIN ( 0 1000 ) ( 2000 1000 ) + NEW met1 0 + SHAPE STRIPE ( 2000 1000 ) via1 + + ROUTED met2 480 + SHAPE FOLLOWPIN ( 2000 1000 ) ( 4000 1000 ) + NEW met2 0 + SHAPE STRIPE ( 4000 1000 ) invalid_via ; +END SPECIALNETS +END DESIGN + diff --git a/testdata/lefdef/invalid_via/tech.lef b/testdata/lefdef/invalid_via/tech.lef new file mode 100644 index 000000000..cae5bcd54 --- /dev/null +++ b/testdata/lefdef/invalid_via/tech.lef @@ -0,0 +1,89 @@ + +VERSION 5.7 ; + +BUSBITCHARS "[]" ; +DIVIDERCHAR "/" ; +USEMINSPACING OBS OFF ; + +UNITS + DATABASE MICRONS 1000 ; +END UNITS + +MANUFACTURINGGRID 0.005 ; + +LAYER met1 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; + PITCH 0.34 ; + OFFSET 0.17 ; + WIDTH 0.14 ; +END met1 + +LAYER via + TYPE CUT ; + WIDTH 0.15 ; + SPACING 0.17 ; +END via + +LAYER met2 + TYPE ROUTING ; + DIRECTION VERTICAL ; + PITCH 0.46 ; + OFFSET 0.23 ; + WIDTH 0.14 ; +END met2 + +LAYER via2 + TYPE CUT ; + WIDTH 0.2 ; + SPACING 0.2 ; +END via2 + +LAYER met3 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; + PITCH 0.68 ; + OFFSET 0.34 ; + WIDTH 0.3 ; +END met3 + +VIA via1 DEFAULT + LAYER via ; + RECT -0.075 -0.075 0.075 0.075 ; + LAYER met1 ; + RECT -0.16 -0.24 0.16 0.24 ; + LAYER met2 ; + RECT -0.13 -0.24 0.13 0.24 ; +END via1 + +VIARULE M1M2_PR GENERATE + LAYER met1 ; + ENCLOSURE 0.085 0.055 ; + LAYER met2 ; + ENCLOSURE 0.055 0.085 ; + LAYER via ; + RECT -0.075 -0.075 0.075 0.075 ; + SPACING 0.32 BY 0.32 ; +END M1M2_PR + +VIA via2 DEFAULT + LAYER via2 ; + RECT -0.1 -0.1 0.1 0.1 ; + LAYER met2 ; + RECT -0.14 -0.24 0.14 0.24 ; + LAYER met3 ; + RECT -0.165 -0.165 0.165 0.165 ; +END via2 + +VIARULE M2M3_PR GENERATE + LAYER met2 ; + ENCLOSURE 0.04 0.085 ; + LAYER met3 ; + ENCLOSURE 0.065 0.065 ; + LAYER via2 ; + RECT -0.1 -0.1 0.1 0.1 ; + SPACING 0.4 BY 0.4 ; +END M2M3_PR + +END LIBRARY + diff --git a/testdata/lefdef/multi_def/au.oas b/testdata/lefdef/multi_def/au.oas new file mode 100644 index 000000000..4f1002177 Binary files /dev/null and b/testdata/lefdef/multi_def/au.oas differ diff --git a/testdata/lefdef/multi_def/comp_a.def b/testdata/lefdef/multi_def/comp_a.def new file mode 100644 index 000000000..993901cc9 --- /dev/null +++ b/testdata/lefdef/multi_def/comp_a.def @@ -0,0 +1,20 @@ +VERSION 5.8 ; +DIVIDERCHAR "/" ; +BUSBITCHARS "[]" ; +DESIGN comp_a ; +UNITS DISTANCE MICRONS 1000 ; +DIEAREA ( 0 0 ) ( 10000 2000 ) ; + +VIAS 2 ; + - via1 + VIARULE M1M2_PR + CUTSIZE 150 150 + LAYERS met1 via met2 + CUTSPACING 170 170 + ENCLOSURE 85 165 55 85 + ROWCOL 2 2 ; + - via2 + VIARULE M2M3_PR + CUTSIZE 200 200 + LAYERS met2 via2 met3 + CUTSPACING 200 200 + ENCLOSURE 40 85 65 65 + ROWCOL 3 3 ; +END VIAS +SPECIALNETS 2 ; + - VGND ( PIN VGND ) ( * VNB ) ( * VGND ) + USE GROUND + + ROUTED met1 480 + SHAPE FOLLOWPIN ( 0 1000 ) ( 2000 1000 ) + NEW met1 0 + SHAPE STRIPE ( 2000 1000 ) via1 + + ROUTED met2 480 + SHAPE FOLLOWPIN ( 2000 1000 ) ( 4000 1000 ) + NEW met2 0 + SHAPE STRIPE ( 4000 1000 ) via2 ; +END SPECIALNETS +END DESIGN + diff --git a/testdata/lefdef/multi_def/comp_a.lef b/testdata/lefdef/multi_def/comp_a.lef new file mode 100644 index 000000000..a0d4cc454 --- /dev/null +++ b/testdata/lefdef/multi_def/comp_a.lef @@ -0,0 +1,12 @@ +VERSION 5.7 ; + NOWIREEXTENSIONATPIN ON ; + DIVIDERCHAR "/" ; + BUSBITCHARS "[]" ; +MACRO comp_a + CLASS BLOCK ; + FOREIGN comp_a ; + ORIGIN 0.000 0.000 ; + SIZE 10000.000 BY 2000.000 ; +END comp_a +END LIBRARY + diff --git a/testdata/lefdef/multi_def/comp_b.def b/testdata/lefdef/multi_def/comp_b.def new file mode 100644 index 000000000..06f13c99d --- /dev/null +++ b/testdata/lefdef/multi_def/comp_b.def @@ -0,0 +1,20 @@ +VERSION 5.8 ; +DIVIDERCHAR "/" ; +BUSBITCHARS "[]" ; +DESIGN comp_b ; +UNITS DISTANCE MICRONS 1000 ; +DIEAREA ( 0 0 ) ( 10000 2000 ) ; + +VIAS 2 ; + - via1 + VIARULE M1M2_PR + CUTSIZE 150 150 + LAYERS met1 via met2 + CUTSPACING 170 170 + ENCLOSURE 85 165 55 85 + ROWCOL 2 1 ; + - via2 + VIARULE M2M3_PR + CUTSIZE 200 200 + LAYERS met2 via2 met3 + CUTSPACING 200 200 + ENCLOSURE 40 85 65 65 + ROWCOL 3 2 ; +END VIAS +SPECIALNETS 2 ; + - VGND ( PIN VGND ) ( * VNB ) ( * VGND ) + USE GROUND + + ROUTED met1 480 + SHAPE FOLLOWPIN ( 0 1000 ) ( 2000 1000 ) + NEW met1 0 + SHAPE STRIPE ( 2000 1000 ) via1 + + ROUTED met2 480 + SHAPE FOLLOWPIN ( 2000 1000 ) ( 4000 1000 ) + NEW met2 0 + SHAPE STRIPE ( 4000 1000 ) via2 ; +END SPECIALNETS +END DESIGN + diff --git a/testdata/lefdef/multi_def/comp_b.lef b/testdata/lefdef/multi_def/comp_b.lef new file mode 100644 index 000000000..87a74672e --- /dev/null +++ b/testdata/lefdef/multi_def/comp_b.lef @@ -0,0 +1,12 @@ +VERSION 5.7 ; + NOWIREEXTENSIONATPIN ON ; + DIVIDERCHAR "/" ; + BUSBITCHARS "[]" ; +MACRO comp_b + CLASS BLOCK ; + FOREIGN comp_b ; + ORIGIN 0.000 0.000 ; + SIZE 10000.000 BY 2000.000 ; +END comp_b +END LIBRARY + diff --git a/testdata/lefdef/multi_def/comp_c.def b/testdata/lefdef/multi_def/comp_c.def new file mode 100644 index 000000000..6285587e0 --- /dev/null +++ b/testdata/lefdef/multi_def/comp_c.def @@ -0,0 +1,16 @@ +VERSION 5.8 ; +DIVIDERCHAR "/" ; +BUSBITCHARS "[]" ; +DESIGN comp_c ; +UNITS DISTANCE MICRONS 1000 ; +DIEAREA ( 0 0 ) ( 10000 2000 ) ; + +SPECIALNETS 2 ; + - VGND ( PIN VGND ) ( * VNB ) ( * VGND ) + USE GROUND + + ROUTED met1 480 + SHAPE FOLLOWPIN ( 0 1000 ) ( 2000 1000 ) + NEW met1 0 + SHAPE STRIPE ( 2000 1000 ) via1 + + ROUTED met2 480 + SHAPE FOLLOWPIN ( 2000 1000 ) ( 4000 1000 ) + NEW met2 0 + SHAPE STRIPE ( 4000 1000 ) via2 ; +END SPECIALNETS +END DESIGN + diff --git a/testdata/lefdef/multi_def/comp_c.lef b/testdata/lefdef/multi_def/comp_c.lef new file mode 100644 index 000000000..96ec21443 --- /dev/null +++ b/testdata/lefdef/multi_def/comp_c.lef @@ -0,0 +1,12 @@ +VERSION 5.7 ; + NOWIREEXTENSIONATPIN ON ; + DIVIDERCHAR "/" ; + BUSBITCHARS "[]" ; +MACRO comp_c + CLASS BLOCK ; + FOREIGN comp_c ; + ORIGIN 0.000 0.000 ; + SIZE 10000.000 BY 2000.000 ; +END comp_c +END LIBRARY + diff --git a/testdata/lefdef/multi_def/layers.map b/testdata/lefdef/multi_def/layers.map new file mode 100644 index 000000000..6def973d9 --- /dev/null +++ b/testdata/lefdef/multi_def/layers.map @@ -0,0 +1,7 @@ +met1 VIA,LEFPIN,PIN,SPNET,NET 1 0 +via VIA,LEFPIN,PIN,SPNET,NET 2 0 +met2 VIA,LEFPIN,PIN,SPNET,NET 3 0 +via2 VIA,LEFPIN,PIN,SPNET,NET 4 0 +met3 VIA,LEFPIN,PIN,SPNET,NET 5 0 + +DIEAREA ALL 100 0 diff --git a/testdata/lefdef/multi_def/main.def b/testdata/lefdef/multi_def/main.def new file mode 100644 index 000000000..2b5572a6c --- /dev/null +++ b/testdata/lefdef/multi_def/main.def @@ -0,0 +1,13 @@ +VERSION 5.8 ; +DIVIDERCHAR "/" ; +BUSBITCHARS "[]" ; +DESIGN caravel ; +UNITS DISTANCE MICRONS 1000 ; +DIEAREA ( 0 0 ) ( 10000 10000 ) ; +COMPONENTS 4 ; +- comp_a comp_a + PLACED ( 0 0 ) N ; +- comp_b comp_b + PLACED ( 0 2000 ) N ; +- comp_c comp_c + PLACED ( 0 4000 ) N ; +#- comp_d comp_d + PLACED ( 0 4000 ) N ; +END COMPONENTS +END DESIGN diff --git a/testdata/lefdef/multi_def/out.oas b/testdata/lefdef/multi_def/out.oas new file mode 100644 index 000000000..3b6e66367 Binary files /dev/null and b/testdata/lefdef/multi_def/out.oas differ diff --git a/testdata/lefdef/multi_def/tech.lef b/testdata/lefdef/multi_def/tech.lef new file mode 100644 index 000000000..cae5bcd54 --- /dev/null +++ b/testdata/lefdef/multi_def/tech.lef @@ -0,0 +1,89 @@ + +VERSION 5.7 ; + +BUSBITCHARS "[]" ; +DIVIDERCHAR "/" ; +USEMINSPACING OBS OFF ; + +UNITS + DATABASE MICRONS 1000 ; +END UNITS + +MANUFACTURINGGRID 0.005 ; + +LAYER met1 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; + PITCH 0.34 ; + OFFSET 0.17 ; + WIDTH 0.14 ; +END met1 + +LAYER via + TYPE CUT ; + WIDTH 0.15 ; + SPACING 0.17 ; +END via + +LAYER met2 + TYPE ROUTING ; + DIRECTION VERTICAL ; + PITCH 0.46 ; + OFFSET 0.23 ; + WIDTH 0.14 ; +END met2 + +LAYER via2 + TYPE CUT ; + WIDTH 0.2 ; + SPACING 0.2 ; +END via2 + +LAYER met3 + TYPE ROUTING ; + DIRECTION HORIZONTAL ; + PITCH 0.68 ; + OFFSET 0.34 ; + WIDTH 0.3 ; +END met3 + +VIA via1 DEFAULT + LAYER via ; + RECT -0.075 -0.075 0.075 0.075 ; + LAYER met1 ; + RECT -0.16 -0.24 0.16 0.24 ; + LAYER met2 ; + RECT -0.13 -0.24 0.13 0.24 ; +END via1 + +VIARULE M1M2_PR GENERATE + LAYER met1 ; + ENCLOSURE 0.085 0.055 ; + LAYER met2 ; + ENCLOSURE 0.055 0.085 ; + LAYER via ; + RECT -0.075 -0.075 0.075 0.075 ; + SPACING 0.32 BY 0.32 ; +END M1M2_PR + +VIA via2 DEFAULT + LAYER via2 ; + RECT -0.1 -0.1 0.1 0.1 ; + LAYER met2 ; + RECT -0.14 -0.24 0.14 0.24 ; + LAYER met3 ; + RECT -0.165 -0.165 0.165 0.165 ; +END via2 + +VIARULE M2M3_PR GENERATE + LAYER met2 ; + ENCLOSURE 0.04 0.085 ; + LAYER met3 ; + ENCLOSURE 0.065 0.065 ; + LAYER via2 ; + RECT -0.1 -0.1 0.1 0.1 ; + SPACING 0.4 BY 0.4 ; +END M2M3_PR + +END LIBRARY +