From 343e340e22e9f5b3b72412b482bdc73b08a539f0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 22 Jun 2019 19:44:33 +0200 Subject: [PATCH] WIP: SPICE reader delegate, unit tests + debugging --- src/db/db/dbNetlistSpiceReader.cc | 101 ++++++++++++++-------- src/db/db/dbNetlistSpiceReader.h | 36 +++++--- src/db/unit_tests/dbNetlistReaderTests.cc | 77 +++++++++++++++++ testdata/algo/nreader6.cir | 30 +++++++ 4 files changed, 195 insertions(+), 49 deletions(-) create mode 100644 testdata/algo/nreader6.cir diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 2f37fc479..3b79eee2f 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -55,6 +55,11 @@ void NetlistSpiceReaderDelegate::finish (db::Netlist * /*netlist*/) // .. nothing yet .. } +bool NetlistSpiceReaderDelegate::wants_subcircuit (const std::string & /*circuit_name*/) +{ + return false; +} + void NetlistSpiceReaderDelegate::error (const std::string &msg) { throw tl::Exception (msg); @@ -160,8 +165,8 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin static const char *allowed_name_chars = "_.:,!+$/&\\#[]|"; -NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate, const std::string &captured_subcircuits) - : mp_netlist (0), mp_stream (0), mp_delegate (delegate), m_captured (captured_subcircuits) +NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate) + : mp_netlist (0), mp_stream (0), mp_delegate (delegate) { static NetlistSpiceReaderDelegate std_delegate; if (! delegate) { @@ -297,6 +302,18 @@ void NetlistSpiceReader::unget_line (const std::string &l) m_stored_line = l; } +bool NetlistSpiceReader::subcircuit_captured (const std::string &nc_name) +{ + std::map::const_iterator c = m_captured.find (nc_name); + if (c != m_captured.end ()) { + return c->second; + } else { + bool cap = mp_delegate->wants_subcircuit (nc_name); + m_captured.insert (std::make_pair (nc_name, cap)); + return cap; + } +} + bool NetlistSpiceReader::read_card () { std::string l = get_line (); @@ -318,7 +335,12 @@ bool NetlistSpiceReader::read_card () } else if (ex.test_without_case ("subckt")) { - read_circuit (ex); + std::string nc = read_name (ex); + if (subcircuit_captured (nc)) { + skip_circuit (ex); + } else { + read_circuit (ex, nc); + } } else if (ex.test_without_case ("ends")) { @@ -347,12 +369,12 @@ bool NetlistSpiceReader::read_card () std::string es; es.push_back (next_char); - if (next_char == 'X' && ! m_captured.match (name)) { - read_subcircuit (ex, name); - } else if (! read_element (ex, es, name)) { + if (! read_element (ex, es, name)) { warn (tl::sprintf (tl::to_string (tr ("Element type '%c' ignored")), next_char)); } + ex.expect_end (); + } else { warn (tl::to_string (tr ("Line ignored"))); } @@ -675,58 +697,67 @@ bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &ele nets.push_back (make_net (*i)); } - return mp_delegate->element (mp_circuit, element, name, tl::to_upper_case (model), value, nets, pv); + if (element == "X" && ! subcircuit_captured (model)) { + if (! pv.empty ()) { + warn (tl::to_string (tr ("Circuit parameters are not allowed currently"))); + } + read_subcircuit (name, tl::to_upper_case (model), nets); + return true; + } else { + return mp_delegate->element (mp_circuit, element, name, tl::to_upper_case (model), value, nets, pv); + } } -void NetlistSpiceReader::read_subcircuit (tl::Extractor &ex, const std::string &sc_name) +void NetlistSpiceReader::read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector &nets) { - std::vector nn; - std::map pv; - read_pin_and_parameters (ex, nn, pv); - - if (nn.empty ()) { - error (tl::to_string (tr ("No circuit name given for subcircuit call"))); - } - if (! pv.empty ()) { - warn (tl::to_string (tr ("Circuit parameters are not allowed currently"))); - } - - std::string nc = nn.back (); - nn.pop_back (); - - if (nn.empty ()) { + if (nets.empty ()) { error (tl::to_string (tr ("A circuit call needs at least one net"))); } - db::Circuit *cc = mp_netlist->circuit_by_name (nc); + db::Circuit *cc = mp_netlist->circuit_by_name (nc_name); if (! cc) { cc = new db::Circuit (); mp_netlist->add_circuit (cc); - cc->set_name (nc); - for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + cc->set_name (nc_name); + for (std::vector::const_iterator i = nets.begin (); i != nets.end (); ++i) { cc->add_pin (std::string ()); } } else { - if (cc->pin_count () != nn.size ()) { - error (tl::sprintf (tl::to_string (tr ("Pin count mismatch between circuit definition and circuit call: %d expected, got %d")), int (cc->pin_count ()), int (nn.size ()))); + if (cc->pin_count () != nets.size ()) { + error (tl::sprintf (tl::to_string (tr ("Pin count mismatch between circuit definition and circuit call: %d expected, got %d")), int (cc->pin_count ()), int (nets.size ()))); } } db::SubCircuit *sc = new db::SubCircuit (cc, sc_name); mp_circuit->add_subcircuit (sc); - for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { - db::Net *net = make_net (*i); - sc->connect_pin (i - nn.begin (), net); + for (std::vector::const_iterator i = nets.begin (); i != nets.end (); ++i) { + sc->connect_pin (i - nets.begin (), *i); } - - ex.expect_end (); } -void NetlistSpiceReader::read_circuit (tl::Extractor &ex) +void NetlistSpiceReader::skip_circuit (tl::Extractor & /*ex*/) { - std::string nc = read_name (ex); + while (! at_end ()) { + std::string l = get_line (); + tl::Extractor ex (l.c_str ()); + if (ex.test_without_case (".")) { + + // control statement + if (ex.test_without_case ("subckt")) { + skip_circuit (ex); + } else if (ex.test_without_case ("ends")) { + break; + } + + } + + } +} + +void NetlistSpiceReader::read_circuit (tl::Extractor &ex, const std::string &nc) +{ std::vector nn; std::map pv; read_pin_and_parameters (ex, nn, pv); diff --git a/src/db/db/dbNetlistSpiceReader.h b/src/db/db/dbNetlistSpiceReader.h index 7664b45f6..7de401f75 100644 --- a/src/db/db/dbNetlistSpiceReader.h +++ b/src/db/db/dbNetlistSpiceReader.h @@ -26,7 +26,6 @@ #include "dbCommon.h" #include "dbNetlistReader.h" #include "tlStream.h" -#include "tlGlobPattern.h" #include #include @@ -57,8 +56,8 @@ public: * * The reader delegate can be configured to recieve subcircuit elements too. * In this case, parameters are allowed. - * Such subcircuits must be included in the captured_subcircuits glob - * pattern when configuring the SPICE reader with the delegate. + * For receiving subcircuit elements, the delegate needs to indicate + * this by returning true upon "wants_subcircuit". */ class DB_PUBLIC NetlistSpiceReaderDelegate : public tl::Object @@ -77,16 +76,23 @@ public: */ virtual void finish (db::Netlist *netlist); + /** + * @brief Returns true, if the delegate wants subcircuit elements with this name + * + * The name is always upper case. + */ + virtual bool wants_subcircuit (const std::string &circuit_name); + /** * @brief Makes a device from an element line * - * @param circuit is the circuit that is currently read. - * @param element is the element code ("M", "R", ...). - * @param name is the element's name. - * @param model is the model name (may be empty). - * @param value is the default value (e.g. registance for resistors) and may be zero. - * @param nets are the nets given in the element line. - * @param parameters are the parameters of the element statement. + * @param circuit The circuit that is currently read. + * @param element The upper-case element code ("M", "R", ...). + * @param name The element's name. + * @param model The upper-case model name (may be empty). + * @param value The default value (e.g. registance for resistors) and may be zero. + * @param nets The nets given in the element line. + * @param parameters The parameters of the element statement (parameter names are upper case). * * The default implementation will create corresponding devices for * some known elements using the Spice writer's parameter conventions. @@ -108,7 +114,7 @@ class DB_PUBLIC NetlistSpiceReader : public NetlistReader { public: - NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate = 0, const std::string &captured_subcircuits = std::string ()); + NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate = 0); virtual ~NetlistSpiceReader (); virtual void read (tl::InputStream &stream, db::Netlist &netlist); @@ -121,15 +127,16 @@ private: std::vector > m_streams; std::auto_ptr > mp_nets_by_name; std::string m_stored_line; - tl::GlobPattern m_captured; + std::map m_captured; void push_stream (const std::string &path); void pop_stream (); bool at_end (); void read_pin_and_parameters (tl::Extractor &ex, std::vector &nn, std::map &pv); bool read_element (tl::Extractor &ex, const std::string &element, const std::string &name); - void read_subcircuit (tl::Extractor &ex, const std::string &sc_name); - void read_circuit (tl::Extractor &ex); + void read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector &nets); + void read_circuit (tl::Extractor &ex, const std::string &name); + void skip_circuit (tl::Extractor &ex); bool read_card (); double read_value (tl::Extractor &ex); std::string read_name (tl::Extractor &ex); @@ -143,6 +150,7 @@ private: void finish (); db::Net *make_net (const std::string &name); void ensure_circuit (); + bool subcircuit_captured (const std::string &nc_name); }; } diff --git a/src/db/unit_tests/dbNetlistReaderTests.cc b/src/db/unit_tests/dbNetlistReaderTests.cc index bfa6a5a70..c662f3fed 100644 --- a/src/db/unit_tests/dbNetlistReaderTests.cc +++ b/src/db/unit_tests/dbNetlistReaderTests.cc @@ -177,3 +177,80 @@ TEST(5_CircuitParameters) "end;\n" ); } + +class MyNetlistReaderDelegate + : public db::NetlistSpiceReaderDelegate +{ +public: + MyNetlistReaderDelegate () : db::NetlistSpiceReaderDelegate () { } + + bool wants_subcircuit (const std::string &circuit_name) + { + return circuit_name == "HVNMOS" || circuit_name == "HVPMOS"; + } + + bool element (db::Circuit *circuit, const std::string &element, const std::string &name, const std::string &model, double value, const std::vector &nets, const std::map ¶ms) + { + if (element == "X") { + + if (nets.size () != 4) { + error (tl::sprintf ("Device subcircuit '%s' requires four nets", model)); + } + + db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (model); + if (! cls) { + cls = new db::DeviceClassMOS4Transistor (); + cls->set_name (model); + circuit->netlist ()->add_device_class (cls); + } + + db::Device *device = new db::Device (cls); + device->set_name (name); + circuit->add_device (device); + + device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_S, nets [0]); + device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_G, nets [1]); + device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_D, nets [2]); + device->connect_terminal (db::DeviceClassMOS4Transistor::terminal_id_B, nets [3]); + + const std::vector &td = cls->parameter_definitions (); + for (std::vector::const_iterator i = td.begin (); i != td.end (); ++i) { + std::map::const_iterator pi = params.find (i->name ()); + if (pi != params.end ()) { + device->set_parameter_value (i->id (), pi->second * 1.5); + } + } + + return true; + + } else { + return db::NetlistSpiceReaderDelegate::element (circuit, element, name, model, value, nets, params); + } + + } +}; + +TEST(6_ReaderWithDelegate) +{ + db::Netlist nl; + + std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader6.cir"); + + MyNetlistReaderDelegate delegate; + db::NetlistSpiceReader reader (&delegate); + tl::InputStream is (path); + reader.read (is, nl); + + EXPECT_EQ (nl.to_string (), + "circuit SUBCKT ($1=$1,$2=A,$3=VDD,$4=Z,$5=gnd,$6=gnd$1);\n" + " device HVPMOS $1 (S=VDD,G=$3,D=Z,B=$1) (L=0.3,W=1.5,AS=0.27,AD=0.27,PS=3.24,PD=3.24);\n" + " device HVPMOS $2 (S=VDD,G=A,D=$3,B=$1) (L=0.3,W=1.5,AS=0.27,AD=0.27,PS=3.24,PD=3.24);\n" + " device HVNMOS $3 (S=gnd,G=$3,D=gnd,B=gnd$1) (L=1.695,W=3.18,AS=0,AD=0,PS=9,PD=9);\n" + " device HVNMOS $4 (S=gnd,G=$3,D=Z,B=gnd$1) (L=0.6,W=0.6,AS=0.285,AD=0.285,PS=1.74,PD=1.74);\n" + " device HVNMOS $5 (S=gnd,G=A,D=$3,B=gnd$1) (L=0.6,W=0.6,AS=0.285,AD=0.285,PS=2.64,PD=2.64);\n" + "end;\n" + "circuit .TOP ();\n" + " subcircuit SUBCKT SUBCKT ($1=(null),$2=(null),$3=(null),$4=(null),$5=VSS,$6=VSS);\n" + "end;\n" + ); +} diff --git a/testdata/algo/nreader6.cir b/testdata/algo/nreader6.cir new file mode 100644 index 000000000..76531a328 --- /dev/null +++ b/testdata/algo/nreader6.cir @@ -0,0 +1,30 @@ +* Tests the ability to translate subcircuits into plain devices + +.SUBCKT HVNMOS S G D B PARAMS: L=0 W=0 AS=0 AD=0 PS=0 PD=0 +MINT S G D B MODEL L=L W=W AS=AS AD=AD PS=PS PD=PD +* parasitic stuff +C1 S G 1.5F*L+5.5*L*W +C2 D G 1.2F*L+5.5*L*W +C3 S B 2.1F*AS+0.2*PS +C4 D B 2.1F*AD+0.2*PD +.ENDS HVNMOS + +.SUBCKT HVPMOS S G D B PARAMS: L=0 W=0 AS=0 AD=0 PS=0 PD=0 +MINT S G D B MODEL L=L W=W AS=AS AD=AD +* parasitic stuff +C1 S G 0.8F*L+4.2*L*W +C2 D G 0.8F*L+4.2*L*W +C3 S B 2.5F*AS+0.3*PS +C4 D B 2.5F*AD+0.3*PD +.ENDS HVPMOS + +.SUBCKT SUBCKT \$1 A VDD Z gnd gnd$1 +X$1 VDD \$3 Z \$1 HVPMOS PARAMS: L=0.2 W=1 AS=0.18 AD=0.18 PS=2.16 PD=2.16 +X$2 VDD A \$3 \$1 HVPMOS PARAMS: L=0.2 W=1 AS=0.18 AD=0.18 PS=2.16 PD=2.16 +X$3 gnd \$3 gnd gnd$1 HVNMOS PARAMS: L=1.13 W=2.12 PS=6 PD=6 AS=0 AD=0 +X$4 gnd \$3 Z gnd$1 HVNMOS PARAMS: L=0.4 W=0.4 PS=1.16 PD=1.16 AS=0.19 AD=0.19 +X$5 gnd A \$3 gnd$1 HVNMOS PARAMS: L=0.4 W=0.4 PS=1.76 PD=1.76 AS=0.19 AD=0.19 +.ENDS SUBCKT + +XSUBCKT IN OUT VDD Z VSS VSS SUBCKT +