diff options
Diffstat (limited to 'src')
104 files changed, 15822 insertions, 0 deletions
diff --git a/src/devices/alldevices.h b/src/devices/alldevices.h new file mode 100644 index 0000000..daa7ee1 --- /dev/null +++ b/src/devices/alldevices.h @@ -0,0 +1,44 @@ +#pragma once + +/** ******************************************* **/ +/** Elementary passive devices **/ +/** ******************************************* **/ +#include <waveguide.h> +#include <directional_coupler.h> +#include <merger.h> +#include <splitter.h> +#include <crossing.h> +#include <pcm_device.h> + +/** ******************************************* **/ +/** Active devices **/ +/** ******************************************* **/ +#include <detector.h> +#include <phaseshifter.h> +#include <mzi_active.h> +#include <mzi.h> + +/** ******************************************* **/ +/** Sources **/ +/** ******************************************* **/ +//#include <bitstream_source.h> +#include <cw_source.h> +#include <value_list_source.h> +#include <electrical_value_list_source.h> + +/** ******************************************* **/ +/** Utilities **/ +/** ******************************************* **/ +#include <probe.h> +//#include <ring.h> +#include <time_monitor.h> + +/** ******************************************* **/ +/** Circuits **/ +/** ******************************************* **/ +#include <crow.h> +#include <octane_cell.h> +#include <octane_segment.h> +#include <octane_matrix.h> +#include <mesh_col.h> +#include <clements.h> diff --git a/src/devices/bitstream_source.cpp b/src/devices/bitstream_source.cpp new file mode 100644 index 0000000..f2e38a6 --- /dev/null +++ b/src/devices/bitstream_source.cpp @@ -0,0 +1,76 @@ +#include <bitstream_source.h> + +#include <random> +#include <cstdlib> + +using std::cout; +using std::endl; + +unsigned int BitstreamSource::m_bits_per_value = 8; +//double BitstreamSource::m_output_jitter = 0.0; + +void BitstreamSource::runner() +{ + // Initialize random number generator + //mt19937 gen(m_seed); + //uniform_int_distribution<> dis(1, m_max_value); // uniform in [1, max] + //int operation_mode = 0; // 0: modulated source , 1: independent pulses + + auto max_val = pow(2.0, m_bits_per_value) - 1; + + cout << name() << ":" << endl; + cout << "signal on: " << m_signal_on << endl; + cout << "signal off: " << m_signal_off << endl; + cout << "bits: "; + cout << endl; + + for(const auto &val : m_values) { + auto val_saturated = val; + if (m_current_value > max_val) + val_saturated = max_val; + + for (unsigned int iBit = 0; iBit < m_bits_per_value; ++iBit) { + bool bit = val_saturated & (1 << iBit); + cout << bit; + } + } + cout << " --> " << (dynamic_cast<sc_signal<OpticalSignal> *>(p_out.get_interface()))->name(); + cout << endl; + cout << endl; + + // Loop over all values + for (unsigned int iVal = 0; iVal < m_values.size(); ++iVal) { + m_current_value = m_values[iVal]; + + if (m_current_value > max_val) + m_current_value = max_val; + + for (unsigned int iBit = 0; iBit < m_bits_per_value; ++iBit) { + // Wait at least one clk cycle, and for enable to be high + do { + // Wait next clk edge + wait(); + cout << "CLK tick (" << this->name() << ")" << endl; + } while (!p_enable->read()); + + // Deduce bit "value" from binary representation + // iBit = 0 represents LSB + bool bit = m_current_value & (1 << iBit); + + // Initialize corresponding signal + auto s = (bit ? OpticalSignal(m_signal_on) : + OpticalSignal(m_signal_off)); + + s.getNewId(); // New Id is necessary to notify signal change + + // Send signal to output with no delay + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + } + } + + // Wait for end of simulation + while (true) { + // Do nothing + wait(); + } +} diff --git a/src/devices/bitstream_source.h b/src/devices/bitstream_source.h new file mode 100644 index 0000000..a7c8715 --- /dev/null +++ b/src/devices/bitstream_source.h @@ -0,0 +1,105 @@ +#pragma once + +#include <systemc.h> +#include <fstream> + +#include <optical_output_port.h> +#include <optical_signal.h> + +using namespace std; + +class BitstreamSource : public sc_module { +public: + // Set it as typedef for now (we may very well never need to change it) + typedef unsigned long operand_type; + +public: + // Ports + sc_port<sc_signal_in_if<bool>> p_enable; + sc_in<bool> p_clk; + sc_port<sc_signal_out_if<OpticalSignal>> p_out; + + // Timed ports writers + OpticalOutputPort m_out_writer; + + // Member variables + static unsigned int m_bits_per_value; + + vector<operand_type> m_values; + operand_type m_current_value = 0; + + OpticalSignal m_signal_on; + OpticalSignal m_signal_off; + + // Processes + void runner(); + + // + inline void setWavelength(const double &wl) + { + m_signal_on.m_wavelength_id = m_signal_on.getIDFromWavelength(wl); + m_signal_off.m_wavelength_id = m_signal_off.getIDFromWavelength(wl); + } + + // + inline void setPower(const double &on, const double &off = 0.0) + { + m_signal_on.m_field = sqrt(on); + m_signal_off.m_field = sqrt(off); + } + + // Constructor + BitstreamSource(sc_module_name name) + : sc_module(name) + , m_out_writer("out_delayed_writer", p_out) + { + SC_HAS_PROCESS(BitstreamSource); + + SC_THREAD(runner); + sensitive << p_clk.pos() << p_clk.neg(); + } + + BitstreamSource(sc_module_name name, + const vector<operand_type> &values, + const OpticalSignal &signal_on, + const OpticalSignal &signal_off = OpticalSignal(0)) + : sc_module(name) + , m_out_writer("out_delayed_writer", p_out) + , m_values(values) + , m_current_value(values.empty() ? 0 : values[0]) + , m_signal_on(signal_on) + , m_signal_off(signal_off) + { + SC_HAS_PROCESS(BitstreamSource); + + SC_THREAD(runner); + sensitive << p_clk.pos() << p_clk.neg(); + } + + BitstreamSource(sc_module_name name, + const std::string &values_file, + const OpticalSignal &signal_on, + const OpticalSignal &signal_off = OpticalSignal(0)) + : sc_module(name) + , m_out_writer("out_delayed_writer", p_out) + , m_signal_on(signal_on) + , m_signal_off(signal_off) + { + // Read values from file + std::ifstream i(values_file); + + int value; + m_values.clear(); + while ( i.good() ) { + i >> value; + m_values.push_back(value); + } + + m_current_value = (m_values.empty() ? 0 : m_values[0]); + + SC_HAS_PROCESS(BitstreamSource); + + SC_THREAD(runner); + sensitive << p_clk.pos() << p_clk.neg(); + } +}; diff --git a/src/devices/clements.cpp b/src/devices/clements.cpp new file mode 100644 index 0000000..a99bfb3 --- /dev/null +++ b/src/devices/clements.cpp @@ -0,0 +1,137 @@ +#include "clements.h" + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +void Clements::init_ports() +{ + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing optical ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + p_in.clear(); + p_out.clear(); + for(size_t i = 0; i < m_N; i++) + { + p_in.push_back(make_unique<spx::oa_port_in_type>(__modname("IN_", i))); + p_out.push_back(make_unique<spx::oa_port_out_type>(__modname("OUT_", i))); + } + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing electrical input ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + p_vphi.clear(); + p_vtheta.clear(); + + size_t num_of_electrical_ports = (m_N * (m_N - 1) / 2); + for(size_t i = 0; i < num_of_electrical_ports; i++) + { + p_vphi.push_back(make_unique<sc_in<double>>(__modname("VPHI_", i))); + p_vtheta.push_back(make_unique<sc_in<double>>(__modname("VTHETA_", i))); + } +} + +void Clements::init() +{ + m_optical_connect.clear(); + m_mesh_cols.clear(); + + size_t num_cols = (m_N != 2) ? m_N : 1; + size_t num_wires = m_N * (num_cols - 1); + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Instantiating submodules + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + for(size_t j = 0; j < num_cols; j++) + { + m_mesh_cols.push_back(make_unique<MeshCol>(__modname("COL_", j), + m_N, j, m_length_cm, m_attenuation_wg_dB_cm, + m_attenuation_coupler_dB, m_attenuation_ps_dB, + m_neff, m_ng)); + m_mesh_cols[j]->init(); + } + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Instantiating optical wires + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + for(size_t i = 0; i < num_wires; i++) + m_optical_connect.push_back(make_unique<spx::oa_signal_type>(__modname("W_", i))); + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Connecting modules (optical) + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + if (m_N == 2) + { + m_mesh_cols[0]->p_in[0]->bind(*p_in[0]); + m_mesh_cols[0]->p_in[1]->bind(*p_in[1]); + m_mesh_cols[0]->p_out[0]->bind(*p_out[0]); + m_mesh_cols[0]->p_out[1]->bind(*p_out[1]); + } + else + { + // first col has inputs that go to ports, + // and outputs going to the first m_N wires + for(size_t i = 0; i < m_N; i++) + { + m_mesh_cols[0]->p_in[i]->bind(*p_in[i]); + m_mesh_cols[0]->p_out[i]->bind(*m_optical_connect[i]); + } + + size_t in_index; + size_t out_index; + for(size_t j = 1; j < num_cols - 1; j++) + { + in_index = (j - 1) * m_N; + out_index = j * m_N; + for(size_t i = 0; i < m_N; i++) + { + m_mesh_cols[j]->p_in[i]->bind(*m_optical_connect[in_index + i]); + m_mesh_cols[j]->p_out[i]->bind(*m_optical_connect[out_index + i]); + } + } + + // last col has outputs that go to ports, + // and inputs coming from the last m_N wires + in_index = (num_cols - 2) * m_N; + for(size_t i = 0; i < m_N; i++) + { + m_mesh_cols[num_cols - 1]->p_in[i]->bind(*m_optical_connect[in_index + i]); + m_mesh_cols[num_cols - 1]->p_out[i]->bind(*p_out[i]); + } + } + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Connecting modules (electrical) + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + // The index of the electrical port advances top to bottom, and + // then moves to the next column + + size_t ports_in_col; + size_t cur_port = 0; + for(size_t j = 0; j < num_cols; j++) + { + ports_in_col = m_N/2; + if ((m_N % 2 == 0) && !(j % 2 == 0)) // if even-odd, there is one less MZI + ports_in_col = m_N/2 - 1; + + for(size_t i = 0; i < ports_in_col; i++) + { + m_mesh_cols[j]->p_vphi[i]->bind(*p_vphi[cur_port]); + m_mesh_cols[j]->p_vtheta[i]->bind(*p_vtheta[cur_port]); + + cur_port ++; + } + } +}
\ No newline at end of file diff --git a/src/devices/clements.h b/src/devices/clements.h new file mode 100644 index 0000000..78de99f --- /dev/null +++ b/src/devices/clements.h @@ -0,0 +1,66 @@ +#pragma once + +#include <specs.h> +#include <spx_module.h> +#include <mesh_col.h> + +/* Clements MZI Mesh + +The mesh has a total of N(N-1)/2 MZIs, as specified in +Optimal design for universal multiport interferometers by Clements et al. (2016). + +Connection guide (above the X is electrical address of phi/theta): + 0 3 + 0__| __ ______ __| __ ______0 + \/ 2 \/ 5 + 1__/\__ __| __ __/\__ __| __1 + 1 \/ 4 \/ + 2__| __ __/\__ __| __ __/\__2 + \/ \/ + 3__/\__ ______ __/\__ ______3 +*/ + +class Clements : public spx_module{ +public: + vector<unique_ptr<spx::oa_port_in_type>> p_in; + vector<unique_ptr<spx::oa_port_out_type>> p_out; + vector<unique_ptr<sc_in<double>>> p_vphi; + vector<unique_ptr<sc_in<double>>> p_vtheta; + + // Member variables + size_t m_N; // represents the number of inputs of the mesh (rows) + + // Parameters for the internal MZIs + double m_length_cm; + double m_attenuation_wg_dB_cm; + double m_attenuation_coupler_dB; + double m_attenuation_ps_dB; + double m_neff; + double m_ng; + + // Submodules and wires + vector<unique_ptr<spx::oa_signal_type>> m_optical_connect; + vector<unique_ptr<MeshCol>> m_mesh_cols; + + // Initialization functions + void init_ports(); + void init(); + + Clements(sc_module_name name, const size_t &N, + const double &length_cm = 1e-2, + const double &attenuation_wg_dB_cm = 0, const double &attenuation_coupler_dB = 0, + const double &attenuation_ps_dB = 0, + const double &neff = 2.2111, const double &ng = 2.2637) + : spx_module(name) + , m_N(N) + , m_length_cm(length_cm) + , m_attenuation_wg_dB_cm(attenuation_wg_dB_cm) + , m_attenuation_coupler_dB(attenuation_coupler_dB) + , m_attenuation_ps_dB(attenuation_ps_dB) + , m_neff(neff) + , m_ng(ng) + { + assert(N > 1); + init_ports(); + } +};
\ No newline at end of file diff --git a/src/devices/crossing.cpp b/src/devices/crossing.cpp new file mode 100644 index 0000000..f8c8f5e --- /dev/null +++ b/src/devices/crossing.cpp @@ -0,0 +1,129 @@ +#include "specs.h" +#include "crossing.h" + +using namespace std; + +void CrossingUni::on_input_changed() +{ + // If it's NAN, it's because it was not specified and thus the linear should be zero (-inf dB) + const double crosstalk_field_lin = (isnan(m_crosstalk_power_dB)) ? 0 : pow(10.0, m_crosstalk_power_dB / 20); + const double transmission_field_lin = pow(10.0, -m_attenuation_power_dB / 20); // due to attenuations + + // Pre-calculate S-parameters + OpticalSignal::field_type S13, S14, S23, S24; + // the second part relates to power that is crossed over (not transmitted) + S13 = polar(transmission_field_lin, 0.0); // From in1 to out1 + S24 = polar(transmission_field_lin, 0.0); // From in2 to out2 + S14 = polar(crosstalk_field_lin, 0.0); // From in1 to out2 (crosstalk) + S23 = polar(crosstalk_field_lin, 0.0); // From in2 to out1 (crosstalk) + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission_power = " << norm(S13) << " W/W" << endl; + cout << "crosstalk_power = " << norm(S14)<< " W/W" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in1.get_interface()))->name(); + cout << " --- ---> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out1.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in2.get_interface()))->name(); + cout << " --- ---> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out2.get_interface()))->name(); + cout << endl; + + cout << endl; + } + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + auto s1 = p_in1->read(); + auto s2 = p_in2->read(); + + // Apply S-parameters + auto s3 = s1 * S13 + s2 * S23; + auto s4 = s1 * S14 + s2 * S24; + + // Get new IDs for signal + s3.getNewId(); + s4.getNewId(); + + // Write to ouput port after delay + m_out1_writer.delayedWrite(s3, sc_time(0, SC_NS)); + m_out2_writer.delayedWrite(s4, sc_time(0, SC_NS)); + } +} + +void CrossingBi::on_input_changed() +{ + // If it's NAN, it's because it was not specified and thus the linear should be zero (-inf dB) + const double crosstalk_field_lin = (isnan(m_crosstalk_power_dB)) ? 0 : pow(10.0, m_crosstalk_power_dB / 20); + const double transmission_field_lin = pow(10.0, -m_attenuation_power_dB / 20); // due to attenuations + + // Pre-calculate S-parameters + OpticalSignal::field_type through = polar(transmission_field_lin, 0.0); + OpticalSignal::field_type cross = polar(crosstalk_field_lin, 0.0); + + OpticalSignal::field_type S02, S03, S12, S13; + // the second part relates to power that is crossed over (not transmitted) + S02 = through; // From in1 to out1 + S13 = through; // From in2 to out2 + S03 = cross; // From in1 to out2 (crosstalk) + S12 = cross; // From in2 to out1 (crosstalk) + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission_power = " << norm(through) << " W/W" << endl; + cout << "crosstalk_power = " << norm(cross)<< " W/W" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_in.get_interface()))->name(); + cout << " --- ---> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p2_out.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_in.get_interface()))->name(); + cout << " --- ---> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p3_out.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p2_in.get_interface()))->name(); + cout << " --- ---> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_out.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p3_in.get_interface()))->name(); + cout << " --- ---> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_out.get_interface()))->name(); + cout << endl; + + cout << endl; + } + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + auto s0_in = p0_in->read(); + auto s1_in = p1_in->read(); + auto s2_in = p2_in->read(); + auto s3_in = p3_in->read(); + + // Apply S-parameters + auto s0_out = s2_in * S02 + s3_in * S03; + auto s1_out = s2_in * S12 + s3_in * S13; + auto s2_out = s0_in * S02 + s1_in * S12; + auto s3_out = s0_in * S03 + s1_in * S13; + + // Get new IDs for signal + s0_out.getNewId(); + s1_out.getNewId(); + s2_out.getNewId(); + s3_out.getNewId(); + + // Write to ouput port after delay + m_p0_out_writer.delayedWrite(s0_out, sc_time(0, SC_NS)); + m_p1_out_writer.delayedWrite(s1_out, sc_time(0, SC_NS)); + m_p2_out_writer.delayedWrite(s2_out, sc_time(0, SC_NS)); + m_p3_out_writer.delayedWrite(s3_out, sc_time(0, SC_NS)); + } +}
\ No newline at end of file diff --git a/src/devices/crossing.h b/src/devices/crossing.h new file mode 100644 index 0000000..8631ba4 --- /dev/null +++ b/src/devices/crossing.h @@ -0,0 +1,113 @@ +#pragma once + +#include <systemc.h> + +#include <spx_module.h> + +#include <optical_output_port.h> +#include <optical_signal.h> + + +/* A waveguide crossing that accounts for */ +/* loss and interchannel crosstalk */ +/* */ +/* (2)p_in2 */ +/* | */ +/* (1)p_in1----+---->p_out1(3) */ +/* | */ +/* v */ +/* p_out2(4) */ +/* */ +/* ---------------------------------------*/ +class CrossingBase : public spx_module { +public: + // Member variables + double m_attenuation_power_dB; + double m_crosstalk_power_dB; + + // Constructor with crosstalk + // Attenuation relates to out1/in1 when there's nothing in in2 + // Crosstalk relates to out2/in1 when there's nothing in in1 + CrossingBase(sc_module_name name, + double attenuation_power_dB = 0, + double crosstalk_power_dB = NAN) + : spx_module(name) + , m_attenuation_power_dB(attenuation_power_dB) + , m_crosstalk_power_dB(crosstalk_power_dB) + { + } +}; + +class CrossingUni : public CrossingBase { +public: + // Ports + spx::oa_port_in_type p_in1; + spx::oa_port_in_type p_in2; + spx::oa_port_out_type p_out1; + spx::oa_port_out_type p_out2; + + // Timed ports writers + OpticalOutputPort m_out1_writer; + OpticalOutputPort m_out2_writer; + + // Processes + void on_input_changed(); + + // Constructor with crosstalk + // Attenuation relates to out1/in1 when there's nothing in in2 + // Crosstalk relates to out2/in1 when there's nothing in in1 + CrossingUni(sc_module_name name, + double attenuation_power_dB = 0, + double crosstalk_power_dB = NAN) + : CrossingBase(name, attenuation_power_dB, crosstalk_power_dB) + , m_out1_writer("out1_delayed_writer", p_out1) + , m_out2_writer("out2_delayed_writer", p_out2) + { + SC_HAS_PROCESS(CrossingUni); + + SC_THREAD(on_input_changed); + sensitive << p_in1 << p_in2; + } +}; + +typedef CrossingUni Crossing; + +class CrossingBi : public CrossingBase { +public: + // Ports + spx::oa_port_in_type p0_in; + spx::oa_port_in_type p1_in; + spx::oa_port_in_type p2_in; + spx::oa_port_in_type p3_in; + spx::oa_port_out_type p0_out; + spx::oa_port_out_type p1_out; + spx::oa_port_out_type p2_out; + spx::oa_port_out_type p3_out; + + // Timed ports writers + OpticalOutputPort m_p0_out_writer; + OpticalOutputPort m_p1_out_writer; + OpticalOutputPort m_p2_out_writer; + OpticalOutputPort m_p3_out_writer; + + // Processes + void on_input_changed(); + + // Constructor with crosstalk + // Attenuation relates to out1/in1 when there's nothing in in2 + // Crosstalk relates to out2/in1 when there's nothing in in1 + CrossingBi(sc_module_name name, + double attenuation_power_dB = 0, + double crosstalk_power_dB = NAN) + : CrossingBase(name, attenuation_power_dB, crosstalk_power_dB) + , m_p0_out_writer("p0_out_delayed_writer", p0_out) + , m_p1_out_writer("p1_out_delayed_writer", p1_out) + , m_p2_out_writer("p2_out_delayed_writer", p2_out) + , m_p3_out_writer("p3_out_delayed_writer", p3_out) + { + SC_HAS_PROCESS(CrossingBi); + + SC_THREAD(on_input_changed); + sensitive << p0_in << p1_in << p2_in << p3_in; + } +};
\ No newline at end of file diff --git a/src/devices/crow.cpp b/src/devices/crow.cpp new file mode 100644 index 0000000..d411aa2 --- /dev/null +++ b/src/devices/crow.cpp @@ -0,0 +1,97 @@ +#include <crow.h> + +using namespace std; + +void CROW::connect_submodules() +{ + // Physical parameters of the components + // double neff = 2.6391; //2.2; + // double ng = 4.3416; //neff; + // double loss_db_cm = 2; //1; + // double coupling_through = (1 - pow(0.83645, 2.0));//0.85; + // double coupling_through = 1 - pow(0.83645, 2.0);//0.85; + // double coupling_through = 1 - pow(0.83645, 2.0);//0.85; + // coupling_through = 1 - 0.83645;//0.85; + //double pi_length = 1.55e-6/2/neff; + //double internal_length = 200*pi_length; + //double internal_length = 1001*pi_length; + // double internal_length = 2.0*((2.0 * M_PI * 3.2544e-6) + 10e-6); // 2pi*R+10um (R = 3.2544um) + // double internal_length = 2.0 * M_PI * (3.2544e-6 + 10e-6/(2.0*M_PI)); // 2pi*R+10um (R = 3.2544um) + //double var_factor = 1.01*pi_length; + // double var_factor = 0; + + // -- Parameterizing components -- // + for (size_t i = 0; i < N; i++) + { + // here is where variation is disabled (0 and uncomment) + // double _wg_length_variation = var_factor*i; + double _wg_length = m_ring_length / 2.0; + // double _wg_length = M_PI * 3.2544e-6 + 10e-6; + //double _wg_length = 200*100.0*(1550e-9 + 0.1e-9 * i)/(2.0*neff); + + //cout << _wg_length_variation << endl; + cout << wg_top.at(i)->name() << endl; + wg_top.at(i)->m_length_cm = _wg_length * 100.0; + wg_top.at(i)->m_neff = m_neff; + wg_top.at(i)->m_ng = m_ng; + wg_top.at(i)->m_attenuation_dB_cm = m_loss_db_cm; + + cout << wg_bot.at(i)->name() << endl; + wg_bot.at(i)->m_length_cm = _wg_length * 100.0; + wg_bot.at(i)->m_neff = m_neff; + wg_bot.at(i)->m_ng = m_ng; + wg_bot.at(i)->m_attenuation_dB_cm = m_loss_db_cm; + + cout << dc.at(i)->name() << endl; + dc.at(i)->m_dc_through_coupling_power = m_coupling_through; + } + dc.at(N)->m_dc_through_coupling_power = m_coupling_through; + + // -- Connecting components -- // + // Before loop, outer connections on left + dc.at(0)->p_in1(p_in); + dc.at(0)->p_out1(p_out_t); + //p_through.p_in(OUT_THROUGH); + + // Loop, inner connections + for (size_t i = 0; i < N; i++) + { + // Need to take care if odd or even, + // as inputs come from different directions + if (i%2 == 0) + { + // If i even, DC(i) input at bottom + dc.at(i)->p_in2(*S_BL.at(i)); + dc.at(i)->p_out2(*S_TL.at(i)); + // If i even, DC(i+1) input at the top + dc.at(i+1)->p_in1(*S_TR.at(i)); + dc.at(i+1)->p_out1(*S_BR.at(i)); + // If even, top waveguide(i) is L->R + wg_top.at(i)->p_in(*S_TL.at(i)); + wg_top.at(i)->p_out(*S_TR.at(i)); + // If even, bottom waveguide(i) is R->L + wg_bot.at(i)->p_in(*S_BR.at(i)); + wg_bot.at(i)->p_out(*S_BL.at(i)); + } + else{ + // If i odd, DC(i) input at top + dc.at(i)->p_in2(*S_TL.at(i)); + dc.at(i)->p_out2(*S_BL.at(i)); + // If i odd, DC(i+1) input at the bottom + dc.at(i+1)->p_in1(*S_BR.at(i)); + dc.at(i+1)->p_out1(*S_TR.at(i)); + // If odd, top waveguide(i) is R->L + wg_top.at(i)->p_in(*S_TR.at(i)); + wg_top.at(i)->p_out(*S_TL.at(i)); + // If odd, bottom waveguide(i) is L->R + wg_bot.at(i)->p_in(*S_BL.at(i)); + wg_bot.at(i)->p_out(*S_BR.at(i)); + } + } + // After loop, outer connections on right + //pd1.p_readout(PD_OUT); + dc.at(N)->p_in2(p_add); + dc.at(N)->p_out2(p_out_d); + + dc.at(0)->m_out1_writer.m_converger = false; +}
\ No newline at end of file diff --git a/src/devices/crow.h b/src/devices/crow.h new file mode 100644 index 0000000..d0e6ed2 --- /dev/null +++ b/src/devices/crow.h @@ -0,0 +1,98 @@ +#pragma once + +#include <systemc.h> +#include <waveguide.h> +#include <directional_coupler.h> +#include <detector.h> +#include <optical_output_port.h> +#include <optical_signal.h> +#include <pcm_device.h> +#include <directional_coupler.h> +#include <merger.h> + +#include <cassert> + +class CROW : public sc_module { +public: + // Ports + /** The optical input ports. */ + sc_port<sc_signal_in_if<OpticalSignal>> p_in; + sc_port<sc_signal_in_if<OpticalSignal>> p_add; + /** The electrical output port. */ + sc_port<sc_signal_out_if<OpticalSignal>> p_out_t; + sc_port<sc_signal_out_if<OpticalSignal>> p_out_d; + + // Member variables + size_t N; + double m_ring_length; + double m_neff = 2.2; + double m_ng = 4.3; + double m_loss_db_cm = 2; + double m_coupling_through = 0.85; + + // Wires + vector<shared_ptr<sc_signal<OpticalSignal>>> S_TL; + vector<shared_ptr<sc_signal<OpticalSignal>>> S_BL; + vector<shared_ptr<sc_signal<OpticalSignal>>> S_TR; + vector<shared_ptr<sc_signal<OpticalSignal>>> S_BR; + //sc_signal<OpticalSignal> terminator; + + // Member submodules + vector<shared_ptr<Waveguide>> wg_top; + vector<shared_ptr<Waveguide>> wg_bot; + vector<shared_ptr<DirectionalCoupler>> dc; + + + virtual void init() + { + assert(N > 0); + S_TL.clear(); + S_BL.clear(); + S_TR.clear(); + S_BR.clear(); + wg_bot.clear(); + wg_top.clear(); + dc.clear(); + stringstream ss; + string _i; + for (size_t i = 0; i < N; ++i) + { + ss.str(std::string()); + ss << i; + _i = ss.str(); + cout << _i << endl; + S_TL.push_back(make_shared<sc_signal<OpticalSignal>>((string("S_TL") + "_" + _i).c_str())); + S_BL.push_back(make_shared<sc_signal<OpticalSignal>>((string("S_BL") + "_" + _i).c_str())); + S_TR.push_back(make_shared<sc_signal<OpticalSignal>>((string("S_TR") + "_" + _i).c_str())); + S_BR.push_back(make_shared<sc_signal<OpticalSignal>>((string("S_BR") + "_" + _i).c_str())); + wg_bot.push_back(make_shared<Waveguide>((string("wg_bot") + "_" + _i).c_str())); + wg_top.push_back(make_shared<Waveguide>((string("wg_top") + "_" + _i).c_str())); + dc.push_back(make_shared<DirectionalCoupler>((string("dc") + "_" + _i).c_str())); + } + ss.str(std::string()); + ss << N; + _i = ss.str(); + cout << _i << endl; + dc.push_back(make_shared<DirectionalCoupler>((string("dc") + "_" + _i).c_str())); + connect_submodules(); + } + + void connect_submodules(); + + /** Constructor for Waveguide + * + * @param name name of the module + * */ + CROW(sc_module_name name, const size_t &nrings = 3, const double &ring_length = 0.0) + : sc_module(name) + , N(nrings) + , m_ring_length(ring_length) + { + } + + void setRingLength(const double &ring_length) + { + assert(ring_length >= 0); + m_ring_length = ring_length; + } +}; diff --git a/src/devices/cw_source.cpp b/src/devices/cw_source.cpp new file mode 100644 index 0000000..4be5934 --- /dev/null +++ b/src/devices/cw_source.cpp @@ -0,0 +1,45 @@ +#include "specs.h" +#include <cw_source.h> + +using std::cout; +using std::endl; + +void CWSource::runner() +{ + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "signal on: " << m_signal_on << endl; + cout << "--> " << + (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name() << endl; + cout << endl; + } + + bool first_run = true; + + while (true) { + // Wait for enable signal + if (! enable.read().to_bool()) + { + // cout << name() << " waiting for enable" << endl; + wait(enable.posedge_event()); + } + // cout << name() << " was enabled" << endl; + auto s = m_signal_on; + s.getNewId(); + + // Write value to output + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + cout << name() << " emitted: " << s << endl; + + // Wait for reset + if ( !first_run || !reset.read().to_bool()) + { + // cout << name() << " waiting for reset" << endl; + wait(reset.posedge_event()); + reset.write(sc_logic(0)); + wait(reset.negedge_event()); + } + // cout << name() << " was reset" << endl; + } +} diff --git a/src/devices/cw_source.h b/src/devices/cw_source.h new file mode 100644 index 0000000..487177c --- /dev/null +++ b/src/devices/cw_source.h @@ -0,0 +1,76 @@ +#pragma once + +#include <systemc.h> +#include <fstream> + +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +class CWSource : public spx_module { +public: + // Ports + spx::oa_port_out_type p_out; + + // Timed ports writers + OpticalOutputPort m_out_writer; + + // Signal characteristic + spx::oa_value_type m_signal_on; + double m_source_wavelength; + + // Source emission control + spx::ed_signal_type enable; + spx::ed_signal_type reset; + + // Processes + void runner(); + + inline void setWavelength(const double &wl) + { + m_source_wavelength = wl; + m_signal_on.m_wavelength_id = m_signal_on.getIDFromWavelength(wl); + } + inline void setFrequency(const double &f) + { + setWavelength(299792458.0 / f); + } + inline void setAmplitudePhase (const double &litude, const double &phase) + { + m_signal_on.m_field = polar(amplitude, phase); + } + inline void setPhase (const double &phase) + { + if (m_signal_on.modulus() == 0) + cerr << "Warning: setting phase has no effect on 0" << endl; + m_signal_on.m_field = polar(m_signal_on.modulus(), phase); + } + inline void setPower (const double &power) + { + m_signal_on.m_field = polar(sqrt(power), m_signal_on.phase()); + } + inline void setPower(const double &power, double phase) + { + setAmplitudePhase(sqrt(power), phase); + } + + // Constructor + CWSource(sc_module_name name) + : spx_module(name) + , m_out_writer("out_delayed_writer", p_out) + { + SC_HAS_PROCESS(CWSource); + + SC_THREAD(runner); + + enable = sc_logic(0); + reset = sc_logic(0); + } + + CWSource(sc_module_name name, const OpticalSignal& signal_on) + : CWSource(name) + { + m_signal_on = signal_on; + } +}; diff --git a/src/devices/detector.cpp b/src/devices/detector.cpp new file mode 100644 index 0000000..b93a5de --- /dev/null +++ b/src/devices/detector.cpp @@ -0,0 +1,136 @@ +#include "specs.h" +#include <detector.h> +#include <cstdlib> // system() +#include <random> +#include <complex> + +using namespace std; + +void Detector::on_port_in_changed() +{ + // always initialize memory + m_memory_in[0] = 0; + + // rng seed + init(); + + while (true) { + // Wait for a new input signal + wait(); + + const auto &p_in_read = p_in->read(); + + auto cur_wavelength_id = p_in_read.m_wavelength_id; + // Updating the field memory + m_memory_in[cur_wavelength_id] = p_in_read.m_field; + m_event_manual_trigger.notify(); + } +} + +void Detector::on_time_tick() +{ + sc_time sampling_time = sc_time(m_sampling_time, SC_SEC); + if (sampling_time.value() == 0) + sampling_time = sc_time::from_value(1); + + OpticalSignal::field_type total_field; + OpticalSignal::field_type::value_type total_power; + double photocurrent; + + // Wait for enable signal + if (! enable.read().to_bool()) + { + // cout << name() << " waiting for enable" << endl; + wait(enable.posedge_event()); + cout << name() << " was enabled" << endl; + } + + while(true) + { + if (sc_pending_activity()) { + wait(sampling_time, m_event_manual_trigger); + //wait(m_event_manual_trigger); // if only time-averaged signal is of interest + } else { + /* wait for reset */ + //TODO (see CWSource code) + wait(); // effectively wait until the end of the simulation + } + + /* Get current time tk*/ + double tk = sc_time_stamp().to_seconds(); + + /* Calculate current output value based on present fields */ + /* Calculate sum of fields at current time*/ + total_field = 0; + total_power = 0; + for (auto field : m_memory_in) + { + double wl = specsGlobalConfig.wavelengths_vector[field.first]; + double freq = 299792458 / wl; + total_field += field.second * exp(complex<double>(0, 2 * M_PI * freq * tk)); + total_power += norm(field.second); + } + + if (norm(total_field) == 0) + total_field = 1e-20*m_rngDist(m_rngGen); + + photocurrent = norm(total_field) * m_responsivity_A_W; + m_cur_readout = photocurrent + (!m_noiseBypass)*noise_gen(photocurrent) + m_darkCurrent_A; + m_cur_readout_no_interf = total_power * m_responsivity_A_W + (!m_noiseBypass)*noise_gen(photocurrent) + m_darkCurrent_A; + + + //m_cur_readout = total_field.real(); + // Write to output port + //p_readout->write(m_cur_readout); + } +} + +/* +Generates a current noise to be applied to the noiseless_readout, +calculated from the responsivity. + +Considers: TIA input referred, shot, and thermal +*/ +double Detector::noise_gen(const double &noiseless_readout) +{ + // elementary charge + const double q = 1.60217e-19; + + // Boltzmann constant + const double K = 1.38064e-23; + + double inoise_tia_2 = m_opFreq_Hz*pow(m_iTIA,2); + double inoise_shot_2 = m_opFreq_Hz*2*q*(noiseless_readout+m_darkCurrent_A); + double inoise_therm_2 = m_opFreq_Hz*4*K*m_temp_K/m_equivR_Ohm; + + // the RMS value is also the variance of the random variable + double inoise_rms = inoise_tia_2 + inoise_shot_2 + inoise_therm_2; + + // mean = noiseless, std = sqrt(variance) + // the second element of the product is the gaussian(0,1) + return sqrt(inoise_rms) * m_rngDist(m_rngGen); +} + +/* +To be implemented in the future, user can specify the wavelength response +of the photodiode +*/ +double Detector::wavelength_dependent_responsivity(const double &wavelength) +{ + (void)wavelength; + if(true) + { + return m_responsivity_A_W; + } + else + { + // custom responsivity curve + return 0; // should return the new responsivity + } +} + +void Detector::init() +{ + std::random_device rd; // Will be used to obtain a seed for the random number engine + m_rngGen.seed(rd()); +} diff --git a/src/devices/detector.h b/src/devices/detector.h new file mode 100644 index 0000000..d2e5e65 --- /dev/null +++ b/src/devices/detector.h @@ -0,0 +1,89 @@ +#pragma once + +#include <systemc.h> +#include <fstream> +#include <random> + +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +// TODO: rename to photodetector +class Detector : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in; + spx::ea_port_out_type p_readout; // TODO: divide into cathode and anode + double m_cur_readout; + double m_cur_readout_no_interf; + + // Input memory for multi-wavelength purposes + std::map<uint32_t,OpticalSignal::field_type> m_memory_in; + + // Detector enable signal + spx::ed_signal_type enable; + + // Member variables + double m_responsivity_A_W; + double m_darkCurrent_A; + double m_opFreq_Hz; + double m_temp_K; + double m_equivR_Ohm; + double m_iTIA; // A/sqrt(Hz) + bool m_noiseBypass; + double m_sampling_time; + + // if each photodiode has an independent RNG device + std::default_random_engine m_rngGen; + std::normal_distribution<double> m_rngDist; + + sc_event m_event_manual_trigger; + + // Init all parameters + void init(); + + // Function that generates the noise applied to the output of this module + double noise_gen(const double &noiseless_readout); + double wavelength_dependent_responsivity(const double &wavelength); + + // Processes + void on_port_in_changed(); + void on_time_tick(); + + virtual void trace(sc_trace_file *Tf) const + { + sc_trace(Tf, m_cur_readout, (string(name()) + ".readout").c_str()); + sc_trace(Tf, m_cur_readout_no_interf, (string(name()) + ".readout_no_interference").c_str()); + } + + // Constructor + Detector(sc_module_name name, + double responsivity_A_W = 1, + double darkCurrent_A = 0, // 100e-12 is an OK value + bool noiseBypass = true, + double opFreq_Hz = 1e9, + double temp_K = 300, + double equivR_Ohm = 400, + double iTIA = 10e-12, + double sampling_time = 1e-12) + : spx_module(name) + , m_responsivity_A_W(responsivity_A_W) + , m_darkCurrent_A(darkCurrent_A) + , m_opFreq_Hz(opFreq_Hz) + , m_temp_K(temp_K) + , m_equivR_Ohm(equivR_Ohm) + , m_iTIA(iTIA) + , m_noiseBypass(noiseBypass) + , m_sampling_time(sampling_time) + , m_rngDist(0,1) + { + SC_HAS_PROCESS(Detector); + enable = sc_logic(0); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + + SC_THREAD(on_time_tick); + } +}; diff --git a/src/devices/directional_coupler.cpp b/src/devices/directional_coupler.cpp new file mode 100644 index 0000000..da0872b --- /dev/null +++ b/src/devices/directional_coupler.cpp @@ -0,0 +1,318 @@ +#include "specs.h" +#include <directional_coupler.h> + +using namespace std; + +void DirectionalCouplerUni::on_port_in1_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in1[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S13, S14, S23, S24; + S13 = polar(transmission_through, m_through_phase_rad); + S24 = polar(transmission_through, m_through_phase_rad); + S14 = polar(transmission_cross, m_cross_phase_rad); + S23 = polar(transmission_cross, m_cross_phase_rad); + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "through_power = " << norm(S13) << " W/W" << endl; + cout << "cross_power = " << norm(S14)<< " W/W" << endl; + cout << "through_field = " << abs(S13) << "" << endl; + cout << "cross_field = " << abs(S14)<< "" << endl; + cout << "insertion loss = " << m_dc_loss << "dB" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in1.get_interface()))->name(); + cout << " --,__,-> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out1.get_interface()))->name(); + cout << endl; + + cout << (dynamic_cast<spx::oa_signal_type *>(p_in2.get_interface()))->name(); + cout << " --' '-> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out2.get_interface()))->name(); + cout << endl; + + cout << endl; + } + + OpticalSignal p_in1_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p_in1_read = p_in1->read(); + + auto cur_wavelength_id = p_in1_read.m_wavelength_id; + // Updating the field memory + m_memory_in1[cur_wavelength_id] = p_in1_read.m_field; + + auto s3 = OpticalSignal(m_memory_in1[cur_wavelength_id] * S13 + + m_memory_in2[cur_wavelength_id] * S23 + , cur_wavelength_id); + + auto s4 = OpticalSignal(m_memory_in1[cur_wavelength_id] * S14 + + m_memory_in2[cur_wavelength_id] * S24 + , cur_wavelength_id); + + m_out1_writer.delayedWrite(s3, sc_time(m_delay_ns, SC_NS)); + m_out2_writer.delayedWrite(s4, sc_time(m_delay_ns, SC_NS)); + } +} + +void DirectionalCouplerUni::on_port_in2_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in2[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S13, S14, S23, S24; + S13 = polar(transmission_through, m_through_phase_rad); + S24 = polar(transmission_through, m_through_phase_rad); + S14 = polar(transmission_cross, m_cross_phase_rad); + S23 = polar(transmission_cross, m_cross_phase_rad); + + OpticalSignal p_in2_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p_in2_read = p_in2->read(); + + auto cur_wavelength_id = p_in2_read.m_wavelength_id; + // Updating the field memory + m_memory_in2[cur_wavelength_id] = p_in2_read.m_field; + + auto s3 = OpticalSignal(m_memory_in1[cur_wavelength_id] * S13 + + m_memory_in2[cur_wavelength_id] * S23 + , cur_wavelength_id); + + auto s4 = OpticalSignal(m_memory_in1[cur_wavelength_id] * S14 + + m_memory_in2[cur_wavelength_id] * S24 + , cur_wavelength_id); + + m_out1_writer.delayedWrite(s3, sc_time(m_delay_ns, SC_NS)); + m_out2_writer.delayedWrite(s4, sc_time(m_delay_ns, SC_NS)); + } +} + + +void DirectionalCouplerBi::on_p0_in_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in0[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S02, S13, S03, S12; + S02 = polar(transmission_through, m_through_phase_rad); + S13 = polar(transmission_through, m_through_phase_rad); + S03 = polar(transmission_cross, m_cross_phase_rad); + S12 = polar(transmission_cross, m_cross_phase_rad); + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "through_power = " << norm(S02) << " W/W" << endl; + cout << "cross_power = " << norm(S03)<< " W/W" << endl; + cout << "through_field = " << abs(S02) << "" << endl; + cout << "cross_field = " << abs(S03)<< "" << endl; + cout << "insertion loss = " << m_dc_loss << "dB" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_in.get_interface()))->name(); + + cout << " --,__,-> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p2_out.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_in.get_interface()))->name(); + cout << " --' '-> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p3_out.get_interface()))->name(); + cout << endl; + + cout << (dynamic_cast<spx::oa_signal_type *>(p0_out.get_interface()))->name(); + cout << " <-,__,-- "; + cout << (dynamic_cast<spx::oa_signal_type *>(p2_in.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_out.get_interface()))->name(); + cout << " <-' '-- "; + cout << (dynamic_cast<spx::oa_signal_type *>(p3_in.get_interface()))->name(); + cout << endl; + + cout << endl; + } + + OpticalSignal p0_in_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p0_in_read = p0_in->read(); + + auto cur_wavelength_id = p0_in_read.m_wavelength_id; + + // Updating the field memory + m_memory_in0[cur_wavelength_id] = p0_in_read.m_field; + + auto s2 = OpticalSignal(m_memory_in0[cur_wavelength_id] * S02 + + m_memory_in1[cur_wavelength_id] * S12 + , cur_wavelength_id); + + auto s3 = OpticalSignal(m_memory_in0[cur_wavelength_id] * S03 + + m_memory_in1[cur_wavelength_id] * S13 + , cur_wavelength_id); + + m_p2_out_writer.delayedWrite(s2, sc_time(m_delay_ns, SC_NS)); + m_p3_out_writer.delayedWrite(s3, sc_time(m_delay_ns, SC_NS)); + } +} + +void DirectionalCouplerBi::on_p1_in_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in1[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S02, S13, S03, S12; + S02 = polar(transmission_through, m_through_phase_rad); + S13 = polar(transmission_through, m_through_phase_rad); + S03 = polar(transmission_cross, m_cross_phase_rad); + S12 = polar(transmission_cross, m_cross_phase_rad); + + OpticalSignal p1_in_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p1_in_read = p1_in->read(); + + auto cur_wavelength_id = p1_in_read.m_wavelength_id; + + // Updating the field memory + m_memory_in1[cur_wavelength_id] = p1_in_read.m_field; + + auto s2 = OpticalSignal(m_memory_in0[cur_wavelength_id] * S02 + + m_memory_in1[cur_wavelength_id] * S12 + , cur_wavelength_id); + + auto s3 = OpticalSignal(m_memory_in0[cur_wavelength_id] * S03 + + m_memory_in1[cur_wavelength_id] * S13 + , cur_wavelength_id); + + m_p2_out_writer.delayedWrite(s2, sc_time(m_delay_ns, SC_NS)); + m_p3_out_writer.delayedWrite(s3, sc_time(m_delay_ns, SC_NS)); + } +} + +void DirectionalCouplerBi::on_p2_in_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in2[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S02, S13, S03, S12; + S02 = polar(transmission_through, m_through_phase_rad); + S13 = polar(transmission_through, m_through_phase_rad); + S03 = polar(transmission_cross, m_cross_phase_rad); + S12 = polar(transmission_cross, m_cross_phase_rad); + + OpticalSignal p2_in_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p2_in_read = p2_in->read(); + + auto cur_wavelength_id = p2_in_read.m_wavelength_id; + + // Updating the field memory + m_memory_in2[cur_wavelength_id] = p2_in_read.m_field; + + auto s0 = OpticalSignal(m_memory_in2[cur_wavelength_id] * S02 + + m_memory_in3[cur_wavelength_id] * S03 + , cur_wavelength_id); + + auto s1 = OpticalSignal(m_memory_in2[cur_wavelength_id] * S12 + + m_memory_in3[cur_wavelength_id] * S13 + , cur_wavelength_id); + + m_p0_out_writer.delayedWrite(s0, sc_time(m_delay_ns, SC_NS)); + m_p1_out_writer.delayedWrite(s1, sc_time(m_delay_ns, SC_NS)); + } +} + +void DirectionalCouplerBi::on_p3_in_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in3[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S02, S13, S03, S12; + S02 = polar(transmission_through, m_through_phase_rad); + S13 = polar(transmission_through, m_through_phase_rad); + S03 = polar(transmission_cross, m_cross_phase_rad); + S12 = polar(transmission_cross, m_cross_phase_rad); + + OpticalSignal p3_in_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p3_in_read = p3_in->read(); + + auto cur_wavelength_id = p3_in_read.m_wavelength_id; + + // Updating the field memory + m_memory_in3[cur_wavelength_id] = p3_in_read.m_field; + + auto s0 = OpticalSignal(m_memory_in2[cur_wavelength_id] * S02 + + m_memory_in3[cur_wavelength_id] * S03 + , cur_wavelength_id); + + auto s1 = OpticalSignal(m_memory_in2[cur_wavelength_id] * S12 + + m_memory_in3[cur_wavelength_id] * S13 + , cur_wavelength_id); + + m_p0_out_writer.delayedWrite(s0, sc_time(m_delay_ns, SC_NS)); + m_p1_out_writer.delayedWrite(s1, sc_time(m_delay_ns, SC_NS)); + } +}
\ No newline at end of file diff --git a/src/devices/directional_coupler.h b/src/devices/directional_coupler.h new file mode 100644 index 0000000..1e907a6 --- /dev/null +++ b/src/devices/directional_coupler.h @@ -0,0 +1,133 @@ +#pragma once + +#include <systemc.h> +#include <map> +#include <optical_output_port.h> +#include <optical_signal.h> +#include "spx_module.h" + + +class DirectionalCouplerBase : public spx_module { +public: + // Member variables + double m_delay_ns = 0; + double m_through_phase_rad = 0; + double m_cross_phase_rad = M_PI/2; + double m_dc_through_coupling_power; + + double m_through_power_dB; + double m_cross_power_dB; + double m_dc_loss; // is in dB + + // Constructor + DirectionalCouplerBase(sc_module_name name, + double dc_through_coupling_power = 0.5, + double dc_loss = 0) + : spx_module(name) + , m_dc_through_coupling_power(dc_through_coupling_power) + , m_dc_loss(dc_loss) + { + // nothing to do + } +}; + +class DirectionalCouplerUni : public DirectionalCouplerBase { +public: + // Ports + spx::oa_port_in_type p_in1; + spx::oa_port_in_type p_in2; + spx::oa_port_out_type p_out1; + spx::oa_port_out_type p_out2; + + // Timed ports writers + OpticalOutputPort m_out1_writer; + OpticalOutputPort m_out2_writer; + + // Memory for multi-wavelength purposes + // maybe with vector it has better performance + std::map<uint32_t,OpticalSignal::field_type> m_memory_in1; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in2; + + // Processes + void on_port_in1_changed(); + void on_port_in2_changed(); + + // Constructor + DirectionalCouplerUni(sc_module_name name, + double dc_through_coupling_power = 0.5, + double dc_loss = 0) + : DirectionalCouplerBase(name, dc_through_coupling_power, dc_loss) + , m_out1_writer("out1_delayed_writer", p_out1) + , m_out2_writer("out2_delayed_writer", p_out2) + { + SC_HAS_PROCESS(DirectionalCouplerUni); + + SC_THREAD(on_port_in1_changed); + sensitive << p_in1; + + SC_THREAD(on_port_in2_changed); + sensitive << p_in2; + } +}; + +typedef DirectionalCouplerUni DirectionalCoupler; + +class DirectionalCouplerBi : public DirectionalCouplerBase { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p0_in; + spx::oa_port_in_type p1_in; + spx::oa_port_in_type p2_in; + spx::oa_port_in_type p3_in; + + /** The optical output ports. */ + spx::oa_port_out_type p0_out; + spx::oa_port_out_type p1_out; + spx::oa_port_out_type p2_out; + spx::oa_port_out_type p3_out; + + // Timed ports writers + OpticalOutputPort m_p0_out_writer; + OpticalOutputPort m_p1_out_writer; + OpticalOutputPort m_p2_out_writer; + OpticalOutputPort m_p3_out_writer; + + // Memory for multi-wavelength purposes + // maybe with vector it has better performance + std::map<uint32_t,OpticalSignal::field_type> m_memory_in0; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in1; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in2; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in3; + + // Processes + void on_p0_in_changed(); + void on_p1_in_changed(); + void on_p2_in_changed(); + void on_p3_in_changed(); + + // Constructor + DirectionalCouplerBi(sc_module_name name, + double dc_through_coupling_power = 0.5, + double dc_loss = 0) + : DirectionalCouplerBase(name, dc_through_coupling_power, dc_loss) + , m_p0_out_writer("p0_out_delayed_writer", p0_out) + , m_p1_out_writer("p1_out_delayed_writer", p1_out) + , m_p2_out_writer("p2_out_delayed_writer", p2_out) + , m_p3_out_writer("p3_out_delayed_writer", p3_out) + { + SC_HAS_PROCESS(DirectionalCouplerBi); + + SC_THREAD(on_p0_in_changed); + sensitive << p0_in; + + SC_THREAD(on_p1_in_changed); + sensitive << p1_in; + + SC_THREAD(on_p2_in_changed); + sensitive << p2_in; + + SC_THREAD(on_p3_in_changed); + sensitive << p3_in; + } +}; diff --git a/src/devices/electrical_value_list_source.cpp b/src/devices/electrical_value_list_source.cpp new file mode 100644 index 0000000..02b6af5 --- /dev/null +++ b/src/devices/electrical_value_list_source.cpp @@ -0,0 +1,67 @@ +#include "specs.h" +#include "electrical_value_list_source.h" + +using std::cout; +using std::cerr; +using std::endl; + +void EVLSource::runner() +{ + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "value list (" << m_values_queue.size() << " values)"; + if (false) + { + cout << ":" << endl; + for (const auto& val : m_values_queue) + cout << "\t" << "@" << sc_time(val.first, SC_SEC) << ": " << val.second << endl; + } + cout << endl; + cout << "--> " << + (dynamic_cast<spx::ea_signal_type *>(p_out.get_interface()))->name() << endl; + cout << endl; + } + + // Wait for enable signal + if (! enable.read().to_bool()) + { + // cout << name() << " waiting for enable" << endl; + wait(enable.posedge_event()); + cout << name() << " was enabled" << endl; + } + + // Emitting 0 at enable (will only go through if there is no value at t=0) + if (m_values_queue.cbegin() == m_values_queue.cend() + || sc_time(m_values_queue.front().first, SC_SEC).value() > 0) + { + cout << "@" << sc_time_stamp() << ", " << name() << " emitted: " << spx::ea_value_type(0) << endl; + p_out->write(spx::ea_value_type(0)); + } + + auto it = m_values_queue.cbegin(); + while (it != m_values_queue.cend()) + { + sc_time now = sc_time_stamp(); + if (it->first < now.to_seconds()) + { + ++it; + continue; + } + sc_time delay = sc_time(it->first, SC_SEC) - now; + + // Wait until next output time + wait(delay); + + auto Vout = it->second; + + // Write value to output + p_out->write(Vout); + cout << "@" << sc_time_stamp() << ", " << name() << " emitted: " << Vout << endl; + + ++it; + } + + // cout << name() << " completed" << endl; + while(true) { wait(); } +} diff --git a/src/devices/electrical_value_list_source.h b/src/devices/electrical_value_list_source.h new file mode 100644 index 0000000..0534784 --- /dev/null +++ b/src/devices/electrical_value_list_source.h @@ -0,0 +1,128 @@ +#pragma once + +#include <systemc.h> +#include <fstream> +#include <algorithm> +#include <vector> +#include <utility> + +#include "strutils.h" +#include "optical_output_port.h" +#include "optical_signal.h" +#include "specs.h" +#include "spx_module.h" + +using std::vector; +using std::pair; + +class EVLSource : public spx_module { +public: + typedef pair<double, spx::ea_value_type> time_value_pair_type; + + // Ports + spx::ea_port_out_type p_out; + + // Signal to emit + vector<time_value_pair_type> m_values_queue; + + // Source emission control + spx::ed_signal_type enable; + + // Processes + void runner(); + + EVLSource(sc_module_name name, const vector<time_value_pair_type> &values = {}) + : spx_module(name) + , m_values_queue(values) + { + sortValues(); + enable = sc_logic(0); + + SC_HAS_PROCESS(EVLSource); + SC_THREAD(runner); + } + + void setValues(const vector<time_value_pair_type> &values) + { + m_values_queue = values; + sortValues(); + } + + void setValues(const string &filename) + { + std::ifstream file(filename); + + if (!file.is_open()) { + cerr << "Error: Cannot open the file." << endl; + exit(1); + } + + string line; + + getline(file, line); + + int lineNumber = 0; + while (getline(file, line)) { + ++lineNumber; + istringstream s(line); + string field; + + double t, V; + + // Skip if line is empty or starts with ; + if (line.empty()) + { + cout << "Skipping empty line." << endl; + continue; + } + // Skip if line starts with ; + if (line[0] == ';') + { + cout << "Skipping commented line: " << line << endl; + continue; + } + + // Read first field (time) + getline(s, field, ','); + strutils::trim(field); + + // Skip header line if it exists + if(lineNumber == 1 && field == "time") + { + cout << "Skipping header line: " << line << endl; + continue; + } + + try { + t = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for time: \"" << field << "\"" << endl; + exit(1); + } + + // Read second field (V) + getline(s, field, ','); + try { + V = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for voltage: \"" << field << "\"" << endl; + exit(1); + } + + cout << t << ": " << V << "V" << endl; + m_values_queue.emplace_back(t, V); + } + + file.close(); + sortValues(); + } + + void sortValues() + { + auto cmp = [](const time_value_pair_type& p1, const time_value_pair_type& p2) { + return p1.first < p2.first; + }; + // Stable sort will keep initial ordering for values with identical time + std::stable_sort(m_values_queue.begin(), m_values_queue.end(), cmp); + } +}; diff --git a/src/devices/generic_2x2_coupler.h b/src/devices/generic_2x2_coupler.h new file mode 100644 index 0000000..6cafc17 --- /dev/null +++ b/src/devices/generic_2x2_coupler.h @@ -0,0 +1,86 @@ +#pragma once + +#include "devices/generic_transmission_device.h" + +class Generic2x2Coupler : public GenericTransmissionDevice { +public: + Generic2x2Coupler(sc_module_name name + , const double &k_power = 0.0 + , const double &insertion_loss = 0.0) + : GenericTransmissionDevice(name, 4) + { + setCouplingFactor(k_power); + setInsertionLoss(insertion_loss); + } + + void setCouplingFactor(const double &k_power) + { + assert(isfinite(k_power) && k_power >= 0 && k_power <= 1); + m_k_power = k_power; + } + + void setInsertionLoss(const double &insertion_loss) + { + assert(isfinite(insertion_loss) && insertion_loss >= 0); + m_insertion_loss = insertion_loss; + } + +private: + + virtual void prepareTM() + { + TM.clear(); + TM.resize(nports); + + double lambda0 = 1550e-9; + double transmission_cross = sqrt(m_k_power) * pow(10.0, -m_insertion_loss/20.0); + transmission_cross = max(0.0, min(1.0, transmission_cross)); + double transmission_through = sqrt(1 - m_k_power) * pow(10.0, -m_insertion_loss/20.0); + transmission_through = max(0.0, min(1.0, transmission_through)); + double phi_through = 0.0; + double phi_cross = M_PI / 2.0; + double tau = 0.0; + size_t k; + + // cout << transmission_through << endl; + // cout << transmission_cross << endl; + // cout << m_k_power << endl << endl << endl; + + // (13, 14, 23, 24 are the only active ones) + + // S13 + k = 0*nports + 2; + TM.Mactive[k] = true; + TM.Malpha[k] = {transmission_through}; + TM.Mphi[k] = {phi_through}; + TM.Mtau[k] = {tau}; + + // S14 + k = 0*nports + 3; + TM.Mactive[k] = true; + TM.Malpha[k] = {transmission_cross}; + TM.Mphi[k] = {phi_cross}; + TM.Mtau[k] = {tau}; + + // S23 + k = 1*nports + 2; + TM.Mactive[k] = true; + TM.Malpha[k] = {transmission_cross}; + TM.Mphi[k] = {phi_cross}; + TM.Mtau[k] = {tau}; + + // S24 + k = 1*nports + 3; + TM.Mactive[k] = true; + TM.Malpha[k] = {transmission_through}; + TM.Mphi[k] = {phi_through}; + TM.Mtau[k] = {tau}; + + // cout << TM.Mphi.size() << endl; + // cout << TM.Mphi[0].size() << endl; + // cout << TM.Mphi[1].size() << endl; + } + + double m_insertion_loss; + double m_k_power; // power coupling factor (1-k = transmission factor) +};
\ No newline at end of file diff --git a/src/devices/generic_transmission_device.cpp b/src/devices/generic_transmission_device.cpp new file mode 100644 index 0000000..e709109 --- /dev/null +++ b/src/devices/generic_transmission_device.cpp @@ -0,0 +1,224 @@ +#include "devices/generic_transmission_device.h" + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + SUFFIX + "_" + to_string(IDX)).c_str()) + +using std::size_t; +using std::pow; + +TransmissionMatrix::paramset_type TransmissionMatrix::operator()(const size_t &i, const size_t &j + , const wavelength_type &lambda, const size_t max_order) const +{ + // row/col to index + const size_t k = i * N + j; + + if (Mactive[k] == false) + return {0,0,0}; + + if (max_order == 0) + return {Malpha[k][0], Mphi[k][0], Mtau[k][0]}; + + // Alias relevant taylor series + auto &Malpha_ = Malpha[k]; + auto &Mphi_ = Mphi[k]; + auto &Mtau_ = Mtau[k]; + + // Get initial parameters (values at lambda0) + double alpha = Malpha_[0]; + double phi = Mphi_[0]; + double tau = Mtau_[0]; + + // Calculate distance to lambda0 + const wavelength_type dlambda = lambda - lambda0; + + // Perform taylor expansion of alpha + double dlambda_n_over_fact_n = 1.0; + for (size_t order = 1; order < min(1 + max_order, Malpha_.size()); ++order) + { + double dalpha_over_dlambda_n = Malpha_[order]; + dlambda_n_over_fact_n *= dlambda / order; + alpha += dalpha_over_dlambda_n * dlambda_n_over_fact_n; + } + + // Perform taylor expansion of phi + dlambda_n_over_fact_n = 1.0; + for (size_t order = 1; order < min(1 + max_order, Mphi_.size()); ++order) + { + double dphi_over_dlambda_n = Mphi_[order]; + dlambda_n_over_fact_n *= dlambda / order; + phi += dphi_over_dlambda_n * dlambda_n_over_fact_n; + } + + // Perform taylor expansion of tau + dlambda_n_over_fact_n = 1.0; + for (size_t order = 1; order < min(1 + max_order, Mtau_.size()); ++order) + { + double dtau_over_dlambda_n = Mtau_[order]; + dlambda_n_over_fact_n *= dlambda / order; + tau += dtau_over_dlambda_n * dlambda_n_over_fact_n; + } + + //cout << lambda << ": " << alpha << ", " << fmod(phi, 2*M_PI) << ", " << tau << endl; + return {alpha,phi,tau}; +} + +void GenericTransmissionDevice::pre_init() +{ + ports_in.clear(); + ports_out.clear(); + ports_out_writers.clear(); + + for (size_t i = 0; i < nports; ++i) + { + ports_in.push_back(make_shared<port_in_type>(__modname("_IN", i))); + ports_out.push_back(make_shared<port_out_type>(__modname("_OUT", i))); + ports_out_writers.push_back(make_shared<OpticalOutputPort>(__modname("_OOP", i), (*ports_out[i]))); + ports_out_writers[i]->m_use_deltas = true; + } +} + +void GenericTransmissionDevice::init() +{ + prepareTM(); + assert(nports == TM.N); + for (size_t i = 0; i < nports; ++i) + { + for (size_t j = 0; j < nports; ++j) + { + sc_spawn_options opts; + if (TM.isActive(i, j)) + { + opts.set_sensitivity(ports_in[i].get()); + sc_spawn( sc_bind(&GenericTransmissionDevice::input_on_i_output_on_j, this, i, j), + (string(name()) + "process_" + to_string(i) + "_" + to_string(j)).c_str(), &opts); + } + } + } +} + +string GenericTransmissionDevice::describe() const +{ + return ""s; +} + +void GenericTransmissionDevice::input_on_i(size_t i) +{ + auto last_signal = OpticalSignal(0); + const auto &p_in = (*ports_in[i]); + const bool active = TM.isInputActive(i); + + while(active) + { + // Wait for new input on i_in + wait(); + + // Read new input from i_in + const auto &s = p_in->read(); + + // cout << "received:" << endl << "\t" << s; + + // Compute delta + const auto deltaE_in = s - last_signal; + + // Store new input signal for next change + last_signal = s; + + // Loop on all device ports + for (size_t j = 0; j < nports; ++j) + { + if (!TM.isActive(i,j)) + continue; + // cout << "(i,j):" << i << ", " << j << endl; + + // Get transmission parameters at this wavelength + auto Tij = TM(i, j, s.getWavelength()); + + // Apply parameters + auto deltaE_out = deltaE_in * polar(Tij.alpha, Tij.phi); + + // cout << Tij.alpha << ", " << Tij.phi << ", " << Tij.tau << endl; + + deltaE_out.getNewId(); + + // Schedule writing the change in output port + ports_out_writers[j]->delayedWrite(deltaE_out, sc_time(Tij.tau, SC_SEC)); + } + } + while (true) { wait(); } +} + +void GenericTransmissionDevice::input_on_i_output_on_j(size_t i, size_t j) +{ + auto last_signal = OpticalSignal(0); + const auto &p_in = (*ports_in[i]); + //auto &p_out = (*ports_out[j]); + auto &p_out_writer = (*ports_out_writers[j]); + const bool active = TM.isActive(i, j); + cout << i << ", " << j << " is active" << endl; + + // Block if non active + while (!active) { wait(); } + + complex<double> Sij; + double delay; + + // Wait for first signal to calculate Sij and delay + volatile bool init_done = false; + while (!init_done) + { + // Wait for first input on p_in + wait(); + + // Read new input from i_in + const auto &s = p_in->read(); + + // cout << "received:" << endl << "\t" << s << endl; + + if (isnan(s.getWavelength())) + continue; + + // Find Sij at that wavelength + const auto Tij = TM(i, j, s.getWavelength()); + Sij = polar(Tij.alpha, Tij.phi); + delay = Tij.tau; + + auto deltaE = (s - last_signal) * Sij; + last_signal = s; + p_out_writer.delayedWrite(deltaE, sc_time(delay, SC_SEC)); + + init_done = true; + + // cout << "init done:" << endl << "\t" << norm(Sij) << ", " << arg(Sij) << ", " << delay << endl; + } + + while(true) + { + // Wait for new input on i_in + wait(); + + // Read new input from i_in + const auto &s = p_in->read(); + + if (s.getWavelength() != last_signal.getWavelength()) + { + cerr << "Cannot handle different wavelength (" << __FUNCTION__ << ")" << endl; + cerr << s << endl; + cerr << last_signal << endl; + sc_stop(); + } + + // cout << "received:" << endl << "\t" << s << endl; + + // Compute new delta + auto deltaE = (s - last_signal) * Sij; + + // Store new input signal for next change + last_signal = s; + + //deltaE_out.getNewId(); + + // Schedule writing the change in output port + p_out_writer.delayedWrite(deltaE, sc_time(delay, SC_SEC)); + //wait(SC_ZERO_TIME); + } +}
\ No newline at end of file diff --git a/src/devices/generic_transmission_device.h b/src/devices/generic_transmission_device.h new file mode 100644 index 0000000..bfd110b --- /dev/null +++ b/src/devices/generic_transmission_device.h @@ -0,0 +1,94 @@ +#pragma once + +#define SC_INCLUDE_DYNAMIC_PROCESSES + +#include <systemc.h> +#include <vector> +#include <valarray> +#include "devices/spx_module.h" +#include "optical_signal.h" +#include "optical_output_port.h" + +using std::vector; +using std::valarray; +using std::string; +using std::cout; +using std::endl; +using std::cerr; + +class TransmissionMatrix { +public: + typedef TransmissionMatrix this_type; + typedef double wavelength_type; + typedef double param_type; + typedef valarray<param_type> taylor_series_type; + typedef valarray<taylor_series_type> matrix_type; + typedef struct{ double alpha, phi, tau; } paramset_type; + + size_t N; // Number of ports (= Nrow = Ncol) in the matrix + // Arrays of matrices describing alpha, phi and the group delay via their taylor coefficients (starting from alpha0) + matrix_type Malpha; + matrix_type Mphi; + matrix_type Mtau; + valarray<bool> Mactive; + wavelength_type lambda0; // Wavelength at which the taylor coefficients were calculated + + paramset_type operator()(const size_t &i, const size_t &j, const wavelength_type &lambda, const size_t max_order=2) const; + inline bool isActive(const size_t &i, const size_t &j) const + { return Mactive[i * N + j]; } + inline bool isInputActive(const size_t &i) const + { + bool ret = false; + for (size_t j = 0; j < N; ++j) + ret |= Mactive[i * N + j]; + return ret; + } + + void clear() + { + N = 0; + lambda0 = 0; + Malpha.resize(0); + Mphi.resize(0); + Mtau.resize(0); + Mactive.resize(0); + } + + void resize(size_t nports) + { + N = nports; + Malpha.resize(nports*nports, {0}); + Mphi.resize(nports*nports, {0}); + Mtau.resize(nports*nports, {0}); + Mactive.resize(nports*nports, false); + } +}; + +class GenericTransmissionDevice : public spx_module { +public: + typedef GenericTransmissionDevice this_type; + + /* ------------------------ */ + size_t nports; + vector<shared_ptr<port_in_type>> ports_in; + vector<shared_ptr<port_out_type>> ports_out; + vector<shared_ptr<OpticalOutputPort>> ports_out_writers; + TransmissionMatrix TM; + + /* ------------------------ */ + virtual void pre_init(); + virtual void init(); + virtual string describe() const; + virtual void prepareTM() = 0; + + // Process input on i + void input_on_i(size_t i); + void input_on_i_output_on_j(size_t i, size_t j); + + GenericTransmissionDevice(sc_module_name name, size_t N = 1) + : spx_module(name) + , nports(N) + { + pre_init(); + } +};
\ No newline at end of file diff --git a/src/devices/generic_waveguide.h b/src/devices/generic_waveguide.h new file mode 100644 index 0000000..d704678 --- /dev/null +++ b/src/devices/generic_waveguide.h @@ -0,0 +1,119 @@ +#pragma once + +#include "devices/generic_transmission_device.h" + +using std::isfinite; + +class GenericWaveguide : public GenericTransmissionDevice { +public: + GenericWaveguide(sc_module_name name + , const double &length = 0 + , const double &loss = 0 + , const double &neff = 1 + , const double &ng = 1 + , const double &D = 0 + , const double &lambda0 = 1.55e-6) + : GenericTransmissionDevice(name, 2) + { + cerr << "GenericWaveguide is disabled (not working)." << endl; + exit(1); + + setLength(length); + setLoss(loss); + setEffectiveIndex(neff); + setGroupIndex(ng); + setDispersion(D); + setLambda0(lambda0); + } + + void setLength(const double &length) + { + assert(isfinite(length) && length >= 0); + m_length = length; + } + + void setLoss(const double &loss) + { + cout << loss << endl; + assert(isfinite(loss)); + m_loss = loss; + } + + void setEffectiveIndex(const double &neff) + { + assert(isfinite(neff) && neff >= 0); + m_neff = neff; + } + + void setGroupIndex(const double &ng) + { + assert(isfinite(ng) && ng >= 0); + m_ng = ng; + } + + void setDispersion(const double &D) + { + assert(isfinite(D)); + m_D = D; + } + + void setLambda0(const double &lambda0) + { + assert(isfinite(lambda0) && lambda0 > 0); + m_lambda0 = lambda0; + } + + +private: + + virtual void prepareTM() + { + TM.clear(); + TM.resize(nports); + + const double c = 299792458.0; + double lambda0 = m_lambda0; + double transmission = pow(10.0, -(m_loss * m_length)/20.0); + double two_pi_L = 2 * M_PI * m_length; + double dphi_0 = fmod(two_pi_L * m_neff, 2 * M_PI); + + // The following 2 lines are wrong... + double dphi_1 = - two_pi_L * m_ng / lambda0 / lambda0; // Here it should be -2pi*L*ng/(lambda*lambda0) (instead of lambda0^2) + double dphi_2 = two_pi_L * 2 * m_ng / lambda0 / lambda0 / lambda0; + + double group_delay_0 = m_length * m_ng / c; + double group_delay_1 = m_length * m_D; + + TM.lambda0 = lambda0; + TM.Mactive = {false, true, true, false}; + TM.Malpha = { + {0}, + {transmission}, + {transmission}, + {0}}; + TM.Mphi = { + {0}, + {dphi_0, dphi_1, dphi_2}, + {dphi_0, dphi_1, dphi_2}, + {0}}; + TM.Mtau = { + {0}, + {group_delay_0, group_delay_1}, + {group_delay_0, group_delay_1}, + {0}}; + + // cout << "----------- " << name() << " -----------" << endl; + // cout << "- alpha: " << TM.Malpha[1][0] << " (V/m)/(V/m)" << endl; + // cout << "- transmission: " << pow(TM.Malpha[1][0],2.0) << " W/W" << endl; + // cout << "- phi: " << TM.Mphi[1][0] << " rad" << endl; + // cout << "- dphi/dlambda: " << TM.Mphi[1][1] << " rad/m" << endl; + // cout << "- group delay: " << TM.Mtau[1][0] << " s" << endl; + } + + double m_loss; // loss in dB/m + double m_neff; // real part of neff + double m_ng; // (real part of) ng + double m_D; // dispersion + double m_lambda0; // dispersion + double m_length; // waveguide length +};
\ No newline at end of file diff --git a/src/devices/merger.cpp b/src/devices/merger.cpp new file mode 100644 index 0000000..2aa3e9f --- /dev/null +++ b/src/devices/merger.cpp @@ -0,0 +1,69 @@ +#include <merger.h> +#include <specs.h> + +using namespace std; + +void Merger::on_port_in1_changed() +{ + const double transmission = pow(10.0, - m_attenuation_dB / 20) / sqrt(2); + m_memory_in1[0] = 0; // initializing for nan wavelength + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission = " << pow(transmission, 2) << " W/W" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in1.get_interface()))->name() << endl; + cout << "\t --> \t" << (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name() << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in2.get_interface()))->name(); + cout << endl; + cout << endl; + } + + OpticalSignal p_in1_read; + + while (true) { + // Wait for a new input signal + wait(); + // Read sum of input signals + p_in1_read = p_in1->read(); + + auto cur_wavelength_id = p_in1_read.m_wavelength_id; + // Updating the field memory + m_memory_in1[cur_wavelength_id] = p_in1_read.m_field; + + auto s = OpticalSignal(m_memory_in1[cur_wavelength_id] + + m_memory_in2[cur_wavelength_id] + , cur_wavelength_id); + + s *= transmission; + + m_out_writer.delayedWrite(s,SC_ZERO_TIME); + } +} + +void Merger::on_port_in2_changed() +{ + const double transmission = pow(10.0, - m_attenuation_dB / 20) / sqrt(2); + m_memory_in2[0] = 0; // initializing for nan wavelength + + OpticalSignal p_in2_read; + + while (true) { + // Wait for a new input signal + wait(); + // Read sum of input signals + p_in2_read = p_in2->read(); + + auto cur_wavelength_id = p_in2_read.m_wavelength_id; + // Updating the field memory + m_memory_in2[cur_wavelength_id] = p_in2_read.m_field; + + auto s = OpticalSignal(m_memory_in1[cur_wavelength_id] + + m_memory_in2[cur_wavelength_id] + , cur_wavelength_id); + + s *= transmission; + + m_out_writer.delayedWrite(s,SC_ZERO_TIME); + } +}
\ No newline at end of file diff --git a/src/devices/merger.h b/src/devices/merger.h new file mode 100644 index 0000000..a3c0704 --- /dev/null +++ b/src/devices/merger.h @@ -0,0 +1,51 @@ +#pragma once + +#include <systemc.h> +#include <map> + +#include <spx_module.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +#include "specs.h" +#include "spx_module.h" + +// A symmetric no-delay merger +class Merger : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in1; + spx::oa_port_in_type p_in2; + spx::oa_port_out_type p_out; + + // Timed ports writers + OpticalOutputPort m_out_writer; + + // Member variables + double m_attenuation_dB; + + // Memory for multi-wavelength purposes + // maybe with vector it has better performance + std::map<uint32_t,OpticalSignal::field_type> m_memory_in1; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in2; + + // Processes + void on_port_in1_changed(); + void on_port_in2_changed(); + + // Constructor + Merger(sc_module_name name, + double attenuation_dB = 0) + : spx_module(name) + , m_out_writer("out_delayed_writer", p_out) + , m_attenuation_dB(attenuation_dB) + { + SC_HAS_PROCESS(Merger); + + SC_THREAD(on_port_in1_changed); + sensitive << p_in1; + + SC_THREAD(on_port_in2_changed); + sensitive << p_in2; + } +}; diff --git a/src/devices/mesh_col.cpp b/src/devices/mesh_col.cpp new file mode 100644 index 0000000..5723b17 --- /dev/null +++ b/src/devices/mesh_col.cpp @@ -0,0 +1,243 @@ +#include <mesh_col.h> + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +void MeshCol::init_ports() +{ + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing optical input ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_in.clear(); + for(size_t i = 0; i < m_N; i++) + { + p_in.push_back(make_unique<spx::oa_port_in_type>(__modname("IN_", i))); + } + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing optical output ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_out.clear(); + for(size_t i = 0; i < m_N; i++) + { + p_out.push_back(make_unique<spx::oa_port_out_type>(__modname("OUT_", i))); + } + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing electrical input ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_vphi.clear(); + p_vtheta.clear(); + + size_t num_of_electrical_ports = m_N/2; + if ((m_N % 2 == 0) && !(m_j % 2 == 0)) // if even-odd, there is one less MZI + num_of_electrical_ports = m_N/2 - 1; + + + for(size_t i = 0; i < num_of_electrical_ports; i++) + { + p_vphi.push_back(make_unique<sc_in<double>>(__modname("VPHI_", i))); + p_vtheta.push_back(make_unique<sc_in<double>>(__modname("VTHETA_", i))); + } +} + +void MeshCol::init() +{ + if (m_N % 2 == 0) + { + if (m_j % 2 == 0) + init_N_even_j_even(); + else + init_N_even_j_odd(); + } + else + { + if (m_j % 2 == 0) + init_N_odd_j_even(); + else + init_N_odd_j_odd(); + } +} + +/* + For N=4 that's how it looks + 0__ __0 + \/ + 1__/\__1 + + 2__ __2 + \/ + 3__/\__3 + + TODO: snap to 2pi phase instead of setting neff = 0 +*/ +void MeshCol::init_N_even_j_even() +{ + for(size_t i = 0; i < m_N/2; i++) + { + m_mzi.push_back(make_unique<MZI>(__modname("MZI_", i), + m_length_cm, m_attenuation_wg_dB_cm, + m_attenuation_coupler_dB, m_attenuation_ps_dB, + m_neff, m_ng)); + + m_mzi[i]->p_in1(*p_in[2*i]); + m_mzi[i]->p_in2(*p_in[2*i+1]); + m_mzi[i]->p_out1(*p_out[2*i]); + m_mzi[i]->p_out2(*p_out[2*i+1]); + m_mzi[i]->p_vphi(*p_vphi[i]); + m_mzi[i]->p_vtheta(*p_vtheta[i]); + m_mzi[i]->init(); + } +} + + +/* For N=4 that's how it looks + + 0______0 + + 1__ __1 + \/ + 2__/\__2 + + 3______3 + + The empty rows have a waveguide that adds zero phase + but can have loss and time delay. + + TODO: snap to 2pi phase instead of setting neff = 0 + +*/ +void MeshCol::init_N_even_j_odd() +{ + assert(m_N >= 4); + + // Zero neff so it doesn't add any phase + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), + m_length_cm, m_attenuation_wg_dB_cm, + m_neff, m_ng)); + m_wg[0]->p_in(*p_in[0]); + m_wg[0]->p_out(*p_out[0]); + + for(size_t i = 1; i <= m_N/2 - 1; i++) + { + m_mzi.push_back(make_unique<MZI>(__modname("MZI_", i), + m_length_cm, m_attenuation_wg_dB_cm, + m_attenuation_coupler_dB, m_attenuation_ps_dB, + m_neff, m_ng)); + + m_mzi[i - 1]->p_in1(*p_in[2 * i - 1]); + m_mzi[i - 1]->p_in2(*p_in[2 * i]); + m_mzi[i - 1]->p_out1(*p_out[2 * i - 1]); + m_mzi[i - 1]->p_out2(*p_out[2 * i]); + m_mzi[i - 1]->p_vphi(*p_vphi[i - 1]); + m_mzi[i - 1]->p_vtheta(*p_vtheta[i - 1]); + m_mzi[i - 1]->init(); + + } + + // Zero neff so it doesn't add any phase + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 1), + m_length_cm, m_attenuation_wg_dB_cm, + m_neff, m_ng)); + m_wg[1]->p_in(*p_in[m_N-1]); + m_wg[1]->p_out(*p_out[m_N-1]); + +} + +/* For N=5 that's how it looks + + 0__ __0 + \/ + 1__/\__1 + + 2__ __2 + \/ + 3__/\__3 + + 4______4 + + The empty rows have a waveguide that adds zero phase + but can have loss and time delay. + + TODO: snap to 2pi phase instead of setting neff = 0 + +*/ +void MeshCol::init_N_odd_j_even() +{ + cout << "odd-even" << endl; + for(size_t i = 0; i < m_N/2; i++) + { + m_mzi.push_back(make_unique<MZI>(__modname("MZI_", i), + m_length_cm, m_attenuation_wg_dB_cm, + m_attenuation_coupler_dB, m_attenuation_ps_dB, + m_neff, m_ng)); + + m_mzi[i]->p_in1(*p_in[2*i]); + m_mzi[i]->p_in2(*p_in[2*i+1]); + m_mzi[i]->p_out1(*p_out[2*i]); + m_mzi[i]->p_out2(*p_out[2*i+1]); + m_mzi[i]->p_vphi(*p_vphi[i]); + m_mzi[i]->p_vtheta(*p_vtheta[i]); + m_mzi[i]->init(); + } + // Zero neff so it doesn't add any phase + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), + m_length_cm, m_attenuation_wg_dB_cm, + m_neff, m_ng)); + m_wg[0]->p_in(*p_in[m_N-1]); + m_wg[0]->p_out(*p_out[m_N-1]); +} + +/* For N=5 that's how it looks + + 0______0 + + 1__ __1 + \/ + 2__/\__2 + + 3__ __3 + \/ + 4__/\__4 + + The empty rows have a waveguide that adds zero phase + but can have loss and time delay. + + TODO: snap to 2pi phase instead of setting neff = 0 + +*/ +void MeshCol::init_N_odd_j_odd() +{ + cout << "odd-odd" << endl; + // Zero neff so it doesn't add any phase + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), + m_length_cm, m_attenuation_wg_dB_cm, + m_neff, m_ng)); + m_wg[0]->p_in(*p_in[0]); + m_wg[0]->p_out(*p_out[0]); + + for(size_t i = 1; i <= m_N/2; i++) + { + m_mzi.push_back(make_unique<MZI>(__modname("MZI_", i), + m_length_cm, m_attenuation_wg_dB_cm, + m_attenuation_coupler_dB, m_attenuation_ps_dB, + m_neff, m_ng)); + + m_mzi[i - 1]->p_in1(*p_in[2 * i - 1]); + m_mzi[i - 1]->p_in2(*p_in[2 * i]); + m_mzi[i - 1]->p_out1(*p_out[2 * i - 1]); + m_mzi[i - 1]->p_out2(*p_out[2 * i]); + m_mzi[i - 1]->p_vphi(*p_vphi[i - 1]); + m_mzi[i - 1]->p_vtheta(*p_vtheta[i - 1]); + m_mzi[i - 1]->init(); + } +}
\ No newline at end of file diff --git a/src/devices/mesh_col.h b/src/devices/mesh_col.h new file mode 100644 index 0000000..e0a6066 --- /dev/null +++ b/src/devices/mesh_col.h @@ -0,0 +1,76 @@ +#pragma once + +#include <systemc.h> +#include <specs.h> +#include <spx_module.h> +#include <mzi.h> +#include <waveguide.h> + +/** + * This class implements a single column of an MZI mesh + * specified in the work: Optimal design for universal multiport interferometers + * by Clements et al. (2016). +*/ + +class MeshCol : public spx_module{ +public: + // Ports + vector<unique_ptr<spx::oa_port_in_type>> p_in; + vector<unique_ptr<spx::oa_port_out_type>> p_out; + vector<unique_ptr<sc_in<double>>> p_vphi; + vector<unique_ptr<sc_in<double>>> p_vtheta; + + // Member variables + size_t m_N; // represents the number of inputs of the mesh (rows) + size_t m_j; // represents the current row being evaluated + + // Parameters for the internal MZIs + double m_length_cm; + double m_attenuation_wg_dB_cm; + double m_attenuation_coupler_dB; + double m_attenuation_ps_dB; + double m_neff; + double m_ng; + + // Submodules + vector<unique_ptr<MZI>> m_mzi; + vector<unique_ptr<Waveguide>> m_wg; + + + // Initialization functions + void init_ports(); + void init(); + + void init_N_even_j_even(); + void init_N_even_j_odd(); + + void init_N_odd_j_even(); + void init_N_odd_j_odd(); + + /** Constructor for MZI Mesh Column + * + * @param name name of the module + * @param N number of rows contained in the module + * @param j current column being created, from 0 to N-1 + * */ + MeshCol(sc_module_name name, const size_t &N, const size_t &j, + const double &length_cm = 1e-2, + const double &attenuation_wg_dB_cm = 0, const double &attenuation_coupler_dB = 0, + const double &attenuation_ps_dB = 0, + const double &neff = 2.2111, const double &ng = 2.2637) + : spx_module(name) + , m_N(N) + , m_j(j) + , m_length_cm(length_cm) + , m_attenuation_wg_dB_cm(attenuation_wg_dB_cm) + , m_attenuation_coupler_dB(attenuation_coupler_dB) + , m_attenuation_ps_dB(attenuation_ps_dB) + , m_neff(neff) + , m_ng(ng) + { + assert(N > 1); + assert(j < N); + + init_ports(); + } +};
\ No newline at end of file diff --git a/src/devices/mzi.cpp b/src/devices/mzi.cpp new file mode 100644 index 0000000..6999fd9 --- /dev/null +++ b/src/devices/mzi.cpp @@ -0,0 +1,55 @@ +#include <mzi.h> + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +using namespace std; + +void MZI::init() +{ + DC1.reset(); + DC2.reset(); + PS1.reset(); + PS2.reset(); + WG1.reset(); + WG2.reset(); + + // Creating instances + WG1 = make_unique<Waveguide>((string(name()) + "/WG1").c_str(), + m_length_cm, m_attenuation_dB_cm, m_neff, m_ng); + WG2 = make_unique<Waveguide>((string(name()) + "/WG2").c_str(), + m_length_cm, m_attenuation_dB_cm, m_neff, m_ng); + DC1 = make_unique<DirectionalCoupler>((string(name()) + "/DC1").c_str(), 0.5, m_attenuation_coupler_dB); + DC2 = make_unique<DirectionalCoupler>((string(name()) + "/DC2").c_str(), 0.5, m_attenuation_coupler_dB); + PS1 = make_unique<PhaseShifter>((string(name()) + "/PS1").c_str(), m_attenuation_ps_dB); + PS2 = make_unique<PhaseShifter>((string(name()) + "/PS2").c_str(), m_attenuation_ps_dB); + + // Connecting + + PS1->p_in(p_in1); + PS1->p_out(w1); + PS1->p_vin(p_vphi); + + DC1->p_in1(w1); + DC1->p_in2(p_in2); + DC1->p_out1(w2); + DC1->p_out2(w5); + + // Upper branch + PS2->p_in(w2); + PS2->p_out(w3); + PS2->p_vin(p_vtheta); + + WG1->p_in(w3); + WG1->p_out(w4); + + // Bottom branch + WG2->p_in(w5); + WG2->p_out(w6); + + // End + DC2->p_in1(w4); + DC2->p_in2(w6); + DC2->p_out1(p_out1); + DC2->p_out2(p_out2); +}
\ No newline at end of file diff --git a/src/devices/mzi.h b/src/devices/mzi.h new file mode 100644 index 0000000..95089ad --- /dev/null +++ b/src/devices/mzi.h @@ -0,0 +1,67 @@ +#pragma once + +#include <systemc.h> + +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +#include <directional_coupler.h> +#include <phaseshifter.h> +#include <waveguide.h> + +/** A unidirectional waveguide module. */ +class MZI : public spx_module { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p_in1, p_in2; + /** The optical output ports. */ + spx::oa_port_out_type p_out1, p_out2; + /** The electrical input ports */ + spx::ea_port_in_type p_vphi, p_vtheta; + + // Member variables + + /** Waveguide-related. */ + double m_length_cm; + double m_attenuation_dB_cm; + double m_neff; + double m_ng; + + /** DC-related */ + double m_attenuation_coupler_dB; + + /** PhaseShifter-related */ + double m_attenuation_ps_dB; + + // Member submodules and wires + spx::oa_signal_type w1, w2, w3, w4, w5, w6; + + unique_ptr<PhaseShifter> PS1, PS2; + unique_ptr<Waveguide> WG1, WG2, WG3; + unique_ptr<DirectionalCoupler> DC1,DC2; + + virtual void init(); + + /** Constructor for MZI + * + * @param name name of the module + * @param length_cm internal branch length in cm + * */ + MZI(sc_module_name name, const double &length_cm = 1e-2, + const double &attenuation_wg_dB_cm = 0, const double &attenuation_coupler_dB = 0, + const double &attenuation_ps_dB = 0, + const double &neff = 2.2111, const double &ng = 2.2637) + : spx_module(name) + , m_length_cm(length_cm) + , m_attenuation_dB_cm(attenuation_wg_dB_cm) + , m_neff(neff) + , m_ng(ng) + , m_attenuation_coupler_dB(attenuation_coupler_dB) + , m_attenuation_ps_dB(attenuation_ps_dB) + { + + } +}; diff --git a/src/devices/mzi_active.cpp b/src/devices/mzi_active.cpp new file mode 100644 index 0000000..4a8b8a8 --- /dev/null +++ b/src/devices/mzi_active.cpp @@ -0,0 +1,206 @@ +#include <mzi_active.h> + +void MZIActiveUni::init() +{ + m_dc1_o1 = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out1").c_str()); + m_dc1_o2 = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out2").c_str()); + m_dc2_i1 = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in1").c_str()); + m_dc2_i2 = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in2").c_str()); + m_ps_in = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_in").c_str()); + m_ps_out = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_out").c_str()); + + m_wg1 = make_shared<WaveguideUni>((string(name()) + "/WG1").c_str()); + m_wg2 = make_shared<WaveguideUni>((string(name()) + "/WG2").c_str()); + m_wg3 = make_shared<WaveguideUni>((string(name()) + "/WG3").c_str()); + m_dc1 = make_shared<DirectionalCouplerUni>((string(name()) + "/DC1").c_str()); + m_dc2 = make_shared<DirectionalCouplerUni>((string(name()) + "/DC2").c_str()); + m_ps = make_shared<PhaseShifterUni>((string(name()) + "/PS1").c_str()); + + connect_submodules(); +} + +void MZIActiveUni::connect_submodules() +{ + // Parametrizing the instances + + // cast as unidirectional devices + WaveguideUni * const wg1 = dynamic_cast<WaveguideUni *>(m_wg1.get()); + WaveguideUni * const wg2 = dynamic_cast<WaveguideUni *>(m_wg2.get()); + WaveguideUni * const wg3 = dynamic_cast<WaveguideUni *>(m_wg3.get()); + DirectionalCouplerUni * const dc1 = dynamic_cast<DirectionalCouplerUni *>(m_dc1.get()); + DirectionalCouplerUni * const dc2 = dynamic_cast<DirectionalCouplerUni *>(m_dc2.get()); + PhaseShifterUni * const ps = dynamic_cast<PhaseShifterUni *>(m_ps.get()); + + + // Top waveguides are divided by two to preserve + // the same length in the arms + wg1->m_length_cm = m_length_cm/2; + wg1->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg1->m_neff = m_neff; + wg1->m_ng = m_ng; + + wg2->m_length_cm = m_length_cm/2; + wg2->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg2->m_neff = m_neff; + wg2->m_ng = m_ng; + + wg3->m_length_cm = m_length_ref_cm; + wg3->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg3->m_neff = m_neff; + wg3->m_ng = m_ng; + + dc1->m_dc_loss = m_dc_loss_dB; + dc2->m_dc_loss = m_dc_loss_dB; + + ps->m_sensitivity = m_ps_sens_rad_v; + ps->m_attenuation_dB = m_ps_loss_dB; + + // Connecting the blocks + + dc1->p_in1(p_in1); + dc1->p_in2(p_in2); + dc1->p_out1(*m_dc1_o1); + dc1->p_out2(*m_dc1_o2); + + // Top arm + wg1->p_in(*m_dc1_o1); + wg1->p_out(*m_ps_in); + + ps->p_in(*m_ps_in); + ps->p_out(*m_ps_out); + ps->p_vin(p_vin); + + wg2->p_in(*m_ps_out); + wg2->p_out(*m_dc2_i1); + + // Bot arm + wg3->p_in(*m_dc1_o2); + wg3->p_out(*m_dc2_i2); + + dc2->p_in1(*m_dc2_i1); + dc2->p_in2(*m_dc2_i2); + dc2->p_out1(p_out1); + dc2->p_out2(p_out2); +} + +void MZIActiveBi::init() +{ + // forward + m_dc1_p2_out = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out1_0").c_str()); + m_dc1_p3_out = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out2_0").c_str()); + m_dc2_p0_in = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in1_0").c_str()); + m_dc2_p1_in = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in2_0").c_str()); + m_ps_p0_in = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_in_0").c_str()); + m_ps_p1_out = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_out_0").c_str()); + + // backward + m_dc1_p2_in = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out1_1").c_str()); + m_dc1_p3_in = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out2_1").c_str()); + m_dc2_p0_out = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in1_1").c_str()); + m_dc2_p1_out = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_in2_1").c_str()); + m_ps_p0_out = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_in_1").c_str()); + m_ps_p1_in = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_out_1").c_str()); + + // devices + m_wg1 = make_shared<WaveguideBi>((string(name()) + "/WG1").c_str()); + m_wg2 = make_shared<WaveguideBi>((string(name()) + "/WG2").c_str()); + m_wg3 = make_shared<WaveguideBi>((string(name()) + "/WG3").c_str()); + m_dc1 = make_shared<DirectionalCouplerBi>((string(name()) + "/DC1").c_str()); + m_dc2 = make_shared<DirectionalCouplerBi>((string(name()) + "/DC2").c_str()); + m_ps = make_shared<PhaseShifterBi>((string(name()) + "/PS1").c_str()); + + connect_submodules(); +} + +void MZIActiveBi::connect_submodules() +{ + // Parametrizing the instances + + // cast as unidirectional devices + WaveguideBi * const wg1 = dynamic_cast<WaveguideBi *>(m_wg1.get()); + WaveguideBi * const wg2 = dynamic_cast<WaveguideBi *>(m_wg2.get()); + WaveguideBi * const wg3 = dynamic_cast<WaveguideBi *>(m_wg3.get()); + DirectionalCouplerBi * const dc1 = dynamic_cast<DirectionalCouplerBi *>(m_dc1.get()); + DirectionalCouplerBi * const dc2 = dynamic_cast<DirectionalCouplerBi *>(m_dc2.get()); + PhaseShifterBi * const ps = dynamic_cast<PhaseShifterBi *>(m_ps.get()); + + + // Top waveguides are divided by two to preserve + // the same length in the arms + wg1->m_length_cm = m_length_cm/2; + wg1->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg1->m_neff = m_neff; + wg1->m_ng = m_ng; + + wg2->m_length_cm = m_length_cm/2; + wg2->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg2->m_neff = m_neff; + wg2->m_ng = m_ng; + + wg3->m_length_cm = m_length_ref_cm; + wg3->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg3->m_neff = m_neff; + wg3->m_ng = m_ng; + + dc1->m_dc_loss = m_dc_loss_dB; + dc2->m_dc_loss = m_dc_loss_dB; + + ps->m_sensitivity = m_ps_sens_rad_v; + ps->m_attenuation_dB = m_ps_loss_dB; + + // Connecting the blocks + + { // forward + dc1->p0_in(p0_in); + dc1->p1_in(p1_in); + dc1->p2_out(*m_dc1_p2_out); + dc1->p3_out(*m_dc1_p3_out); + + // Top arm + wg1->p0_in(*m_dc1_p2_out); + wg1->p1_out(*m_ps_p0_in); + + ps->p0_in(*m_ps_p0_in); + ps->p1_out(*m_ps_p1_out); + ps->p_vin(p_vin); + + wg2->p0_in(*m_ps_p1_out); + wg2->p1_out(*m_dc2_p0_in); + + // Bot arm + wg3->p0_in(*m_dc1_p3_out); + wg3->p1_out(*m_ps_p1_out); + + dc2->p0_in(*m_dc2_p0_in); + dc2->p1_in(*m_ps_p1_out); + dc2->p2_out(p2_out); + dc2->p3_out(p3_out); + } + + { // backward + dc1->p0_out(p0_out); + dc1->p1_out(p1_out); + dc1->p2_in(*m_dc1_p2_in); + dc1->p3_in(*m_dc1_p3_in); + + // Top arm + wg1->p0_out(*m_dc1_p2_in); + wg1->p1_in(*m_ps_p0_out); + + ps->p0_out(*m_ps_p0_out); + ps->p1_in(*m_ps_p1_in); + ps->p_vin(p_vin); + + wg2->p0_out(*m_ps_p1_in); + wg2->p1_in(*m_dc2_p0_out); + + // Bot arm + wg3->p0_out(*m_dc1_p3_in); + wg3->p1_in(*m_ps_p1_in); + + dc2->p0_out(*m_dc2_p0_out); + dc2->p1_out(*m_ps_p1_in); + dc2->p2_in(p2_in); + dc2->p3_in(p3_in); + } +}
\ No newline at end of file diff --git a/src/devices/mzi_active.h b/src/devices/mzi_active.h new file mode 100644 index 0000000..890322e --- /dev/null +++ b/src/devices/mzi_active.h @@ -0,0 +1,179 @@ +#pragma once + +#include "specs.h" +#include <systemc.h> +#include <waveguide.h> +#include <phaseshifter.h> +#include <directional_coupler.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +//TODO: pass element's characteristics as argument, for instance, pass an example of waveguide, +// or an example of DC such that it copies. Requires: constructors in each element to build +// by copying. + +class MZIActiveBase : public spx_module { +public: + // Member variables + double m_ps_sens_rad_v; + double m_length_cm; + double m_length_ref_cm; + double m_attenuation_dB_cm; + double m_neff; + double m_ng; + double m_dc_loss_dB; // loss of the splitter/merger + double m_ps_loss_dB; // loss of the phase shifter + + virtual void init() = 0; + virtual void connect_submodules() = 0; + + // Member submodules + shared_ptr<WaveguideBase> m_wg1, m_wg2, m_wg3; + shared_ptr<DirectionalCouplerBase> m_dc1, m_dc2; + shared_ptr<PhaseShifterBase> m_ps; + + /** Constructor for Waveguide + * + * @param name name of the module + * @param m_ps_sens_rad_v sensitivity of phase shifter + * @param m_length_cm length of the phase-shifter arm + * @param m_length_ref_cm length of the reference arm + * @param m_attenuation_dB_cm loss per cm of wgs + * @param m_neff neff of wgs + * @param m_ng ng of wgs + * @param m_dc_loss_dB insertion loss of the y-junctions + * @param m_ps_loss_dB insertion loss of the phase shifter + * */ + MZIActiveBase(sc_module_name name, + double ps_sens_rad_v = 1, + double length_cm = 0, double length_ref_cm = 0, + double attenuation_dB_cm = 0, + double neff = 2.2, double ng = 2.6, double dc_loss_dB = 0, + double ps_loss_dB = 0) + : spx_module(name) + , m_ps_sens_rad_v(ps_sens_rad_v) + , m_length_cm(length_cm) + , m_length_ref_cm(length_ref_cm) + , m_attenuation_dB_cm(attenuation_dB_cm) + , m_neff(neff) + , m_ng(ng) + , m_dc_loss_dB(dc_loss_dB) + , m_ps_loss_dB(ps_loss_dB) + { + } +}; + +class MZIActiveUni : public MZIActiveBase { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p_in1, p_in2; + /** The optical output ports. */ + spx::oa_port_out_type p_out1, p_out2; + /** The electrical input ports */ + spx::ea_port_in_type p_vin; + + // Wires + shared_ptr<spx::oa_signal_type> m_dc1_o1, m_dc1_o2, m_dc2_i1, m_dc2_i2; + shared_ptr<spx::oa_signal_type> m_ps_in, m_ps_out; + + virtual void init(); + virtual void connect_submodules(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param m_ps_sens_rad_v sensitivity of phase shifter + * @param m_length_cm length of the phase-shifter arm + * @param m_length_ref_cm length of the reference arm + * @param m_attenuation_dB_cm loss per cm of wgs + * @param m_neff neff of wgs + * @param m_ng ng of wgs + * @param m_dc_loss_dB insertion loss of the y-junctions + * @param m_ps_loss_dB insertion loss of the phase shifter + * */ + MZIActiveUni(sc_module_name name, + double ps_sens_rad_v = 1, + double length_cm = 0, + double length_ref_cm = 0, + double attenuation_dB_cm = 0, + double neff = 2.2, + double ng = 2.6, + double dc_loss_dB = 0, + double ps_loss_dB = 0) + : MZIActiveBase(name, + ps_sens_rad_v, + length_cm, + length_ref_cm, + attenuation_dB_cm, + neff, + ng, + dc_loss_dB, + ps_loss_dB) + { + + } +}; + +typedef MZIActiveUni MZIActive; + +class MZIActiveBi : public MZIActiveBase { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p0_in; + spx::oa_port_in_type p1_in; + spx::oa_port_in_type p2_in; + spx::oa_port_in_type p3_in; + + /** The optical output ports. */ + spx::oa_port_out_type p0_out; + spx::oa_port_out_type p1_out; + spx::oa_port_out_type p2_out; + spx::oa_port_out_type p3_out; + + /** The electrical input ports */ + spx::ea_port_in_type p_vin; + + // Wires + shared_ptr<spx::oa_signal_type> m_dc1_p2_out, m_dc1_p3_out, m_dc2_p0_in, m_dc2_p1_in; + shared_ptr<spx::oa_signal_type> m_dc1_p2_in, m_dc1_p3_in, m_dc2_p0_out, m_dc2_p1_out; + shared_ptr<spx::oa_signal_type> m_ps_p0_in, m_ps_p1_in, m_ps_p0_out, m_ps_p1_out; + + virtual void init(); + virtual void connect_submodules(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param m_ps_sens_rad_v sensitivity of phase shifter + * @param m_length_cm length of the phase-shifter arm + * @param m_length_ref_cm length of the reference arm + * @param m_attenuation_dB_cm loss per cm of wgs + * @param m_neff neff of wgs + * @param m_ng ng of wgs + * @param m_dc_loss_dB insertion loss of the y-junctions + * @param m_ps_loss_dB insertion loss of the phase shifter + * */ + MZIActiveBi(sc_module_name name, + double ps_sens_rad_v = 1, + double length_cm = 0, + double length_ref_cm = 0, + double attenuation_dB_cm = 0, + double neff = 2.2, + double ng = 2.6, + double dc_loss_dB = 0, + double ps_loss_dB = 0) + : MZIActiveBase(name, + ps_sens_rad_v, + length_cm, + length_ref_cm, + attenuation_dB_cm, + neff, + ng, + dc_loss_dB, + ps_loss_dB) + { + + } +}; diff --git a/src/devices/octane_cell.cpp b/src/devices/octane_cell.cpp new file mode 100644 index 0000000..1b5c59c --- /dev/null +++ b/src/devices/octane_cell.cpp @@ -0,0 +1,91 @@ +#include <octane_cell.h> + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +void OctaneCell::init() +{ + if(m_isCompact) + init_compact(); + else + init_full(); +} + +void OctaneCell::init_full() +{ + // Physical parameters of the components + double lambda = 1550e-9; // the wavelength used by the circuit + double neff = 2.2; + double ng = 2.2; + double loss_per_cm = 0; // in waveguide + double loss_merger = 0; + double length_for_2pi = 100*lambda/(neff); + + // Constructing the instances + m_wg1 = make_unique<Waveguide>(__modname("WG_", 0), length_for_2pi, loss_per_cm, neff, ng); + m_wg2 = make_unique<Waveguide>(__modname("WG_", 1), length_for_2pi, loss_per_cm, neff, ng); + m_wg3 = make_unique<Waveguide>(__modname("WG_", 2), length_for_2pi, loss_per_cm, neff, ng); + m_wg4 = make_unique<Waveguide>(__modname("WG_", 3), length_for_2pi, loss_per_cm, neff, ng); + + m_merger = make_unique<Merger>(__modname("Y_", 0), loss_merger); + + m_pcm = make_unique<PCMElement>(__modname("PCM_", 0), m_meltEnergy, m_nStates, 0); + + m_pd = make_unique<Detector>(__modname("PD_", 0)); + + // Connecting + m_wg1->p_in(p_in_r); + m_wg1->p_out(m_merg_in1); + + m_wg2->p_in(p_in_c); + m_wg2->p_out(m_merg_in2); + + m_merger->p_in1(m_merg_in1); + m_merger->p_in2(m_merg_in2); + m_merger->p_out(m_merg_out); + + m_wg3->p_in(m_merg_out); + m_wg3->p_out(m_pcm_in); + + m_pcm->p_in(m_pcm_in); + m_pcm->p_out(m_pcm_out); + + // m_wg4->p_in(m_pcm_in); + m_wg4->p_in(m_pcm_out); + m_wg4->p_out(m_pd_in); + + m_pd->p_in(m_pd_in); + m_pd->p_readout(p_readout); +} + +void OctaneCell::init_compact() +{ + // Physical parameters of the components + double lambda = 1550e-9; // the wavelength used by the circuit + double neff = 2.2; + double ng = 2.2; + double loss_per_cm = 0; // in waveguide + double loss_merger = 0; + double length_for_2pi = 100*lambda/(neff); + + (void)ng; + (void)loss_per_cm; + (void)length_for_2pi; + + m_merger = make_unique<Merger>(__modname("Y_", 0), loss_merger); + + m_pcm = make_unique<PCMElement>(__modname("PCM_", 0), m_meltEnergy, m_nStates, 0); + + m_pd = make_unique<Detector>(__modname("PD_", 0)); + + // Connecting + m_merger->p_in1(p_in_r); + m_merger->p_in2(p_in_c); + m_merger->p_out(m_merg_out); + + m_pcm->p_in(m_merg_out); + m_pcm->p_out(m_pcm_out); + + m_pd->p_in(m_pcm_out); + m_pd->p_readout(p_readout); +}
\ No newline at end of file diff --git a/src/devices/octane_cell.h b/src/devices/octane_cell.h new file mode 100644 index 0000000..aadcd20 --- /dev/null +++ b/src/devices/octane_cell.h @@ -0,0 +1,61 @@ +#pragma once + +#include <systemc.h> +#include <waveguide.h> +#include <pcm_device.h> +#include <merger.h> +#include <detector.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +/** The minimal octane cell composed of merger, waveguides, PCM and a detector. */ +/** For now, all photonic parameters will be internal and constant. The only */ +/** one that can be changed is the PCM levels.*/ + +//TODO: pass element's characteristics as argument, for instance, pass an example of waveguide, +// or an example of DC, PCM such that it copies. Requires: constructors in each element to build +// by copying. + +class OctaneCell : public sc_module { +public: + // Ports + /** The optical input ports. */ + sc_port<sc_signal_in_if<OpticalSignal>> p_in_r, p_in_c; + /** The electrical output port. */ + sc_out<double> p_readout; + + // Member variables + double m_meltEnergy; + bool m_isCompact; + int m_nStates; + + // Wires + sc_signal<OpticalSignal> m_merg_in1, m_merg_in2, m_merg_out, m_pcm_in, m_pcm_out, m_pd_in; + + // Member submodules + unique_ptr<Waveguide> m_wg1, m_wg2, m_wg3, m_wg4; + unique_ptr<Merger> m_merger; + unique_ptr<PCMElement> m_pcm; + unique_ptr<Detector> m_pd; + + // This function should be called right after + // Instantiation (either declaration or make_shared or new) + void init(); + void init_compact(); + void init_full(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param meltEnergy energy needed to amorphize by one step + * @param nStates number of internal states supported by the PCM + * */ + OctaneCell(sc_module_name name, double meltEnergy = 10e-12, int nStates = 63, bool isCompact = false) + : sc_module(name) + , m_meltEnergy(meltEnergy) + , m_isCompact(isCompact) + , m_nStates(nStates) + { + } + +}; diff --git a/src/devices/octane_matrix.cpp b/src/devices/octane_matrix.cpp new file mode 100644 index 0000000..d8126f3 --- /dev/null +++ b/src/devices/octane_matrix.cpp @@ -0,0 +1,365 @@ +#include <octane_matrix.h> + + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +#define __outname(SUFFIX, IDX, IDY) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX) + "_" + to_string(IDY)).c_str()) + +using std::size_t; +using std::pow; + +void OctaneMatrix::init_ports() +{ + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing input ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_in_r.clear(); + p_in_c.clear(); + + // cout << "Initializing input ports of " << name() << endl; + + for(size_t i = 0; i < m_rows; i++) + p_in_r.push_back(make_unique<port_in_type>(__modname("IN_R_", i))); + + for(size_t i = 0; i < m_columns; i++) + p_in_c.push_back(make_unique<port_in_type>(__modname("IN_C_", i))); + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing output port matrix + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_readout.clear(); // clearing before start + + // cout << "Initializing output ports of " << name() << endl; + + vector<unique_ptr<sc_out<double>>> temp_vector; + + for(size_t i = 0; i < m_rows; i++) + { + temp_vector.clear(); // moving to new row + for(size_t j = 0; j < m_columns; j++) + { + temp_vector.push_back(make_unique<sc_out<double>>(__outname("PD_", i, j))); + } + p_readout.push_back(std::move(temp_vector)); // adding new row + } + + // cout << "Successful port initialization of " << name() << endl; + +} + +void OctaneMatrix::init() +{ + + double crossing_loss = 0; + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing circuit elements (waveguides, DCs, octanecell) + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + m_optical_connect_horizontal.clear(); + m_optical_connect_vertical.clear(); + m_segment.clear(); + m_cross.clear(); + + // cout << "Initializing submodules of " << name() << endl; + + // Optical wires + unsigned long num_of_nets_horizontal = 3*m_rows*(m_columns-1); + unsigned long num_of_nets_vertical = (m_rows-1)*m_columns; + + for(unsigned long i = 0; i < num_of_nets_horizontal; i++) + m_optical_connect_horizontal.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("wh_", i))); + for(unsigned long i = 0; i < num_of_nets_vertical; i++) + m_optical_connect_vertical.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("wv_", i))); + + // Crossings + for(unsigned long i = 0; i < m_rows; i++) + { // Last column doesn't have crossings, thus the -1 in the loop + for(unsigned long j = 0; j < (m_columns - 1); j++) + { + m_cross.push_back(make_unique<Crossing>(__outname("X_", i, j), crossing_loss)); + } + } + + // Octane segments + bool term_row = false; + bool term_col = false; + double coupling_through_row = 0; + double coupling_through_col = 0; + + for(unsigned long i = 0; i < m_rows; i++) + { + for(unsigned long j = 0; j < m_columns; j++) + { + if(isLastRow(i) && isLastCol(j)) + { + // Last element + term_col = true; + term_row = true; + coupling_through_row = 0; + coupling_through_col = 0; + } + else if(isLastRow(i)) + { + // Term row + term_col = false; + term_row = true; + coupling_through_row = (1.0 - 1.0/(m_columns - j)); + coupling_through_col = 0; + } + else if(isLastCol(j)) + { + // Term col + term_col = true; + term_row = false; + coupling_through_row = 0; + coupling_through_col = (1.0 - 1.0/(m_rows - i)); + } + else + { + // Inner element + term_col = false; + term_row = false; + coupling_through_row = (1.0 - 1.0/(m_columns - j)); + coupling_through_col = (1.0 - 1.0/(m_rows - i)); + } + auto temp_segment = make_unique<OctaneSegment> + ( + __outname("SEG_", i, j), + term_row, term_col, + coupling_through_row, coupling_through_col, + m_meltEnergy, m_nStates, m_isCompact + ); + temp_segment->init(); // mandatory initialization + m_segment.push_back(std::move(temp_segment)); + } + } + + // cout << "Successful module initialization of " << name() << endl; + + // Modules are constructed in their vectors, time to connect ! + connect_submodules(); + +} + +void OctaneMatrix::connect_submodules() +{ + + // cout << "Connecting submodules of " << name() << endl; + + // If there is only one element in the matrix + // the pins are connected directly + if((m_rows == 1) && (m_columns == 1)) + { + // cout << name() << "contains only one element. " << endl; + + m_segment.at(0)->p_in[0]->bind(*p_in_r.at(0)); + m_segment.at(0)->p_in[1]->bind(*p_in_c.at(0)); + m_segment.at(0)->p_readout->bind(*p_readout[0][0]); + + // cout << "Successful connection of " << name() << endl; + return; + } + + // Column vector + if(m_columns == 1) + { + // cout << name() << "contains only one column. " << endl; + + for(unsigned long i = 0; i < m_rows; i++) + { + // in segment, in row is p_in[0] + m_segment.at(i)->p_in[0]->bind(*p_in_r.at(i)); + + if(isFirstRow(i)) // should connect to the port + m_segment.at(i)->p_in[1]->bind(*p_in_c.at(0)); + else // connects to intermediate net which is output of the previous block + m_segment.at(i)->p_in[1]->bind(*m_optical_connect_vertical.at(i-1)); + + if(!isLastRow(i)) // there's only one output in vector segment + m_segment.at(i)->p_out[0]->bind(*m_optical_connect_vertical.at(i)); + + m_segment.at(i)->p_readout->bind(*p_readout[i][0]); + + } + // cout << "Successful connection of " << name() << endl; + return; + } + + // Row vector + if(m_rows == 1) + { + // cout << name() << "contains only one row. " << endl; + + for(unsigned long j = 0; j < m_columns; j++) + { + if(isFirstCol(j)) + { + // in segment, in row is p_in[0] + m_segment.at(j)->p_in[0]->bind(*p_in_r.at(0)); + m_segment.at(j)->p_in[1]->bind(*m_optical_connect_horizontal.at(3*j+1)); + m_segment.at(j)->p_out[0]->bind(*m_optical_connect_horizontal.at(3*j)); + + // in1, out1 are horizontal connections + m_cross.at(j)->p_in1.bind(*m_optical_connect_horizontal.at(3*j)); + m_cross.at(j)->p_in2.bind(*p_in_c.at(j)); + m_cross.at(j)->p_out1.bind(*m_optical_connect_horizontal.at(3*j+2)); + m_cross.at(j)->p_out2.bind(*m_optical_connect_horizontal.at(3*j+1)); + } + else if(!isLastCol(j)) + { // middle element + m_segment.at(j)->p_in[0]->bind(*m_optical_connect_horizontal.at(3*j-1)); + m_segment.at(j)->p_in[1]->bind(*m_optical_connect_horizontal.at(3*j+1)); + m_segment.at(j)->p_out[0]->bind(*m_optical_connect_horizontal.at(3*j)); + + // in1, out1 are horizontal connections + m_cross.at(j)->p_in1.bind(*m_optical_connect_horizontal.at(3*j)); + m_cross.at(j)->p_in2.bind(*p_in_c.at(j)); + m_cross.at(j)->p_out1.bind(*m_optical_connect_horizontal.at(3*j+2)); + m_cross.at(j)->p_out2.bind(*m_optical_connect_horizontal.at(3*j+1)); + } + else + { // last column here + m_segment.at(j)->p_in[0]->bind(*m_optical_connect_horizontal.at(3*j-1)); + m_segment.at(j)->p_in[1]->bind(*p_in_c.at(j)); + } + m_segment.at(j)->p_readout->bind(*p_readout[0][j]); + } + // cout << "Successful connection of " << name() << endl; + return; + } + + // Getting here means it's an actual matrix with more than one element + unsigned long cur_pos; + unsigned long cur_cross; + unsigned long seg_out_h; + unsigned long seg_out_v; + for(unsigned long i = 0; i < m_rows; i++) + { + for(unsigned long j = 0; j < m_columns; j++) + { + cur_pos = m_columns*i + j; + cur_cross = (m_columns-1)*i + j; + seg_out_h = 3*(m_columns-1)*i+3*j; + seg_out_v = m_columns*i+j; + // cout << "cur_pos: " << cur_pos << endl; + // cout << "cur_cross: " << cur_cross << endl; + // cout << "out_h: " << seg_out_h << endl; + // cout << "out_v: " << seg_out_v << endl; + + if(isFirstRow(i) && isFirstCol(j)) + { + // cout << "first element" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*p_in_r.at(i)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_segment.at(cur_pos)->p_out[1]->bind(*m_optical_connect_vertical.at(seg_out_v)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*p_in_c.at(j)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(isFirstRow(i) && !isLastCol(j)) + { + // cout << "first row not last col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_segment.at(cur_pos)->p_out[1]->bind(*m_optical_connect_vertical.at(seg_out_v)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*p_in_c.at(j)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(isFirstRow(i) && isLastCol(j)) + { + // cout << "first row last col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*p_in_c.at(j)); + // no row output + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_vertical.at(seg_out_v)); + } + else if(!isFirstRow(i) && !isLastRow(i) && isFirstCol(j)) + { + // cout << "middle row first col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*p_in_r.at(i)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_segment.at(cur_pos)->p_out[1]->bind(*m_optical_connect_vertical.at(seg_out_v)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(!isFirstRow(i) && !isLastRow(i) && !isLastCol(j)) + { + // cout << "middle row middle col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_segment.at(cur_pos)->p_out[1]->bind(*m_optical_connect_vertical.at(seg_out_v)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(!isFirstRow(i) && !isLastRow(i) && isLastCol(j)) + { + // cout << "middle row last col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + // no row output + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_vertical.at(seg_out_v)); + } + else if(isLastRow(i) && isFirstCol(j)) + { + // cout << "last row first col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*p_in_r.at(i)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(isLastRow(i) && !isLastCol(j)) + { + // cout << "last row middle col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(isLastRow(i) && isLastCol(j)) + { + // cout << "last row last col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + } + else + cout<< endl << endl << "This condition should not appear" << endl << endl; + + m_segment.at(cur_pos)->p_readout->bind(*p_readout[i][j]); + + } + } +}
\ No newline at end of file diff --git a/src/devices/octane_matrix.h b/src/devices/octane_matrix.h new file mode 100644 index 0000000..afcf06a --- /dev/null +++ b/src/devices/octane_matrix.h @@ -0,0 +1,99 @@ +#pragma once + +#include <systemc.h> +#include <octane_segment.h> +#include <crossing.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +// TODO: remove these (are in spx_module.h) +typedef sc_signal_in_if<OpticalSignal > if_in_type; +typedef sc_signal_out_if<OpticalSignal > if_out_type; +typedef sc_port<if_in_type> port_in_type; +typedef sc_port<if_out_type> port_out_type; + + +/** Building block for the OCTANE circuit + * The current implementation includes all + * intermediate components allowing modelling of imperfections, + * but at the cost of increased module count. + * + * A simpler way would be to disregard all intermediate waveguides + * and model their effects on `octane_cell` + * + * Maybe I could add an argument to init(bool isCompact), defining + * if the model will be compact or not. +*/ + +//TODO: pass element's characteristics as argument, for instance, pass an example of waveguide, +// or an example of DC, PCM such that it copies. Requires: constructors in each element to build +// by copying. + +class OctaneMatrix : public sc_module { +public: + // Ports + /** The optical input ports. */ + vector<unique_ptr<port_in_type>> p_in_r; + vector<unique_ptr<port_in_type>> p_in_c; + + /** The optical output ports. */ + vector<unique_ptr<port_out_type>> p_out; + + /** The electric output ports in matrix format*/ + // TODO: verify if a matrix library should be used + vector<vector<unique_ptr<sc_out<double>>>> p_readout; + + // Define the size of the matrix + std::size_t m_rows; + std::size_t m_columns; + + bool m_isCompact; + + // For internal octane_cell + double m_meltEnergy; + int m_nStates; + + // Wires + vector<unique_ptr<sc_signal<OpticalSignal>>> m_optical_connect_horizontal; + vector<unique_ptr<sc_signal<OpticalSignal>>> m_optical_connect_vertical; + + // Member submodules + vector<unique_ptr<Crossing>> m_cross; + vector<unique_ptr<OctaneSegment>> m_segment; + + //Init() must be called after declaration + void init(); + void init_ports(); + void connect_submodules(); + + // auxiliar functions for readability + // they were written with 0 to N-1 indexing in mind! + inline bool isFirstRow(unsigned long i){return (i==0);} + bool isFirstCol(unsigned long j){return (j==0);} + bool isLastRow(unsigned long i){return (i==m_rows-1);} + bool isLastCol(unsigned long j){return (j==m_columns-1);} + + /** Constructor for Octane Matrix + * + * @param name name of the module + * @param meltEnergy energy needed to amorphize by one step + * @param nStates number of internal states supported by the PCM + * */ + OctaneMatrix(sc_module_name name, std::size_t rows, std::size_t columns, + double meltEnergy = 10e-12, int nStates = 63, bool isCompact = false) + : sc_module(name) + , m_rows(rows) + , m_columns(columns) + , m_isCompact(isCompact) + , m_meltEnergy(meltEnergy) + , m_nStates(nStates) + { + + assert(rows > 0); + assert(columns > 0); + + init_ports(); // At least the ports should always be + // in the constructor + } + +}; diff --git a/src/devices/octane_segment.cpp b/src/devices/octane_segment.cpp new file mode 100644 index 0000000..32dcd84 --- /dev/null +++ b/src/devices/octane_segment.cpp @@ -0,0 +1,396 @@ +#include <octane_segment.h> + + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +using std::size_t; +using std::pow; + +void OctaneSegment::init_ports() +{ + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing input ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_in.clear(); + + // cout << "Initializing input ports of " << name() << endl; + // 0 is row, 1 is column + p_in.push_back(make_unique<port_in_type>("IN_R")); + p_in.push_back(make_unique<port_in_type>("IN_C")); + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing output ports based on termination + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_out.clear(); + + // cout << "Initializing output ports of " << name() << endl; + // If there is only one output, the vector has only one element + if (m_term_row && m_term_col) + { + ; // no optical output + } + else if(m_term_row) + { + // Last row, so only has row outputs + p_out.push_back(make_unique<port_out_type>("OUT_R")); + } + else if(m_term_col) + { + // Last column, so only has only column outputs + p_out.push_back(make_unique<port_out_type>("OUT_C")); + } + else + { + // Internal elements have both outputs + p_out.push_back(make_unique<port_out_type>("OUT_R")); + p_out.push_back(make_unique<port_out_type>("OUT_C")); + } + p_readout = make_unique<sc_out<double>>("OUT_PD"); + + // cout << "Successful port initialization of " << name() << endl; + +} + +void OctaneSegment::init() +{ + if(m_isCompact) + init_compact(); + else + init_full(); + +} + +void OctaneSegment::init_full() +{ + // Physical parameters of the components + double lambda = 1550e-9; // the wavelength used by the circuit + double neff = 2.2; + double ng = 2.2; + double loss_per_cm = 0; // in waveguide + double dc_loss = 0; + double length_for_2pi = 100*lambda/(neff); + double length_for_pi_2 = length_for_2pi/4; + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing circuit elements (waveguides, DCs, octanecell) + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + m_wg.clear(); + m_dc.clear(); + m_optical_connect.clear(); + + // cout << "Initializing submodules of " << name() << endl; + + m_oct = make_unique<OctaneCell>(__modname("OCT_", 0), m_meltEnergy, m_nStates, m_isCompact); + m_oct->init(); + + if (m_term_row && m_term_col) + { + // Last element + for(size_t i = 0; i < 2; i++) + { + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", i), length_for_2pi, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", i))); + } + } + else if(m_term_row) + { + // Last row + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_R_", 0), m_coupling_through_row, dc_loss)); + for(size_t i = 0; i < 4; i++) + { + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", i), length_for_2pi, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", i))); + } + // Adjusting the last waveguide to give pi/2 shift + // compensating for the lack of DC + m_wg.at(3)->m_length_cm = length_for_2pi + length_for_pi_2; + // We have one extra wire with respect to waveguides + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 4))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + } + else if(m_term_col) + { + // Last column + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_C_", 0), m_coupling_through_col, dc_loss)); + for(size_t i = 0; i < 4; i++) + { + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", i), length_for_2pi, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", i))); + } + // Adjusting the last waveguide to give pi/2 shift + // compensating for the lack of DC + m_wg.at(3)->m_length_cm = length_for_2pi + length_for_pi_2; + // We have one extra wire with respect to waveguides + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 4))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + } + else + { + // Internal element + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_R_", 0), m_coupling_through_row, dc_loss)); + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_C_", 0), m_coupling_through_col, dc_loss)); + for(size_t i = 0; i < 6; i++) + { + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", i), length_for_2pi, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", i))); + } + // We have two extra wires with respect to waveguides + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 6))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 7))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 1))); // terminator + } + + // cout << "Successful module initialization of " << name() << endl; + + // Modules are constructed in their vectors, time to connect ! + connect_submodules_full(); + +} + +void OctaneSegment::connect_submodules_full() +{ + + // cout << "Connecting submodules of " << name() << endl; + + if (m_term_row && m_term_col) + { + // Last element + m_wg.at(0)->p_in(*p_in.at(0)); + m_wg.at(0)->p_out(*m_optical_connect.at(0)); + + m_wg.at(1)->p_in(*p_in.at(1)); + m_wg.at(1)->p_out(*m_optical_connect.at(1)); + + m_oct->p_in_r(*m_optical_connect.at(0)); + m_oct->p_in_c(*m_optical_connect.at(1)); + + } + else if(m_term_row) + { + // Last row + m_wg.at(0)->p_in(*p_in.at(0)); // input at row + m_wg.at(0)->p_out(*m_optical_connect.at(0)); + + m_dc.at(0)->p_in1(*m_optical_connect.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(5)); // Terminated + m_dc.at(0)->p_out1(*m_optical_connect.at(1)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(2)); // Cross + + m_wg.at(1)->p_in(*m_optical_connect.at(1)); + m_wg.at(1)->p_out(*p_out.at(0)); // Out row + + m_wg.at(2)->p_in(*m_optical_connect.at(2)); + m_wg.at(2)->p_out(*m_optical_connect.at(3)); + + // Side without DC (must be conmpensated with pi/2) + m_wg.at(3)->p_in(*p_in.at(1)); + m_wg.at(3)->p_out(*m_optical_connect.at(4)); + + m_oct->p_in_r(*m_optical_connect.at(3)); + m_oct->p_in_c(*m_optical_connect.at(4)); + } + else if(m_term_col) + { + // Last column + m_wg.at(0)->p_in(*p_in.at(1)); // input at col + m_wg.at(0)->p_out(*m_optical_connect.at(0)); + + m_dc.at(0)->p_in1(*m_optical_connect.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(5)); // Terminated + m_dc.at(0)->p_out1(*m_optical_connect.at(1)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(2)); // Cross + + m_wg.at(1)->p_in(*m_optical_connect.at(1)); + m_wg.at(1)->p_out(*p_out.at(0)); // Out column + + m_wg.at(2)->p_in(*m_optical_connect.at(2)); + m_wg.at(2)->p_out(*m_optical_connect.at(3)); + + // Side without DC (must be conmpensated with pi/2) + m_wg.at(3)->p_in(*p_in.at(0)); + m_wg.at(3)->p_out(*m_optical_connect.at(4)); + + m_oct->p_in_r(*m_optical_connect.at(4)); + m_oct->p_in_c(*m_optical_connect.at(3)); + } + else + { + // Internal element + m_wg.at(0)->p_in(*p_in.at(0)); // input at row + m_wg.at(0)->p_out(*m_optical_connect.at(0)); + + m_dc.at(0)->p_in1(*m_optical_connect.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(8)); // Terminated + m_dc.at(0)->p_out1(*m_optical_connect.at(1)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(2)); // Cross + + m_wg.at(1)->p_in(*m_optical_connect.at(1)); + m_wg.at(1)->p_out(*p_out.at(0)); // Out row + + m_wg.at(2)->p_in(*m_optical_connect.at(2)); + m_wg.at(2)->p_out(*m_optical_connect.at(3)); + + m_wg.at(3)->p_in(*p_in.at(1)); + m_wg.at(3)->p_out(*m_optical_connect.at(4)); + + m_dc.at(1)->p_in1(*m_optical_connect.at(4)); + m_dc.at(1)->p_in2(*m_optical_connect.at(9)); // Terminated + m_dc.at(1)->p_out1(*m_optical_connect.at(5)); // Through + m_dc.at(1)->p_out2(*m_optical_connect.at(6)); // Cross + + m_wg.at(4)->p_in(*m_optical_connect.at(5)); + m_wg.at(4)->p_out(*p_out.at(1)); // Out col + + m_wg.at(5)->p_in(*m_optical_connect.at(6)); + m_wg.at(5)->p_out(*m_optical_connect.at(7)); + + m_oct->p_in_r(*m_optical_connect.at(3)); + m_oct->p_in_c(*m_optical_connect.at(7)); + } + m_oct->p_readout(*p_readout); + + // cout << "Successful connection of " << name() << endl; + +} + +void OctaneSegment::init_compact() +{ + // Physical parameters of the components + double lambda = 1550e-9; // the wavelength used by the circuit + double neff = 2.2; + double ng = 2.2; + double loss_per_cm = 0; // in waveguide + double dc_loss = 0; + double length_for_2pi = 100*lambda/(neff); + double length_for_pi_2 = length_for_2pi/4; + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing circuit elements (waveguides, DCs, octanecell) + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + m_wg.clear(); + m_dc.clear(); + m_optical_connect.clear(); + + // cout << "Initializing submodules of " << name() << endl; + + m_oct = make_unique<OctaneCell>(__modname("OCT_", 0), m_meltEnergy, m_nStates, m_isCompact); + m_oct->init(); + + if (m_term_row && m_term_col) + { + ; + } + else if(m_term_row) + { + // Last row + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_R_", 0), m_coupling_through_row, dc_loss)); + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), length_for_pi_2, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 0))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 1))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + } + else if(m_term_col) + { + // Last column + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_C_", 0), m_coupling_through_col, dc_loss)); + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), length_for_pi_2, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 0))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 1))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + } + else + { + // Internal element + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_R_", 0), m_coupling_through_row, dc_loss)); + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_C_", 0), m_coupling_through_col, dc_loss)); + // We have two extra wires with respect to waveguides + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 0))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 1))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 1))); // terminator + } + + // cout << "Successful module initialization of " << name() << endl; + + // Modules are constructed in their vectors, time to connect ! + connect_submodules_compact(); + +} + +void OctaneSegment::connect_submodules_compact() +{ + + // cout << "Connecting submodules of " << name() << endl; + + if (m_term_row && m_term_col) + { + // Last element + m_oct->p_in_r(*p_in.at(0)); + m_oct->p_in_c(*p_in.at(1)); + + } + else if(m_term_row) + { + // Last row + m_dc.at(0)->p_in1(*p_in.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(2)); // Terminated + m_dc.at(0)->p_out1(*p_out.at(0)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(0)); // Cross + + // Side without DC (must be conmpensated with pi/2) + m_wg.at(0)->p_in(*p_in.at(1)); + m_wg.at(0)->p_out(*m_optical_connect.at(1)); + + m_oct->p_in_r(*m_optical_connect.at(0)); + m_oct->p_in_c(*m_optical_connect.at(1)); + } + else if(m_term_col) + { + // Last column + m_dc.at(0)->p_in1(*p_in.at(1)); + m_dc.at(0)->p_in2(*m_optical_connect.at(2)); // Terminated + m_dc.at(0)->p_out1(*p_out.at(0)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(0)); // Cross + + // Side without DC (must be conmpensated with pi/2) + m_wg.at(0)->p_in(*p_in.at(0)); + m_wg.at(0)->p_out(*m_optical_connect.at(1)); + + m_oct->p_in_r(*m_optical_connect.at(1)); + m_oct->p_in_c(*m_optical_connect.at(0)); + } + else + { + // Internal element + m_dc.at(0)->p_in1(*p_in.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(2)); // Terminated + m_dc.at(0)->p_out1(*p_out.at(0)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(0)); // Cross + + m_dc.at(1)->p_in1(*p_in.at(1)); + m_dc.at(1)->p_in2(*m_optical_connect.at(3)); // Terminated + m_dc.at(1)->p_out1(*p_out.at(1)); // Through + m_dc.at(1)->p_out2(*m_optical_connect.at(1)); // Cross + + m_oct->p_in_r(*m_optical_connect.at(0)); + m_oct->p_in_c(*m_optical_connect.at(1)); + } + m_oct->p_readout(*p_readout); + + // cout << "Successful connection of " << name() << endl; + +}
\ No newline at end of file diff --git a/src/devices/octane_segment.h b/src/devices/octane_segment.h new file mode 100644 index 0000000..ea28f03 --- /dev/null +++ b/src/devices/octane_segment.h @@ -0,0 +1,98 @@ +#pragma once + +#include <systemc.h> +#include <waveguide.h> +#include <directional_coupler.h> +#include <octane_cell.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +// TODO: remove these (are in spx_module.h) +typedef sc_signal_in_if<OpticalSignal > if_in_type; +typedef sc_signal_out_if<OpticalSignal > if_out_type; +typedef sc_port<if_in_type> port_in_type; +typedef sc_port<if_out_type> port_out_type; + + +/** Building block for the OCTANE circuit + * The current implementation includes all + * intermediate components allowing modelling of imperfections, + * but at the cost of increased module count. + * + * A simpler way would be to disregard all intermediate waveguides + * and model their effects on `octane_cell` + * + * Maybe I could add an argument to init(bool isCompact), defining + * if the model will be compact or not. +*/ + +//TODO: pass element's characteristics as argument, for instance, pass an example of waveguide, +// or an example of DC, PCM such that it copies. Requires: constructors in each element to build +// by copying. + +class OctaneSegment : public sc_module { +public: + // Ports + /** The optical input ports. */ + vector<unique_ptr<port_in_type>> p_in; + + /** The optical output ports. */ + vector<unique_ptr<port_out_type>> p_out; + + /** The electric output port*/ + unique_ptr<sc_out<double>> p_readout; + + // Member variables + // Defines the number of ports and elements + bool m_term_row; + bool m_term_col; + bool m_isCompact; + + // Defines the coupling in the DCs (should change as more elements are added) + double m_coupling_through_row; + double m_coupling_through_col; + + // For internal octane_cell + double m_meltEnergy; + int m_nStates; + + + // Wires + vector<unique_ptr<sc_signal<OpticalSignal>>> m_optical_connect; + + // Member submodules + vector<unique_ptr<Waveguide>> m_wg; + vector<unique_ptr<DirectionalCoupler>> m_dc; + unique_ptr<OctaneCell> m_oct; + + // init() should be called after declaration + void init(); + void init_compact(); + void init_full(); + void init_ports(); + void connect_submodules_full(); + void connect_submodules_compact(); + + /** Constructor for Octane Segment + * + * @param name name of the module + * @param meltEnergy energy needed to amorphize by one step + * @param nStates number of internal states supported by the PCM + * */ + OctaneSegment(sc_module_name name, bool term_row, bool term_col, + double coupling_through_row, double coupling_through_col, + double meltEnergy, int nStates, bool isCompact = false) + : sc_module(name) + , m_term_row(term_row) + , m_term_col(term_col) + , m_isCompact(isCompact) + , m_coupling_through_row(coupling_through_row) + , m_coupling_through_col(coupling_through_col) + , m_meltEnergy(meltEnergy) + , m_nStates(nStates) + { + init_ports(); // At least the ports should always be + // in the constructor + } + +}; diff --git a/src/devices/pcm_device.cpp b/src/devices/pcm_device.cpp new file mode 100644 index 0000000..dc018da --- /dev/null +++ b/src/devices/pcm_device.cpp @@ -0,0 +1,150 @@ +#include <algorithm> +#include <iostream> + +#include <pcm_device.h> + +#define ZERO_PULSE_THRESHOLD (1e-10) + +using namespace std; + +void PCMElement::on_input_changed() +{ + // setting the transmission for the initial state + update_transmission_local(); + + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "Emelt = " << m_meltEnergy*1e9 << " nJ" << endl; + cout << "Nstates = " << m_nStates << "" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name(); + cout << endl; + cout << endl; + } + + m_last_pulse_power = 0; + bool in_window = false; + vector<pulse_sample_t> vector_of_samples; + uint32_t cur_wavelength_id; + m_memory_in[0] = 0; + double total_in_power = 0; + + while (true) { + // Wait next input change + wait(); + + // Read signal and store field in memory for that wavelength + auto s = p_in->read(); + cur_wavelength_id = s.m_wavelength_id; + m_memory_in[cur_wavelength_id] = s.m_field; + + // Summing powers of all wavelengths + total_in_power = 0; + for (auto lambdaID_field : m_memory_in) + { + total_in_power += norm(lambdaID_field.second); + } + + // Storage of optical state on the input + const sc_time &now = sc_time_stamp(); + + bool last_was_zero = m_last_pulse_power < ZERO_PULSE_THRESHOLD; + bool current_is_zero = total_in_power < ZERO_PULSE_THRESHOLD; + bool rising_edge = last_was_zero && !current_is_zero; + if (!rising_edge) + { + // Enter here when: + // - a signal is received on top of another one (pulse intersect) + // - a signal ends (power falls to 0) + + // Record the event + vector_of_samples.emplace_back(now.to_seconds(), total_in_power); + } + else + { + // Enter here when: + // - a signal starts (power rises from 0 to non-0 value) + // This case is necessarily the first rise + + // Will unroll events and check if there was a phase change before according to + // the vector and advance the state accordingly to get the transmission + // cout << "\t\t first rise detected" << endl; + in_window = phase_change(vector_of_samples, true); + if (!in_window) + vector_of_samples.clear(); + + // Record the current event + vector_of_samples.emplace_back(now.to_seconds(), total_in_power); + } + + m_last_pulse_power = total_in_power; + + // Write attenuated signal to output + s *= m_transmission; + s.getNewId(); + + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + } +} + +bool PCMElement::phase_change(const vector<pulse_sample_t> &samples, const bool &local) +{ + // Here should decide between sending the + // sim off to external or resolving inside + + // Should set new values for m_state and m_transmission + + double energy_absorbed = 0; + double dur_pulse = 0; + // will return true if we shouldn't erase the vector + bool in_influence_window = false; + + if (samples.empty()) + return true; + + if (sc_time_stamp().to_seconds() - samples.back().first < influence_time_ns*1e-9) + return true; + + if (local) + { + // Integrating power along each duration + // pulse 0 stays at pow[0] for tim[1]-tim[0] seconds + // the last sample is zero in power, just here to get the final time + for (unsigned int i = 0; i < samples.size()-1; i++) + { + dur_pulse = samples[i+1].first - samples[i].first; + energy_absorbed += samples[i].second * dur_pulse; + } + // if enough energy and not saturated + if ((energy_absorbed > m_meltEnergy) && (m_state < m_nStates)) + { + // cout << sc_time_stamp() << ": \n\t" << name() << " phase changed with " + // << energy_absorbed*1e6 << " uJ" << endl; + m_state++; + // cout << "\tnew state: " << m_state << endl; + update_transmission_local(); + } + } + else // external phase change simulation + { + // send the simulation to external software + cout << "TODO: implement handles to external sim" + << endl; + } + return in_influence_window; +} + +void PCMElement::update_transmission_local() +{ + double sp = 0.85; + double ep = 0.95; + double speed = 3; + m_transmission = sp+(ep-sp)*tanh(speed*m_state/m_nStates); + // cout << "\tnew trans_power: " << m_transmission << "- W/W" << endl; + + // For field need to do a square root + m_transmission = sqrt(m_transmission); +} diff --git a/src/devices/pcm_device.h b/src/devices/pcm_device.h new file mode 100644 index 0000000..4510591 --- /dev/null +++ b/src/devices/pcm_device.h @@ -0,0 +1,60 @@ +#pragma once + +#include <vector> + +#include <systemc.h> + +#include <spx_module.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +#include "specs.h" +#include "spx_module.h" + +class PCMElement : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in; + spx::oa_port_out_type p_out; + + // Timed ports writers + OpticalOutputPort m_out_writer; + + // Auxiliary struct - stores tuples of value-time + typedef pair<double,double> pulse_sample_t; + + // Member variables + double m_meltEnergy; + int m_state; + int m_nStates; + double m_transmission = 1; + double influence_time_ns = 1; + + /** Current information about the optical input, before any attenuation and phase shift. */ + double m_last_pulse_power; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in; + + + // Processes + void on_input_changed(); + bool phase_change(const vector<pulse_sample_t> &vec, const bool &local); + void update_transmission_local(); + + + // Constructor + PCMElement(sc_module_name name, + double meltEnergy = 0, + int nStates = 0, + int state = 0) + : spx_module(name) + , m_out_writer("out_delayed_writer", p_out) + , m_meltEnergy(meltEnergy) + , m_state(state) + , m_nStates(nStates) + { + SC_HAS_PROCESS(PCMElement); + + SC_THREAD(on_input_changed); + sensitive << p_in; + } +}; diff --git a/src/devices/phaseshifter.cpp b/src/devices/phaseshifter.cpp new file mode 100644 index 0000000..620243d --- /dev/null +++ b/src/devices/phaseshifter.cpp @@ -0,0 +1,198 @@ +#include "specs.h" +#include <phaseshifter.h> + +using namespace std; + +void PhaseShifterUni::on_port_in_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + m_memory_in[0] = 0; // initializing for nan wavelength + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission_power = " << pow(transmission_field,2) << " W/W" << endl; + cout << "sensitivity = " << m_sensitivity << " rad/V" << endl; + cout << "initial phase delay = " << m_phaseshift_rad << " rad @1.55)" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name(); + cout << endl << "\t^" << endl; + cout << "\t|" << endl << "\t"; + cout << (dynamic_cast<spx::ea_signal_type *>(p_vin.get_interface()))->name(); + cout << endl; + cout << endl; + } + while (true) { + // Wait for a new input signal + wait(); + + // Read the input signal and apply attenuation + OpticalSignal s = p_in->read(); + + + // Store field value in memory + m_memory_in[s.m_wavelength_id] = s.m_field; + + // Get new ID for output event + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_sensitivity * p_vin->read()); + + // Write to ouput port after zero delay + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + } +} + +void PhaseShifterUni::on_port_vin_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + + uint32_t cur_wavelength_id = 0; + OpticalSignal::field_type cur_field = (complex<double>)0; + + while (true) + { + wait(); + // Read the new phase-delay + m_phaseshift_rad = m_sensitivity * p_vin->read(); + + // writes the signals of all wavelengths with the new phase shift + for(auto id_field : m_memory_in) + { + cur_wavelength_id = id_field.first; + cur_field = id_field.second; + auto s = OpticalSignal(cur_field, cur_wavelength_id); + + // Get a new ID for the signal + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_phaseshift_rad); + + // Write to ouput port after zero delay + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + } + } +} + + +void PhaseShifterBi::on_p0_in_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + m_memory_p0[0] = 0; // initializing for nan wavelength + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission_power = " << pow(transmission_field,2) << " W/W" << endl; + cout << "sensitivity = " << m_sensitivity << " rad/V" << endl; + cout << "initial phase delay = " << m_phaseshift_rad << " rad @1.55)" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_out.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_out.get_interface()))->name(); + cout << " <-- "; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_in.get_interface()))->name(); + cout << endl << "\t^" << endl; + cout << "\t|" << endl << "\t"; + cout << (dynamic_cast<spx::ea_signal_type *>(p_vin.get_interface()))->name(); + cout << endl; + cout << endl; + } + while (true) { + // Wait for a new input signal + wait(); + + // Read the input signal and apply attenuation + OpticalSignal s = p0_in->read(); + + + // Store field value in memory + m_memory_p0[s.m_wavelength_id] = s.m_field; + + // Get new ID for output event + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_sensitivity * p_vin->read()); + + // Write to ouput port after zero delay + m_p1_writer.delayedWrite(s, SC_ZERO_TIME); + } +} + +void PhaseShifterBi::on_p1_in_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + m_memory_p1[0] = 0; // initializing for nan wavelength + + while (true) { + // Wait for a new input signal + wait(); + + // Read the input signal and apply attenuation + OpticalSignal s = p1_in->read(); + + // Store field value in memory + m_memory_p1[s.m_wavelength_id] = s.m_field; + + // Get new ID for output event + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_sensitivity * p_vin->read()); + + // Write to ouput port after zero delay + m_p0_writer.delayedWrite(s, SC_ZERO_TIME); + } +} + +void PhaseShifterBi::on_port_vin_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + + uint32_t cur_wavelength_id = 0; + OpticalSignal::field_type cur_field = (complex<double>)0; + + while (true) + { + wait(); + // Read the new phase-delay + m_phaseshift_rad = m_sensitivity * p_vin->read(); + + // writes the signals of all wavelengths with the new phase shift + for(auto id_field : m_memory_p0) + { + cur_wavelength_id = id_field.first; + cur_field = id_field.second; + auto s = OpticalSignal(cur_field, cur_wavelength_id); + + // Get a new ID for the signal + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_phaseshift_rad); + + // Write to ouput port after zero delay + m_p1_writer.delayedWrite(s, SC_ZERO_TIME); + } + for(auto id_field : m_memory_p1) + { + cur_wavelength_id = id_field.first; + cur_field = id_field.second; + auto s = OpticalSignal(cur_field, cur_wavelength_id); + + // Get a new ID for the signal + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_phaseshift_rad); + + // Write to ouput port after zero delay + m_p0_writer.delayedWrite(s, SC_ZERO_TIME); + } + } +} diff --git a/src/devices/phaseshifter.h b/src/devices/phaseshifter.h new file mode 100644 index 0000000..04ac803 --- /dev/null +++ b/src/devices/phaseshifter.h @@ -0,0 +1,166 @@ +#pragma once + +#include "specs.h" +#include <systemc.h> + +#include <spx_module.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +/** An electrically-controllable phase shifter. */ +class PhaseShifterBase : public spx_module { +public: + // Member variables + /** The phaseshift applied to the pulse, in rad. */ + double m_phaseshift_rad = 0; + + /** The attenuation of the device in dB. */ + double m_attenuation_dB; + + /** The responsivity of the phase-shifter in rad/V. */ + double m_sensitivity = 1; + + /** Constructor for PhaseShifter + * + * @param name name of the module + * */ + PhaseShifterBase(sc_module_name name, double attenuation_dB = 0) + : spx_module(name) + , m_phaseshift_rad(0) + , m_attenuation_dB(attenuation_dB) + {} +}; + +class PhaseShifterUni : public PhaseShifterBase { +public: + // Ports + /** The electrical input port. */ + spx::ea_port_in_type p_vin; + /** The optical input port. */ + spx::oa_port_in_type p_in; + /** The optical output port. */ + spx::oa_port_out_type p_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out_writer; + + /** Memory of the current input value for all wavelengths. */ + std::map<uint32_t,OpticalSignal::field_type> m_memory_in; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * Will use the current m_phaseshift_rad at that moment. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p_in + * */ + void on_port_in_changed(); + + /** Auxiliary process of the module. + * + * Updates @param m_phaseshift_rad and creates a new event + * using the current optical input, sending it to the output + * after zero time. + * + * **SystemC type:** thread + * + * **Sensitivity list:** v_in + * */ + void on_port_vin_changed(); + + /** Constructor for PhaseShifter + * + * @param name name of the module + * */ + PhaseShifterUni(sc_module_name name, double attenuation_dB = 0) + : PhaseShifterBase(name, attenuation_dB) + , m_out_writer("out_delayed_writer", p_out) + { + SC_HAS_PROCESS(PhaseShifterUni); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + SC_THREAD(on_port_vin_changed); + sensitive << p_vin; + } +}; + +typedef PhaseShifterUni PhaseShifter; + +class PhaseShifterBi : public PhaseShifterBase { +public: + // Ports + /** The electrical input port. */ + spx::ea_port_in_type p_vin; + + /** The optical input ports. */ + spx::oa_port_in_type p0_in; + spx::oa_port_in_type p1_in; + + /** The optical output ports. */ + spx::oa_port_out_type p0_out; + spx::oa_port_out_type p1_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_p0_writer; + OpticalOutputPort m_p1_writer; + + /** Memory of the current input value for all wavelengths. */ + std::map<uint32_t,OpticalSignal::field_type> m_memory_p0; + std::map<uint32_t,OpticalSignal::field_type> m_memory_p1; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * Will use the current m_phaseshift_rad at that moment. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p0_in + * */ + void on_p0_in_changed(); + void on_p1_in_changed(); + + /** Auxiliary process of the module. + * + * Updates @param m_phaseshift_rad and creates a new event + * using the current optical input, sending it to the output + * after zero time. + * + * **SystemC type:** thread + * + * **Sensitivity list:** v_in + * */ + void on_port_vin_changed(); + + /** Constructor for PhaseShifter + * + * @param name name of the module + * */ + PhaseShifterBi(sc_module_name name, double attenuation_dB = 0) + : PhaseShifterBase(name, attenuation_dB) + , m_p0_writer("p0_out_delayed_writer", p0_out) + , m_p1_writer("p1_out_delayed_writer", p1_out) + { + SC_HAS_PROCESS(PhaseShifterBi); + + SC_THREAD(on_p0_in_changed); + sensitive << p0_in; + SC_THREAD(on_p1_in_changed); + sensitive << p1_in; + SC_THREAD(on_port_vin_changed); + sensitive << p_vin; + } +}; diff --git a/src/devices/probe.cpp b/src/devices/probe.cpp new file mode 100644 index 0000000..f9054e9 --- /dev/null +++ b/src/devices/probe.cpp @@ -0,0 +1,164 @@ +#include <ios> +#include <probe.h> +#include <cstdlib> // system() + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + + +void Probe::on_port_in_changed() +{ + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "Signal: " << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name() << endl; + cout << "trace power: " << m_trace_power << endl; + cout << "trace modulus: " << m_trace_modulus << endl; + cout << "trace phase: " << m_trace_phase << endl; + cout << "trace wavelength: " << m_trace_wavelength << endl; + cout << endl; + } + + while (true) { + // Wait for a new input signal + wait(); + + // Check if enabled first + if (!enable.read().to_bool()) + { + wait(enable.posedge_event()); + continue; + } + + auto &s = p_in->read(); + // cout << name() << ": " << s << endl; + if (!isnan(s.getWavelength())) + { + if (m_trace_power) + m_trace_sig_power.write(s.power()); + if (m_trace_modulus) + m_trace_sig_modulus.write(s.modulus()); + if (m_trace_phase) + m_trace_sig_phase.write(s.phase()); + if (m_trace_wavelength) + // m_trace_sig_wavelength.write(299792458.0 / s.m_wavelength); + m_trace_sig_wavelength.write(s.getWavelength()); + } + } +} + +void PowerProbe::on_port_in_changed() +{ + //m_trace_sig.write(0); + + while (true) { + // Wait for a new input signal + wait(); + auto &s = p_in->read(); + if (!isnan(s.getWavelength())) + m_trace_sig_power.write(s.power()); + } +} + +void PhaseProbe::on_port_in_changed() +{ + //m_trace_sig.write(0); + + while (true) { + // Wait for a new input signal + wait(); + auto &s = p_in->read(); + if (!isnan(s.getWavelength())) + m_trace_sig_phase.write(s.phase()); + } +} + +//__modname(SUFFIX, IDX) +void MLambdaProbe::setTraceFile(sc_trace_file *Tf) +{ + m_Tf = Tf; + if (!m_Tf) + return; + + unique_ptr<sc_signal<double>> temp_signal; + std::vector<unique_ptr<sc_signal<double>>> temp_vector; + + // for each wavelength, create three signals: + // power, abs, phase, that will monitor these characteristics in + // the input optical signals that arrive + int i = 0; + for(const auto &wl : m_lambdas) + { + stringstream ss; + // int wl_nm = floor(wl*1e9); + // int wl_sub_nm = round((wl*1e9 - wl_nm) * 1e6); + // ss << wl_nm << "nm" << wl_sub_nm; + ss << setprecision(6) << std::fixed << wl*1e9; + string wl_str = ss.str(); + // Necessary because '.' is interpreted by traces as the hierarchical separator + std::replace(wl_str.begin(), wl_str.end(), '.', '_'); + + temp_vector.clear(); + + temp_signal = make_unique<sc_signal<double>>(__modname("trace_pow", i*3)); + sc_trace(m_Tf, *temp_signal, (string(this->name()) + ".power@" + wl_str).c_str()); + temp_vector.push_back(std::move(temp_signal)); + + temp_signal = make_unique<sc_signal<double>>(__modname("trace_mod", i*3)); + sc_trace(m_Tf, *temp_signal, (string(this->name()) + ".abs@" + wl_str).c_str()); + temp_vector.push_back(std::move(temp_signal)); + + temp_signal = make_unique<sc_signal<double>>(__modname("trace_phase", i*3)); + sc_trace(m_Tf, *temp_signal, (string(this->name()) + ".phase@" + wl_str).c_str()); + temp_vector.push_back(std::move(temp_signal)); + + //m_lambda_signals[m_lambdas[i]] = std::move(temp_vector); + //or + m_lambda_signals.insert({wl, std::move(temp_vector)}); + + ++i; + } +} +void MLambdaProbe::on_port_in_changed() +{ + // std::map< int, std::vector< unique_ptr<sc_signal<double> > > > wl_map; + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << " ready ("; + for (const auto &p : m_lambda_signals) + { + cout << p.first << ", "; + } + cout << "\b\b)" << endl; + cout << "Signal: " << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name() << endl; + cout << endl; + } + //m_trace_sig.write(0); + + while (true) { + // Wait for a new input signal + wait(); + + // Check if enabled first + if (!enable.read().to_bool()) + { + wait(enable.posedge_event()); + continue; + } + + auto &s = p_in->read(); + auto search = m_lambda_signals.find(s.getWavelength()); + + if (search != m_lambda_signals.end()) + { + //cout << name() << " received supported signal (lambda = " << s.getWavelength() << "m)" << endl; + m_lambda_signals[s.getWavelength()][0]->write(s.power()); + m_lambda_signals[s.getWavelength()][1]->write(s.modulus()); + m_lambda_signals[s.getWavelength()][2]->write(s.phase()); + } + else { + cout << name() << " received unsupported signal (lambda = " << s.getWavelength() << "m)" << endl; + } + } +}
\ No newline at end of file diff --git a/src/devices/probe.h b/src/devices/probe.h new file mode 100644 index 0000000..10865ba --- /dev/null +++ b/src/devices/probe.h @@ -0,0 +1,131 @@ +#pragma once + +#include <systemc.h> +#include <fstream> +#include <map> +#include <set> + +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +/* +This class defines an ideal optical probe. It will sample the signal at its input and +write it in a file. Any OpticalSignal can be its input, without caring for optical +splitting, as it is completely ideal. +*/ +class Probe : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in; + + // Processes + virtual void on_port_in_changed(); + + // Member variables + + // If given a valid trace file as argument + // Will not save to txt and rather use VCD tracing + sc_trace_file *m_Tf; + sc_signal<double> m_trace_sig_power; + sc_signal<double> m_trace_sig_modulus; + sc_signal<double> m_trace_sig_phase; + sc_signal<double> m_trace_sig_wavelength; + + sc_signal<sc_logic> enable; + bool m_trace_power; + bool m_trace_modulus; + bool m_trace_phase; + bool m_trace_wavelength; + + // Constructor + Probe(sc_module_name name, const bool &trace_power = true, const bool &trace_modulus = true + , const bool &trace_phase = true, const bool &trace_wavelength = true) + : spx_module(name) + , m_Tf(nullptr) + , enable((string(this->name()) + "_enable").c_str(), sc_logic(1)) + , m_trace_power(trace_power) + , m_trace_modulus(trace_modulus) + , m_trace_phase(trace_phase) + , m_trace_wavelength(trace_wavelength) + { + SC_HAS_PROCESS(Probe); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } + + // Constructor overloading + Probe(sc_module_name name, sc_trace_file *Tf, const bool &trace_power = true, const bool &trace_modulus = true + , const bool &trace_phase = true, const bool &trace_wavelength = true) + : Probe(name, trace_power, trace_modulus, trace_phase, trace_wavelength) + { + setTraceFile(Tf); + } + + void setTraceFile(sc_trace_file *Tf) + { + if (m_Tf == Tf) + return; + m_Tf = Tf; + if (m_Tf) + { + if (m_trace_power) sc_trace(m_Tf, m_trace_sig_power, (string(this->name()) + ".power").c_str()); + if (m_trace_modulus) sc_trace(m_Tf, m_trace_sig_modulus, (string(this->name()) + ".abs").c_str()); + if (m_trace_phase) sc_trace(m_Tf, m_trace_sig_phase, (string(this->name()) + ".phase").c_str()); + if (m_trace_wavelength) sc_trace(m_Tf, m_trace_sig_wavelength, (string(this->name()) + ".wavelength").c_str()); + } + } +}; + +class PowerProbe : public Probe { +public: + virtual void on_port_in_changed(); +}; + +class PhaseProbe : public Probe { +public: + virtual void on_port_in_changed(); +}; + +class MLambdaProbe : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in; + + // Processes + virtual void on_port_in_changed(); + + // Member variables + + // If given a valid trace file as argument + // Will not save to txt and rather use VCD tracing + sc_signal<sc_logic> enable; + sc_trace_file *m_Tf; + + sc_signal<double> m_trace_sig_power; + sc_signal<double> m_trace_sig_modulus; + sc_signal<double> m_trace_sig_phase; + + std::set<double> m_lambdas; + std::map< double, std::vector< unique_ptr<sc_signal<double> > > > m_lambda_signals; + + void setTraceFile(sc_trace_file *Tf); + void prepare(); + + // Constructor + MLambdaProbe(sc_module_name name, set<double> lambdas = {}) + : spx_module(name) + , enable((string(this->name()) + "_enable").c_str(), sc_logic(1)) + , m_Tf(nullptr) + , m_lambdas(lambdas) + { + SC_HAS_PROCESS(MLambdaProbe); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } + + // TODO: overload the constructor for the case of not informing lambdas, need to check all components +}; diff --git a/src/devices/ring.cpp b/src/devices/ring.cpp new file mode 100644 index 0000000..42f901e --- /dev/null +++ b/src/devices/ring.cpp @@ -0,0 +1,8 @@ +#include <waveguide.h> + +//void Ring::on_port_tuning_changed() +//{ +// // Tuning of the ring parameters... +// // e.g. wg.neff = ... +//} + diff --git a/src/devices/ring.h b/src/devices/ring.h new file mode 100644 index 0000000..0633c13 --- /dev/null +++ b/src/devices/ring.h @@ -0,0 +1,99 @@ +#pragma once + +#include <systemc.h> + +#include <waveguide.h> +#include <directional_coupler.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +/** A unidirectional waveguide module. */ +class Ring : public sc_module { +public: + // Ports + /** The optical input port. */ + sc_port<sc_signal_in_if<OpticalSignal>> p_in; + /** The optical output port. */ + sc_port<sc_signal_out_if<OpticalSignal>> p_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out_writer; + + sc_signal<OpticalSignal> sig_in; + sc_signal<OpticalSignal> sig_internal_0; + sc_signal<OpticalSignal> sig_internal_1; + sc_signal<OpticalSignal> sig_out; + DirectionalCoupler dc; + Waveguide wg; + + // Member variables + /** The attenuation inside the waveguide, in dB/cm. */ + double m_attenuation_dB_cm = 0.2; + /** The effective index of the waveguide */ + double m_neff = 2.2111; + /** The group index of the waveguide */ + double m_ng = 2.2637; + + /** The coupling from waveguide to ring, in dB. */ + double m_cross_power_dB = 10*std::log10(0.5) - 0.15; + double m_through_power_dB = 10*std::log10(0.5) - 0.15; + double m_cross_phase_rad = M_PI / 2; + double m_through_phase_rad = M_PI / 2; + + /** The radius of the ring in um. */ + double m_radius_um = 0; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p_in + * */ + void on_port_in_changed(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param length_cm length of the waveguide in cm + * */ + Ring(sc_module_name name, double length_cm = 30e-6) + : sc_module(name) + , m_out_writer("out_delayed_writer", p_out) + , dc((std::string(name) + ".DC1").c_str()) + , wg((std::string(name) + ".WG1").c_str()) + { + dc.p_in1(sig_in); + dc.p_in2(sig_internal_1); + dc.p_out1(sig_out); + dc.p_out2(sig_internal_0); + + wg.p_in(sig_internal_0); + wg.p_out(sig_internal_1); + + p_in(sig_in); + p_out(sig_out); + + setLength(length_cm); + + SC_HAS_PROCESS(Waveguide); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } + + void setRadius(double radius_um) { + wg.setLength(2 * M_PI * radius_um * 1e-4); + m_radius_um = radius_um; + } + + void setLength(double length_cm) { + setRadius(length_cm * 1e4 / 2 / M_PI); + } +}; diff --git a/src/devices/splitter.cpp b/src/devices/splitter.cpp new file mode 100644 index 0000000..e6464f6 --- /dev/null +++ b/src/devices/splitter.cpp @@ -0,0 +1,41 @@ +#include <splitter.h> +#include <specs.h> + +void Splitter::on_port_in_changed() +{ + const double transmission = pow(10.0, - m_attenuation_dB / 20); + const double S12 = transmission * sqrt(m_split_ratio); + const double S13 = transmission * sqrt(1 - m_split_ratio); + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission = " << pow(transmission, 2) << " W/W" << endl; + cout << "splitting ratio_power = 0.5" << " W/W" << endl; + cout << "\t " << (dynamic_cast<spx::oa_signal_type *>(p_out1.get_interface()))->name() << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name() << " -->" << endl; + cout << "\t " << (dynamic_cast<spx::oa_signal_type *>(p_out2.get_interface()))->name(); + cout << endl; + cout << endl; + } + + while (true) { + // Wait for a new input signal + wait(); + + // Read input signal + const auto &s = p_in->read(); + + // Apply device's transmission + auto s1 = S12 * s; + auto s2 = S13 * s; + + // Get new IDs + s1.getNewId(); + s2.getNewId(); + + // Write to output ports after delay + m_out1_writer.delayedWrite(s1, SC_ZERO_TIME); + m_out2_writer.delayedWrite(s2, SC_ZERO_TIME); + } +} diff --git a/src/devices/splitter.h b/src/devices/splitter.h new file mode 100644 index 0000000..1d5e730 --- /dev/null +++ b/src/devices/splitter.h @@ -0,0 +1,88 @@ +#pragma once + +#include <systemc.h> + +#include <spx_module.h> +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +/** A 1x2 waveguide splitter module. + * + * This device copies any output to its two output, with power values defines by the + * split ratio and attenuation. + * */ +class Splitter : public spx_module { +public: + // Ports + /** The optical input port. */ + spx::oa_port_in_type p_in; + /** The first optical output port. */ + spx::oa_port_out_type p_out1; + /** The second optical output port. */ + spx::oa_port_out_type p_out2; + +private: + // Timed ports writers + /** Timed writer to the first output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out1_writer; + /** Timed writer to the second output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out2_writer; + +public: + // Member variables + /** The power transmission from input to branch 1, exclusive of losses + * + * The power transmission from input to branch 2 is (1 - m_split_ratio) + * */ + double m_split_ratio; + + /** The delay for an output to propagate to the outputs in ns. + * + * The delay is the same for both outputs. + * */ + // static double m_delay_ns; + /** The attenuation of the signal in dB. + * + * The attenuation is applied before the split (although it does not really make a + * difference. */ + double m_attenuation_dB; + + // Processes + /** Main process of the module. + * + * It copies the input to both output, after attenuation and delay. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p_in + * */ + void on_port_in_changed(); + + // Constructor + /** Constructor for Splitter + * + * @param name name of the module + * @param split_ratio ratio of the light which goes to the first ouptut + * (pre-attenuation) + * */ + Splitter(sc_module_name name, double split_ratio = 0.5, double attenuation_dB = 0) + : spx_module(name) + , m_out1_writer("out1_delayed_writer", p_out1) + , m_out2_writer("out2_delayed_writer", p_out2) + , m_split_ratio(split_ratio) + , m_attenuation_dB(attenuation_dB) + { + SC_HAS_PROCESS(Splitter); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } +}; diff --git a/src/devices/spx_module.h b/src/devices/spx_module.h new file mode 100644 index 0000000..0a4506f --- /dev/null +++ b/src/devices/spx_module.h @@ -0,0 +1,36 @@ +#pragma once + +#include "optical_signal.h" +#include "specs.h" + +#include <systemc.h> +#include <string> + +using std::string; +using namespace std::string_literals; + +class spx_module : public sc_module { +public: + typedef spx_module this_type; + typedef spx::oa_value_type sigval_type; + typedef spx::oa_signal_type sig_type; + typedef spx::oa_if_in_type if_in_type; + typedef spx::oa_if_out_type if_out_type; + typedef spx::oa_port_in_type port_in_type; + typedef spx::oa_port_out_type port_out_type; + + enum ModuleFlags { + NON_LINEAR = 0b0001, + TIME_VARIANT = 0b0010, + FREQUENCY_DEPENDENT = 0b0100, + }; + + ModuleFlags flags = FREQUENCY_DEPENDENT; + + virtual void init() {} + virtual string describe() const { return ""s; } + + spx_module(sc_module_name name) + : sc_module(name) + {} +};
\ No newline at end of file diff --git a/src/devices/subcircuit_instance.h b/src/devices/subcircuit_instance.h new file mode 100644 index 0000000..5c835e8 --- /dev/null +++ b/src/devices/subcircuit_instance.h @@ -0,0 +1,25 @@ +#pragma once + +#include <memory> +#include <vector> + +#include "specs.h" +#include "spx_module.h" + +using std::vector; +using std::string; +using std::shared_ptr; + +class SubcircuitInstance: public spx_module { +public: + // vector<shared_ptr<spx::oa_port_inout_type>> ports; + vector<shared_ptr<sc_object>> signals; + vector<shared_ptr<sc_object>> modules; + + SubcircuitInstance(sc_module_name name) + : spx_module(name) + {} + + ~SubcircuitInstance() + {} +};
\ No newline at end of file diff --git a/src/devices/time_monitor.cpp b/src/devices/time_monitor.cpp new file mode 100644 index 0000000..0c4ab91 --- /dev/null +++ b/src/devices/time_monitor.cpp @@ -0,0 +1,152 @@ + +#include "time_monitor.h" +#include "optical_output_port.h" +#include "specs.h" +#include "sysc_utils.h" +#include <vector> +#include <set> + +using namespace std; + +string vec2str(const vector<bool> &v) +{ + string str; + for (const auto &x : v) + if (x) + str += "1|"; + else + str += "0|"; + + return str; +} + +void TimeMonitor::on_trigger() { + auto first = system_clock::now(); + auto last = first; + + set<OpticalOutputPort *> oops = sc_get_all_module_by_type<OpticalOutputPort>(); + sc_time delay = sc_time(m_poll_period, SC_SEC); + + //wait(1, SC_PS); + while (true) { + // Wait for next polling event + if (!sc_pending_activity()) + { + // cout << "a" << endl; + wait(); + } + else if (sc_pending_activity_at_current_time() && delay.value() != 0) + { + // cout << "b" << endl; + wait(delay); + } + else if (sc_pending_activity_at_current_time() && delay.value() == 0) + { + //cout << "c" << endl; + wait(delay); + } + else if (sc_pending_activity_at_future_time() && delay.value() != 0) + { + // cout << "d" << endl; + wait(delay); + } + else if (sc_pending_activity_at_future_time() && delay.value() == 0) + { + // cout << "e" << endl; + wait(sc_time_to_pending_activity()); + } + else + cerr << "How did you get here ?" << endl; + + + // Get current wall clock + auto now = system_clock::now(); + std::chrono::duration<double> elapsed_seconds = now - last; + + // Report if it's time to do it + if (elapsed_seconds.count() >= m_wallclock_period) + { + #if 0 + double error_sum = 0; + double max_error = 0; + double emitted_power_sum = 0; + double max_emitted_power = 0; + string max_error_name; + complex<double> culprit_emitted; + double culprit_emitted_pow = 0.0; + complex<double> culprit_wanted; + vector<bool> is_empty(oops.size(), false); + int i = 0; + int max_i = 0; + for (const auto &oop : oops) + { + is_empty[i] = oop->isempty(); + error_sum += oop->current_err_fd(); + max_error = max(max_error, oop->current_err_fd()); + if (max_error == oop->current_err_fd()) + // if (strcmp(oop->name(), "crow.dc_0.out1_delayed_writer") == 0) + { + max_error_name = oop->name(); + // deprecated following lines on 05/04: + // culprit_emitted = oop->m_emitted_val_fd; + // culprit_emitted_pow = oop->m_emitted_val_fd.power(); + culprit_wanted = oop->m_cur_val_fd; + max_i = i; + } + // deprecated following lines on 05/04: + // emitted_power_sum += oop->m_emitted_val_fd.power(); + // max_emitted_power = max(max_emitted_power, oop->m_emitted_val_fd.power()); + i++; + } + #endif + + std::chrono::duration<double> elapsed_seconds_since_start = now - first; + last = now; + auto t = sc_time_stamp().to_seconds(); + cout << "---------------------------------------------------------------------------" << endl; + cerr << "Time monitor incompatible with new multi-wavelength features" << endl; + exit(1); + cout << "Current simulation time (after "<< (int)(elapsed_seconds_since_start.count()) <<"s): " << endl + << "\tIn NS: " << t*1e9 << endl + << "\tIn PS: " << t*1e12 << endl + << "\tDelta-cycles at current time: " << sc_delta_count_at_current_time() << endl + //<< "\tCumulated error in ports: " << error_sum << endl + //<< "\tIsempty: " << vec2str(is_empty) << endl + //<< "\tCulprit: " << max_error_name << endl + //<< "\tCulprit i: " << max_i << endl + //<< setprecision(12) + //<< "\tWanted: " << norm(culprit_wanted) << "W " << arg(culprit_wanted) << "rad" << endl + //<< "\tEmitted: " << norm(culprit_emitted) << "W " << arg(culprit_emitted) << "rad" << endl + //<< "\tDiff: " << abs(norm(culprit_emitted) - norm(culprit_wanted)) << "W " << abs(arg(culprit_emitted) - arg(culprit_wanted)) << "rad" << endl + //<< "\tMax error in ports: " << max_error << endl + //<< "\tCumulated power output of all ports: " << emitted_power_sum << endl + //<< "\tMax port power output: " << max_emitted_power << endl + << "\tSimulation speed (NS/s): " << t*1e9 / elapsed_seconds_since_start.count() << endl; + + // if (max_error < specsGlobalConfig.oop_configs[0]->m_abs_tol_power) + if (false) + { + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // DOES NOT WORK IF CODE IS COMPILED WITH -O3 ??? + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + specsGlobalConfig.drop_all_events = true; + wait(SC_ZERO_TIME); + while (true) { + volatile bool isempty = true; + for (const auto &oop : oops) { + isempty &= oop->isempty(); + if (oop->isempty()) + { + cout << oop->name() << " didnt empty its queue" << endl; + } + } + if (isempty) + break; + wait(SC_ZERO_TIME); + } + specsGlobalConfig.drop_all_events = false; + wait(sc_time::from_value(1)); + } + } + } +} diff --git a/src/devices/time_monitor.h b/src/devices/time_monitor.h new file mode 100644 index 0000000..62d5626 --- /dev/null +++ b/src/devices/time_monitor.h @@ -0,0 +1,26 @@ +#pragma once + +#include <systemc.h> +#include <chrono> +#include <iostream> + +using std::chrono::system_clock; +using std::shared_ptr; +using std::make_shared; + +class TimeMonitor : public sc_module { +public: + double m_poll_period; // in seconds + double m_wallclock_period; // in seconds + + void on_trigger(); + + TimeMonitor(sc_module_name name, double poll_period=100e-12, double wallclock_period = 2) + : sc_module(name), + m_poll_period(poll_period), + m_wallclock_period(wallclock_period) + { + SC_HAS_PROCESS(TimeMonitor); + SC_THREAD(on_trigger); + } +}; diff --git a/src/devices/value_list_source.cpp b/src/devices/value_list_source.cpp new file mode 100644 index 0000000..a10c1a1 --- /dev/null +++ b/src/devices/value_list_source.cpp @@ -0,0 +1,69 @@ +#include "specs.h" +#include "value_list_source.h" + +using std::cout; +using std::cerr; +using std::endl; + +void VLSource::runner() +{ + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "value list (" << m_values_queue.size() << " values)"; + if (false) + { + cout << ":" << endl; + for (const auto& val : m_values_queue) + cout << "\t" << "@" << sc_time(val.first, SC_SEC) << ": " << val.second << endl; + } + cout << endl; + cout << "--> " << + (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name() << endl; + cout << endl; + } + + // Wait for enable signal + if (! enable.read().to_bool()) + { + // cout << name() << " waiting for enable" << endl; + wait(enable.posedge_event()); + cout << name() << " was enabled" << endl; + } + + // Emitting 0 at enable (will only go through if there is no value at t=0) + if (m_values_queue.cbegin() == m_values_queue.cend() + || sc_time(m_values_queue.front().first, SC_SEC).value() > 0) + { + cout << "@" << sc_time_stamp() << ", " << name() << " emitted: " << spx::oa_value_type(0) << endl; + m_out_writer.delayedWrite(spx::oa_value_type(0), SC_ZERO_TIME); + } + + auto it = m_values_queue.cbegin(); + while (it != m_values_queue.cend()) + { + sc_time now = sc_time_stamp(); + sc_time next_emit; + if (sc_time(it->first, SC_SEC) < now) + { + cout << name() << ": invalid time for signal emission !" << endl; + ++it; + continue; + } + sc_time delay = sc_time(it->first, SC_SEC) - now; + + // Wait until next output time + wait(delay); + + auto s = it->second; + + // Write value to output + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + cout << "@" << sc_time_stamp() << ", " << name() << " emitted: " << s << endl; + + ++it; + } + + // cout << name() << " completed" << endl; + while(true) { wait(); } +} diff --git a/src/devices/value_list_source.h b/src/devices/value_list_source.h new file mode 100644 index 0000000..a7ddb6e --- /dev/null +++ b/src/devices/value_list_source.h @@ -0,0 +1,140 @@ +#pragma once + +#include <systemc.h> +#include <fstream> +#include <algorithm> +#include <vector> +#include <utility> + +#include "strutils.h" +#include "optical_output_port.h" +#include "optical_signal.h" +#include "specs.h" +#include "spx_module.h" + +using std::vector; +using std::pair; + +class VLSource : public spx_module { +public: + typedef pair<double, spx::oa_value_type> time_value_pair_type; + // Ports + spx::oa_port_out_type p_out; + + // Timed ports writers + OpticalOutputPort m_out_writer; + + // Signal to emit + vector<time_value_pair_type> m_values_queue; + + // Source emission control + spx::ed_signal_type enable; + + // Processes + void runner(); + + VLSource(sc_module_name name, const vector<time_value_pair_type> &values = {}) + : spx_module(name) + , m_out_writer((string(this->name()) + "_out_writer").c_str(), p_out) + , m_values_queue(values) + { + sortValues(); + enable = sc_logic(0); + + SC_HAS_PROCESS(VLSource); + SC_THREAD(runner); + } + + void setValues(const vector<time_value_pair_type> &values) + { + m_values_queue = values; + sortValues(); + } + + void setValues(const string &filename) + { + std::ifstream file(filename); + + if (!file.is_open()) { + cerr << "Error: Cannot open the file." << endl; + exit(1); + } + + string line; + + getline(file, line); + + int lineNumber = 0; + while (getline(file, line)) { + ++lineNumber; + istringstream s(line); + string field; + + double t, P, WL; + + // Skip if line is empty or starts with ; + if (line.empty()) + { + cout << "Skipping empty line." << endl; + continue; + } + // Skip if line starts with ; + if (line[0] == ';') + { + cout << "Skipping commented line: " << line << endl; + continue; + } + + // Read first field (time) + getline(s, field, ','); + strutils::trim(field); + + // Skip header line if it exists + if(lineNumber == 1 && field == "time") + { + cout << "Skipping header line: " << line << endl; + continue; + } + + try { + t = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for time: \"" << field << "\"" << endl; + exit(1); + } + + // Read second field (P) + getline(s, field, ','); + try { + P = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for power: \"" << field << "\"" << endl; + exit(1); + } + + // Read wavelength (WL) + std::getline(s, field); + try { + WL = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for wavelength: \"" << field << "\"" << endl; + exit(1); + } + + cout << t << ": " << P << "W @" << WL*1e9 << "nm" << endl; + m_values_queue.emplace_back(t, OpticalSignal(sqrt(P), WL)); + } + + file.close(); + sortValues(); + } + + void sortValues() + { + auto cmp = [](const time_value_pair_type& p1, const time_value_pair_type& p2) { + return p1.first < p2.first; + }; + // Stable sort will keep initial ordering for values with identical time + std::stable_sort(m_values_queue.begin(), m_values_queue.end(), cmp); + } +}; diff --git a/src/devices/waveguide.cpp b/src/devices/waveguide.cpp new file mode 100644 index 0000000..dc20ec7 --- /dev/null +++ b/src/devices/waveguide.cpp @@ -0,0 +1,280 @@ +#include "specs.h" +#include <waveguide.h> + +using namespace std; + +void WaveguideUni::on_port_in_changed() +{ + const double c = 299792458.0; + + // attenuation in dB + m_attenuation_dB = m_attenuation_dB_cm * m_length_cm; + + // transmission in field: 10^(-dB/20) + const double transmission = pow(10.0, - m_attenuation_dB / 20.0); + + // vg = c/ng + // => delay = L / vg = (L * ng) / c + const double group_delay_ns = 1e9 * m_length_cm * 1e-2 / (c / m_ng); + + // precalculate 2 * pi * L + double phase_delay_factor = 2.0 * M_PI * m_length_cm * 1e-2; + + // ng = neff - lambda * dneff/dlambda + // => dneff/dlambda = (neff - ng)/lambda + double dneff_dlambda = (m_neff - m_ng) / 1.55e-6; + + // Parameters relative to dispersion + double d2neff_dlambda2_over_2 = -1 * c * m_D / (2 * 1.55e-6); + double dng_dlambda = c*m_D; + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "length = " << m_length_cm/100 << " m" << endl; + cout << "neff = " << m_neff << "" << endl; + cout << "ng = " << m_ng << "" << endl; + cout << "transmission_power = " << pow(transmission, 2) << " W/W" << endl; + cout << "group delay = " << group_delay_ns << " ns" << endl; + // cout << "dneff/dlambda = " << dneff_dlambda*1e-6 << " um^-1" << endl; + cout << "phase delay = " << 1e9 * m_length_cm * 1e-2 / (c / m_neff) << " ns"; + cout << " (" + << fmod((2 * M_PI * m_neff / 1.55e-6) * m_length_cm * 1e-2, 2 * M_PI) + << "rad @1.55)" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name(); + cout << endl; + cout << endl; + } + + if(m_D == 0) + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay = phase_delay_factor * neff / s.getWavelength(); + + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_out_writer.delayedWrite(s, sc_time(group_delay_ns, SC_NS)); + } + } + else // dispersion has a defined value + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6) + + d2neff_dlambda2_over_2 * pow(s.getWavelength() - 1.55e-6, 2); + const double ng = m_ng + dng_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay_disp = phase_delay_factor * neff / s.getWavelength(); + const double group_delay_ns_disp = 1e9 * m_length_cm * 1e-2 / (c / ng); + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay_disp); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_out_writer.delayedWrite(s, sc_time(group_delay_ns_disp, SC_NS)); + } + } +} + + +void WaveguideBi::on_p0_in_changed() +{ + const double c = 299792458.0; + + // attenuation in dB + m_attenuation_dB = m_attenuation_dB_cm * m_length_cm; + + // transmission in field: 10^(-dB/20) + const double transmission = pow(10.0, - m_attenuation_dB / 20.0); + + // vg = c/ng + // => delay = L / vg = (L * ng) / c + const double group_delay_ns = 1e9 * m_length_cm * 1e-2 / (c / m_ng); + + // precalculate 2 * pi * L + double phase_delay_factor = 2.0 * M_PI * m_length_cm * 1e-2; + + // ng = neff - lambda * dneff/dlambda + // => dneff/dlambda = (neff - ng)/lambda + double dneff_dlambda = (m_neff - m_ng) / 1.55e-6; + + // Parameters relative to dispersion + double d2neff_dlambda2_over_2 = -1 * c * m_D / (2 * 1.55e-6); + double dng_dlambda = c*m_D; + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "length = " << m_length_cm/100 << " m" << endl; + cout << "neff = " << m_neff << "" << endl; + cout << "ng = " << m_ng << "" << endl; + cout << "transmission_power = " << pow(transmission, 2) << " W/W" << endl; + cout << "group delay = " << group_delay_ns << " ns" << endl; + // cout << "dneff/dlambda = " << dneff_dlambda*1e-6 << " um^-1" << endl; + cout << "phase delay = " << 1e9 * m_length_cm * 1e-2 / (c / m_neff) << " ns"; + cout << " (" + << fmod((2 * M_PI * m_neff / 1.55e-6) * m_length_cm * 1e-2, 2 * M_PI) + << "rad @1.55)" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_out.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_out.get_interface()))->name(); + cout << " <-- "; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_in.get_interface()))->name(); + cout << endl; + cout << endl; + } + + if(m_D == 0) + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p0_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay = phase_delay_factor * neff / s.getWavelength(); + + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_p1_out_writer.delayedWrite(s, sc_time(group_delay_ns, SC_NS)); + } + } + else // dispersion has a defined value + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p0_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6) + + d2neff_dlambda2_over_2 * pow(s.getWavelength() - 1.55e-6, 2); + const double ng = m_ng + dng_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay_disp = phase_delay_factor * neff / s.getWavelength(); + const double group_delay_ns_disp = 1e9 * m_length_cm * 1e-2 / (c / ng); + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay_disp); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_p1_out_writer.delayedWrite(s, sc_time(group_delay_ns_disp, SC_NS)); + } + } +} + +void WaveguideBi::on_p1_in_changed() +{ + const double c = 299792458.0; + + // attenuation in dB + m_attenuation_dB = m_attenuation_dB_cm * m_length_cm; + + // transmission in field: 10^(-dB/20) + const double transmission = pow(10.0, - m_attenuation_dB / 20.0); + + // vg = c/ng + // => delay = L / vg = (L * ng) / c + const double group_delay_ns = 1e9 * m_length_cm * 1e-2 / (c / m_ng); + + // precalculate 2 * pi * L + double phase_delay_factor = 2.0 * M_PI * m_length_cm * 1e-2; + + // ng = neff - lambda * dneff/dlambda + // => dneff/dlambda = (neff - ng)/lambda + double dneff_dlambda = (m_neff - m_ng) / 1.55e-6; + + // Parameters relative to dispersion + double d2neff_dlambda2_over_2 = -1 * c * m_D / (2 * 1.55e-6); + double dng_dlambda = c*m_D; + + if(m_D == 0) + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p1_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay = phase_delay_factor * neff / s.getWavelength(); + + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_p0_out_writer.delayedWrite(s, sc_time(group_delay_ns, SC_NS)); + } + } + else // dispersion has a defined value + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p1_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6) + + d2neff_dlambda2_over_2 * pow(s.getWavelength() - 1.55e-6, 2); + const double ng = m_ng + dng_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay_disp = phase_delay_factor * neff / s.getWavelength(); + const double group_delay_ns_disp = 1e9 * m_length_cm * 1e-2 / (c / ng); + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay_disp); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_p0_out_writer.delayedWrite(s, sc_time(group_delay_ns_disp, SC_NS)); + } + } +}
\ No newline at end of file diff --git a/src/devices/waveguide.h b/src/devices/waveguide.h new file mode 100644 index 0000000..c686bba --- /dev/null +++ b/src/devices/waveguide.h @@ -0,0 +1,154 @@ +#pragma once + +#include <systemc.h> + +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +/** The base class for the waveguide module */ +class WaveguideBase : public spx_module { +public: + // Member variables + + /** The length of the waveguide in cm. */ + double m_length_cm; + /** The attenuation of the waveguide in dB. */ + double m_attenuation_dB; + /** The attenuation inside the waveguide, in dB/cm */ + double m_attenuation_dB_cm; + + /** The effective index of the waveguide */ + double m_neff; + /** The group index of the waveguide */ + double m_ng; + /** The group velocity dispersion Dlambda of the waveguide in [s/m/m] */ + double m_D; + + /** Constructor for WaveguideBase + * + * @param name name of the module + * @param length_cm length of the waveguide in cm + * */ + WaveguideBase(sc_module_name name, double length_cm = 1e-2, + double attenuation_dB_cm = 0, double neff = 2.2111, + double ng = 2.2637, double D = 0) + : spx_module(name) + , m_length_cm(length_cm) + , m_attenuation_dB_cm(attenuation_dB_cm) + , m_neff(neff) + , m_ng(ng) + , m_D(D) + { + // nothing to do + } + + inline void setLength(double length_cm) { + if (length_cm < 0) { + std::cerr << "Error: waveguide length < 0" << std::endl; + } + m_length_cm = length_cm; + } +}; + + +/** A unidirectional waveguide module */ +class WaveguideUni : public WaveguideBase { +public: + // Ports + /** The optical input port. */ + spx::oa_port_in_type p_in; + /** The optical output port. */ + spx::oa_port_out_type p_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out_writer; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p_in + * */ + void on_port_in_changed(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param length_cm length of the waveguide in cm + * */ + WaveguideUni(sc_module_name name, double length_cm = 1e-2, + double attenuation_dB_cm = 0, double neff = 2.2111, + double ng = 2.2637, double D = 0) + : WaveguideBase(name, length_cm, attenuation_dB_cm, neff, ng, D) + , m_out_writer("out_delayed_writer", p_out) + { + SC_HAS_PROCESS(WaveguideUni); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } +}; + +typedef WaveguideUni Waveguide; + +/** A bidirectional waveguide module */ +class WaveguideBi : public WaveguideBase { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p0_in; + spx::oa_port_in_type p1_in; + + /** The optical output ports. */ + spx::oa_port_out_type p0_out; + spx::oa_port_out_type p1_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_p0_out_writer; + OpticalOutputPort m_p1_out_writer; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p0_in or p1_in + * */ + void on_p0_in_changed(); + void on_p1_in_changed(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param length_cm length of the waveguide in cm + * */ + WaveguideBi(sc_module_name name, double length_cm = 1e-2, + double attenuation_dB_cm = 0, double neff = 2.2111, + double ng = 2.2637, double D = 0) + : WaveguideBase(name, length_cm, attenuation_dB_cm, neff, ng, D) + , m_p0_out_writer("p0_out_delayed_writer", p0_out) + , m_p1_out_writer("p1_out_delayed_writer", p1_out) + { + SC_HAS_PROCESS(WaveguideBi); + + SC_THREAD(on_p0_in_changed); + sensitive << p0_in; + SC_THREAD(on_p1_in_changed); + sensitive << p1_in; + } +}; diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..2dc9e93 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,325 @@ +#include <ctime> +#include <iomanip> +#include <sstream> +#include <chrono> + +#include <optical_signal.h> +#include <time_monitor.h> +#include <tb/alltestbenches.h> +#include <utils/strutils.h> + +#include <systemc.h> + +#include <args.hxx> + +#include <specs.h> +#include "optical_output_port.h" +#include "parser/parse_tree.h" + +class OpticalOutputPort; + +using namespace std; +using namespace std::chrono; +using namespace literals; + +#include "../build/parser/parser.tab.h" +#include "../build/parser/parser.yy.h" +extern int yydebug; + +int do_list_tests() +{ + cout << "Valid testbench identifiers:" << endl; + if (tb_map.empty()) + cout << "\t # No test available #" << endl; + for (const auto &test_pair : tb_map) { + cout << "\t" << test_pair.first << endl; + } + return 0; +} + +int do_test(const string &testname) +{ + // Try to find the test specified by testname from the tb_mab + auto it = tb_map.find(testname); + if (it == tb_map.end()) { + // Not found + cerr << "Test not found: " << testname << endl; + return 1; + } + + // Call the testbench function associated with testname + auto start = high_resolution_clock::now(); + it->second(); + auto stop = high_resolution_clock::now(); + auto duration = duration_cast<microseconds>(stop - start); + std::cout << "Total runtime: " << duration.count()/1000.0 << " ms" << std::endl; + return 0; +} + +int do_circuit(const string &filename, bool is_dry_run = false, const string& json_filename = "") +{ + // Check whether the file exists + FILE *f; + if (filename == "-"s) { + f = NULL; + } else if (!(f = fopen(filename.c_str(), "r"))) { + cerr << "Error: File not found \"" << filename << "\"" << endl; + return 1; + } + + cout << "╔═══════════════════╗" << endl; + cout << "║ PARSING CIRCUIT ║" << endl; + cout << "╚═══════════════════╝" << endl; + + yyscan_t scanner; + YY_BUFFER_STATE buf; + + yylex_init(&scanner); + buf = yy_create_buffer(f, YY_BUF_SIZE, scanner); + yy_switch_to_buffer(buf, scanner); + + ParseTree pt("ROOT"); + int parsing_result = yyparse(scanner, &pt); + + //yy_delete_buffer(buf, scanner); + yylex_destroy(scanner); + + // Close the file + if (f) + fclose(f); + + // Return if unsuccessful + if (parsing_result != 0) { + return parsing_result; + } + + pt.print(); + + cout << "╔══════════════════════╗" << endl; + cout << "║ BUILDING CIRCUIT ║" << endl; + cout << "╚══════════════════════╝" << endl; + + pt.build_circuit(); + + if (!json_filename.empty()) + { + ofstream outfile; + outfile.open(json_filename, ios::out | ios::trunc); + outfile << pt.to_json(); + outfile.close(); + cout << "Exported flattened circuit as JSON > " << json_filename << endl; + } + + if (!is_dry_run) + { + cout << "╔══════════════════════╗" << endl; + cout << "║ SIMULATION ║" << endl; + cout << "╚══════════════════════╝" << endl; + + specsGlobalConfig.runAnalysis(); + } + else + { + cout << "╔══════════════════════╗" << endl; + cout << "║ SIMULATION SKIPPED ║" << endl; + cout << "╚══════════════════════╝" << endl; + } + + cout << "╔═══════════════════╗" << endl; + cout << "║ DONE ║" << endl; + cout << "╚═══════════════════╝" << endl; + + return 0; +} + +int raphael_main() { cout<< "Hello world" << endl; return 0; } + +int tests_module_registry_access(); + +int sc_main(int argc, char *argv[]) +{ + // Define command line interface using args + args::ArgumentParser parser("The Scalable Photonic Event-driven Circuit Simulator.", ""); + args::ValueFlag<string> file( + parser, "circuit", "Simulate from circuit file", { 'f', "file" }); + args::ValueFlag<int> set_timescale(parser, + "set_timescale", + "Set the engine timescale (as log10(timestep/1s))" + "(0: seconds, -3: ms, -6:µs, ..., -15: fs)", + { 'R', "resolution" }); + args::ValueFlag<string> set_simulator_mode(parser, + "set_simulator_mode", + "Set simulator mode. Possible values:\n" + " - time-domain, event-driven, td (default)\n" + " - sampled-time\n" + " - frequency-domain, fd", + { 'm', "mode"}); + args::Flag run_manual_test(parser, + "run_manual_test", + "Run manual test function", + { 'T', "run-manual-test" }); +#if YYDEBUG + args::Flag debug_netlist_parser(parser, + "debug_netlist_parser", + "Run with netlist parser debug information", + { "debug-parser" }); +#endif + args::ValueFlag<string> set_tracefile(parser, + "set_tracefile", + "Set the default trace file", + { 'o', "output" }); + args::ValueFlag<string> export_json(parser, + "export_json", + "Export json of completed circuit to file", + { "json" }); + args::Flag set_dry_run(parser, + "dry_run", + "Do not run simulation directives", + { 'n', "dryrun" }); + // args::ValueFlag<double> set_reltol(parser, + // "set_reltol", + // "Set the value of the rel_tol parameter", + // { "reltol" }); + args::ValueFlag<double> set_reltol(parser, + "set_reltol", + "Set the value of the relative tolerance parameter for field", + { "reltol" }); + args::ValueFlag<double> set_abstol(parser, + "set_abstol", + "Set the value of the absolute tolerance parameter for field", + { "abstol" }); + + args::ValueFlag<size_t> set_nrings_crow(parser, + "set_nrings_crow", + "temporary", + { "nrings_crow" }); + + args::Flag set_verbose_component_initialization(parser, + "set_verbose_component_initialization", + "Print components detail before starting simulation", + { "vci", "verbose_ci" }); + + args::Flag list_tests(parser, + "list_tests", + "List available testbenches", + { 'p', "list-testbenches" }); + args::Flag add_time_monitor(parser, + "add_time_monitor", + "Monitor simulation status", + { "mon" }); + args::ValueFlag<string> test( + parser, + "test_name", + "Run the specified testbench (use -p to list available testbenches)", + { 't', "testbench" }); + args::HelpFlag help(parser, "help", "Display this help menu", { 'h', "help" }); + args::CompletionFlag completion(parser, { "complete" }); + try { + parser.ParseCLI(argc, argv); + } catch (const args::Completion &e) { + cout << e.what(); + return 0; + } catch (const args::Help &) { + cout << parser; + return 0; + } catch (const args::ParseError &e) { + cerr << e.what() << endl; + cerr << parser; + return 1; + } catch (const args::ValidationError &e) { + cerr << e.what() << endl; + cerr << parser; + return 1; + } + + if (set_simulator_mode) { + const string &s = set_simulator_mode.Get(); + if (strutils::iequals(s, "event-driven") || strutils::iequals(s, "time-domain") || strutils::iequals(s, "td")) + { + cout << "Optical ports working in event-driven mode by default" << endl; + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + } + else if (strutils::iequals(s, "sampled-time")) + { + cout << "Optical ports working in sampled-time mode by default" << endl; + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::SAMPLED_TIME; + } + else if (strutils::iequals(s, "frequency-domain") || strutils::iequals(s, "fd")) + { + cout << "Simulation mode set to frequency domain" << endl; + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::FREQUENCY_DOMAIN; + } + else + { + cerr << "Unknown mode: '" << s << "'" << endl; + return 1; + } + } + if (set_reltol) { + if (set_reltol.Get() > 0) { + specsGlobalConfig.default_reltol = set_reltol.Get(); + } else { + cerr << "Invalid abs_tol_phase value" << endl; + return 1; + } + } + if (set_abstol) { + if (set_abstol.Get() > 0) { + specsGlobalConfig.default_abstol = set_abstol.Get(); + } else { + cerr << "Invalid abs_tol_power value" << endl; + return 1; + } + } + if (set_verbose_component_initialization) { + specsGlobalConfig.verbose_component_initialization = set_verbose_component_initialization.Get(); + } + if (set_nrings_crow) { + #if BUILD_TB == 1 + //nrings_crow = set_nrings_crow.Get(); + #endif + } + if (set_timescale) { + cout << set_timescale.Get() << endl; + specsGlobalConfig.engine_timescale = SPECSConfig::EngineTimescale(set_timescale.Get()); + } + if (run_manual_test) { + return 0; + } +#if YYDEBUG + if (debug_netlist_parser) { + yydebug = 1; + } +#endif + if (list_tests) { + return do_list_tests(); + } + if (set_tracefile) { + cout << "Using trace file: " << set_tracefile.Get() << endl; + // TODO: validate filename + specsGlobalConfig.trace_filename = set_tracefile.Get(); + } + + shared_ptr<TimeMonitor> tm; + if (add_time_monitor) { + if (specsGlobalConfig.simulation_mode == OpticalOutputPortMode::FREQUENCY_DOMAIN) + tm = make_shared<TimeMonitor>("TM", 0, 0.5); + else + tm = make_shared<TimeMonitor>("TM", 1e-14, 0.5); + } + + if (file) { + return do_circuit(file.Get(), set_dry_run.Get(), export_json.Get()); + } + if (set_dry_run) + { + return 0; + } + if (test) { + return do_test(test.Get()); + } + + // Print help if nothing else was done + cout << parser; + return 1; +} diff --git a/src/optical_output_port.cpp b/src/optical_output_port.cpp new file mode 100644 index 0000000..7a6fe9c --- /dev/null +++ b/src/optical_output_port.cpp @@ -0,0 +1,424 @@ +#include <optical_output_port.h> +#include <optical_signal.h> +#include <specs.h> + +string oopPortMode2str(OpticalOutputPortMode mode) +{ + switch (mode) { + case EVENT_DRIVEN: + return "Event-driven time-domain"; + case SAMPLED_TIME: + return "Emulated time-driven time-domain"; + case FREQUENCY_DOMAIN: + return "Event-driven frequency-domain"; + default: + return "UNDEFINED"; + } +} + +OpticalOutputPort::OpticalOutputPort(sc_module_name name, port_type &p) + : sc_module(name) + , m_port(p) + , m_config(nullptr) +{ + SC_HAS_PROCESS(OpticalOutputPort); + + // SC_THREAD(on_data_ready); + // sensitive << m_event_queue; + + SC_THREAD(on_data_ready); + sensitive << m_event_queue; + + // SC_THREAD(on_data_ready_fd); + // sensitive << m_event_queue_fd; + + // SC_THREAD(drop_all_events); + // sensitive << specsGlobalConfig.drop_all_events; +} + +// void OpticalOutputPort::drop_all_events() { +// while(true) +// { +// wait(); +// cout << name() << ": dropping all events!" << endl; +// while (!isempty()) +// { +// cout << name() << ": dropping all events" << endl; +// m_event_queue.cancel_all(); +// m_queue = queue_type(); +// m_emitted_val = OpticalSignal(0); +// m_queue_td = queue_type(); +// m_emitted_val = OpticalSignal(0); +// m_queue_fd = queue_type(); +// m_emitted_val = OpticalSignal(0); +// m_cur_val = OpticalSignal(0); +// wait(SC_ZERO_TIME); +// } +// } +// } + +inline bool OpticalOutputPort::check_emit_by_abstol(const OpticalSignal::field_type &desired, + const OpticalSignal::field_type &last) +{ + double vector_distance = abs(desired - last); + return vector_distance > m_abstol; +} + +inline bool OpticalOutputPort::check_emit_by_reltol(const OpticalSignal::field_type &desired, + const OpticalSignal::field_type &last) +{ + double vector_distance = abs(desired - last); + double last_size = abs(last); + return (vector_distance/last_size) > m_reltol; +} + +void OpticalOutputPort::applyConfig() { + if (!m_config) + { + // Shouldn't ever need to come here if prepareSimulation was called + cerr << "Found OpticalOutputPort without configuration. "; + cerr << "Did you forget to run prepareSimulation() ?" << endl; + exit(1); + //m_config = make_shared<OpticalOutputPortConfig>(); + } + + m_temporal_resolution = sc_time::from_value(m_config->m_timestep_value); + m_mode = m_config->m_mode; + m_reltol = m_config->m_reltol; + m_abstol = m_config->m_abstol; +} + +void OpticalOutputPort::start_of_simulation() { + applyConfig(); +} + +void OpticalOutputPort::on_data_ready() +{ + // Initialize output queue + m_queue = queue_type(); + + spx::oa_value_type::field_type desired; + + while (true) { + // Wait for data ready notification + wait(); + + // Check if output queue is empty (would be a bug) + if (m_queue.size() == 0) { + if (specsGlobalConfig.drop_all_events) + continue; + cerr << "error: write cancelled, because no values are present" << endl; + if (true) sc_stop(); + else continue; + } + + // Get current time + sc_time now = sc_time_stamp(); + + // Get the next queue item and pop it from the queue + auto tuple = m_queue.top(); + m_queue.pop(); + + // If next event is also now, notify event queue + // if (m_queue.top().first == now) + // m_event_queue.notify(SC_ZERO_TIME); + + // Assign to more readable names + const auto &t = tuple.first; //time + const auto &s = tuple.second; //signal + + // Check whether the signal should indeed be emitted now (if not, it's a bug) + if (t != now) + { + cerr << "error: desync in optical output port " << this->name() << endl; + cerr << "\t - expected time:" << t.to_seconds() << endl; + cerr << "\t - current time:" << now.to_seconds() << endl; + cerr << "\t - signal:" << s << endl; + if (true) sc_stop(); + else continue; + } + + // Check signal error to decice whether to emit signal + bool emit_signal = false; + bool pass_abstol = false; + bool pass_reltol = false; + + uint32_t wlid = s.m_wavelength_id; + + //auto &desired = m_desired_fields[wlid]; + auto &emitted = m_emitted_fields[wlid]; + + // Store the desired output value which we know is valid + if (m_use_deltas) + { + desired = m_desired_fields[wlid] + s.m_field; + m_desired_fields[wlid] = desired; + } + else + desired = s.m_field; + + // Decide whether to emit signal or not + pass_abstol = check_emit_by_abstol(desired, emitted); + pass_reltol = check_emit_by_reltol(desired, emitted); + + // Only emits when passes both tests + emit_signal = pass_abstol && pass_reltol; + + // Emit zeros instead of signals smaller than a 10 abstol + // TODO: check this !! + if (emit_signal && (abs(desired) < 10*m_abstol)) + desired = complex<double>(0,0); + + + // cout << "emit: " << emit_signal << endl; + // Emit the desired output value if its stars are aligned + if (emit_signal || m_skip_next_convergence_check || m_skip_convergence_check) + { + // cout << dynamic_cast<spx::oa_signal_type *>(m_port.get_interface())->name(); + // cout << " emitting " << m_desired_fields[wlid] << endl; + m_skip_next_convergence_check = false; + + // Replace the stored emitted output value at that wavelength + emitted = desired; + + // Write the value to the port + m_port->write(spx::oa_value_type(desired, wlid)); + } + } +} + +// Should be removed +void OpticalOutputPort::on_data_ready_fd() +{ + cerr << "Using deprecated function: " << __FUNCTION__ << endl; + exit(1); +#if 0 + // Initialize output queue + m_queue_fd = queue_type(); + + m_cur_val_fd = OpticalSignal(0); + m_emitted_val_fd = OpticalSignal(0); + + // Current error between cur_val and emitted_val + double err_abs_power = 0; + double err_abs_phase = 0; + + while (true) { + // Wait for data ready notification + wait(); + + // Check if output queue is empty (would be a bug) + if (m_queue_fd.size() == 0) { + if (specsGlobalConfig.drop_all_events) + continue; + cerr << "error: write cancelled, because no values are present" << endl; + if (true) sc_stop(); + else continue; + } + + // Get current time + sc_time now = sc_time_stamp(); + + // Get the next queue item and pop it from the queue + auto tuple = m_queue_fd.top(); + m_queue_fd.pop(); + + // Assign to more readable names + const auto &t = tuple.first; + const auto &s = tuple.second; + + // Check whether the signal should indeed be emitted now (if not, it's a bug) + if (t != now) + { + cerr << "error: desync in optical output port" << endl; + cerr << "\t - expected time:" << t.to_seconds() << endl; + cerr << "\t - current time:" << now.to_seconds() << endl; + if (true) sc_stop(); + else continue; + } + + // If the new desired signal has NaN wavelength, ignore it + if (isnan(s.getWavelength())) + { + continue; + } + + // Check if wavelength is the same as stored signal + bool wavelength_nan = isnan(m_emitted_val_fd.getWavelength()); + bool wavelength_same = s.m_wavelength_id == m_emitted_val_fd.m_wavelength_id; + bool wavelength_greater = s.getWavelength() > m_emitted_val_fd.getWavelength(); + bool wavelength_ok = wavelength_same || wavelength_nan || wavelength_greater; + + // Check signal error to decice whether to emit signal + bool emit_signal = false; + bool pass_abstol = false; + bool pass_reltol = false; + + // If new wavelength should not be emitted, don't do anything and + // wait for the next signal + if (!wavelength_ok) + continue; + + // Here we know s can be emitted. Update the desired output value + if (m_use_deltas) + m_cur_val_fd += s; + else + m_cur_val_fd = s; + + // If previous wavelength was NaN or smaller than current wavelength, emit new signal + if (wavelength_nan || wavelength_greater) + { + emit_signal = true; + } + + // If same wavelength, decide whether to emit signal or not based on tolerances + if (wavelength_same) + { + pass_abstol = check_emit_by_abstol(m_cur_val_fd, m_emitted_val_fd); + pass_reltol = check_emit_by_reltol(m_cur_val_fd, m_emitted_val_fd); + + // Only emits when passes both tests + emit_signal = pass_abstol && pass_reltol; + + // Emit zeros instead of signals smaller than 10 abstol + if (emit_signal && (abs(m_cur_val_fd.m_field) < 10*m_abstol)) + m_cur_val_fd.m_field = complex<double>(0,0); + } + + // Emit the desired output value if its stars are aligned + if (emit_signal) + { + // Replace the stored emitted output value + m_emitted_val_fd = m_cur_val_fd; + + // Write the value to the port + m_port->write(m_cur_val_fd); + } + } +#endif +} + +sc_time OpticalOutputPort::snap_to_next_valid_time(const sc_time &t, const unsigned int resolution_multiplier) +{ + // Squash time to closest timestamp using device temporal resolution and multiplier + + // Find the effective number of ticks/timestep of the output port + sc_time::value_type dt_val = m_temporal_resolution.value() * resolution_multiplier; + + // Should be at least one tick + if (dt_val <= 1) + return t; + + // Snap the time to the closest multiple of dt + //auto t_snap_value = dt_val * round((double)t.value() / dt_val); + auto t_snap_value = dt_val * ((t.value() + dt_val/2)/ dt_val); + + // If we end up before current time, snap to the next multiple of dt + // We do this instead of using ceil() because in the case where the event + // is far into the future, it is more accurate to round(). + if (t_snap_value < sc_time_stamp().value()) + t_snap_value += dt_val; + + // Return the snaped time value (in number of simulation ticks) + return sc_time::from_value(t_snap_value); +} + +void OpticalOutputPort::delayedWriteEventDriven(const OpticalSignal &value, const sc_time &delay, const unsigned int resolution_multiplier) +{ + // Calculate the simulation time at which event was requested to be emitted. + auto t = sc_time_stamp() + delay; + if (t.value() - sc_time_stamp().value() != delay.value()) + cerr << "Event cannot be correctly described with current timestep" << endl; + + // + if (true /*&& resolution_multiplier > 1*/) { + // squash time to closest timestamp using device temporal resolution + t = snap_to_next_valid_time(t, resolution_multiplier); + } + + + // find first scheduled signal with this timestamp and this lambda + auto it = std::find_if(m_queue.begin(), m_queue.end(), [&value,&t](const auto &x) { + return t == x.first && value.m_wavelength_id == x.second.m_wavelength_id; + }); + + // check if an event was already scheduled for the same timestamp + if (it == m_queue.cend()) { + // if not, just schedule the new event + m_queue.push(std::make_pair(t, value)); + m_event_queue.notify(t - sc_time_stamp()); + } + else { + // if yes, just replace or sum with the old one + if (m_use_deltas) + it->second += value; + else + it->second = value; + } +} + +void OpticalOutputPort::delayedWriteSampledTime(const OpticalSignal &value, const sc_time &delay, const unsigned int resolution_multiplier) +{ + (void)value; + (void)delay; + (void)resolution_multiplier; + // TODO + // Goal here will be to manage, maybe, some averaging + // i.e. when we output sampled-time, if we receive event-driven signals, we convert them + // to sampled-time by averaging over the timestep and emitting the average +} + +void OpticalOutputPort::immediateWriteFrequencyDomain(const OpticalSignal &value) +{ +#if 1 + //if (specsGlobalConfig.drop_all_events) + // return; + //if (value.power() <= m_abs_tol_power && m_emitted_val_fd.power() <= m_abs_tol_power) + // return; + + const sc_time &now = sc_time_stamp(); + if (m_queue.empty() || m_queue.cbegin()->first != now) { + // if the queue is empty, or contains no event for current time, + // push the signal directly + m_queue.push(make_pair(now, value)); + m_event_queue.notify(SC_ZERO_TIME); + } + else { + // if the queue is contains an event for current time + if (m_queue.cbegin()->second.m_wavelength_id == value.m_wavelength_id) { + // if wavelengths are equal, just replace or sum with the old one + if (m_use_deltas) + m_queue.begin()->second += value; + else + m_queue.begin()->second = value; + //m_event_queue_fd.cancel_all(); + //m_event_queue_fd.notify(SC_ZERO_TIME); + } else { + // otherwise just schedule event separately + m_queue.push(make_pair(now, value)); + m_event_queue.notify(SC_ZERO_TIME); + } + } +#else + cerr << "Using implemented function: " << __FUNCTION__ << endl; +#endif +} + +void OpticalOutputPort::delayedWrite(const OpticalSignal &value, const sc_time &delay, const unsigned int resolution_multiplier) +{ + switch(m_mode) { + case OpticalOutputPortMode::EVENT_DRIVEN: + delayedWriteEventDriven(value, delay, resolution_multiplier); + return; + case OpticalOutputPortMode::SAMPLED_TIME: + delayedWriteSampledTime(value, delay, resolution_multiplier); + return; + case OpticalOutputPortMode::FREQUENCY_DOMAIN: + //delayedWriteEventDriven(value, SC_ZERO_TIME, 1); + immediateWriteFrequencyDomain(value); + default: + break; // throw here + } + +} + diff --git a/src/optical_output_port.h b/src/optical_output_port.h new file mode 100644 index 0000000..c0cd7dd --- /dev/null +++ b/src/optical_output_port.h @@ -0,0 +1,178 @@ +#pragma once + +#include <cstdlib> +#include <deque> +#include <systemc.h> +#include <utility> +#include <vector> +#include <queue> +#include <map> + +#include <optical_signal.h> +#include <pqueue.h> + +using std::cout; +using std::endl; + +using std::pair; +using std::vector; +using std::priority_queue; + +enum OpticalOutputPortMode { + PORT_MODE_MINVAL, + EVENT_DRIVEN, + SAMPLED_TIME, + NO_DELAY, + PORT_MODE_MAXVAL, + + // aliases + TIME_DOMAIN = EVENT_DRIVEN, + FREQUENCY_DOMAIN = NO_DELAY, + DEFAULT = EVENT_DRIVEN, + UNDEFINED = PORT_MODE_MAXVAL, +}; + +string oopPortMode2str(OpticalOutputPortMode mode); + +class OpticalOutputPortConfig { +public: + OpticalOutputPortMode m_mode = OpticalOutputPortMode::DEFAULT; + double m_abstol = 1e-8; + double m_reltol = 1e-4; + sc_time::value_type m_timestep_value = 1; // relative to systemc timestep + + OpticalOutputPortConfig() {} +}; + +class OpticalOutputPort : public sc_module { +public: + typedef OpticalOutputPort this_type; + typedef sc_port<sc_signal_out_if<OpticalSignal>> port_type; + typedef pair<sc_time, OpticalSignal> pair_type; + typedef PQueue<pair_type> queue_type; + + +// private: +public: + port_type &m_port; + queue_type m_queue; + // queue_type m_queue_fd; + // queue_type m_queue_td; + sc_event_queue m_event_queue; + // sc_event_queue m_event_queue_td; + // sc_event_queue m_event_queue_fd; + // OpticalSignal m_cur_val; + // OpticalSignal m_cur_val_td; + // OpticalSignal m_cur_val_fd; + // OpticalSignal m_emitted_val; + // OpticalSignal m_emitted_val_fd; + + std::map<uint32_t,OpticalSignal::field_type> m_desired_fields; + std::map<uint32_t,OpticalSignal::field_type> m_emitted_fields; + + sc_time m_temporal_resolution; + OpticalOutputPortMode m_mode; + double m_reltol; + double m_abstol; + bool m_use_deltas = false; + bool m_converger = false; + bool m_skip_next_convergence_check = false; + bool m_skip_convergence_check = false; + + std::shared_ptr<const OpticalOutputPortConfig> m_config; + + // void drop_all_events(); + void on_data_ready(); + void on_data_ready_fd(); + bool check_emit_by_abstol(const OpticalSignal::field_type &desired, const OpticalSignal::field_type &last); + bool check_emit_by_reltol(const OpticalSignal::field_type &desired, const OpticalSignal::field_type &last); + + void applyConfig(); + virtual void start_of_simulation(); + sc_time snap_to_next_valid_time(const sc_time &t, const unsigned int resolution_multiplier=1); + void delayedWriteEventDriven(const OpticalSignal &value, const sc_time &delay, const unsigned int resolution_multiplier=1); + void delayedWriteSampledTime(const OpticalSignal &value, const sc_time &delay, const unsigned int resolution_multiplier=1); + void immediateWriteFrequencyDomain(const OpticalSignal &value); + +public: + OpticalOutputPort(sc_module_name name, port_type &p); + + ~OpticalOutputPort() {} + + // Set config + void setConfig(shared_ptr<OpticalOutputPortConfig> &config) + { + m_config = config; + } + const shared_ptr<const OpticalOutputPortConfig>& getConfig() const + { + return m_config; + } + + void drop_queue() + { + if (sc_get_simulator_status() == SC_RUNNING) + { + cerr << "Cannot drop if simulation is running" << endl; + exit(1); + } + m_event_queue.cancel_all(); + m_queue = queue_type(); + } + + void reset() + { + drop_queue(); + m_desired_fields.clear(); + m_emitted_fields.clear(); + } + + void swap_wavelengths(uint32_t wl1, uint32_t wl2) + { + drop_queue(); + + // Create entries with 0 if they don't exist + m_desired_fields.emplace(wl1, 0); + m_desired_fields.emplace(wl2, 0); + m_emitted_fields.emplace(wl1, 0); + m_emitted_fields.emplace(wl2, 0); + + // Swap wl1 and wl2 entries + swap(m_desired_fields.at(wl1), m_desired_fields.at(wl2)); + swap(m_emitted_fields.at(wl1), m_emitted_fields.at(wl2)); + } + + void delete_wavelength(uint32_t wl) + { + drop_queue(); + // Create entries with 0 if they don't exist + m_desired_fields.erase(wl); + m_emitted_fields.erase(wl); + } + + // inline double current_err() const { + // double err_abs_power = abs(m_cur_val.power() - m_emitted_val.power()); + // double err_abs_phase = abs(m_cur_val.phase() - m_emitted_val.phase()); + // if (m_cur_val.power() > m_abstol) + // return max(err_abs_power, err_abs_phase); + // return err_abs_power; + // } + + inline double current_err_fd() const { + cerr << "Using deprecated function: " << __FUNCTION__ << endl; + exit(1); + // double err_abs_power = abs(m_cur_val_fd.power() - m_emitted_val_fd.power()); + // double err_abs_phase = abs(m_cur_val_fd.phase() - m_emitted_val_fd.phase()); + // if (m_cur_val_fd.power() >= m_abstol) + // return max(err_abs_power, err_abs_phase); + // return err_abs_power; + return 0; + } + + inline bool isempty() const + { + return m_queue.empty(); + } + + void delayedWrite(const OpticalSignal &value, const sc_time &delay, const unsigned int resolution_multiplier=1); +}; diff --git a/src/optical_signal.cpp b/src/optical_signal.cpp new file mode 100644 index 0000000..704dfd6 --- /dev/null +++ b/src/optical_signal.cpp @@ -0,0 +1,157 @@ +#include <optical_signal.h> +#include <specs.h> + +using namespace std::complex_literals; + +unsigned int OpticalSignal::nextId = 0; + +OpticalSignal &OpticalSignal::operator+=(const OpticalSignal &rhs) +{ + // cout << "Summing two signals: " << endl; + // cout << "\t" << *this << endl; + // cout << "\t" << rhs << endl; + + auto this_wavelength = this->getWavelength(); + auto rhs_wavelength = rhs.getWavelength(); + + if (this_wavelength == rhs_wavelength) + m_field += rhs.m_field; + else if (isnan(rhs_wavelength)) + (void)rhs; // nothing to do + else if (isnan(this_wavelength)) + *this = rhs; + else if (specsGlobalConfig.simulation_mode == FREQUENCY_DOMAIN) + { + // TODO: move that somewhere else ??? + // Replace with rhs + if (rhs_wavelength > this_wavelength) + *this = rhs; + } + else + cerr << "Attempted to sum signals of different wavelengths, but current" + << "simulation mode doesn't allow it (" << __FUNCTION__ << ")" << endl; + // cout << "\t--> " << *this << endl; + return *this; +} + +OpticalSignal &OpticalSignal::operator-=(const OpticalSignal &rhs) +{ + // cout << "Substracting two signals: " << endl; + // cout << "\t" << *this << endl; + // cout << "\t" << rhs << endl; + + auto this_wavelength = this->getWavelength(); + auto rhs_wavelength = rhs.getWavelength(); + + if (this_wavelength == rhs_wavelength) + m_field -= rhs.m_field; + else if (isnan(rhs_wavelength)) + (void)rhs; // nothing to do + else if (isnan(this_wavelength)) + *this = rhs; + else if (specsGlobalConfig.simulation_mode == FREQUENCY_DOMAIN) + { + // TODO: move that somewhere else ??? + // Replace with rhs + if (rhs_wavelength > this_wavelength) + *this = rhs; + } + else + cerr << "Attempted to substract signals of different wavelengths, but current" + << "simulation mode doesn't allow it (" << __FUNCTION__ << ")" << endl; + // cout << "\t--> " << *this << endl; + return *this; +} + +OpticalSignal OpticalSignal::sumSignals(OpticalSignal s0, OpticalSignal s1) +{ + cerr << "Use of deprecated" << __FUNCTION__<< endl; + + // Maybe doing just the sum is faster than this + if (s0.m_field == 0.0) { + OpticalSignal os_sum = s1; + os_sum.getNewId(); + return os_sum; + } + if (s1.m_field == (complex<double>) 0) { + OpticalSignal os_sum = s0; + os_sum.getNewId(); + return os_sum; + } + + if (s0.getWavelength() != s1.getWavelength()) + { + bool ignore_wavelength_error = false; + if (ignore_wavelength_error) + { + cerr << "Signals have different wavelengths:" << std::endl; + cerr << "\t" << s0.getWavelength() << "(" << norm(s0.m_field) << ")" + << "\t" << s1.getWavelength() << "(" << norm(s1.m_field) << ")" + << std::endl; + + sc_stop(); + } else { + OpticalSignal os_sum = s0; + if (norm(s1.m_field) > norm(s0.m_field)) + os_sum = s1; + os_sum.getNewId(); + return os_sum; + } + } + + complex<double> Asum = s0.m_field + s1.m_field; + + OpticalSignal os_sum = s0; + os_sum.m_field = Asum; + os_sum.getNewId(); + + return os_sum; +} + +uint32_t OpticalSignal::getIDFromWavelength(const double &wavelength) +{ + auto it = find (specsGlobalConfig.wavelengths_vector.cbegin(), + specsGlobalConfig.wavelengths_vector.cend(), + wavelength); + + uint32_t index; + + if (it != specsGlobalConfig.wavelengths_vector.cend()) + { // Found + index = it - specsGlobalConfig.wavelengths_vector.cbegin(); + } + else + { // Didn't find, has to add it to the vector + specsGlobalConfig.wavelengths_vector.push_back(wavelength); + index = specsGlobalConfig.wavelengths_vector.size() - 1; + // -1 because of zero indexing + } + + if (index == (uint32_t)(-1)) + { + cerr << "Error: too many wavelengths being used ." << endl; + exit(1); + } + + return index; +} + +double OpticalSignal::getWavelength() const +{ + if (m_wavelength_id >= specsGlobalConfig.wavelengths_vector.size()) + { + cerr << "Wavelength not found in global vector." << endl; + exit(1); + } + return specsGlobalConfig.wavelengths_vector[m_wavelength_id]; +} + +double OpticalSignal::getWavelength(const uint32_t &wavelength_id) +{ + if (wavelength_id >= specsGlobalConfig.wavelengths_vector.size()) + { + cerr << "Wavelength not found in global vector." << endl; + exit(1); + } + return specsGlobalConfig.wavelengths_vector[wavelength_id]; +}
\ No newline at end of file diff --git a/src/optical_signal.h b/src/optical_signal.h new file mode 100644 index 0000000..b597b98 --- /dev/null +++ b/src/optical_signal.h @@ -0,0 +1,251 @@ +#pragma once + +#include <iomanip> +#include <iostream> +#include <string> +#include <utility> +#include <vector> +#include <algorithm> +#include <cmath> +#include <complex> +#include <stdlib.h> + +#include <systemc.h> + +using namespace std; + +class OpticalSignal { +public: + typedef complex<double> field_type; + +private: + // class variable for holding next id to attribute + static unsigned int nextId; + +public: + field_type m_field; // complex field of the signal (V/m) + uint32_t m_wavelength_id; // accesses the global vector of wavelengths + unsigned int m_id; + + // Constructor from values + OpticalSignal(const field_type &field = 0.0, + const double &wavelength = numeric_limits<double>::quiet_NaN()) + : m_field(field) + { + if (!isnan(wavelength)) + m_wavelength_id = getIDFromWavelength(wavelength); + else + m_wavelength_id = 0; + getNewId(); + } + + // Constructor sending an id + OpticalSignal(const field_type &field, + const uint32_t &wavelength_id) + : m_field(field) + , m_wavelength_id(wavelength_id) + { + getNewId(); + } + + // Copy constructor + OpticalSignal(const OpticalSignal &s) + : m_field(s.m_field) + , m_wavelength_id(s.m_wavelength_id) + , m_id(s.m_id) + { getNewId(); } + + virtual ~OpticalSignal() {} + + inline void getNewId() + { + m_id = nextId++; + } + + void setWavelength(const double &wavelength) + { + if (!isnan(wavelength)) + m_wavelength_id = getIDFromWavelength(wavelength); + else + m_wavelength_id = 0; + } + + double getWavelength() const; + + static double getWavelength(const uint32_t &wavelength_id); + + uint32_t getIDFromWavelength(const double &wavelength); + + inline static complex<double> amplitudePhaseToField(double amplitude, double phase) + { + return polar(amplitude, phase); + } + + inline double modulus() const { + return abs(m_field); + } + + inline double power() const { + return norm(m_field); + } + + inline double phase() const { + return arg(m_field); + } + + // Equality is used to determine if a signal changes + // which is why true `==` is used here in field + // TODO: check if we need to use is_close() instead + // For wavelength, there should be no need not to use `==` + // for now, as no calculation is done on it. But in the future, + // we might need to review this as well + bool operator==(const OpticalSignal &rhs) const + { +#if 0 + // Work with ID + return rhs.m_id == m_id + || (isnan(rhs.m_wavelength) && isnan(m_wavelength)); +#else + // Work with field value + return rhs.m_field == m_field + && rhs.m_wavelength_id == m_wavelength_id; +#endif + } + + bool operator!=(const OpticalSignal &rhs) const + { + return !(*this == rhs); + } + + bool operator<(const OpticalSignal &rhs) const + { + return rhs.modulus() < modulus(); + } + + inline void advancePhaseByPhase(const double &phase_rad) + { + m_field *= polar(1.0, phase_rad); + } + + inline void advancePhaseByTime(const double &time_s) + { + // phase in rad = w * t = (2pi * (c/lambda)) * t + constexpr const auto c_2pi = 2 * M_PI * 299792458.0; + const auto phase_rad = (c_2pi / this->getWavelength()) * time_s; + + advancePhaseByPhase(phase_rad); + } + + static OpticalSignal sumSignals( + OpticalSignal s0, + OpticalSignal s1); + + operator field_type() const { + return m_field; + } + + OpticalSignal operator=(const OpticalSignal &rhs) + { + m_field = rhs.m_field; + m_wavelength_id = rhs.m_wavelength_id; + getNewId(); + return *this; + } + + OpticalSignal &operator+=(const OpticalSignal &rhs); + OpticalSignal &operator-=(const OpticalSignal &rhs); + + OpticalSignal &operator*=(const field_type &rhs) + { + m_field *= rhs; + return *this; + } + + OpticalSignal &operator*=(const double &rhs) + { + m_field *= rhs; + return *this; + } + + OpticalSignal &operator/=(const double &rhs) + { + (*this) *= (1 / rhs); + return *this; + } + + inline friend OpticalSignal operator+(OpticalSignal lhs, const OpticalSignal &rhs); + inline friend OpticalSignal operator-(OpticalSignal lhs, const OpticalSignal &rhs); + + template <typename T> + inline friend OpticalSignal operator*(OpticalSignal lhs, const T &rhs); + + template <typename T> + inline friend OpticalSignal operator*(const T &lhs, OpticalSignal rhs); + + template <typename T> + inline friend OpticalSignal operator/(OpticalSignal lhs, const T &rhs); + + inline friend std::ostream &operator<<(std::ostream &os, const OpticalSignal &s); + inline friend void + sc_trace(sc_trace_file *tf, const OpticalSignal &s, const std::string &NAME); +}; + +OpticalSignal operator+(OpticalSignal lhs, const OpticalSignal &rhs) +{ + return lhs += rhs; +} + +OpticalSignal operator-(OpticalSignal lhs, const OpticalSignal &rhs) +{ + return lhs -= rhs; +} + +template <typename T> +OpticalSignal operator*(OpticalSignal lhs, const T &rhs) +{ + return lhs *= rhs; +} + +template <typename T> +OpticalSignal operator*(const T &lhs, OpticalSignal rhs) +{ + return rhs *= lhs; +} + +template <typename T> +inline OpticalSignal operator/(OpticalSignal lhs, const T &rhs) +{ + return lhs /= rhs; +} + +inline std::ostream &operator<<(std::ostream &os, const OpticalSignal &s) +{ + using std::defaultfloat; + using std::fixed; + using std::setfill; + using std::setprecision; + using std::setw; + + std::ostringstream oss; + + oss << "[" << setw(4) << setfill('0') << s.m_id << setfill(' ') << "] "; + oss << " @ " << setw(4) << setprecision(8) << s.getWavelength() * 1e9 << setw(2) + << (isnan(s.getWavelength()) ? "" : " nm"); + oss << " (" << setprecision(6) << fixed << s.modulus() << " V.m⁻¹"; + oss << ", " << setprecision(6) << fixed << s.power() << " W"; + oss << ", " << setprecision(4) << fixed << s.phase() << " rad"; + oss << ")"; + + os << oss.str(); + return os; +} + +inline void sc_trace(sc_trace_file *tf, const OpticalSignal &s, const std::string &NAME) +{ + cerr << "Use of deprecated" << __FUNCTION__<< endl; + + // Note: C++ complex doesn't provide access to real and imaginary part + // Therefore we cannot have references or pointers to it... + sc_trace(tf, s.m_id, NAME + ".id"); + sc_trace(tf, s.m_wavelength_id, NAME + ".wavelengthid"); +} 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); +} diff --git a/src/specs.cpp b/src/specs.cpp new file mode 100644 index 0000000..2b9727d --- /dev/null +++ b/src/specs.cpp @@ -0,0 +1,383 @@ +#include "bitstream_source.h" +#include "optical_signal.h" +#include "sysc_utils.h" +#include <specs.h> +#include "spx_module.h" +#include "value_list_source.h" +#include "electrical_value_list_source.h" + +#include <systemc.h> +#include <optical_output_port.h> +#include <spx_module.h> +#include <cw_source.h> +#include <probe.h> +#include <detector.h> +#include <generic_transmission_device.h> + +using std::string; + +SPECSConfig specsGlobalConfig { "SPX" }; + +SPECSConfig::SPECSConfig(sc_module_name name) + : sc_module(name) +{ + drop_all_events = false; + // Simulation options + engine_timescale = HUNDRED_FS; + simulation_mode = OpticalOutputPortMode::DEFAULT; + analysis_type = AnalysisType::TIME_DOMAIN; + + // Port options + default_abstol = 1e-8; + default_reltol = 1e-4; + default_resolution_multiplier = 1; + + // Trace options + trace_filename = "traces/delete_me"; + default_trace_file = nullptr; + trace_all_optical_nets = 1; +} + +void SPECSConfig::runAnalysis() +{ + applyEngineResolution(); + prepareSimulation(); + + switch (analysis_type) { + case CW_OPERATING_POINT: + runOPAnalysis(); + break; + case CW_SWEEP: + runDCAnalysis(); + break; + case TIME_DOMAIN: + runTRANAnalysis(); + break; + default: + cerr << "Undefined Analysis type"; + sc_stop(); + } +} + +void SPECSConfig::runOPAnalysis() +{ + auto all_probes = sc_get_all_module_by_type<Probe>(); + auto all_mlprobes = sc_get_all_module_by_type<MLambdaProbe>(); + auto all_photodetectors = sc_get_all_module_by_type<Detector>(); + auto all_oop = sc_get_all_module_by_type<OpticalOutputPort>(); + auto all_cws = sc_get_all_module_by_type<CWSource>(); + + // Run to initialize all threads and register first values + sc_start(); + + // Set values of signals according to NODESET directive + for (auto &nodeset_order : nodeset_orders) + { + nodeset_order.second.first->write(nodeset_order.second.second); + } + + // Disable probes for OP point + for (auto probe: all_probes) { + probe->enable = sc_logic(0); + } + + // Disable ML probes for OP point + for (auto mlprobe: all_mlprobes) { + mlprobe->enable = sc_logic(0); + } + + // Disable photodetectors + for (auto pdet: all_photodetectors) { + pdet->enable = sc_logic(0); + } + + // Set all ports mode to NO_DELAY + for (auto oop: all_oop) { + oop->m_mode = OpticalOutputPortMode::NO_DELAY; + } + + // Activate all CW sources + for (auto cws: all_cws) + cws->enable = sc_logic(1); + + // Run operating point simulation + sc_start(); + + // Print results on the command line + printOPAnalysisResult(); +} + +void SPECSConfig::runDCAnalysis() +{ + auto all_probes = sc_get_all_module_by_type<Probe>(); + auto all_mlprobes = sc_get_all_module_by_type<MLambdaProbe>(); + auto all_photodetectors = sc_get_all_module_by_type<Detector>(); + auto all_oop = sc_get_all_module_by_type<OpticalOutputPort>(); + auto all_sig = sc_get_all_object_by_type<sc_signal<OpticalSignal, SC_MANY_WRITERS>>(); + auto all_cws = sc_get_all_module_by_type<CWSource>(); + + // Run to initialize all threads and register first values + sc_start(); + + // Enable normal probes + for (auto probe: all_probes) { + probe->enable = sc_logic(1); + } + + // Disable ML probes + for (auto mlprobe: all_mlprobes) { + mlprobe->enable = sc_logic(0); + } + + // Enable photodetectors + for (auto pdet: all_photodetectors) { + // Remark: Photodetectors could in fact be left enabled ? + // Would that serve any purpose though...? + pdet->enable = sc_logic(0); + } + + // Set all ports mode to NO_DELAY + for (auto oop: all_oop) { + oop->m_mode = OpticalOutputPortMode::NO_DELAY; + } + + // Activate CW sources + for (auto cws: all_cws) { + cws->enable = sc_logic(1); + } + + const auto &order = *cw_sweep_orders.begin(); + wavelengths_vector.reserve(order.second.second.size()); + cout << "Starting sweep on " << order.first; + cout << " (" << order.second.second.size() << " points)" << endl; + for (const auto &val : order.second.second) + { + // Apply sweep param + order.second.first(val); + + //cout << order.first << " = " << val << endl; + + // Reset OOP and set internal signals to new wavelength + for (auto oop: all_oop) { + #if 1 // reset OOP + oop->reset(); + // TODO: also reset photodetectors? + #elif 0 // remove previous wavelengths from OOP but keep value + if (wavelengths_vector.size() >= 2) + { + oop->swap_wavelengths(wavelengths_vector.size()-1, wavelengths_vector.size()-2); + oop->delete_wavelength(wavelengths_vector.size()-2); + oop->m_skip_next_convergence_check = true; + } + #else + // do nothing + #endif + } + + // run simulation and advance one tick + sc_start(sc_time::from_value(1)); + + // Reset CW sources + for (auto cws: all_cws) + { + cws->reset.write(sc_logic(1)); + } + + //printOPAnalysisResult(); + } +} + +void SPECSConfig::runTRANAnalysis() +{ + auto all_probes = sc_get_all_module_by_type<Probe>(); + auto all_mlprobes = sc_get_all_module_by_type<MLambdaProbe>(); + auto all_photodetectors = sc_get_all_module_by_type<Detector>(); + auto all_oop = sc_get_all_module_by_type<OpticalOutputPort>(); + auto all_vl_src = sc_get_all_module_by_type<VLSource>(); + auto all_evl_src = sc_get_all_module_by_type<EVLSource>(); + + // Run OP analysis + runOPAnalysis(); + + // Set values of signals according to IC directive + for (auto &ic_order : ic_orders) + { + //ic_order.second.first->write(ic_order.second.second); + } + + // Enable probes + for (auto probe: all_probes) { + probe->enable = sc_logic(1); + } + + // Enable ML probes + for (auto mlprobe: all_mlprobes) { + mlprobe->enable = sc_logic(1); + } + + // Enable photodetectors + for (auto pdet: all_photodetectors) { + pdet->enable = sc_logic(1); + } + + // Re-apply configuration of all ports + for (auto oop: all_oop) { + oop->applyConfig(); + } + + // Activate TRAN sources + for (auto src: all_vl_src) + src->enable = sc_logic(1); + for (auto src: all_evl_src) + src->enable = sc_logic(1); + + // Strart TRAN simulation + if (isfinite(tran_duration)) + sc_start(tran_duration, SC_SEC); + else + sc_start(); + + cout << "Simulated " << sc_time_stamp() << endl; +} + +void SPECSConfig::applyDefaultOpticalOutputPortConfig() { + //assert(!oop_configs.empty()); + auto oop_default_config = make_shared<OpticalOutputPortConfig>(); + oop_default_config->m_mode = simulation_mode; + oop_default_config->m_abstol = default_abstol; + oop_default_config->m_reltol = default_reltol; + oop_default_config->m_timestep_value = default_resolution_multiplier; // relative to systemc timestep + + // apply default config to all optical output ports which don't have one + auto all_oop = sc_get_all_module_by_type<OpticalOutputPort>(); + for (auto oop: all_oop) { + if(!oop->getConfig().get()) + oop->setConfig(oop_default_config); + // FIXME: only works for first call to sc_start !! + } +} + +void SPECSConfig::applyDefaultTraceFileToAllSignals() { + if (!default_trace_file) + return; + + auto all_probes = sc_get_all_object_by_type<Probe>(); + for (auto p: all_probes) { + p->setTraceFile(default_trace_file); + } + + // auto all_mlprobes = sc_get_all_object_by_type<MLambdaProbe>(); + // for (auto p: all_mlprobes) { + // p->m_Tf = default_trace_file; + // p->prepare(); + // } + + auto all_mlambda_probes = sc_get_all_object_by_type<MLambdaProbe>(); + for (auto p: all_mlambda_probes) { + p->setTraceFile(default_trace_file); + } + + if (trace_all_optical_nets) + { + auto all_optical_sigs = sc_get_all_object_by_type<sc_signal<OpticalSignal, SC_MANY_WRITERS>>(); + for (auto &sig: all_optical_sigs) { + string signame = sig->name(); + + // Check if the signal is explicitly marked as uninteresting + stringstream ss (signame); + string base_signame; + while (getline (ss, base_signame, '/')) {} + if (base_signame.empty() || base_signame[0] == '_') + continue; + //cout << signame << endl; + + auto p = make_shared<Probe>(("PROBE{" + signame + "}").c_str(), true, true, true, true); + p->setTraceFile(default_trace_file); + p->p_in(*sig); + additional_objects.push_back(p); + } + } + // TODO: refactor ↓ + auto all_pdets = sc_get_all_object_by_type<Detector>(); + for (auto &pdet: all_pdets) { + string detname = pdet->name(); + cout << detname << endl; + pdet->trace(default_trace_file); + //sc_trace(default_trace_file, , (string(pdet->name()) + ".readout").c_str()); + } +} + +string SPECSConfig::analysisTypeDesc() const +{ + switch (analysis_type) { + case CW_OPERATING_POINT: + return "CW OPERATING POINT"; + case CW_SWEEP: + return "CW SWEEP"; + case TIME_DOMAIN: + return "TIME DOMAIN"; + default: + return "UNDEFINED"; + } +} + +void SPECSConfig::printConfig() const +{ + cout << "Current SPECS config: " << endl; + cout << "- simulation mode: " << oopPortMode2str(simulation_mode) << endl; + cout << "- analysis type: " << analysisTypeDesc() << endl; + cout << "- engine timescale (log10 of s): " << engine_timescale << endl; + cout << "- abstol: " << default_abstol << endl; + cout << "- default reltol: " << default_reltol << endl; + cout << "- resolution multiplier: " << default_resolution_multiplier << endl; + cout << "- trace all optical nets: " << trace_all_optical_nets << endl; +} + +void SPECSConfig::printOPAnalysisResult() const +{ + cout << "╔═══════════════════╗" << endl; + cout << "║ OPERATING POINT ║" << endl; + cout << "╠═══════════════════╣" << endl; + cout << "║ ╨───┄┄" << endl; + auto all_optical_sigs = sc_get_all_object_by_type<sc_signal<OpticalSignal, SC_MANY_WRITERS>>(); + for (auto &sig: all_optical_sigs) { + string signame = sig->name(); + cout << "║ "; + cout << "- " << signame << " (OPTICAL)" << " = " << sig->read() << endl; + } + cout << "║ ╥───┄┄" << endl; + cout << "╚═══════════════════╝" << endl; +} + +void SPECSConfig::prepareSimulation() { + auto all_spx_mod = sc_get_all_object_by_type<spx_module>(); + for (auto &mod : all_spx_mod) + mod->init(); + printConfig(); + verifyConfig(); + + { // open default trace_file + if (!default_trace_file && trace_filename.size()) + default_trace_file = sc_create_vcd_trace_file(trace_filename.c_str()); + + default_trace_file->set_time_unit(std::pow(10, 15 + engine_timescale), SC_FS); + } + + applyDefaultOpticalOutputPortConfig(); + applyDefaultTraceFileToAllSignals(); + if (default_trace_file) + sc_trace(default_trace_file, *this, ""); +} + +inline void sc_trace(sc_trace_file *tf, const SPECSConfig &s, string parent_tree) +{ + parent_tree += (parent_tree.size() ? "." : ""); + sc_trace(tf, s.default_abstol, parent_tree + "abstol"); + sc_trace(tf, s.default_reltol, parent_tree + "reltol"); + sc_trace(tf, s.default_resolution_multiplier, parent_tree + "resolution_multiplier"); + sc_trace(tf, s.engine_timescale, parent_tree + "engine_timescale"); + sc_trace(tf, s.simulation_mode, parent_tree + "simulation_mode"); + sc_trace(tf, s.analysis_type, parent_tree + "analysis_type"); + sc_trace(tf, s.trace_all_optical_nets, parent_tree + "trace_all_optical_nets"); + sc_trace(tf, s.verbose_component_initialization, parent_tree + "verbose_component_initialization"); +}
\ No newline at end of file diff --git a/src/specs.h b/src/specs.h new file mode 100644 index 0000000..fe56d6a --- /dev/null +++ b/src/specs.h @@ -0,0 +1,159 @@ +#pragma once + +#include <sysc_utils.h> +#include "optical_signal.h" +#include "optical_output_port.h" + +#include <systemc.h> +#include <vector> +#include <map> +#include <utility> +#include <functional> + +using std::vector; +using std::map; +using std::pair; +using std::make_shared; +using std::shared_ptr; +using std::string; +using std::function; + +namespace spx { + // Value types + typedef OpticalSignal oa_value_type; + typedef double ea_value_type; + typedef sc_logic ed_value_type; + + // Signal types + typedef sc_signal<oa_value_type, SC_MANY_WRITERS> oa_signal_type; + typedef sc_signal<ea_value_type, SC_MANY_WRITERS> ea_signal_type; + typedef sc_signal<ed_value_type, SC_MANY_WRITERS> ed_signal_type; + typedef sc_signal<sc_lv_base, SC_MANY_WRITERS> ed_bus_type; + + // Optical analog IF type + typedef sc_signal_in_if<oa_value_type> oa_if_in_type; + typedef sc_signal_out_if<oa_value_type> oa_if_out_type; + typedef sc_signal_inout_if<oa_value_type> oa_if_inout_type; + + // Electrical analog IF type + typedef sc_signal_in_if<ea_value_type> ea_if_in_type; + typedef sc_signal_out_if<ea_value_type> ea_if_out_type; + typedef sc_signal_inout_if<ea_value_type> ea_if_inout_type; + + // Optical analog ports + typedef sc_port<oa_if_in_type, 0, SC_ZERO_OR_MORE_BOUND> oa_port_in_type; + typedef sc_port<oa_if_out_type> oa_port_out_type; + typedef sc_port<oa_if_inout_type> oa_port_inout_type; + + // Electrical analog ports + typedef sc_port<ea_if_in_type, 0, SC_ZERO_OR_MORE_BOUND> ea_port_in_type; + typedef sc_port<ea_if_out_type> ea_port_out_type; + typedef sc_port<ea_if_inout_type> ea_port_inout_type; +}; + +class SPECSConfig : public sc_module { +public: + enum EngineTimescale { + ONE_SEC = 0, + HUNDRED_MS = -1, + TEN_MS = -2, + ONE_MS = -3, + HUNDRED_US = -4, + TEN_US = -5, + ONE_US = -6, + HUNDRED_NS = -7, + TEN_NS = -8, + ONE_NS = -9, + HUNDRED_PS = -10, + TEN_PS = -11, + ONE_PS = -12, + HUNDRED_FS = -13, + TEN_FS = -14, + ONE_FS = -15, + }; + + enum AnalysisType { + ANALYSIS_TYPE_MINVAL, + CW_OPERATING_POINT, + CW_SWEEP, + TIME_DOMAIN, + ANALYSIS_TYPE_MAXVAL, + + // aliases + DEFAULT = TIME_DOMAIN, + OP = CW_OPERATING_POINT, + DC = CW_SWEEP, + TRAN = TIME_DOMAIN, + UNDEFINED = ANALYSIS_TYPE_MAXVAL, + }; + + // Hold simulation objects + vector<shared_ptr<sc_object>> additional_objects; + map<string, pair<sc_signal<OpticalSignal, SC_MANY_WRITERS> *, OpticalSignal>> ic_orders; + map<string, pair<sc_signal<OpticalSignal, SC_MANY_WRITERS> *, OpticalSignal>> nodeset_orders; + + // Simulation options + EngineTimescale engine_timescale = ONE_FS; + OpticalOutputPortMode simulation_mode; + AnalysisType analysis_type; + map<string, pair<function<void(double)>, vector<double>>> cw_sweep_orders; + double tran_duration = std::numeric_limits<double>::infinity(); + + // Port options + double default_abstol = 1e-8; + double default_reltol = 1e-4; + sc_time::value_type default_resolution_multiplier = 1; + + // For multi-wavelength support + vector<double> wavelengths_vector; + + // Trace options + string trace_filename = ""; + sc_trace_file *default_trace_file = nullptr; + bool trace_all_optical_nets = 1; + + // other + sc_signal<bool, SC_MANY_WRITERS> drop_all_events; + bool verbose_component_initialization = true; + + SPECSConfig(sc_module_name name); + + ~SPECSConfig() {} + + virtual void before_end_of_elaboration() + {} + + void runAnalysis(); + void runOPAnalysis(); + void runDCAnalysis(); + void runTRANAnalysis(); + + void applyEngineResolution() { + // set engine time resolution + sc_set_time_resolution(std::pow(10, 15+engine_timescale), SC_FS); + } + void applyDefaultOpticalOutputPortConfig(); + void traceSettings(); + void applyDefaultTraceFileToAllSignals(); + void verifyConfig() + { + assert(default_abstol > 0); + assert(default_reltol > 0); + assert(default_resolution_multiplier >= 1); + assert(ONE_FS <= engine_timescale && engine_timescale <= ONE_SEC); + assert(PORT_MODE_MINVAL < simulation_mode && simulation_mode < PORT_MODE_MAXVAL); + assert(ANALYSIS_TYPE_MINVAL < analysis_type && analysis_type < ANALYSIS_TYPE_MAXVAL); + } + string analysisTypeDesc() const; + void printConfig() const; + void printOPAnalysisResult() const; + void prepareSimulation(); + inline void register_object(shared_ptr<sc_object> object) { + additional_objects.push_back(object); + } + + inline friend void + sc_trace(sc_trace_file *tf, const SPECSConfig &s, string parent_tree); +}; + +extern SPECSConfig specsGlobalConfig;
\ No newline at end of file diff --git a/src/tb/alltestbenches.cpp b/src/tb/alltestbenches.cpp new file mode 100644 index 0000000..107dfdc --- /dev/null +++ b/src/tb/alltestbenches.cpp @@ -0,0 +1,28 @@ +#include <map> +#include <tb/alltestbenches.h> + +#if defined(BUILD_TB) && BUILD_TB == 1 +std::map<std::string, tb_func_t> tb_map = { + { "wg", wg_tb_run }, + { "waveguide", wg_tb_run }, + { "merg", Merger_tb_run }, + { "merger", Merger_tb_run }, + { "dc", DirectionalCoupler_tb_run }, + { "directional_coupler", DirectionalCoupler_tb_run }, + { "spli", Splitter_tb_run }, + { "splitter", Splitter_tb_run }, + { "det", Detector_tb_run }, + { "detector", Detector_tb_run }, + { "pcm", PCMElement_tb_run }, + { "mzi", MZI_tb_run }, + { "ring", Ring_tb_run }, + { "ac_add_drop", freqsweep_tb_run_add_drop }, + { "ac_crow", crow_tb_run }, + { "lambda", lambda_tb_run }, + { "phaseshifter", ps_tb_run }, + { "ps", ps_tb_run }, + { "mesh", mesh_tb_run }, +}; +#else +std::map<std::string, tb_func_t> tb_map = {}; +#endif diff --git a/src/tb/alltestbenches.h b/src/tb/alltestbenches.h new file mode 100644 index 0000000..49043bb --- /dev/null +++ b/src/tb/alltestbenches.h @@ -0,0 +1,22 @@ +#pragma once + +#include <tb/detector_tb.h> +#include <tb/directional_coupler_tb.h> +#include <tb/merger_tb.h> +#include <tb/pcm_device_tb.h> +#include <tb/splitter_tb.h> +#include <tb/wg_tb.h> +#include "tb/mzi_tb.h" +#include "tb/ring_tb.h" +#include "tb/freqsweep_tb.h" +#include "tb/crow_tb.h" +#include "tb/lambda_tb.h" +#include "tb/phase_shifter_tb.h" +#include "mesh_tb.h" + +#include <map> +#include <string> + +typedef void (*tb_func_t)(); + +extern std::map<std::string, tb_func_t> tb_map; diff --git a/src/tb/crow_tb.cpp b/src/tb/crow_tb.cpp new file mode 100644 index 0000000..91a816c --- /dev/null +++ b/src/tb/crow_tb.cpp @@ -0,0 +1,216 @@ +#include <ctime> +#include <iomanip> +#include <tb/crow_tb.h> + +#include <cstdlib> +#include <unistd.h> +#include <chrono> + +#include <waveguide.h> +#include <directional_coupler.h> +#include <probe.h> +#include <crow.h> +#include <specs.h> +#include "time_monitor.h" + +#include "general_utils.h" + +using namespace std; +using namespace std::chrono; + + +/* ----------------------------------------------------------------------------- * + This testbench is called freqsweep because it is capable of this + type of simulation, but it is also possible to set it as a time-domain + simulation depending on the mode chosen when calling SPECS. Example: + + specs -t ac_add_drop -m fd -> will perform the frequency sweep + specs -t ac_add_drop -m td -> will perform the time domain simulation + +/ ----------------------------------------------------------------------------- */ + +// ---------------- Use this variable to change the size of the CROW ----------- + size_t nrings_crow = 3; +// ----------------------------------------------------------------------------- + +void crow_tb::run_td() +{ + bool verbose = true; + if (specsGlobalConfig.simulation_mode == OpticalOutputPortMode::TIME_DOMAIN) + { + int npulses = 4; + double lambda = 1554.3719e-9; + lambda = 1.561931948453715e-6; + lambda = 1538.3277504e-9; + lambda = 1543e-9; + lambda = 1556.5742279e-9; + lambda = 1556.32782563158025368466e-9; + lambda = 1556e-9; + lambda = 1553e-9; + //lambda = 1550e-9; + double tpulse = 1e-9; + double deadtime = tpulse; + + + // Wait one tick that all sc_threads are started and on their first `wait` call + wait(SC_ZERO_TIME); + + if (verbose) + { + cout << "----------------------------" <<endl; + cout << "Starting time-domain simulation" << endl; + } + for (int i = 0; i < npulses; ++i) + { + IN->write(OpticalSignal(1, lambda)); + wait(tpulse, SC_SEC); + // //ADD.write(OpticalSignal(polar<double>(1, M_PI_2), lambda)); + // //ADD.write(OpticalSignal(polar<double>(0,0), lambda)); + IN->write(OpticalSignal(0, lambda)); + // ADD.write(OpticalSignal(0, lambda)); + wait(deadtime, SC_SEC); + } + } + + while (true) { wait(); } +} + +void crow_tb::run_fd() +{ + bool verbose = true; + if (specsGlobalConfig.simulation_mode == OpticalOutputPortMode::FREQUENCY_DOMAIN) + { + auto lambda_center = 1550e-9; + auto lambda_span = 2e-9; + auto dlambda = 0.001e-9; + auto lambda_min = lambda_center - lambda_span/2; + auto lambda_max = lambda_center + lambda_span/2; + // lambda_min = 1545e-9; + // lambda_max = 1569e-9; + lambda_min = 299792458.0/195e12; + lambda_max = 299792458.0/190e12; + lambda_min = 1556e-9; + lambda_span = lambda_max - lambda_min; + dlambda = lambda_span / 10000; + + // Wait one tick that all sc_threads are started and on their first `wait` call + wait(SC_ZERO_TIME); + //wait(lambda_min, SC_SEC); + + auto tic = high_resolution_clock::now(); + auto toc = high_resolution_clock::now(); + + if (verbose) + { + cout << "----------------------------" <<endl; + cout << "Starting sweep" << endl; + } + int i = 0; + int n = ceil(lambda_span / dlambda); + for (auto lambda = lambda_min; lambda < lambda_max + dlambda; lambda += dlambda) + { + ++i; + if (true || (verbose && i % (int)(n/20) == 0)) + { + toc = high_resolution_clock::now(); + auto duration = duration_cast<microseconds>(toc - tic); + tic = toc; + cout << duration.count()/1000.0 << "ms" << endl; + cout << endl; + cout << fixed << setprecision(1) << (lambda-lambda_min)/lambda_span * 100.0 << "%" << endl; + cout << setprecision(20) << lambda * 1e9 << "nm" << endl; + } + IN->write(OpticalSignal(sqrt(1), lambda)); + ADD->write(OpticalSignal(0, lambda)); + //ADD.write(OpticalSignal(polar<double>(0, 0), lambda)); + //ADD.write(OpticalSignal(polar<double>(sqrt(0.5), -M_PI_2), lambda)); + wait(dlambda, SC_SEC); + } + if (verbose) + { + cout << "----------------------------" <<endl; + cout << "Sweep over (" << n << " points)" << endl; + } + } + while (true) { wait(); } +} + +void crow_tb::monitor() +{ + unsigned long long event_counter = 0; + while(true) + { + wait(); + continue; + event_counter++; + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN: " << IN->read() << std::endl + << "\tADD: " << ADD->read() << std::endl + << "\tTHROUGH: " << THROUGH->read() << std::endl + << "\tDROP: " << DROP->read() << std::endl + << "\tCOUNT: " << event_counter << std::endl; + } + +} + +void crow_tb_run() +{ + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type IN("IN"), THROUGH("THROUGH"), DROP("DROP"), ADD("ADD"); + + crow_tb tb1("tb1"); + tb1.IN(IN); + tb1.ADD(ADD); + tb1.THROUGH(THROUGH); + tb1.DROP(DROP); + + // pid_t pid = fork(); + CROW *pc; + // if(pid) + pc = new CROW("crow", nrings_crow); + // else + // pc = new Crow("crow", 5); + pc->p_in(IN); + pc->p_add(ADD); + pc->p_out_t(THROUGH); + pc->p_out_d(DROP); + + pc->setRingLength(2*30e-6); + pc->m_loss_db_cm = 2.0; + pc->m_coupling_through = 1 - pow(0.83645, 2.0); + // pc->m_coupling_through = 0.5; + pc->m_neff = 2.6391; + pc->m_ng = 4.3416; + + pc->m_ring_length = 500.0*1.55e-6 + 1.55e-6/2.0; + pc->m_loss_db_cm = 2.0; + pc->m_coupling_through = 1 - 0.2; + pc->m_neff = 1; + pc->m_ng = 2; + + pc->init(); //instantiating all nets + + Probe pthrough("pthrough", true, true, false, true); + pthrough.p_in(THROUGH); + // + Probe pcross("pcross", true, true, false, false); + pcross.p_in(DROP); + + specsGlobalConfig.trace_filename = "traces/crow_tb"; + + // Apply SPECS options specific to the testbench + // specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(50e-9, SC_SEC); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/crow_tb.h b/src/tb/crow_tb.h new file mode 100644 index 0000000..473708d --- /dev/null +++ b/src/tb/crow_tb.h @@ -0,0 +1,30 @@ +#pragma once + +#include <optical_signal.h> +#include <specs.h> +#include <systemc.h> + +class crow_tb : public sc_module { +public: + spx::oa_port_out_type IN; + spx::oa_port_out_type ADD; + spx::oa_port_in_type THROUGH; + spx::oa_port_in_type DROP; + + void run_fd(); + void run_td(); + void monitor(); + + SC_CTOR(crow_tb) + { + SC_THREAD(run_fd); + SC_THREAD(run_td); + SC_THREAD(monitor); + if (false) + sensitive << IN << ADD << THROUGH << DROP; + } +}; + +void crow_tb_run(); + +extern size_t nrings_crow; diff --git a/src/tb/detector_tb.cpp b/src/tb/detector_tb.cpp new file mode 100644 index 0000000..e296d26 --- /dev/null +++ b/src/tb/detector_tb.cpp @@ -0,0 +1,127 @@ +#include <ctime> +#include <iomanip> +#include <tb/detector_tb.h> + +#include "general_utils.h" + +void Detector_tb::run_1() +{ + IN->write(OpticalSignal(sqrt(1.0), 1550e-9)); + wait(100, SC_MS); + IN->write(OpticalSignal(0, 1550e-9)); + wait(100, SC_MS); + IN->write(OpticalSignal(sqrt(1.0), 1551e-9)); + + + + while (true) { wait(); } +} + +void Detector_tb::monitor() +{ + unsigned int event_counter = 0; + unsigned int success_counter = 0; + const unsigned int test_number = 3; + while(true) + { + wait(); + event_counter++; + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN: " << IN->read() << std::endl + << "\tREADOUT: " << READOUT.read() << std::endl + << "\tREADOUT2: " << READOUT2.read() << std::endl + << "\tCOUNT: " << event_counter << std::endl; + + if(event_counter == 2) + { + if ((is_close(READOUT.read(), 1, 1e-3)) && (is_close(READOUT2.read(), 1, 1e-3))) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 1A as both output currents!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 4) + { + if ((is_close(READOUT.read(), 0, 1e-4)) && (is_close(READOUT2.read(), 0, 1e-4))) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0A as output currents!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 6) + { + if (!is_close(READOUT.read(), READOUT2.read(), 1e-7)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected slightly different currents (different noise seed)!" << std::endl; + std::cout << "Is the noise ON?" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Test finished!" << std::endl; + std::cout << "Success rate: " << success_counter << "/" << test_number << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + } +} + +void Detector_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type IN; + sc_signal<double> READOUT, READOUT2; + + Detector uut("uut", 1, 100e-12, false); + uut.p_in(IN); + uut.p_readout(READOUT); + + Detector uut2("uut2", 1, 100e-12, false); + uut2.p_in(IN); + uut2.p_readout(READOUT2); + + Detector_tb tb("tb"); + tb.IN(IN); + tb.READOUT(READOUT); + tb.READOUT2(READOUT2); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "detector_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + Probe probe_detector("probe_detector",specsGlobalConfig.default_trace_file); + probe_detector.p_in(IN); + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // custom traces should come after prepareSimulation !! + sc_trace(specsGlobalConfig.default_trace_file, READOUT, "READOUT"); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/detector_tb.h b/src/tb/detector_tb.h new file mode 100644 index 0000000..39b05dc --- /dev/null +++ b/src/tb/detector_tb.h @@ -0,0 +1,30 @@ +#pragma once + +#include <detector.h> +#include <optical_signal.h> +#include <systemc.h> +#include <probe.h> +#include <specs.h> +#include <waveguide.h> + +SC_MODULE(Detector_tb) +{ +public: + spx::oa_port_out_type IN; + sc_in<double> READOUT, READOUT2; + + void run_1(); + void monitor(); + SC_CTOR(Detector_tb) + { + SC_HAS_PROCESS(Detector_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN << READOUT << READOUT2; + } +}; + +void Detector_tb_run(); + diff --git a/src/tb/directional_coupler_tb.cpp b/src/tb/directional_coupler_tb.cpp new file mode 100644 index 0000000..1954414 --- /dev/null +++ b/src/tb/directional_coupler_tb.cpp @@ -0,0 +1,150 @@ +#include <ctime> +#include <iomanip> +#include <tb/directional_coupler_tb.h> + +#include "general_utils.h" +#include "generic_2x2_coupler.h" + +void DirectionalCoupler_tb::run_1() +{ + IN1->write(OpticalSignal(1.0, 1550e-9)); + wait(100, SC_MS); + IN2->write(OpticalSignal(1.0, 1550e-9)); + wait(100, SC_MS); + + while (true) { wait(); } +} + +void DirectionalCoupler_tb::monitor() +{ + unsigned int event_counter = 0; + unsigned int success_counter = 0; + const unsigned int test_number = 3; + + while(true) + { + wait(); + event_counter++; + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN1: " << IN1->read() << std::endl + << "\tIN2: " << IN2->read() << std::endl + << "\tOUT1: " << OUT1->read() << std::endl + << "\tOUT2: " << OUT2->read() << std::endl + << "\tCOUNT: " << event_counter << std::endl; + + if(event_counter == 2) + { + if (is_close(norm(OUT1->read().m_field), 0.05, 1e-4) && is_close(norm(OUT2->read().m_field), 0.45, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0.05W/0.45 as output power!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + if (is_close(arg(OUT2->read().m_field), 1.5708, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected pi/2 as phase in OUT2!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 4) + { + if (is_close(arg(OUT1->read().m_field), 1.2490, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 1.2490 as phase in OUT1!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Test finished!" << std::endl; + std::cout << "Success rate: " << success_counter << "/" << test_number << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } +} + +void DirectionalCoupler_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + + spx::oa_signal_type IN1, IN2, IN3, IN4, OUT1, OUT2; + spx::oa_signal_type TERM_r, TERM_w; + + // Unidirectional variant + // DirectionalCoupler uut("uut", 0.5, 0); + // uut.m_dc_through_coupling_power = 0.1; + // uut.m_dc_loss = 3.0103; + + // uut.p_in1(IN1); + // uut.p_in2(IN2); + // uut.p_out1(OUT1); + // uut.p_out2(OUT2); + + // Bidirectional variant + DirectionalCouplerBi uut("uut", 0.5, 0); + uut.m_dc_through_coupling_power = 0.1; + uut.m_dc_loss = 3.0103; + + uut.p0_in(IN1); + uut.p0_out(TERM_w); + uut.p1_in(IN2); + uut.p1_out(TERM_w); + uut.p2_in(TERM_r); + uut.p2_out(OUT1); + uut.p3_in(TERM_r); + uut.p3_out(OUT2); + + // Generic bidirectional variant + // Generic2x2Coupler uut("uut", 1-0.1, 3.0103); + // uut.ports_in[0]->bind(IN1); + // uut.ports_in[1]->bind(IN2); + // uut.ports_in[2]->bind(TERM_r); + // uut.ports_in[3]->bind(TERM_r); + // uut.ports_out[0]->bind(TERM_w); + // uut.ports_out[1]->bind(TERM_w); + // uut.ports_out[2]->bind(OUT1); + // uut.ports_out[3]->bind(OUT2); + + DirectionalCoupler_tb tb("tb"); + tb.IN1(IN1); + tb.IN2(IN2); + tb.OUT1(OUT1); + tb.OUT2(OUT2); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "directional_coupler_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + Probe probe_dc_1("probe_dc_1",specsGlobalConfig.default_trace_file); + probe_dc_1.p_in(OUT1); + Probe probe_dc_2("probe_dc_2",specsGlobalConfig.default_trace_file); + probe_dc_2.p_in(OUT2); + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/directional_coupler_tb.h b/src/tb/directional_coupler_tb.h new file mode 100644 index 0000000..c137b31 --- /dev/null +++ b/src/tb/directional_coupler_tb.h @@ -0,0 +1,31 @@ +#pragma once + +#include <directional_coupler.h> +#include <optical_signal.h> +#include <systemc.h> +#include <probe.h> +#include <specs.h> + +SC_MODULE(DirectionalCoupler_tb) +{ +public: + spx::oa_port_out_type IN1; + spx::oa_port_out_type IN2; + spx::oa_port_in_type OUT1; + spx::oa_port_in_type OUT2; + + void run_1(); + void monitor(); + SC_CTOR(DirectionalCoupler_tb) + { + SC_HAS_PROCESS(DirectionalCoupler_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN1 << IN2 << OUT1 << OUT2; + } +}; + +void DirectionalCoupler_tb_run(); + diff --git a/src/tb/freqsweep_tb.cpp b/src/tb/freqsweep_tb.cpp new file mode 100644 index 0000000..f44aa34 --- /dev/null +++ b/src/tb/freqsweep_tb.cpp @@ -0,0 +1,213 @@ +#include <ctime> +#include <iomanip> +#include <tb/freqsweep_tb.h> + +#include <cstdlib> +#include <unistd.h> + +#include "time_monitor.h" + +#include "general_utils.h" + +/* ----------------------------------------------------------------------------- * + This testbench is called freqsweep because it is capable of this + type of simulation, but it is also possible to set it as a time-domain + simulation depending on the mode chosen when calling SPECS. Example: + + specs -t ac_add_drop -m fd -> will perform the frequency sweep + specs -t ac_add_drop -m td -> will perform the time domain simulation + +/ ----------------------------------------------------------------------------- */ + +void freqsweep_tb::run_1() +{ + if (specsGlobalConfig.simulation_mode == OpticalOutputPortMode::FREQUENCY_DOMAIN) + { + auto lambda_center = 1550.3e-9; + auto lambda_span = 1e-9; + auto dlambda = 0.002e-9; + auto lambda_min = lambda_center - lambda_span/2; + auto lambda_max = lambda_center + lambda_span/2; + // lambda_min = 1550.250e-9; + // lambda_max = 1550.303e-9; + //wait(lambda_min * 1e9, SC_SEC); + + wait(SC_ZERO_TIME); + cout << "----------------------------" <<endl; + cout << "Starting sweep" << endl; + for (auto lambda = lambda_min; lambda < lambda_max + dlambda; lambda += dlambda) + { + cout << setprecision(7) << lambda * 1e9 << "nm" << endl; + IN->write(OpticalSignal(1, lambda)); + wait(dlambda, SC_SEC); + } + } else { + for (int i = 0; i < 1; ++i) + { + auto lambda = 1550.302e-9; + IN->write(OpticalSignal(1,lambda)); + wait(1, SC_NS); + IN->write(OpticalSignal(0, lambda)); + wait(1, SC_NS); + } + } + + while (true) { wait(); } +} + +void freqsweep_tb::monitor() +{ + unsigned int event_counter = 0; + unsigned int success_counter = 0; + const unsigned int test_number = 3; + while(true) + { + wait(); + continue; + } +} + +void freqsweep_tb_run_add_drop() +{ + specsGlobalConfig.applyEngineResolution(); + //specsGlobalConfig.oop_configs[0]->m_mode = OpticalOutputPortMode::EVENT_DRIVEN; + //specsGlobalConfig.oop_configs[0]->m_mode = OpticalOutputPortMode::FREQUENCY_DOMAIN; + + // First test for the LBR paper to DAC + // Single ring resonator, compare the response to theoretical + // Also compare time of simulation wrt photontorch for same ring. + + // Characteristics: + // Add-drop ring + // Internal wg length (cm): + // Internal wg loss (dB/cm): + // Internal wg neff: + // Internal wg ng: + // DC ratio : 0.15 cross, 0.85 through + // DC coupling loss (dB) : 0 + // Total length for resonance: lambda/2neff + double neff = 1.0; + double loss_db_cm = 1.0; + double coupling_through = 0.85; + + spx::oa_signal_type IN, T_OUT, X_OUT, X_TERM; + spx::oa_signal_type INNER_RING[4]; + + freqsweep_tb tb1("tb1"); + tb1.IN(IN); + tb1.OUT(X_OUT); + + DirectionalCoupler dc1("dc1", coupling_through, 0); + dc1.p_in1(INNER_RING[0]); + dc1.p_out1(INNER_RING[1]); + dc1.p_in2(IN); + dc1.p_out2(T_OUT); + + Waveguide wg1("wg1", 100.0*1550.0e-6/(2*neff), loss_db_cm, neff, neff); + wg1.p_in(INNER_RING[1]); + wg1.p_out(INNER_RING[2]); + + Waveguide wg2("wg2", 100.0*1550.0e-6/(2*neff), loss_db_cm, neff, neff); + wg2.p_in(INNER_RING[3]); + wg2.p_out(INNER_RING[0]); + + DirectionalCoupler dc2("dc2", coupling_through, 0); + dc2.p_in1(X_TERM); + dc2.p_out1(X_OUT); + dc2.p_in2(INNER_RING[2]); + dc2.p_out2(INNER_RING[3]); + + Probe pthrough("ptrough"); + pthrough.p_in(T_OUT); + + Probe pcross("pcross"); + pcross.p_in(X_OUT); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "freqsweep_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + // could have forced frequency domain like this, but taking command line input instead + // specsGlobalConfig.simulation_mode = OpticalOutputPortMode::FREQUENCY_DOMAIN; + specsGlobalConfig.trace_all_optical_nets = 0; + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} + +// void freqsweep_tb_run_crow() +// { +// if (specsGlobalConfig.oop_configs[0]->m_mode == OpticalOutputPortMode::FREQUENCY_DOMAIN) +// specsGlobalConfig.engine_timescale = SPECSConfig::ONE_FS; + +// specsGlobalConfig.applyEngineResolution(); + +// sc_signal<OpticalSignal> IN("IN"), T_OUT("T_OUT"), X_OUT("X_OUT"), ADD("ADD"); + +// freqsweep_tb tb1("tb1"); +// tb1.IN(IN); +// tb1.IN(IN); +// tb1.OUT(T_OUT); + +// // pid_t pid = fork(); +// CROW *pc; +// // if(pid) +// pc = new CROW("crow", nrings); +// // else +// // pc = new CROW("crow", 5); +// pc->p_in(IN); +// pc->p_add(ADD); +// pc->p_out_t(T_OUT); +// pc->p_out_d(X_OUT); + +// Probe pthrough("ptrough"); +// pthrough.p_in(T_OUT); +// // +// Probe pcross("pcross"); +// pcross.p_in(X_OUT); + +// // shared_ptr<TimeMonitor> tm; +// // if (specsGlobalConfig.oop_configs[0]->m_mode == OpticalOutputPortMode::FREQUENCY_DOMAIN) +// // tm = make_shared<TimeMonitor>("TM", 0, 0.01); +// // else +// // tm = make_shared<TimeMonitor>("TM", 1e-14, 0.5); + +// // // Open Trace file +// // sc_trace_file *Tf; +// // std::time_t now = std::time(nullptr); +// // char mbstr[100]; +// // std::strftime(mbstr, sizeof(mbstr), "%F-%H-%M-%S-", std::localtime(&now)); +// // +// // std::string trace_filename = "traces/"; +// // //trace_filename += mbstr; +// // trace_filename += "freqsweep_tb"; + +// // auto engineTime = std::pow(10, 15+specsGlobalConfig.engine_timescale); +// // Tf = sc_create_vcd_trace_file(trace_filename.c_str()); +// // ((sc_trace_file *)Tf)->set_time_unit(engineTime, SC_FS); +// // sc_trace(Tf, IN, "IN"); +// // sc_trace(Tf, X_OUT, "X_OUT"); +// // sc_trace(Tf, T_OUT, "T_OUT"); + +// if (specsGlobalConfig.trace_filename.empty()) +// specsGlobalConfig.trace_filename = "traces/crow_tb"; + +// // Start simulation +// specsGlobalConfig.prepareSimulation(); +// sc_start(); // run until sc_stop() + +// std::cout << std::endl << std::endl; +// std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + +// sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +// } diff --git a/src/tb/freqsweep_tb.h b/src/tb/freqsweep_tb.h new file mode 100644 index 0000000..d5a328c --- /dev/null +++ b/src/tb/freqsweep_tb.h @@ -0,0 +1,33 @@ +#pragma once + +#include <optical_signal.h> +#include <systemc.h> +#include <waveguide.h> +#include <directional_coupler.h> +#include <probe.h> +#include <crow.h> +#include <specs.h> + +SC_MODULE(freqsweep_tb) +{ +public: + spx::oa_port_out_type IN; + spx::oa_port_in_type OUT; + + void run_1(); + void monitor(); + SC_CTOR(freqsweep_tb) + { + SC_HAS_PROCESS(freqsweep_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN << OUT; + } +}; + +//void freqsweep_tb_run(); +void freqsweep_tb_run_add_drop(); +void freqsweep_tb_run_crow(); + diff --git a/src/tb/lambda_tb.cpp b/src/tb/lambda_tb.cpp new file mode 100644 index 0000000..96a5084 --- /dev/null +++ b/src/tb/lambda_tb.cpp @@ -0,0 +1,102 @@ +#include <ctime> +#include <iomanip> +#include <tb/lambda_tb.h> + +#include "general_utils.h" +#include "devices/generic_waveguide.h" + +void lambda_tb::run_1() +{ + // IN1.write(OpticalSignal(sqrt(2.0), 1550e-9)); + // wait(0, SC_FS); + // IN2.write(OpticalSignal(sqrt(2.0), 1660e-9)); + // while(true) {wait();} + wait(1,SC_SEC); + sc_stop(); +} + +void lambda_tb::monitor() +{ + unsigned int event_counter = 0; + unsigned int success_counter = 0; + const unsigned int test_number = 0; + while(true) + { + wait(); + event_counter++; + std::cout << sc_time_stamp() << ":" << std::endl + << "\tOUT: " << OUT.read() << std::endl + << "\tCOUNT: " << event_counter << std::endl; + } + +} + +void lambda_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type I1, I2, YOUT, WGOUT; + spx::oa_signal_type DCO1, DCO2, DCI1, DCI2; + + CWSource cw1("cw1"); + cw1.setWavelength(1550e-9); + cw1.setPower(2); + cw1.enable.write(SC_LOGIC_1); + cw1.p_out(I1); + + CWSource cw2("cw2"); + cw2.setWavelength(1660e-9); + cw2.setPower(2); + cw2.enable.write(SC_LOGIC_1); + cw2.p_out(I2); + + Merger merg1("merg1"); + merg1.p_in1(I1); + merg1.p_in2(I2); + merg1.p_out(YOUT); + + Waveguide wg1("wg1", 0.1, 0, 1, 1); + wg1.m_attenuation_dB_cm = 0; + wg1.p_in(YOUT); + wg1.p_out(DCI1); + + DirectionalCoupler dc1("dc1"); + dc1.p_in1(DCI1); + dc1.p_in2(DCI2); + dc1.p_out1(DCO1); + dc1.p_out2(DCO2); + + Waveguide wg2("wg2", 100*1001*1550e-9/(2), 0, 1, 1); + wg2.p_in(DCO2); + wg2.p_out(DCI2); + + lambda_tb tb("tb"); + tb.OUT(DCO1); + // tb.IN1(I1); + // tb.IN2(I2); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "lambda_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + MLambdaProbe probe("probe_lambda", {1550e-9, 1650e-9, 1660e-9}); + // Probe probe("test_probe",specsGlobalConfig.default_trace_file); + probe.p_in(DCO1); + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/lambda_tb.h b/src/tb/lambda_tb.h new file mode 100644 index 0000000..a5dc203 --- /dev/null +++ b/src/tb/lambda_tb.h @@ -0,0 +1,31 @@ +#pragma once + +#include <optical_signal.h> +#include <systemc.h> +#include <waveguide.h> +#include <merger.h> +#include <directional_coupler.h> +#include <cw_source.h> +#include <probe.h> +#include <specs.h> + +SC_MODULE(lambda_tb) +{ +public: + sc_in<OpticalSignal> OUT; + // sc_out<OpticalSignal> IN1, IN2; + + void run_1(); + void monitor(); + SC_CTOR(lambda_tb) + { + SC_HAS_PROCESS(lambda_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << OUT; + } +}; + +void lambda_tb_run(); diff --git a/src/tb/merger_tb.cpp b/src/tb/merger_tb.cpp new file mode 100644 index 0000000..1c86643 --- /dev/null +++ b/src/tb/merger_tb.cpp @@ -0,0 +1,126 @@ +#include <ctime> +#include <iomanip> +#include <tb/merger_tb.h> + +#include "general_utils.h" + +void Merger_tb::run_1() +{ + IN1->write(OpticalSignal(1.0, 1550e-9)); + wait(100, SC_MS); + IN2->write(OpticalSignal(1.0, 1550e-9)); + wait(100, SC_MS); + IN1->write(OpticalSignal(sqrt(2.0), 1550e-9)); + IN2->write(OpticalSignal(sqrt(2.0), 1550e-9)); + wait(100, SC_MS); +} + +void Merger_tb::monitor() +{ + unsigned int event_counter = 0; + unsigned int success_counter = 0; + const unsigned int test_number = 4; + while(true) + { + wait(); + event_counter++; + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN1: " << IN1->read() << std::endl + << "\tIN2: " << IN2->read() << std::endl + << "\tOUT: " << OUT->read() << std::endl + << "\tCOUNT: " << event_counter << std::endl; + if(event_counter == 2) + { + if (is_close(norm(OUT->read().m_field), 0.5, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0.5W as output power!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + if (is_close(arg(OUT->read().m_field), 0, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0 as output phase!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 4) + { + if (is_close(norm(OUT->read().m_field), 2, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 2W as output power!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 6) + { + if (is_close(norm(OUT->read().m_field), 4, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 2W as output power!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Test finished!" << std::endl; + std::cout << "Success rate: " << success_counter << "/" << test_number << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } +} + +void Merger_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type IN1, IN2, OUT; + + Merger uut("uut", 3.0103); + uut.m_attenuation_dB = 0; + uut.p_in1(IN1); + uut.p_in2(IN2); + uut.p_out(OUT); + + Merger_tb tb("tb"); + tb.IN1(IN1); + tb.IN2(IN2); + tb.OUT(OUT); + + // Open Trace file + // sc_trace_file *Tf; + std::string trace_filename = "traces/"; + trace_filename += "merger_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + Probe probe_merger("probe_merger",specsGlobalConfig.default_trace_file); + probe_merger.p_in(OUT); + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/merger_tb.h b/src/tb/merger_tb.h new file mode 100644 index 0000000..a71fd2a --- /dev/null +++ b/src/tb/merger_tb.h @@ -0,0 +1,30 @@ +#pragma once + +#include <merger.h> +#include <optical_signal.h> +#include <systemc.h> +#include <probe.h> +#include <specs.h> + +SC_MODULE(Merger_tb) +{ +public: + spx::oa_port_out_type IN1; + spx::oa_port_out_type IN2; + spx::oa_port_in_type OUT; + + void run_1(); + void monitor(); + SC_CTOR(Merger_tb) + { + SC_HAS_PROCESS(Merger_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN1 << IN2 << OUT; + } +}; + +void Merger_tb_run(); + diff --git a/src/tb/mesh_tb.cpp b/src/tb/mesh_tb.cpp new file mode 100644 index 0000000..9effd11 --- /dev/null +++ b/src/tb/mesh_tb.cpp @@ -0,0 +1,130 @@ +#include <ctime> +#include <iomanip> +#include <tb/mesh_tb.h> + +#include "general_utils.h" + +void mesh_tb::run_1() +{ + IN->write(OpticalSignal(sqrt(1.0), 1550e-9)); + vtheta.write(M_PI); + vphi.write(1); + wait(1, SC_NS); + // vtheta.write(M_PI_2); + // wait(1, SC_NS); + // vtheta.write(M_PI); + // wait(1, SC_NS); + // vphi.write(1); + // wait(1, SC_NS); + + while (true) { wait(); } +} + +void mesh_tb::monitor() +{ + while(true) + { + wait(); + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN: " << IN->read() << std::endl + << "\tPHI: " << vphi.read() << std::endl + << "\tTHETA: " << vtheta.read() << std::endl + << "\tOUT1: " << OUT1->read() << std::endl + << "\tOUT2: " << OUT2->read() << std::endl + << "\tOUT3: " << OUT3->read() << std::endl + << "\tOUT4: " << OUT4->read() << std::endl + << "\tOUT5: " << OUT5->read() << std::endl; + } + +} + +void mesh_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type SIG, ZERO, OUT1, OUT2, OUT3, OUT4, OUT5; + sc_signal<double> vphi,vtheta,e_zero; + + Clements mesh1("mesh1", 5, 1, 0, 0, 0, 0, 1); + mesh1.p_in[0]->bind(SIG); + mesh1.p_in[1]->bind(SIG); + mesh1.p_in[2]->bind(SIG); + mesh1.p_in[3]->bind(SIG); + mesh1.p_in[4]->bind(SIG); + mesh1.p_out[0]->bind(OUT1); + mesh1.p_out[1]->bind(OUT2); + mesh1.p_out[2]->bind(OUT3); + mesh1.p_out[3]->bind(OUT4); + mesh1.p_out[4]->bind(OUT5); + + mesh1.p_vphi[0]->bind(e_zero); + mesh1.p_vtheta[0]->bind(vtheta); + + mesh1.p_vphi[1]->bind(e_zero); + mesh1.p_vtheta[1]->bind(e_zero); + + mesh1.p_vphi[2]->bind(e_zero); + mesh1.p_vtheta[2]->bind(vtheta); + + mesh1.p_vphi[3]->bind(e_zero); + mesh1.p_vtheta[3]->bind(e_zero); + + mesh1.p_vphi[4]->bind(e_zero); + mesh1.p_vtheta[4]->bind(e_zero); + + mesh1.p_vphi[5]->bind(e_zero); + mesh1.p_vtheta[5]->bind(e_zero); + + mesh1.p_vphi[6]->bind(e_zero); + mesh1.p_vtheta[6]->bind(e_zero); + + mesh1.p_vphi[7]->bind(e_zero); + mesh1.p_vtheta[7]->bind(e_zero); + + mesh1.p_vphi[8]->bind(e_zero); + mesh1.p_vtheta[8]->bind(e_zero); + + mesh1.p_vphi[9]->bind(e_zero); + mesh1.p_vtheta[9]->bind(e_zero); + + mesh1.init(); + + mesh_tb tb("tb"); + tb.IN(SIG); + tb.OUT1(OUT1); + tb.OUT2(OUT2); + tb.OUT3(OUT3); + tb.OUT4(OUT4); + tb.OUT5(OUT5); + tb.vphi(vphi); + tb.vtheta(vtheta); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "mesh_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + + Probe probe_mzi_1("probe_mzi_1",specsGlobalConfig.default_trace_file); + probe_mzi_1.p_in(OUT1); + Probe probe_mzi_2("probe_mzi_2",specsGlobalConfig.default_trace_file); + probe_mzi_2.p_in(OUT2); + Probe probe_in("probe_in",specsGlobalConfig.default_trace_file); + probe_in.p_in(SIG); + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/mesh_tb.h b/src/tb/mesh_tb.h new file mode 100644 index 0000000..1e7e3dd --- /dev/null +++ b/src/tb/mesh_tb.h @@ -0,0 +1,29 @@ +#pragma once + +#include <optical_signal.h> +#include <systemc.h> +#include <probe.h> +#include <specs.h> +#include <clements.h> + +SC_MODULE(mesh_tb) +{ +public: + spx::oa_port_out_type IN; + spx::oa_port_in_type OUT1, OUT2, OUT3, OUT4, OUT5; + sc_out<double> vphi,vtheta; + + void run_1(); + void monitor(); + SC_CTOR(mesh_tb) + { + SC_HAS_PROCESS(mesh_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN << OUT1 << OUT2 << OUT3 << OUT4 << OUT5 << vphi << vtheta; + } +}; + +void mesh_tb_run(); diff --git a/src/tb/mzi_tb.cpp b/src/tb/mzi_tb.cpp new file mode 100644 index 0000000..2b9905a --- /dev/null +++ b/src/tb/mzi_tb.cpp @@ -0,0 +1,86 @@ +#include <ctime> +#include <iomanip> +#include <tb/mzi_tb.h> + +#include "general_utils.h" + +void mzi_tb::run_1() +{ + IN->write(OpticalSignal(sqrt(1.0), 1550e-9)); + wait(1, SC_NS); + vtheta.write(M_PI_2); + wait(1, SC_NS); + vtheta.write(M_PI); + wait(1, SC_NS); + vphi.write(1); + wait(1, SC_NS); + + while (true) { wait(); } +} + +void mzi_tb::monitor() +{ + while(true) + { + wait(); + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN: " << IN->read() << std::endl + << "\tPHI: " << vphi.read() << std::endl + << "\tTHETA: " << vtheta.read() << std::endl + << "\tOUT1: " << OUT1->read() << std::endl + << "\tOUT2: " << OUT2->read() << std::endl; + } + +} + +void MZI_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type IN1, IN2, OUT1, OUT2; + spx::ea_signal_type vphi,vtheta; + + MZI mzi1("mzi1",0,0,0,0,0,1); + mzi1.p_in1(IN1); + mzi1.p_in2(IN2); + mzi1.p_out1(OUT1); + mzi1.p_out2(OUT2); + mzi1.p_vphi(vphi); + mzi1.p_vtheta(vtheta); + + mzi_tb tb("tb"); + tb.IN(IN1); + tb.OUT1(OUT1); + tb.OUT2(OUT2); + tb.vphi(vphi); + tb.vtheta(vtheta); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "mzi_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + + Probe probe_mzi_1("probe_mzi_1",specsGlobalConfig.default_trace_file); + probe_mzi_1.p_in(OUT1); + Probe probe_mzi_2("probe_mzi_2",specsGlobalConfig.default_trace_file); + probe_mzi_2.p_in(OUT2); + Probe probe_in("probe_in",specsGlobalConfig.default_trace_file); + probe_in.p_in(IN1); + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/mzi_tb.h b/src/tb/mzi_tb.h new file mode 100644 index 0000000..16ed624 --- /dev/null +++ b/src/tb/mzi_tb.h @@ -0,0 +1,31 @@ +#pragma once + +#include <optical_signal.h> +#include <systemc.h> +#include <waveguide.h> +#include <directional_coupler.h> +#include <mzi.h> +#include <probe.h> +#include <specs.h> + +SC_MODULE(mzi_tb) +{ +public: + spx::oa_port_out_type IN; + spx::oa_port_in_type OUT1, OUT2; + sc_out<double> vphi,vtheta; + + void run_1(); + void monitor(); + SC_CTOR(mzi_tb) + { + SC_HAS_PROCESS(mzi_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN << OUT1 << OUT2 << vphi << vtheta; + } +}; + +void MZI_tb_run(); diff --git a/src/tb/pcm_device_tb.cpp b/src/tb/pcm_device_tb.cpp new file mode 100644 index 0000000..b279e6c --- /dev/null +++ b/src/tb/pcm_device_tb.cpp @@ -0,0 +1,129 @@ +#include <ctime> +#include <iomanip> +#include <tb/pcm_device_tb.h> + +#include "general_utils.h" + +void PCMElement_tb::run_1() +{ + IN->write(OpticalSignal(sqrt(1.0e-3), 1.55e-6)); + wait(10, SC_MS); + IN->write(OpticalSignal(sqrt(2*1e-3), 1.55e-6)); // to 2W + wait(10, SC_MS); + IN->write(OpticalSignal(0, 1.55e-6)); + wait(10, SC_MS); + IN->write(OpticalSignal(sqrt(1.0e-3), 1.55e-6)); + wait(10, SC_MS); + IN->write(OpticalSignal(sqrt(2*1.0e-3), 1.55e-6)); + wait(10, SC_MS); + IN->write(OpticalSignal(0, 1.55e-6)); + wait(10, SC_MS); + IN->write(OpticalSignal(sqrt(1.0e-3), 1.55e-6)); + + + while (true) { wait(); } +} + +void PCMElement_tb::monitor() +{ + unsigned int event_counter = 0; + unsigned int success_counter = 0; + const unsigned int test_number = 3; + while(true) + { + wait(); + event_counter++; + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN: " << IN->read() << std::endl + << "\tOUT: " << OUT->read() << std::endl + << "\tCOUNT: " << event_counter << std::endl; + + if(event_counter == 2) + { + if (is_close(norm(OUT->read().m_field), 0.000850, 1e-8)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0.850mW as output power!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 8) + { + if (is_close(norm(OUT->read().m_field), 0.000854758, 1e-8)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0.854mW as output power!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 14) + { + if (is_close(norm(OUT->read().m_field), 0.000859495, 1e-8)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0.859mW as output power!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Test finished!" << std::endl; + std::cout << "Success rate: " << success_counter << "/" << test_number << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } +} + +void PCMElement_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type IN, OUT; + + PCMElement pcm("pcm", 25e-6, 63, 0); + pcm.p_in(IN); + pcm.p_out(OUT); + + PCMElement_tb tb("tb"); + tb.IN(IN); + tb.OUT(OUT); + + // Open Trace file + + std::string trace_filename = "traces/"; + trace_filename += "pcm_device_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + Probe probe_in("in",specsGlobalConfig.default_trace_file); + probe_in.p_in(IN); + + Probe probe_out("out",specsGlobalConfig.default_trace_file); + probe_out.p_in(OUT); + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // extra traces should come after prepareSimulation + sc_trace(specsGlobalConfig.default_trace_file, pcm.m_state, "STATE"); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/pcm_device_tb.h b/src/tb/pcm_device_tb.h new file mode 100644 index 0000000..a9b7195 --- /dev/null +++ b/src/tb/pcm_device_tb.h @@ -0,0 +1,29 @@ +#pragma once + +#include <systemc.h> + +#include <optical_signal.h> +#include <pcm_device.h> +#include <probe.h> +#include <specs.h> + +SC_MODULE(PCMElement_tb) +{ +public: + spx::oa_port_out_type IN; + spx::oa_port_in_type OUT; + + void run_1(); + void monitor(); + SC_CTOR(PCMElement_tb) + { + SC_HAS_PROCESS(PCMElement_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN << OUT; + } +}; + +void PCMElement_tb_run(); diff --git a/src/tb/phase_shifter_tb.cpp b/src/tb/phase_shifter_tb.cpp new file mode 100644 index 0000000..5369e7c --- /dev/null +++ b/src/tb/phase_shifter_tb.cpp @@ -0,0 +1,129 @@ +#include <ctime> +#include <iomanip> +#include <tb/phase_shifter_tb.h> + +#include "general_utils.h" +#include "specs.h" + +void ps_tb::run_1() +{ + V_PS.write(1.0); + wait(SC_ZERO_TIME); + IN1->write(OpticalSignal(sqrt(1.0), 1550e-9)); + IN2->write(OpticalSignal(sqrt(1.0), 1660e-9)); + wait(100,SC_MS); + V_PS.write(2.0); + wait(100, SC_MS); + + while (true) { wait(); } +} + +void ps_tb::monitor() +{ + unsigned int event_counter = 0; + unsigned int success_counter = 0; + const unsigned int test_number = 3; + while(true) + { + wait(); + event_counter++; + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN1: " << IN1->read() << std::endl + << "\tIN2: " << IN2->read() << std::endl + << "\tOUT: " << OUT->read() << std::endl + << "\tCOUNT: " << event_counter << std::endl; + + if(event_counter == 2) + { + if (is_close(abs(OUT->read().m_field), 0.630210, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0.630210W as output field!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 4) + { + if (is_close(arg(OUT->read().m_field), 2, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 2 rad as output phase!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 5) + { + if (is_close(arg(OUT->read().m_field), 2, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 2 as output phase!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Test finished!" << std::endl; + std::cout << "Success rate: " << success_counter << "/" << test_number << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + +} + +void ps_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type IN1, IN2, OUT, YOUT; + spx::ea_signal_type V_PS; + + Merger merg1("merg1"); + merg1.p_in1(IN1); + merg1.p_in2(IN2); + merg1.p_out(YOUT); + + PhaseShifter ps1("ps1", 1); + ps1.p_in(YOUT); + ps1.p_vin(V_PS); + ps1.p_out(OUT); + + // Connect testbench to uut + ps_tb tb("tb"); + tb.IN1(IN1); + tb.IN2(IN2); + tb.OUT(OUT); + tb.V_PS(V_PS); + + // Attach probes + MLambdaProbe probe_out("out", {1550e-9, 1660e-9}); + probe_out.p_in(OUT); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "ps_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/phase_shifter_tb.h b/src/tb/phase_shifter_tb.h new file mode 100644 index 0000000..a59538d --- /dev/null +++ b/src/tb/phase_shifter_tb.h @@ -0,0 +1,32 @@ +#pragma once + +#include <optical_signal.h> +#include <systemc.h> +#include <waveguide.h> +#include <phaseshifter.h> +#include <merger.h> +#include <probe.h> +#include <specs.h> + +SC_MODULE(ps_tb) +{ +public: + spx::oa_port_out_type IN1; + spx::oa_port_out_type IN2; + sc_out<double> V_PS; + spx::oa_port_in_type OUT; + + void run_1(); + void monitor(); + SC_CTOR(ps_tb) + { + SC_HAS_PROCESS(ps_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN1 << IN2 << OUT; + } +}; + +void ps_tb_run(); diff --git a/src/tb/ring_tb.cpp b/src/tb/ring_tb.cpp new file mode 100644 index 0000000..cc3aaf2 --- /dev/null +++ b/src/tb/ring_tb.cpp @@ -0,0 +1,85 @@ +#include <ctime> +#include <iomanip> +#include <tb/ring_tb.h> + +#include "general_utils.h" + +void ring_tb::run_1() +{ + IN.write(OpticalSignal(sqrt(1.0), 1550e-9)); + wait(10, SC_NS); + IN.write(OpticalSignal(0, 1550e-9)); + wait(10, SC_NS); + + IN.write(OpticalSignal(sqrt(1.0), 1550e-9)); + wait(10, SC_NS); + IN.write(OpticalSignal(0, 1550e-9)); + wait(10, SC_NS); + + while(true) + { + wait(); + } +} + +void ring_tb::monitor() +{ + while(true) + { + wait(); + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN: " << IN.read() << std::endl + << "\tOUT: " << OUT.read() << std::endl; + } + +} + +void Ring_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + double neff = 1.0; + double loss_db_cm = 0; + double coupling_through = 0.85; + + spx::oa_signal_type IN, OUT; + spx::oa_signal_type RING1, RING2; + + DirectionalCoupler dc1("dc1", coupling_through, 0); + dc1.p_in1(IN); + dc1.p_out1(OUT); + dc1.p_in2(RING1); + dc1.p_out2(RING2); + + Waveguide wg1("wg1", 1*100.0*1550.0e-6/(2*neff), loss_db_cm, neff, neff); + wg1.p_in(RING2); + wg1.p_out(RING1); + + ring_tb tb("tb"); + tb.IN(IN); + tb.OUT(OUT); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "ring_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + Probe probe_ring("probe_ring",specsGlobalConfig.default_trace_file); + probe_ring.p_in(OUT); + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/ring_tb.h b/src/tb/ring_tb.h new file mode 100644 index 0000000..b995513 --- /dev/null +++ b/src/tb/ring_tb.h @@ -0,0 +1,29 @@ +#pragma once + +#include <optical_signal.h> +#include <systemc.h> +#include <waveguide.h> +#include <directional_coupler.h> +#include <probe.h> +#include <specs.h> + +SC_MODULE(ring_tb) +{ +public: + sc_out<OpticalSignal> IN; + sc_in<OpticalSignal> OUT; + + void run_1(); + void monitor(); + SC_CTOR(ring_tb) + { + SC_HAS_PROCESS(ring_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN << OUT; + } +}; + +void Ring_tb_run(); diff --git a/src/tb/splitter_tb.cpp b/src/tb/splitter_tb.cpp new file mode 100644 index 0000000..eeb3420 --- /dev/null +++ b/src/tb/splitter_tb.cpp @@ -0,0 +1,123 @@ +#include <ctime> +#include <iomanip> +#include <tb/splitter_tb.h> + +#include "general_utils.h" + +void Splitter_tb::run_1() +{ + IN->write(OpticalSignal(sqrt(1.0), 1550e-9)); + wait(100, SC_MS); + IN->write(OpticalSignal(-sqrt(1.0), 1550e-9)); + wait(100, SC_MS); + IN->write(OpticalSignal(sqrt(2.0), 1550e-9)); + wait(100, SC_MS); + IN->write(OpticalSignal(-sqrt(2.0), 1550e-9)); + wait(10, SC_PS); + IN->write(OpticalSignal(sqrt(3.0), 1550e-9)); + wait(100, SC_MS); + + wait(OUT1->value_changed_event()); + wait(1, SC_SEC); + sc_stop(); +} + +void Splitter_tb::monitor() +{ + unsigned int event_counter = 0; + unsigned int success_counter = 0; + const unsigned int test_number = 3; + while(true) + { + wait(); + event_counter++; + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN: " << IN->read() << std::endl + << "\tOUT1: " << OUT1->read() << std::endl + << "\tOUT2: " << OUT2->read() << std::endl + << "\tCOUNT: " << event_counter << std::endl; + + if(event_counter == 10) + { + if (is_close(norm(OUT1->read().m_field), 0.75, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0.75W as output power!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + if (is_close(norm(OUT1->read().m_field), norm(OUT2->read().m_field), 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected same power in both ends!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + if (is_close(arg(OUT1->read().m_field), 0, 1e-4) && is_close(arg(OUT2->read().m_field), 0, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected zero phase in both ends!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Test finished!" << std::endl; + std::cout << "Success rate: " << success_counter << "/" << test_number << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } +} + +void Splitter_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type IN, OUT1, OUT2; + + Splitter uut("uut"); + uut.m_attenuation_dB = 3.0103; + uut.p_in(IN); + uut.p_out1(OUT1); + uut.p_out2(OUT2); + + Splitter_tb tb("tb"); + tb.IN(IN); + tb.OUT1(OUT1); + tb.OUT2(OUT2); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "splitter_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + Probe probe_splitter_1("probe_splitter_1",specsGlobalConfig.default_trace_file); + probe_splitter_1.p_in(OUT1); + + Probe probe_splitter_2("probe_splitter_2",specsGlobalConfig.default_trace_file); + probe_splitter_2.p_in(OUT2); + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/splitter_tb.h b/src/tb/splitter_tb.h new file mode 100644 index 0000000..68d2a8d --- /dev/null +++ b/src/tb/splitter_tb.h @@ -0,0 +1,29 @@ +#pragma once + +#include <optical_signal.h> +#include <splitter.h> +#include <systemc.h> +#include <probe.h> +#include <specs.h> + +SC_MODULE(Splitter_tb) +{ +public: + spx::oa_port_out_type IN; + spx::oa_port_in_type OUT1; + spx::oa_port_in_type OUT2; + + void run_1(); + void monitor(); + SC_CTOR(Splitter_tb) + { + SC_HAS_PROCESS(Splitter_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN << OUT1 << OUT2; + } +}; + +void Splitter_tb_run(); diff --git a/src/tb/wg_tb.cpp b/src/tb/wg_tb.cpp new file mode 100644 index 0000000..7ccebf5 --- /dev/null +++ b/src/tb/wg_tb.cpp @@ -0,0 +1,143 @@ +#include <ctime> +#include <iomanip> +#include <tb/wg_tb.h> + +#include "general_utils.h" +#include "devices/generic_waveguide.h" +#include "specs.h" + +void wg_tb::run_1() +{ + IN->write(OpticalSignal(sqrt(1.0), 1550e-9)); + wait(100, SC_MS); + IN->write(OpticalSignal(0, 1550e-9)); + wait(100, SC_MS); + IN->write(OpticalSignal(sqrt(2.0), 1550e-9)); + wait(100, SC_MS); + IN->write(OpticalSignal(0, 1550e-9)); + wait(10, SC_PS); + IN->write(OpticalSignal(sqrt(3.0), 1550e-9)); + wait(100, SC_MS); + + while (true) { wait(); } +} + +void wg_tb::monitor() +{ + unsigned int event_counter = 0; + unsigned int success_counter = 0; + const unsigned int test_number = 3; + while(true) + { + wait(); + event_counter++; + std::cout << sc_time_stamp() << ":" << std::endl + << "\tIN: " << IN->read() << std::endl + << "\tOUT: " << OUT->read() << std::endl + << "\tCOUNT: " << event_counter << std::endl; + + if(event_counter == 2) + { + if (is_close(norm(OUT->read().m_field), 0.5, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 0.5W as output power!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 6) + { + if (is_close(arg(OUT->read().m_field), -2.4322, 1e-4)) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected -2.4322 rad as output phase!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + if(event_counter == 10) + { + if (is_close(norm(OUT->read().m_field), 1.5, 1e-4) && (is_close(arg(OUT->read().m_field), -2.4322, 1e-4))) + success_counter++; + else + { + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Failure!" << std::endl; + std::cout << "Expected 1.5W as output power and -2.4322 as output phase!" << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + + std::cout << "-----------------/! \\---------------" << std::endl; + std::cout << "Test finished!" << std::endl; + std::cout << "Success rate: " << success_counter << "/" << test_number << std::endl; + std::cout << "-----------------/! \\---------------" << std::endl; + } + } + +} + +void wg_tb_run() +{ + // Apply SPECS resolution before creating any device + specsGlobalConfig.applyEngineResolution(); + + spx::oa_signal_type IN("sig_in"), OUT("sig_out"); + spx::oa_signal_type TERM_r, TERM_w; + + // Unidirectional variant + // Waveguide uut("uut", 1, 0, 1, 1); + // uut.m_attenuation_dB_cm = 3.0103; + // uut.p_in(IN); + // uut.p_out(OUT); + + // Bidirectional variant + WaveguideBi uut("uut", 1, 0, 1, 1); + uut.m_attenuation_dB_cm = 3.0103; + uut.p0_in(IN); + uut.p0_out(TERM_w); + uut.p1_in(TERM_r); + uut.p1_out(OUT); + + // GenericWaveguide uut("uut",3.0103*100,1,1,0.01); + // uut.ports_in[0]->bind(IN); + // uut.ports_in[1]->bind(TERM_r); + // uut.ports_out[0]->bind(TERM_w); + // uut.ports_out[1]->bind(OUT); + + // Connect testbench to uut + wg_tb tb("tb"); + tb.IN(IN); + tb.OUT(OUT); + + // Attach probes + Probe probe_in("in"); + probe_in.p_in(IN); + + Probe probe_out("out"); + probe_out.p_in(OUT); + + // Open Trace file + std::string trace_filename = "traces/"; + trace_filename += "waveguide_tb"; + specsGlobalConfig.trace_filename = trace_filename; + + // Apply SPECS options specific to the testbench + specsGlobalConfig.simulation_mode = OpticalOutputPortMode::EVENT_DRIVEN; + specsGlobalConfig.trace_all_optical_nets = 0; + + // Run SPECS pre-simulation code + specsGlobalConfig.prepareSimulation(); + + // Start simulation + sc_start(); + + std::cout << std::endl << std::endl; + std::cout << ".vcd trace file: " << specsGlobalConfig.trace_filename << std::endl; + + sc_close_vcd_trace_file(specsGlobalConfig.default_trace_file); +} diff --git a/src/tb/wg_tb.h b/src/tb/wg_tb.h new file mode 100644 index 0000000..5d0a84f --- /dev/null +++ b/src/tb/wg_tb.h @@ -0,0 +1,28 @@ +#pragma once + +#include <optical_signal.h> +#include <systemc.h> +#include <waveguide.h> +#include <probe.h> +#include <specs.h> + +SC_MODULE(wg_tb) +{ +public: + spx::oa_port_out_type IN; + spx::oa_port_in_type OUT; + + void run_1(); + void monitor(); + SC_CTOR(wg_tb) + { + SC_HAS_PROCESS(wg_tb); + + SC_THREAD(run_1); + + SC_THREAD(monitor); + sensitive << IN << OUT; + } +}; + +void wg_tb_run(); diff --git a/src/utils/general_utils.cpp b/src/utils/general_utils.cpp new file mode 100644 index 0000000..0ba55be --- /dev/null +++ b/src/utils/general_utils.cpp @@ -0,0 +1 @@ +#include "general_utils.h" diff --git a/src/utils/general_utils.h b/src/utils/general_utils.h new file mode 100644 index 0000000..14757a9 --- /dev/null +++ b/src/utils/general_utils.h @@ -0,0 +1,35 @@ +#pragma once + +#include <cmath> +#include <vector> +#include <iostream> + +using std::abs; +using std::vector; +using std::cerr; +using std::endl; + +template <typename T1, typename T2> +inline bool is_close(const T1& x1, const T2& x2, const double &precision = 1e-12) +{ + return abs(x1-x2) < precision; +} + +//double linspace(double minval, double maxval, double npoints); + +template <typename T> +vector<T> range(const T &minval, const T &maxval, const T &step) +{ + vector<T> ret; + if (maxval <= minval || step <= 0) + { + cerr << "Invalid range parameters" << endl; + // because i don't have time to cover all cases + exit(1); + } + int n = floor((maxval - minval) / step); + ret.reserve(n); + for (T val = minval; val < maxval; val += step) + ret.push_back(val); + return ret; +}
\ No newline at end of file diff --git a/src/utils/pqueue.h b/src/utils/pqueue.h new file mode 100644 index 0000000..4ec5dfd --- /dev/null +++ b/src/utils/pqueue.h @@ -0,0 +1,35 @@ +#pragma once + +#include <queue> +#include <vector> + +using std::priority_queue; +using std::vector; +using std::greater; +using std::size_t; + +template <class T> +class PQueue: public priority_queue<T, vector<T>, greater<T>>{ +public: + public: + typedef typename + std::priority_queue<T>::container_type::iterator iterator; + typedef typename + std::priority_queue<T>::container_type::const_iterator const_iterator; + + iterator begin() { + return this->c.begin(); + } + iterator end() { + return this->c.end(); + } + const_iterator cbegin() const { + return this->c.cbegin(); + } + const_iterator cend() const { + return this->c.cend(); + } + size_t size() const { + return this->c.size(); + } +}; diff --git a/src/utils/strutils.h b/src/utils/strutils.h new file mode 100644 index 0000000..94e7ab7 --- /dev/null +++ b/src/utils/strutils.h @@ -0,0 +1,79 @@ +#pragma once + +#include <algorithm> // std::transform +#include <string> + +namespace strutils { +// trim from start (in place) +inline void ltrim(std::string &s) +{ + s.erase(s.begin(), + std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); +} + +// trim from end (in place) +inline void rtrim(std::string &s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }) + .base(), + s.end()); +} + +// trim from both ends (in place) +inline void trim(std::string &s) +{ + ltrim(s); + rtrim(s); +} + +// trim from start (copying) +inline std::string ltrim_copy(std::string s) +{ + ltrim(s); + return s; +} + +// trim from end (copying) +inline std::string rtrim_copy(std::string s) +{ + rtrim(s); + return s; +} + +// trim from both ends (copying) +inline std::string trim_copy(std::string s) +{ + trim(s); + return s; +} +// trim from end (in place) +inline void rtrim_comments(std::string &s, const std::string &comment_tokens) +{ + size_t pos = s.find_first_of(comment_tokens); + if (pos != s.npos) + s.erase(pos); +} +inline void toupper(std::string &s) +{ + std::transform(s.begin(), s.end(), s.begin(), ::toupper); +} + +inline bool strcontains(const std::string &s, const std::string &sub) +{ + return s.find(sub) != std::string::npos; +} + +inline bool streq(const std::string &s1, const std::string &s2) +{ + return s1 == s2; +} + +inline bool iequals(const std::string& a, const std::string& b) +{ + return std::equal(a.begin(), a.end(), + b.begin(), b.end(), + [](char a, char b) { + return tolower(a) == tolower(b); + }); +} +}; diff --git a/src/utils/sysc_utils.cpp b/src/utils/sysc_utils.cpp new file mode 100644 index 0000000..9b63632 --- /dev/null +++ b/src/utils/sysc_utils.cpp @@ -0,0 +1,70 @@ +#include <sysc_utils.h> + +#include <string> + + +inline bool sc_object_is_module(const sc_object &obj) { + return strcmp(obj.kind(), "sc_module") == 0; +} + +// Return vector containing all children objects (expanded) of obj +set<sc_object *> sc_collect_children_object(sc_object* obj) +{ + auto v = obj->get_child_objects(); + auto children = set<sc_object *>(v.begin(), v.end()); + auto all_children = children; + for (auto child : children) + if ( child ) + { + auto children_children = sc_collect_children_object(child); + for (auto child_child : children_children) + all_children.insert(child_child); + } + return all_children; +} + +// Return vector containing all children objects (expanded) of obj +set<sc_module *> sc_collect_children_module(sc_module* obj) +{ + auto v = obj->get_child_objects(); + auto children = set<sc_object *>(v.begin(), v.end()); + set<sc_module *> all_children; + for (auto obj: children) + { + auto mod = dynamic_cast<sc_module *>(obj); + if(mod) { + all_children.insert(mod); + auto children2 = sc_collect_children_module(mod); + all_children.insert(children2.begin(), children2.end()); + } + } + return all_children; +} + +set<sc_module *> sc_get_all_module() { + set<sc_module *> all_modules; + + for ( auto obj : sc_get_top_level_objects() ) { + auto mod = dynamic_cast<sc_module *>(obj); + if (mod) + { + all_modules.insert(mod); + auto all_children = sc_collect_children_module(mod); + for (auto child : all_children) + all_modules.insert(child); + } + } + return all_modules; +} + +set<sc_object *> sc_get_all_object() { + set<sc_object *> all_objects; + + for ( auto obj : sc_get_top_level_objects() ) { + all_objects.insert(obj); + auto all_children = sc_collect_children_object(obj); + for (auto child : all_children) + all_objects.insert(child); + } + return all_objects; +}
\ No newline at end of file diff --git a/src/utils/sysc_utils.h b/src/utils/sysc_utils.h new file mode 100644 index 0000000..be275a0 --- /dev/null +++ b/src/utils/sysc_utils.h @@ -0,0 +1,67 @@ +#pragma once + +#include <systemc.h> + +#include <vector> +#include <set> + +using std::vector; +using std::set; + +bool sc_object_is_module(const sc_object &obj); + +// Return vector containing all children objects (expanded) of obj +set<sc_object *> sc_collect_children_object(sc_object* obj); + +// Return vector containing all children objects (expanded) of obj +set<sc_module *> sc_collect_children_module(sc_module* obj); + +// Return vector containing all sc_module registered with engine +set<sc_module *> sc_get_all_module(); + +// Return vector containing all sc_object registered with engine +set<sc_object *> sc_get_all_object(); + +// Return vector containing all sc_module of a certain type +template<typename T> +set<T *> sc_get_all_module_by_type(); + +// Return vector containing all sc_object of a certain type +template<typename T> +set<T *> sc_get_all_object_by_type(); + +//////////////////////////////////////// +// Definition of template functions +//////////////////////////////////////// + + +template<typename T> +set<T *> sc_get_all_module_by_type() { + set<sc_module *> all_modules = sc_get_all_module(); + set<T *> all_requested; + for (auto mod : all_modules) + { + // cout << mod->name() << endl; + auto requested = dynamic_cast<T *>(mod); + if (requested) + all_requested.insert(requested); + } + return all_requested; +} + +template<typename T> +set<T *> sc_get_all_object_by_type() { + set<sc_object *> all_objects = sc_get_all_object(); + set<T *> all_requested; + + for (auto obj : all_objects) + { + //cout << obj->name() << endl; + auto requested = dynamic_cast<T *>(obj); + if (requested) { + //cout << "\\__( WE WANT IT )" << endl; + all_requested.insert(requested); + } + } + return all_requested; +}
\ No newline at end of file |
