From be5e16c125ed68c263d98ae345fd08b3865fa079 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Wed, 22 Feb 2023 13:11:30 +0100 Subject: [PATCH] WIP: bug fixing, new test cases --- src/db/db/dbNetlistSpiceReader.cc | 65 +++--- src/db/db/dbNetlistSpiceReader.h | 14 +- src/db/db/dbNetlistSpiceReaderDelegate.cc | 34 ++- src/db/db/dbNetlistSpiceReaderDelegate.h | 31 ++- .../dbNetlistSpiceReaderExpressionParser.cc | 137 +++++++----- .../db/dbNetlistSpiceReaderExpressionParser.h | 2 + src/db/db/gsiDeclDbNetlist.cc | 4 +- src/db/unit_tests/dbNetlistReaderTests.cc | 200 +++++++++++++++++- testdata/algo/nreader17.cir | 15 ++ testdata/algo/nreader18.cir | 59 ++++++ 10 files changed, 464 insertions(+), 97 deletions(-) create mode 100644 testdata/algo/nreader17.cir create mode 100644 testdata/algo/nreader18.cir diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 593156527..4b577b2a1 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -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 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 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 > 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 nets; for (std::vector::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 (); } } diff --git a/src/db/db/dbNetlistSpiceReader.h b/src/db/db/dbNetlistSpiceReader.h index 93b27bfcd..1f36d2bb0 100644 --- a/src/db/db/dbNetlistSpiceReader.h +++ b/src/db/db/dbNetlistSpiceReader.h @@ -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 mp_delegate; + bool m_strict; }; } diff --git a/src/db/db/dbNetlistSpiceReaderDelegate.cc b/src/db/db/dbNetlistSpiceReaderDelegate.cc index 26191f19a..bc7d372ae 100644 --- a/src/db/db/dbNetlistSpiceReaderDelegate.cc +++ b/src/db/db/dbNetlistSpiceReaderDelegate.cc @@ -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 @@ -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 &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 (); } diff --git a/src/db/db/dbNetlistSpiceReaderDelegate.h b/src/db/db/dbNetlistSpiceReaderDelegate.h index 52c306245..521773a3b 100644 --- a/src/db/db/dbNetlistSpiceReaderDelegate.h +++ b/src/db/db/dbNetlistSpiceReaderDelegate.h @@ -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 &strings, std::map &pv, const std::map &variables); + void parse_element_components (const std::string &s, std::vector &strings, std::map &pv, const std::map &variables); /** * @brief Reads a value from the extractor (with formula evaluation) */ - static double read_value (tl::Extractor &ex, const std::map &variables); + static tl::Variant read_value(tl::Extractor &ex, const std::map &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 &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; }; } diff --git a/src/db/db/dbNetlistSpiceReaderExpressionParser.cc b/src/db/db/dbNetlistSpiceReaderExpressionParser.cc index 788a54475..939de34d0 100644 --- a/src/db/db/dbNetlistSpiceReaderExpressionParser.cc +++ b/src/db/db/dbNetlistSpiceReaderExpressionParser.cc @@ -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 ¶ms, 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; diff --git a/src/db/db/dbNetlistSpiceReaderExpressionParser.h b/src/db/db/dbNetlistSpiceReaderExpressionParser.h index a8d28d0c0..18c1fd8d0 100644 --- a/src/db/db/dbNetlistSpiceReaderExpressionParser.h +++ b/src/db/db/dbNetlistSpiceReaderExpressionParser.h @@ -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; diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index c3481ddbf..45174644a 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -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; } diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc index 1782efe85..3fd88ca69 100644 --- a/src/db/unit_tests/dbNetlistReaderTests.cc +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -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 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"); +} diff --git a/testdata/algo/nreader17.cir b/testdata/algo/nreader17.cir new file mode 100644 index 000000000..42ebb0aaf --- /dev/null +++ b/testdata/algo/nreader17.cir @@ -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 + diff --git a/testdata/algo/nreader18.cir b/testdata/algo/nreader18.cir new file mode 100644 index 000000000..f2b61b9da --- /dev/null +++ b/testdata/algo/nreader18.cir @@ -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