Layer mapping scheme enhancements, UI enhancements

* A map file can now be specified and should be read relative
  to the technology
* Giving a map file will disable the pattern-based mapping
* UI enhanced accordingly
* Added UI option to configure macro resolution mode
* Bugfix: LEF files have not been read relative to the tech
* Bugfix: Some options where not persistent
This commit is contained in:
Matthias Koefferlein 2020-05-02 00:14:06 +02:00
parent 0e68b910fa
commit 97022a3a87
8 changed files with 781 additions and 632 deletions

View File

@ -77,7 +77,7 @@ LEFDEFReaderOptions::LEFDEFReaderOptions ()
m_special_routing_suffix (""), m_special_routing_suffix (""),
m_special_routing_datatype (0), m_special_routing_datatype (0),
m_separate_groups (false), m_separate_groups (false),
m_consider_map_file (true), m_map_file (),
m_macro_resolution_mode (0) m_macro_resolution_mode (0)
{ {
// .. nothing yet .. // .. nothing yet ..
@ -125,7 +125,7 @@ LEFDEFReaderOptions::LEFDEFReaderOptions (const LEFDEFReaderOptions &d)
m_special_routing_suffix (d.m_special_routing_suffix), m_special_routing_suffix (d.m_special_routing_suffix),
m_special_routing_datatype (d.m_special_routing_datatype), m_special_routing_datatype (d.m_special_routing_datatype),
m_separate_groups (d.m_separate_groups), m_separate_groups (d.m_separate_groups),
m_consider_map_file (d.m_consider_map_file), m_map_file (d.m_map_file),
m_macro_resolution_mode (d.m_macro_resolution_mode), m_macro_resolution_mode (d.m_macro_resolution_mode),
m_lef_files (d.m_lef_files) m_lef_files (d.m_lef_files)
{ {
@ -298,45 +298,6 @@ LEFDEFReaderState::read_map_file (const std::string &path, db::Layout &layout)
} }
} }
void
LEFDEFReaderState::import_map_file_heuristics (const std::string &main_path, db::Layout &layout)
{
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), layout);
} else if (map_files.size () == 1) {
read_map_file (tl::combine_path (input_dir, map_files.front ()), layout);
}
} catch (tl::Exception &ex) {
// ignore read errors on map file (this is a heuristics!)
tl::error << ex.msg ();
}
}
std::pair <bool, unsigned int> std::pair <bool, unsigned int>
LEFDEFReaderState::open_layer (db::Layout &layout, const std::string &n, LayerPurpose purpose) LEFDEFReaderState::open_layer (db::Layout &layout, const std::string &n, LayerPurpose purpose)
{ {

View File

@ -515,14 +515,14 @@ public:
m_separate_groups = f; m_separate_groups = f;
} }
bool consider_map_file () const const std::string &map_file () const
{ {
return m_consider_map_file; return m_map_file;
} }
void set_consider_map_file (bool f) void set_map_file (const std::string &f)
{ {
m_consider_map_file = f; m_map_file = f;
} }
/** /**
@ -584,7 +584,7 @@ private:
std::string m_special_routing_suffix; std::string m_special_routing_suffix;
int m_special_routing_datatype; int m_special_routing_datatype;
bool m_separate_groups; bool m_separate_groups;
bool m_consider_map_file; std::string m_map_file;
unsigned int m_macro_resolution_mode; unsigned int m_macro_resolution_mode;
std::vector<std::string> m_lef_files; std::vector<std::string> m_lef_files;
}; };
@ -637,14 +637,6 @@ public:
*/ */
void read_map_file (const std::string &path, db::Layout &layout); void read_map_file (const std::string &path, db::Layout &layout);
/**
* @brief Imports a .map file present next to the input files
* "main_path" path of an input file (DEF). If a suitable .map file is found at this path,
* it is loaded into the reader state object. This will eventually disable any other layer
* mapping except for the global layers (region, outline, placement blockage).
*/
void import_map_file_heuristics (const std::string &main_path, db::Layout &layout);
/** /**
* @brief Sets the layer map * @brief Sets the layer map
*/ */

View File

