From 40fd350d85c4cb17f783d8f285499a870fe830e0 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Mon, 20 Feb 2023 22:06:45 +0100 Subject: [PATCH] WIP --- src/db/db/dbNetlistSpiceReader.cc | 1195 +++++++++++++++++++---------- src/db/db/dbNetlistSpiceReader.h | 79 +- src/db/db/gsiDeclDbNetlist.cc | 47 +- 3 files changed, 819 insertions(+), 502 deletions(-) diff --git a/src/db/db/dbNetlistSpiceReader.cc b/src/db/db/dbNetlistSpiceReader.cc index 04e8d94e8..2e70870b8 100644 --- a/src/db/db/dbNetlistSpiceReader.cc +++ b/src/db/db/dbNetlistSpiceReader.cc @@ -218,14 +218,26 @@ void NetlistSpiceReaderDelegate::parse_element_components (const std::string &s, } } -double NetlistSpiceReaderDelegate::read_atomic_value (tl::Extractor &ex) +double NetlistSpiceReaderDelegate::read_atomic_value (tl::Extractor &ex, const std::map &variables) { + std::string var; + if (ex.test ("(")) { - double v = read_dot_expr (ex); + double v = read_dot_expr (ex, variables); ex.expect (")"); return v; + } else if (try_read_word (var)) { + + auto v = variables.find (tl::to_upper_case (var)); + if (v != variables.end ()) { + return v->second; + } else { + error (tl::sprintf (tl::to_string (tr ("Undefined parameter '%s'")), var)); + return 0.0; + } + } else { double v = 0.0; @@ -264,15 +276,15 @@ double NetlistSpiceReaderDelegate::read_atomic_value (tl::Extractor &ex) } } -double NetlistSpiceReaderDelegate::read_bar_expr (tl::Extractor &ex) +double NetlistSpiceReaderDelegate::read_bar_expr (tl::Extractor &ex, const std::map &variables) { - double v = read_atomic_value (ex); + double v = read_atomic_value (ex, variables); while (true) { if (ex.test ("+")) { - double vv = read_atomic_value (ex); + double vv = read_atomic_value (ex, variables); v += vv; } else if (ex.test ("+")) { - double vv = read_atomic_value (ex); + double vv = read_atomic_value (ex, variables); v -= vv; } else { break; @@ -281,15 +293,15 @@ double NetlistSpiceReaderDelegate::read_bar_expr (tl::Extractor &ex) return v; } -double NetlistSpiceReaderDelegate::read_dot_expr (tl::Extractor &ex) +double NetlistSpiceReaderDelegate::read_dot_expr (tl::Extractor &ex, const std::map &variables) { - double v = read_bar_expr (ex); + double v = read_bar_expr (ex, variables); while (true) { if (ex.test ("*")) { - double vv = read_bar_expr (ex); + double vv = read_bar_expr (ex, variables); v *= vv; } else if (ex.test ("/")) { - double vv = read_bar_expr (ex); + double vv = read_bar_expr (ex, variables); v /= vv; } else { break; @@ -298,18 +310,18 @@ double NetlistSpiceReaderDelegate::read_dot_expr (tl::Extractor &ex) return v; } -double NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex) +double NetlistSpiceReaderDelegate::read_value (tl::Extractor &ex, const std::map &variables) { - return read_dot_expr (ex); + return read_dot_expr (ex, variables); } -bool NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &value) +bool NetlistSpiceReaderDelegate::try_read_value (const std::string &s, double &value, const std::map &variables) { tl::Extractor ve (s.c_str ()); double vv = 0; if (ve.try_read (vv) || ve.test ("(")) { ve = tl::Extractor (s.c_str ()); - value = read_value (ve); + value = read_value (ve, variables); return true; } else { return false; @@ -635,19 +647,54 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin // ------------------------------------------------------------------------------------------------------ -NetlistSpiceReader::SpiceReaderStream::SpiceReaderStream () +class SpiceReaderStream +{ +public: + SpiceReaderStream (); + ~SpiceReaderStream (); + + void set_stream (tl::InputStream &stream); + void set_stream (tl::InputStream *stream); + void close (); + + std::pair get_line(); + int line_number () const; + std::string source () const; + bool at_end () const; + + void swap (SpiceReaderStream &other) + { + std::swap (mp_stream, other.mp_stream); + std::swap (m_owns_stream, other.m_owns_stream); + std::swap (mp_text_stream, other.mp_text_stream); + std::swap (m_line_number, other.m_line_number); + std::swap (m_stored_line, other.m_stored_line); + std::swap (m_has_stored_line, other.m_has_stored_line); + } + +private: + tl::InputStream *mp_stream; + bool m_owns_stream; + tl::TextInputStream *mp_text_stream; + int m_line_number; + std::string m_stored_line; + bool m_has_stored_line; +}; + + +SpiceReaderStream::SpiceReaderStream () : mp_stream (0), m_owns_stream (false), mp_text_stream (0), m_line_number (0), m_stored_line (), m_has_stored_line (false) { // .. nothing yet .. } -NetlistSpiceReader::SpiceReaderStream::~SpiceReaderStream () +SpiceReaderStream::~SpiceReaderStream () { close (); } void -NetlistSpiceReader::SpiceReaderStream::close () +SpiceReaderStream::close () { delete mp_text_stream; mp_text_stream = 0; @@ -660,7 +707,7 @@ NetlistSpiceReader::SpiceReaderStream::close () } std::pair -NetlistSpiceReader::SpiceReaderStream::get_line () +SpiceReaderStream::get_line () { if (at_end ()) { return std::make_pair (std::string (), false); @@ -694,25 +741,25 @@ NetlistSpiceReader::SpiceReaderStream::get_line () } int -NetlistSpiceReader::SpiceReaderStream::line_number () const +SpiceReaderStream::line_number () const { return m_line_number; } std::string -NetlistSpiceReader::SpiceReaderStream::source () const +SpiceReaderStream::source () const { return mp_stream->source (); } bool -NetlistSpiceReader::SpiceReaderStream::at_end () const +SpiceReaderStream::at_end () const { return !m_has_stored_line && mp_text_stream->at_end (); } void -NetlistSpiceReader::SpiceReaderStream::set_stream (tl::InputStream &stream) +SpiceReaderStream::set_stream (tl::InputStream &stream) { close (); mp_stream = &stream; @@ -723,7 +770,7 @@ NetlistSpiceReader::SpiceReaderStream::set_stream (tl::InputStream &stream) } void -NetlistSpiceReader::SpiceReaderStream::set_stream (tl::InputStream *stream) +SpiceReaderStream::set_stream (tl::InputStream *stream) { close (); mp_stream = stream; @@ -735,66 +782,703 @@ NetlistSpiceReader::SpiceReaderStream::set_stream (tl::InputStream *stream) // ------------------------------------------------------------------------------------------------------ -NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate) - : mp_netlist (0), mp_delegate (delegate), m_stream () +struct ParametersLessFunction { - static NetlistSpiceReaderDelegate std_delegate; - if (! delegate) { - mp_delegate.reset (&std_delegate); - } -} + typedef std::map parameters_type; -NetlistSpiceReader::~NetlistSpiceReader () -{ - // .. nothing yet .. -} - -void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist) -{ - tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading netlist ")) + stream.source ()); - - m_stream.set_stream (stream); - - mp_netlist = &netlist; - mp_circuit = 0; - mp_anonymous_top_circuit = 0; - mp_nets_by_name.reset (0); - m_global_nets.clear (); - m_circuits_read.clear (); - - // SPICE netlists are case insensitive - netlist.set_case_sensitive (false); - - try { - - mp_delegate->start (&netlist); - - while (! at_end ()) { - read_card (); + bool operator< (const parameters_type &a, const parameters_type &b) + { + if (a.size () != b.size ()) { + return a.size () < b.size (); } - build_global_nets (); + auto ia = a.begin (); + auto ib = b.begin (); + while (ia != a.end ()) { + if (ia->first != ib->first) { + return ia->first < ib->first; + } + double avg = 0.5 * fabs (ia->second + ib->second); + if (fabs (ia->second - ib->second) > avg * db::epsilon) { + return ia->second < ib->second; + } + } - mp_delegate->finish (&netlist); - finish (); + return false; + } +}; - } catch (tl::Exception &ex) { +struct SpiceCard +{ + SpiceCard (int _file_id, int _line, const std::string &_text) + : file_id (_file_id), line (_line), text (_text) + { } - // NOTE: because we do a peek to capture the "+" line continuation character, we're - // one line ahead. - std::string fmt_msg = tl::sprintf ("%s in %s, line %d", ex.msg (), m_stream.source (), m_stream.line_number ()); - finish (); - throw tl::Exception (fmt_msg); + int file_id; + int line; + std::string text; +}; - } catch (...) { +class SpiceCachedCircuit +{ +public: + typedef std::list cards_type; + typedef cards_type::const_iterator cards_iterator; + typedef std::map parameters_type; - finish (); - throw; + SpiceCachedCircuit (const std::string &name) + : m_name (name) + { + // .. nothing yet .. + } + + void set_parameters (const parameters_type &pv) + { + m_parameters = pv; + } + + parameters_type ¶meters () const + { + return m_parameters; + } + + cards_iterator begin_cards () const + { + return m_cards.begin (); + } + + cards_iterator end_cards () const + { + return m_cards.end (); + } + + void add_card (const SpiceCard &card) + { + m_cars.push_back (card); + } + +private: + std::string m_name; + parameters_type m_parameters; + cards_type m_cards; +}; + +class SpiceCircuitDict +{ +public: + typedef std::map parameters_type; + + SpiceCircuitDict (); + ~SpiceCircuitDict (); + + void read (NetlistSpiceReader::SpiceReaderStream &stream); + void build (db::Netlist *netlist); + void finish (); + +private: + NetlistSpiceReader *mp_reader; + tl::weak_ptr mp_delegate; + Netlist *mp_netlist; + std::vector m_paths; + std::map m_file_id_per_path; + std::list m_streams; + SpiceReaderStream m_stream; + int m_file_id; + std::map m_circuits; + std::map m_cached_circuits; + SpiceCachedCircuit *mp_circuit; + SpiceCachedCircuit *mp_anonymous_top_level_circuit; + std::set m_called_circuits; + db::Circuit *mp_netlist_circuit; + std::unique_ptr > mp_nets_by_name; + std::map m_captured; + std::vector m_global_nets; + std::set m_global_net_names; + std::map m_variables; + + void push_stream (const std::string &path); + void pop_stream (); + bool at_end (); + 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); + bool read_card (); + void ensure_circuit (); + Circuit *build_circuit(SpiceCachedCircuit *circuit, const parameters_type &pv); + std::string read_name (tl::Extractor &ex); + std::string get_line (); + void error (const std::string &msg); + void warn (const std::string &msg); + void error (const std::string &msg, const SpiceCard &card); + void warn (const std::string &msg, const SpiceCard &card); + const std::string &file_path (int file_id) const; + int file_id (const std::string &path); + db::Circuit *circuit_for (const SpiceCachedCircuit *cached_circuit, const parameters_type &pv); + void register_circuit_for (const SpiceCachedCircuit *cc, const parameters_type &pv, db::Circuit *circuit); + const SpiceCachedCircuit *cached_circuit (const std::string &name) const; + SpiceCachedCircuit *create_cached_circuit (const std::string &name); + void make_net (const std::string &name); + void process_card (const SpiceCard &card); + bool subcircuit_captured (const std::string &nc_name); + bool process_element (tl::Extractor &ex, const std::string &prefix, const std::string &name, const SpiceCard &card); + void build_global_nets (); +}; + +SpiceCircuitDict::SpiceCircuitDict (NetlistSpiceReader *reader, NetlistSpiceReaderDelegate *delegate) + : mp_reader (reader), mp_delegate (delegate) +{ + m_file_id = -1; + mp_circuit = mp_anonymous_top_level_circuit = 0; +} + +SpiceCircuitDict::~SpiceCircuitDict () +{ + for (auto c = m_cached_circuits.begin (); c != m_cached_circuits.end (); ++c) { + delete c->second; + } + m_cached_circuits.clear (); + m_circuits.clear (); + mp_reader = 0; + mp_delegate = 0; +} + +const std::string & +SpiceCircuitDict::file_path (int file_id) const +{ + if (file_id < 0 || file_id > int (m_paths.size ())) { + static std::string empty; + return empty; + } else { + return m_paths [file_id]; + } +} + +int +SpiceCircuitDict::file_id (const std::string &path) +{ + auto ip = m_file_id_per_path.find (path); + if (ip != m_file_id_per_path.end ()) { + return ip->second; + } + + int id = int (m_paths.size ()); + m_file_id_per_path.insert (std::make_pair (path, id)); + m_paths.push_back (path); + return id; +} + +db::Circuit * +SpiceCircuitDict::circuit_for (const SpiceCachedCircuit *cc, const parameters_type &pv) +{ + auto c = m_circuits.find (cc); + if (c == m_circuits.end ()) { + return 0; + } + auto cp = c->second.find (pv); + if (cp == c->second.end ()) { + return 0; + } + return cp->second; +} + +void +SpiceCircuitDict::register_circuit_for (const SpiceCachedCircuit *cc, const parameters_type &pv, db::Circuit *circuit) +{ + m_circuits [cc][pv] = c; +} + +const SpiceCachedCircuit * +SpiceCircuitDict::cached_circuit (const std::string &name) const +{ + auto c = m_cached_circuits.find (name); + return c == m_cached_circuits.end () ? 0 : c->second; +} + +SpiceCachedCircuit * +SpiceCircuitDict::create_cached_circuit (const std::string &name) +{ + auto c = m_cached_circuits.find (name); + if (c != m_cached_circuits.end ()) { + return c->second; + } + + SpiceCachedCircuit *cc = new SpiceCachedCircuit (name); + m_cached_circuits.insert (std::make_pair (name, cc)); + return cc; +} + +void +SpiceCircuitDict::read (NetlistSpiceReader::SpiceReaderStream &stream) +{ + m_stream.set_stream (stream); + mp_netlist = 0; + + mp_circuit = 0; + mp_anonymous_top_level_circuit = 0; + m_global_nets.clear (); + m_called_circuits.clear (); + + while (! at_end ()) { + read_card (); + } + + NetlistSpiceReader::SpiceReaderStream null_stream; + m_stream.set_stream (null_stream); +} + +void +SpiceCircuitDict::push_stream (const std::string &path) +{ + tl::URI current_uri (m_stream.source ()); + tl::URI new_uri (path); + + tl::InputStream *istream; + if (current_uri.scheme ().empty () && new_uri.scheme ().empty ()) { + if (tl::is_absolute (path)) { + istream = new tl::InputStream (path); + } else { + istream = new tl::InputStream (tl::combine_path (tl::dirname (m_stream.source ()), path)); + } + } else { + istream = new tl::InputStream (current_uri.resolved (new_uri).to_abstract_path ()); + } + + m_streams.push_back (SpiceReaderStream ()); + m_streams.back ().swap (m_stream); + m_stream.set_stream (istream); + + m_file_id = file_id (m_stream.source ()); +} + +void +SpiceCircuitDict::pop_stream () +{ + if (! m_streams.empty ()) { + + m_stream.swap (m_streams.back ()); + m_streams.pop_back (); + + m_file_id = file_id (m_stream.source ()); } } -void NetlistSpiceReader::build_global_nets () +bool +SpiceCircuitDict::at_end () +{ + return m_stream.at_end () && m_streams.empty (); +} + +void +SpiceCircuitDict::error (const std::string &msg) +{ + std::string fmt_msg = tl::sprintf ("%s in %s, line %d", msg, m_stream.source (), m_stream.line_number ()); + throw tl::Exception (fmt_msg); +} + +void +SpiceCircuitDict::error (const std::string &msg, const SpiceCard &card) +{ + std::string fmt_msg = tl::sprintf ("%s in %s, line %d", msg, file_path (card.file_id), card.line); + throw tl::Exception (fmt_msg); +} + +void +SpiceCircuitDict::warn (const std::string &msg) +{ + std::string fmt_msg = tl::sprintf ("%s in %s, line %d", msg, m_stream.source (), m_stream.line_number ()); + tl::warn << fmt_msg; +} + +void +SpiceCircuitDict::warn (const std::string &msg, const SpiceCard &card) +{ + std::string fmt_msg = tl::sprintf ("%s in %s, line %d", msg, file_path (card.file_id), card.line); + tl::warn << fmt_msg; +} + +std::string +SpiceCircuitDict::get_line () +{ + std::pair lp; + + while (true) { + + lp = m_stream.get_line (); + if (! lp.second) { + + if (m_streams.empty ()) { + break; + } else { + pop_stream (); + } + + } else { + + tl::Extractor ex (lp.first.c_str ()); + if (ex.test_without_case (".include") || ex.test_without_case (".inc")) { + + std::string path; + ex.read_word_or_quoted (path, allowed_name_chars); + + push_stream (path); + + } else if (ex.at_end () || ex.test ("*")) { + + // skip empty and comment lines + + } else { + break; + } + + } + + } + + return lp.first; +} + +std::string +SpiceCircuitDict::read_name (tl::Extractor &ex) +{ + std::string n; + ex.read_word_or_quoted (n, allowed_name_chars); + return mp_netlist->normalize_name (n); +} + +bool +SpiceCircuitDict::read_card () +{ + std::string l = get_line (); + if (l.empty ()) { + return false; + } + + tl::Extractor ex (l.c_str ()); + std::string name; + + if (ex.test_without_case (".")) { + + // control statement + if (ex.test_without_case ("model")) { + + // ignore model statements + + } else if (ex.test_without_case ("global")) { + + while (! ex.at_end ()) { + std::string n = mp_delegate->translate_net_name (read_name (ex)); + if (m_global_net_names.find (n) == m_global_net_names.end ()) { + m_global_nets.push_back (n); + m_global_net_names.insert (n); + } + } + + } else if (ex.test_without_case ("subckt")) { + + std::string nc = read_name (ex); + read_circuit (ex, nc); + + } else if (ex.test_without_case ("ends")) { + + return true; + + } else if (ex.test_without_case ("end")) { + + // ignore end statements + + } else if (! mp_delegate->control_statement (l)) { + + std::string s; + ex.read_word (s); + s = tl::to_lower_case (s); + warn (tl::to_string (tr ("Control statement ignored: ")) + s); + + } + + } else if (ex.try_read_word (name)) { + + ensure_circuit (); + + if (ex.test ("=")) { + + name = tl::to_upper_case (name); + + // @@@ parameter assignment + double value = read_value (); // @@@ must be possible to compute + // @@@ set variable + mp_circuit->make_parameter (name, value); // @@@ turn circuit "pin" into model parameter + + } + + if (name[0] == 'X') { + + // register circuit calls so we can figure out the top level circuits + + tl::Extractor ex2 (l.c_str ()); + ex2.skip (); + ++ex2; + + std::vector nn; + parameters_type pv; + NetlistSpiceReaderDelegate::parse_element_components (ex2.get (), nn, pv); + + if (! nn.empty ()) { + m_called_circuits.insert (nn.back ()); + } + + } + + mp_circuit->add_card (SpiceCard (m_file_id, m_stream.line_number (), l); + + } else { + warn (tl::to_string (tr ("Line ignored"))); + } + + return false; +} + +void +SpiceCircuitDict::ensure_circuit () +{ + if (! mp_circuit) { + + // TODO: make top name configurable + mp_circuit = new SpiceCachedCircuit (".TOP"); + mp_anonymous_top_circuit = mp_circuit; + + } +} + +void +SpiceCircuitDict::read_circuit (tl::Extractor &ex, const std::string &nc) +{ + std::vector nn; + std::map pv; + NetlistSpiceReaderDelegate::parse_element_components (ex.skip (), nn, pv); + + if (cached_circuit (nc)) { + error (tl::sprintf (tl::to_string (tr ("Redefinition of circuit %s")), nc)); + } + + SpiceCachedCircuit *cc = create_cached_circuit (nc); + cc->set_pins (nn); + cc->set_parameters (pv); + + std::swap (cc, mp_circuit); + + while (! at_end ()) { + if (read_card ()) { + break; + } + } + + std::swap (cc, mp_circuit); +} + +void +SpiceCircuitDict::finish () +{ + m_streams.clear (); + m_stream.close (); + + mp_netlist = 0; + mp_delegate = 0; + mp_circuit = 0; + mp_nets_by_name.reset (0); +} + +// ......................... + +void +SpiceCircuitDict::build (db::Netlist *netlist) +{ + mp_netlist = &netlist; + m_captured.clear (); + + mp_delegate->start (&netlist); + + for (auto c = m_cached_circuits.begin (); c != m_cached_circuits.end (); ++c) { + if (m_called_circuits.find (c->first) == m_called_circuits.end () && mp_delegate->wants_subcircuit (c->first)) { + // we have a top circuit candidate + build_circuit (c->second, c->second->parameters ()); + } + } + + build_global_nets (); + mp_delegate->finish (mp_netlist); + + mp_netlist = 0; +} + +db::Circuit * +SpiceCircuitDict::build_circuit (SpiceCachedCircuit *cc, const parameters_type &pv) +{ + db::Circuit *c = circuit_for (cc, pv); + if (c) { + return c; + } + + c = new db::Circuit (); + mp_netlist->add_circuit (c); + c->set_name (nc); // @@@ unique name!!!! + + for (auto p = cc->begin_pins (); p != cc->end_pins (); ++p) { + // we'll make the names later + c->add_pin (std::string ()); + } + + register_circuit_for (cc, pv, c); + + std::unique_ptr > n2n (mp_nets_by_name.release ()); + mp_nets_by_name.reset (0); + + std::swap (var, m_variables); + std::swap (c, mp_netlist_circuit); + std::swap (cc, mp_circuit); + + // produce the explicit pins + for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { + db::Net *net = make_net (*i); + // use the net name to name the pin (otherwise SPICE pins are always unnamed) + size_t pin_id = i - nn.begin (); + if (! i->empty ()) { + c->add_pin (net->name ()); + } else { + c->add_pin (std::string ()); + } + mp_netlist_circuit->connect_pin (pin_id, net); + } + + for (auto card = cc->begin_cards (); card != cc->end_cards (); ++card) { + process_card (*card); + } + + mp_nets_by_name.reset (n2n.release ()); + + std::swap (cc, mp_circuit); + std::swap (c, mp_netlist_circuit); + std::swap (var, m_variables); +} + +void +SpiceCircuitDict::make_net (const std::string &name) +{ + if (! mp_nets_by_name.get ()) { + mp_nets_by_name.reset (new std::map ()); + } + + std::map::const_iterator n2n = mp_nets_by_name->find (name); + + db::Net *net = 0; + if (n2n == mp_nets_by_name->end ()) { + + net = new db::Net (); + net->set_name (name); + mp_circuit->add_net (net); + + mp_nets_by_name->insert (std::make_pair (name, net)); + + } else { + net = n2n->second; + } + + return net; +} + +void +SpiceCircuitDict::process_card (const SpiceCard &card) +{ + tl::Extractor ex (card->text.c_str ()); + + std::string name; + if (ex.try_read_word (name) && ex.test ("=")) { + + m_variables.insert (std::make_pair (tl::to_upper_case (word), read_value (ex))); + + } else { + + ex = tl::Extractor (card->text.c_str ()); + ex.skip (); + + if (is_alpha (*ex)) { + + ++ex; + name = read_name (ex); + + std::string prefix; + prefix.push_back (to_upper (*ex)); + + if (! process_element (ex, es, name, card)) { + warn (tl::sprintf (tl::to_string (tr ("Element type '%s' ignored")), prefix), card); + } + + } else { + warn (tl::to_string (tr ("Line ignored")), card); + } + + } +} + +bool +SpiceCircuitDict::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 +SpiceCircuitDict::process_element (tl::Extractor &ex, const std::string &prefix, const std::string &name, const SpiceCard &card) +{ + // generic parse + std::vector nn; + std::map pv; + std::string model; + double value = 0.0; + + mp_delegate->parse_element (ex.skip (), prefix, model, value, nn, pv); + + model = mp_netlist->normalize_name (model); + + 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)))); + } + + if (prefix == "X" && ! subcircuit_captured (model)) { + + db::SpiceCachedCircuit *cc = cached_circuit (model); + if (! cc) { + error (tl::sprintf (tl::to_string (tr ("Subcircuit '%s' not found in netlist")), model), card); + } + + 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 (nets.size ())), card); + } + + db::Circuit *c = build_circuit (cc, pv); + + db::SubCircuit *sc = new db::SubCircuit (c, name); + mp_netlist_circuit->add_subcircuit (sc); + + for (std::vector::const_iterator i = nets.begin (); i != nets.end (); ++i) { + sc->connect_pin (i - nets.begin (), *i); + } + + return true; + + } else { + return mp_delegate->element (mp_circuit, element, name, model, value, nets, pv); + } +} + +void +SpiceCircuitDict::build_global_nets () { for (std::vector::const_iterator gn = m_global_nets.begin (); gn != m_global_nets.end (); ++gn) { @@ -835,373 +1519,44 @@ void NetlistSpiceReader::build_global_nets () } } +// ------------------------------------------------------------------------------------------------------ -void NetlistSpiceReader::finish () +NetlistSpiceReader::NetlistSpiceReader (NetlistSpiceReaderDelegate *delegate) + : mp_netlist (0), mp_delegate (delegate), m_stream () { - m_streams.clear (); - m_stream.close (); - - mp_netlist = 0; - mp_circuit = 0; - mp_nets_by_name.reset (0); + static NetlistSpiceReaderDelegate std_delegate; + if (! delegate) { + mp_delegate.reset (&std_delegate); + } } -void NetlistSpiceReader::push_stream (const std::string &path) +NetlistSpiceReader::~NetlistSpiceReader () { - tl::URI current_uri (m_stream.source ()); - tl::URI new_uri (path); - - tl::InputStream *istream; - if (current_uri.scheme ().empty () && new_uri.scheme ().empty ()) { - if (tl::is_absolute (path)) { - istream = new tl::InputStream (path); - } else { - istream = new tl::InputStream (tl::combine_path (tl::dirname (m_stream.source ()), path)); - } - } else { - istream = new tl::InputStream (current_uri.resolved (new_uri).to_abstract_path ()); - } - - m_streams.push_back (SpiceReaderStream ()); - m_streams.back ().swap (m_stream); - m_stream.set_stream (istream); + // .. nothing yet .. } -void NetlistSpiceReader::pop_stream () +void NetlistSpiceReader::read (tl::InputStream &stream, db::Netlist &netlist) { - if (! m_streams.empty ()) { - m_stream.swap (m_streams.back ()); - m_streams.pop_back (); - } -} + tl::SelfTimer timer (tl::verbosity () >= 21, tl::to_string (tr ("Reading netlist ")) + stream.source ()); -bool NetlistSpiceReader::at_end () -{ - return m_stream.at_end () && m_streams.empty (); -} + // SPICE netlists are case insensitive + netlist.set_case_sensitive (false); -std::string NetlistSpiceReader::get_line () -{ - std::pair lp; + SpiceCircuitDict dict (this, mp_delegate); - while (true) { + try { - lp = m_stream.get_line (); - if (! lp.second) { + dict.read (stream); + dict.build (&netlist); - if (m_streams.empty ()) { - break; - } else { - pop_stream (); - } + dict.finish (); - } else { + } catch (...) { - tl::Extractor ex (lp.first.c_str ()); - if (ex.test_without_case (".include") || ex.test_without_case (".inc")) { - - std::string path; - ex.read_word_or_quoted (path, allowed_name_chars); - - push_stream (path); - - } else if (ex.at_end () || ex.test ("*")) { - - // skip empty and comment lines - - } else { - break; - } - - } - - } - - return lp.first; -} - -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 (); - if (l.empty ()) { - return false; - } - - tl::Extractor ex (l.c_str ()); - - ex.skip (); - char next_char = toupper (*ex); - - if (ex.test_without_case (".")) { - - // control statement - if (ex.test_without_case ("model")) { - - // ignore model statements - - } else if (ex.test_without_case ("global")) { - - while (! ex.at_end ()) { - std::string n = mp_delegate->translate_net_name (read_name (ex)); - if (m_global_net_names.find (n) == m_global_net_names.end ()) { - m_global_nets.push_back (n); - m_global_net_names.insert (n); - } - } - - } else if (ex.test_without_case ("subckt")) { - - 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")) { - - return true; - - } else if (ex.test_without_case ("end")) { - - // ignore end statements - - } else if (! mp_delegate->control_statement (l)) { - - std::string s; - ex.read_word (s); - s = tl::to_lower_case (s); - warn (tl::to_string (tr ("Control statement ignored: ")) + s); - - } - - } else if (isalpha (next_char)) { - - ++ex; - - std::string name = read_name (ex); - ensure_circuit (); - - std::string es; - es.push_back (next_char); - - if (! read_element (ex, es, name)) { - warn (tl::sprintf (tl::to_string (tr ("Element type '%c' ignored")), next_char)); - } - - } else { - warn (tl::to_string (tr ("Line ignored"))); - } - - return false; -} - -void NetlistSpiceReader::error (const std::string &msg) -{ - throw tl::Exception (msg); -} - -void NetlistSpiceReader::warn (const std::string &msg) -{ - std::string fmt_msg = tl::sprintf ("%s in %s, line %d", msg, m_stream.source (), m_stream.line_number ()); - tl::warn << fmt_msg; -} - -void NetlistSpiceReader::ensure_circuit () -{ - if (! mp_circuit) { - - mp_circuit = new db::Circuit (); - // TODO: make top name configurable - mp_circuit->set_name (".TOP"); - mp_anonymous_top_circuit = mp_circuit; - mp_netlist->add_circuit (mp_circuit); + dict.finish (); + throw; } } -db::Net *NetlistSpiceReader::make_net (const std::string &name) -{ - if (! mp_nets_by_name.get ()) { - mp_nets_by_name.reset (new std::map ()); - } - - std::map::const_iterator n2n = mp_nets_by_name->find (name); - - db::Net *net = 0; - if (n2n == mp_nets_by_name->end ()) { - - net = new db::Net (); - net->set_name (name); - mp_circuit->add_net (net); - - mp_nets_by_name->insert (std::make_pair (name, net)); - - } else { - net = n2n->second; - } - - return net; -} - -std::string NetlistSpiceReader::read_name (tl::Extractor &ex) -{ - std::string n; - ex.read_word_or_quoted (n, allowed_name_chars); - return mp_netlist->normalize_name (n); -} - -bool NetlistSpiceReader::read_element (tl::Extractor &ex, const std::string &element, const std::string &name) -{ - // generic parse - std::vector nn; - std::map pv; - std::string model; - double value = 0.0; - - mp_delegate->parse_element (ex.skip (), element, model, value, nn, pv); - - model = mp_netlist->normalize_name (model); - - 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)))); - } - - if (element == "X" && ! subcircuit_captured (model)) { - if (! pv.empty ()) { - warn (tl::to_string (tr ("Circuit parameters are not allowed currently"))); - } - read_subcircuit (name, model, nets); - return true; - } else { - return mp_delegate->element (mp_circuit, element, name, model, value, nets, pv); - } -} - -void NetlistSpiceReader::read_subcircuit (const std::string &sc_name, const std::string &nc_name, const std::vector &nets) -{ - 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_name); - - // we'll make the names later ... - for (std::vector::const_iterator i = nets.begin (); i != nets.end (); ++i) { - cc->add_pin (std::string ()); - } - - } else { - - 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 = nets.begin (); i != nets.end (); ++i) { - sc->connect_pin (i - nets.begin (), *i); - } -} - -void NetlistSpiceReader::skip_circuit (tl::Extractor & /*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; - mp_delegate->parse_element_components (ex.skip (), nn, pv); - - for (std::vector::iterator i = nn.begin (); i != nn.end (); ++i) { - *i = mp_delegate->translate_net_name (mp_netlist->normalize_name (*i)); - } - - if (! pv.empty ()) { - warn (tl::to_string (tr ("Circuit parameters are not allowed currently"))); - } - - db::Circuit *cc = mp_netlist->circuit_by_name (nc); - 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->add_pin (std::string ()); - } - - } else { - - if (cc->pin_count () != nn.size ()) { - error (tl::sprintf (tl::to_string (tr ("Pin count mismatch between implicit (through call) and explicit circuit definition: %d expected, got %d in circuit %s")), int (cc->pin_count ()), int (nn.size ()), nc)); - } - - } - - if (m_circuits_read.find (cc) != m_circuits_read.end ()) { - error (tl::sprintf (tl::to_string (tr ("Redefinition of circuit %s")), nc)); - } - m_circuits_read.insert (cc); - - std::unique_ptr > n2n (mp_nets_by_name.release ()); - mp_nets_by_name.reset (0); - - std::swap (cc, mp_circuit); - - // produce the explicit pins - for (std::vector::const_iterator i = nn.begin (); i != nn.end (); ++i) { - db::Net *net = make_net (*i); - // use the net name to name the pin (otherwise SPICE pins are always unnamed) - size_t pin_id = i - nn.begin (); - if (! i->empty ()) { - mp_circuit->rename_pin (pin_id, net->name ()); - } - mp_circuit->connect_pin (pin_id, net); - } - - while (! at_end ()) { - if (read_card ()) { - break; - } - } - - mp_nets_by_name.reset (n2n.release ()); - std::swap (cc, mp_circuit); -} - } diff --git a/src/db/db/dbNetlistSpiceReader.h b/src/db/db/dbNetlistSpiceReader.h index 45d273939..25ee403af 100644 --- a/src/db/db/dbNetlistSpiceReader.h +++ b/src/db/db/dbNetlistSpiceReader.h @@ -127,7 +127,7 @@ public: * @param nn Out parameter: the net names * @param pv Out parameter: the parameter values (key/value pairs) */ - virtual void parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector &nn, std::map &pv); + virtual void parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector &nn, std::map &pv, const std::map ¶ms); /** * @brief Produces an error with the given message @@ -138,22 +138,22 @@ public: * @brief Reads a set of string components and parameters from the string * A special key "param:" is recognized for starting a parameter list. */ - void parse_element_components (const std::string &s, std::vector &strings, std::map &pv); + static 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) */ - double read_value (tl::Extractor &ex); + static double read_value (tl::Extractor &ex, const std::map &variables); /** * @brief Tries to read a value from the extractor (with formula evaluation) */ - bool try_read_value (const std::string &s, double &v); + static bool try_read_value (const std::string &s, double &v, const std::map &variables); private: - double read_atomic_value (tl::Extractor &ex); - double read_dot_expr (tl::Extractor &ex); - double read_bar_expr (tl::Extractor &ex); + static double read_atomic_value (tl::Extractor &ex, const std::map &variables); + static double read_dot_expr (tl::Extractor &ex, const std::map &variables); + static double read_bar_expr (tl::Extractor &ex, const std::map &variables); }; /** @@ -169,70 +169,7 @@ public: virtual void read (tl::InputStream &stream, db::Netlist &netlist); private: - - class SpiceReaderStream - { - public: - SpiceReaderStream (); - ~SpiceReaderStream (); - - void set_stream (tl::InputStream &stream); - void set_stream (tl::InputStream *stream); - void close (); - - std::pair get_line(); - int line_number () const; - std::string source () const; - bool at_end () const; - - void swap (SpiceReaderStream &other) - { - std::swap (mp_stream, other.mp_stream); - std::swap (m_owns_stream, other.m_owns_stream); - std::swap (mp_text_stream, other.mp_text_stream); - std::swap (m_line_number, other.m_line_number); - std::swap (m_stored_line, other.m_stored_line); - std::swap (m_has_stored_line, other.m_has_stored_line); - } - - private: - tl::InputStream *mp_stream; - bool m_owns_stream; - tl::TextInputStream *mp_text_stream; - int m_line_number; - std::string m_stored_line; - bool m_has_stored_line; - }; - - db::Netlist *mp_netlist; - db::Circuit *mp_circuit; - db::Circuit *mp_anonymous_top_circuit; - tl::weak_ptr mp_delegate; - std::list m_streams; - SpiceReaderStream m_stream; - std::unique_ptr > mp_nets_by_name; - std::map m_captured; - std::vector m_global_nets; - std::set m_global_net_names; - std::set m_circuits_read; - - void push_stream (const std::string &path); - void pop_stream (); - bool at_end (); - bool read_element (tl::Extractor &ex, const std::string &element, const std::string &name); - 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 (); - std::string read_name (tl::Extractor &ex); - std::string get_line (); - void error (const std::string &msg); - void warn (const std::string &msg); - void finish (); - db::Net *make_net (const std::string &name); - void ensure_circuit (); - bool subcircuit_captured (const std::string &nc_name); - void build_global_nets (); + friend class SpiceReaderDict; }; } diff --git a/src/db/db/gsiDeclDbNetlist.cc b/src/db/db/gsiDeclDbNetlist.cc index c083786ac..13a3e3f35 100644 --- a/src/db/db/gsiDeclDbNetlist.cc +++ b/src/db/db/gsiDeclDbNetlist.cc @@ -2456,7 +2456,7 @@ class NetlistSpiceReaderDelegateImpl { public: NetlistSpiceReaderDelegateImpl () - : db::NetlistSpiceReaderDelegate () + : db::NetlistSpiceReaderDelegate (), mp_variables (0) { // .. nothing yet .. } @@ -2566,15 +2566,16 @@ public: ParseElementData parse_element_helper (const std::string &s, const std::string &element) { ParseElementData data; - db::NetlistSpiceReaderDelegate::parse_element (s, element, data.model_name_nc (), data.value_nc (), data.net_names_nc (), data.parameters_nc ()); + db::NetlistSpiceReaderDelegate::parse_element (s, element, data.model_name_nc (), data.value_nc (), data.net_names_nc (), data.parameters_nc (), variables ()); return data; } - virtual void parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector &nn, std::map &pv) + virtual void parse_element (const std::string &s, const std::string &element, std::string &model, double &value, std::vector &nn, std::map &pv, const std::map &variables) { try { m_error.clear (); + mp_variables = &variables; ParseElementData data; if (cb_parse_element.can_issue ()) { @@ -2588,12 +2589,18 @@ public: nn = data.net_names (); pv = data.parameters (); + mp_variables = 0; + } catch (tl::Exception &) { + mp_variables = 0; if (! m_error.empty ()) { db::NetlistSpiceReaderDelegate::error (m_error); } else { throw; } + } catch (...) { + mp_variables = 0; + throw; } } @@ -2616,6 +2623,12 @@ public: } } + const std::map &variables () const + { + static std::map empty; + return mp_variables ? *mp_variables : empty; + } + gsi::Callback cb_start; gsi::Callback cb_finish; gsi::Callback cb_control_statement; @@ -2626,6 +2639,7 @@ public: private: std::string m_error; + const std::map *mp_variables; }; static void start_fb (NetlistSpiceReaderDelegateImpl *delegate, db::Netlist *netlist) @@ -2663,20 +2677,20 @@ static ParseElementData parse_element_fb (NetlistSpiceReaderDelegateImpl *delega return delegate->parse_element_helper (s, element); } -static tl::Variant value_from_string (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s) +static tl::Variant value_from_string (NetlistSpiceReaderDelegateImpl * /*delegate*/, const std::string &s, const std::map &variables) { tl::Variant res; double v = 0.0; - if (delegate->try_read_value (s, v)) { + if (db::NetlistSpiceReaderDelegate::try_read_value (s, v, variables)) { res = v; } return res; } -static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl *delegate, const std::string &s) +static ParseElementComponentsData parse_element_components (NetlistSpiceReaderDelegateImpl * /*delegate*/, const std::string &s, const std::map &variables) { ParseElementComponentsData data; - delegate->parse_element_components (s, data.strings_nc (), data.parameters_nc ()); + db::NetlistSpiceReaderDelegate::parse_element_components (s, data.strings_nc (), data.parameters_nc (), variables); return data; } @@ -2765,6 +2779,13 @@ Class db_NetlistSpiceReaderDelegate ("db", "Netl "\n" "This method has been introduced in version 0.27.1\n" ) + + gsi::method ("variables", &NetlistSpiceReaderDelegateImpl::variables, + "@brief Gets the variables defined inside the SPICE file during execution of 'parse_element'\n" + "In order to evaluate formulas, this method allows accessing the variables that are " + "present during the execution of the SPICE reader.\n" + "\n" + "This method has been introduced in version 0.28.7." + ) + gsi::callback ("parse_element", &NetlistSpiceReaderDelegateImpl::parse_element_helper, &NetlistSpiceReaderDelegateImpl::cb_parse_element, gsi::arg ("s"), gsi::arg ("element"), "@brief Parses an element card\n" @@ -2800,22 +2821,26 @@ Class db_NetlistSpiceReaderDelegate ("db", "Netl "@brief Issues an error with the given message.\n" "Use this method to generate an error." ) + - gsi::method_ext ("value_from_string", &value_from_string, gsi::arg ("s"), + gsi::method_ext ("value_from_string", &value_from_string, gsi::arg ("s"), gsi::arg ("variables", std::map (), "{}"), "@brief Translates a string into a value\n" "This function simplifies the implementation of SPICE readers by providing a translation of a unit-annotated string " "into double values. For example, '1k' is translated to 1000.0. In addition, simple formula evaluation is supported, e.g " "'(1+3)*2' is translated into 8.0.\n" "\n" - "This method has been introduced in version 0.27.1\n" + "The variables dictionary defines named variables with the given values.\n" + "\n" + "This method has been introduced in version 0.27.1. The variables argument has been added in version 0.28.7.\n" ) + - gsi::method_ext ("parse_element_components", &parse_element_components, gsi::arg ("s"), + gsi::method_ext ("parse_element_components", &parse_element_components, gsi::arg ("s"), gsi::arg ("variables", std::map (), "{}"), "@brief Parses a string into string and parameter components.\n" "This method is provided to simplify the implementation of 'parse_element'. It takes a string and splits it into " "string arguments and parameter values. For example, 'a b c=6' renders two string arguments in 'nn' and one parameter ('C'->6.0). " "It returns data \\ParseElementComponentsData object with the strings and parameters.\n" "The parameter names are already translated to upper case.\n" "\n" - "This method has been introduced in version 0.27.1\n" + "The variables dictionary defines named variables with the given values.\n" + "\n" + "This method has been introduced in version 0.27.1. The variables argument has been added in version 0.28.7.\n" ), "@brief Provides a delegate for the SPICE reader for translating device statements\n" "Supply a customized class to provide a specialized reading scheme for devices. "