aboutsummaryrefslogtreecommitdiff
path: root/src/parser/parse_tree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser/parse_tree.cpp')
-rw-r--r--src/parser/parse_tree.cpp972
1 files changed, 972 insertions, 0 deletions
diff --git a/src/parser/parse_tree.cpp b/src/parser/parse_tree.cpp
new file mode 100644
index 0000000..76c67b0
--- /dev/null
+++ b/src/parser/parse_tree.cpp
@@ -0,0 +1,972 @@
+#include "parse_tree.h"
+#include "parse_directive.h"
+#include "parse_element.h"
+#include "specs.h"
+
+#include <sstream>
+#include <iomanip>
+
+using std::ostringstream;
+
+using spx::oa_value_type;
+using spx::ea_value_type;
+using spx::ed_value_type;
+
+using spx::ed_bus_type;
+using spx::oa_signal_type;
+using spx::ea_signal_type;
+using spx::ed_signal_type;
+using spx::ed_bus_type;
+
+#include "../build/parser/parser.tab.h"
+#include "../build/parser/parser.yy.h"
+
+vector<shared_ptr<sc_object>> ParseNet::create(const string &name, bool force_bidir) const
+{
+ if ( !bidirectional() && !force_bidir )
+ return { create_uni(name) };
+ else
+ {
+ assert(type() == OANALOG && "Only optical nets can be bidirectional for now");
+ return { create_uni((name + "_0").c_str()), create_uni((name + "_1").c_str()) };
+ }
+}
+
+shared_ptr<sc_object> ParseNet::create_uni(const string &name) const
+{
+ switch (m_type)
+ {
+ case OANALOG:
+ return { make_shared<spx::oa_signal_type>(name.c_str()) };
+ case EANALOG:
+ return { make_shared<spx::ea_signal_type>(name.c_str()) };
+ case EDIGITAL:
+ if (m_size == 1)
+ return { make_shared<spx::ed_signal_type>(name.c_str()) };
+ else
+ return { make_shared<spx::ed_bus_type>(name.c_str(), sc_lv_base(m_size)) };
+ default:
+ cerr << "Unknown net type: " << name << endl;
+ exit(1);
+ }
+ return { nullptr };
+}
+
+string ParseNet::name_from_id(int id)
+{
+ assert(id >= 0);
+ ostringstream ss;
+ ss << "N" << std::setfill('0') << std::setw(5) << id;
+ return ss.str();
+}
+
+// return next bidir net which doesn't have a corresponding signal in circuit_signals
+map<string, ParseNet>::iterator ParseTreeCreationHelper::next_fresh_bidir_net()
+{
+ for (auto it = pt->nets.begin(); it != pt->nets.end(); ++it)
+ {
+ const auto &net = *it;
+
+ // check if net is bidirectional
+ if (!net.second.bidirectional())
+ continue;
+
+ // check if net has a corresponding signal instanciated
+ auto it_sig = find_if(circuit_signals.cbegin(), circuit_signals.cend(), [&it](const auto &p)
+ { return p.first == it->first; }
+ );
+ if (it_sig != circuit_signals.cend())
+ continue;
+
+ // check if net is unbound
+ auto it_elem = next_fresh_element_bound_to(it->first);
+ if (it_elem == pt->elements.cend())
+ continue;
+
+ return it;
+ }
+ return pt->nets.end();
+}
+
+// return next bidir net which doesn't have a corresponding signal in circuit_signals
+map<string, ParseNet>::iterator ParseTreeCreationHelper::next_fresh_net()
+{
+ for (auto it = pt->nets.begin(); it != pt->nets.end(); ++it)
+ {
+ // check if net has a corresponding signal
+ auto it_sig = find_if(circuit_signals.cbegin(), circuit_signals.cend(), [&it](const auto &p)
+ { return p.first == it->first; }
+ );
+ if (it_sig != circuit_signals.cend())
+ continue;
+
+ // check if net is unbound
+ auto it_elem = next_fresh_element_bound_to(it->first);
+ if (it_elem == pt->elements.cend())
+ continue;
+
+ return it;
+ }
+ return pt->nets.end();
+}
+
+// return next element which is connected to "net_name" and doesnt have a corresponding
+// module in circuit_modules and isn't already in the backlog
+vector<ParseElement *>::iterator ParseTreeCreationHelper::next_fresh_element_bound_to(const string &net_name, const set<const ParseElement *> &excludes)
+{
+ // Loop over elements
+ for (auto it = pt->elements.begin(); it != pt->elements.end(); ++it)
+ {
+ const auto &elem = *it;
+
+ // Check if element is part of exlude set
+ if (excludes.find(elem) != excludes.cend())
+ continue;
+
+ // Check if element is part of backlog set
+ if (elements_backlog.find(elem) != elements_backlog.cend())
+ continue;
+
+ // Check if net_name is in element nets
+ if (find(elem->nets.cbegin(), elem->nets.cend(), net_name) == elem->nets.cend())
+ continue;
+
+ // Check if element has a corresponding module instantiated
+ auto it_mod = find_if(circuit_modules.cbegin(), circuit_modules.cend(), [&it](const auto &p)
+ { return p.first == (*it)->name; }
+ );
+
+ // if not, return current iterator `it`
+ if (it_mod == circuit_modules.cend())
+ return it;
+ }
+ return pt->elements.end();
+}
+
+void ParseTreeCreationHelper::create_signals(const ParseElement *elem)
+{
+ // element should be in the parsetree
+ assert(find(pt->elements.begin(), pt->elements.end(), elem) != pt->elements.end());
+
+ // for all nets connected to elements
+ for (const auto &net : elem->nets)
+ {
+ // find whether it has already been instantiated
+ if (circuit_signals.find(net) == circuit_signals.end())
+ {
+ // if not, do it
+ circuit_signals.emplace(net, pt->nets.at(net).create(net));
+
+ // verify the net had no connection (it would be a bug otherwise,
+ // since it hadn't been instantiated)
+ assert(pt->nets.at(net).m_connect_count == 0);
+
+ // find all elements connected to net and add them to the elements backlog
+ // FIXME: potential bug here if an element can have both bidir and unidir nets
+ auto it = next_fresh_element_bound_to(net, {elem});
+ while (it != pt->elements.end())
+ {
+ elements_backlog.insert(*it);
+ it = next_fresh_element_bound_to(net, {elem});
+ }
+ }
+ }
+}
+
+void ParseTreeCreationHelper::upgrade_signal(const string &net_name)
+{
+ // Upgrade even if: net.bidirectional() is false
+
+ // The signal should exist because it has been created by pt_helper.create_signals()
+ // but it doesn't hurt to check once more time
+ assert(circuit_signals.find(net_name) != circuit_signals.end());
+
+ // Set the bidirectional flag to true
+ pt->nets[net_name].m_bidirectional = true;
+
+ // Check if the net was created as unidirectional (only one signal)
+ if (circuit_signals.at(net_name).size() == 1)
+ {
+ // if the existing signal is unidirectional
+
+ // check if a port is already connected to the signal
+ if (pt->nets[net_name].m_connect_count)
+ {
+ // if yes, that means a port is already connected to the signal
+ // we cannot easily re-instantiate it as it may be bound to a port;
+ // instead, we add a second signal to the vector to make it bidirectional
+ circuit_signals[net_name].push_back(pt->nets[net_name].create_uni(net_name + "_1"));
+
+ // For bidirectional signals, first port to connect binds to signal_0
+ // for writing and signal_1 for reading
+ // Therefore we have to "re-create" this scenario, as if the first device
+ // connected to a bidirectional net. But for this we need to know which
+ // if the first device was a writer or a reader.
+
+ // first determine if the connected port was writing or reading the signal
+ bool has_writer = pt->nets[net_name].m_connect_writer_count;
+ bool has_reader = pt->nets[net_name].m_connect_reader_count;
+
+ // only one should be true since the net was unidirectional
+ assert(has_writer ^ has_reader);
+
+ // if a writer, then it's as expected
+ if (has_writer)
+ {
+ // if a writer, then it's as expected:
+ // the next port will connect to signal_1 for writing
+ // we just need to update the number of "readers" connected to the net
+ pt->nets[net_name].m_readers_count++;
+ }
+ else
+ {
+ // if a writer, then the next port needs to reverse its order.
+ // for this we swap signal_0 and signal_1
+ swap(circuit_signals[net_name][0], circuit_signals[net_name][1]);
+ // then we need to update the number of "writers" connected to the net
+ pt->nets[net_name].m_writers_count++;
+ }
+ }
+ else
+ {
+ // otherwise, we can recreate the first signal as well
+ circuit_signals[net_name].clear(); // will delete the net through the constructor
+ circuit_signals[net_name] = pt->nets.at(net_name).create(net_name);
+ }
+ }
+}
+
+string ParseElement::to_json() const
+{
+ // name
+ // kind()
+ // nets
+ // args
+ // kwargs
+
+ stringstream ss;
+ ss << "{";
+ ss << "\"name\":" << '"' << name << '"';
+ ss << ',';
+ ss << "\"type\":" << '"' << kind() << '"';
+ ss << ',';
+ ss << "\"nets\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < nets.size(); ++i)
+ {
+ ss << '"' << nets[i] << '"';
+ if (i != nets.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"args\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < args.size(); ++i)
+ {
+ ss << args[i].to_json();
+ if (i != args.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"kwargs\":";
+ {
+ ss << '{';
+ for (auto it = kwargs.begin(); it != kwargs.end(); ++it)
+ {
+ if (it != kwargs.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << '}';
+ return ss.str();
+}
+
+ParseTree::ParseTree(const string &name, const ParseSubcircuit &subcircuit, const map<string, Variable> &kwargs)
+: ParseTree(name)
+{
+ parent = subcircuit.parent;
+ is_subcircuit = true;
+
+ // take kwargs as local assignments
+ local_assignments = subcircuit.kwargs;
+
+ for (const auto &p : kwargs)
+ {
+ if (local_assignments.count(p.first) == 0)
+ {
+ cerr << name << ": Unknown subcircuit parameter " << p.first << endl;
+ exit(1);
+ }
+ local_assignments[p.first] = p.second;
+ }
+
+ yyscan_t scanner;
+ YY_BUFFER_STATE buf;
+
+ yylex_init(&scanner);
+ buf = yy_scan_string(subcircuit.netlist.c_str(), scanner);
+ yy_switch_to_buffer(buf, scanner);
+
+ int parsing_result = yyparse(scanner, this);
+
+ //yy_delete_buffer(buf, scanner);
+ yylex_destroy(scanner);
+
+ // Return if unsuccessful
+ if (parsing_result != 0) {
+ cerr << name << ": failed parsing subcircuit netlist" << endl;
+ exit(1);
+ }
+
+ if (directives.size() > 0)
+ {
+ cerr << name << ": found a directive in subcircuit, ignoring." << endl;
+ directives.clear();
+ }
+
+ if (analyses.size() > 0)
+ {
+ cerr << name << ": found an analysis in subcircuit, ignoring." << endl;
+ analyses.clear();
+ }
+ // print();
+}
+
+int ParseTree::register_directive(ParseDirective *directive)
+{
+ directive->parent = this;
+ directives.push_back(directive);
+ // cout << "Created directive " << directive->kind() << endl;
+ // cout << "current number of directives: " << directives.size() << endl;
+ return directives.size() - 1;
+
+}
+
+int ParseTree::register_element(ParseElement *element)
+{
+ element->name = name_prefix() + element->name;
+ strutils::toupper(element->name);
+ element->parent = this;
+ elements.push_back(element);
+ // cout << "Created element " << element->name << endl;
+ // cout << "current number of elements: " << elements.size() << endl;
+ return elements.size() - 1;
+}
+
+int ParseTree::register_analysis(ParseAnalysis *analysis)
+{
+ if ( !analyses.empty() )
+ {
+ cerr << "Attempted to register a new analysis (" << analysis->kind() << ")" << endl;
+ cerr << "But another one was already specified (" << analyses[0]->kind() << ")" << endl;
+ exit(1);
+ }
+ analysis->parent = this;
+ analyses.push_back(analysis);
+ // cout << "Created analysis " << analysis->kind() << endl;
+ // cout << "current number of analysis: " << analyses.size() << endl;
+ return analyses.size() - 1;
+}
+
+int ParseTree::register_subcircuit(string name)
+{
+ strutils::toupper(name);
+ //name = name_prefix() + name;
+
+ auto it = find_if(subcircuits.cbegin(), subcircuits.cend(),
+ [&name](const ParseSubcircuit *p){ return p->name == name;});
+ if (it != subcircuits.cend())
+ {
+ cerr << "Attempted to register a new subcircuit with name " << name << endl;
+ cerr << "But another subcircuit with this name already exists" << endl;
+ exit(1);
+ }
+
+ subcircuits.push_back(new ParseSubcircuit(name, this));
+ int i = subcircuits.size() - 1;
+
+ // TODO: Copy variables definitions ?
+
+ // cout << "Created subcircuit " << subcircuits[i]->name << endl;
+ // cout << "(current number of subcircuits: " << subcircuits.size() << ")" << endl;
+
+ return i;
+}
+
+const ParseSubcircuit *ParseTree::find_subcircuit(const string &name) const
+{
+ auto pred = [&name](const ParseSubcircuit *p) {
+ return p->name == name;
+ };
+ auto it = find_if(subcircuits.begin(), subcircuits.end(), pred);
+ if (it != subcircuits.end())
+ return *it;
+
+ cerr << "Subcircuit definition not found: " << name << endl;
+ exit(1);
+}
+
+void ParseTree::print() const
+{
+ cout << "-----------" << endl;
+ cout << global_assignments.size() << " global assignments" << endl;
+ for (const auto &x : global_assignments)
+ cout << " - " << x.first << " (" << x.second.kind()
+ << ") = " << x.second.get_str() << endl;
+
+ cout << "-----------" << endl;
+ cout << local_assignments.size() << " local assignments" << endl;
+ for (const auto &x : local_assignments)
+ cout << " - " << x.first << " (" << x.second.kind()
+ << ") = " << x.second.get_str() << endl;
+
+ cout << "-----------" << endl;
+ cout << elements.size() << " elements" << endl;
+ for (const auto &x : elements)
+ {
+ cout << " - ";
+ x->print();
+ }
+
+ cout << "-----------" << endl;
+ cout << nets.size() << " named nets" << endl;
+ for (const auto &x : nets)
+ cout << " - " << x.first << ": " << x.second.type_str()
+ << "<" << x.second.size() << ">"
+ << (x.second.bidirectional() ? "b" : "u")
+ << " (" << x.second.m_writers_count << "|" << x.second.m_readers_count << ")"
+ << endl;
+
+ cout << "-----------" << endl;
+ cout << directives.size() << " directives" << endl;
+ for (const auto &x : directives)
+ {
+ cout << " - ";
+ x->print();
+ }
+
+ cout << "-----------" << endl;
+ cout << subcircuits.size() << " subcircuits" << endl;
+ for (const auto &x : subcircuits)
+ {
+ cout << " - ";
+ x->print();
+ }
+
+ cout << "-----------" << endl;
+ cout << analyses.size() << " analyses" << endl;
+ for (const auto &x : analyses)
+ {
+ cout << " - ";
+ x->print();
+ }
+}
+
+string ParseDirective::to_json() const
+{
+ stringstream ss;
+ ss << "{";
+ ss << "\"type\":" << '"' << kind() << '"';
+ ss << ',';
+ ss << "\"args\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < args.size(); ++i)
+ {
+ ss << args[i].to_json();
+ if (i != args.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"kwargs\":";
+ {
+ ss << '{';
+ for (auto it = kwargs.begin(); it != kwargs.end(); ++it)
+ {
+ if (it != kwargs.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << '}';
+ return ss.str();
+}
+
+string ParseAnalysis::to_json() const
+{
+ stringstream ss;
+ ss << "{";
+ ss << "\"type\":" << '"' << kind() << '"';
+ ss << ',';
+ ss << "\"args\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < args.size(); ++i)
+ {
+ ss << args[i].to_json();
+ if (i != args.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"kwargs\":";
+ {
+ ss << '{';
+ for (auto it = kwargs.begin(); it != kwargs.end(); ++it)
+ {
+ if (it != kwargs.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << '}';
+ return ss.str();
+}
+
+string ParseSubcircuit::to_json() const
+{
+ stringstream ss;
+ ss << "TODO";
+ return ss.str();
+}
+
+string ParseNet::to_json() const
+{
+ stringstream ss;
+ ss << "{";
+ ss << "\"type\":" << '"' << type_str() << '"';
+ ss << ',';
+ ss << "\"size\":" << m_size;
+ ss << ',';
+ ss << "\"bidirectional\":" << bidirectional();
+ ss << ',';
+ ss << "\"readers\":" << m_readers_count;
+ ss << ',';
+ ss << "\"writers\":" << m_readers_count;
+ ss << '}';
+ return ss.str();
+}
+
+string ParseTree::to_json() const
+{
+ stringstream ss;
+ ss << "{";
+ ss << "\"name\":" << '"' << name << '"';
+ ss << ',';
+ ss << "\"parent\":";
+ if (parent)
+ ss << '"' << parent->name << '"';
+ else
+ ss << "null";
+ ss << ',';
+ ss << "\"is_subcircuit\":" << (is_subcircuit ? "true" : "false");
+ ss << ',';
+ ss << "\"unnamed_nets\":" << unnamed_net_count;
+ ss << ',';
+ ss << "\"nets\":";
+ {
+ ss << '{';
+ for (auto it = nets.begin(); it != nets.end(); ++it)
+ {
+ if (it != nets.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << ',';
+ ss << "\"elements\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < elements.size(); ++i)
+ {
+ ss << elements[i]->to_json();
+ if (i != elements.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"directives\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < directives.size(); ++i)
+ {
+ ss << directives[i]->to_json();
+ if (i != directives.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"analyses\":";
+ {
+ ss << '[';
+ for (size_t i = 0; i < analyses.size(); ++i)
+ {
+ ss << analyses[i]->to_json();
+ if (i != analyses.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ ss << ',';
+ ss << "\"subcircuits\":";
+ {
+ ss << "\"TODO\"";
+ if (false)
+ {
+ ss << '[';
+ for (size_t i = 0; i < subcircuits.size(); ++i)
+ {
+ ss << subcircuits[i]->to_json();
+ if (i != subcircuits.size() - 1)
+ ss << ", ";
+ }
+ ss << ']';
+ }
+ }
+ ss << ',';
+ ss << "\"local_variables\":";
+ {
+ ss << '{';
+ for (auto it = local_assignments.begin(); it != local_assignments.end(); ++it)
+ {
+ if (it != local_assignments.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << ',';
+ ss << "\"global_variables\":";
+ {
+ ss << '{';
+ for (auto it = global_assignments.begin(); it != global_assignments.end(); ++it)
+ {
+ if (it != global_assignments.begin())
+ ss << ',';
+ ss << '"' << it->first << '"' << ':';
+ ss << it->second.to_json();
+ }
+ ss << '}';
+ }
+ ss << '}';
+ return ss.str();
+}
+
+void ParseTree::build_circuit()
+{
+ cout << "Flattening..." << endl;
+ // flatten current parse tree by expanding subcircuits
+ flatten();
+ cout << "Done (flattening)" << endl;
+
+ auto pt_helper = ParseTreeCreationHelper(this);
+
+ auto &elements_backlog = pt_helper.elements_backlog;
+ auto &circuit_signals = pt_helper.circuit_signals;
+ auto &circuit_modules = pt_helper.circuit_modules;
+
+ // Find first bidirectional net in nets that is not in circuit_nets
+ auto next_net = pt_helper.next_fresh_bidir_net();
+ while(next_net != nets.end())
+ {
+ cout << "Elaborating network of " << next_net->first << "..." << endl;
+
+ // Find elements which are connected
+ auto elem = pt_helper.next_fresh_element_bound_to(next_net->first);
+ while (elem != elements.end())
+ {
+ cout << "Creating " << (*elem)->name << " (reason: connection to bidir net)" << endl;
+ auto mod = (*elem)->create(pt_helper);
+ cout << "Done creating " << (*elem)->name << endl;
+ if (mod->name() != (*elem)->name)
+ {
+ cerr << "Error: a module with name '" << (*elem)->name << "' already exists or";
+ cerr << " it was renamed due to a bad naming." << endl;
+ cerr << "Make sure there is no naming conflict in the netlist" << endl;
+ exit(1);
+ }
+ circuit_modules.emplace(mod->name(), mod);
+
+ // Process backlog elements (elements that were connected to the nets)
+ while ( !elements_backlog.empty() )
+ {
+ auto it = elements_backlog.begin();
+ auto &backlog_elem = *it;
+ cout << "Creating " << backlog_elem->name << " (reason: backlog)" << endl;
+ auto mod = backlog_elem->create(pt_helper);
+ cout << "Done creating " << backlog_elem->name << endl;
+
+ if (mod->name() != (*it)->name)
+ {
+ cerr << "Error: a module with name '" << (*it)->name << "' already exists or";
+ cerr << " it was renamed due to a bad naming." << endl;
+ cerr << "Make sure there is no naming conflict in the netlist" << endl;
+ exit(1);
+ }
+
+ circuit_modules.emplace(mod->name(), mod);
+ elements_backlog.erase(*it);
+ }
+ elem = pt_helper.next_fresh_element_bound_to(next_net->first);
+ }
+
+ cout << "Done (elaborating network of " << next_net->first << ")" << endl;
+
+ // At this point all elements connected to the bidirectional net have been created
+ // move on to the next one
+ next_net = pt_helper.next_fresh_bidir_net();
+ }
+ cout << "Done with bidirectional nets" << endl;
+ // At this point all bidirectional nets have been created, and all devices
+ // connected to them as well we can proceed with unidirectional nets
+ next_net = pt_helper.next_fresh_net();
+ while(next_net != nets.end())
+ {
+ cout << "Elaborating network of " << next_net->first << "..." << endl;
+
+ // Find elements which are connected
+ auto elem = pt_helper.next_fresh_element_bound_to(next_net->first);
+ while (elem != elements.end())
+ {
+ cout << "Creating " << (*elem)->name << " (reason: connection to unidir net)" << endl;
+ auto mod = (*elem)->create(pt_helper);
+ cout << "Done creating " << (*elem)->name << endl;
+ if (mod->name() != (*elem)->name)
+ {
+ cerr << "Error: a module with name '" << (*elem)->name << "' already exists or";
+ cerr << " it was renamed due to a bad naming." << endl;
+ cerr << "Make sure there is no naming conflict in the netlist" << endl;
+ exit(1);
+ }
+ circuit_modules.emplace(mod->name(), mod);
+
+ // Process backlog elements (elements that were connected to the nets)
+ while ( !elements_backlog.empty() )
+ {
+ auto it = elements_backlog.begin();
+ auto &backlog_elem = *it;
+ cout << "Creating " << backlog_elem->name << " (reason: backlog)" << endl;
+ auto mod = backlog_elem->create(pt_helper);
+ cout << "Done creating " << backlog_elem->name << endl;
+
+ if (mod->name() != (*it)->name)
+ {
+ cerr << "Error: a module with name '" << (*it)->name << "' already exists or";
+ cerr << " it was renamed due to a bad naming." << endl;
+ cerr << "Make sure there is no naming conflict in the netlist" << endl;
+ exit(1);
+ }
+
+ circuit_modules.emplace(mod->name(), mod);
+ elements_backlog.erase(*it);
+ }
+ elem = pt_helper.next_fresh_element_bound_to(next_net->first);
+ }
+
+ cout << "Done (elaborating network of " << next_net->first << ")" << endl;
+
+ // At this point all elements connected to the bidirectional net have been created
+ // move on to the next one
+ next_net = pt_helper.next_fresh_net();
+ }
+ for (const auto &x : directives)
+ x->create();
+
+ if (! analyses.empty())
+ analyses.at(0)->create();
+
+ // Add all nets and elements to SPECSGlobalConfig
+ for (const auto &x: circuit_modules)
+ specsGlobalConfig.register_object(x.second);
+ for (const auto &x: circuit_signals)
+ for (const auto &sig: x.second)
+ specsGlobalConfig.register_object(sig);
+}
+
+void ParseTree::flatten()
+{
+ cout << "Flattening " << name << endl;
+
+ vector<ParseElement *> elements_flat;
+ map<string, ParseNet> nets_flat = nets;
+
+ // First, find all subcircuit instances (X elements) and ingest their parse-tree
+ // (flattening the global parse-tree)
+ for (auto &elem : elements)
+ {
+ // try to cast as XElement
+ XElement *xelem = dynamic_cast<XElement *>(elem);
+
+ // if unsuccessful, it means the device doesnt need to be flattened
+ if (!xelem)
+ {
+ elements_flat.push_back(elem);
+ continue;
+ }
+
+ // get subcircuit name
+ string subcircuit_name = xelem->args.back().as_string();
+ strutils::toupper(subcircuit_name);
+
+ // get subcircuit definition in element's parent
+ // TODO: figure out if we want global subcircuits
+ auto subcircuit = xelem->parent->find_subcircuit(subcircuit_name);
+
+ // parse the subcircuit netlist into a new parse tree
+ ParseTree sub_pt(xelem->name, *subcircuit, xelem->kwargs);
+
+ // flatten it
+ sub_pt.flatten();
+
+ // Check the number of nets (in args)
+ if(xelem->args.size() - 1 != subcircuit->ports.size())
+ {
+ cerr << "Wrong number of nets for subcircuit instance: ";
+ cerr << xelem->name << endl;
+ exit(1);
+ }
+
+ // Compute net_name equivalence between internal and external nets
+ map<string, string> net_names_translations;
+ for (size_t i = 0; i < subcircuit->ports.size(); ++i)
+ {
+ Variable v = xelem->args[i]; // naming from Xnn ... line
+ string internal_net_name = sub_pt.name_prefix() + subcircuit->ports[i]; // naming from .SUBCKT line
+ string external_net_name = "";
+
+ // build full exposed net_name
+ string net_base;
+
+ // extract from Variable (either string or integer)
+ // normally parser should always transform it to a string variable
+ // for us
+ if (v.type == Variable::STRING)
+ net_base = v.as_string();
+ else if (v.type == Variable::INTEGER)
+ net_base = ParseNet::name_from_id(v.as_integer());
+ else
+ {
+ cerr << "Incompatible net name for " << xelem->name << endl;
+ exit(1);
+ }
+
+ // if a single underscore was given, use the unnamed net count to
+ // define a unique net
+ // FIXME: this way is not correct; it should really increment the
+ // parent's unnamed net count; I think it would be acceptable to
+ // discard const for this, as the user specifically doesn't care about
+ // the actual name of unnamed nets.
+ if (net_base == "_")
+ net_base = "_" + to_string(this->unnamed_net_count++);
+
+ // add current prefix
+ external_net_name = name_prefix() + net_base;
+
+ // see if the net was already defined (was used by another device)
+ if (nets.find(external_net_name) != nets.end())
+ {
+ // if so, combined the previously defined net with the internal one
+ bool ok = sub_pt.nets[internal_net_name].combine_with(nets.at(external_net_name));
+ if (!ok)
+ {
+ cerr << "Could not reconcile nets: " << internal_net_name;
+ cerr << " and " << external_net_name << endl;
+ cerr << "During flattening of " << xelem->name << endl;
+ exit(1);
+ }
+ }
+ // Add the (potentially combined) net to pt_helper nets
+ nets_flat[external_net_name] = sub_pt.nets.at(internal_net_name);
+
+ // Update the net name translations list
+ net_names_translations[internal_net_name] = external_net_name;
+
+ // Erase the internal net_name (replaced by the global net name)
+ sub_pt.nets.erase(internal_net_name);
+ } // for (size_t i = 0; i < subcircuit->ports.size(); ++i)
+
+ // show result of translation
+ if (false)
+ for (const auto &p : net_names_translations)
+ cout << p.first << " <==> " << p.second << endl;
+
+ // translate internal net names by modifying the elements within the
+ // subcircuit which are bound to them
+ for (auto subelem : sub_pt.elements)
+ {
+ // loop over translation, and update if necessary
+ for (const auto &p : net_names_translations)
+ {
+ auto it = find(subelem->nets.begin(), subelem->nets.end(), p.first);
+ if (it == subelem->nets.end())
+ continue;
+ *it = p.second;
+ }
+ } // for (auto subelem : sub_pt.elements)
+
+ // insert the (globally named) internal-only nets into flattened netlist
+ nets_flat.insert(sub_pt.nets.cbegin(), sub_pt.nets.cend());
+ // insert the (potentially updated) elements to flattened netlist
+ elements_flat.insert(elements_flat.end(), sub_pt.elements.cbegin(), sub_pt.elements.cend());
+
+ // finally, free the memory of the elem object
+ // note that the pointer itself is still in the elements vector!
+ // we cannot remove it due to the for loop
+ delete elem;
+ elem = nullptr;
+ } // for (auto elem : elements)
+
+ // Print new list of elements and nets
+ if (false)
+ {
+ cout << "-----------" << endl;
+ cout << elements_flat.size() << " elements" << endl;
+ for (const auto &x : elements_flat)
+ {
+ cout << " - ";
+ x->print();
+ }
+
+ cout << "-----------" << endl;
+ cout << nets_flat.size() << " named nets" << endl;
+ for (const auto &x : nets_flat)
+ cout << " - " << x.first << ": " << x.second.type_str()
+ << "<" << x.second.size() << ">"
+ << (x.second.bidirectional() ? "b" : "u")
+ << " (" << x.second.m_writers_count << "|" << x.second.m_readers_count << ")"
+ << endl;
+ }
+
+ // move to flattened nets and elements
+ nets = nets_flat;
+ elements = elements_flat;
+
+ return;
+}
+
+map<string, Variable> ParseTree::global_assignments =
+{
+ { "pi", Variable(M_PI) },
+ { "c", Variable(299792458.0) },
+}; \ No newline at end of file