aboutsummaryrefslogtreecommitdiff
path: root/src/devices
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices')
-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
53 files changed, 6423 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;
+ }
+};