@ -31,6 +31,7 @@
#include "dbDEFImporter.h" #include "dbDEFImporter.h"
#include "dbLEFDEFImporter.h" #include "dbLEFDEFImporter.h"
#include "dbLayoutUtils.h" #include "dbLayoutUtils.h"
#include "dbTechnology.h"
namespace db namespace db
{ {
@ -103,14 +104,32 @@ public:
{ {
return "LEFDEF"; return "LEFDEF";
} }
private: private:
tl::InputStream &m_stream; tl::InputStream &m_stream;
db::LayerMap m_layer_map; db::LayerMap m_layer_map;
std::string correct_path (const std::string &fn) std::string correct_path (const std::string &fn, const db::Layout &layout)
{ {
if (! tl::is_absolute (fn)) { if (! tl::is_absolute (fn)) {
// if a technology is given and the file can be found in the technology's base path, take it
// from there.
std::string tn = layout.meta_info_value ("technology");
const db::Technology *tech = 0;
if (! tn.empty ()) {
tech = db::Technologies::instance ()->technology_by_name (tn);
}
if (tech && ! tech->base_path ().empty ()) {
std::string new_fn = tl::combine_path (tech->base_path (), fn);
if (tl::file_exists (new_fn)) {
return new_fn;
}
}
return tl::combine_path (m_stream.absolute_path (), fn); return tl::combine_path (m_stream.absolute_path (), fn);
} else { } else {
return fn; return fn;
} }
@ -126,8 +145,8 @@ private:
db::LEFDEFReaderState state (lefdef_options, layout); db::LEFDEFReaderState state (lefdef_options, layout);
if (lefdef_options->consider_map_file ()) { if (! lefdef_options->map_file ().empty ()) {
state.import_map_file_heuristics (m_stream.absolute_path (), layout); state.read_map_file (correct_path (lefdef_options->map_file (), layout), layout);
} }
layout.dbu (lefdef_options->dbu ()); layout.dbu (lefdef_options->dbu ());
@ -140,7 +159,7 @@ private:
for (std::vector<std::string>::const_iterator l = lefdef_options->begin_lef_files (); l != lefdef_options->end_lef_files (); ++l) { for (std::vector<std::string>::const_iterator l = lefdef_options->begin_lef_files (); l != lefdef_options->end_lef_files (); ++l) {
std::string lp = correct_path (*l); std::string lp = correct_path (*l, layout);
tl::InputStream lef_stream (lp); tl::InputStream lef_stream (lp);
tl::log << tl::to_string (tr ("Reading")) << " " << lp; tl::log << tl::to_string (tr ("Reading")) << " " << lp;
@ -159,7 +178,7 @@ private:
for (std::vector<std::string>::const_iterator l = lefdef_options->begin_lef_files (); l != lefdef_options->end_lef_files (); ++l) { for (std::vector<std::string>::const_iterator l = lefdef_options->begin_lef_files (); l != lefdef_options->end_lef_files (); ++l) {
std::string lp = correct_path (*l); std::string lp = correct_path (*l, layout);
tl::InputStream lef_stream (lp); tl::InputStream lef_stream (lp);
tl::log << tl::to_string (tr ("Reading")) << " " << lp; tl::log << tl::to_string (tr ("Reading")) << " " << lp;
@ -167,8 +186,7 @@ private:
} }
// Additionally read all LEF files next to the DEF file and if there is a single .map file // Additionally read all LEF files next to the DEF 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 ()); std::string input_dir = tl::absolute_path (m_stream.absolute_path ());
@ -205,6 +223,39 @@ private:
} }
}; };
namespace {
struct MacroResolutionModeConverter
{
public:
MacroResolutionModeConverter ()
{
m_values.push_back ("default");
m_values.push_back ("always-lef");
m_values.push_back ("always-cellref");
}
std::string to_string (unsigned int v) const
{
return v < m_values.size () ? m_values[v] : std::string ();
}
void from_string (const std::string &s, unsigned int &v) const
{
v = 0;
for (unsigned int i = 0; i < (unsigned int) m_values.size (); ++i) {
if (m_values [i] == s) {
v = i;
}
}
}
private:
std::vector<std::string> m_values;
};
}
class LEFDEFFormatDeclaration class LEFDEFFormatDeclaration
: public db::StreamFormatDeclaration : public db::StreamFormatDeclaration
{ {
@ -280,7 +331,10 @@ class LEFDEFFormatDeclaration
tl::make_member (&LEFDEFReaderOptions::produce_special_routing, &LEFDEFReaderOptions::set_produce_special_routing, "produce-special-routing") + tl::make_member (&LEFDEFReaderOptions::produce_special_routing, &LEFDEFReaderOptions::set_produce_special_routing, "produce-special-routing") +
tl::make_member (&LEFDEFReaderOptions::special_routing_suffix, &LEFDEFReaderOptions::set_special_routing_suffix, "special-routing-suffix") + tl::make_member (&LEFDEFReaderOptions::special_routing_suffix, &LEFDEFReaderOptions::set_special_routing_suffix, "special-routing-suffix") +
tl::make_member (&LEFDEFReaderOptions::special_routing_datatype, &LEFDEFReaderOptions::set_special_routing_datatype, "special-routing-datatype") + tl::make_member (&LEFDEFReaderOptions::special_routing_datatype, &LEFDEFReaderOptions::set_special_routing_datatype, "special-routing-datatype") +
tl::make_member (&LEFDEFReaderOptions::begin_lef_files, &LEFDEFReaderOptions::end_lef_files, &LEFDEFReaderOptions::push_lef_file, "lef-files") tl::make_member (&LEFDEFReaderOptions::begin_lef_files, &LEFDEFReaderOptions::end_lef_files, &LEFDEFReaderOptions::push_lef_file, "lef-files") +
tl::make_member (&LEFDEFReaderOptions::macro_resolution_mode, &LEFDEFReaderOptions::set_macro_resolution_mode, "macro-resolution-mode", MacroResolutionModeConverter ()) +
tl::make_member (&LEFDEFReaderOptions::separate_groups, &LEFDEFReaderOptions::set_separate_groups, "separate-groups") +
tl::make_member (&LEFDEFReaderOptions::map_file, &LEFDEFReaderOptions::set_map_file, "map-file")
); );
} }
}; };

