diff --git a/src/db/db/dbNetlistSpiceReaderDelegate.cc b/src/db/db/dbNetlistSpiceReaderDelegate.cc index 326d920aa..905360073 100644 --- a/src/db/db/dbNetlistSpiceReaderDelegate.cc +++ b/src/db/db/dbNetlistSpiceReaderDelegate.cc @@ -225,7 +225,7 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s, // a parameter std::string pn = mp_netlist ? mp_netlist->normalize_name (n) : tl::to_upper_case (n); - pv [pn] = read_value (ex, variables); + pv [pn] = read_value (ex, variables, pv); } else { @@ -304,44 +304,81 @@ void NetlistSpiceReaderDelegate::parse_element (const std::string &s, const std: // (same for C, L instead of R) if (nn.size () < 2) { - error (tl::to_string (tr ("Not enough specs for a R, C or L device"))); + error (tl::to_string (tr ("Not enough specs (nodes, value, model) for a R, C or L device"))); + } else if (nn.size () > 5) { + error (tl::to_string (tr ("Too many specs (nodes, value, model) for a R, C or L device"))); } + // Variations are (here for "C" element): + // (1) Cname n1 n2 C=value [other params] + // (2) Cname n1 n2 value [params] + // (3) Cname n1 n2 model C=value [other params] + // Cname n1 n2 n3 C=value [other params] -> not supported, cannot tell from (3) without further analysis + // (4) Cname n1 n2 model value [params] + // Cname n1 n2 n3 value [params] -> not supported, cannot tell from (4) without further analysis + // (5) Cname n1 n2 n3 model C=value [other params] + // (6) Cname n1 n2 value model [params] + // (7) Cname n1 n2 n3 model value [params] + // (8) Cname n1 n2 n3 value model [params] + auto rv = pv.find (element); - if (rv != pv.end ()) { - // value given by parameter - value = rv->second.to_double (); - - if (nn.size () >= 3) { - // Rname n1 n2 model [params] - // Rname n1 n2 n3 model [params] - model = nn.back (); - nn.pop_back (); + bool has_value = false; + if (nn.size () == 2) { + if (rv != pv.end ()) { + value = rv->second.to_double (); // (1) + has_value = true; } - - } else if (nn.size () >= 3) { - + } else if (nn.size () == 3) { if (try_read_value (nn.back (), value, variables)) { - - // Rname n1 n2 value - // Rname n1 n2 n3 value + has_value = true; // (2) nn.pop_back (); - } else { - - // Rname n1 n2 value model [params] - // Rname n1 n2 n3 value model [params] + model = nn.back (); // (3) + nn.pop_back (); + if (rv != pv.end ()) { + value = rv->second.to_double (); + has_value = true; + } + } + } else if (nn.size () == 4) { + if (try_read_value (nn.back (), value, variables)) { + has_value = true; // (4) + nn.pop_back (); + } else if (rv != pv.end ()) { + value = rv->second.to_double (); // (5) + has_value = true; model = nn.back (); nn.pop_back (); - if (! try_read_value (nn.back (), value, variables)) { - error (tl::to_string (tr ("Can't find a value for a R, C or L device"))); - } else { - nn.pop_back (); - } - + } else if (try_read_value (nn[2], value, variables)) { + has_value = true; // (6) + model = nn.back (); + nn.pop_back (); + nn.pop_back (); + } else { + model = nn.back (); // fall back to (5) + nn.pop_back (); } + } else { + if (try_read_value (nn.back (), value, variables)) { + has_value = true; // (7) + nn.pop_back (); + model = nn.back (); + nn.pop_back (); + } else if (try_read_value (nn[3], value, variables)) { + has_value = true; // (8) + model = nn.back (); + nn.pop_back (); + nn.pop_back (); + } + } + if (rv != pv.end ()) { + pv.erase (rv); + } + + if (! has_value) { + error (tl::to_string (tr ("Can't find a value for a R, C or L device"))); } } else { @@ -653,6 +690,13 @@ NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map &variables1, const std::map &variables2) +{ + NetlistSpiceReaderExpressionParser parser (&variables1, &variables2); + return parser.read (ex); +} + bool NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &v, const std::map &variables) { diff --git a/src/db/db/dbNetlistSpiceReaderDelegate.h b/src/db/db/dbNetlistSpiceReaderDelegate.h index 6e304ed80..75e47824f 100644 --- a/src/db/db/dbNetlistSpiceReaderDelegate.h +++ b/src/db/db/dbNetlistSpiceReaderDelegate.h @@ -154,6 +154,11 @@ public: */ static tl::Variant read_value(tl::Extractor &ex, const std::map &variables); + /** + * @brief Reads a value from the extractor (with formula evaluation and two levels of variables) + */ + static tl::Variant read_value(tl::Extractor &ex, const std::map &variables1, const std::map &variables2); + /** * @brief Tries to read a value from the extractor (with formula evaluation) */ diff --git a/src/db/db/dbNetlistSpiceReaderExpressionParser.cc b/src/db/db/dbNetlistSpiceReaderExpressionParser.cc index ef2ad63d4..dcfd46332 100644 --- a/src/db/db/dbNetlistSpiceReaderExpressionParser.cc +++ b/src/db/db/dbNetlistSpiceReaderExpressionParser.cc @@ -47,8 +47,15 @@ static bool to_bool (const tl::Variant &v) 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; + mp_variables1 = vars; + mp_variables2 = 0; +} + +NetlistSpiceReaderExpressionParser::NetlistSpiceReaderExpressionParser (const variables_type *vars1, const variables_type *vars2, double def_scale) + : m_def_scale (def_scale) +{ + mp_variables1 = vars1; + mp_variables2 = vars2; } // expression syntax taken from ngspice: @@ -264,13 +271,20 @@ NetlistSpiceReaderExpressionParser::read_atomic_value (tl::Extractor &ex, bool * } else { - auto vi = mp_variables->find (var); - if (vi != mp_variables->end ()) { - return vi->second; - } else { - // keep word as string value - return tl::Variant (var); + if (mp_variables1) { + auto vi = mp_variables1->find (var); + if (vi != mp_variables1->end ()) { + return vi->second; + } } + if (mp_variables2) { + auto vi = mp_variables2->find (var); + if (vi != mp_variables2->end ()) { + return vi->second; + } + } + // keep word as string value + return tl::Variant (var); } diff --git a/src/db/db/dbNetlistSpiceReaderExpressionParser.h b/src/db/db/dbNetlistSpiceReaderExpressionParser.h index 06684e124..822788587 100644 --- a/src/db/db/dbNetlistSpiceReaderExpressionParser.h +++ b/src/db/db/dbNetlistSpiceReaderExpressionParser.h @@ -46,6 +46,7 @@ public: typedef std::map variables_type; NetlistSpiceReaderExpressionParser (const variables_type *vars, double def_scale = 1.0); + NetlistSpiceReaderExpressionParser (const variables_type *vars1, const variables_type *vars2, double def_scale = 1.0); tl::Variant read (tl::Extractor &ex) const; tl::Variant read (const std::string &s) const; @@ -53,7 +54,7 @@ public: bool try_read (const std::string &s, tl::Variant &v) const; private: - const variables_type *mp_variables; + const variables_type *mp_variables1, *mp_variables2; double m_def_scale; tl::Variant read_atomic_value (tl::Extractor &ex, bool *status) const; diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc index d82d3b02e..927859725 100644 --- a/src/db/unit_tests/dbNetlistReaderTests.cc +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -752,6 +752,68 @@ TEST(19_ngspice_ref) ); } +// using parameters evaluated before inside formulas +TEST(19b_ngspice_ref) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader19b.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,V=1.5)' XPMOS (D=Q,G=I,S=VDD,B=VDD);\n" + " subcircuit 'NMOS4_STANDARD(L=0.15,NF=4,V=1.5)' XNMOS (D=Q,G=I,S=VSS,B=VSS);\n" + " subcircuit 'NMOS4_STANDARD(L=0.15,NF=2,V=1.5)' XDUMMY0 (D=VSS,G=VSS,S=VSS,B=VSS);\n" + " subcircuit 'NMOS4_STANDARD(L=0.15,NF=2,V=1.5)' XDUMMY1 (D=VSS,G=VSS,S=VSS,B=VSS);\n" + " subcircuit 'PMOS4_STANDARD(L=0.15,NF=2,V=1.5)' XDUMMY2 (D=VDD,G=VDD,S=VDD,B=VDD);\n" + " subcircuit 'PMOS4_STANDARD(L=0.15,NF=2,V=1.5)' XDUMMY3 (D=VDD,G=VDD,S=VDD,B=VDD);\n" + "end;\n" + "circuit 'PMOS4_STANDARD(L=0.15,NF=4,V=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,V=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,V=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,V=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" + ); +} + +// issue #1319, clarification +TEST(20_precendence) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::testdata (), "algo"), "nreader20.cir"); + + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); + + EXPECT_EQ (nl.to_string (), + "circuit TEST ();\n" + " device CAP '1' (A=A,B=B) (C=5e-12,A=0,P=0);\n" + " device CAP '2A' (A=A,B=B) (C=2.5e-12,A=0,P=0);\n" + " device CAP '2B' (A=A,B=B) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL1 '3' (A=A,B=B) (C=5e-12,A=0,P=0);\n" + " device CAP3 '4' (A=A,B=B,W=CAP_MODEL1) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL2 '5' (A=A,B=B,W=C) (C=5e-12,A=0,P=0);\n" + " device CAP_MODEL1 '6' (A=A,B=B) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL2 '7A' (A=A,B=B,W=C) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL2 '7B' (A=A,B=B,W=C) (C=2.5e-12,A=0,P=0);\n" + " device CAP_MODEL2 '8' (A=A,B=B,W=C) (C=2.5e-12,A=0,P=0);\n" + "end;\n" + ); +} + TEST(100_ExpressionParser) { std::map vars; diff --git a/testdata/algo/nreader19b.cir b/testdata/algo/nreader19b.cir new file mode 100644 index 000000000..5e2585fe8 --- /dev/null +++ b/testdata/algo/nreader19b.cir @@ -0,0 +1,29 @@ +* 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 v=1.5 l=0.15 nf=4 +XXnmos Q I VSS VSS nmos4_standard v=1.5 l=0.15 nf=4 +XXDUMMY0 VSS VSS VSS VSS nmos4_standard v=1.5 l=0.15 nf=2 +XXDUMMY1 VSS VSS VSS VSS nmos4_standard v=1.5 l=0.15 nf=2 +XXDUMMY2 VDD VDD VDD VDD pmos4_standard v=1.5 l=0.15 nf=2 +XXDUMMY3 VDD VDD VDD VDD pmos4_standard v=1.5 l=0.15 nf=2 + +* NOTE: "W" in the "ad" formula uses the previously computed parameter "W" +.subckt pmos4_standard D G S B v=0.1 l=0.018 nf=4 +MM1 D G S B sky130_fd_pr__pfet_01v8 L=l W='v * nf ' ad='int((nf+1)/2) * W/nf**2 * 0.29' as='int((nf+2)/2) * W/nf**2 * 0.29' ++ pd='2*int((nf+1)/2) * (W/nf**2 + 0.29)' ps='2*int((nf+2)/2) * (W/nf**2 + 0.29)' nrd='0.29 / W' nrs='0.29 / W' ++ m=1 +.ends + +* NOTE: "W" in the "ad" formula uses the previously computed parameter "W" +.subckt nmos4_standard D G S B v=0.1 l=0.018 nf=4 +MM1 D G S B sky130_fd_pr__nfet_01v8 L=l W='v * nf ' ad='int((nf+1)/2) * W/nf**2 * 0.29' as='int((nf+2)/2) * W/nf**2 * 0.29' ++ pd='2*int((nf+1)/2) * (W/nf**2 + 0.29)' ps='2*int((nf+2)/2) * (W/nf**2 + 0.29)' nrd='0.29 / W' nrs='0.29 / W' ++ m=1 +.ends + +.end diff --git a/testdata/algo/nreader20.cir b/testdata/algo/nreader20.cir new file mode 100644 index 000000000..7680c8468 --- /dev/null +++ b/testdata/algo/nreader20.cir @@ -0,0 +1,37 @@ + +.subckt test + +* Variations are: +* (1) Cname n1 n2 C=value [other params] +* (2) Cname n1 n2 value [params] +* (3) Cname n1 n2 model C=value [other params] +* Cname n1 n2 n3 C=value [other params] -> not supported, cannot tell from (3) without further analysis +* (4) Cname n1 n2 model value [params] +* Cname n1 n2 n3 value [params] -> not supported, cannot tell from (4) without further analysis +* (5) Cname n1 n2 n3 model C=value [other params] +* (6) Cname n1 n2 value model [params] +* (7) Cname n1 n2 n3 model value [params] +* (8) Cname n1 n2 n3 value model [params] + +* ngspice takes second C parameter (5p) +c1 a b M=1 l=50.000u w=50.000u c=2.5p c=5p +* ngspice ignores C parameter and uses 2.5p +c2a a b M=1 l=50.000u w=50.000u 2.5p c=5p +* ngspice ignores C parameter and uses 2.5p +c2b a b 2.5p M=1 l=50.000u w=50.000u c=5p +* ngspice ignores first C parameter and uses 5p +c3 a b cap_model1 c=2.5p M=1 l=50.000u w=50.000u c=5p +* ngspice ignores C parameter and uses 2.5p +c4 a b cap_model1 2.5p M=1 l=50.000u w=50.000u c=5p +* ngspice ignores first C parameter and uses 5p +c5 a b c cap_model2 M=1 l=50.000u w=50.000u c=2.5p c=5p +* C parameter must not be present for this to be recognized as value +c6 a b 2.5p cap_model1 M=1 l=50.000u w=50.000u +* ngspice ignores C parameter and uses 2.5p +c7a a b c cap_model2 2.5p M=1 l=50.000u w=50.000u c=5p +* ngspice ignores C parameter and uses 2.5p +c7b a b c cap_model2 M=1 l=50.000u w=50.000u 2.5p c=5p +* ngspice ignores C parameter and uses 2.5p +c8 a b c 2.5p cap_model2 M=1 l=50.000u w=50.000u c=5p +.ends +