mirror of https://github.com/KLayout/klayout.git
Debugging, cleanup.
This commit is contained in:
parent
be79d3fbcd
commit
2ad11c0a35
|
|
@ -1192,7 +1192,13 @@ SpiceNetlistBuilder::subcircuit_captured (const std::string &nc_name)
|
|||
bool
|
||||
SpiceNetlistBuilder::process_element (tl::Extractor &ex, const std::string &prefix, const std::string &name)
|
||||
{
|
||||
// generic parse
|
||||
// low-level SPICE line parsing
|
||||
// - "element" will be the element characters ("R", "M", ...)
|
||||
// - "model" will be the model name if given or empty if no model name is there
|
||||
// - "value" will be the direct value (for "R", "C", "L"), usually also available as parameter "R", "C" or "L"
|
||||
// - "nn" will be a list of node (net) names
|
||||
// - "pv" will be a list of key/value pairs of the parameters
|
||||
|
||||
std::vector<std::string> nn;
|
||||
NetlistSpiceReader::parameters_type pv;
|
||||
std::string model;
|
||||
|
|
@ -1203,6 +1209,25 @@ SpiceNetlistBuilder::process_element (tl::Extractor &ex, const std::string &pref
|
|||
|
||||
model = mp_netlist->normalize_name (model);
|
||||
|
||||
// checks if the element is associated with a SPICE profile
|
||||
|
||||
const db::DeviceClass *dc = mp_delegate->device_class (element, model);
|
||||
if (dc) {
|
||||
|
||||
// check the number of terminals
|
||||
const std::vector<std::string> &to = dc->spice_profile (mp_delegate->profile ()).terminal_order;
|
||||
if (nn.size () != to.size ()) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Element '%s' bound to model '%s' in SPICE profile '%s' requires %d terminals, but got %d")),
|
||||
element, model, mp_delegate->profile (), int (to.size ()), int (nn.size ())));
|
||||
}
|
||||
|
||||
// indicates that we must not use the element name further
|
||||
element.clear ();
|
||||
|
||||
}
|
||||
|
||||
// obtains the db::Net objects from the net names
|
||||
|
||||
std::vector<db::Net *> nets;
|
||||
for (std::vector<std::string>::const_iterator i = nn.begin (); i != nn.end (); ++i) {
|
||||
nets.push_back (make_net (mp_delegate->translate_net_name (*i)));
|
||||
|
|
@ -1252,7 +1277,10 @@ SpiceNetlistBuilder::process_element (tl::Extractor &ex, const std::string &pref
|
|||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
// element to device translation
|
||||
return mp_delegate->element (mp_netlist_circuit, element, name, model, value, nets, pv);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,18 @@ NetlistSpiceReaderDelegate::set_read_all_parameters (bool f)
|
|||
m_read_all_parameters = f;
|
||||
}
|
||||
|
||||
bool
|
||||
NetlistSpiceReaderDelegate::legacy_mode () const
|
||||
{
|
||||
return m_legacy_mode;
|
||||
}
|
||||
|
||||
void
|
||||
NetlistSpiceReaderDelegate::set_legacy_mode (bool f)
|
||||
{
|
||||
m_legacy_mode = f;
|
||||
}
|
||||
|
||||
void NetlistSpiceReaderDelegate::set_netlist (db::Netlist *netlist, const std::string &profile)
|
||||
{
|
||||
m_options = NetlistSpiceReaderOptions ();
|
||||
|
|
@ -225,30 +237,7 @@ void NetlistSpiceReaderDelegate::parse_element (const std::string &s, std::strin
|
|||
def_values_per_element (element, pv);
|
||||
parse_element_components (s, nn, pv, variables);
|
||||
|
||||
auto dp = m_spice_profiles.end ();
|
||||
|
||||
if (! nn.empty ()) {
|
||||
dp = m_spice_profiles.find (std::make_pair (element, nn.back ()));
|
||||
}
|
||||
|
||||
if (dp != m_spice_profiles.end ()) {
|
||||
|
||||
// element bound to a SPICE element through the device class
|
||||
|
||||
model = dp->first.second;
|
||||
nn.pop_back ();
|
||||
|
||||
// indicates that we must not use the element name further
|
||||
element.clear ();
|
||||
|
||||
const std::vector<std::string> &to = dp->second->spice_profile (m_profile).terminal_order;
|
||||
|
||||
if (nn.size () != to.size ()) {
|
||||
error (tl::sprintf (tl::to_string (tr ("Element '%s' bound to model '%s' in SPICE profile '%s' requires %d terminals, but got %d")),
|
||||
element, model, m_profile, int (to.size ()), int (nn.size ())));
|
||||
}
|
||||
|
||||
} else if (element == "X") {
|
||||
if (! m_legacy_mode && element == "X") {
|
||||
|
||||
// subcircuit call:
|
||||
// Xname n1 n2 ... nn circuit [params]
|
||||
|
|
@ -432,7 +421,7 @@ private:
|
|||
}
|
||||
|
||||
static tl::Variant
|
||||
eval_parameter_expression (const std::string &expr, const std::string &name, const std::map<std::string, tl::Variant> ¶ms, const std::map<std::string, const db::DeviceParameterDefinition *> &all_params, double value)
|
||||
eval_parameter_expression (const std::string &name, const std::string &expr, const std::map<std::string, tl::Variant> ¶ms, const std::map<std::string, const db::DeviceParameterDefinition *> &all_params, double value)
|
||||
{
|
||||
// shortcuts
|
||||
if (expr.empty ()) {
|
||||
|
|
@ -505,6 +494,16 @@ static db::DeviceClass *bootstrap_device_class (db::Netlist *netlist, const std:
|
|||
return 0;
|
||||
}
|
||||
|
||||
const db::DeviceClass *NetlistSpiceReaderDelegate::device_class (const std::string &element, const std::string &model) const
|
||||
{
|
||||
auto dp = m_spice_profiles.find (std::make_pair (element, model));
|
||||
if (dp != m_spice_profiles.end ()) {
|
||||
return dp->second;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector<db::Net *> &nets, const std::map<std::string, tl::Variant> ¶ms)
|
||||
{
|
||||
std::string cn = model;
|
||||
|
|
@ -600,32 +599,45 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
std::map<std::string, std::string>::const_iterator id;
|
||||
if ((id = dict.find (p->first)) != dict.end ()) {
|
||||
|
||||
if (p->second) {
|
||||
device->set_parameter_value (p->second->id (), eval_parameter_expression (p->first, id->second, params, all_params, value));
|
||||
} else {
|
||||
tl::Variant pv = eval_parameter_expression (p->first, id->second, params, all_params, value);
|
||||
device->set_parameter_value_create (p->first, pv, false, default_from_value (pv));
|
||||
tl::Variant pv = eval_parameter_expression (p->first, id->second, params, all_params, value);
|
||||
if (! pv.is_nil ()) {
|
||||
if (p->second) {
|
||||
device->set_parameter_value (p->second->id (), pv);
|
||||
} else {
|
||||
device->set_parameter_value_create (p->first, pv, false, default_from_value (pv));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (p->second && p->second->is_primary () && (id = dict.find ("*!")) != dict.end ()) {
|
||||
|
||||
device->set_parameter_value (p->second->id (), eval_parameter_expression (p->first, id->second, params, all_params, value));
|
||||
tl::Variant pv = eval_parameter_expression (p->first, id->second, params, all_params, value);
|
||||
if (! pv.is_nil ()) {
|
||||
device->set_parameter_value (p->second->id (), pv);
|
||||
}
|
||||
|
||||
} else if (p->second && ! p->second->is_primary () && (id = dict.find ("*?")) != dict.end ()) {
|
||||
|
||||
device->set_parameter_value (p->second->id (), eval_parameter_expression (p->first, id->second, params, all_params, value));
|
||||
tl::Variant pv = eval_parameter_expression (p->first, id->second, params, all_params, value);
|
||||
if (! pv.is_nil ()) {
|
||||
device->set_parameter_value (p->second->id (), pv);
|
||||
}
|
||||
|
||||
} else if (p->second && (id = dict.find ("**")) != dict.end ()) {
|
||||
|
||||
device->set_parameter_value (p->second->id (), eval_parameter_expression (p->first, id->second, params, all_params, value));
|
||||
tl::Variant pv = eval_parameter_expression (p->first, id->second, params, all_params, value);
|
||||
if (! pv.is_nil ()) {
|
||||
device->set_parameter_value (p->second->id (), pv);
|
||||
}
|
||||
|
||||
} else if ((id = dict.find ("*")) != dict.end ()) {
|
||||
|
||||
if (p->second) {
|
||||
device->set_parameter_value (p->second->id (), eval_parameter_expression (p->first, id->second, params, all_params, value));
|
||||
} else {
|
||||
tl::Variant pv = eval_parameter_expression (p->first, id->second, params, all_params, value);
|
||||
device->set_parameter_value_create (p->first, pv, false, default_from_value (pv));
|
||||
tl::Variant pv = eval_parameter_expression (p->first, id->second, params, all_params, value);
|
||||
if (! pv.is_nil ()) {
|
||||
if (p->second) {
|
||||
device->set_parameter_value (p->second->id (), pv);
|
||||
} else {
|
||||
device->set_parameter_value_create (p->first, pv, false, default_from_value (pv));
|
||||
}
|
||||
}
|
||||
|
||||
} else if (m_read_all_parameters) {
|
||||
|
|
|
|||
|
|
@ -106,6 +106,26 @@ public:
|
|||
*/
|
||||
void set_read_all_parameters (bool f);
|
||||
|
||||
/**
|
||||
* @brief Gets a flag indicating legacy mode
|
||||
*
|
||||
* This flag controls the parsing of SPICE lines in the default
|
||||
* implementation of "parse_element". With this flag set to true (default),
|
||||
* the elements are restricted to their original meaning, i.e. "R"
|
||||
* can have two or three nets and a direct value. With this flag set to
|
||||
* false, all elements can have any number of terminals, the last entry
|
||||
* is the model and a direct value is not allowed.
|
||||
*
|
||||
* This flag allows configuring the SPICE reading without
|
||||
* need to reimplementing a delegate.
|
||||
*/
|
||||
bool legacy_mode () const;
|
||||
|
||||
/**
|
||||
* @brief Sets a flag indicating legacy mode
|
||||
*/
|
||||
void set_legacy_mode (bool f);
|
||||
|
||||
/**
|
||||
* @brief Called when the netlist reading starts
|
||||
*/
|
||||
|
|
@ -211,11 +231,27 @@ public:
|
|||
*/
|
||||
void apply_parameter_scaling (db::Device *device) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the device class registered for an element/model combination
|
||||
*
|
||||
* Returns a null pointer if no such combination is registered.
|
||||
*/
|
||||
const db::DeviceClass *device_class (const std::string &element, const std::string &model) const;
|
||||
|
||||
/**
|
||||
* @brief Gets the SPICE profile
|
||||
*/
|
||||
const std::string &profile () const
|
||||
{
|
||||
return m_profile;
|
||||
}
|
||||
|
||||
private:
|
||||
db::Netlist *mp_netlist;
|
||||
NetlistSpiceReaderOptions m_options;
|
||||
std::string m_profile;
|
||||
bool m_read_all_parameters;
|
||||
bool m_legacy_mode;
|
||||
std::map<std::pair<std::string, std::string>, const db::DeviceClass *> m_spice_profiles;
|
||||
|
||||
void def_values_per_element (const std::string &element, std::map<std::string, tl::Variant> &pv);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ static const char *s_not_connect_prefix = "nc_";
|
|||
// --------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceWriterDelegate::NetlistSpiceWriterDelegate ()
|
||||
: mp_writer (0), mp_netlist (0), m_write_all_parameters (true)
|
||||
: mp_writer (0), mp_netlist (0), m_write_all_parameters (false)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
|
|||
|
|
@ -418,12 +418,60 @@ Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "Netl
|
|||
gsi::method_ext ("parse_element", &parse_element_fb, "@hide") +
|
||||
gsi::method_ext ("control_statement", &control_statement_fb, "@hide") +
|
||||
gsi::method_ext ("translate_net_name", &translate_net_name_fb, "@hide") +
|
||||
// provided for test purposes
|
||||
gsi::method ("read_all_parameters=", &NetlistSpiceReaderDelegateImpl::read_all_parameters,
|
||||
"@hide\n"
|
||||
gsi::method ("read_all_parameters", &NetlistSpiceReaderDelegateImpl::read_all_parameters,
|
||||
"@brief Gets a flag indicating whether to read all SPICE parameters\n"
|
||||
"\n"
|
||||
"With this flag set to false, unknown device parameters are ignored. Parameters are declared by the "
|
||||
"device classes present in the netlist, before the reader is used. SPICE profiles can be used to "
|
||||
"configure the parameters accepted by the SPICE reader, in addition to fixed and pre-defined parameters. "
|
||||
"With 'read_all_parameters' set to true, all parameters are read, even if they are not declared as fixed "
|
||||
"parameters by the device classes or as incoming parameters in their SPICE profiles.\n"
|
||||
"\n"
|
||||
"Note, that reimplementing \"element\" may change that behavior.\n"
|
||||
"\n"
|
||||
"This attribute has been introduced in version 0.31.0."
|
||||
) +
|
||||
gsi::method ("read_all_parameters=", &NetlistSpiceReaderDelegateImpl::set_read_all_parameters, gsi::arg ("f"),
|
||||
"@hide\n"
|
||||
"@brief Sets a flag indicating whether to read all SPICE parameters\n"
|
||||
"See \\read_all_parameters for details about this attribute.\n"
|
||||
"\n"
|
||||
"This attribute has been introduced in version 0.31.0."
|
||||
) +
|
||||
gsi::method ("legacy_mode=", &NetlistSpiceReaderDelegateImpl::legacy_mode,
|
||||
"@brief Gets a flag indicating legacy mode\n"
|
||||
"\n"
|
||||
"This flag controls the parsing of SPICE lines in the default\n"
|
||||
"implementation of \"parse_element\". With this flag set to true (default),\n"
|
||||
"the elements are restricted to their original SPICE meaning, i.e. \"R\"\n"
|
||||
"can have two or three nets and a direct value. With this flag set to\n"
|
||||
"false, all elements can have any number of terminals, the last entry\n"
|
||||
"is the model and a direct value is not allowed. In non-legacy mode, all\n"
|
||||
"elements share the same notation and can be bound to devices through\n"
|
||||
"SPICE profiles without restrictions.\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"* Allowed always:\n"
|
||||
"R A B MODEL R=1k\n"
|
||||
"\n"
|
||||
"* Allowed with legacy_mode=true (default):\n"
|
||||
"R A B 1k\n"
|
||||
"\n"
|
||||
"* Allowed with legacy_mode=false with a device class named 'MODEL' and\n"
|
||||
"* accepting 4 terminals for the 'R' element in its SPICE profile:\n"
|
||||
"R A B C D MODEL A=17.5 P=105\n"
|
||||
"@/code\n"
|
||||
"\n"
|
||||
"This flag allows configuring the SPICE reading without\n"
|
||||
"need for reimplementing a delegate. Note, that reimplementing\n"
|
||||
"\"parse_element\" may change that behavior.\n"
|
||||
"\n"
|
||||
"This attribute has been introduced in version 0.31.0."
|
||||
) +
|
||||
gsi::method ("legacy_mode=", &NetlistSpiceReaderDelegateImpl::set_legacy_mode, gsi::arg ("f"),
|
||||
"@brief Sets a flag indicating legacy mode\n"
|
||||
"See \\legacy_mode for details about this attribute.\n"
|
||||
"\n"
|
||||
"This attribute has been introduced in version 0.31.0."
|
||||
) +
|
||||
gsi::callback ("start", &NetlistSpiceReaderDelegateImpl::start, &NetlistSpiceReaderDelegateImpl::cb_start, gsi::arg ("netlist"),
|
||||
"@brief This method is called when the reader starts reading a netlist\n"
|
||||
|
|
@ -537,6 +585,11 @@ Class<NetlistSpiceReaderDelegateImpl> db_NetlistSpiceReaderDelegate ("db", "Netl
|
|||
"\n"
|
||||
"See \\NetlistSpiceReader for more details.\n"
|
||||
"\n"
|
||||
"'SPICE profiles' significantly reduce the need for reimplementing a SPICE reader delegate. "
|
||||
"These profiles are descriptors attached to device classes. By pre-registering device classes "
|
||||
"in the netlist before reading SPICE files, these classes will describe themselves and how they "
|
||||
"like to be read from and written to SPICE files.\n"
|
||||
"\n"
|
||||
"This class has been introduced in version 0.26. Profiles have been added to the device classes in version 0.31.0."
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue