WIP: bug fixing, new test cases

This commit is contained in:
Matthias Koefferlein 2023-02-22 13:11:30 +01:00
parent 44e123f343
commit be5e16c125
10 changed files with 464 additions and 97 deletions

View File

@ -453,7 +453,7 @@ SpiceCircuitDict::read (tl::InputStream &stream)
read_card ();
}
} catch (NetlistSpiceReaderDelegateError &ex) {
} catch (NetlistSpiceReaderError &ex) {
// Translate the exception and add a location
error (ex.msg ());
@ -612,6 +612,8 @@ SpiceCircuitDict::read_card ()
std::string name;
ex.read_word (name);
name = mp_netlist->normalize_name (name);
ex.test ("=");
tl::Variant value = NetlistSpiceReaderExpressionParser (&m_variables).read (ex);
@ -634,16 +636,14 @@ SpiceCircuitDict::read_card ()
if (ex.test ("=")) {
name = tl::to_upper_case (name);
name = mp_netlist->normalize_name (name);
tl::Variant value = NetlistSpiceReaderDelegate::read_value (ex, m_variables);
m_variables [name] = value;
mp_circuit->make_parameter (name, value);
}
if (name[0] == 'X') {
} else if (name[0] == 'X') {
// register circuit calls so we can figure out the top level circuits
@ -653,7 +653,7 @@ SpiceCircuitDict::read_card ()
std::vector<std::string> nn;
parameters_type pv;
NetlistSpiceReaderDelegate::parse_element_components (ex2.get (), nn, pv, m_variables);
mp_delegate->parse_element_components (ex2.get (), nn, pv, m_variables);
if (! nn.empty ()) {
m_called_circuits.insert (nn.back ());
@ -689,7 +689,7 @@ SpiceCircuitDict::read_circuit (tl::Extractor &ex, const std::string &nc)
{
std::vector<std::string> nn;
NetlistSpiceReader::parameters_type pv;
NetlistSpiceReaderDelegate::parse_element_components (ex.skip (), nn, pv, m_variables);
mp_delegate->parse_element_components (ex.skip (), nn, pv, m_variables);
if (cached_circuit (nc)) {
error (tl::sprintf (tl::to_string (tr ("Redefinition of circuit %s")), nc));
@ -764,7 +764,7 @@ private:
};
SpiceNetlistBuilder::SpiceNetlistBuilder (SpiceCircuitDict *dict, Netlist *netlist, NetlistSpiceReaderDelegate *delegate)
: mp_dict (dict), mp_delegate (delegate), mp_netlist (netlist), m_strict (false)
: mp_dict (dict), mp_delegate (delegate), mp_netlist (netlist), m_strict (true)
{
mp_circuit = 0;
mp_netlist_circuit = 0;
@ -829,7 +829,7 @@ SpiceNetlistBuilder::build ()
mp_current_card = 0;
m_captured.clear ();
mp_delegate->start (mp_netlist);
mp_delegate->do_start ();
for (auto c = mp_dict->begin_circuits (); c != mp_dict->end_circuits (); ++c) {
if (mp_dict->is_top_circuit (c->first) && ! subcircuit_captured (c->first)) {
@ -839,9 +839,9 @@ SpiceNetlistBuilder::build ()
}
build_global_nets ();
mp_delegate->finish (mp_netlist);
mp_delegate->do_finish ();
} catch (NetlistSpiceReaderDelegateError &ex) {
} catch (NetlistSpiceReaderError &ex) {
// translate the error and add a source location
error (ex.msg ());
@ -913,7 +913,10 @@ SpiceNetlistBuilder::build_circuit (const SpiceCachedCircuit *cc, const paramete
std::unique_ptr<std::map<std::string, db::Net *> > n2n (mp_nets_by_name.release ());
mp_nets_by_name.reset (0);
NetlistSpiceReader::parameters_type vars;
NetlistSpiceReader::parameters_type vars = cc->parameters ();
for (auto p = pv.begin (); p != pv.end (); ++p) {
vars [p->first] = p->second;
}
std::swap (vars, m_variables);
std::swap (c, mp_netlist_circuit);
@ -981,7 +984,7 @@ SpiceNetlistBuilder::process_card (const SpiceCard &card)
std::string name;
if (ex.try_read_word (name) && ex.test ("=")) {
m_variables.insert (std::make_pair (tl::to_upper_case (name), NetlistSpiceReaderDelegate::read_value (ex, m_variables)));
m_variables.insert (std::make_pair (mp_netlist->normalize_name (name), NetlistSpiceReaderDelegate::read_value (ex, m_variables)));
} else {
@ -1035,7 +1038,7 @@ SpiceNetlistBuilder::process_element (tl::Extractor &ex, const std::string &pref
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 (mp_netlist->normalize_name (*i))));
nets.push_back (make_net (mp_delegate->translate_net_name (*i)));
}
if (prefix == "X" && ! subcircuit_captured (model)) {
@ -1118,7 +1121,7 @@ SpiceNetlistBuilder::build_global_nets ()
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate)
: mp_delegate (delegate)
: mp_delegate (delegate), m_strict (false)
{
static NetlistSpiceReaderDelegate std_delegate;
if (! delegate) {
@ -1135,20 +1138,32 @@ void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist)
{
tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading netlist ")) + stream.source ());
// SPICE netlists are case insensitive
netlist.set_case_sensitive (false);
SpiceCircuitDict dict (this, &netlist, mp_delegate.get ());
try {
dict.read (stream);
dict.finish ();
mp_delegate->set_netlist (&netlist);
// SPICE netlists are case insensitive
netlist.set_case_sensitive (false);
SpiceCircuitDict dict (this, &netlist, mp_delegate.get ());
try {
dict.read (stream);
dict.finish ();
} catch (...) {
dict.finish ();
throw;
}
SpiceNetlistBuilder builder (&dict, &netlist, mp_delegate.get ());
builder.set_strict (m_strict);
builder.build ();
mp_delegate->set_netlist (0);
} catch (...) {
dict.finish ();
mp_delegate->set_netlist (0);
throw;
}
SpiceNetlistBuilder builder (&dict, &netlist, mp_delegate.get ());
builder.build ();
}
}

View File

@ -42,11 +42,11 @@ class NetlistSpiceReaderDelegate;
/**
* @brief A specialized exception class to handle netlist reader delegate errors
*/
class DB_PUBLIC NetlistSpiceReaderDelegateError
class DB_PUBLIC NetlistSpiceReaderError
: public tl::Exception
{
public:
NetlistSpiceReaderDelegateError (const std::string &msg)
NetlistSpiceReaderError (const std::string &msg)
: tl::Exception (msg)
{ }
};
@ -65,8 +65,18 @@ public:
virtual void read (tl::InputStream &stream, db::Netlist &netlist);
/**
* @brief Sets or resets strict mode
* In strict mode, all subcircuits need to be present in the net list for example.
*/
void set_strict (bool s)
{
m_strict = s;
}
private:
tl::weak_ptr<NetlistSpiceReaderDelegate> mp_delegate;
bool m_strict;
};
}

View File

@ -123,7 +123,7 @@ std::string NetlistSpiceReaderDelegate::translate_net_name (const std::string &n
void NetlistSpiceReaderDelegate::error (const std::string &msg)
{
throw NetlistSpiceReaderDelegateError (msg);
throw NetlistSpiceReaderError (msg);
}
template <class Cls>
@ -194,8 +194,8 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
if (ex.try_read_word (n) && ex.test ("=")) {
// a parameter. Note that parameter names are always made upper case.
pv.insert (std::make_pair (tl::to_upper_case (n), read_value (ex, variables)));
// a parameter
pv [mp_netlist ? mp_netlist->normalize_name (n) : n] = read_value (ex, variables);
} else {
@ -207,13 +207,24 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s,
std::string comp_name = parse_component (ex);
// resolve variables if string type
auto v = variables.find (comp_name);
if (v != variables.end () && v->second.is_a_string ()) {
comp_name = v->second.to_string ();
if (mp_netlist) {
comp_name = mp_netlist->normalize_name (comp_name);
}
strings.push_back (comp_name);
// resolve variables if string type
auto v = variables.find (comp_name);
if (v != variables.end ()) {
if (v->second.is_a_string ()) {
strings.push_back (v->second.to_string ());
} else if (v->second.can_convert_to_double ()) {
// NOTE: this allows using a variable name "x" instead of "x=x"
pv [comp_name] = v->second;
} else {
strings.push_back (comp_name);
}
} else {
strings.push_back (comp_name);
}
}
@ -536,11 +547,11 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
return true;
}
double
tl::Variant
NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map<std::string, tl::Variant> &variables)
{
NetlistSpiceReaderExpressionParser parser (&variables);
return parser.read (ex).to_double ();
return parser.read (ex);
}
bool
@ -552,6 +563,9 @@ NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &v, con
tl::Extractor ex (s.c_str ());
bool res = parser.try_read (ex, vv);
if (res && ! vv.can_convert_to_double ()) {
res = false;
}
if (res) {
v = vv.to_double ();
}

View File

@ -124,17 +124,44 @@ public:
* @brief Reads a set of string components and parameters from the string
* A special key "param:" is recognized for starting a parameter list.
*/
static 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);
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);
/**
* @brief Reads a value from the extractor (with formula evaluation)
*/
static double read_value (tl::Extractor &ex, const std::map<std::string, tl::Variant> &variables);
static tl::Variant read_value(tl::Extractor &ex, const std::map<std::string, tl::Variant> &variables);
/**
* @brief Tries to read a value from the extractor (with formula evaluation)
*/
static bool try_read_value (const std::string &s, double &v, const std::map<std::string, tl::Variant> &variables);
/**
* @brief External interface for start
*/
void do_start ()
{
start (mp_netlist);
}
/**
* @brief External interface for finish
*/
void do_finish ()
{
finish (mp_netlist);
}
/**
* @brief Sets the netlist
*/
void set_netlist (db::Netlist *netlist)
{
mp_netlist = netlist;
}
private:
db::Netlist *mp_netlist;
};
}

View File

@ -30,6 +30,20 @@ namespace db
// ------------------------------------------------------------------------------------------------------
static bool to_bool (const tl::Variant &v)
{
if (v.is_bool ()) {
return v.to_bool ();
} else if (v.is_nil ()) {
return false;
} else if (v.can_convert_to_double ()) {
return v.to_double () != 0.0;
} else {
return true;
}
}
// ------------------------------------------------------------------------------------------------------
NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const variables_type *vars)
{
static variables_type empty_variables;
@ -40,55 +54,55 @@ NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const va
// https://nmg.gitlab.io/ngspice-manual/circuitdescription/paramparametricnetlists/syntaxofexpressions.html
static double sqrt_f (double v) { return sqrt (v); }
static double sin_f (double v) { return sin (v); }
static double cos_f (double v) { return cos (v); }
static double tan_f (double v) { return tan (v); }
static double sinh_f (double v) { return sinh (v); }
static double cosh_f (double v) { return cosh (v); }
static double tanh_f (double v) { return tanh (v); }
static double asin_f (double v) { return asin (v); }
static double acos_f (double v) { return acos (v); }
static double atan_f (double v) { return atan (v); }
static double asinh_f (double v) { return asinh (v); }
static double acosh_f (double v) { return acosh (v); }
static double atanh_f (double v) { return atanh (v); }
static double exp_f (double v) { return exp (v); }
static double ln_f (double v) { return log (v); }
static double log_f (double v) { return log10 (v); }
static double abs_f (double v) { return abs (v); }
static double nint_f (double v) { return round (v); }
static double floor_f (double v) { return floor (v); }
static double ceil_f (double v) { return ceil (v); }
static double sgn_f (double v) { return v == 0.0 ? 0.0 : (v < 0.0 ? -1.0 : 1.0); }
static double int_f (double v) { return sgn_f (v) * floor (sgn_f (v) * v); }
static double sin_f (double v) { return sin (v); }
static double cos_f (double v) { return cos (v); }
static double tan_f (double v) { return tan (v); }
static double sinh_f (double v) { return sinh (v); }
static double cosh_f (double v) { return cosh (v); }
static double tanh_f (double v) { return tanh (v); }
static double asin_f (double v) { return asin (v); }
static double acos_f (double v) { return acos (v); }
static double atan_f (double v) { return atan (v); }
static double asinh_f (double v) { return asinh (v); }
static double acosh_f (double v) { return acosh (v); }
static double atanh_f (double v) { return atanh (v); }
static double exp_f (double v) { return exp (v); }
static double ln_f (double v) { return log (v); }
static double log_f (double v) { return log10 (v); }
static double abs_f (double v) { return abs (v); }
static double nint_f (double v) { return nearbyint (v); } // we basically should we the rounding mode before ...
static double floor_f (double v) { return floor (v); }
static double ceil_f (double v) { return ceil (v); }
static double sgn_f (double v) { return v == 0.0 ? 0.0 : (v < 0.0 ? -1.0 : 1.0); }
static double int_f (double v) { return sgn_f (v) * floor (sgn_f (v) * v); }
tl::Variant
NetlistSpiceReaderExpressionParser::eval_func (const std::string &name, const std::vector<tl::Variant> &params, bool * /*status*/) const
{
double (*f) (double) = 0;
if (name == "sqrt") { f = sqrt_f; } else
if (name == "sin") { f = sin_f; } else
if (name == "cos") { f = cos_f; } else
if (name == "tan") { f = tan_f; } else
if (name == "sinh") { f = sinh_f; } else
if (name == "cosh") { f = cosh_f; } else
if (name == "tanh") { f = tanh_f; } else
if (name == "asin") { f = asin_f; } else
if (name == "acos") { f = acos_f; } else
if (name == "atan" || name == "arctan") { f = atan_f; } else
if (name == "asinh") { f = asinh_f; } else
if (name == "acosh") { f = acosh_f; } else
if (name == "atanh") { f = atanh_f; } else
if (name == "exp") { f = exp_f; } else
if (name == "ln") { f = ln_f; } else
if (name == "log") { f = log_f; } else
if (name == "abs") { f = abs_f; } else
if (name == "nint") { f = nint_f; } else
if (name == "floor") { f = floor_f; } else
if (name == "ceil") { f = ceil_f; } else
if (name == "sgn") { f = sgn_f; } else
if (name == "int") { f = int_f; }
if (name == "SQRT") { f = sqrt_f; } else
if (name == "SIN") { f = sin_f; } else
if (name == "COS") { f = cos_f; } else
if (name == "TAN") { f = tan_f; } else
if (name == "SINH") { f = sinh_f; } else
if (name == "COSH") { f = cosh_f; } else
if (name == "TANH") { f = tanh_f; } else
if (name == "ASIN") { f = asin_f; } else
if (name == "ACOS") { f = acos_f; } else
if (name == "ATAN" || name == "arctan") { f = atan_f; } else
if (name == "ASINH") { f = asinh_f; } else
if (name == "ACOSH") { f = acosh_f; } else
if (name == "ATANH") { f = atanh_f; } else
if (name == "EXP") { f = exp_f; } else
if (name == "LN") { f = ln_f; } else
if (name == "LOG") { f = log_f; } else
if (name == "ABS") { f = abs_f; } else
if (name == "NINT") { f = nint_f; } else
if (name == "FLOOR") { f = floor_f; } else
if (name == "CEIL") { f = ceil_f; } else
if (name == "SGN") { f = sgn_f; } else
if (name == "INT") { f = int_f; }
if (f != 0) {
@ -98,7 +112,7 @@ NetlistSpiceReaderExpressionParser::eval_func (const std::string &name, const st
return tl::Variant ((*f) (params.front ().to_double ()));
}
} else if (name == "pwr" || name == "pow") {
} else if (name == "PWR" || name == "POW") {
if (params.size () < 2 || ! params [0].can_convert_to_double () || ! params [1].can_convert_to_double ()) {
return tl::Variant ();
@ -106,15 +120,15 @@ NetlistSpiceReaderExpressionParser::eval_func (const std::string &name, const st
return tl::Variant (pow (params [0].to_double (), params [1].to_double ()));
}
} else if (name == "ternary_fcn") {
} else if (name == "TERNERY_FCN") {
if (params.size () < 3) {
return tl::Variant ();
} else {
return params [0].to_bool () ? params [1] : params [2];
return to_bool (params [0]) ? params [1] : params [2];
}
} else if (name == "min") {
} else if (name == "MIN") {
if (params.size () < 1) {
return tl::Variant ();
@ -128,7 +142,7 @@ NetlistSpiceReaderExpressionParser::eval_func (const std::string &name, const st
}
return v;
} else if (name == "max") {
} else if (name == "MAX") {
if (params.size () < 1) {
return tl::Variant ();
@ -167,7 +181,7 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool *
} else if (ex.test ("!")) {
tl::Variant v = read_atomic_value (ex, status);
return tl::Variant (! v.to_bool ());
return tl::Variant (! to_bool (v));
} else if (ex.test ("(")) {
@ -220,6 +234,8 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool *
} else if (ex.try_read_word (var)) {
var = tl::to_upper_case (var);
if (ex.test ("(")) {
// a function
@ -247,7 +263,7 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool *
} else {
auto vi = mp_variables->find (tl::to_upper_case (var));
auto vi = mp_variables->find (var);
if (vi != mp_variables->end ()) {
return vi->second;
} else {
@ -434,13 +450,13 @@ tl::Variant NetlistSpiceReaderExpressionParser::read_logical_op (tl::Extractor &
if (status && !*status) {
return tl::Variant ();
}
v = tl::Variant (v.to_bool () && vv.to_bool ());
v = tl::Variant (to_bool (v) && to_bool (vv));
} else if (ex.test ("||")) {
tl::Variant vv = read_compare_expr (ex, status);
if (status && !*status) {
return tl::Variant ();
}
v = tl::Variant (v.to_bool () && vv.to_bool ());
v = tl::Variant (to_bool (v) || to_bool (vv));
} else {
break;
}
@ -470,7 +486,7 @@ tl::Variant NetlistSpiceReaderExpressionParser::read_ternary_op (tl::Extractor &
if (status && !*status) {
return tl::Variant ();
}
v = v.to_bool () ? vv1 : vv2;
v = to_bool (v) ? vv1 : vv2;
}
return v;
@ -494,6 +510,12 @@ static const char *start_quote (tl::Extractor &ex)
}
}
tl::Variant NetlistSpiceReaderExpressionParser::read (const std::string &s) const
{
tl::Extractor ex (s.c_str ());
return read (ex);
}
tl::Variant NetlistSpiceReaderExpressionParser::read (tl::Extractor &ex) const
{
try {
@ -509,10 +531,17 @@ tl::Variant NetlistSpiceReaderExpressionParser::read (tl::Extractor &ex) const
return res;
} catch (tl::Exception &error) {
throw NetlistSpiceReaderDelegateError (error.msg ());
// recast the exception, so we can process it later
throw NetlistSpiceReaderError (error.msg ());
}
}
bool NetlistSpiceReaderExpressionParser::try_read (const std::string &s, tl::Variant &value) const
{
tl::Extractor ex (s.c_str ());
return try_read (ex, value);
}
bool NetlistSpiceReaderExpressionParser::try_read (tl::Extractor &ex, tl::Variant &value) const
{
tl::Extractor ex_saved = ex;

View File

@ -48,7 +48,9 @@ public:
NetlistSpiceReaderExpressionParser (const variables_type *vars);
tl::Variant read (tl::Extractor &ex) const;
tl::Variant read (const std::string &s) const;
bool try_read (tl::Extractor &ex, tl::Variant &v) const;
bool try_read (const std::string &s, tl::Variant &v) const;
private:
const variables_type *mp_variables;

View File

@ -2688,10 +2688,10 @@ static tl::Variant value_from_string (NetlistSpiceReaderDelegateImpl * /*delegat
return res;
}
static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl * /*delegate*/, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables)
static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s, const db::NetlistSpiceReader::parameters_type &variables)
{
ParseElementComponentsData data;
db::NetlistSpiceReaderDelegate::parse_element_components (s, data.strings_nc (), data.parameters_nc (), variables);
delegate->parse_element_components (s, data.strings_nc (), data.parameters_nc (), variables);
return data;
}

View File

@ -22,6 +22,7 @@
#include "dbNetlistSpiceReader.h"
#include "dbNetlistSpiceReaderDelegate.h"
#include "dbNetlistSpiceReaderExpressionParser.h"
#include "dbNetlist.h"
#include "dbNetlistDeviceClasses.h"
@ -176,11 +177,23 @@ TEST(4_ReaderWithUnconnectedPins)
TEST(5_CircuitParameters)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader5.cir");
db::NetlistSpiceReader reader;
reader.set_strict (true);
try {
db::Netlist nl;
tl::InputStream is (path);
reader.read (is, nl);
// strict mode makes this sample fail
EXPECT_EQ (true, false);
} catch (...) {
// ..
}
db::Netlist nl;
reader.set_strict (false);
tl::InputStream is (path);
reader.read (is, nl);
@ -620,3 +633,186 @@ TEST(16_issue898)
"end;\n"
);
}
TEST(17_RecursiveExpansion)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader17.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
EXPECT_EQ (nl.to_string (),
"circuit .TOP ();\n"
" subcircuit 'SUB1(L=0.15,W=1.5)' SUB1A (N1=A,N2=B,N3=C);\n"
" subcircuit 'SUB1(L=0.25,W=3)' SUB1B (N1=A,N2=B,N3=C);\n"
"end;\n"
"circuit 'SUB1(L=0.15,W=1.5)' (N1=N1,N2=N2,N3=N3);\n"
" subcircuit 'SUB2(L=0.15,M=1,W=1.5)' SUB2A (N1=N1,N2=N2,N3=N3);\n"
" subcircuit 'SUB2(L=0.15,M=2,W=1.5)' SUB2B (N1=N1,N2=N2,N3=N3);\n"
"end;\n"
"circuit 'SUB2(L=0.15,M=1,W=1.5)' (N1=N1,N2=N2,N3=N3);\n"
" device NMOS NMOS (S=N1,G=N2,D=N3,B=N1) (L=150000,W=1500000,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
"circuit 'SUB2(L=0.15,M=2,W=1.5)' (N1=N1,N2=N2,N3=N3);\n"
" device NMOS NMOS (S=N1,G=N2,D=N3,B=N1) (L=150000,W=3000000,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
"circuit 'SUB1(L=0.25,W=3)' (N1=N1,N2=N2,N3=N3);\n"
" subcircuit 'SUB2(L=0.25,M=1,W=3)' SUB2A (N1=N1,N2=N2,N3=N3);\n"
" subcircuit 'SUB2(L=0.25,M=2,W=3)' SUB2B (N1=N1,N2=N2,N3=N3);\n"
"end;\n"
"circuit 'SUB2(L=0.25,M=1,W=3)' (N1=N1,N2=N2,N3=N3);\n"
" device NMOS NMOS (S=N1,G=N2,D=N3,B=N1) (L=250000,W=3000000,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
"circuit 'SUB2(L=0.25,M=2,W=3)' (N1=N1,N2=N2,N3=N3);\n"
" device NMOS NMOS (S=N1,G=N2,D=N3,B=N1) (L=250000,W=6000000,AS=0,AD=0,PS=0,PD=0);\n"
"end;\n"
);
}
TEST(18_XSchemOutput)
{
db::Netlist nl;
std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader18.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.15U,NF=4,W=1.5U)' XPMOS (D=Q,G=I,S=VDD,B=VDD);\n"
" subcircuit 'NMOS4_STANDARD(L=0.15U,NF=4,W=1.5U)' XNMOS (D=Q,G=I,S=VSS,B=VSS);\n"
" subcircuit 'NMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY0 (D=VSS,G=VSS,S=VSS,B=VSS);\n"
" subcircuit 'NMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY1 (D=VSS,G=VSS,S=VSS,B=VSS);\n"
" subcircuit 'PMOS4_STANDARD(L=0.15U,NF=2,W=1.5U)' XDUMMY2 (D=VDD,G=VDD,S=VDD,B=VDD);\n"
" 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=D,G=G,D=S,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=2.685,PD=1.79);\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=D,G=G,D=S,B=B) (L=0.15,W=6,AS=0.32625,AD=0.2175,PS=2.685,PD=1.79);\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=D,G=G,D=S,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=3.58,PD=1.79);\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=D,G=G,D=S,B=B) (L=0.15,W=3,AS=0.435,AD=0.2175,PS=3.58,PD=1.79);\n"
"end;\n"
);
}
TEST(100_ExpressionParser)
{
std::map<std::string, tl::Variant> vars;
vars["A"] = 17.5;
vars["B"] = 42;
vars["S"] = "string";
tl::Variant v;
db::NetlistSpiceReaderExpressionParser parser (&vars);
EXPECT_EQ (parser.read ("1.75").to_string (), "1.75");
EXPECT_EQ (parser.read ("-1.75").to_string (), "-1.75");
EXPECT_EQ (parser.read ("-a*0.1").to_string (), "-1.75");
EXPECT_EQ (parser.read ("-A*0.1").to_string (), "-1.75");
EXPECT_EQ (parser.read ("b/6").to_string (), "7");
EXPECT_EQ (parser.read ("B/6").to_string (), "7");
EXPECT_EQ (parser.read ("s").to_string (), "string");
EXPECT_EQ (parser.read ("S").to_string (), "string");
EXPECT_EQ (parser.read ("!0").to_string (), "true");
EXPECT_EQ (parser.read ("!1").to_string (), "false");
EXPECT_EQ (parser.read ("4*2+1").to_string (), "9");
EXPECT_EQ (parser.read ("4*2-1").to_string (), "7");
EXPECT_EQ (parser.read ("4/2-1").to_string (), "1");
EXPECT_EQ (parser.read ("4%2-1").to_string (), "-1");
EXPECT_EQ (parser.read ("5%2-1").to_string (), "0");
EXPECT_EQ (parser.read ("2**2*2+1").to_string (), "9");
EXPECT_EQ (parser.read ("2**2*(2+1)").to_string (), "12");
EXPECT_EQ (parser.read ("pow(2,2)*(2+1)").to_string (), "12");
EXPECT_EQ (parser.read ("POW(2,2)*(2+1)").to_string (), "12");
EXPECT_EQ (parser.read ("pwr(2,2)*(2+1)").to_string (), "12");
EXPECT_EQ (parser.read ("PWR(2,2)*(2+1)").to_string (), "12");
EXPECT_EQ (parser.read ("3==2+1").to_string (), "true");
EXPECT_EQ (parser.read ("4==2+1").to_string (), "false");
EXPECT_EQ (parser.read ("3!=2+1").to_string (), "false");
EXPECT_EQ (parser.read ("4!=2+1").to_string (), "true");
EXPECT_EQ (parser.read ("2<2+1").to_string (), "true");
EXPECT_EQ (parser.read ("3<2+1").to_string (), "false");
EXPECT_EQ (parser.read ("4<2+1").to_string (), "false");
EXPECT_EQ (parser.read ("2<=2+1").to_string (), "true");
EXPECT_EQ (parser.read ("3<=2+1").to_string (), "true");
EXPECT_EQ (parser.read ("4<=2+1").to_string (), "false");
EXPECT_EQ (parser.read ("2>2+1").to_string (), "false");
EXPECT_EQ (parser.read ("3>2+1").to_string (), "false");
EXPECT_EQ (parser.read ("4>2+1").to_string (), "true");
EXPECT_EQ (parser.read ("2>=2+1").to_string (), "false");
EXPECT_EQ (parser.read ("3>=2+1").to_string (), "true");
EXPECT_EQ (parser.read ("4>=2+1").to_string (), "true");
EXPECT_EQ (parser.read ("1==2||2==2").to_string (), "true");
EXPECT_EQ (parser.read ("1==2||3==2").to_string (), "false");
EXPECT_EQ (parser.read ("1==2&&2==2").to_string (), "false");
EXPECT_EQ (parser.read ("1==1&&2==2").to_string (), "true");
EXPECT_EQ (parser.read ("1==2?2:3").to_string (), "3");
EXPECT_EQ (parser.read ("ternery_fcn(1==2,2,3)").to_string (), "3");
EXPECT_EQ (parser.read ("1==1?2:3").to_string (), "2");
EXPECT_EQ (parser.read ("ternery_fcn(1==1,2,3)").to_string (), "2");
EXPECT_EQ (parser.read ("sin(0)").to_string (), "0");
EXPECT_EQ (parser.read ("sin(atan(1.0)*2)").to_string (), "1");
EXPECT_EQ (parser.read ("cos(0)").to_string (), "1");
EXPECT_EQ (parser.read ("cos(atan(1.0)*2)").to_string (), "0");
EXPECT_EQ (parser.read ("tan(0)").to_string (), "0");
EXPECT_EQ (parser.read ("tan(atan(1.0))").to_string (), "1");
EXPECT_EQ (parser.read ("sin(asin(0.5))").to_string (), "0.5");
EXPECT_EQ (parser.read ("cos(acos(0.5))").to_string (), "0.5");
EXPECT_EQ (parser.read ("ln(exp(0.5))").to_string (), "0.5");
EXPECT_EQ (parser.read ("exp(0.0)").to_string (), "1");
EXPECT_EQ (parser.read ("log(10**0.5)").to_string (), "0.5");
EXPECT_EQ (parser.read ("int(-0.5)").to_string (), "0");
EXPECT_EQ (parser.read ("int(-1.5)").to_string (), "-1");
EXPECT_EQ (parser.read ("int(0.5)").to_string (), "0");
EXPECT_EQ (parser.read ("int(1.5)").to_string (), "1");
EXPECT_EQ (parser.read ("floor(-0.5)").to_string (), "-1");
EXPECT_EQ (parser.read ("floor(-1.5)").to_string (), "-2");
EXPECT_EQ (parser.read ("floor(0.5)").to_string (), "0");
EXPECT_EQ (parser.read ("floor(1.5)").to_string (), "1");
EXPECT_EQ (parser.read ("ceil(-0.5)").to_string (), "0");
EXPECT_EQ (parser.read ("ceil(-1.5)").to_string (), "-1");
EXPECT_EQ (parser.read ("ceil(0.5)").to_string (), "1");
EXPECT_EQ (parser.read ("ceil(1.5)").to_string (), "2");
EXPECT_EQ (parser.read ("nint(-0.5)").to_string (), "0");
EXPECT_EQ (parser.read ("nint(-1.5)").to_string (), "-2");
EXPECT_EQ (parser.read ("nint(0.5)").to_string (), "0");
EXPECT_EQ (parser.read ("nint(1.5)").to_string (), "2");
EXPECT_EQ (parser.read ("min(4,1,3)").to_string (), "1");
EXPECT_EQ (parser.read ("min(4,3)").to_string (), "3");
EXPECT_EQ (parser.read ("min(4)").to_string (), "4");
EXPECT_EQ (parser.read ("max(1,4,3)").to_string (), "4");
EXPECT_EQ (parser.read ("max(4,3)").to_string (), "4");
EXPECT_EQ (parser.read ("max(4)").to_string (), "4");
EXPECT_EQ (parser.read ("max(a,b)").to_string (), "42");
EXPECT_EQ (parser.try_read ("a syntax error", v), false);
v = tl::Variant ();
EXPECT_EQ (parser.try_read ("1+2*(2+1)-1", v), true);
EXPECT_EQ (v.to_string (), "6");
EXPECT_EQ (parser.try_read ("{1+2*(2+1)-1)", v), false);
EXPECT_EQ (parser.try_read ("'1+2*(2+1)-1)", v), false);
EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1)", v), false);
EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1'", v), false);
v = tl::Variant ();
EXPECT_EQ (parser.try_read ("{1+2*(2+1)-1}", v), true);
EXPECT_EQ (v.to_string (), "6");
v = tl::Variant ();
EXPECT_EQ (parser.try_read ("'1+2*(2+1)-1'", v), true);
EXPECT_EQ (v.to_string (), "6");
v = tl::Variant ();
EXPECT_EQ (parser.try_read ("\"1+2*(2+1)-1\"", v), true);
EXPECT_EQ (v.to_string (), "6");
}