View File

@ -347,7 +347,7 @@ LEFImporter::read_geometries (db::Layout &layout, db::Cell &cell, LayerPurpose p
std::string vn = get (); std::string vn = get ();
db::Cell *vc = reader_state ()->via_cell (vn); db::Cell *vc = reader_state ()->via_cell (vn);
if (! vc) { if (! vc) {
warn ("Unknown via: " + vn); warn (tl::to_string (tr ("Unknown via: ")) + vn);
} }
if (iterate) { if (iterate) {
@ -549,7 +549,7 @@ LEFImporter::read_viadef_by_geometry (Layout &layout, db::Cell &cell, ViaDesc &v
via_desc.m1 = routing_layers[0]; via_desc.m1 = routing_layers[0];
via_desc.m2 = routing_layers[1]; via_desc.m2 = routing_layers[1];
} else { } else {
warn ("Can't determine routing layers for via: " + n); warn (tl::to_string (tr ("Can't determine routing layers for via: ")) + n);
} }
reset_cellname (); reset_cellname ();
@ -724,6 +724,11 @@ void
LEFImporter::read_macro (Layout &layout) LEFImporter::read_macro (Layout &layout)
{ {
std::string mn = get (); std::string mn = get ();
if (m_macros_by_name.find (mn) != m_macros_by_name.end ()) {
error (tl::to_string (tr ("Duplicate MACRO name: ")) + mn);
}
set_cellname (mn); set_cellname (mn);
db::Cell &cell = layout.cell (layout.add_cell ()); db::Cell &cell = layout.cell (layout.add_cell ());
@ -813,20 +818,11 @@ LEFImporter::read_macro (Layout &layout)
} else if (test ("FOREIGN")) { } else if (test ("FOREIGN")) {
if (foreign_cell) { if (foreign_cell) {
error ("Duplicate FOREIGN definition"); error (tl::to_string (tr ("Duplicate FOREIGN definition")));
} }
std::string cn = get (); std::string cn = get ();
db::cell_index_type ci;
std::pair<bool, db::cell_index_type> c = layout.cell_by_name (cn.c_str ());
if (c.first) {
ci = c.second;
} else {
ci = layout.add_cell (cn.c_str ());
layout.cell (ci).set_ghost_cell (true);
}
db::Point vec; db::Point vec;
db::FTrans ft; db::FTrans ft;
if (! peek (";")) { if (! peek (";")) {
@ -836,11 +832,24 @@ LEFImporter::read_macro (Layout &layout)
expect (";"); expect (";");
foreign_cell = &layout.cell (ci); if (options ().macro_resolution_mode () != 1) {
// What is the definition of the FOREIGN transformation?
// Guessing: this transformation moves the lower-left origin to 0,0 db::cell_index_type ci;
foreign_trans = db::Trans (db::Point () - vec) * db::Trans (ft); std::pair<bool, db::cell_index_type> c = layout.cell_by_name (cn.c_str ());
foreign_name = cn; if (c.first) {
ci = c.second;
} else {
ci = layout.add_cell (cn.c_str ());
layout.cell (ci).set_ghost_cell (true);
}
foreign_cell = &layout.cell (ci);
// What is the definition of the FOREIGN transformation?
// Guessing: this transformation moves the lower-left origin to 0,0
foreign_trans = db::Trans (db::Point () - vec) * db::Trans (ft);
foreign_name = cn;
}
} else if (test ("OBS")) { } else if (test ("OBS")) {
@ -855,38 +864,42 @@ LEFImporter::read_macro (Layout &layout)
} }
if (options ().macro_resolution_mode () == 1 || (! foreign_cell && options ().macro_resolution_mode () != 2)) { if (! foreign_cell) {
// actually implement the real cell if (options ().macro_resolution_mode () != 2) {
layout.rename_cell (cell.cell_index (), mn.c_str ()); // actually implement the real cell
std::pair <bool, unsigned int> dl = open_layer (layout, std::string (), Outline); layout.rename_cell (cell.cell_index (), mn.c_str ());
if (dl.first) {
cell.shapes (dl.second).insert (db::Box (-origin, -origin + size));
}
m_macros_by_name.insert (std::make_pair (mn, std::make_pair (&cell, db::Trans ()))); std::pair <bool, unsigned int> dl = open_layer (layout, std::string (), Outline);
if (dl.first) {
cell.shapes (dl.second).insert (db::Box (-origin, -origin + size));
}
} else if (! foreign_cell) { m_macros_by_name.insert (std::make_pair (mn, std::make_pair (&cell, db::Trans ())));
// macro resolution mode #2 (always create a MACRO reference, no LEF geometry)
db::cell_index_type ci;
std::pair<bool, db::cell_index_type> c = layout.cell_by_name (mn.c_str ());
if (c.first) {
ci = c.second;
} else { } else {
ci = layout.add_cell (mn.c_str ());
layout.cell (ci).set_ghost_cell (true);
}
layout.delete_cell (cell.cell_index ()); // macro resolution mode #2 (always create a MACRO reference, no LEF geometry)
m_macros_by_name.insert (std::make_pair (mn, std::make_pair (&layout.cell (ci), db::Trans ())));
db::cell_index_type ci;
std::pair<bool, db::cell_index_type> c = layout.cell_by_name (mn.c_str ());
if (c.first) {
ci = c.second;
} else {
ci = layout.add_cell (mn.c_str ());
layout.cell (ci).set_ghost_cell (true);
}
layout.delete_cell (cell.cell_index ());
m_macros_by_name.insert (std::make_pair (mn, std::make_pair (&layout.cell (ci), db::Trans ())));
}
} else if (foreign_name != mn) { } else if (foreign_name != mn) {
warn ("FOREIGN name differs from MACRO name in macro: " + mn); warn (tl::to_string (tr ("FOREIGN name differs from MACRO name in macro: ")) + mn);
layout.rename_cell (cell.cell_index (), mn.c_str ()); layout.rename_cell (cell.cell_index (), mn.c_str ());

View File

@ -466,17 +466,20 @@ gsi::Class<db::LEFDEFReaderOptions> decl_lefdef_config ("db", "LEFDEFReaderConfi
"\n" "\n"
"This property has been added in version 0.26.5.\n" "This property has been added in version 0.26.5.\n"
) + ) +
gsi::method ("consider_map_file", &db::LEFDEFReaderOptions::consider_map_file, gsi::method ("map_file", &db::LEFDEFReaderOptions::map_file,
"@brief Gets a value indicating whether to consider reading .map files next to DEF files.\n" "@brief Gets the layer map file to use.\n"
"If this property is set to true (the default), the DEF reader will look for .map files next to the " "If a layer map file is given, the reader will pull the layer mapping from this file. The layer mapping rules "
"DEF file. If such a file is found, it will be used to map DEF layers to layout layers. The layer mapping " "specified in the reader options are ignored in this case. These are the name suffix rules for vias, blockages, routing, "
"settings specified in the reader options are ignored in this case.\n" "special routing, pins etc. and the corresponding datatype rules. The \\layer_map attribute will also be ignored. "
"\n"
"The layer map file path will be resolved relative to the technology base path if the LEF/DEF reader options are "
"used in the context of a technology.\n"
"\n" "\n"
"This property has been added in version 0.26.5.\n" "This property has been added in version 0.26.5.\n"
) + ) +
gsi::method ("consider_map_file=", &db::LEFDEFReaderOptions::set_consider_map_file, gsi::arg ("flag"), gsi::method ("map_file=", &db::LEFDEFReaderOptions::set_map_file, gsi::arg ("file"),
"@brief Sets a value indicating whether to consider reading .map files next to DEF files.\n" "@brief Sets the layer map file to use.\n"
"See \\consider_map_file for details about this property.\n" "See \\map_file for details about this property.\n"
"\n" "\n"
"This property has been added in version 0.26.5.\n" "This property has been added in version 0.26.5.\n"
) + ) +
@ -513,6 +516,21 @@ gsi::Class<db::LEFDEFReaderOptions> decl_lefdef_config ("db", "LEFDEFReaderConfi
"@brief Detailed LEF/DEF reader options\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. " "This class is a aggregate belonging to the \\LoadLayoutOptions class. It provides options for the LEF/DEF reader. "
"These options have been placed into a separate class to account for their complexity." "These options have been placed into a separate class to account for their complexity."
"\n"
"This class specifically handles layer mapping. This is the process of generating layer names or GDS layer/datatypes "
"from LEF/DEF layers and purpose combinations. There are basically two ways: to use a map file or to use pattern-based production rules.\n"
"\n"
"To use a layer map file, set the \\map_file attribute to the name of the layer map file. The layer map "
"file lists the GDS layer and datatype numbers to generate for the geometry.\n"
"\n"
"The pattern-based approach will use the layer name and attach a purpose-dependent suffix to it. "
"Use the ..._suffix attributes to specify this suffix. For routing, the corresponding attribute is \\routing_suffix for example. "
"A purpose can also be mapped to a specific GDS datatype using the corresponding ..._datatype attributes.\n"
"The decorated or undecorated names are looked up in a layer mapping table in the next step. The layer mapping table "
"is specified using the \\layer_map attribute. This table can be used to map layer names to specific GDS layers "
"by using entries of the form 'NAME: layer-number'.\n"
"\n"
"If a layer map file is present, the pattern-based attributes are ignored.\n"
); );
} }

View File

@ -363,7 +363,7 @@ LEFDEFReaderOptionsEditor::LEFDEFReaderOptionsEditor (QWidget *parent)
connect (del_lef_files, SIGNAL (clicked ()), this, SLOT (del_lef_files_clicked ())); connect (del_lef_files, SIGNAL (clicked ()), this, SLOT (del_lef_files_clicked ()));
connect (move_lef_files_up, SIGNAL (clicked ()), this, SLOT (move_lef_files_up_clicked ())); connect (move_lef_files_up, SIGNAL (clicked ()), this, SLOT (move_lef_files_up_clicked ()));
connect (move_lef_files_down, SIGNAL (clicked ()), this, SLOT (move_lef_files_down_clicked ())); connect (move_lef_files_down, SIGNAL (clicked ()), this, SLOT (move_lef_files_down_clicked ()));
connect (consider_map_file, SIGNAL (stateChanged (int)), this, SLOT (consider_map_file_state_changed ())); connect (browse_mapfile, SIGNAL (clicked ()), this, SLOT (browse_mapfile_clicked ()));
lay::activate_help_links (help_label); lay::activate_help_links (help_label);
} }
@ -451,7 +451,8 @@ LEFDEFReaderOptionsEditor::commit (db::FormatSpecificReaderOptions *options, con
data->set_labels_suffix (tl::to_string (suffix_labels->text ())); data->set_labels_suffix (tl::to_string (suffix_labels->text ()));
data->set_labels_datatype (datatype_labels->text ().toInt ()); data->set_labels_datatype (datatype_labels->text ().toInt ());
data->set_separate_groups (separate_groups->isChecked ()); data->set_separate_groups (separate_groups->isChecked ());
data->set_consider_map_file (consider_map_file->isChecked ()); data->set_map_file (tl::to_string (mapfile_path->text ()));
data->set_macro_resolution_mode (foreign_mode->currentIndex ());
data->clear_lef_files (); data->clear_lef_files ();
for (int i = 0; i < lef_files->count (); ++i) { for (int i = 0; i < lef_files->count (); ++i) {
@ -512,7 +513,9 @@ LEFDEFReaderOptionsEditor::setup (const db::FormatSpecificReaderOptions *options
suffix_labels->setText (tl::to_qstring (data->labels_suffix ())); suffix_labels->setText (tl::to_qstring (data->labels_suffix ()));
datatype_labels->setText (QString::number (data->labels_datatype ())); datatype_labels->setText (QString::number (data->labels_datatype ()));
separate_groups->setChecked (data->separate_groups ()); separate_groups->setChecked (data->separate_groups ());
consider_map_file->setChecked (data->consider_map_file ()); mapfile_path->setText (tl::to_qstring (data->map_file ()));
layer_map_mode->setCurrentIndex (data->map_file ().empty () ? 1 : 0);
foreign_mode->setCurrentIndex (data->macro_resolution_mode ());
checkbox_changed (); checkbox_changed ();
@ -529,13 +532,6 @@ LEFDEFReaderOptionsEditor::setup (const db::FormatSpecificReaderOptions *options
} }
} }
void
LEFDEFReaderOptionsEditor::consider_map_file_state_changed ()
{
warn1->setVisible (consider_map_file->isChecked ());
warn2->setVisible (consider_map_file->isChecked ());
}
void void
LEFDEFReaderOptionsEditor::checkbox_changed () LEFDEFReaderOptionsEditor::checkbox_changed ()
{ {
@ -563,6 +559,22 @@ LEFDEFReaderOptionsEditor::checkbox_changed ()
datatype_labels->setEnabled (produce_labels->isChecked ()); datatype_labels->setEnabled (produce_labels->isChecked ());
} }
void
LEFDEFReaderOptionsEditor::browse_mapfile_clicked ()
{
std::string title, filters;
title = tl::to_string (QObject::tr ("Select Layer Map File"));
filters = tl::to_string (QObject::tr ("LEF/DEF layer map files (*.map);;All files (*)"));
QString file = QFileDialog::getOpenFileName (this, tl::to_qstring (title), QString (), tl::to_qstring (filters));
if (! file.isNull ()) {
if (mp_tech) {
mapfile_path->setText (tl::to_qstring (mp_tech->correct_path (tl::to_string (file))));
} else {
mapfile_path->setText (file);
}
}
}
void void
LEFDEFReaderOptionsEditor::add_lef_file_clicked () LEFDEFReaderOptionsEditor::add_lef_file_clicked ()
{ {

View File

@ -98,7 +98,7 @@ private slots:
void del_lef_files_clicked (); void del_lef_files_clicked ();
void move_lef_files_up_clicked (); void move_lef_files_up_clicked ();
void move_lef_files_down_clicked (); void move_lef_files_down_clicked ();
void consider_map_file_state_changed (); void browse_mapfile_clicked ();
private: private:
tl::weak_ptr<db::Technology> mp_tech; tl::weak_ptr<db::Technology> mp_tech;