aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorClément Zrounba <6691770+clement-z@users.noreply.github.com>2023-09-30 23:06:01 +0200
committerClément Zrounba <6691770+clement-z@users.noreply.github.com>2023-09-30 23:26:46 +0200
commitff9b8bb838ecdfbfc1dc81038fcf3b2a87636982 (patch)
tree21f27be782ce11c6d00b96ce100a2bff88141b2e /src
downloadspecs-ff9b8bb838ecdfbfc1dc81038fcf3b2a87636982.tar.gz
specs-ff9b8bb838ecdfbfc1dc81038fcf3b2a87636982.zip
Initial release
Diffstat (limited to 'src')
-rw-r--r--src/devices/alldevices.h44
-rw-r--r--src/devices/bitstream_source.cpp76
-rw-r--r--src/devices/bitstream_source.h105
-rw-r--r--src/devices/clements.cpp137
-rw-r--r--src/devices/clements.h66
-rw-r--r--src/devices/crossing.cpp129
-rw-r--r--src/devices/crossing.h113
-rw-r--r--src/devices/crow.cpp97
-rw-r--r--src/devices/crow.h98
-rw-r--r--src/devices/cw_source.cpp45
-rw-r--r--src/devices/cw_source.h76
-rw-r--r--src/devices/detector.cpp136
-rw-r--r--src/devices/detector.h89
-rw-r--r--src/devices/directional_coupler.cpp318
-rw-r--r--src/devices/directional_coupler.h133
-rw-r--r--src/devices/electrical_value_list_source.cpp67
-rw-r--r--src/devices/electrical_value_list_source.h128
-rw-r--r--src/devices/generic_2x2_coupler.h86
-rw-r--r--src/devices/generic_transmission_device.cpp224
-rw-r--r--src/devices/generic_transmission_device.h94
-rw-r--r--src/devices/generic_waveguide.h119
-rw-r--r--src/devices/merger.cpp69
-rw-r--r--src/devices/merger.h51
-rw-r--r--src/devices/mesh_col.cpp243
-rw-r--r--src/devices/mesh_col.h76
-rw-r--r--src/devices/mzi.cpp55
-rw-r--r--src/devices/mzi.h67
-rw-r--r--src/devices/mzi_active.cpp206
-rw-r--r--src/devices/mzi_active.h179
-rw-r--r--src/devices/octane_cell.cpp91
-rw-r--r--src/devices/octane_cell.h61
-rw-r--r--src/devices/octane_matrix.cpp365
-rw-r--r--src/devices/octane_matrix.h99
-rw-r--r--src/devices/octane_segment.cpp396
-rw-r--r--src/devices/octane_segment.h98
-rw-r--r--src/devices/pcm_device.cpp150
-rw-r--r--src/devices/pcm_device.h60
-rw-r--r--src/devices/phaseshifter.cpp198
-rw-r--r--src/devices/phaseshifter.h166
-rw-r--r--src/devices/probe.cpp164
-rw-r--r--src/devices/probe.h131
-rw-r--r--src/devices/ring.cpp8
-rw-r--r--src/devices/ring.h99
-rw-r--r--src/devices/splitter.cpp41
-rw-r--r--src/devices/splitter.h88
-rw-r--r--src/devices/spx_module.h36
-rw-r--r--src/devices/subcircuit_instance.h25
-rw-r--r--src/devices/time_monitor.cpp152
-rw-r--r--src/devices/time_monitor.h26
-rw-r--r--src/devices/value_list_source.cpp69
-rw-r--r--src/devices/value_list_source.h140
-rw-r--r--src/devices/waveguide.cpp280
-rw-r--r--src/devices/waveguide.h154
-rw-r--r--src/main.cpp325
-rw-r--r--src/optical_output_port.cpp424
-rw-r--r--src/optical_output_port.h178
-rw-r--r--src/optical_signal.cpp157
-rw-r--r--src/optical_signal.h251
-rw-r--r--src/parser/parse_analysis.cpp109
-rw-r--r--src/parser/parse_analysis.h94
-rw-r--r--src/parser/parse_directive.cpp132
-rw-r--r--src/parser/parse_directive.h68
-rw-r--r--src/parser/parse_element.cpp1266
-rw-r--r--src/parser/parse_element.h104
-rw-r--r--src/parser/parse_tree.cpp972
-rw-r--r--src/parser/parse_tree.h890
-rw-r--r--src/parser/parser.l438
-rw-r--r--src/parser/parser.y961
-rw-r--r--src/specs.cpp383
-rw-r--r--src/specs.h159
-rw-r--r--src/tb/alltestbenches.cpp28
-rw-r--r--src/tb/alltestbenches.h22
-rw-r--r--src/tb/crow_tb.cpp216
-rw-r--r--src/tb/crow_tb.h30
-rw-r--r--src/tb/detector_tb.cpp127
-rw-r--r--src/tb/detector_tb.h30
-rw-r--r--src/tb/directional_coupler_tb.cpp150
-rw-r--r--src/tb/directional_coupler_tb.h31
-rw-r--r--src/tb/freqsweep_tb.cpp213
-rw-r--r--src/tb/freqsweep_tb.h33
-rw-r--r--src/tb/lambda_tb.cpp102
-rw-r--r--src/tb/lambda_tb.h31
-rw-r--r--src/tb/merger_tb.cpp126
-rw-r--r--src/tb/merger_tb.h30
-rw-r--r--src/tb/mesh_tb.cpp130
-rw-r--r--src/tb/mesh_tb.h29
-rw-r--r--src/tb/mzi_tb.cpp86
-rw-r--r--src/tb/mzi_tb.h31
-rw-r--r--src/tb/pcm_device_tb.cpp129
-rw-r--r--src/tb/pcm_device_tb.h29
-rw-r--r--src/tb/phase_shifter_tb.cpp129
-rw-r--r--src/tb/phase_shifter_tb.h32
-rw-r--r--src/tb/ring_tb.cpp85
-rw-r--r--src/tb/ring_tb.h29
-rw-r--r--src/tb/splitter_tb.cpp123
-rw-r--r--src/tb/splitter_tb.h29
-rw-r--r--src/tb/wg_tb.cpp143
-rw-r--r--src/tb/wg_tb.h28
-rw-r--r--src/utils/general_utils.cpp1
-rw-r--r--src/utils/general_utils.h35
-rw-r--r--src/utils/pqueue.h35
-rw-r--r--src/utils/strutils.h79
-rw-r--r--src/utils/sysc_utils.cpp70
-rw-r--r--src/utils/sysc_utils.h67
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 &amplitude, 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