mirror of https://github.com/KLayout/klayout.git
Spice reader enhancements
Basic goal is to align ngspice and KLayout Spice format comprehension. ".options scale" was implemented together with a number of other patches. Consistency has been confirmed with respect to these features and formula evaluation.
This commit is contained in:
parent
6955d7d0c7
commit
db8f9d5bcb
|
|
@ -146,7 +146,7 @@ public:
|
|||
* @brief Creates an empty device parameter definition
|
||||
*/
|
||||
DeviceParameterDefinition ()
|
||||
: m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true), m_si_scaling (1.0)
|
||||
: m_name (), m_description (), m_default_value (0.0), m_id (0), m_is_primary (true), m_si_scaling (1.0), m_geo_scaling (0.0)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -154,8 +154,8 @@ public:
|
|||
/**
|
||||
* @brief Creates a device parameter definition with the given name and description
|
||||
*/
|
||||
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true, double si_scaling = 1.0)
|
||||
: m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary), m_si_scaling (si_scaling)
|
||||
DeviceParameterDefinition (const std::string &name, const std::string &description, double default_value = 0.0, bool is_primary = true, double si_scaling = 1.0, double geo_scaling = 0.0)
|
||||
: m_name (name), m_description (description), m_default_value (default_value), m_id (0), m_is_primary (is_primary), m_si_scaling (si_scaling), m_geo_scaling (geo_scaling)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
}
|
||||
|
|
@ -192,18 +192,10 @@ public:
|
|||
m_description = d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the parameter default value
|
||||
*/
|
||||
double default_value () const
|
||||
{
|
||||
return m_default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the SI unit scaling factor
|
||||
*
|
||||
* Some parameters are given in micrometers for example. This
|
||||
* Some parameters are given in micrometers - for example W and L of MOS devices. This
|
||||
* scaling factor gives the translation to SI units (1e-6 for micrometers).
|
||||
*/
|
||||
double si_scaling () const
|
||||
|
|
@ -212,7 +204,43 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the parameter description
|
||||
* @brief Set the SI unit scaling factor
|
||||
*/
|
||||
void set_si_scaling (double s)
|
||||
{
|
||||
m_si_scaling = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the geometry scaling exponent
|
||||
*
|
||||
* The geometry scaling exponent is used for example when applying .option scale
|
||||
* in Spice reading. It is 0 for "no scaling", 1 for linear scaling and 2 for
|
||||
* quadratic scaling.
|
||||
*/
|
||||
double geo_scaling_exponent () const
|
||||
{
|
||||
return m_geo_scaling;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the geometry scaling exponent
|
||||
*/
|
||||
void set_geo_scaling_exponent (double e)
|
||||
{
|
||||
m_geo_scaling = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the parameter default value
|
||||
*/
|
||||
double default_value () const
|
||||
{
|
||||
return m_default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the parameter default value
|
||||
*/
|
||||
void set_default_value (double d)
|
||||
{
|
||||
|
|
@ -267,6 +295,7 @@ private:
|
|||
size_t m_id;
|
||||
bool m_is_primary;
|
||||
double m_si_scaling;
|
||||
double m_geo_scaling;
|
||||
|
||||
void set_id (size_t id)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -487,10 +487,10 @@ DeviceClassResistor::DeviceClassResistor ()
|
|||
equivalent_terminal_id (terminal_id_A, terminal_id_B);
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("R", "Resistance (Ohm)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("L", "Length (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("W", "Width (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("L", "Length (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("W", "Width (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12, 2.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
|
|
@ -526,8 +526,8 @@ DeviceClassCapacitor::DeviceClassCapacitor ()
|
|||
equivalent_terminal_id (terminal_id_A, terminal_id_B);
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("C", "Capacitance (Farad)", 0.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12, 2.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
|
|
@ -580,8 +580,8 @@ DeviceClassDiode::DeviceClassDiode ()
|
|||
add_terminal_definition (db::DeviceTerminalDefinition ("A", "Anode"));
|
||||
add_terminal_definition (db::DeviceTerminalDefinition ("C", "Cathode"));
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("A", "Area (square micrometer)", 0.0, false, 1e-12, 2.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("P", "Perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
|
|
@ -608,12 +608,12 @@ DeviceClassMOS3Transistor::DeviceClassMOS3Transistor ()
|
|||
add_terminal_definition (db::DeviceTerminalDefinition ("D", "Drain"));
|
||||
equivalent_terminal_id (terminal_id_D, terminal_id_S);
|
||||
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0, true, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0, true, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false, 1e-12));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false, 1e-12));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("L", "Gate length (micrometer)", 0.0, true, 1e-6, 1.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("W", "Gate width (micrometer)", 0.0, true, 1e-6, 1.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AS", "Source area (square micrometer)", 0.0, false, 1e-12, 2.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AD", "Drain area (square micrometer)", 0.0, false, 1e-12, 2.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PS", "Source perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PD", "Drain perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -886,12 +886,12 @@ DeviceClassBJT3Transistor::DeviceClassBJT3Transistor ()
|
|||
add_terminal_definition (db::DeviceTerminalDefinition ("E", "Emitter"));
|
||||
|
||||
// NOTE: the emitter area and the emitter count are the primary parameters
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AE", "Emitter area (square micrometer)", 0.0, true, 1e-12));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PE", "Emitter perimeter (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AB", "Base area (square micrometer)", 0.0, false, 1e-12));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PB", "Base perimeter (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AC", "Collector area (square micrometer)", 0.0, false, 1e-12));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PC", "Collector perimeter (micrometer)", 0.0, false, 1e-6));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AE", "Emitter area (square micrometer)", 0.0, true, 1e-12, 2.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PE", "Emitter perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AB", "Base area (square micrometer)", 0.0, false, 1e-12, 2.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PB", "Base perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("AC", "Collector area (square micrometer)", 0.0, false, 1e-12, 2.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("PC", "Collector perimeter (micrometer)", 0.0, false, 1e-6, 1.0));
|
||||
add_parameter_definition (db::DeviceParameterDefinition ("NE", "Emitter count", 1.0, true));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -388,6 +388,7 @@ private:
|
|||
void read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector<db::Net *> &nets);
|
||||
void read_circuit (tl::Extractor &ex, const std::string &name);
|
||||
bool read_card ();
|
||||
void read_options (tl::Extractor &ex);
|
||||
void ensure_circuit ();
|
||||
std::string get_line ();
|
||||
void error (const std::string &msg);
|
||||
|
|
@ -614,6 +615,10 @@ SpiceCircuitDict::read_card ()
|
|||
std::string nc = read_name (ex, mp_netlist);
|
||||
read_circuit (ex, nc);
|
||||
|
||||
} else if (ex.test_without_case (".options")) {
|
||||
|
||||
read_options (ex);
|
||||
|
||||
} else if (ex.test_without_case (".ends")) {
|
||||
|
||||
return true;
|
||||
|
|
@ -680,6 +685,56 @@ SpiceCircuitDict::read_card ()
|
|||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SpiceCircuitDict::read_options (tl::Extractor &ex)
|
||||
{
|
||||
while (! ex.at_end ()) {
|
||||
|
||||
std::string n;
|
||||
ex.read_word_or_quoted (n, allowed_name_chars);
|
||||
n = tl::to_lower_case (n);
|
||||
|
||||
double v = 0.0;
|
||||
std::string w;
|
||||
if (ex.test ("=")) {
|
||||
if (ex.try_read (v)) {
|
||||
// take value
|
||||
} else {
|
||||
// skip until end or next space
|
||||
ex.skip ();
|
||||
while (! ex.at_end () && ! isspace (*ex)) {
|
||||
++ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: further options?
|
||||
const double min_value = 1e-18;
|
||||
if (n == "scale") {
|
||||
if (v > min_value) {
|
||||
mp_delegate->options ().scale = v;
|
||||
}
|
||||
} else if (n == "defad") {
|
||||
if (v > min_value) {
|
||||
mp_delegate->options ().defad = v;
|
||||
}
|
||||
} else if (n == "defas") {
|
||||
if (v > min_value) {
|
||||
mp_delegate->options ().defas = v;
|
||||
}
|
||||
} else if (n == "defl") {
|
||||
if (v > min_value) {
|
||||
mp_delegate->options ().defl = v;
|
||||
}
|
||||
} else if (n == "defw") {
|
||||
if (v > min_value) {
|
||||
mp_delegate->options ().defw = v;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SpiceCircuitDict::ensure_circuit ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -86,6 +86,18 @@ static std::string unescape_name (const std::string &n)
|
|||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceReaderOptions::NetlistSpiceReaderOptions ()
|
||||
{
|
||||
scale = 1.0;
|
||||
defad = 0.0;
|
||||
defas = 0.0;
|
||||
// ngspice defaults:
|
||||
defw = 100e-6;
|
||||
defl = 100e-6;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceReaderDelegate::NetlistSpiceReaderDelegate ()
|
||||
: mp_netlist (0)
|
||||
{
|
||||
|
|
@ -107,7 +119,7 @@ void NetlistSpiceReaderDelegate::finish (db::Netlist * /*netlist*/)
|
|||
// .. nothing yet ..
|
||||
}
|
||||
|
||||
bool NetlistSpiceReaderDelegate::control_statement(const std::string & /*line*/)
|
||||
bool NetlistSpiceReaderDelegate::control_statement (const std::string & /*line*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -196,7 +208,8 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
|
|||
if (ex.try_read_word (n) && ex.test ("=")) {
|
||||
|
||||
// a parameter
|
||||
pv [mp_netlist ? mp_netlist->normalize_name (n) : tl::to_upper_case (n)] = read_value (ex, variables);
|
||||
std::string pn = mp_netlist ? mp_netlist->normalize_name (n) : tl::to_upper_case (n);
|
||||
pv [pn] = read_value (ex, variables);
|
||||
|
||||
} else {
|
||||
|
||||
|
|
@ -231,8 +244,21 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
|
|||
}
|
||||
}
|
||||
|
||||
void NetlistSpiceReaderDelegate::def_values_per_element (const std::string &element, std::map<std::string, tl::Variant> &pv)
|
||||
{
|
||||
if (element == "M") {
|
||||
|
||||
pv.insert (std::make_pair ("W", m_options.defw));
|
||||
pv.insert (std::make_pair ("L", m_options.defl));
|
||||
pv.insert (std::make_pair ("AD", m_options.defad));
|
||||
pv.insert (std::make_pair ("AS", m_options.defas));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void NetlistSpiceReaderDelegate::parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector<std::string> &nn, std::map<std::string, tl::Variant> &pv, const std::map<std::string, tl::Variant> &variables)
|
||||
{
|
||||
def_values_per_element (element, pv);
|
||||
parse_element_components (s, nn, pv, variables);
|
||||
|
||||
// interpret the parameters according to the code
|
||||
|
|
@ -337,6 +363,8 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
std::map<std::string, tl::Variant> params = pv;
|
||||
std::vector<size_t> terminal_order;
|
||||
|
||||
size_t defp = std::numeric_limits<size_t>::max ();
|
||||
|
||||
double mult = 1.0;
|
||||
auto mp = params.find ("M");
|
||||
if (mp != params.end ()) {
|
||||
|
|
@ -378,9 +406,20 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
error (tl::to_string (tr ("A 'R' element requires two or three nets")));
|
||||
}
|
||||
|
||||
// Apply multiplier
|
||||
// Apply multiplier (divider, according to ngspice manual)
|
||||
value /= mult;
|
||||
|
||||
defp = db::DeviceClassResistor::param_id_R;
|
||||
|
||||
// Apply multiplier to other parameters
|
||||
static const char *scale_params[] = { "A", "P", "W" };
|
||||
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
|
||||
auto p = params.find (scale_params [i]);
|
||||
if (p != params.end ()) {
|
||||
p->second = tl::Variant (p->second.to_double () * mult);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (element == "L") {
|
||||
|
||||
if (nets.size () == 2) {
|
||||
|
|
@ -398,9 +437,11 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
error (tl::to_string (tr ("A 'L' element requires two nets")));
|
||||
}
|
||||
|
||||
// Apply multiplier
|
||||
// Apply multiplier (divider, according to ngspice manual)
|
||||
value /= mult;
|
||||
|
||||
defp = db::DeviceClassInductor::param_id_L;
|
||||
|
||||
} else if (element == "C") {
|
||||
|
||||
if (nets.size () == 2) {
|
||||
|
|
@ -432,6 +473,17 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
// Apply multiplier
|
||||
value *= mult;
|
||||
|
||||
defp = db::DeviceClassCapacitor::param_id_C;
|
||||
|
||||
// Apply multiplier to other parameters
|
||||
static const char *scale_params[] = { "A", "P" };
|
||||
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
|
||||
auto p = params.find (scale_params [i]);
|
||||
if (p != params.end ()) {
|
||||
p->second = tl::Variant (p->second.to_double () * mult);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (element == "D") {
|
||||
|
||||
if (cls) {
|
||||
|
|
@ -445,10 +497,13 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
cls = make_device_class<db::DeviceClassDiode> (circuit, cn);
|
||||
}
|
||||
|
||||
// Apply multiplier to "A"
|
||||
auto p = params.find ("A");
|
||||
if (p != params.end ()) {
|
||||
p->second = tl::Variant (p->second.to_double () * mult);
|
||||
// Apply multiplier
|
||||
static const char *scale_params[] = { "A", "P" };
|
||||
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
|
||||
auto p = params.find (scale_params [i]);
|
||||
if (p != params.end ()) {
|
||||
p->second = tl::Variant (p->second.to_double () * mult);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (element == "Q") {
|
||||
|
|
@ -479,10 +534,13 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
}
|
||||
}
|
||||
|
||||
// Apply multiplier to "AE"
|
||||
auto p = params.find ("AE");
|
||||
if (p != params.end ()) {
|
||||
p->second = tl::Variant (p->second.to_double () * mult);
|
||||
// Apply multiplier
|
||||
static const char *scale_params[] = { "AE", "PE", "AB", "PB", "AC", "PC" };
|
||||
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
|
||||
auto p = params.find (scale_params [i]);
|
||||
if (p != params.end ()) {
|
||||
p->second = tl::Variant (p->second.to_double () * mult);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (element == "M") {
|
||||
|
|
@ -502,10 +560,13 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
}
|
||||
}
|
||||
|
||||
// Apply multiplier to "W"
|
||||
auto p = params.find ("W");
|
||||
if (p != params.end ()) {
|
||||
p->second = tl::Variant (p->second.to_double () * mult);
|
||||
// Apply multiplier
|
||||
static const char *scale_params[] = { "W", "AD", "AS", "PD", "PS" };
|
||||
for (size_t i = 0; i < sizeof (scale_params) / sizeof (scale_params[0]); ++i) {
|
||||
auto p = params.find (scale_params [i]);
|
||||
if (p != params.end ()) {
|
||||
p->second = tl::Variant (p->second.to_double () * mult);
|
||||
}
|
||||
}
|
||||
|
||||
// issue #1304
|
||||
|
|
@ -537,23 +598,18 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
|
|||
}
|
||||
|
||||
|
||||
size_t defp = std::numeric_limits<size_t>::max ();
|
||||
if (dynamic_cast<db::DeviceClassCapacitor *> (cls)) {
|
||||
defp = db::DeviceClassCapacitor::param_id_C;
|
||||
} else if (dynamic_cast<db::DeviceClassResistor *> (cls)) {
|
||||
defp = db::DeviceClassResistor::param_id_R;
|
||||
} else if (dynamic_cast<db::DeviceClassInductor *> (cls)) {
|
||||
defp = db::DeviceClassInductor::param_id_L;
|
||||
}
|
||||
|
||||
std::vector<db::DeviceParameterDefinition> &pd = cls->parameter_definitions_non_const ();
|
||||
for (std::vector<db::DeviceParameterDefinition>::iterator i = pd.begin (); i != pd.end (); ++i) {
|
||||
auto v = params.find (i->name ());
|
||||
double pv = 0.0;
|
||||
if (v != params.end ()) {
|
||||
device->set_parameter_value (i->id (), v->second.to_double () / i->si_scaling ());
|
||||
pv = v->second.to_double ();
|
||||
} else if (i->id () == defp) {
|
||||
device->set_parameter_value (i->id (), value / i->si_scaling ());
|
||||
pv = value;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
device->set_parameter_value (i->id (), pv / i->si_scaling () * pow (m_options.scale, i->geo_scaling_exponent ()));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,14 @@ class Circuit;
|
|||
class DeviceClass;
|
||||
class Device;
|
||||
|
||||
struct DB_PUBLIC NetlistSpiceReaderOptions
|
||||
{
|
||||
NetlistSpiceReaderOptions ();
|
||||
|
||||
double scale;
|
||||
double defad, defas, defw, defl;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A delegate to handle various forms of devices and translates them
|
||||
*
|
||||
|
|
@ -55,6 +63,22 @@ public:
|
|||
NetlistSpiceReaderDelegate ();
|
||||
virtual ~NetlistSpiceReaderDelegate ();
|
||||
|
||||
/**
|
||||
* @brief Gets the reader options
|
||||
*/
|
||||
NetlistSpiceReaderOptions &options ()
|
||||
{
|
||||
return m_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the reader options
|
||||
*/
|
||||
const NetlistSpiceReaderOptions &options () const
|
||||
{
|
||||
return m_options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Called when the netlist reading starts
|
||||
*/
|
||||
|
|
@ -122,7 +146,6 @@ public:
|
|||
|
||||
/**
|
||||
* @brief Reads a set of string components and parameters from the string
|
||||
* A special key "param:" is recognized for starting a parameter list.
|
||||
*/
|
||||
void parse_element_components (const std::string &s, std::vector<std::string> &strings, std::map<std::string, tl::Variant> &pv, const std::map<std::string, tl::Variant> &variables);
|
||||
|
||||
|
|
@ -162,6 +185,9 @@ public:
|
|||
|
||||
private:
|
||||
db::Netlist *mp_netlist;
|
||||
NetlistSpiceReaderOptions m_options;
|
||||
|
||||
void def_values_per_element (const std::string &element, std::map<std::string, tl::Variant> &pv);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ static bool to_bool (const tl::Variant &v)
|
|||
}
|
||||
// ------------------------------------------------------------------------------------------------------
|
||||
|
||||
NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const variables_type *vars)
|
||||
NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const variables_type *vars, double def_scale)
|
||||
: m_def_scale (def_scale)
|
||||
{
|
||||
static variables_type empty_variables;
|
||||
mp_variables = vars ? vars : &empty_variables;
|
||||
|
|
@ -202,7 +203,7 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool *
|
|||
*status = true;
|
||||
}
|
||||
|
||||
double f = 1.0;
|
||||
double f = m_def_scale;
|
||||
if (*ex == 't' || *ex == 'T') {
|
||||
f = 1e12;
|
||||
} else if (*ex == 'g' || *ex == 'G') {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class DB_PUBLIC NetlistSpiceReaderExpressionParser
|
|||
public:
|
||||
typedef std::map<std::string, tl::Variant> variables_type;
|
||||
|
||||
NetlistSpiceReaderExpressionParser (const variables_type *vars);
|
||||
NetlistSpiceReaderExpressionParser (const variables_type *vars, double def_scale = 1.0);
|
||||
|
||||
tl::Variant read (tl::Extractor &ex) const;
|
||||
tl::Variant read (const std::string &s) const;
|
||||
|
|
@ -54,6 +54,7 @@ public:
|
|||
|
||||
private:
|
||||
const variables_type *mp_variables;
|
||||
double m_def_scale;
|
||||
|
||||
tl::Variant read_atomic_value (tl::Extractor &ex, bool *status) const;
|
||||
tl::Variant read_dot_expr (tl::Extractor &ex, bool *status) const;
|
||||
|
|
|
|||
|
|
@ -790,19 +790,20 @@ Class<db::DeviceTerminalDefinition> decl_dbDeviceTerminalDefinition ("db", "Devi
|
|||
"This class has been added in version 0.26."
|
||||
);
|
||||
|
||||
static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value, bool is_primary, double si_scaling)
|
||||
static db::DeviceParameterDefinition *new_parameter_definition (const std::string &name, const std::string &description, double default_value, bool is_primary, double si_scaling, double geo_scaling_exponent)
|
||||
{
|
||||
return new db::DeviceParameterDefinition (name, description, default_value, is_primary, si_scaling);
|
||||
return new db::DeviceParameterDefinition (name, description, default_value, is_primary, si_scaling, geo_scaling_exponent);
|
||||
}
|
||||
|
||||
Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "DeviceParameterDefinition",
|
||||
gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), gsi::arg ("is_primary", true), gsi::arg ("si_scaling", 1.0),
|
||||
gsi::constructor ("new", &gsi::new_parameter_definition, gsi::arg ("name"), gsi::arg ("description", std::string ()), gsi::arg ("default_value", 0.0), gsi::arg ("is_primary", true), gsi::arg ("si_scaling", 1.0), gsi::arg ("geo_scaling_exponent", 0.0),
|
||||
"@brief Creates a new parameter definition.\n"
|
||||
"@param name The name of the parameter\n"
|
||||
"@param description The human-readable description\n"
|
||||
"@param default_value The initial value\n"
|
||||
"@param is_primary True, if the parameter is a primary parameter (see \\is_primary=)\n"
|
||||
"@param si_scaling The scaling factor to SI units\n"
|
||||
"@param geo_scaling_exponent Indicates how the parameter scales with geometrical scaling (0: no scaling, 1.0: linear, 2.0: quadratic)\n"
|
||||
) +
|
||||
gsi::method ("name", &db::DeviceParameterDefinition::name,
|
||||
"@brief Gets the name of the parameter."
|
||||
|
|
@ -834,7 +835,26 @@ Class<db::DeviceParameterDefinition> decl_dbDeviceParameterDefinition ("db", "De
|
|||
) +
|
||||
gsi::method ("si_scaling", &db::DeviceParameterDefinition::si_scaling,
|
||||
"@brief Gets the scaling factor to SI units.\n"
|
||||
"For parameters in micrometers for example, this factor will be 1e-6."
|
||||
"For parameters in micrometers - for example W and L of MOS devices - this factor can be set to 1e-6 to reflect "
|
||||
"the unit."
|
||||
) +
|
||||
gsi::method ("si_scaling=", &db::DeviceParameterDefinition::set_si_scaling,
|
||||
"@brief Sets the scaling factor to SI units.\n"
|
||||
"\n"
|
||||
"This setter has been added in version 0.28.6."
|
||||
) +
|
||||
gsi::method ("geo_scaling_exponent", &db::DeviceParameterDefinition::geo_scaling_exponent,
|
||||
"@brief Gets the geometry scaling exponent.\n"
|
||||
"This value is used when applying '.options scale' in the SPICE reader for example. "
|
||||
"It is zero for 'no scaling', 1.0 for linear scaling and 2.0 for quadratic scaling.\n"
|
||||
"\n"
|
||||
"This attribute has been added in version 0.28.6."
|
||||
) +
|
||||
gsi::method ("geo_scaling_exponent=", &db::DeviceParameterDefinition::set_geo_scaling_exponent,
|
||||
"@brief Sets the geometry scaling exponent.\n"
|
||||
"See \\geo_scaling_exponent for details.\n"
|
||||
"\n"
|
||||
"This attribute has been added in version 0.28.6."
|
||||
) +
|
||||
gsi::method ("id", &db::DeviceParameterDefinition::id,
|
||||
"@brief Gets the ID of the parameter.\n"
|
||||
|
|
|
|||
|
|
@ -704,16 +704,50 @@ TEST(18_XSchemOutput)
|
|||
" subcircuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY3 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
|
||||
"end;\n"
|
||||
"circuit 'PMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=2.685,PD=1.79);\n"
|
||||
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=1.305,AD=0.87,PS=10.74,PD=7.16);\n"
|
||||
"end;\n"
|
||||
"circuit 'NMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=2.685,PD=1.79);\n"
|
||||
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=1.305,AD=0.87,PS=10.74,PD=7.16);\n"
|
||||
"end;\n"
|
||||
"circuit 'NMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=3.58,PD=1.79);\n"
|
||||
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.87,AD=0.435,PS=7.16,PD=3.58);\n"
|
||||
"end;\n"
|
||||
"circuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=3.58,PD=1.79);\n"
|
||||
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.87,AD=0.435,PS=7.16,PD=3.58);\n"
|
||||
"end;\n"
|
||||
);
|
||||
}
|
||||
|
||||
TEST(19_ngspice_ref)
|
||||
{
|
||||
db::Netlist nl;
|
||||
|
||||
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader19.cir");
|
||||
|
||||
db::NetlistSpiceReader reader;
|
||||
tl::InputStream is (path);
|
||||
reader.read (is, nl);
|
||||
|
||||
EXPECT_EQ (nl.to_string (),
|
||||
"circuit .TOP ();\n"
|
||||
" subcircuit 'PMOS4_STANDARD(L=0.15,NF=4,W=1.5)' XPMOS (D=Q,G=I,S=VDD,B=VDD);\n"
|
||||
" subcircuit 'NMOS4_STANDARD(L=0.15,NF=4,W=1.5)' XNMOS (D=Q,G=I,S=VSS,B=VSS);\n"
|
||||
" subcircuit 'NMOS4_STANDARD(L=0.15,NF=2,W=1.5)' XDUMMY0 (D=VSS,G=VSS,S=VSS,B=VSS);\n"
|
||||
" subcircuit 'NMOS4_STANDARD(L=0.15,NF=2,W=1.5)' XDUMMY1 (D=VSS,G=VSS,S=VSS,B=VSS);\n"
|
||||
" subcircuit 'PMOS4_STANDARD(L=0.15,NF=2,W=1.5)' XDUMMY2 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
|
||||
" subcircuit 'PMOS4_STANDARD(L=0.15,NF=2,W=1.5)' XDUMMY3 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
|
||||
"end;\n"
|
||||
"circuit 'PMOS4_STANDARD(L=0.15,NF=4,W=1.5)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=3.99,PD=2.66);\n"
|
||||
"end;\n"
|
||||
"circuit 'NMOS4_STANDARD(L=0.15,NF=4,W=1.5)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=3.99,PD=2.66);\n"
|
||||
"end;\n"
|
||||
"circuit 'NMOS4_STANDARD(L=0.15,NF=2,W=1.5)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__NFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=4.16,PD=2.08);\n"
|
||||
"end;\n"
|
||||
"circuit 'PMOS4_STANDARD(L=0.15,NF=2,W=1.5)' (D=D,G=G,S=S,B=B);\n"
|
||||
" device SKY130_FD_PR__PFET_01V8 M1 (S=S,G=G,D=D,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=4.16,PD=2.08);\n"
|
||||
"end;\n"
|
||||
);
|
||||
}
|
||||
|
|
@ -828,3 +862,18 @@ TEST(100_ExpressionParser)
|
|||
EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1\"", v), true);
|
||||
EXPECT_EQ (v.to_string (), "6");
|
||||
}
|
||||
|
||||
TEST(101_ExpressionParserWithDefScale)
|
||||
{
|
||||
std::map<std::string, tl::Variant> vars;
|
||||
vars["A"] = 17.5;
|
||||
|
||||
tl::Variant v;
|
||||
|
||||
db::NetlistSpiceReaderExpressionParser parser (&vars, 1e-3);
|
||||
|
||||
EXPECT_EQ (parser.read ("1.75").to_string (), "0.00175");
|
||||
EXPECT_EQ (parser.read ("-1.75u").to_string (), "-1.75e-06");
|
||||
EXPECT_EQ (parser.read ("1.75k").to_string (), "1750");
|
||||
EXPECT_EQ (parser.read ("2*A").to_string (), "0.035");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
* Test
|
||||
|
||||
.options scale=1e-6
|
||||
|
||||
.model sky130_fd_pr__pfet_01v8 NMOS level=8 version=3.3.0
|
||||
.model sky130_fd_pr__nfet_01v8 NMOS level=8 version=3.3.0
|
||||
|
||||
XXpmos Q I VDD VDD pmos4_standard w=1.5 l=0.15 nf=4
|
||||
XXnmos Q I VSS VSS nmos4_standard w=1.5 l=0.15 nf=4
|
||||
XXDUMMY0 VSS VSS VSS VSS nmos4_standard w=1.5 l=0.15 nf=2
|
||||
XXDUMMY1 VSS VSS VSS VSS nmos4_standard w=1.5 l=0.15 nf=2
|
||||
XXDUMMY2 VDD VDD VDD VDD pmos4_standard w=1.5 l=0.15 nf=2
|
||||
XXDUMMY3 VDD VDD VDD VDD pmos4_standard w=1.5 l=0.15 nf=2
|
||||
|
||||
.subckt pmos4_standard D G S B w=0.1 l=0.018 nf=4
|
||||
MM1 D G S B sky130_fd_pr__pfet_01v8 L=l W='w * nf ' ad='int((nf+1)/2) * W/nf * 0.29' as='int((nf+2)/2) * W/nf * 0.29'
|
||||
+ pd='2*int((nf+1)/2) * (W/nf + 0.29)' ps='2*int((nf+2)/2) * (W/nf + 0.29)' nrd='0.29 / W' nrs='0.29 / W'
|
||||
+ m=1
|
||||
.ends
|
||||
|
||||
.subckt nmos4_standard D G S B w=0.1 l=0.018 nf=4
|
||||
MM1 D G S B sky130_fd_pr__nfet_01v8 L=l W='w * nf ' ad='int((nf+1)/2) * W/nf * 0.29' as='int((nf+2)/2) * W/nf * 0.29'
|
||||
+ pd='2*int((nf+1)/2) * (W/nf + 0.29)' ps='2*int((nf+2)/2) * (W/nf + 0.29)' nrd='0.29 / W' nrs='0.29 / W'
|
||||
+ m=1
|
||||
.ends
|
||||
|
||||
.end
|
||||
|
|
@ -625,14 +625,9 @@ class DBNetlist_TestClass < TestBase
|
|||
dc.terminal_definitions.each { |pd| names << pd.name }
|
||||
assert_equal(names, [])
|
||||
|
||||
pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1", 2.0)
|
||||
assert_equal(pd.default_value, 2.0)
|
||||
pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1")
|
||||
pd.default_value = 1.0
|
||||
assert_equal(pd.default_value, 1.0)
|
||||
pd.is_primary = false
|
||||
assert_equal(pd.is_primary?, false)
|
||||
pd.is_primary = true
|
||||
assert_equal(pd.is_primary?, true)
|
||||
|
||||
dc.add_parameter(pd)
|
||||
|
||||
|
|
@ -1181,6 +1176,24 @@ END
|
|||
|
||||
end
|
||||
|
||||
def test_16_deviceParameterObject
|
||||
|
||||
pd = RBA::DeviceParameterDefinition::new("P1", "Parameter 1", 2.0, false, 17.5, 2.0)
|
||||
assert_equal(pd.default_value, 2.0)
|
||||
pd.default_value = 1.0
|
||||
assert_equal(pd.default_value, 1.0)
|
||||
assert_equal(pd.is_primary?, false)
|
||||
pd.is_primary = true
|
||||
assert_equal(pd.is_primary?, true)
|
||||
assert_equal(pd.si_scaling, 17.5)
|
||||
pd.si_scaling = 1.0
|
||||
assert_equal(pd.si_scaling, 1.0)
|
||||
assert_equal(pd.geo_scaling_exponent, 2.0)
|
||||
pd.geo_scaling_exponent = 1.0
|
||||
assert_equal(pd.geo_scaling_exponent, 1.0)
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
load("test_epilogue.rb")
|
||||
|
|
|
|||
Loading…
Reference in New Issue