15
testdata/algo/nreader17.cir vendored Normal file
View File

@ -0,0 +1,15 @@
* recursive expansion of parametrized subcircuits
Xsub1a a b c sub1 w=1.5 l=0.15
Xsub1b a b c sub1 w=3.0 l=0.25
.subckt sub1 n1 n2 n3 w=1.0 l=0.5
Xsub2a n1 n2 n3 sub2 w l m=1
Xsub2b n1 n2 n3 sub2 w l m=2
.ends
.subckt sub2 n1 n2 n3 w=0.0 l=0.0 m=0
Mnmos n1 n2 n3 n1 nmos w=w l=l m=m
.ends

59
testdata/algo/nreader18.cir vendored Normal file
View File

@ -0,0 +1,59 @@
** sch_path:
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/inverter1_generated_sky130_fd_pr/inverter1/inverter1.sch
**.subckt inverter1 I Q VDD VSS
*.ipin I
*.opin Q
*.iopin VDD
*.iopin VSS
XXpmos Q I VDD VDD pmos4_standard w=1.5u l=150n nf=4
XXnmos Q I VSS VSS nmos4_standard w=1.5u l=150n nf=4
XXDUMMY0 VSS VSS VSS VSS nmos4_standard w=1.5u l=150n nf=2
XXDUMMY1 VSS VSS VSS VSS nmos4_standard w=1.5u l=150n nf=2
XXDUMMY2 VDD VDD VDD VDD pmos4_standard w=1.5u l=150n nf=2
XXDUMMY3 VDD VDD VDD VDD pmos4_standard w=1.5u l=150n nf=2
**.ends
* expanding symbol: BAG_prim/pmos4_standard/pmos4_standard.sym # of pins=4
** sym_path:
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/BAG2_technology_definition/BAG_prim/pmos4_standard/pmos4_standard.sym
** sch_path:
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/BAG2_technology_definition/BAG_prim/pmos4_standard/pmos4_standard.sch
.subckt pmos4_standard D G S B model w l nf
w=100n
l=18n
nf=4
model=pmos4_standard
spiceprefix=X
*.iopin D
*.iopin G
*.iopin S
*.iopin B
MM1 D G S B sky130_fd_pr__pfet_01v8 L=l W=w ad='int((nf+1)/2) * W/nf * 0.29u' as='int((nf+2)/2) * W/nf * 0.29u'
+ pd='2*int((nf+1)/2) * (W + 0.29u)/nf' ps='2*int((nf+2)/2) * (W + 0.29u)/nf' nrd='0.29u / W' nrs='0.29u / W'
+ sa=0 sb=0 sd=0 mult=1 m=nf
.ends
* expanding symbol: BAG_prim/nmos4_standard/nmos4_standard.sym # of pins=4
** sym_path:
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/BAG2_technology_definition/BAG_prim/nmos4_standard/nmos4_standard.sym
** sch_path:
*+ /home/matthias/dev/bag/mosaic_bag/opensource_db_template/BAG2_technology_definition/BAG_prim/nmos4_standard/nmos4_standard.sch
.subckt nmos4_standard D G S B model w l nf
w=100n
l=18n
nf=4
model=nmos4_standard
spiceprefix=X
*.iopin D
*.iopin G
*.iopin S
*.iopin B
MM1 D G S B sky130_fd_pr__nfet_01v8 L=l W=w ad='int((nf+1)/2) * W/nf * 0.29u' as='int((nf+2)/2) * W/nf * 0.29u'
+ pd='2*int((nf+1)/2) * (W + 0.29u)/nf' ps='2*int((nf+2)/2) * (W + 0.29u)/nf' nrd='0.29u / W' nrs='0.29u / W'
+ sa=0 sb=0 sd=0 mult=1 m=nf
.ends
.end