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:
Matthias Koefferlein 2023-03-12 15:36:50 +01:00
parent 6955d7d0c7
commit db8f9d5bcb
11 changed files with 355 additions and 78 deletions

View File

@ -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)
{

View File

@ -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));
}

View File

@ -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 ()
{

View File

@ -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;

View File

@ -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);
};
}

View File

@ -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') {

View File

@ -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;

View File

@ -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"

View File

@ -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");
}

27
testdata/algo/nreader19.cir vendored Normal file
View File

@ -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

View File

@ -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")