diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 129fc248d..c019ef7c4 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -104,46 +104,66 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin std::string cn = model; db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (cn); - if (cls) { + if (element == "R") { - // use given class - - } else if (element == "R") { - - if (cn.empty ()) { - cn = "RES"; + if (cls) { + if (! dynamic_cast(cls)) { + error (tl::sprintf (tl::to_string (tr ("Class %s not a resistor device class as required by 'R' element")), cn)); + } + } else { + if (cn.empty ()) { + cn = "RES"; + } + cls = make_device_class (circuit, cn); } - cls = make_device_class (circuit, cn); // Apply multiplier value /= mult; } else if (element == "L") { - if (cn.empty ()) { - cn = "IND"; + if (cls) { + if (! dynamic_cast(cls)) { + error (tl::sprintf (tl::to_string (tr ("Class %s not a inductor device class as required by 'L' element")), cn)); + } + } else { + if (cn.empty ()) { + cn = "IND"; + } + cls = make_device_class (circuit, cn); } - cls = make_device_class (circuit, cn); // Apply multiplier value /= mult; } else if (element == "C") { - if (cn.empty ()) { - cn = "CAP"; + if (cls) { + if (! dynamic_cast(cls)) { + error (tl::sprintf (tl::to_string (tr ("Class %s not a capacitor device class as required by 'C' element")), cn)); + } + } else { + if (cn.empty ()) { + cn = "CAP"; + } + cls = make_device_class (circuit, cn); } - cls = make_device_class (circuit, cn); // Apply multiplier value *= mult; } else if (element == "D") { - if (cn.empty ()) { - cn = "DIODE"; + if (cls) { + if (! dynamic_cast(cls)) { + error (tl::sprintf (tl::to_string (tr ("Class %s not a diode device class as required by 'D' element")), cn)); + } + } else { + if (cn.empty ()) { + cn = "DIODE"; + } + cls = make_device_class (circuit, cn); } - cls = make_device_class (circuit, cn); // Apply multiplier to "A" std::map::iterator p; @@ -154,18 +174,30 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin } else if (element == "Q") { - if (nets.size () == 3) { - if (cn.empty ()) { - cn = "BJT3"; - } - cls = make_device_class (circuit, cn); - } else if (nets.size () == 4) { - if (cn.empty ()) { - cn = "BJT4"; - } - cls = make_device_class (circuit, cn); - } else { + if (nets.size () != 3 && nets.size () != 4) { error (tl::to_string (tr ("'Q' element needs to have 3 or 4 terminals"))); + } else if (cls) { + if (nets.size () == 3) { + if (! dynamic_cast(cls)) { + error (tl::sprintf (tl::to_string (tr ("Class %s not a 3-terminal BJT device class as required by 'Q' element")), cn)); + } + } else { + if (! dynamic_cast(cls)) { + error (tl::sprintf (tl::to_string (tr ("Class %s not a 4-terminal BJT device class as required by 'Q' element")), cn)); + } + } + } else { + if (nets.size () == 3) { + if (cn.empty ()) { + cn = "BJT3"; + } + cls = make_device_class (circuit, cn); + } else { + if (cn.empty ()) { + cn = "BJT4"; + } + cls = make_device_class (circuit, cn); + } } // Apply multiplier to "AE" @@ -177,22 +209,28 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin } else if (element == "M") { - if (nets.size () == 4) { - if (cn.empty ()) { - cn = "MOS4"; + if (cls) { + if (! dynamic_cast(cls)) { + error (tl::sprintf (tl::to_string (tr ("Class %s not a 4-terminal MOS device class as required by 'M' element")), cn)); } - cls = make_device_class (circuit, cn); - - // Apply multiplier to "W" - std::map::iterator p; - p = params.find ("W"); - if (p != params.end ()) { - p->second *= mult; - } - } else { - error (tl::to_string (tr ("'M' element needs to have 4 terminals"))); + if (nets.size () == 4) { + if (cn.empty ()) { + cn = "MOS4"; + } + cls = make_device_class (circuit, cn); + } else { + error (tl::to_string (tr ("'M' element needs to have 4 terminals"))); + } } + + // Apply multiplier to "W" + std::map::iterator p; + p = params.find ("W"); + if (p != params.end ()) { + p->second *= mult; + } + } else { error (tl::sprintf (tl::to_string (tr ("Not a known element type: '%s'")), element)); } diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc index 12f0a97f6..4e4779588 100644 --- a/src/db/unit_tests/dbNetlistReaderTests.cc +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -359,28 +359,75 @@ TEST(9_DeviceMultipliers) std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader9.cir"); - db::NetlistSpiceReader reader; - tl::InputStream is (path); - reader.read (is, nl); + { + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); - std::string nl_string = nl.to_string (); - // normalization of exponential representation: - nl_string = tl::replaced (nl_string, "e-009", "e-09"); + std::string nl_string = nl.to_string (); + // normalization of exponential representation: + nl_string = tl::replaced (nl_string, "e-009", "e-09"); - EXPECT_EQ (nl_string, - "circuit .TOP ();\n" - " device RES $1 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n" - " device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n" - " device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n" - " device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n" - " device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n" - " device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n" - " device DIODE $1 (A='1',C='2') (A=20,P=0);\n" - " device DIODE $2 (A='3',C='4') (A=10,P=0);\n" - " device BIP $1 (C='1',B='2',E='3',S='4') (AE=20,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n" - " device BIP $2 (C='1',B='2',E='3',S='4') (AE=10,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n" - "end;\n" - ); + EXPECT_EQ (nl_string, + "circuit .TOP ();\n" + " device RES $1 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n" + " device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n" + " device RMODEL $3 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n" + " device RMODEL $4 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n" + " device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n" + " device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n" + " device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n" + " device CMODEL $3 (A='1',B='2') (C=2e-09,A=0,P=0);\n" + " device CMODEL $4 (A='3',B='4') (C=1e-09,A=0,P=0);\n" + " device IND $1 (A='1',B='2') (L=5e-10);\n" + " device IND $2 (A='3',B='4') (L=1e-09);\n" + " device LMODEL $3 (A='1',B='2') (L=5e-10);\n" + " device LMODEL $4 (A='3',B='4') (L=1e-09);\n" + " device DIODE $1 (A='1',C='2') (A=20,P=0);\n" + " device DIODE $2 (A='3',C='4') (A=10,P=0);\n" + " device BIP $1 (C='1',B='2',E='3',S='4') (AE=20,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n" + " device BIP $2 (C='1',B='2',E='3',S='4') (AE=10,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n" + "end;\n" + ); + } + + db::Circuit *top = nl.circuit_by_name (".TOP"); + nl.remove_circuit (top); + + // read once again, this time with known classes (must not trigger issue-652) + { + db::NetlistSpiceReader reader; + tl::InputStream is (path); + reader.read (is, nl); + + std::string nl_string = nl.to_string (); + // normalization of exponential representation: + nl_string = tl::replaced (nl_string, "e-009", "e-09"); + + EXPECT_EQ (nl_string, + "circuit .TOP ();\n" + " device RES $1 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n" + " device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n" + " device RMODEL $3 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n" + " device RMODEL $4 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n" + " device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n" + " device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n" + " device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n" + " device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n" + " device CMODEL $3 (A='1',B='2') (C=2e-09,A=0,P=0);\n" + " device CMODEL $4 (A='3',B='4') (C=1e-09,A=0,P=0);\n" + " device IND $1 (A='1',B='2') (L=5e-10);\n" + " device IND $2 (A='3',B='4') (L=1e-09);\n" + " device LMODEL $3 (A='1',B='2') (L=5e-10);\n" + " device LMODEL $4 (A='3',B='4') (L=1e-09);\n" + " device DIODE $1 (A='1',C='2') (A=20,P=0);\n" + " device DIODE $2 (A='3',C='4') (A=10,P=0);\n" + " device BIP $1 (C='1',B='2',E='3',S='4') (AE=20,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n" + " device BIP $2 (C='1',B='2',E='3',S='4') (AE=10,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n" + "end;\n" + ); + } } TEST(10_SubcircuitsNoPins) diff --git a/testdata/algo/nreader9.cir b/testdata/algo/nreader9.cir index 302b7a0a2..e68e4bd20 100644 --- a/testdata/algo/nreader9.cir +++ b/testdata/algo/nreader9.cir @@ -1,10 +1,18 @@ R$1 1 2 1.7k M=2 R$2 3 4 1.7k +R$3 1 2 1.7k RMODEL M=2 +R$4 3 4 1.7k RMODEL M$1 1 2 3 4 NMOS W=2u L=7u M=2 M$2 1 2 3 4 PMOS W=2u L=7u C$1 1 2 1e-9 M=2 C$2 3 4 1e-9 +C$3 1 2 1e-9 CMODEL M=2 +C$4 3 4 1e-9 CMODEL +L$1 1 2 1e-9 M=2 +L$2 3 4 1e-9 +L$3 1 2 1e-9 LMODEL M=2 +L$4 3 4 1e-9 LMODEL D$1 1 2 DIODE A=10P M=2 D$2 3 4 DIODE A=10P Q$1 1 2 3 4 BIP AE=10P M=2