diff options
Diffstat (limited to 'src/parser')
| -rw-r--r-- | src/parser/parse_analysis.cpp | 109 | ||||
| -rw-r--r-- | src/parser/parse_analysis.h | 94 | ||||
| -rw-r--r-- | src/parser/parse_directive.cpp | 132 | ||||
| -rw-r--r-- | src/parser/parse_directive.h | 68 | ||||
| -rw-r--r-- | src/parser/parse_element.cpp | 1266 | ||||
| -rw-r--r-- | src/parser/parse_element.h | 104 | ||||
| -rw-r--r-- | src/parser/parse_tree.cpp | 972 | ||||
| -rw-r--r-- | src/parser/parse_tree.h | 890 | ||||
| -rw-r--r-- | src/parser/parser.l | 438 | ||||
| -rw-r--r-- | src/parser/parser.y | 961 |
10 files changed, 5034 insertions, 0 deletions
diff --git a/src/parser/parse_analysis.cpp b/src/parser/parse_analysis.cpp new file mode 100644 index 0000000..3168858 --- /dev/null +++ b/src/parser/parse_analysis.cpp @@ -0,0 +1,109 @@ +#include "parse_analysis.h" +#include "cw_source.h" +#include "devices/alldevices.h" +#include "directional_coupler.h" +#include "optical_output_port.h" +#include "parse_tree.h" +#include "specs.h" +#include "strutils.h" +#include "sysc_utils.h" +#include "general_utils.h" +#include <functional> + +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; + +void OPAnalysis::create() const +{ + specsGlobalConfig.analysis_type = SPECSConfig::OP; + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::FREQUENCY_DOMAIN; +} + +void DCAnalysis::create() const +{ + specsGlobalConfig.analysis_type = SPECSConfig::DC; + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::FREQUENCY_DOMAIN; + + assert(sweep_orders.size() > 0); + + set<CWSource *> all_cws = sc_get_all_object_by_type<CWSource>(); + vector<CWSource *> all_cws_vec(all_cws.begin(), all_cws.end()); + + int i = 0; + for (const auto &sweep_order: sweep_orders) + { + string element_name = parent->name_prefix() + sweep_order.first.first; + string attribute_name = sweep_order.first.second; + + auto is_desired_element = [&element_name](const auto &x){ return x->name() == element_name; }; + auto it = std::find_if(all_cws_vec.begin(), all_cws_vec.end(), is_desired_element); + if (it == all_cws_vec.end()) + { + cerr << "Element not found: " << element_name << endl; + exit(1); + } + CWSource *elem = *it; + // cout << elem << endl; + // cout << &CWSource::setWavelength << endl; + + function<void(double)> f; + if (attribute_name == "WL" || attribute_name == "WAVELENGTH" || attribute_name == "LAMBDA") + { + f = std::bind(&CWSource::setWavelength, elem, placeholders::_1); + } + else if (attribute_name == "P" || attribute_name == "POW" || attribute_name == "POWER") + { + f = std::bind(static_cast<void (CWSource::*)(const double&)>(&CWSource::setPower), elem, placeholders::_1); + } + else if (attribute_name == "F" || attribute_name == "FREQ" || attribute_name == "FREQUENCY") + { + f = std::bind((&CWSource::setFrequency), elem, placeholders::_1); + } + else + { + cerr << "Unknown attribute for " << sweep_order.first.first << ": " << attribute_name << endl; + exit(1); + } + auto values = range(sweep_order.second[0], sweep_order.second[1], sweep_order.second[2]); + auto order = pair<function<void(double)>, vector<double>>(f, values); + auto order_name = attribute_name + "(" + element_name + ")"; + specsGlobalConfig.cw_sweep_orders.emplace(order_name, order); + ++i; + } +} + +void TRANAnalysis::create() const +{ + specsGlobalConfig.analysis_type = SPECSConfig::TRAN; + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::TIME_DOMAIN; + + assert(args.size() <= 1); + + if (args.size() >= 1) + { + double val = args[0].as_double(); + // cout << val << endl; + if (val > 0) + specsGlobalConfig.tran_duration = args[0].as_double(); + else + { + cerr << "Negative simulation duration is not accepted" << endl; + exit(1); + } + } + + for (const auto &p : kwargs) + { + string kw = p.first; + strutils::toupper(kw); + cerr << "Unknown keyword " << kw << endl; + exit(1); + } +} diff --git a/src/parser/parse_analysis.h b/src/parser/parse_analysis.h new file mode 100644 index 0000000..9e3f44c --- /dev/null +++ b/src/parser/parse_analysis.h @@ -0,0 +1,94 @@ +#pragma once + +#include "parse_tree.h" +#include "specs.h" +#include "strutils.h" + +#include <initializer_list> +#include <utility> +#include <memory> +#include <string> +#include <vector> +#include <map> +#include <iostream> + +using std::pair; +using std::make_pair; +using std::string; +using std::vector; +using std::map; +using std::cout; +using std::cerr; +using std::endl; +using std::pair; +using std::unique_ptr; + +/* OP: operating point analysis */ +struct OPAnalysis : public ParseAnalysis { + /* Import constructor from ParseAnalysis */ + using ParseAnalysis::ParseAnalysis; + + virtual ParseAnalysis* clone() const + { return new OPAnalysis(*this); } + virtual void create() const; + virtual string kind() const + { return "OP"; } +}; + +/* DC: stepped-source operating point analysis */ +struct DCAnalysis : public ParseAnalysis { + typedef pair<string, string> sweep_param_type; + typedef vector<double> sweep_range_type; + typedef pair<sweep_param_type, sweep_range_type> sweep_order_type; + + /* Import constructor from ParseAnalysis */ + using ParseAnalysis::ParseAnalysis; + + map<sweep_param_type, sweep_range_type> sweep_orders; + + void register_sweep_order(sweep_param_type param, sweep_range_type range) + { + strutils::toupper(param.first); + strutils::toupper(param.second); + if (sweep_orders.count(param) != 0) + { + cerr << "A sweep order for " << param.second << "(" << param.first << ")"; + cerr << " was already recoreded." << endl; + exit(1); + } + auto n = (range[1] - range[0]) / range[2]; + n = max(0.0, n); + n = floor(n); + if (n <= 1.5) + { + cerr << "Sweep order must contain at least 2 point." << endl; + exit(1); + } + cout << "Recorded sweep order for " << param.second << "(" << param.first << ")"; + cout << " (" << n << "points)" << endl; + sweep_orders.emplace(param, range); + } + + void register_sweep_order(sweep_order_type order) + { + register_sweep_order(order.first, order.second); + } + + virtual ParseAnalysis* clone() const + { return new DCAnalysis(*this); } + virtual void create() const; + virtual string kind() const + { return "DC"; } +}; + +/* TRAN: transient analysis */ +struct TRANAnalysis : public ParseAnalysis { + /* Import constructor from ParseElement */ + using ParseAnalysis::ParseAnalysis; + + virtual ParseAnalysis* clone() const + { return new TRANAnalysis(*this); } + virtual void create() const; + virtual string kind() const + { return "TRAN"; } +};
\ No newline at end of file diff --git a/src/parser/parse_directive.cpp b/src/parser/parse_directive.cpp new file mode 100644 index 0000000..e66e8cf --- /dev/null +++ b/src/parser/parse_directive.cpp @@ -0,0 +1,132 @@ +#include "parse_directive.h" +#include "devices/alldevices.h" +#include "directional_coupler.h" +#include "optical_signal.h" +#include "parse_tree.h" +#include "specs.h" + +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; + +void OPTIONSDirective::create() const +{ + // Verify number of nodes + assert(args.size() == 0); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "TRACEALL") + specsGlobalConfig.trace_all_optical_nets = p.second.as_boolean(); + else if (kw == "ABSTOL") + specsGlobalConfig.default_abstol = p.second.as_double(); + else if (kw == "RELTOL") + specsGlobalConfig.default_reltol = p.second.as_double(); + else if (kw == "TS" || kw == "TIMESCALE") + specsGlobalConfig.engine_timescale = (SPECSConfig::EngineTimescale)p.second.as_integer(); + else if (kw == "RESOLUTION") + specsGlobalConfig.default_resolution_multiplier = p.second.as_double(); + else if (kw == "TRACEALL") + specsGlobalConfig.trace_all_optical_nets = p.second.as_double(); + else if (kw == "TEST_VARIABLE") + cout << kw << "(" << p.second.kind() << "): " << p.second.get_str() << endl; + else { + cerr << "Unknown keyword: " << p.first; + cerr << " (value: " << p.second.get_str() << " (" << p.second.kind() << "))" <<endl; + exit(1); + } + } +} + +void NODESETDirective::print() const +{ + ParseDirective::print(); + cout << "{"; + for (const auto &x : net_assignments) + { + for (const auto &ass : x.second) + cout << ass.first << "(" << x.first << ") = " + << ass.second.get_str() << " (" << ass.second.kind() << ") , "; + } + if ( !net_assignments.empty() ) + cout << "\b\b"; + cout << "}" << endl; +} + +void NODESETDirective::create() const +{ + cerr << "NODESET is disabled pending bugfixes" << endl; + exit(1); +#if 0 + map<string, pair<spx::oa_signal_type *, oa_value_type>> ic_map; + auto &signals = parent->circuit_nets; + for (const auto &x : net_assignments) + { + const auto &key = x.first; + if (signals.count(key) == 0) + { + cerr << "Net not found " << key << endl; + exit(1); + } + if (ic_map.count(key) == 0) + { + //FIXME: + auto sig = dynamic_cast<oa_signal_type *>(signals[key].get()); + auto initial_val = sig->read(); + auto sig_address = dynamic_cast<oa_signal_type *>(signals[key].get()); + pair<oa_signal_type *, oa_value_type> assignment{sig_address, initial_val}; + ic_map.emplace(key, assignment); + } + oa_value_type signal_val = ic_map[key].second; + for (const auto &assignment: x.second) + { + string attribute_name = assignment.first; + strutils::toupper(attribute_name); + const Variable &value = assignment.second; + + // cout << attribute_name << endl; + if (attribute_name == "P" || attribute_name == "POW" || attribute_name == "POWER") + { + signal_val.m_field = polar(sqrt(value.as_double()), arg(signal_val.m_field)); + // cout << "################" << endl; + // cout << signal_val << endl; + } + else if (attribute_name == "PHASE" || attribute_name == "PHI") + { + signal_val.m_field = polar(abs(signal_val.m_field), value.as_double()); + // cout << "################" << endl; + // cout << signal_val << endl; + } + else if (attribute_name == "WL" || attribute_name == "LAMBDA" || attribute_name == "WAVELENGTH") + { + signal_val.setWavelength(value.as_double()); + // cout << "################" << endl; + // cout << signal_val << endl; + } + else + { + cerr << "Unknown attribute" << endl; + exit(1); + } + } + specsGlobalConfig.nodeset_orders[key] = pair<oa_signal_type *, oa_value_type>( + dynamic_cast<oa_signal_type *>(signals[key].get()) + , signal_val); + } +#endif +} + +void ICDirective::create() const +{ + cerr << "IC directive is not implemented" << endl; + exit(1); +}
\ No newline at end of file diff --git a/src/parser/parse_directive.h b/src/parser/parse_directive.h new file mode 100644 index 0000000..212fd4a --- /dev/null +++ b/src/parser/parse_directive.h @@ -0,0 +1,68 @@ +#pragma once + +#include "parse_tree.h" + +#include <initializer_list> +#include <utility> +#include <memory> +#include <string> +#include <vector> +#include <map> +#include <iostream> + +using std::pair; +using std::make_pair; +using std::string; +using std::vector; +using std::map; +using std::cout; +using std::cerr; +using std::endl; +using std::pair; +using std::unique_ptr; + +/* OPTIONS: specify simulator options */ +struct OPTIONSDirective : public ParseDirective { + /* Import constructor from ParseDirective */ + using ParseDirective::ParseDirective; + + virtual ParseDirective* clone() const + { return new OPTIONSDirective(*this); } + virtual void create() const; + virtual string kind() const + { return "OPTIONS"; } +}; + +/* NODESET: specify initial guesses */ +struct NODESETDirective : public ParseDirective { + typedef string net_name; + typedef string property_name; + typedef Variable property_val; + typedef pair<property_name, property_val> assignment; + + map<net_name, vector<assignment>> net_assignments; + + /* Import constructor from ParseDirective */ + using ParseDirective::ParseDirective; + + virtual void print() const; + + virtual ParseDirective* clone() const + { return new NODESETDirective(*this); } + virtual void create() const; + virtual string kind() const + { return "NODESET"; } +}; + +/* IC: specify initial conditions options */ +// Inherit from NODESETDirective as options are the same. +struct ICDirective : public NODESETDirective { + /* Import constructor from ParseDirective */ + using NODESETDirective::NODESETDirective; + + virtual ParseDirective* clone() const + { return new ICDirective(*this); } + virtual void create() const; + virtual string kind() const + { return "IC"; } +};
\ No newline at end of file diff --git a/src/parser/parse_element.cpp b/src/parser/parse_element.cpp new file mode 100644 index 0000000..14de6e9 --- /dev/null +++ b/src/parser/parse_element.cpp @@ -0,0 +1,1266 @@ +#include "parse_element.h" +#include "alldevices.h" +#include "subcircuit_instance.h" +#include "specs.h" +#include "strutils.h" + +#include <string> + +/** ******************************************* **/ +/** Helper macros and defines **/ +/** ******************************************* **/ +#define AS_READER (false) +#define AS_WRITER (!AS_READER) + +#define INSTANTIATE_AND_CONNECT_UNI(ELEM_NAME, pt_helper) \ +ELEM_NAME::element_type_base *ELEM_NAME::instantiate_and_connect_uni(ParseTreeCreationHelper &pt_helper) const + +#define INSTANTIATE_AND_CONNECT_BI(ELEM_NAME, pt_helper) \ +ELEM_NAME::element_type_base *ELEM_NAME::instantiate_and_connect_bi(ParseTreeCreationHelper &pt_helper) const + +#define INSTANTIATE(pt_helper, bidirectional) \ +\ +if (bidirectional)\ + obj = (element_type_base *)instantiate_and_connect_bi(pt_helper);\ +else\ + obj = (element_type_base *)instantiate_and_connect_uni(pt_helper); + +/** ******************************************* **/ +/** Waveguide **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(WGElement, pt_helper) +{ + // create object with default params + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[1], AS_WRITER); + + // return the module + return obj; +} + +INSTANTIATE_AND_CONNECT_BI(WGElement, pt_helper) +{ + // create object with default params + element_type_bi *obj = new element_type_bi(name.c_str()); + + // Update unidirectional nets connected to the waveguide + if (!pt_helper.nets->at(nets[0]).bidirectional()) + (*pt_helper.nets)[nets[0]].m_writers_count++; + if (!pt_helper.nets->at(nets[1]).bidirectional()) + (*pt_helper.nets)[nets[1]].m_readers_count++; + + // Bidirectional waveguide needs all nets as bidir + for (const auto &net : nets) + pt_helper.upgrade_signal(net); + + // connect ports + pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]); + + return obj; +} + +sc_module *WGElement::create(ParseTreeCreationHelper &pt_helper) const +{ + element_type_base *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Prevent connecting a waveguide to itself, it will hang the execution!!! + if (nets[0] == nets[1]) + { + cerr << "Error: A waveguide cannot be looped back upon himself" << endl; + exit(1); + } + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Check if device has to be bidirectional + bool bidirectional = false; + for (const auto &net : nets) + { + // for waveguide, if any net is bidir, module has to be bidir + bidirectional |= pt_helper.nets->at(net).bidirectional(); + } + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, bidirectional); + + // Parse positional arguments + if(args.size() > 0) + obj->m_length_cm = args[0].as_double() * 100; + if(args.size() > 1) + obj->m_neff = args[1].as_double(); + if(args.size() > 2) + obj->m_ng = args[2].as_double(); + if(args.size() > 3) + obj->m_attenuation_dB_cm = args[3].as_double(); + if(args.size() > 4) + obj->m_D = args[4].as_double(); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "NEFF") + obj->m_neff = p.second.as_double(); + else if (kw == "NG") + obj->m_ng = p.second.as_double(); + else if (kw == "L" || kw == "LENGTH") + obj->m_length_cm = p.second.as_double() * 100; + else if (kw == "ATT") + obj->m_attenuation_dB_cm = p.second.as_double(); + else if (kw == "D") + obj->m_D = p.second.as_double(); + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + + return obj; +} + +/** ******************************************* **/ +/** Merger **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(MergerElement, pt_helper) +{ + // create object with default params + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in1, nets[0], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in2, nets[1], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[2], AS_WRITER); + + // return the module + return obj; +} + +sc_module *MergerElement::create(ParseTreeCreationHelper &pt_helper) const +{ + element_type_base *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + // Parse positional arguments + if(args.size() > 0) + obj->m_attenuation_dB = args[0].as_double(); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "IL" || kw == "INSERTION_LOSS") + obj->m_attenuation_dB = p.second.as_double(); + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + return obj; +} + +/** ******************************************* **/ +/** Splitter **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(SplitterElement, pt_helper) +{ + // create object with default params + element_type_uni *obj = new element_type_base(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out1, nets[1], AS_WRITER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out2, nets[2], AS_WRITER); + + // return the module + return obj; +} + +sc_module *SplitterElement::create(ParseTreeCreationHelper &pt_helper) const +{ + element_type_base *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + // Parse positional arguments + if(args.size() > 0) + obj->m_split_ratio = args[0].as_double(); + if(args.size() > 1) + obj->m_attenuation_dB = args[1].as_double(); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "IL" || kw == "INSERTION_LOSS") + obj->m_attenuation_dB = p.second.as_double(); + else if (kw == "SPLITTING_RATIO" || kw == "RATIO") + obj->m_split_ratio = p.second.as_double(); + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + return obj; +} + +/** ******************************************* **/ +/** Directional coupler **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(DCElement, pt_helper) +{ + // create object with default params + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect p_in1 + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in1, nets[0], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in2, nets[1], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out1, nets[2], AS_WRITER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out2, nets[3], AS_WRITER); + + // return the module + return obj; +} + +INSTANTIATE_AND_CONNECT_BI(DCElement, pt_helper) +{ + // create object with default params + element_type_bi *obj = new element_type_bi(name.c_str()); + + // Update unidirectional nets connected to the waveguide + if (!pt_helper.nets->at(nets[0]).bidirectional()) + (*pt_helper.nets)[nets[0]].m_writers_count++; + if (!pt_helper.nets->at(nets[1]).bidirectional()) + (*pt_helper.nets)[nets[1]].m_writers_count++; + if (!pt_helper.nets->at(nets[2]).bidirectional()) + (*pt_helper.nets)[nets[2]].m_readers_count++; + if (!pt_helper.nets->at(nets[3]).bidirectional()) + (*pt_helper.nets)[nets[3]].m_readers_count++; + + // Bidirectional DC needs all nets as bidir + for (const auto &net : nets) + pt_helper.upgrade_signal(net); + + // connect ports + pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p2_in, obj->p2_out, nets[2]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p3_in, obj->p3_out, nets[3]); + + // return the module + return obj; +} + +sc_module *DCElement::create(ParseTreeCreationHelper &pt_helper) const +{ + // TODO: change from field to power? + cout << "Warning: coupling power for DC currently set to field." << endl; + + element_type_base *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Check if device has to be bidirectional + bool bidirectional = false; + for (const auto &net : nets) + { + // for waveguide, if any net is bidir, module has to be bidir + bidirectional |= pt_helper.nets->at(net).bidirectional(); + } + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, bidirectional); + + // Parse positional arguments + if(args.size() > 0) + // convert from cross-coupling field coef + obj->m_dc_through_coupling_power = 1 - pow(args[0].as_double(), 2); + if(args.size() > 1) + obj->m_dc_loss = args[1].as_double(); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "K" || kw == "KF" || kw == "KFIELD") + // convert from cross-coupling field coef + obj->m_dc_through_coupling_power = 1 - pow(p.second.as_double(), 2); + else if (kw == "KP" || kw == "KPOW" || kw == "KPOWER") + // convert from cross-coupling field coef + obj->m_dc_through_coupling_power = 1 - p.second.as_double(); + else if (kw == "T") + // convert from cross-coupling field coef + obj->m_dc_through_coupling_power = pow(p.second.as_double(), 2); + else if (kw == "LOSS") + obj->m_dc_loss = p.second.as_double(); + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + return obj; +} + +/** ******************************************* **/ +/** Phase-shifter **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(PhaseShifterElement, pt_helper) +{ + // create object with default params + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[1], AS_WRITER); + pt_helper.connect_uni<spx::ea_signal_type>(obj->p_vin, nets[2], AS_READER); + + // return the module + return obj; +} + +INSTANTIATE_AND_CONNECT_BI(PhaseShifterElement, pt_helper) +{ + // create object with default params + element_type_bi *obj = new element_type_bi(name.c_str()); + + // Update unidirectional nets connected to the waveguide + if (!pt_helper.nets->at(nets[0]).bidirectional()) + (*pt_helper.nets)[nets[0]].m_writers_count++; + if (!pt_helper.nets->at(nets[1]).bidirectional()) + (*pt_helper.nets)[nets[1]].m_readers_count++; + + // Bidirectional phase-shifter needs only optical nets as bidir + pt_helper.upgrade_signal(nets[0]); + pt_helper.upgrade_signal(nets[1]); + + // connect ports + pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]); + pt_helper.connect_uni<spx::ea_signal_type>(obj->p_vin, nets[2], AS_READER); + + // return the module + return obj; +} + +sc_module *PhaseShifterElement::create(ParseTreeCreationHelper &pt_helper) const +{ + element_type_base *obj = nullptr; + + cout << "Warning (" << name << "): default coupling power for DC currently set to field." << endl; + + // Verify number of nodes + assert(nets.size() == n_nets); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Check if device has to be bidirectional + // for PS, if any optical net is bidir, module has to be bidir + bool bidirectional = false; + bidirectional |= pt_helper.nets->at(nets[0]).bidirectional(); + bidirectional |= pt_helper.nets->at(nets[1]).bidirectional(); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, bidirectional); + + // Verify number of args + assert(args.size() <= 2); + + // Parse positional arguments + if(args.size() > 0) + obj->m_sensitivity = args[0].as_double(); + if(args.size() > 1) + obj->m_attenuation_dB = args[1].as_double(); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + + if (kw == "ATTENUATION" || kw == "ATT") + obj->m_attenuation_dB = p.second.as_double(); + else if (kw == "SENSITIVITY" || kw == "GAIN" || kw == "G") + obj->m_sensitivity = p.second.as_double(); + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + return obj; +} + +/** ******************************************* **/ +/** MZI **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(MZIElement, pt_helper) +{ + // create object with default params + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in1, nets[0], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in2, nets[1], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out1, nets[2], AS_WRITER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out2, nets[3], AS_WRITER); + pt_helper.connect_uni<spx::ea_signal_type>(obj->p_vin, nets[4], AS_READER); + + return obj; +} + +INSTANTIATE_AND_CONNECT_BI(MZIElement, pt_helper) +{ + // create object with default params + element_type_bi *obj = new element_type_bi(name.c_str()); + + // Update unidirectional nets connected to the waveguide + if (!pt_helper.nets->at(nets[0]).bidirectional()) + (*pt_helper.nets)[nets[0]].m_writers_count++; + if (!pt_helper.nets->at(nets[1]).bidirectional()) + (*pt_helper.nets)[nets[1]].m_writers_count++; + if (!pt_helper.nets->at(nets[2]).bidirectional()) + (*pt_helper.nets)[nets[2]].m_readers_count++; + if (!pt_helper.nets->at(nets[3]).bidirectional()) + (*pt_helper.nets)[nets[3]].m_readers_count++; + + // Bidirectional phase-shifter needs only optical nets as bidir + pt_helper.upgrade_signal(nets[0]); + pt_helper.upgrade_signal(nets[1]); + pt_helper.upgrade_signal(nets[2]); + pt_helper.upgrade_signal(nets[3]); + + // connect ports + pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p2_in, obj->p2_out, nets[2]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p3_in, obj->p3_out, nets[3]); + pt_helper.connect_uni<spx::ea_signal_type>(obj->p_vin, nets[4], AS_READER); + + return obj; +} + +sc_module *MZIElement::create(ParseTreeCreationHelper &pt_helper) const +{ + element_type_base *obj = nullptr; + + cout << "Warning (" << name << "): default coupling power for DC currently set to field." << endl; + + // Verify number of nodes + assert(nets.size() == n_nets); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Check if device has to be bidirectional + // for PS, if any optical net is bidir, module has to be bidir + bool bidirectional = false; + bidirectional |= pt_helper.nets->at(nets[0]).bidirectional(); + bidirectional |= pt_helper.nets->at(nets[1]).bidirectional(); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, bidirectional); + + assert(args.size() <= 1); + // Parse positional arguments + if(args.size() > 0) + // convert from cross-coupling field coef + obj->m_ps_sens_rad_v = args[0].as_double(); + // TODO: other positional args + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "SENSITIVITY") + // convert from cross-coupling field coef + obj->m_ps_sens_rad_v = p.second.as_double(); + else if (kw == "LENGTH") + obj->m_length_cm = 100 * p.second.as_double(); + else if (kw == "LENGTH_REF") + obj->m_length_ref_cm = 100 * p.second.as_double(); + else if (kw == "NEFF") + obj->m_neff = p.second.as_double(); + else if (kw == "NG") + obj->m_ng = p.second.as_double(); + else if (kw == "ATT") + obj->m_attenuation_dB_cm = p.second.as_double(); + else if (kw == "IL_DC") + obj->m_dc_loss_dB = p.second.as_double(); + else if (kw == "IL_PHASESHIFTER") + obj->m_ps_loss_dB = p.second.as_double(); + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + return obj; +} + +/** ******************************************* **/ +/** Crossing **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(CrossingElement, pt_helper) +{ + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in1, nets[0], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in2, nets[1], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out1, nets[2], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out2, nets[3], AS_READER); + + // return the module + return obj; +} + +INSTANTIATE_AND_CONNECT_BI(CrossingElement, pt_helper) +{ + element_type_bi *obj = new element_type_bi(name.c_str()); + + // Update unidirectional nets connected to the waveguide + if (!pt_helper.nets->at(nets[0]).bidirectional()) + (*pt_helper.nets)[nets[0]].m_writers_count++; + if (!pt_helper.nets->at(nets[1]).bidirectional()) + (*pt_helper.nets)[nets[1]].m_writers_count++; + if (!pt_helper.nets->at(nets[2]).bidirectional()) + (*pt_helper.nets)[nets[2]].m_readers_count++; + if (!pt_helper.nets->at(nets[3]).bidirectional()) + (*pt_helper.nets)[nets[3]].m_readers_count++; + + // Bidirectional crossing needs all optical nets as bidir + pt_helper.upgrade_signal(nets[0]); + pt_helper.upgrade_signal(nets[1]); + pt_helper.upgrade_signal(nets[2]); + pt_helper.upgrade_signal(nets[3]); + + // connect ports + pt_helper.connect_bi<spx::oa_signal_type>(obj->p0_in, obj->p0_out, nets[0]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p1_in, obj->p1_out, nets[1]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p2_in, obj->p2_out, nets[2]); + pt_helper.connect_bi<spx::oa_signal_type>(obj->p3_in, obj->p3_out, nets[3]); + + // return the module + return obj; +} + +sc_module *CrossingElement::create(ParseTreeCreationHelper &pt_helper) const +{ + element_type_base *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Verify number of positional args + assert(args.size() <= 2); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + // Parse positional arguments + if(args.size() > 0) + obj->m_attenuation_power_dB = args[0].as_double(); + if(args.size() > 1) + obj->m_crosstalk_power_dB = args[1].as_double(); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "ATTENUATION" || kw == "ATT") + obj->m_attenuation_power_dB = p.second.as_double(); + else if (kw == "CROSSTALK" || kw == "XTALK") + obj->m_crosstalk_power_dB = p.second.as_double(); + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + return obj; +} + +/** ******************************************* **/ +/** CW Source **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(CWSourceElement, pt_helper) +{ + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[0], AS_WRITER); + + // return the module + return obj; +} + +sc_module *CWSourceElement::create(ParseTreeCreationHelper &pt_helper) const +{ + CWSource *obj = nullptr; + + // Verify number of nets + assert(nets.size() == 1); + + // Verify number of positional args + assert(args.size() <= 3); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + // Parse positional arguments + if(args.size() > 1) + // convert from cross-coupling field coef + obj->setWavelength(args[1].as_double()); + if(args.size() > 2) + obj->setPower(args[2].as_double()); + if(args.size() > 3) + obj->setPhase(args[3].as_double()); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "WL" || kw == "WAVELENGTH") + // convert from cross-coupling field coef + obj->setWavelength(p.second.as_double()); + else if (kw == "POWER") + // convert from cross-coupling field coef + obj->setPower(p.second.as_double()); + else if (kw == "PHI") + continue; // handle it after all power commands are executed + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + // TODO: make this cleaner (priority between setting kwargs...) + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "PHI") + obj->setPhase(p.second.as_double()); + } + return obj; +} + +/** ******************************************* **/ +/** VL Source **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(VLSourceElement, pt_helper) +{ + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[0], AS_WRITER); + + // return the module + return obj; +} + +sc_module *VLSourceElement::create(ParseTreeCreationHelper &pt_helper) const +{ + VLSource *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Verify number of positional args + assert(args.size() <= 1); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + // Parse positional arguments + if(args.size() > 1) + obj->setValues(args[0].as_string()); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "FILE") + { + cerr << "not implemented" << endl; + exit(1); + } + else if (kw == "SCALE") + { + cerr << "not implemented" << endl; + exit(1); + } + else if (kw == "VALUES") + { + // cout << "here" << endl; + vector<VLSource::time_value_pair_type> values; + bool is_string = p.second.type == Variable::STRING; + bool is_list = p.second.type == Variable::LIST; + if (!is_list && !is_string) + { + // cout << "A" << endl; + cerr << "Variable passed to keyword 'values' should be either:"<< endl; + cerr << " - an Nx3 list [[t0, p0, wl0], ...] with t0, p0 and wl0 convertible to double" + << " (or None if no change from previous entry)" << endl; + cerr << " - a filename containing the list" << endl; + exit(1); + } + + if (is_list) + { + for (const Variable &var : p.second.vec) + { + if (var.type != Variable::LIST || var.vec.size() != 3) + { + // cout << "B" << endl; + cerr << "Variable passed to keyword 'values' should be a an Nx3 list [[t0, p0, wl0], ...]" << endl; + cerr << "with t0, p0 and wl0 convertible to double (or None if no change from previous entry)" << endl; + exit(1); + } + for (const Variable &ivar : var.vec) + { + // cout << "C" << ivar.get_str() << endl; + + if (ivar.is_number() || ivar.is_none()) + continue; + + cerr << "Variable passed to keyword 'values' should be a an Nx3 list [[t0, p0, wl0], ...]" << endl; + cerr << "with t0, p0 and wl0 convertible to double (or None if no change from previous entry)" << endl; + exit(1); + } + double t, p, wl; + int none_cnt = 0; + if (var.vec[0].is_number()) + t = var.vec[0].as_double(); + else if (var.vec[0].is_none() && ! values.empty()) + { + t = values.back().first; + ++none_cnt; + } + else + { + cerr << "First values list entry should be fully specified" << endl; + exit(1); + } + + if (var.vec[1].is_number()) + p = var.vec[1].as_double(); + else if (var.vec[1].is_none() && ! values.empty()) + { + p = values.back().second.power(); + ++none_cnt; + } + else + { + cerr << "First values list entry should be fully specified" << endl; + exit(1); + } + + if (var.vec[2].is_number()) + wl = var.vec[2].as_double(); + else if (var.vec[2].is_none() && ! values.empty()) + { + wl = values.back().second.getWavelength(); + ++none_cnt; + } + else + { + cerr << "First values list entry should be fully specified" << endl; + exit(1); + } + + if (none_cnt < 3) + // OpticalSignal takes amplitude as value in the constructor + values.emplace_back(t, spx::oa_value_type(sqrt(p), wl)); + else + { + cerr << "Values list entry cannot be all none" << endl; + } + } + obj->setValues(values); + } + + if (is_string) + { + obj->setValues(p.second.as_string()); + } + } + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + + } + return obj; +} + +/** ******************************************* **/ +/** EVL Source **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(EVLSourceElement, pt_helper) +{ + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::ea_signal_type>(obj->p_out, nets[0], AS_WRITER); + + // return the module + return obj; +} + +sc_module *EVLSourceElement::create(ParseTreeCreationHelper &pt_helper) const +{ + EVLSource *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Verify number of positional args + assert(args.size() <= 1); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + // Parse positional arguments + if(args.size() > 1) + obj->setValues(args[0].as_string()); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "FILE") + { + cerr << "not implemented" << endl; + exit(1); + } + else if (kw == "SCALE") + { + cerr << "not implemented" << endl; + exit(1); + } + else if (kw == "VALUES") + { + // cout << "here" << endl; + vector<EVLSource::time_value_pair_type> values; + bool is_string = p.second.type == Variable::STRING; + bool is_list = p.second.type == Variable::LIST; + if (!is_list && !is_string) + { + cerr << "Variable passed to keyword 'values' should be either:"<< endl; + cerr << " - an Nx2 list [[t0, V0], ...] with t0 and V0 convertible to double" + << " (or None if no change from previous entry)" << endl; + cerr << " - a filename containing the list" << endl; + exit(1); + } + + if (is_list) + { + for (const Variable &var : p.second.vec) + { + if (var.type != Variable::LIST || var.vec.size() != 2) + { + cerr << "Variable passed to keyword 'values' should be either:"<< endl; + cerr << " - an Nx2 list [[t0, V0], ...] with t0 and V0 convertible to double" + << " (or None if no change from previous entry)" << endl; + cerr << " - a filename containing the list" << endl; + exit(1); + } + for (const Variable &ivar : var.vec) + { + if (ivar.is_number() || ivar.is_none()) + continue; + + cerr << "Variable passed to keyword 'values' should be either:"<< endl; + cerr << " - an Nx2 list [[t0, V0], ...] with t0 and V0 convertible to double" + << " (or None if no change from previous entry)" << endl; + cerr << " - a filename containing the list" << endl; + exit(1); + } + double t, V; + int none_cnt = 0; + if (var.vec[0].is_number()) + t = var.vec[0].as_double(); + else if (var.vec[0].is_none() && ! values.empty()) + { + t = values.back().first; + ++none_cnt; + } + else + { + cerr << "First values list entry should be fully specified" << endl; + exit(1); + } + + if (var.vec[1].is_number()) + V = var.vec[1].as_double(); + else if (var.vec[1].is_none() && ! values.empty()) + { + V = values.back().second; + ++none_cnt; + } + else + { + cerr << "First values list entry should be fully specified" << endl; + exit(1); + } + + if (none_cnt < 2) + values.emplace_back(t, spx::ea_value_type(V)); + else + { + cerr << "Values list entry cannot be all none" << endl; + } + } + obj->setValues(values); + } + + if (is_string) + { + obj->setValues(p.second.as_string()); + } + } + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + + } + return obj; +} + +/** ******************************************* **/ +/** Probe **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(ProbeElement, pt_helper) +{ + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect p_in + pt_helper.connect_uni<spx::oa_signal_type>( + obj->p_in, + nets[0], + AS_READER + ); + + // return the module + return obj; +} + +sc_module *ProbeElement::create(ParseTreeCreationHelper &pt_helper) const +{ + Probe *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Verify number of positional args + assert(args.size() <= 4); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + if (args.empty() && kwargs.empty()) + { + // cout << "empty args" << endl; + obj->m_trace_power = true; + obj->m_trace_modulus = true; + obj->m_trace_phase = true; + obj->m_trace_wavelength = true; + } + + // Parse positional args + for (size_t i = 0; i < args.size(); ++i) + { + switch (i) { + case 0: + obj->m_trace_power = args[i].as_boolean(); + break; + case 1: + obj->m_trace_modulus = args[i].as_boolean(); + break; + case 2: + obj->m_trace_phase = args[i].as_boolean(); + break; + case 3: + obj->m_trace_wavelength = args[i].as_boolean(); + break; + default: + cerr << "Too many input arguments for " << name << endl; + exit(1); + } + } + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "POWER" || kw == "POW" || kw == "P") + obj->m_trace_power = p.second.as_boolean(); + else if (kw == "MAGNITUDE" || kw == "MAG" || kw == "MODULUS" || kw == "MOD" || kw == "E") + obj->m_trace_modulus = p.second.as_boolean(); + else if (kw == "PHASE" || kw == "PHI") + obj->m_trace_phase = p.second.as_boolean(); + else if (kw == "WAVELENGTH" || kw == "WL") + obj->m_trace_wavelength = p.second.as_boolean(); + else { + cerr << name << ": unknown keyword: " << p.first << endl; + exit(1); + } + } + cout<< "created " << obj->name() << endl; + return obj; +} + +/** ******************************************* **/ +/** Multilambda Probe **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(MLProbeElement, pt_helper) +{ + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect p_in + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER); + + // return the module + return obj; +} + +sc_module *MLProbeElement::create(ParseTreeCreationHelper &pt_helper) const +{ + element_type_base *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Verify number of positional args + assert(args.size() <= 1); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + // Parse positional args + if (args.size() > 0) + { + if (args[0].is_list()) + { + for(auto v: args[0].vec) + { + obj->m_lambdas.insert(v.as_double()); + } + } + else { + cerr << "Positional arg to MLPROBE should be a list of wavelengths (e.g. [1.55e-6, 1.54e-6,...])" << endl; + exit(1); + } + } + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "WAVELENGTHS" || kw == "WL") { + if (p.second.is_list()) + { + for(auto v: p.second.vec) + { + obj->m_lambdas.insert(v.as_double()); + //obj->m_lambdas.push_back(v.as_double()); + } + } + else { + cerr << "value of kwarg \"" << kw << "\" to MLPROBE should be a list of wavelengths (e.g. [1.55e-6, 1.54e-6,...])" << endl; + exit(1); + } + } else { + cerr << name << ": unknown keyword: " << p.first << endl; + exit(1); + } + } + + return obj; +} + +/** ******************************************* **/ +/** Photodetector **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(PCMCellElement, pt_helper) +{ + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER); + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_out, nets[1], AS_WRITER); + + // return the module + return obj; +} + +sc_module *PCMCellElement::create(ParseTreeCreationHelper &pt_helper) const +{ + element_type_base *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Verify number of positional args + assert(args.size() <= 2); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + // Parse positional arguments + if(args.size() > 0) + obj->m_meltEnergy = args[0].as_double(); + if(args.size() > 1) + obj->m_nStates = args[1].as_integer(); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "MELT_ENERGY" || kw == "EMELT") + obj->m_meltEnergy = p.second.as_double(); + else if (kw == "N" || kw == "NSTATES") + obj->m_nStates = p.second.as_integer(); + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + if (obj->m_meltEnergy == 0 || obj->m_nStates == 0) + { + cerr << "PCM Cell needs values for both Emelt and Nstates" << endl; + exit(1); + } + cout << "-----" << endl; + return obj; +} + + +/** ******************************************* **/ +/** Photodetector **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(PhotodetectorElement, pt_helper) +{ + element_type_uni *obj = new element_type_uni(name.c_str()); + + // connect ports + pt_helper.connect_uni<spx::oa_signal_type>(obj->p_in, nets[0], AS_READER); + pt_helper.connect_uni<spx::ea_signal_type>(obj->p_readout, nets[1], AS_WRITER); + + // return the module + return obj; +} + +sc_module *PhotodetectorElement::create(ParseTreeCreationHelper &pt_helper) const +{ + element_type_base *obj = nullptr; + + // Verify number of nets + assert(nets.size() == n_nets); + + // Verify number of positional args + assert(args.size() <= 2); + + // Create signals if they don't exist + pt_helper.create_signals(this); + + // Create the object and connect ports to signals + INSTANTIATE(pt_helper, false); + + // Parse positional arguments + if(args.size() > 0) + obj->m_sampling_time = args[0].as_double(); + if(args.size() > 1) + obj->m_responsivity_A_W = args[1].as_double(); + + // Parse keyword arguments + for (auto &p: kwargs) + { + string kw = p.first; + strutils::toupper(kw); + if (kw == "SAMPLING_TIME" || kw == "TS") + obj->m_sampling_time = p.second.as_double(); + else if (kw == "R" || kw == "RESPONSIVITY" || kw == "GAIN") + obj->m_responsivity_A_W = p.second.as_double(); + else { + cerr << "Unknown keyword: " << p.first << endl; + exit(1); + } + } + return obj; +} + +/** ******************************************* **/ +/** Subcircuit instance **/ +/** ******************************************* **/ +INSTANTIATE_AND_CONNECT_UNI(XElement, pt_helper) +{ + (void)pt_helper; + return nullptr; +} + +sc_module *XElement::create(ParseTreeCreationHelper &pt_helper) const +{ + (void)pt_helper; + cerr << "X element should never be called directly" << endl; + exit(1); + return nullptr; +} diff --git a/src/parser/parse_element.h b/src/parser/parse_element.h new file mode 100644 index 0000000..58ad694 --- /dev/null +++ b/src/parser/parse_element.h @@ -0,0 +1,104 @@ +#pragma once + +#include "alldevices.h" +#include "subcircuit_instance.h" +#include "parse_tree.h" +#include "specs.h" + +#include <initializer_list> +#include <utility> +#include <memory> +#include <string> +#include <vector> +#include <map> +#include <iostream> + +using std::pair; +using std::make_pair; +using std::string; +using std::vector; +using std::map; +using std::cout; +using std::cerr; +using std::endl; +using std::pair; +using std::unique_ptr; + +/** ******************************************* **/ +/** Helper macros for declaring devices **/ +/** ******************************************* **/ +#define DECLARE_UNIDIR_ELEMENT(ELEM_NAME, ELEM_NAME_LONG, MOD_CLASS_PREFIX, N_NETS) \ + struct ELEM_NAME : public ParseElement { \ + typedef MOD_CLASS_PREFIX element_type_base; \ + typedef MOD_CLASS_PREFIX element_type_uni; \ + \ + const size_t n_nets = N_NETS; \ + \ + /* Import constructor from ParseElement */ \ + using ParseElement::ParseElement; \ + \ + virtual ParseElement *clone() const \ + { return new ELEM_NAME(*this); } \ + /* Implement virtual function create(...) to construct element */ \ + virtual sc_module *create(ParseTreeCreationHelper &pt_helper) const; \ + virtual element_type_base * \ + instantiate_and_connect_uni(ParseTreeCreationHelper &pt_helper) const; \ + \ + virtual string kind() const { return ELEM_NAME_LONG; } \ + }; + +/*************************************************/ + +#define DECLARE_BIDIR_ELEMENT(ELEM_NAME, ELEM_NAME_LONG, MOD_CLASS_PREFIX, N_NETS) \ + \ + struct ELEM_NAME : public ParseElement { \ + typedef MOD_CLASS_PREFIX##Base element_type_base; \ + typedef MOD_CLASS_PREFIX##Uni element_type_uni; \ + typedef MOD_CLASS_PREFIX##Bi element_type_bi; \ + \ + const size_t n_nets = N_NETS; \ + \ + /* Import constructor from ParseElement */ \ + using ParseElement::ParseElement; \ + \ + virtual ParseElement *clone() const \ + { return new ELEM_NAME(*this); } \ + /* Implement virtual function create(...) to construct element */ \ + virtual sc_module *create(ParseTreeCreationHelper &pt_helper) const; \ + virtual element_type_base * \ + instantiate_and_connect_uni(ParseTreeCreationHelper &pt_helper) const; \ + virtual element_type_base * \ + instantiate_and_connect_bi(ParseTreeCreationHelper &pt_helper) const; \ + \ + virtual string kind() const { return string(ELEM_NAME_LONG) + " (bidir)"; } \ + }; + +/** ******************************************* **/ +/** Devices declarations **/ +/** ******************************************* **/ +DECLARE_BIDIR_ELEMENT(WGElement, "WAVEGUIDE", Waveguide, 2); +DECLARE_BIDIR_ELEMENT(DCElement, "DIRECTIONAL COUPLER", DirectionalCoupler, 4); +DECLARE_UNIDIR_ELEMENT(MergerElement, "MERGER", Merger, 3); +DECLARE_UNIDIR_ELEMENT(SplitterElement, "SPLITTER", Splitter, 3); +DECLARE_BIDIR_ELEMENT(PhaseShifterElement, "PHASE SHIFTER", PhaseShifter, 3); +DECLARE_BIDIR_ELEMENT(MZIElement, "MZI MODULATOR", MZIActive, 5); +//DECLARE_UNIDIR_ELEMENT(MZIElement, "MZI MODULATOR (2 PHASE-SHIFTER)", MZI, 6); +DECLARE_BIDIR_ELEMENT(CrossingElement, "CROSSING", Crossing, 4); +DECLARE_UNIDIR_ELEMENT(CWSourceElement, "CW SOURCE", CWSource, 1); +DECLARE_UNIDIR_ELEMENT(VLSourceElement, "VALUE LIST SOURCE (OPTICAL)", VLSource, 1); +DECLARE_UNIDIR_ELEMENT(EVLSourceElement, "VALUE LIST SOURCE (ELECTRICAL)", EVLSource, 1); + +// TODO: make the following bidirectional +DECLARE_UNIDIR_ELEMENT(PCMCellElement, "PCM CELL", PCMElement, 2); +DECLARE_UNIDIR_ELEMENT(PhotodetectorElement, "PHOTODETECTOR", Detector, 2); +DECLARE_UNIDIR_ELEMENT(ProbeElement, "PROBE", Probe, 1); +DECLARE_UNIDIR_ELEMENT(MLProbeElement, "MULTIWAVELENGTH PROBE", MLambdaProbe, 1); + +// TODO: take care of subcircuit instance... +DECLARE_UNIDIR_ELEMENT(XElement, "SUBCIRCUIT", SubcircuitInstance, 1); + +/** ******************************************* **/ +/** Undefine macros **/ +/** ******************************************* **/ +#undef DECLARE_UNIDIR_ELEMENT +#undef DECLARE_BIDIR_ELEMENT
\ No newline at end of file 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 diff --git a/src/parser/parse_tree.h b/src/parser/parse_tree.h new file mode 100644 index 0000000..fbcefbc --- /dev/null +++ b/src/parser/parse_tree.h @@ -0,0 +1,890 @@ +#pragma once + +#include "specs.h" +#include "strutils.h" + +#include <initializer_list> +#include <utility> +#include <memory> +#include <string> +#include <vector> +#include <map> +#include <iostream> + +using std::pair; +using std::make_pair; +using std::string; +using std::vector; +using std::map; +using std::cout; +using std::cerr; +using std::endl; +using std::pair; +using std::unique_ptr; + +struct ParseTree; +struct SUBCKTDirective; + +/* Struct to hold a typed variable */ +struct Variable { + enum Type { + TYPE_MIN = -1, + DOUBLE = 0, + COMPLEX_DOUBLE, + INTEGER, + BOOLEAN, + STRING, + LIST, + NONE, + TYPE_MAX, + }; + Type type; + double num; + complex<double> cnum; + int inum; + bool bnum; + string str; + vector<Variable> vec; + + Variable(const Variable &v) { *this = v; } + Variable(Type type): type(type) {} + Variable(double num = 0): type(DOUBLE), num(num) {} + explicit Variable(const complex<double> &cnum): type(COMPLEX_DOUBLE), cnum(cnum) {} + explicit Variable(int inum): type(INTEGER), inum(inum) {} + explicit Variable(bool bnum): type(BOOLEAN), bnum(bnum) {} + explicit Variable(const string &str): type(STRING), str(str) {} + explicit Variable(const vector<Variable> &vec): type(LIST), vec(vec) {} + explicit Variable(const vector<double> &dvec): type(LIST) + { + vec.resize(dvec.size()); + for (size_t i = 0; i < dvec.size(); ++i) + { + vec[i] = Variable(dvec[i]); + } + } + + bool type_is_valid() const { + return TYPE_MIN < type && type < TYPE_MAX; + } + + bool is_none() const { + return type == NONE; + } + + bool is_list() const { + return type == LIST; + } + + bool is_number() const { + return type == DOUBLE || type == INTEGER || type == BOOLEAN; + } + + string get_str() const + { + stringstream ss; + switch (type) + { + case STRING: + return str; + case DOUBLE: + ss << num; + return ss.str(); + case COMPLEX_DOUBLE: + ss << cnum.real() << " + " << cnum.imag() << "i"; + return ss.str(); + case INTEGER: + ss << inum; + return ss.str(); + case BOOLEAN: + ss << bnum; + return ss.str(); + case LIST: + ss << "["; + for (size_t i = 0; i < vec.size(); ++i) + { + ss << vec[i].to_json(); + if (i != vec.size() - 1) + ss << ", "; + } + ss << "]"; + return ss.str(); + case NONE: + return "None"; + default: + cerr << "error: cannot convert variable to string" << endl; + exit(1); + } + } + + string to_json() const + { + stringstream ss; + ss << '{'; + ss << "\"type\":" << '"' << kind() << '"' << ','; + ss << "\"value\":"; + { + stringstream tss; + switch (type) { + case STRING: + tss << '"' << str << '"'; + break; + case DOUBLE: + tss << scientific << num; + break; + case COMPLEX_DOUBLE: + tss << "{\"real\":"; + tss << scientific << cnum.real(); + tss << ",\"imag\":" << cnum.imag(); + tss << '}'; + break; + case INTEGER: + tss << inum; + break; + case BOOLEAN: + tss << (bnum ? "true" : "false"); + break; + case LIST: + tss << "["; + for (size_t i = 0; i < vec.size(); ++i) { + tss << vec[i].to_json(); + if (i != vec.size() - 1) + tss << ", "; + } + tss << "]"; + break; + case NONE: + tss << "null"; + break; + default: + cerr << "error: cannot convert variable to json representation" << endl; + exit(1); + } + ss << tss.str(); + } + ss << '}'; + return ss.str(); + } + + double as_double() const + { + if (type == DOUBLE) + return num; + if (type == INTEGER) + return inum; + if (type == BOOLEAN) + return bnum; + cerr << "Variable cannot be converted to a double value: " << get_str() << endl; + exit(1); + } + + int as_integer() const + { + if (type == DOUBLE) + return num; + if (type == INTEGER) + return inum; + if (type == BOOLEAN) + return bnum; + cerr << "Variable cannot be converted to an integer value: " << get_str() << endl; + exit(1); + } + + bool as_boolean() const + { + if (type == DOUBLE) + return num; + if (type == INTEGER) + return inum; + if (type == BOOLEAN) + return bnum; + cerr << "Variable cannot be converted to a boolean value: " << get_str() << endl; + exit(1); + } + + complex<double> as_complex() const + { + if (type == DOUBLE) + return num; + if (type == COMPLEX_DOUBLE) + return cnum; + if (type == INTEGER) + return inum; + if (type == BOOLEAN) + return bnum; + cerr << "Variable cannot be converted to a complex value: " << get_str() << endl; + exit(1); + } + + string as_string() const + { + if (type == STRING) + return str; + cerr << "Variable is not a string: " << get_str() << endl; + exit(1); + return 0; + } + + string kind() const + { + if (type == DOUBLE) + return "DOUBLE"; + if (type == COMPLEX_DOUBLE) + return "COMPLEX_DOUBLE"; + if (type == INTEGER) + return "INTEGER"; + if (type == BOOLEAN) + return "BOOLEAN"; + if (type == STRING) + return "STRING"; + if (type == LIST) + return "LIST"; + if (type == NONE) + return "NONE"; + + return "UNDEFINED"; + } + + Variable &operator+=(const Variable &rhs) + { + num = as_double() + rhs.as_double(); + type = DOUBLE; + return *this; + } + Variable &operator-=(const Variable &rhs) + { + num = as_double() - rhs.as_double(); + type = DOUBLE; + return *this; + } + Variable &operator*=(const Variable &rhs) + { + num = as_double() * rhs.as_double(); + type = DOUBLE; + return *this; + } + Variable &operator/=(const Variable &rhs) + { + num = as_double() / rhs.as_double(); + type = DOUBLE; + return *this; + } + Variable &operator^=(const Variable &rhs) + { + num = pow(as_double(), rhs.as_double()); + type = DOUBLE; + return *this; + } + Variable &operator=(const Variable &rhs) = default; + operator string() const { return get_str(); } +}; + +/* Dummy circuit directive struct */ +struct ParseDirective { + ParseTree *parent; + vector<Variable> args; // list of non-keyword arguments found on netlist + map<string, Variable> kwargs; // list of kw arguments found on netlist + + ParseDirective() + {} + + ParseDirective(const ParseDirective& other) = default; + + virtual void print() const + { + cout << "DIRECTIVE[" << kind() << "] "; + cout << "{"; + for (const auto &x : args) + cout << x.get_str() << " (" << x.kind() << "), "; + if ( !args.empty() ) + cout << "\b\b"; + cout << "} "; + cout << "{"; + for (const auto &x : kwargs) + cout << x.first << " (" << x.second.kind() << ") = " << x.second.get_str() << ", "; + if ( !kwargs.empty() ) + cout << "\b\b"; + cout << "}" << endl; + } + virtual string to_json() const; + + virtual ParseDirective *clone() const = 0; + virtual void create() const = 0; + virtual string kind() const { return "UNDEFINED"; } + virtual ~ParseDirective() {} +}; + +/* Dummy circuit analysis struct */ +struct ParseAnalysis { + const ParseTree *parent; + vector<Variable> args; // list of non-keyword arguments found on netlist + map<string, Variable> kwargs; // list of kw arguments found on netlist + + ParseAnalysis() + {} + + ParseAnalysis(const ParseAnalysis& other) = default; + + void print() const + { + cout << "ANALYSIS[" << kind() << "] "; + cout << "{"; + for (const auto &x : args) + cout << x.get_str() << " (" << x.kind() << "), "; + if ( !args.empty() ) + cout << "\b\b"; + cout << "} "; + cout << "{"; + for (const auto &x : kwargs) + cout << x.first << " (" << x.second.kind() << ") = " << x.second.get_str() << ", "; + if ( !kwargs.empty() ) + cout << "\b\b"; + cout << "}" << endl; + } + virtual string to_json() const; + + virtual ParseAnalysis *clone() const = 0; + virtual void create() const = 0; + virtual string kind() const { return "UNDEFINED"; } + virtual ~ParseAnalysis() {} +}; + +/* SUBCKT: subcircuit definition */ +struct ParseSubcircuit { + string name; // name of the subcircuit + string netlist; // subcircuit netlist (extracted from parent netlist) + vector<string> ports; // name of ports + map<string, Variable> kwargs; // list of kw arguments found on netlist + const ParseTree *parent; + + ParseSubcircuit(const string &name, ParseTree *parent = nullptr) + : name(name) + , parent(parent) + {} + + ParseSubcircuit(const ParseSubcircuit& other) = default; + + void register_port(const string &port_name) + { + auto it = find(ports.cbegin(), ports.cend(), port_name); + if (it == ports.cend()) + { + ports.push_back(port_name); + } + else + { + cerr << "Subcircuit cannot have duplicate port names"; + cerr << "(" << port_name << " was already defined)" << endl; + exit(1); + } + } + //ParseTree *instantiate(const string &instance_name, const map<string,Variable> &kwargs) const; + void print() const + { + cout << name << " (" << kind() << ") "; + for (const auto &x : ports) + cout << x << " "; + cout << "{"; + for (const auto &x : kwargs) + cout << x.first << " (" << x.second.kind() << ") = " << x.second.get_str() << ", "; + if ( !kwargs.empty() ) + cout << "\b\b"; + cout << "}" << endl; + if (false) + { + cout << "###########################" << endl; + cout << "## Start netlist of " << name << endl; + cout << "###########################" << endl; + cout << netlist; + cout << "###########################" << endl; + cout << "## End netlist of " << name << endl; + cout << "###########################" << endl; + } + } + string to_json() const; + string kind() const + { return "SUBCIRCUIT DEFINITION"; } + ParseSubcircuit *clone() const + { return new ParseSubcircuit(*this); } +}; + +/* Dummy circuit net struct (although close to what it should look like) */ +struct ParseNet { + /* Possible net types */ + enum Type { + NONE, + OANALOG, + EANALOG, + EDIGITAL, + }; + + /* Member variables */ + Type m_type; + unsigned int m_size; // number of "wires (more than 1 for buses) + bool m_bidirectional = false; // whether the signal should be bidirectional (for oanalog and eanalog representing forward/backward waves) + unsigned int m_writers_count = 0; // number of writers + unsigned int m_readers_count = 0; // number of readers + unsigned int m_ports_count = 0; // number of individual ports connected (in total) + unsigned int m_connect_count = 0; // number of ports actually connected (updated during creation phase only) + unsigned int m_connect_writer_count = 0; // number of writer ports actually connected (updated during creation phase only) + unsigned int m_connect_reader_count = 0; // number of reader ports actually connected (updated during creation phase only) + + /* Constructor */ + ParseNet(Type type = NONE, unsigned int size = 1): m_type(type), m_size(size) {} + ParseNet(const ParseNet &n) = default; + inline ParseNet& operator=(const ParseNet &n) = default; + bool combine_with(const ParseNet &n) + { + if (m_type != n.m_type) + return false; + if (m_size != n.m_size) + return false; + if (m_connect_count || n.m_connect_count) + return false; + + m_bidirectional |= n.m_bidirectional; + m_writers_count += n.m_writers_count; + m_readers_count += n.m_readers_count; + m_ports_count += n.m_ports_count; + + return true; + } + + /* const Getters */ + unsigned int size() const { return m_size; } + Type type() const { return m_type; } + bool bidirectional() const + { + return m_bidirectional || (m_writers_count == 2); + //return m_bidirectional || (m_writers_count == 2) || (m_readers_count == 2); + } + bool validate() const + { + return m_writers_count <= 2 && m_ports_count <= 2; + } + + /* setters */ + inline bool setType(Type type, bool force = false) { + //cout << type_str() << " - " << ParseNet(type).type_str() << endl; + if (m_type == type) + // no need to do anything + return true; + if (m_type == NONE || force) + { + m_type = type; + return true; + } + // cerr << "Incompatible net type assignment" << endl; + return false; + } + + string type_str() const + { + switch (m_type) { + case Type::NONE: + return "NONE"; + case Type::OANALOG: + return "OANALOG"; + case Type::EANALOG: + return "EANALOG"; + case Type::EDIGITAL: + return "EDIGITAL"; + default: + cerr << "Unexpected type" << endl; + exit(1); + } + } + + string to_json() const; + + // create the net + vector<shared_ptr<sc_object>> create(const string &name, bool force_bidir = false) const; + shared_ptr<sc_object> create_uni(const string &name) const; + + /* Get a generic net name from net number (different from id) */ + static string name_from_id(int net_number); +}; + +struct ParseTreeCreationHelper; + +/* Dummy circuit element struct (although close to )*/ +struct ParseElement { + typedef sc_module element_type_base; + + string name; + const ParseTree *parent; + vector<string> nets; // name of parent nets connected to element + vector<Variable> args; // list of non-keyword arguments found on netlist + map<string, Variable> kwargs; // list of kw arguments found on netlist + + const int n_nets = 0; + const bool bidirectionalable = false; + + ParseElement(const string &name) + : name(name) + {} + + ParseElement(const string &name, const vector<string> &nets) + : name(name) + , nets(nets) + {} + + ParseElement(const ParseElement& other) = default; + + void print() const + { + cout << name << " (" << kind() << ") "; + for (const auto &x : nets) + cout << x << " "; + cout << "{"; + for (const auto &x : args) + cout << x.get_str() << " (" << x.kind() << "), "; + if ( !args.empty() ) + cout << "\b\b"; + cout << "} "; + cout << "{"; + for (const auto &x : kwargs) + cout << x.first << " (" << x.second.kind() << ") = " << x.second.get_str() << ", "; + if ( !kwargs.empty() ) + cout << "\b\b"; + cout << "}" << endl; + } + + virtual ParseElement *clone() const = 0; + virtual sc_module *create(ParseTreeCreationHelper &pt_helper) const = 0; + virtual element_type_base *instantiate_and_connect_uni(ParseTreeCreationHelper &pt_helper) const = 0; + virtual element_type_base *instantiate_and_connect_bi(ParseTreeCreationHelper &pt_helper) const + { + (void) pt_helper; + cerr << "Elements of type " << kind() << " cannot be bidirectional" << endl; + exit(1); + } + + virtual string kind() const { return "UNDEFINED"; } + virtual ~ParseElement() {} + + virtual string to_json() const; +}; + +struct ParseTree { + string name; + const ParseTree *parent = nullptr; + bool is_subcircuit = false; + size_t unnamed_net_count = 0; + + map<string, ParseNet> nets; + vector<string> ports; // only for subcircuits + vector<ParseElement *> elements; + vector<ParseAnalysis *> analyses; + vector<ParseDirective *> directives; + vector<ParseSubcircuit *> subcircuits; + map<string, Variable> local_assignments; + static map<string, Variable> global_assignments; + + ParseTree(const string &name = "ROOT") + : name(name) + {} + + ParseTree(const string &name, const ParseSubcircuit &subcircuit, const map<string, Variable> &kwargs); + + int register_directive(ParseDirective *directive); + int register_element(ParseElement *element); + int register_analysis(ParseAnalysis *analysis); + int register_subcircuit(string name); + const ParseSubcircuit *find_subcircuit(const string &name) const; + + string get_or_create_net(string net_name, unsigned int size = 1, + ParseNet::Type type = ParseNet::NONE) + { + if (net_name == "_") + { + net_name = "_" + to_string(unnamed_net_count++); + } + + string full_net_name = name_prefix() + net_name; + if (nets.count(full_net_name) == 0) + { + nets.emplace(full_net_name, ParseNet(type, size)); + return full_net_name; + } + if (nets[full_net_name].size() != size) + { + cerr << "Incompatible size: " << net_name << "<" << size << ">" + << " (previously declared size was: " << nets[full_net_name].size() + << ")" << endl; + exit(1); + } + if (nets[full_net_name].type() == ParseNet::NONE && type != ParseNet::NONE) + nets[full_net_name].m_type = type; + if (type == ParseNet::NONE && nets[full_net_name].type() != ParseNet::NONE) + type = nets[full_net_name].m_type; + if (nets[full_net_name].type() != type) + { + cerr << "Incompatible type: " << net_name << "[" << ParseNet(type).type_str() << "]" + << " (previously declared type was: " << nets[full_net_name].type_str() + << ")" << endl; + exit(1); + } + return full_net_name; + } + + string get_or_create_net(int i, unsigned int size = 1, + ParseNet::Type type = ParseNet::NONE) + { + return get_or_create_net(ParseNet::name_from_id(i), size, type); + } + string get_or_create_net(Variable v, unsigned int size = 1, + ParseNet::Type type = ParseNet::NONE) + { + if (v.type == Variable::STRING) + return get_or_create_net(v.as_string(), size, type); + else if (v.type == Variable::INTEGER) + return get_or_create_net(v.as_integer(), size, type); + else + { + cerr << "Incompatible variable type for creating a net" << endl; + exit(1); + } + } + + string name_prefix() const + { + string ret = ""; + if ( !is_subcircuit && parent ) + { + ret = parent->name_prefix(); + if ( name.empty() ) + { + cerr << "Name of child parse tree should not be empty" << endl; + exit(1); + } + } + ret += name + "/"; + strutils::toupper(ret); + return ret; + } + + template <typename T> + void set_local_variable(string &name, T val) + { + // will throw a compile error if type T is not supported by Variable + local_assignments[name] = val; + } + + template <typename T> + void set_global_variable(string &name, T val) + { + // will throw a compile error if type T is not supported by Variable + global_assignments[name] = val; + } + + Variable get_variable(string &name) + { + // try to find variable in locals table first + auto it = local_assignments.find(name); + if (it != local_assignments.end()) + return it->second; + + // try to find variable in globals table + it = global_assignments.find(name); + if (it != global_assignments.end()) + return it->second; + + // error + cerr << "Variable not found: " << name << endl; + exit(1); + } + + void print() const; + string to_json() const; + + ParseTree *clone() const + { + ParseTree *clone = new ParseTree(name); + + clone->parent = nullptr; + clone->ports = ports; + clone->nets = nets; + clone->local_assignments = local_assignments; + clone->unnamed_net_count = unnamed_net_count; + + for (const auto &x : elements) + clone->elements.push_back(x->clone()); + for (const auto &x : analyses) + clone->analyses.push_back(x->clone()); + for (const auto &x : directives) + clone->directives.push_back(x->clone()); + for (const auto &x : subcircuits) + clone->subcircuits.push_back(x->clone()); + + return clone; + } + void build_circuit(); + void flatten(); + + ~ParseTree() + { + if (!parent) + { + for (const auto &x : elements) + if(x) delete x; + for (const auto &x : analyses) + if(x) delete x; + for (const auto &x : directives) + if(x) delete x; + for (const auto &x : subcircuits) + if(x) delete x; + } + } +}; + +struct ParseTreeCreationHelper { + ParseTree *pt; + + // backlog: elements to be created in priority + map<string, ParseNet> *nets; + + // backlog: elements to be created in priority + set<const ParseElement *> elements_backlog; + + // Circuit elements generated by build + map<string, vector<shared_ptr<sc_object>>> circuit_signals; + map<string, shared_ptr<sc_module>> circuit_modules; + + ParseTreeCreationHelper() + : pt(nullptr) + {} + + ParseTreeCreationHelper(ParseTree *parse_tree) + { + resetParseTree(parse_tree); + } + + void resetParseTree(ParseTree *parse_tree) + { + pt = parse_tree; + nets = &parse_tree->nets; + elements_backlog.clear(); + circuit_signals.clear(); + circuit_modules.clear(); + } + + // return next bidir net which doesn't have a corresponding signal in circuit_signals + map<string, ParseNet>::iterator next_fresh_bidir_net(); + + // return next net which doesn't have a corresponding signal in circuit_signals + map<string, ParseNet>::iterator next_fresh_net(); + + // return next element which is connected to "net_name" and doesnt have a corresponding module in circuit_modules + vector<ParseElement *>::iterator next_fresh_element_bound_to(const string &net_name, const set<const ParseElement *> &excludes = {}); + + // upgrade a signal to bidirectional + void upgrade_signal(const string &name); + + // Create signals connected to a given element + void create_signals(const ParseElement *elem); + + // Connect a port to a signal using the net name + template <typename signal_type, typename port_type> + void connect_uni(port_type &port, const string& net_name, bool as_writer); + + // Connect a bidirectional port to a signal pair using the net name + template <typename signal_type, typename port_in_type, typename port_out_type> + void connect_bi(port_in_type &p_in, port_out_type &p_out, const string& net_name); + + ~ParseTreeCreationHelper() + {} +}; + +/** ******************************************* **/ +/** Non-specialized template implementations **/ +/** ******************************************* **/ + +template <typename signal_type, typename port_type> +void ParseTreeCreationHelper::connect_uni(port_type &port, const string& net_name, bool as_writer) +{ + assert(pt->nets.find(net_name) != pt->nets.end()); + const auto &net = pt->nets.at(net_name); + + // Get the signal to be written or read + signal_type *sig = nullptr; + if ( !net.bidirectional()) + { + auto sig_raw = circuit_signals.at(net_name)[0].get(); + sig = dynamic_cast<signal_type *>(sig_raw); + } + else + { + int i_write = pt->nets.at(net_name).m_connect_count; + int i_read = (i_write + 1) % 2; + int i = as_writer ? i_write : i_read; + + auto sig_raw = circuit_signals.at(net_name)[i].get(); + sig = dynamic_cast<signal_type *>(sig_raw); + } + + // Check it could correctly be cast to requested type + if (!sig) + { + cerr << "Wrong signal type for " << sig << " connected to " + << net_name << endl; + exit(1); + } + + // Connect the port to the signal + port.bind(*sig); + + // Update connection count of the net + pt->nets[net_name].m_connect_count++; + if (as_writer) + pt->nets[net_name].m_connect_writer_count++; + else + pt->nets[net_name].m_connect_reader_count++; +} + +template <typename signal_type, typename port_in_type, typename port_out_type> +void ParseTreeCreationHelper::connect_bi(port_in_type &p_in, port_out_type &p_out, const string& net_name) +{ + assert(pt->nets.find(net_name) != pt->nets.end()); + const auto &net = pt->nets.at(net_name); + + if (net.bidirectional()) + { + + // First get the signal to be written or read (in the right order) + int i_write = pt->nets.at(net_name).m_connect_count; + int i_read = (i_write + 1) % 2; + + auto sig_writeable_raw = circuit_signals.at(net_name)[i_write].get(); + auto sig_readable_raw = circuit_signals.at(net_name)[i_read].get(); + auto sig_writeable = dynamic_cast<signal_type *>(sig_writeable_raw); + auto sig_readable = dynamic_cast<signal_type *>(sig_readable_raw); + + // Check they could correctly be cast to requested type + if (!sig_writeable) + { + cerr << "Wrong signal type for " << sig_writeable << " connected to " + << net_name << endl; + exit(1); + } + if (!sig_readable) + { + cerr << "Wrong signal type for " << sig_readable << " connected to " + << net_name << endl; + exit(1); + } + + // Connect ports to both signals + p_out.bind(*sig_writeable); + p_in.bind(*sig_readable); + + // Update connection count of the net + pt->nets[net_name].m_connect_count++; + pt->nets[net_name].m_connect_writer_count++; + pt->nets[net_name].m_connect_reader_count++; + } + else + { + cerr << "Expected a bidirectional net for " << net_name << endl; + exit(1); + } +}
\ No newline at end of file diff --git a/src/parser/parser.l b/src/parser/parser.l new file mode 100644 index 0000000..ca689a3 --- /dev/null +++ b/src/parser/parser.l @@ -0,0 +1,438 @@ +ALPHA [[:alpha:]] +DIGIT [0-9] +ALPHA_NUM {ALPHA}|{DIGIT} +ALPHA_PLUS {ALPHA}|[_] +ALPHA_PLUS_NUM {ALPHA}|[_]|{DIGIT} +D {DIGIT} + +DOUBLE (({D}+)|({D}*\.{D}+))([eE][-+]?[0-9]+)? + +BIDIR_SPECIFIER [&] +BIN_OPERATOR [,=\*/\+\-\^] +PAR_OPERATOR [\{\}\(\)\[\]<>] +OPERATOR {BIN_OPERATOR}|{PAR_OPERATOR}|{BIDIR_SPECIFIER} + + +WS [ Â \t] +WS_BREAK (?:[^{WS}]) + +/* EOL_DOS \r\n */ +/* EOL_UNIX \n */ +/* EOL_OLD_MAC \n */ +EOL_PORTABLE \r?\n +EOL {EOL_PORTABLE} + +%option warn +%option yylineno +%option noyywrap +%option caseless +%option reentrant +%option bison-bridge +%option stack + +%{ +#include <string> +#include <sstream> // for conversions of numbers from str +#include <cstring> // for strcpy +#include <vector> +#include "parse_tree.h" + +#include "parser.tab.h" + +using std::string; +using std::vector; + +#define YY_NO_INPUT +#define YY_NO_UNPUT +#define YY_EXTRA_TYPE void* + +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size, yyscan_t scanner); +void yypush_buffer_state ( YY_BUFFER_STATE buffer, yyscan_t scanner); +void yypop_buffer_state ( yyscan_t scanner ); +int yyget_lineno ( yyscan_t scanner ); +int yyget_column ( yyscan_t scanner ); +void yyset_extra ( YY_EXTRA_TYPE user_defined, yyscan_t scanner ); +YY_EXTRA_TYPE yyget_extra ( yyscan_t scanner ); + +int yylerror(yyscan_t scanner, const char *p) +{ + cerr << "error (line " << yyget_lineno(scanner) << "): " << p << endl; + string * str = (string *)yyget_extra(scanner); + if (str) + cerr << "\twhile parsing line: " << *str << endl; + return 3; +} + +ParseTree *cur_pt = nullptr; + +static int internal_subckt_count = 0; +static size_t delete_me_npos = 0; + +template<typename T> +static T from_string(const string &str) +{ + std::stringstream ss(str); + T ret; + ss >> ret; + return ret; +} + +%} + +/*** States ***/ +/* %x IN_DQUOTES */ +%x INCLUDE +%s SUBCKT_DECLARATION +%x SUBCKT_DEFINITION +/*Quoted string (returns char[]) (capture until next '"' and throw error on newline)*/ +%x IN_DQUOTES + + + +/* To ignore case: (?i:xxx) */ + +%% /*** Rules section ***/ + +^\.include {yy_push_state(INCLUDE, yyscanner);} +<INCLUDE>{WS}* /* eat the whitespace */ +<INCLUDE>[^ ;\t\n\r]+ { /* got the include file name */ + yyin = fopen( yytext, "r" ); + + if ( ! yyin ) + { + cerr << "Error: included file not found \"" << yytext << "\"" << endl; + exit(1); + } + cout << "Including file: " << yytext << endl; + yypush_buffer_state(yy_create_buffer( yyin, YY_BUF_SIZE, yyscanner), yyscanner); + + //yy_push_state(INITIAL, yyscanner); + BEGIN(INITIAL); + } + +<IN_DQUOTES>\" { + yy_pop_state(yyscanner); + // cout << "leaving dquotes mode" << endl; + return '"'; + } +<IN_DQUOTES>{EOL} { exit(yylerror(yyscanner, "Unmatched double quote")); } +<IN_DQUOTES>[^\"\r\n]* { + // cout << "found quoted string: " << yytext << endl; + yylval_param->s_ptr = new string(yytext); + return T_STR; + } + +<SUBCKT_DECLARATION>{EOL} { + // end of subcircuit declaration line (.subckt name node1 node2...) + // go into subckt_definition state, which copies all text to a string + // until the corresponding .ends is reached + yylval_param->s_ptr = new string; + internal_subckt_count = 0; + delete_me_npos = 0; + // cout << endl << "going to subckt definition mode" << endl; + BEGIN(SUBCKT_DEFINITION); //needs BEGIN and not yy_push_state +} + +<SUBCKT_DEFINITION>^\.SUBCKT { + // cout << "hello2" << endl; + // increase internal count for keeping track of included subcircuits + internal_subckt_count++; + // but reject the rule and go to the next matching pattern + // (i.e. save the line to the string) + REJECT; +} + +<SUBCKT_DEFINITION>^\.ENDS { + if (internal_subckt_count) + { + // found .ends of included subcircuit + // decrease internal count + --internal_subckt_count; + // but reject the rule and go to the next matching pattern + // (i.e. save the line to the string) + REJECT; + } + else + { + // found .ends of current subcircuit + // go back to normal parsing + //BEGIN(INITIAL); + yy_pop_state(yyscanner); + // return .ends token so that the subcircuit string is recorded + return T_DIRECTIVE_ENDS; + } +} + +<SUBCKT_DEFINITION>. { + /* save characters to buffer as-is */ + *yylval_param->s_ptr += yytext; +} + +<SUBCKT_DEFINITION>{EOL} { + /* save characters to buffer */ + *yylval_param->s_ptr += yytext; + + // cout << "found subckt line: " << yylval_param->s_ptr->substr(delete_me_npos); + // delete_me_npos = yylval_param->s_ptr->size(); +} + +<SUBCKT_DEFINITION><<EOF>> { + cerr << "Reached end of file before end of subcircuit definition" << endl; + exit(1); +} + +\" { + yy_push_state(IN_DQUOTES, yyscanner); + // cout << "entering dquotes mode" << endl; + return '"'; + } + +[+-]?{D}+ { + /* signed integer number */ + yylval_param->i_val = from_string<int>(yytext); + return T_INT; +} + +[+-]?{DOUBLE} { + /* signed floating point number */ + yylval_param->d_val = from_string<double>(yytext); + return T_NUM; +} + +(;[^\n]*) { + /* inline comment starts with ; */ + continue; +} + +^(\*[^\n]*) { + /* full line comment starts with **/ + continue; +} + +^X({ALPHA_PLUS_NUM})+ { + /* subcircuit instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_X; +} + +^WG({ALPHA_PLUS_NUM})+ { + /* waveguide instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_WG; +} + +^MERGER({ALPHA_PLUS_NUM})+ { + /* merger instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_MERGER; +} + +^SPLITTER({ALPHA_PLUS_NUM})+ { + /* merger instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_SPLITTER; +} + +^COUPLER({ALPHA_PLUS_NUM})+ { + /* DC instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_COUPLER; +} + +^PSHIFT({ALPHA_PLUS_NUM})+ { + /* PHASESHIFTER instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_PSHIFT; +} + +^MZI({ALPHA_PLUS_NUM})+ { + /* MZI instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_MZI; +} + +^CROSSING({ALPHA_PLUS_NUM})+ { + /* CROSSING instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_CROSSING; +} + +^CWSRC({ALPHA_PLUS_NUM})+ { + /* Continuous-wave source instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_CWSRC; +} + +^VLSRC({ALPHA_PLUS_NUM})+ { + /* Value-list source instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_VLSRC; +} + +^EVLSRC({ALPHA_PLUS_NUM})+ { + /* Electric Value-list source instance name */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_EVLSRC; +} + +^PROBE({ALPHA_PLUS_NUM})+ { + /* Probe instance */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_PROBE; +} + +^MLPROBE({ALPHA_PLUS_NUM})+ { + /* Probe instance */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_MLPROBE; +} + +^PDET({ALPHA_PLUS_NUM})+ { + /* Probe instance */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_PDET; +} + +^PCMCELL({ALPHA_PLUS_NUM})+ { + /* Probe instance */ + yylval_param->s_ptr = new string(yytext); + return T_ELEM_PCMCELL; +} + +^\.ASSIGN { return T_LOCAL_ASSIGNMENT; } +^\.PARAM { return T_LOCAL_ASSIGNMENT; } +^\.SAVE { return T_DIRECTIVE_SAVE; } + +^\.OP { return T_ANALYSIS_OP; } +^\.DC { return T_ANALYSIS_DC; } +^\.TRAN { return T_ANALYSIS_TRAN; } + +^\.OPTIONS { return T_DIRECTIVE_OPTIONS; } +^\.NODESET { return T_DIRECTIVE_NODESET; } +^\.IC { exit(1); } +^\.SUBCKT { + /* Subcircuit declaration */ + // enter new parser state + yy_push_state(SUBCKT_DECLARATION, yyscanner); + // return token so bison can correctly parse the declaration + return T_DIRECTIVE_SUBCKT; +} + +<INITIAL>^\.ENDS { + cerr << "Unexpeced '.ENDS' directive" << endl; + exit(1); +} + +{ALPHA_PLUS}{ALPHA_PLUS_NUM}*/{WS}*= { + yylval_param->s_ptr = new string(yytext); + + return T_ASSIGN_ID; +} + +{ALPHA_PLUS}{ALPHA_PLUS_NUM}* { + yylval_param->s_ptr = new string(yytext); + + /* + if (yylval_param->s_ptr == "null") + return T_NONE; + if (yylval_param->s_ptr == "true") + { + yylval_param->b_val = true; + return T_BOOL; + } + if (yylval_param->s_ptr == "false") + { + yylval_param->b_val = false; + return T_BOOL; + } + */ + return T_STR; +} + +{OPERATOR} { + yylval_param->c_val = yytext[0]; + return yytext[0]; +} + +{WS} ; + +^.*/{EOL} { + string * str = (string *)yyget_extra(yyscanner); + if (!str) + str = new string; + *str = yytext; + yyset_extra((void *)str, yyscanner); + REJECT; +} + +(?# the following lines are for compatibility with KiCad generated netlists) +(?# they were added fast with relatively no checking for potential problems if) +(?# the keywords are found in subcircuit files or included files) +^\.TITLE({WS}.*)?/{EOL} { + // cout << "Found '.title' directive, ignoring." << endl; +} +^\.END { + // cout << "Found '.end' directive, ignoring." << endl; +} + +(?# Ignore EOL followed by '+' -- line continuation) +{EOL}\+ ; + +{EOL} { return '\n'; } + +<INITIAL,INCLUDE><<EOF>> { + yypop_buffer_state(yyscanner); + + if ( !YY_CURRENT_BUFFER ) + { + yyterminate(); + } + return '\n'; + } + +<SUBCKT_DECLARATION,IN_DQUOTES><<EOF>> { + exit(yylerror(yyscanner, "Unexpected end of file.")); +} + +<*>. { + stringstream msg; + msg << "Unexpected character: \""; + switch (yytext[0]) + { + case '\r': + msg << "\\r"; + break; + case '\n': + msg << "\\n"; + break; + case '\t': + msg << "\\t"; + break; + case '\b': + msg << "\\b"; + break; + default: + msg << yytext[0]; + break; + } + msg << "\""; + exit(yylerror(yyscanner, msg.str().c_str())); + } +%% + +/* END */ + +/* ^.OP(?:{WS}+) { return T_DIRECTIVE_OP; } */ +/* ^.DC(?:{WS}+) { return T_DIRECTIVE_DC; } */ + +/* Quoted string with 2 strings on the same line (returns char []) +\"[^\"\\]*\" { + int n = strlen(yytext) - 2; + yylval_param->sv = new char[n+1]; + strncpy(yylval_param->sv, yytext + 1, n); + yylval_param->sv[n] = '\0'; + return T_QUOTED_STR; +} +*/ + + diff --git a/src/parser/parser.y b/src/parser/parser.y new file mode 100644 index 0000000..36be00f --- /dev/null +++ b/src/parser/parser.y @@ -0,0 +1,961 @@ +%define api.pure full +%param { yyscan_t scanner } + + +%code requires{ + typedef void * yyscan_t; + // char *yyget_text ( yyscan_t yyscanner ); + // int yyget_lineno ( yyscan_t yyscanner ); + // int yylex(YYSTYPE * yylval, yyscan_t scanner); + + //extern int yylineno; + //extern char *yytext; +} + +%{ +#include <iostream> +#include <string> +#include <vector> +#include <cstring> +#include <cmath> +#include <memory> +#include "parse_tree.h" +#include "parse_element.h" +#include "parse_analysis.h" +#include "parse_directive.h" + +#include "../build/parser/parser.tab.h" +#include "../build/parser/parser.yy.h" + +using std::string; +using std::vector; +using std::pow; + +extern ParseTree *cur_pt; + +/*int yyerror(yyscan_t scanner, ParseTree *pt, const char *err) +{ + (void)pt; + (void)p; + printf("Error in the netlist (line %u:\"%s\")\n", yylineno, yytext); + printf("-- Error at line %u:\n\t%s\n", yylineno, err); + return 0; +} +*/ +int yyerror(yyscan_t scanner, ParseTree *pt, const char *err); +%} + +%union { + double d_val; + int i_val; + char c_val; + string *s_ptr; + char *c_ptr; + vector<double> *vec_d_ptr; + pair<string, Variable> *assig_ptr; + Variable *variable_ptr; + vector<Variable> *vec_var_ptr; + pair<string, string> *elemattr_ptr; + pair<string, pair<string, Variable>> *netval_assign_ptr; + pair<pair<string, string>, vector<double>> *cw_sweep_order_ptr; +} + +// %define api.value.type {struct YYSTYPE} + +%token <d_val> T_NUM +%token <i_val> T_INT +%token <i_val> T_NONE +%token <s_ptr> T_STR +%token <s_ptr> T_ASSIGN_ID +%token <s_ptr> T_ELEM_CWSRC T_ELEM_VLSRC T_ELEM_EVLSRC +%token <s_ptr> T_ELEM_WG T_ELEM_COUPLER T_ELEM_MERGER T_ELEM_SPLITTER +%token <s_ptr> T_ELEM_PSHIFT T_ELEM_MZI T_ELEM_CROSSING T_ELEM_PCMCELL +%token <s_ptr> T_ELEM_PROBE T_ELEM_MLPROBE T_ELEM_PDET +%token <s_ptr> T_ELEM_X +%token <i_val> T_ANALYSIS_OP T_ANALYSIS_DC T_ANALYSIS_TRAN +%token <i_val> T_DIRECTIVE_OPTIONS T_DIRECTIVE_NODESET +%token <i_val> T_DIRECTIVE_SUBCKT +%token <s_ptr> T_DIRECTIVE_ENDS +%token <s_ptr> T_DIRECTIVE_SAVE +%token <i_val> T_LOCAL_ASSIGNMENT +%type <c_val> '=' + + +// Available circuit elements +%type <i_val> element.wg +%type <i_val> element.merger +%type <i_val> element.splitter +%type <i_val> element.coupler +%type <i_val> element.pshift +%type <i_val> element.mzi +%type <i_val> element.crossing +%type <i_val> element.pcm_cell +%type <i_val> element.cwsrc +%type <i_val> element.vlsrc +%type <i_val> element.evlsrc +%type <i_val> element.probe +%type <i_val> element.mlprobe +%type <i_val> element.pdet +%type <i_val> element.x + +// Available analysis +%type <i_val> analysis.op +%type <i_val> analysis.dc +%type <i_val> analysis.tran +%type <cw_sweep_order_ptr> cw_sweep_order + +// Available directives +%type <i_val> directive.options +%type <i_val> directive.nodeset + +// Rules for circuit nets (value is index in PT elements) +%type <s_ptr> net_name.base.str +%type <s_ptr> net_name_base +%type <s_ptr> net_name +%type <s_ptr> net.oanalog net.eanalog net.edigital +%type <s_ptr> net.oa.explicit_bidir +%type <s_ptr> net.oa.bidir +%type <s_ptr> net.oa.in +%type <s_ptr> net.oa.out + +//%type <s_ptr> bus_name bus bus.oanalog bus.eanalog bus.edigital + +// Rules related to subcircuits +%type <i_val> subcircuit +%type <i_val> subcircuit.with_ports +%type <i_val> subcircuit.with_assignments +%type <s_ptr> port_name + +// Rules for circuit elements (value is index in PT elements arrays) +%type <i_val> atomelement element +%type <i_val> element.with_args element.with_kwargs +%type <i_val> element.x.with_args element.x.with_kwargs + +// Rules for circuit analysis (value is ptr to ParseAnalysis) +%type <i_val> atomanalysis analysis +%type <i_val> analysis.with_args analysis.with_kwargs + +// Rules for simulator directives (value is ptr to ParseDirective) +%type <i_val> atomdirective directive +%type <i_val> directive.with_args directive.with_kwargs + +// For argument parsing +%type <assig_ptr> assignment +%type <variable_ptr> variable variable_base variable_or_empty +%type <variable_ptr> variable.free_expr variable.delimited_expr +//%type <variable_ptr> variable.str_without_quotes +%type <netval_assign_ptr> netval_assignment +%type <elemattr_ptr> element_attribute + +// For expression parsing +%type <variable_ptr> expr term fact pow constant +%type <variable_ptr> listvar.tail listvar.unbound listvar.bound //TODO: check + +// Add parse tree parameter to yyparse signature +%parse-param {ParseTree *pt} +//%verbose +//%define parse.error detailed +//%define parse.trace + +// On start, set current parse tree to the one passed in call to yyparse +%initial-action { + cur_pt = pt; + if (!pt) + yyerror(scanner, pt, "Invalid parse tree pointer"); +} + + +%% + +input: %empty + | input line '\n' +; + +line: %empty + | element { /* cout << "Inserted element: " << cur_pt->elements[$1]->name << endl; */ } + | analysis { /* cout << "Registered analysis [...]" << endl; */ } + | directive { /* cout << "Registered directive [...]" << endl; */ } + | subcircuit { /* cout << "Parsed subcircuit [...]" << endl; */ } + | local_assignment { /* cout << "Processed local assignment [...]" << endl; */ } +; + +local_assignment: T_LOCAL_ASSIGNMENT assignment + { + cur_pt->set_local_variable($2->first, $2->second); + } + | local_assignment assignment + { + cur_pt->set_local_variable($2->first, $2->second); + } +; + +/* --------- Directives definitions parsing ----------- */ + +directive.options: T_DIRECTIVE_OPTIONS { $$ = cur_pt->register_directive(new OPTIONSDirective); } +; + +directive.nodeset: T_DIRECTIVE_NODESET { $$ = cur_pt->register_directive(new NODESETDirective); } + | directive.nodeset netval_assignment + { + static_cast<NODESETDirective *>(cur_pt->directives[$1])->net_assignments[$2->first].push_back($2->second); + $$ = $1; + } +; +subcircuit.with_ports: T_DIRECTIVE_SUBCKT T_STR + { + // Register a new subcircuit in current parse tree + $$ = cur_pt->register_subcircuit(*$2); + delete $2; + } + | subcircuit.with_ports port_name + { + if (!cur_pt->subcircuits[$1]->kwargs.empty()) + { + cerr << "Expected keyword assignment in subcircuit declaration but got: " << *$2 << endl; + exit(1); + } + // cout << *$2 << endl; + cur_pt->subcircuits[$1]->register_port(*$2); + $$ = $1; + delete $2; + } +; +subcircuit.with_assignments: + subcircuit.with_assignments assignment + { + cur_pt->subcircuits[$1]->kwargs[$2->first] = $2->second; + + $$ = $1; + delete $2; + } +| subcircuit.with_ports +; + +subcircuit: subcircuit.with_assignments T_DIRECTIVE_ENDS + { + // cout << "Parsed subcircuit " << cur_pt->subcircuits[$1]->name << endl; + cur_pt->subcircuits[$1]->netlist = *$2; + delete $2; + } +; + +/* --------- Elements definitions parsing ----------- */ + +element.wg: T_ELEM_WG net.oa.in net.oa.out + { + $$ = cur_pt->register_element(new WGElement(*$1, {*$2, *$3})); + delete $1; + delete $2; + delete $3; + } +; + +element.merger: T_ELEM_MERGER net.oa.in net.oa.in net.oa.out + { + $$ = cur_pt->register_element(new MergerElement(*$1, {*$2, *$3, *$4})); + delete $1; + delete $2; + delete $3; + delete $4; + } +; + +element.splitter: T_ELEM_SPLITTER net.oa.in net.oa.out net.oa.out + { + $$ = cur_pt->register_element(new SplitterElement(*$1, {*$2, *$3, *$4})); + delete $1; + delete $2; + delete $3; + delete $4; + } +; + +element.coupler: T_ELEM_COUPLER net.oa.in net.oa.in net.oa.out net.oa.out + { + $$ = cur_pt->register_element(new DCElement(*$1, {*$2, *$3, *$4, *$5})); + delete $1; + delete $2; + delete $3; + delete $4; + delete $5; + } +; + +element.pshift: T_ELEM_PSHIFT net.oa.in net.oa.out net.eanalog + { + $$ = cur_pt->register_element(new PhaseShifterElement(*$1, {*$2, *$3, *$4})); + delete $1; + delete $2; + delete $3; + delete $4; + } +; + +element.mzi: T_ELEM_MZI net.oa.in net.oa.in net.oa.out net.oa.out net.eanalog + { + $$ = cur_pt->register_element(new MZIElement(*$1, {*$2, *$3, *$4, *$5, *$6})); + delete $1; + delete $2; + delete $3; + delete $4; + delete $5; + delete $6; + } +; + +element.crossing: T_ELEM_CROSSING net.oa.in net.oa.in net.oa.out net.oa.out + { + $$ = cur_pt->register_element(new CrossingElement(*$1, {*$2, *$3, *$4, *$5})); + delete $1; + delete $2; + delete $3; + delete $4; + delete $5; + } +; + +element.pcm_cell: T_ELEM_PCMCELL net.oa.in net.oa.out + { + $$ = cur_pt->register_element(new PCMCellElement(*$1, {*$2, *$3})); + delete $1; + delete $2; + delete $3; + } +; + +element.cwsrc: T_ELEM_CWSRC net.oa.out + { + $$ = cur_pt->register_element(new CWSourceElement(*$1, {*$2})); + delete $1; + delete $2; + } +; + +element.vlsrc: T_ELEM_VLSRC net.oa.out + { + $$ = cur_pt->register_element(new VLSourceElement(*$1, {*$2})); + delete $1; + delete $2; + } +; + +element.evlsrc: T_ELEM_EVLSRC net.eanalog + { + $$ = cur_pt->register_element(new EVLSourceElement(*$1, {*$2})); + delete $1; + delete $2; + } +; + +element.probe: T_ELEM_PROBE net.oa.in + { + $$ = cur_pt->register_element(new ProbeElement(*$1, {*$2})); + delete $1; + delete $2; + } +; + +element.mlprobe: T_ELEM_MLPROBE net.oa.in + { + $$ = cur_pt->register_element(new MLProbeElement(*$1, {*$2})); + delete $1; + delete $2; + } +; + +element.pdet: T_ELEM_PDET net.oa.in net.eanalog + { + $$ = cur_pt->register_element(new PhotodetectorElement(*$1, {*$2, *$3})); + delete $1; + delete $2; + delete $3; + } +; + +element.x: T_ELEM_X + { + $$ = cur_pt->register_element(new XElement(*$1)); + delete $1; + } +; + +/* --------- Analyses definitions parsing ----------- */ + +analysis.op: T_ANALYSIS_OP + { $$ = cur_pt->register_analysis(new OPAnalysis()); } +; + +analysis.dc: T_ANALYSIS_DC + { $$ = cur_pt->register_analysis(new DCAnalysis()); } + | analysis.dc cw_sweep_order + { + auto ana = dynamic_cast<DCAnalysis *>(cur_pt->analyses[$1]); + if (ana->sweep_orders.size()) + { + cerr << "Registering more than one DC sweep is not supported yet." << endl; + exit(1); + } + ana->register_sweep_order(*$2); + delete $2; + } +; + +analysis.tran: T_ANALYSIS_TRAN + { $$ = cur_pt->register_analysis(new TRANAnalysis()); } +; + + +/* ---------- Directives arguments parsing ----------- */ + +atomdirective: directive.options { $$ = $1; } + | directive.nodeset { $$ = $1; } +; + +directive.with_args: + directive.with_args variable + { + // cout << "found positional arg: " << $2->get_str() << endl; + if (!cur_pt->directives[$1]->kwargs.empty()) + yyerror(scanner, pt, "positional arguments cannot follow keyword arguments"); + cur_pt->directives[$1]->args.emplace_back(*$2); + $$ = $1; + delete $2; + } +| atomdirective { $$ = $1; } +; + +directive.with_kwargs: + directive.with_kwargs assignment + { + // cout << "found keyword arg: " << $2->second.get_str() << endl; + cur_pt->directives[$1]->kwargs[$2->first] = $2->second; + $$ = $1; + delete $2; + } +| directive.with_args +; + +directive: directive.with_kwargs +; + +/* --------- Elements arguments parsing ----------- */ + +atomelement: element.wg { $$ = $1; } + | element.merger { $$ = $1; } + | element.splitter { $$ = $1; } + | element.coupler { $$ = $1; } + | element.pshift { $$ = $1; } + | element.mzi { $$ = $1; } + | element.crossing { $$ = $1; } + | element.pcm_cell { $$ = $1; } + | element.cwsrc { $$ = $1; } + | element.vlsrc { $$ = $1; } + | element.evlsrc { $$ = $1; } + | element.probe { $$ = $1; } + | element.mlprobe { $$ = $1; } + | element.pdet { $$ = $1; } +; + +element.with_args: + element.with_args variable + { + // cout << "found positional arg: " << $2->get_str() << endl; + if (!cur_pt->elements[$1]->kwargs.empty()) + yyerror(scanner, pt, "positional arguments cannot follow keyword arguments"); + cur_pt->elements[$1]->args.emplace_back(*$2); + $$ = $1; + + // TODO: for X element only, nets are stored as args + // Xnnnn n1 n2 n3 ... sub_name **sub_kwargs + // because the number of nets is not known when reading n1 n2 n3... + // [n1 n2 n3 ... subname] are also args + // when the time an arg is found, the previous one (if it exists), can be considered a net + // and therefore added to the element's nets array and discarded from args + // this will ensure that args will contain only [sub_name] + // for this to work, the X element cannot have args beyond sub_name (only kwargs) + if (cur_pt->elements[$1]->args.size() == 2) + { + // check if the args[0] is a valid net name + // create net with name args[0] (by default with type NONE) + // add args[0] to nets + // delete args[0] from args + } + + delete $2; + } +| atomelement { $$ = $1; } +; + +element.with_kwargs: + element.with_kwargs assignment + { + // cout << "found keyword arg: " << $2->second.get_str() << endl; + cur_pt->elements[$1]->kwargs[$2->first] = $2->second; + $$ = $1; + delete $2; + } +| element.with_args +; + +element.x.with_args: + element.x.with_args net_name_base + { + // cout << "found positional arg: " << $2->get_str() << endl; + if (!cur_pt->elements[$1]->kwargs.empty()) + yyerror(scanner, pt, "positional arguments cannot follow keyword arguments"); + cur_pt->elements[$1]->args.emplace_back(Variable(*$2)); + $$ = $1; + + // TODO: for X element only, nets are stored as args + // Xnnnn n1 n2 n3 ... sub_name **sub_kwargs + // because the number of nets is not known when reading n1 n2 n3... + // [n1 n2 n3 ... subname] are also args + // when the time an arg is found, the previous one (if it exists), can be considered a net + // and therefore added to the element's nets array and discarded from args + // this will ensure that args will contain only [sub_name] + // for this to work, the X element cannot have args beyond sub_name (only kwargs) + if (cur_pt->elements[$1]->args.size() == 2) + { + // check if the args[0] is a valid net name + // create net with name args[0] (by default with type NONE) + // add args[0] to nets + // delete args[0] from args + } + + delete $2; + } +| element.x { $$ = $1; } +; + +element.x.with_kwargs: + element.x.with_kwargs assignment + { + // cout << "found keyword arg: " << $2->second.get_str() << endl; + cur_pt->elements[$1]->kwargs[$2->first] = $2->second; + $$ = $1; + delete $2; + } +| element.x.with_args +; + +element: + element.with_kwargs +| element.x.with_kwargs +; + +/* ---------- Analysis arguments parsing ----------- */ + +atomanalysis: analysis.op { $$ = $1; } + | analysis.dc { $$ = $1; } + | analysis.tran { $$ = $1; } +; + +analysis.with_args: + analysis.with_args variable + { + // cout << "found positional arg: " << $2->get_str() << endl; + if (!cur_pt->analyses[$1]->kwargs.empty()) + yyerror(scanner, pt, "positional arguments cannot follow keyword arguments"); + cur_pt->analyses[$1]->args.emplace_back(*$2); + $$ = $1; + delete $2; + } +| atomanalysis { $$ = $1; } +; + +analysis.with_kwargs: + analysis.with_kwargs assignment + { + // cout << "found keyword arg: " << $2->second.get_str() << endl; + cur_pt->analyses[$1]->kwargs[$2->first] = $2->second; + $$ = $1; + delete $2; + } +| analysis.with_args +; + +analysis: analysis.with_kwargs + +/* --------- NET type parsing ----------- */ + +net.oanalog: net_name + { + bool ok = cur_pt->nets[*$1].setType(ParseNet::OANALOG); + if (!ok) + { + stringstream ret; + ret << "Error creating net '" + *$1 + "': net already exists with a different type" << endl; + ret << "\t (new type: " << "OANALOG" << ", old type: " << cur_pt->nets[*$1].type_str() << ")" << endl; + yyerror(scanner, pt, ret.str().c_str()); + } + cur_pt->nets[*$1].m_ports_count++; + $$ = $1; + } +; + +net.oa.explicit_bidir: + net.oanalog '&' + { + // this net will be both written to and read from by the new device + // so increment both reader and writer count + // and set the explicit "bidirectional" flag to true + cur_pt->nets[*$1].m_bidirectional = true; + cur_pt->nets[*$1].m_readers_count++; + cur_pt->nets[*$1].m_writers_count++; + $$ = $1; + } +; + +net.oa.bidir: + net.oa.explicit_bidir +| net.oanalog + { + // this net will be both written to and read from by the new device + // so increment both reader and writer count + cur_pt->nets[*$1].m_readers_count++; + cur_pt->nets[*$1].m_writers_count++; + $$ = $1; + } +; + +net.oa.in: + net.oanalog + { + // this net will only be read from by the new device + // so increment only reader count + cur_pt->nets[*$1].m_readers_count++; + $$ = $1; + } +| net.oa.explicit_bidir + { + // bidir already got a writer and reader increment, but + // this net will only be read from by the new device + // so decrement writer count + cur_pt->nets[*$1].m_writers_count--; + $$ = $1; + } +; + +net.oa.out: + net.oanalog + { + // this net will only be written to by the new device + // so increment only writer count + cur_pt->nets[*$1].m_writers_count++; + $$ = $1; + } +| net.oa.explicit_bidir + { + // bidir already got a writer and reader increment, but + // this net will only be written to by the new device + // so decrement reader count + cur_pt->nets[*$1].m_readers_count--; + $$ = $1; + } +; + +net.eanalog: net_name + { + bool ok = cur_pt->nets[*$1].setType(ParseNet::EANALOG); + if (!ok) + { + stringstream ret; + ret << "Error creating net '" + *$1 + "': net already exists with a different type" << endl; + ret << "\t (new type: " << "EANALOG" << ", old type: " << cur_pt->nets[*$1].type_str() << ")" << endl; + yyerror(scanner, pt, ret.str().c_str()); + } + $$ = $1; + } + +net.edigital: net_name + { + bool ok = cur_pt->nets[*$1].setType(ParseNet::EDIGITAL); + if (!ok) + { + stringstream ret; + ret << "Error creating net '" + *$1 + "': net already exists with a different type" << endl; + ret << "\t (new type: " << "EDIGITAL" << ", old type: " << cur_pt->nets[*$1].type_str() << ")"; + yyerror(scanner, pt, ret.str().c_str()); + } + $$ = $1; + } +; + +/* --------- BUS type parsing ----------- */ + +/* +bus.oanalog: bus_name + { + cur_pt->nets[*$1].m_type = ParseNet::OANALOG; + $$ = $1; + } +; + +bus.eanalog: bus_name + { + cur_pt->nets[*$1].m_type = ParseNet::EANALOG; + $$ = $1; + } +; + +bus.edigital: bus_name + { + cur_pt->nets[*$1].m_type = ParseNet::EDIGITAL; + $$ = $1; + } +; +bus: bus.oanalog { $$ = $1; } + | bus.eanalog { $$ = $1; } + | bus.edigital { $$ = $1; } +; +*/ +/* --------- NET and BUS name parsing ----------- */ + +net_name.base.str: + T_STR { $$ = $1; } + | '/' T_STR + { + $$ = $2; + *$2 = "/" + *$2; + } + | '/' T_INT + { + stringstream ss; + ss << "/" << $2; + $$ = new string(ss.str()); + } + | net_name.base.str '-' T_STR + { + $$ = $1; + *$1 += "-" + *$3; + delete $3; + } +; + +net_name_base: T_INT { $$ = new string(ParseNet::name_from_id($1)); } + | net_name.base.str { $$ = $1; } +; + +net_name: net_name_base + { + $$ = new string(cur_pt->get_or_create_net(*$1)); + delete $1; + } +; + +/* +bus_name: net_name { $$ = $1; } + | net_name_base '<' T_INT '>' + { + $$ = new string(cur_pt->get_or_create_net(*$1, $3)); + delete $1; + } +; +*/ +port_name: net_name_base { $$ = $1; } +; + +/* --------- Assignment and variable ----------- */ + +netval_assignment: '/' net_name '/' T_STR '=' variable.delimited_expr + { + $$ = new pair<string, pair<string, Variable>>( + *$2, + pair<string, Variable>(*$4, *$6) + ); + delete $2; + delete $4; + delete $6; + } +; + +cw_sweep_order: element_attribute variable_base variable_base variable_base + { + // cout << $1->first << "->" << $1->second; + // cout << " = [" << $2->as_double(); + // cout << ":" << $4->as_double(); + // cout << ":" << $3->as_double() << "]" << endl; + $$ = new pair<pair<string, string>, vector<double>>(*$1, {$2->as_double(), $3->as_double(), $4->as_double()}); + delete $1; + delete $2; + delete $3; + delete $4; + } +; + +element_attribute: '/' T_STR '/' T_STR + { + // cout << *$2 << "->" << *$4 << endl; + $$ = new pair<string, string>(*$2,*$4); + delete $2; + delete $4; + } +; + +assignment: T_ASSIGN_ID '=' variable + { + $$ = new pair<string, Variable>(*$1, *$3); + delete $1; + delete $3; + } +; + +variable_base: + T_INT + { + $$ = new Variable($1); + } + | T_NUM + { + $$ = new Variable($1); + } + | T_NONE + { + $$ = new Variable(Variable::NONE); + } + | '{' T_STR '}' + { + $$ = new Variable(cur_pt->get_variable(*$2)); + delete $2; + } + | '"' T_STR '"' + { + $$ = new Variable(*$2); + delete $2; + } + | listvar.bound + { + $$ = $1; + } +; + +variable.delimited_expr: + '{' expr '}' + { + $$ = $2; + } +| variable_base; +; + +variable.free_expr: + expr + { + $$ = $1; + } +; + +variable: + variable.free_expr +; + +/* +variable.str_without_quotes: + T_STR + { + if (*$1 == "None") + $$ = new Variable(Variable::NONE); + else + $$ = new Variable(*$1); + delete $1; + } +| variable; +; +*/ + +variable_or_empty: + variable + | %empty + { + $$ = new Variable(Variable::NONE); + } +; + +/* ------------ Expression parsing ------------- */ + +constant: variable_base { $$ = $1; } +; + +expr: + expr '+' term { $$ = $1; *$$ += *$3; delete $3; } +| expr '-' term { $$ = $1; *$$ -= *$3; delete $3; } +| term { $$ = $1; } +; + +term: + term '*' fact { $$ = $1; *$$ *= *$3; delete $3; } +| term '/' fact { $$ = $1; *$$ /= *$3; delete $3; } +| fact { $$ = $1; } +; + +fact: + fact '^' pow { $$ = $1; *$$ ^= *$3; delete $3; } +| pow { $$ = $1; } +; + +pow: + constant +| '(' expr ')' { $$ = $2; } +; + +// --------------- variables list (elements can have different types) +listvar.tail: variable_or_empty + { + $$ = new Variable(Variable::LIST); + $$->vec.insert($$->vec.begin(), *$1); + delete $1; + } +; + +listvar.unbound: variable_or_empty ',' listvar.tail + { + $3->vec.insert($3->vec.begin(), *$1); + $$ = $3; + delete $1; + } + | variable_or_empty ',' listvar.unbound + { + $3->vec.insert($3->vec.begin(), *$1); + $$ = $3; + delete $1; + } +; + +listvar.bound: '[' listvar.tail ']' { $$ = $2; } + | '[' listvar.unbound ']' { $$ = $2; } +; + +%% + +int yyerror(yyscan_t scanner, ParseTree *pt, const char *err) +{ + (void)pt; + (void)scanner; + string token = yyget_text(scanner); + stringstream ss; + for (char c : token) + { + switch (c) + { + case '\r': + ss << "\\r"; + break; + case '\n': + ss << "\\n"; + break; + case '\t': + ss << "\\t"; + break; + case '\b': + ss << "\\b"; + break; + default: + ss << c; + break; + } + } + string token_escaped = ss.str(); + printf("-- Error in the netlist (line %u): %s\n", yyget_lineno(scanner), err); + printf("-- \"%s\"\n", token_escaped.c_str()); + //cerr << err << endl; + exit(1); +} |
