diff options
Diffstat (limited to 'src/devices')
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 &litude, const double &phase) + { + m_signal_on.m_field = polar(amplitude, phase); + } + inline void setPhase (const double &phase) + { + if (m_signal_on.modulus() == 0) + cerr << "Warning: setting phase has no effect on 0" << endl; + m_signal_on.m_field = polar(m_signal_on.modulus(), phase); + } + inline void setPower (const double &power) + { + m_signal_on.m_field = polar(sqrt(power), m_signal_on.phase()); + } + inline void setPower(const double &power, double phase) + { + setAmplitudePhase(sqrt(power), phase); + } + + // Constructor + CWSource(sc_module_name name) + : spx_module(name) + , m_out_writer("out_delayed_writer", p_out) + { + SC_HAS_PROCESS(CWSource); + + SC_THREAD(runner); + + enable = sc_logic(0); + reset = sc_logic(0); + } + + CWSource(sc_module_name name, const OpticalSignal& signal_on) + : CWSource(name) + { + m_signal_on = signal_on; + } +}; diff --git a/src/devices/detector.cpp b/src/devices/detector.cpp new file mode 100644 index 0000000..b93a5de --- /dev/null +++ b/src/devices/detector.cpp @@ -0,0 +1,136 @@ +#include "specs.h" +#include <detector.h> +#include <cstdlib> // system() +#include <random> +#include <complex> + +using namespace std; + +void Detector::on_port_in_changed() +{ + // always initialize memory + m_memory_in[0] = 0; + + // rng seed + init(); + + while (true) { + // Wait for a new input signal + wait(); + + const auto &p_in_read = p_in->read(); + + auto cur_wavelength_id = p_in_read.m_wavelength_id; + // Updating the field memory + m_memory_in[cur_wavelength_id] = p_in_read.m_field; + m_event_manual_trigger.notify(); + } +} + +void Detector::on_time_tick() +{ + sc_time sampling_time = sc_time(m_sampling_time, SC_SEC); + if (sampling_time.value() == 0) + sampling_time = sc_time::from_value(1); + + OpticalSignal::field_type total_field; + OpticalSignal::field_type::value_type total_power; + double photocurrent; + + // Wait for enable signal + if (! enable.read().to_bool()) + { + // cout << name() << " waiting for enable" << endl; + wait(enable.posedge_event()); + cout << name() << " was enabled" << endl; + } + + while(true) + { + if (sc_pending_activity()) { + wait(sampling_time, m_event_manual_trigger); + //wait(m_event_manual_trigger); // if only time-averaged signal is of interest + } else { + /* wait for reset */ + //TODO (see CWSource code) + wait(); // effectively wait until the end of the simulation + } + + /* Get current time tk*/ + double tk = sc_time_stamp().to_seconds(); + + /* Calculate current output value based on present fields */ + /* Calculate sum of fields at current time*/ + total_field = 0; + total_power = 0; + for (auto field : m_memory_in) + { + double wl = specsGlobalConfig.wavelengths_vector[field.first]; + double freq = 299792458 / wl; + total_field += field.second * exp(complex<double>(0, 2 * M_PI * freq * tk)); + total_power += norm(field.second); + } + + if (norm(total_field) == 0) + total_field = 1e-20*m_rngDist(m_rngGen); + + photocurrent = norm(total_field) * m_responsivity_A_W; + m_cur_readout = photocurrent + (!m_noiseBypass)*noise_gen(photocurrent) + m_darkCurrent_A; + m_cur_readout_no_interf = total_power * m_responsivity_A_W + (!m_noiseBypass)*noise_gen(photocurrent) + m_darkCurrent_A; + + + //m_cur_readout = total_field.real(); + // Write to output port + //p_readout->write(m_cur_readout); + } +} + +/* +Generates a current noise to be applied to the noiseless_readout, +calculated from the responsivity. + +Considers: TIA input referred, shot, and thermal +*/ +double Detector::noise_gen(const double &noiseless_readout) +{ + // elementary charge + const double q = 1.60217e-19; + + // Boltzmann constant + const double K = 1.38064e-23; + + double inoise_tia_2 = m_opFreq_Hz*pow(m_iTIA,2); + double inoise_shot_2 = m_opFreq_Hz*2*q*(noiseless_readout+m_darkCurrent_A); + double inoise_therm_2 = m_opFreq_Hz*4*K*m_temp_K/m_equivR_Ohm; + + // the RMS value is also the variance of the random variable + double inoise_rms = inoise_tia_2 + inoise_shot_2 + inoise_therm_2; + + // mean = noiseless, std = sqrt(variance) + // the second element of the product is the gaussian(0,1) + return sqrt(inoise_rms) * m_rngDist(m_rngGen); +} + +/* +To be implemented in the future, user can specify the wavelength response +of the photodiode +*/ +double Detector::wavelength_dependent_responsivity(const double &wavelength) +{ + (void)wavelength; + if(true) + { + return m_responsivity_A_W; + } + else + { + // custom responsivity curve + return 0; // should return the new responsivity + } +} + +void Detector::init() +{ + std::random_device rd; // Will be used to obtain a seed for the random number engine + m_rngGen.seed(rd()); +} diff --git a/src/devices/detector.h b/src/devices/detector.h new file mode 100644 index 0000000..d2e5e65 --- /dev/null +++ b/src/devices/detector.h @@ -0,0 +1,89 @@ +#pragma once + +#include <systemc.h> +#include <fstream> +#include <random> + +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +// TODO: rename to photodetector +class Detector : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in; + spx::ea_port_out_type p_readout; // TODO: divide into cathode and anode + double m_cur_readout; + double m_cur_readout_no_interf; + + // Input memory for multi-wavelength purposes + std::map<uint32_t,OpticalSignal::field_type> m_memory_in; + + // Detector enable signal + spx::ed_signal_type enable; + + // Member variables + double m_responsivity_A_W; + double m_darkCurrent_A; + double m_opFreq_Hz; + double m_temp_K; + double m_equivR_Ohm; + double m_iTIA; // A/sqrt(Hz) + bool m_noiseBypass; + double m_sampling_time; + + // if each photodiode has an independent RNG device + std::default_random_engine m_rngGen; + std::normal_distribution<double> m_rngDist; + + sc_event m_event_manual_trigger; + + // Init all parameters + void init(); + + // Function that generates the noise applied to the output of this module + double noise_gen(const double &noiseless_readout); + double wavelength_dependent_responsivity(const double &wavelength); + + // Processes + void on_port_in_changed(); + void on_time_tick(); + + virtual void trace(sc_trace_file *Tf) const + { + sc_trace(Tf, m_cur_readout, (string(name()) + ".readout").c_str()); + sc_trace(Tf, m_cur_readout_no_interf, (string(name()) + ".readout_no_interference").c_str()); + } + + // Constructor + Detector(sc_module_name name, + double responsivity_A_W = 1, + double darkCurrent_A = 0, // 100e-12 is an OK value + bool noiseBypass = true, + double opFreq_Hz = 1e9, + double temp_K = 300, + double equivR_Ohm = 400, + double iTIA = 10e-12, + double sampling_time = 1e-12) + : spx_module(name) + , m_responsivity_A_W(responsivity_A_W) + , m_darkCurrent_A(darkCurrent_A) + , m_opFreq_Hz(opFreq_Hz) + , m_temp_K(temp_K) + , m_equivR_Ohm(equivR_Ohm) + , m_iTIA(iTIA) + , m_noiseBypass(noiseBypass) + , m_sampling_time(sampling_time) + , m_rngDist(0,1) + { + SC_HAS_PROCESS(Detector); + enable = sc_logic(0); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + + SC_THREAD(on_time_tick); + } +}; diff --git a/src/devices/directional_coupler.cpp b/src/devices/directional_coupler.cpp new file mode 100644 index 0000000..da0872b --- /dev/null +++ b/src/devices/directional_coupler.cpp @@ -0,0 +1,318 @@ +#include "specs.h" +#include <directional_coupler.h> + +using namespace std; + +void DirectionalCouplerUni::on_port_in1_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in1[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S13, S14, S23, S24; + S13 = polar(transmission_through, m_through_phase_rad); + S24 = polar(transmission_through, m_through_phase_rad); + S14 = polar(transmission_cross, m_cross_phase_rad); + S23 = polar(transmission_cross, m_cross_phase_rad); + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "through_power = " << norm(S13) << " W/W" << endl; + cout << "cross_power = " << norm(S14)<< " W/W" << endl; + cout << "through_field = " << abs(S13) << "" << endl; + cout << "cross_field = " << abs(S14)<< "" << endl; + cout << "insertion loss = " << m_dc_loss << "dB" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in1.get_interface()))->name(); + cout << " --,__,-> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out1.get_interface()))->name(); + cout << endl; + + cout << (dynamic_cast<spx::oa_signal_type *>(p_in2.get_interface()))->name(); + cout << " --' '-> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out2.get_interface()))->name(); + cout << endl; + + cout << endl; + } + + OpticalSignal p_in1_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p_in1_read = p_in1->read(); + + auto cur_wavelength_id = p_in1_read.m_wavelength_id; + // Updating the field memory + m_memory_in1[cur_wavelength_id] = p_in1_read.m_field; + + auto s3 = OpticalSignal(m_memory_in1[cur_wavelength_id] * S13 + + m_memory_in2[cur_wavelength_id] * S23 + , cur_wavelength_id); + + auto s4 = OpticalSignal(m_memory_in1[cur_wavelength_id] * S14 + + m_memory_in2[cur_wavelength_id] * S24 + , cur_wavelength_id); + + m_out1_writer.delayedWrite(s3, sc_time(m_delay_ns, SC_NS)); + m_out2_writer.delayedWrite(s4, sc_time(m_delay_ns, SC_NS)); + } +} + +void DirectionalCouplerUni::on_port_in2_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in2[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S13, S14, S23, S24; + S13 = polar(transmission_through, m_through_phase_rad); + S24 = polar(transmission_through, m_through_phase_rad); + S14 = polar(transmission_cross, m_cross_phase_rad); + S23 = polar(transmission_cross, m_cross_phase_rad); + + OpticalSignal p_in2_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p_in2_read = p_in2->read(); + + auto cur_wavelength_id = p_in2_read.m_wavelength_id; + // Updating the field memory + m_memory_in2[cur_wavelength_id] = p_in2_read.m_field; + + auto s3 = OpticalSignal(m_memory_in1[cur_wavelength_id] * S13 + + m_memory_in2[cur_wavelength_id] * S23 + , cur_wavelength_id); + + auto s4 = OpticalSignal(m_memory_in1[cur_wavelength_id] * S14 + + m_memory_in2[cur_wavelength_id] * S24 + , cur_wavelength_id); + + m_out1_writer.delayedWrite(s3, sc_time(m_delay_ns, SC_NS)); + m_out2_writer.delayedWrite(s4, sc_time(m_delay_ns, SC_NS)); + } +} + + +void DirectionalCouplerBi::on_p0_in_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in0[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S02, S13, S03, S12; + S02 = polar(transmission_through, m_through_phase_rad); + S13 = polar(transmission_through, m_through_phase_rad); + S03 = polar(transmission_cross, m_cross_phase_rad); + S12 = polar(transmission_cross, m_cross_phase_rad); + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "through_power = " << norm(S02) << " W/W" << endl; + cout << "cross_power = " << norm(S03)<< " W/W" << endl; + cout << "through_field = " << abs(S02) << "" << endl; + cout << "cross_field = " << abs(S03)<< "" << endl; + cout << "insertion loss = " << m_dc_loss << "dB" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_in.get_interface()))->name(); + + cout << " --,__,-> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p2_out.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_in.get_interface()))->name(); + cout << " --' '-> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p3_out.get_interface()))->name(); + cout << endl; + + cout << (dynamic_cast<spx::oa_signal_type *>(p0_out.get_interface()))->name(); + cout << " <-,__,-- "; + cout << (dynamic_cast<spx::oa_signal_type *>(p2_in.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_out.get_interface()))->name(); + cout << " <-' '-- "; + cout << (dynamic_cast<spx::oa_signal_type *>(p3_in.get_interface()))->name(); + cout << endl; + + cout << endl; + } + + OpticalSignal p0_in_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p0_in_read = p0_in->read(); + + auto cur_wavelength_id = p0_in_read.m_wavelength_id; + + // Updating the field memory + m_memory_in0[cur_wavelength_id] = p0_in_read.m_field; + + auto s2 = OpticalSignal(m_memory_in0[cur_wavelength_id] * S02 + + m_memory_in1[cur_wavelength_id] * S12 + , cur_wavelength_id); + + auto s3 = OpticalSignal(m_memory_in0[cur_wavelength_id] * S03 + + m_memory_in1[cur_wavelength_id] * S13 + , cur_wavelength_id); + + m_p2_out_writer.delayedWrite(s2, sc_time(m_delay_ns, SC_NS)); + m_p3_out_writer.delayedWrite(s3, sc_time(m_delay_ns, SC_NS)); + } +} + +void DirectionalCouplerBi::on_p1_in_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in1[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S02, S13, S03, S12; + S02 = polar(transmission_through, m_through_phase_rad); + S13 = polar(transmission_through, m_through_phase_rad); + S03 = polar(transmission_cross, m_cross_phase_rad); + S12 = polar(transmission_cross, m_cross_phase_rad); + + OpticalSignal p1_in_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p1_in_read = p1_in->read(); + + auto cur_wavelength_id = p1_in_read.m_wavelength_id; + + // Updating the field memory + m_memory_in1[cur_wavelength_id] = p1_in_read.m_field; + + auto s2 = OpticalSignal(m_memory_in0[cur_wavelength_id] * S02 + + m_memory_in1[cur_wavelength_id] * S12 + , cur_wavelength_id); + + auto s3 = OpticalSignal(m_memory_in0[cur_wavelength_id] * S03 + + m_memory_in1[cur_wavelength_id] * S13 + , cur_wavelength_id); + + m_p2_out_writer.delayedWrite(s2, sc_time(m_delay_ns, SC_NS)); + m_p3_out_writer.delayedWrite(s3, sc_time(m_delay_ns, SC_NS)); + } +} + +void DirectionalCouplerBi::on_p2_in_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in2[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S02, S13, S03, S12; + S02 = polar(transmission_through, m_through_phase_rad); + S13 = polar(transmission_through, m_through_phase_rad); + S03 = polar(transmission_cross, m_cross_phase_rad); + S12 = polar(transmission_cross, m_cross_phase_rad); + + OpticalSignal p2_in_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p2_in_read = p2_in->read(); + + auto cur_wavelength_id = p2_in_read.m_wavelength_id; + + // Updating the field memory + m_memory_in2[cur_wavelength_id] = p2_in_read.m_field; + + auto s0 = OpticalSignal(m_memory_in2[cur_wavelength_id] * S02 + + m_memory_in3[cur_wavelength_id] * S03 + , cur_wavelength_id); + + auto s1 = OpticalSignal(m_memory_in2[cur_wavelength_id] * S12 + + m_memory_in3[cur_wavelength_id] * S13 + , cur_wavelength_id); + + m_p0_out_writer.delayedWrite(s0, sc_time(m_delay_ns, SC_NS)); + m_p1_out_writer.delayedWrite(s1, sc_time(m_delay_ns, SC_NS)); + } +} + +void DirectionalCouplerBi::on_p3_in_changed() +{ + m_through_power_dB = 10*log10(m_dc_through_coupling_power) - m_dc_loss; + m_cross_power_dB = 10*log10(1.0 - m_dc_through_coupling_power) - m_dc_loss; + + m_memory_in3[0] = 0; // initializing for nan wavelength + + const double transmission_through = pow(10.0, m_through_power_dB / 20.0); + const double transmission_cross = pow(10.0, m_cross_power_dB / 20.0); + + // Pre-calculate S-parameters + OpticalSignal::field_type S02, S13, S03, S12; + S02 = polar(transmission_through, m_through_phase_rad); + S13 = polar(transmission_through, m_through_phase_rad); + S03 = polar(transmission_cross, m_cross_phase_rad); + S12 = polar(transmission_cross, m_cross_phase_rad); + + OpticalSignal p3_in_read; + + while (true) { + // Wait for a new input signal + wait(); + + // Read current inputs + p3_in_read = p3_in->read(); + + auto cur_wavelength_id = p3_in_read.m_wavelength_id; + + // Updating the field memory + m_memory_in3[cur_wavelength_id] = p3_in_read.m_field; + + auto s0 = OpticalSignal(m_memory_in2[cur_wavelength_id] * S02 + + m_memory_in3[cur_wavelength_id] * S03 + , cur_wavelength_id); + + auto s1 = OpticalSignal(m_memory_in2[cur_wavelength_id] * S12 + + m_memory_in3[cur_wavelength_id] * S13 + , cur_wavelength_id); + + m_p0_out_writer.delayedWrite(s0, sc_time(m_delay_ns, SC_NS)); + m_p1_out_writer.delayedWrite(s1, sc_time(m_delay_ns, SC_NS)); + } +}
\ No newline at end of file diff --git a/src/devices/directional_coupler.h b/src/devices/directional_coupler.h new file mode 100644 index 0000000..1e907a6 --- /dev/null +++ b/src/devices/directional_coupler.h @@ -0,0 +1,133 @@ +#pragma once + +#include <systemc.h> +#include <map> +#include <optical_output_port.h> +#include <optical_signal.h> +#include "spx_module.h" + + +class DirectionalCouplerBase : public spx_module { +public: + // Member variables + double m_delay_ns = 0; + double m_through_phase_rad = 0; + double m_cross_phase_rad = M_PI/2; + double m_dc_through_coupling_power; + + double m_through_power_dB; + double m_cross_power_dB; + double m_dc_loss; // is in dB + + // Constructor + DirectionalCouplerBase(sc_module_name name, + double dc_through_coupling_power = 0.5, + double dc_loss = 0) + : spx_module(name) + , m_dc_through_coupling_power(dc_through_coupling_power) + , m_dc_loss(dc_loss) + { + // nothing to do + } +}; + +class DirectionalCouplerUni : public DirectionalCouplerBase { +public: + // Ports + spx::oa_port_in_type p_in1; + spx::oa_port_in_type p_in2; + spx::oa_port_out_type p_out1; + spx::oa_port_out_type p_out2; + + // Timed ports writers + OpticalOutputPort m_out1_writer; + OpticalOutputPort m_out2_writer; + + // Memory for multi-wavelength purposes + // maybe with vector it has better performance + std::map<uint32_t,OpticalSignal::field_type> m_memory_in1; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in2; + + // Processes + void on_port_in1_changed(); + void on_port_in2_changed(); + + // Constructor + DirectionalCouplerUni(sc_module_name name, + double dc_through_coupling_power = 0.5, + double dc_loss = 0) + : DirectionalCouplerBase(name, dc_through_coupling_power, dc_loss) + , m_out1_writer("out1_delayed_writer", p_out1) + , m_out2_writer("out2_delayed_writer", p_out2) + { + SC_HAS_PROCESS(DirectionalCouplerUni); + + SC_THREAD(on_port_in1_changed); + sensitive << p_in1; + + SC_THREAD(on_port_in2_changed); + sensitive << p_in2; + } +}; + +typedef DirectionalCouplerUni DirectionalCoupler; + +class DirectionalCouplerBi : public DirectionalCouplerBase { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p0_in; + spx::oa_port_in_type p1_in; + spx::oa_port_in_type p2_in; + spx::oa_port_in_type p3_in; + + /** The optical output ports. */ + spx::oa_port_out_type p0_out; + spx::oa_port_out_type p1_out; + spx::oa_port_out_type p2_out; + spx::oa_port_out_type p3_out; + + // Timed ports writers + OpticalOutputPort m_p0_out_writer; + OpticalOutputPort m_p1_out_writer; + OpticalOutputPort m_p2_out_writer; + OpticalOutputPort m_p3_out_writer; + + // Memory for multi-wavelength purposes + // maybe with vector it has better performance + std::map<uint32_t,OpticalSignal::field_type> m_memory_in0; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in1; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in2; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in3; + + // Processes + void on_p0_in_changed(); + void on_p1_in_changed(); + void on_p2_in_changed(); + void on_p3_in_changed(); + + // Constructor + DirectionalCouplerBi(sc_module_name name, + double dc_through_coupling_power = 0.5, + double dc_loss = 0) + : DirectionalCouplerBase(name, dc_through_coupling_power, dc_loss) + , m_p0_out_writer("p0_out_delayed_writer", p0_out) + , m_p1_out_writer("p1_out_delayed_writer", p1_out) + , m_p2_out_writer("p2_out_delayed_writer", p2_out) + , m_p3_out_writer("p3_out_delayed_writer", p3_out) + { + SC_HAS_PROCESS(DirectionalCouplerBi); + + SC_THREAD(on_p0_in_changed); + sensitive << p0_in; + + SC_THREAD(on_p1_in_changed); + sensitive << p1_in; + + SC_THREAD(on_p2_in_changed); + sensitive << p2_in; + + SC_THREAD(on_p3_in_changed); + sensitive << p3_in; + } +}; diff --git a/src/devices/electrical_value_list_source.cpp b/src/devices/electrical_value_list_source.cpp new file mode 100644 index 0000000..02b6af5 --- /dev/null +++ b/src/devices/electrical_value_list_source.cpp @@ -0,0 +1,67 @@ +#include "specs.h" +#include "electrical_value_list_source.h" + +using std::cout; +using std::cerr; +using std::endl; + +void EVLSource::runner() +{ + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "value list (" << m_values_queue.size() << " values)"; + if (false) + { + cout << ":" << endl; + for (const auto& val : m_values_queue) + cout << "\t" << "@" << sc_time(val.first, SC_SEC) << ": " << val.second << endl; + } + cout << endl; + cout << "--> " << + (dynamic_cast<spx::ea_signal_type *>(p_out.get_interface()))->name() << endl; + cout << endl; + } + + // Wait for enable signal + if (! enable.read().to_bool()) + { + // cout << name() << " waiting for enable" << endl; + wait(enable.posedge_event()); + cout << name() << " was enabled" << endl; + } + + // Emitting 0 at enable (will only go through if there is no value at t=0) + if (m_values_queue.cbegin() == m_values_queue.cend() + || sc_time(m_values_queue.front().first, SC_SEC).value() > 0) + { + cout << "@" << sc_time_stamp() << ", " << name() << " emitted: " << spx::ea_value_type(0) << endl; + p_out->write(spx::ea_value_type(0)); + } + + auto it = m_values_queue.cbegin(); + while (it != m_values_queue.cend()) + { + sc_time now = sc_time_stamp(); + if (it->first < now.to_seconds()) + { + ++it; + continue; + } + sc_time delay = sc_time(it->first, SC_SEC) - now; + + // Wait until next output time + wait(delay); + + auto Vout = it->second; + + // Write value to output + p_out->write(Vout); + cout << "@" << sc_time_stamp() << ", " << name() << " emitted: " << Vout << endl; + + ++it; + } + + // cout << name() << " completed" << endl; + while(true) { wait(); } +} diff --git a/src/devices/electrical_value_list_source.h b/src/devices/electrical_value_list_source.h new file mode 100644 index 0000000..0534784 --- /dev/null +++ b/src/devices/electrical_value_list_source.h @@ -0,0 +1,128 @@ +#pragma once + +#include <systemc.h> +#include <fstream> +#include <algorithm> +#include <vector> +#include <utility> + +#include "strutils.h" +#include "optical_output_port.h" +#include "optical_signal.h" +#include "specs.h" +#include "spx_module.h" + +using std::vector; +using std::pair; + +class EVLSource : public spx_module { +public: + typedef pair<double, spx::ea_value_type> time_value_pair_type; + + // Ports + spx::ea_port_out_type p_out; + + // Signal to emit + vector<time_value_pair_type> m_values_queue; + + // Source emission control + spx::ed_signal_type enable; + + // Processes + void runner(); + + EVLSource(sc_module_name name, const vector<time_value_pair_type> &values = {}) + : spx_module(name) + , m_values_queue(values) + { + sortValues(); + enable = sc_logic(0); + + SC_HAS_PROCESS(EVLSource); + SC_THREAD(runner); + } + + void setValues(const vector<time_value_pair_type> &values) + { + m_values_queue = values; + sortValues(); + } + + void setValues(const string &filename) + { + std::ifstream file(filename); + + if (!file.is_open()) { + cerr << "Error: Cannot open the file." << endl; + exit(1); + } + + string line; + + getline(file, line); + + int lineNumber = 0; + while (getline(file, line)) { + ++lineNumber; + istringstream s(line); + string field; + + double t, V; + + // Skip if line is empty or starts with ; + if (line.empty()) + { + cout << "Skipping empty line." << endl; + continue; + } + // Skip if line starts with ; + if (line[0] == ';') + { + cout << "Skipping commented line: " << line << endl; + continue; + } + + // Read first field (time) + getline(s, field, ','); + strutils::trim(field); + + // Skip header line if it exists + if(lineNumber == 1 && field == "time") + { + cout << "Skipping header line: " << line << endl; + continue; + } + + try { + t = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for time: \"" << field << "\"" << endl; + exit(1); + } + + // Read second field (V) + getline(s, field, ','); + try { + V = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for voltage: \"" << field << "\"" << endl; + exit(1); + } + + cout << t << ": " << V << "V" << endl; + m_values_queue.emplace_back(t, V); + } + + file.close(); + sortValues(); + } + + void sortValues() + { + auto cmp = [](const time_value_pair_type& p1, const time_value_pair_type& p2) { + return p1.first < p2.first; + }; + // Stable sort will keep initial ordering for values with identical time + std::stable_sort(m_values_queue.begin(), m_values_queue.end(), cmp); + } +}; diff --git a/src/devices/generic_2x2_coupler.h b/src/devices/generic_2x2_coupler.h new file mode 100644 index 0000000..6cafc17 --- /dev/null +++ b/src/devices/generic_2x2_coupler.h @@ -0,0 +1,86 @@ +#pragma once + +#include "devices/generic_transmission_device.h" + +class Generic2x2Coupler : public GenericTransmissionDevice { +public: + Generic2x2Coupler(sc_module_name name + , const double &k_power = 0.0 + , const double &insertion_loss = 0.0) + : GenericTransmissionDevice(name, 4) + { + setCouplingFactor(k_power); + setInsertionLoss(insertion_loss); + } + + void setCouplingFactor(const double &k_power) + { + assert(isfinite(k_power) && k_power >= 0 && k_power <= 1); + m_k_power = k_power; + } + + void setInsertionLoss(const double &insertion_loss) + { + assert(isfinite(insertion_loss) && insertion_loss >= 0); + m_insertion_loss = insertion_loss; + } + +private: + + virtual void prepareTM() + { + TM.clear(); + TM.resize(nports); + + double lambda0 = 1550e-9; + double transmission_cross = sqrt(m_k_power) * pow(10.0, -m_insertion_loss/20.0); + transmission_cross = max(0.0, min(1.0, transmission_cross)); + double transmission_through = sqrt(1 - m_k_power) * pow(10.0, -m_insertion_loss/20.0); + transmission_through = max(0.0, min(1.0, transmission_through)); + double phi_through = 0.0; + double phi_cross = M_PI / 2.0; + double tau = 0.0; + size_t k; + + // cout << transmission_through << endl; + // cout << transmission_cross << endl; + // cout << m_k_power << endl << endl << endl; + + // (13, 14, 23, 24 are the only active ones) + + // S13 + k = 0*nports + 2; + TM.Mactive[k] = true; + TM.Malpha[k] = {transmission_through}; + TM.Mphi[k] = {phi_through}; + TM.Mtau[k] = {tau}; + + // S14 + k = 0*nports + 3; + TM.Mactive[k] = true; + TM.Malpha[k] = {transmission_cross}; + TM.Mphi[k] = {phi_cross}; + TM.Mtau[k] = {tau}; + + // S23 + k = 1*nports + 2; + TM.Mactive[k] = true; + TM.Malpha[k] = {transmission_cross}; + TM.Mphi[k] = {phi_cross}; + TM.Mtau[k] = {tau}; + + // S24 + k = 1*nports + 3; + TM.Mactive[k] = true; + TM.Malpha[k] = {transmission_through}; + TM.Mphi[k] = {phi_through}; + TM.Mtau[k] = {tau}; + + // cout << TM.Mphi.size() << endl; + // cout << TM.Mphi[0].size() << endl; + // cout << TM.Mphi[1].size() << endl; + } + + double m_insertion_loss; + double m_k_power; // power coupling factor (1-k = transmission factor) +};
\ No newline at end of file diff --git a/src/devices/generic_transmission_device.cpp b/src/devices/generic_transmission_device.cpp new file mode 100644 index 0000000..e709109 --- /dev/null +++ b/src/devices/generic_transmission_device.cpp @@ -0,0 +1,224 @@ +#include "devices/generic_transmission_device.h" + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + SUFFIX + "_" + to_string(IDX)).c_str()) + +using std::size_t; +using std::pow; + +TransmissionMatrix::paramset_type TransmissionMatrix::operator()(const size_t &i, const size_t &j + , const wavelength_type &lambda, const size_t max_order) const +{ + // row/col to index + const size_t k = i * N + j; + + if (Mactive[k] == false) + return {0,0,0}; + + if (max_order == 0) + return {Malpha[k][0], Mphi[k][0], Mtau[k][0]}; + + // Alias relevant taylor series + auto &Malpha_ = Malpha[k]; + auto &Mphi_ = Mphi[k]; + auto &Mtau_ = Mtau[k]; + + // Get initial parameters (values at lambda0) + double alpha = Malpha_[0]; + double phi = Mphi_[0]; + double tau = Mtau_[0]; + + // Calculate distance to lambda0 + const wavelength_type dlambda = lambda - lambda0; + + // Perform taylor expansion of alpha + double dlambda_n_over_fact_n = 1.0; + for (size_t order = 1; order < min(1 + max_order, Malpha_.size()); ++order) + { + double dalpha_over_dlambda_n = Malpha_[order]; + dlambda_n_over_fact_n *= dlambda / order; + alpha += dalpha_over_dlambda_n * dlambda_n_over_fact_n; + } + + // Perform taylor expansion of phi + dlambda_n_over_fact_n = 1.0; + for (size_t order = 1; order < min(1 + max_order, Mphi_.size()); ++order) + { + double dphi_over_dlambda_n = Mphi_[order]; + dlambda_n_over_fact_n *= dlambda / order; + phi += dphi_over_dlambda_n * dlambda_n_over_fact_n; + } + + // Perform taylor expansion of tau + dlambda_n_over_fact_n = 1.0; + for (size_t order = 1; order < min(1 + max_order, Mtau_.size()); ++order) + { + double dtau_over_dlambda_n = Mtau_[order]; + dlambda_n_over_fact_n *= dlambda / order; + tau += dtau_over_dlambda_n * dlambda_n_over_fact_n; + } + + //cout << lambda << ": " << alpha << ", " << fmod(phi, 2*M_PI) << ", " << tau << endl; + return {alpha,phi,tau}; +} + +void GenericTransmissionDevice::pre_init() +{ + ports_in.clear(); + ports_out.clear(); + ports_out_writers.clear(); + + for (size_t i = 0; i < nports; ++i) + { + ports_in.push_back(make_shared<port_in_type>(__modname("_IN", i))); + ports_out.push_back(make_shared<port_out_type>(__modname("_OUT", i))); + ports_out_writers.push_back(make_shared<OpticalOutputPort>(__modname("_OOP", i), (*ports_out[i]))); + ports_out_writers[i]->m_use_deltas = true; + } +} + +void GenericTransmissionDevice::init() +{ + prepareTM(); + assert(nports == TM.N); + for (size_t i = 0; i < nports; ++i) + { + for (size_t j = 0; j < nports; ++j) + { + sc_spawn_options opts; + if (TM.isActive(i, j)) + { + opts.set_sensitivity(ports_in[i].get()); + sc_spawn( sc_bind(&GenericTransmissionDevice::input_on_i_output_on_j, this, i, j), + (string(name()) + "process_" + to_string(i) + "_" + to_string(j)).c_str(), &opts); + } + } + } +} + +string GenericTransmissionDevice::describe() const +{ + return ""s; +} + +void GenericTransmissionDevice::input_on_i(size_t i) +{ + auto last_signal = OpticalSignal(0); + const auto &p_in = (*ports_in[i]); + const bool active = TM.isInputActive(i); + + while(active) + { + // Wait for new input on i_in + wait(); + + // Read new input from i_in + const auto &s = p_in->read(); + + // cout << "received:" << endl << "\t" << s; + + // Compute delta + const auto deltaE_in = s - last_signal; + + // Store new input signal for next change + last_signal = s; + + // Loop on all device ports + for (size_t j = 0; j < nports; ++j) + { + if (!TM.isActive(i,j)) + continue; + // cout << "(i,j):" << i << ", " << j << endl; + + // Get transmission parameters at this wavelength + auto Tij = TM(i, j, s.getWavelength()); + + // Apply parameters + auto deltaE_out = deltaE_in * polar(Tij.alpha, Tij.phi); + + // cout << Tij.alpha << ", " << Tij.phi << ", " << Tij.tau << endl; + + deltaE_out.getNewId(); + + // Schedule writing the change in output port + ports_out_writers[j]->delayedWrite(deltaE_out, sc_time(Tij.tau, SC_SEC)); + } + } + while (true) { wait(); } +} + +void GenericTransmissionDevice::input_on_i_output_on_j(size_t i, size_t j) +{ + auto last_signal = OpticalSignal(0); + const auto &p_in = (*ports_in[i]); + //auto &p_out = (*ports_out[j]); + auto &p_out_writer = (*ports_out_writers[j]); + const bool active = TM.isActive(i, j); + cout << i << ", " << j << " is active" << endl; + + // Block if non active + while (!active) { wait(); } + + complex<double> Sij; + double delay; + + // Wait for first signal to calculate Sij and delay + volatile bool init_done = false; + while (!init_done) + { + // Wait for first input on p_in + wait(); + + // Read new input from i_in + const auto &s = p_in->read(); + + // cout << "received:" << endl << "\t" << s << endl; + + if (isnan(s.getWavelength())) + continue; + + // Find Sij at that wavelength + const auto Tij = TM(i, j, s.getWavelength()); + Sij = polar(Tij.alpha, Tij.phi); + delay = Tij.tau; + + auto deltaE = (s - last_signal) * Sij; + last_signal = s; + p_out_writer.delayedWrite(deltaE, sc_time(delay, SC_SEC)); + + init_done = true; + + // cout << "init done:" << endl << "\t" << norm(Sij) << ", " << arg(Sij) << ", " << delay << endl; + } + + while(true) + { + // Wait for new input on i_in + wait(); + + // Read new input from i_in + const auto &s = p_in->read(); + + if (s.getWavelength() != last_signal.getWavelength()) + { + cerr << "Cannot handle different wavelength (" << __FUNCTION__ << ")" << endl; + cerr << s << endl; + cerr << last_signal << endl; + sc_stop(); + } + + // cout << "received:" << endl << "\t" << s << endl; + + // Compute new delta + auto deltaE = (s - last_signal) * Sij; + + // Store new input signal for next change + last_signal = s; + + //deltaE_out.getNewId(); + + // Schedule writing the change in output port + p_out_writer.delayedWrite(deltaE, sc_time(delay, SC_SEC)); + //wait(SC_ZERO_TIME); + } +}
\ No newline at end of file diff --git a/src/devices/generic_transmission_device.h b/src/devices/generic_transmission_device.h new file mode 100644 index 0000000..bfd110b --- /dev/null +++ b/src/devices/generic_transmission_device.h @@ -0,0 +1,94 @@ +#pragma once + +#define SC_INCLUDE_DYNAMIC_PROCESSES + +#include <systemc.h> +#include <vector> +#include <valarray> +#include "devices/spx_module.h" +#include "optical_signal.h" +#include "optical_output_port.h" + +using std::vector; +using std::valarray; +using std::string; +using std::cout; +using std::endl; +using std::cerr; + +class TransmissionMatrix { +public: + typedef TransmissionMatrix this_type; + typedef double wavelength_type; + typedef double param_type; + typedef valarray<param_type> taylor_series_type; + typedef valarray<taylor_series_type> matrix_type; + typedef struct{ double alpha, phi, tau; } paramset_type; + + size_t N; // Number of ports (= Nrow = Ncol) in the matrix + // Arrays of matrices describing alpha, phi and the group delay via their taylor coefficients (starting from alpha0) + matrix_type Malpha; + matrix_type Mphi; + matrix_type Mtau; + valarray<bool> Mactive; + wavelength_type lambda0; // Wavelength at which the taylor coefficients were calculated + + paramset_type operator()(const size_t &i, const size_t &j, const wavelength_type &lambda, const size_t max_order=2) const; + inline bool isActive(const size_t &i, const size_t &j) const + { return Mactive[i * N + j]; } + inline bool isInputActive(const size_t &i) const + { + bool ret = false; + for (size_t j = 0; j < N; ++j) + ret |= Mactive[i * N + j]; + return ret; + } + + void clear() + { + N = 0; + lambda0 = 0; + Malpha.resize(0); + Mphi.resize(0); + Mtau.resize(0); + Mactive.resize(0); + } + + void resize(size_t nports) + { + N = nports; + Malpha.resize(nports*nports, {0}); + Mphi.resize(nports*nports, {0}); + Mtau.resize(nports*nports, {0}); + Mactive.resize(nports*nports, false); + } +}; + +class GenericTransmissionDevice : public spx_module { +public: + typedef GenericTransmissionDevice this_type; + + /* ------------------------ */ + size_t nports; + vector<shared_ptr<port_in_type>> ports_in; + vector<shared_ptr<port_out_type>> ports_out; + vector<shared_ptr<OpticalOutputPort>> ports_out_writers; + TransmissionMatrix TM; + + /* ------------------------ */ + virtual void pre_init(); + virtual void init(); + virtual string describe() const; + virtual void prepareTM() = 0; + + // Process input on i + void input_on_i(size_t i); + void input_on_i_output_on_j(size_t i, size_t j); + + GenericTransmissionDevice(sc_module_name name, size_t N = 1) + : spx_module(name) + , nports(N) + { + pre_init(); + } +};
\ No newline at end of file diff --git a/src/devices/generic_waveguide.h b/src/devices/generic_waveguide.h new file mode 100644 index 0000000..d704678 --- /dev/null +++ b/src/devices/generic_waveguide.h @@ -0,0 +1,119 @@ +#pragma once + +#include "devices/generic_transmission_device.h" + +using std::isfinite; + +class GenericWaveguide : public GenericTransmissionDevice { +public: + GenericWaveguide(sc_module_name name + , const double &length = 0 + , const double &loss = 0 + , const double &neff = 1 + , const double &ng = 1 + , const double &D = 0 + , const double &lambda0 = 1.55e-6) + : GenericTransmissionDevice(name, 2) + { + cerr << "GenericWaveguide is disabled (not working)." << endl; + exit(1); + + setLength(length); + setLoss(loss); + setEffectiveIndex(neff); + setGroupIndex(ng); + setDispersion(D); + setLambda0(lambda0); + } + + void setLength(const double &length) + { + assert(isfinite(length) && length >= 0); + m_length = length; + } + + void setLoss(const double &loss) + { + cout << loss << endl; + assert(isfinite(loss)); + m_loss = loss; + } + + void setEffectiveIndex(const double &neff) + { + assert(isfinite(neff) && neff >= 0); + m_neff = neff; + } + + void setGroupIndex(const double &ng) + { + assert(isfinite(ng) && ng >= 0); + m_ng = ng; + } + + void setDispersion(const double &D) + { + assert(isfinite(D)); + m_D = D; + } + + void setLambda0(const double &lambda0) + { + assert(isfinite(lambda0) && lambda0 > 0); + m_lambda0 = lambda0; + } + + +private: + + virtual void prepareTM() + { + TM.clear(); + TM.resize(nports); + + const double c = 299792458.0; + double lambda0 = m_lambda0; + double transmission = pow(10.0, -(m_loss * m_length)/20.0); + double two_pi_L = 2 * M_PI * m_length; + double dphi_0 = fmod(two_pi_L * m_neff, 2 * M_PI); + + // The following 2 lines are wrong... + double dphi_1 = - two_pi_L * m_ng / lambda0 / lambda0; // Here it should be -2pi*L*ng/(lambda*lambda0) (instead of lambda0^2) + double dphi_2 = two_pi_L * 2 * m_ng / lambda0 / lambda0 / lambda0; + + double group_delay_0 = m_length * m_ng / c; + double group_delay_1 = m_length * m_D; + + TM.lambda0 = lambda0; + TM.Mactive = {false, true, true, false}; + TM.Malpha = { + {0}, + {transmission}, + {transmission}, + {0}}; + TM.Mphi = { + {0}, + {dphi_0, dphi_1, dphi_2}, + {dphi_0, dphi_1, dphi_2}, + {0}}; + TM.Mtau = { + {0}, + {group_delay_0, group_delay_1}, + {group_delay_0, group_delay_1}, + {0}}; + + // cout << "----------- " << name() << " -----------" << endl; + // cout << "- alpha: " << TM.Malpha[1][0] << " (V/m)/(V/m)" << endl; + // cout << "- transmission: " << pow(TM.Malpha[1][0],2.0) << " W/W" << endl; + // cout << "- phi: " << TM.Mphi[1][0] << " rad" << endl; + // cout << "- dphi/dlambda: " << TM.Mphi[1][1] << " rad/m" << endl; + // cout << "- group delay: " << TM.Mtau[1][0] << " s" << endl; + } + + double m_loss; // loss in dB/m + double m_neff; // real part of neff + double m_ng; // (real part of) ng + double m_D; // dispersion + double m_lambda0; // dispersion + double m_length; // waveguide length +};
\ No newline at end of file diff --git a/src/devices/merger.cpp b/src/devices/merger.cpp new file mode 100644 index 0000000..2aa3e9f --- /dev/null +++ b/src/devices/merger.cpp @@ -0,0 +1,69 @@ +#include <merger.h> +#include <specs.h> + +using namespace std; + +void Merger::on_port_in1_changed() +{ + const double transmission = pow(10.0, - m_attenuation_dB / 20) / sqrt(2); + m_memory_in1[0] = 0; // initializing for nan wavelength + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission = " << pow(transmission, 2) << " W/W" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in1.get_interface()))->name() << endl; + cout << "\t --> \t" << (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name() << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in2.get_interface()))->name(); + cout << endl; + cout << endl; + } + + OpticalSignal p_in1_read; + + while (true) { + // Wait for a new input signal + wait(); + // Read sum of input signals + p_in1_read = p_in1->read(); + + auto cur_wavelength_id = p_in1_read.m_wavelength_id; + // Updating the field memory + m_memory_in1[cur_wavelength_id] = p_in1_read.m_field; + + auto s = OpticalSignal(m_memory_in1[cur_wavelength_id] + + m_memory_in2[cur_wavelength_id] + , cur_wavelength_id); + + s *= transmission; + + m_out_writer.delayedWrite(s,SC_ZERO_TIME); + } +} + +void Merger::on_port_in2_changed() +{ + const double transmission = pow(10.0, - m_attenuation_dB / 20) / sqrt(2); + m_memory_in2[0] = 0; // initializing for nan wavelength + + OpticalSignal p_in2_read; + + while (true) { + // Wait for a new input signal + wait(); + // Read sum of input signals + p_in2_read = p_in2->read(); + + auto cur_wavelength_id = p_in2_read.m_wavelength_id; + // Updating the field memory + m_memory_in2[cur_wavelength_id] = p_in2_read.m_field; + + auto s = OpticalSignal(m_memory_in1[cur_wavelength_id] + + m_memory_in2[cur_wavelength_id] + , cur_wavelength_id); + + s *= transmission; + + m_out_writer.delayedWrite(s,SC_ZERO_TIME); + } +}
\ No newline at end of file diff --git a/src/devices/merger.h b/src/devices/merger.h new file mode 100644 index 0000000..a3c0704 --- /dev/null +++ b/src/devices/merger.h @@ -0,0 +1,51 @@ +#pragma once + +#include <systemc.h> +#include <map> + +#include <spx_module.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +#include "specs.h" +#include "spx_module.h" + +// A symmetric no-delay merger +class Merger : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in1; + spx::oa_port_in_type p_in2; + spx::oa_port_out_type p_out; + + // Timed ports writers + OpticalOutputPort m_out_writer; + + // Member variables + double m_attenuation_dB; + + // Memory for multi-wavelength purposes + // maybe with vector it has better performance + std::map<uint32_t,OpticalSignal::field_type> m_memory_in1; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in2; + + // Processes + void on_port_in1_changed(); + void on_port_in2_changed(); + + // Constructor + Merger(sc_module_name name, + double attenuation_dB = 0) + : spx_module(name) + , m_out_writer("out_delayed_writer", p_out) + , m_attenuation_dB(attenuation_dB) + { + SC_HAS_PROCESS(Merger); + + SC_THREAD(on_port_in1_changed); + sensitive << p_in1; + + SC_THREAD(on_port_in2_changed); + sensitive << p_in2; + } +}; diff --git a/src/devices/mesh_col.cpp b/src/devices/mesh_col.cpp new file mode 100644 index 0000000..5723b17 --- /dev/null +++ b/src/devices/mesh_col.cpp @@ -0,0 +1,243 @@ +#include <mesh_col.h> + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +void MeshCol::init_ports() +{ + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing optical input ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_in.clear(); + for(size_t i = 0; i < m_N; i++) + { + p_in.push_back(make_unique<spx::oa_port_in_type>(__modname("IN_", i))); + } + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing optical output ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_out.clear(); + for(size_t i = 0; i < m_N; i++) + { + p_out.push_back(make_unique<spx::oa_port_out_type>(__modname("OUT_", i))); + } + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing electrical input ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_vphi.clear(); + p_vtheta.clear(); + + size_t num_of_electrical_ports = m_N/2; + if ((m_N % 2 == 0) && !(m_j % 2 == 0)) // if even-odd, there is one less MZI + num_of_electrical_ports = m_N/2 - 1; + + + for(size_t i = 0; i < num_of_electrical_ports; i++) + { + p_vphi.push_back(make_unique<sc_in<double>>(__modname("VPHI_", i))); + p_vtheta.push_back(make_unique<sc_in<double>>(__modname("VTHETA_", i))); + } +} + +void MeshCol::init() +{ + if (m_N % 2 == 0) + { + if (m_j % 2 == 0) + init_N_even_j_even(); + else + init_N_even_j_odd(); + } + else + { + if (m_j % 2 == 0) + init_N_odd_j_even(); + else + init_N_odd_j_odd(); + } +} + +/* + For N=4 that's how it looks + 0__ __0 + \/ + 1__/\__1 + + 2__ __2 + \/ + 3__/\__3 + + TODO: snap to 2pi phase instead of setting neff = 0 +*/ +void MeshCol::init_N_even_j_even() +{ + for(size_t i = 0; i < m_N/2; i++) + { + m_mzi.push_back(make_unique<MZI>(__modname("MZI_", i), + m_length_cm, m_attenuation_wg_dB_cm, + m_attenuation_coupler_dB, m_attenuation_ps_dB, + m_neff, m_ng)); + + m_mzi[i]->p_in1(*p_in[2*i]); + m_mzi[i]->p_in2(*p_in[2*i+1]); + m_mzi[i]->p_out1(*p_out[2*i]); + m_mzi[i]->p_out2(*p_out[2*i+1]); + m_mzi[i]->p_vphi(*p_vphi[i]); + m_mzi[i]->p_vtheta(*p_vtheta[i]); + m_mzi[i]->init(); + } +} + + +/* For N=4 that's how it looks + + 0______0 + + 1__ __1 + \/ + 2__/\__2 + + 3______3 + + The empty rows have a waveguide that adds zero phase + but can have loss and time delay. + + TODO: snap to 2pi phase instead of setting neff = 0 + +*/ +void MeshCol::init_N_even_j_odd() +{ + assert(m_N >= 4); + + // Zero neff so it doesn't add any phase + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), + m_length_cm, m_attenuation_wg_dB_cm, + m_neff, m_ng)); + m_wg[0]->p_in(*p_in[0]); + m_wg[0]->p_out(*p_out[0]); + + for(size_t i = 1; i <= m_N/2 - 1; i++) + { + m_mzi.push_back(make_unique<MZI>(__modname("MZI_", i), + m_length_cm, m_attenuation_wg_dB_cm, + m_attenuation_coupler_dB, m_attenuation_ps_dB, + m_neff, m_ng)); + + m_mzi[i - 1]->p_in1(*p_in[2 * i - 1]); + m_mzi[i - 1]->p_in2(*p_in[2 * i]); + m_mzi[i - 1]->p_out1(*p_out[2 * i - 1]); + m_mzi[i - 1]->p_out2(*p_out[2 * i]); + m_mzi[i - 1]->p_vphi(*p_vphi[i - 1]); + m_mzi[i - 1]->p_vtheta(*p_vtheta[i - 1]); + m_mzi[i - 1]->init(); + + } + + // Zero neff so it doesn't add any phase + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 1), + m_length_cm, m_attenuation_wg_dB_cm, + m_neff, m_ng)); + m_wg[1]->p_in(*p_in[m_N-1]); + m_wg[1]->p_out(*p_out[m_N-1]); + +} + +/* For N=5 that's how it looks + + 0__ __0 + \/ + 1__/\__1 + + 2__ __2 + \/ + 3__/\__3 + + 4______4 + + The empty rows have a waveguide that adds zero phase + but can have loss and time delay. + + TODO: snap to 2pi phase instead of setting neff = 0 + +*/ +void MeshCol::init_N_odd_j_even() +{ + cout << "odd-even" << endl; + for(size_t i = 0; i < m_N/2; i++) + { + m_mzi.push_back(make_unique<MZI>(__modname("MZI_", i), + m_length_cm, m_attenuation_wg_dB_cm, + m_attenuation_coupler_dB, m_attenuation_ps_dB, + m_neff, m_ng)); + + m_mzi[i]->p_in1(*p_in[2*i]); + m_mzi[i]->p_in2(*p_in[2*i+1]); + m_mzi[i]->p_out1(*p_out[2*i]); + m_mzi[i]->p_out2(*p_out[2*i+1]); + m_mzi[i]->p_vphi(*p_vphi[i]); + m_mzi[i]->p_vtheta(*p_vtheta[i]); + m_mzi[i]->init(); + } + // Zero neff so it doesn't add any phase + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), + m_length_cm, m_attenuation_wg_dB_cm, + m_neff, m_ng)); + m_wg[0]->p_in(*p_in[m_N-1]); + m_wg[0]->p_out(*p_out[m_N-1]); +} + +/* For N=5 that's how it looks + + 0______0 + + 1__ __1 + \/ + 2__/\__2 + + 3__ __3 + \/ + 4__/\__4 + + The empty rows have a waveguide that adds zero phase + but can have loss and time delay. + + TODO: snap to 2pi phase instead of setting neff = 0 + +*/ +void MeshCol::init_N_odd_j_odd() +{ + cout << "odd-odd" << endl; + // Zero neff so it doesn't add any phase + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), + m_length_cm, m_attenuation_wg_dB_cm, + m_neff, m_ng)); + m_wg[0]->p_in(*p_in[0]); + m_wg[0]->p_out(*p_out[0]); + + for(size_t i = 1; i <= m_N/2; i++) + { + m_mzi.push_back(make_unique<MZI>(__modname("MZI_", i), + m_length_cm, m_attenuation_wg_dB_cm, + m_attenuation_coupler_dB, m_attenuation_ps_dB, + m_neff, m_ng)); + + m_mzi[i - 1]->p_in1(*p_in[2 * i - 1]); + m_mzi[i - 1]->p_in2(*p_in[2 * i]); + m_mzi[i - 1]->p_out1(*p_out[2 * i - 1]); + m_mzi[i - 1]->p_out2(*p_out[2 * i]); + m_mzi[i - 1]->p_vphi(*p_vphi[i - 1]); + m_mzi[i - 1]->p_vtheta(*p_vtheta[i - 1]); + m_mzi[i - 1]->init(); + } +}
\ No newline at end of file diff --git a/src/devices/mesh_col.h b/src/devices/mesh_col.h new file mode 100644 index 0000000..e0a6066 --- /dev/null +++ b/src/devices/mesh_col.h @@ -0,0 +1,76 @@ +#pragma once + +#include <systemc.h> +#include <specs.h> +#include <spx_module.h> +#include <mzi.h> +#include <waveguide.h> + +/** + * This class implements a single column of an MZI mesh + * specified in the work: Optimal design for universal multiport interferometers + * by Clements et al. (2016). +*/ + +class MeshCol : public spx_module{ +public: + // Ports + vector<unique_ptr<spx::oa_port_in_type>> p_in; + vector<unique_ptr<spx::oa_port_out_type>> p_out; + vector<unique_ptr<sc_in<double>>> p_vphi; + vector<unique_ptr<sc_in<double>>> p_vtheta; + + // Member variables + size_t m_N; // represents the number of inputs of the mesh (rows) + size_t m_j; // represents the current row being evaluated + + // Parameters for the internal MZIs + double m_length_cm; + double m_attenuation_wg_dB_cm; + double m_attenuation_coupler_dB; + double m_attenuation_ps_dB; + double m_neff; + double m_ng; + + // Submodules + vector<unique_ptr<MZI>> m_mzi; + vector<unique_ptr<Waveguide>> m_wg; + + + // Initialization functions + void init_ports(); + void init(); + + void init_N_even_j_even(); + void init_N_even_j_odd(); + + void init_N_odd_j_even(); + void init_N_odd_j_odd(); + + /** Constructor for MZI Mesh Column + * + * @param name name of the module + * @param N number of rows contained in the module + * @param j current column being created, from 0 to N-1 + * */ + MeshCol(sc_module_name name, const size_t &N, const size_t &j, + const double &length_cm = 1e-2, + const double &attenuation_wg_dB_cm = 0, const double &attenuation_coupler_dB = 0, + const double &attenuation_ps_dB = 0, + const double &neff = 2.2111, const double &ng = 2.2637) + : spx_module(name) + , m_N(N) + , m_j(j) + , m_length_cm(length_cm) + , m_attenuation_wg_dB_cm(attenuation_wg_dB_cm) + , m_attenuation_coupler_dB(attenuation_coupler_dB) + , m_attenuation_ps_dB(attenuation_ps_dB) + , m_neff(neff) + , m_ng(ng) + { + assert(N > 1); + assert(j < N); + + init_ports(); + } +};
\ No newline at end of file diff --git a/src/devices/mzi.cpp b/src/devices/mzi.cpp new file mode 100644 index 0000000..6999fd9 --- /dev/null +++ b/src/devices/mzi.cpp @@ -0,0 +1,55 @@ +#include <mzi.h> + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +using namespace std; + +void MZI::init() +{ + DC1.reset(); + DC2.reset(); + PS1.reset(); + PS2.reset(); + WG1.reset(); + WG2.reset(); + + // Creating instances + WG1 = make_unique<Waveguide>((string(name()) + "/WG1").c_str(), + m_length_cm, m_attenuation_dB_cm, m_neff, m_ng); + WG2 = make_unique<Waveguide>((string(name()) + "/WG2").c_str(), + m_length_cm, m_attenuation_dB_cm, m_neff, m_ng); + DC1 = make_unique<DirectionalCoupler>((string(name()) + "/DC1").c_str(), 0.5, m_attenuation_coupler_dB); + DC2 = make_unique<DirectionalCoupler>((string(name()) + "/DC2").c_str(), 0.5, m_attenuation_coupler_dB); + PS1 = make_unique<PhaseShifter>((string(name()) + "/PS1").c_str(), m_attenuation_ps_dB); + PS2 = make_unique<PhaseShifter>((string(name()) + "/PS2").c_str(), m_attenuation_ps_dB); + + // Connecting + + PS1->p_in(p_in1); + PS1->p_out(w1); + PS1->p_vin(p_vphi); + + DC1->p_in1(w1); + DC1->p_in2(p_in2); + DC1->p_out1(w2); + DC1->p_out2(w5); + + // Upper branch + PS2->p_in(w2); + PS2->p_out(w3); + PS2->p_vin(p_vtheta); + + WG1->p_in(w3); + WG1->p_out(w4); + + // Bottom branch + WG2->p_in(w5); + WG2->p_out(w6); + + // End + DC2->p_in1(w4); + DC2->p_in2(w6); + DC2->p_out1(p_out1); + DC2->p_out2(p_out2); +}
\ No newline at end of file diff --git a/src/devices/mzi.h b/src/devices/mzi.h new file mode 100644 index 0000000..95089ad --- /dev/null +++ b/src/devices/mzi.h @@ -0,0 +1,67 @@ +#pragma once + +#include <systemc.h> + +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +#include <directional_coupler.h> +#include <phaseshifter.h> +#include <waveguide.h> + +/** A unidirectional waveguide module. */ +class MZI : public spx_module { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p_in1, p_in2; + /** The optical output ports. */ + spx::oa_port_out_type p_out1, p_out2; + /** The electrical input ports */ + spx::ea_port_in_type p_vphi, p_vtheta; + + // Member variables + + /** Waveguide-related. */ + double m_length_cm; + double m_attenuation_dB_cm; + double m_neff; + double m_ng; + + /** DC-related */ + double m_attenuation_coupler_dB; + + /** PhaseShifter-related */ + double m_attenuation_ps_dB; + + // Member submodules and wires + spx::oa_signal_type w1, w2, w3, w4, w5, w6; + + unique_ptr<PhaseShifter> PS1, PS2; + unique_ptr<Waveguide> WG1, WG2, WG3; + unique_ptr<DirectionalCoupler> DC1,DC2; + + virtual void init(); + + /** Constructor for MZI + * + * @param name name of the module + * @param length_cm internal branch length in cm + * */ + MZI(sc_module_name name, const double &length_cm = 1e-2, + const double &attenuation_wg_dB_cm = 0, const double &attenuation_coupler_dB = 0, + const double &attenuation_ps_dB = 0, + const double &neff = 2.2111, const double &ng = 2.2637) + : spx_module(name) + , m_length_cm(length_cm) + , m_attenuation_dB_cm(attenuation_wg_dB_cm) + , m_neff(neff) + , m_ng(ng) + , m_attenuation_coupler_dB(attenuation_coupler_dB) + , m_attenuation_ps_dB(attenuation_ps_dB) + { + + } +}; diff --git a/src/devices/mzi_active.cpp b/src/devices/mzi_active.cpp new file mode 100644 index 0000000..4a8b8a8 --- /dev/null +++ b/src/devices/mzi_active.cpp @@ -0,0 +1,206 @@ +#include <mzi_active.h> + +void MZIActiveUni::init() +{ + m_dc1_o1 = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out1").c_str()); + m_dc1_o2 = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out2").c_str()); + m_dc2_i1 = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in1").c_str()); + m_dc2_i2 = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in2").c_str()); + m_ps_in = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_in").c_str()); + m_ps_out = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_out").c_str()); + + m_wg1 = make_shared<WaveguideUni>((string(name()) + "/WG1").c_str()); + m_wg2 = make_shared<WaveguideUni>((string(name()) + "/WG2").c_str()); + m_wg3 = make_shared<WaveguideUni>((string(name()) + "/WG3").c_str()); + m_dc1 = make_shared<DirectionalCouplerUni>((string(name()) + "/DC1").c_str()); + m_dc2 = make_shared<DirectionalCouplerUni>((string(name()) + "/DC2").c_str()); + m_ps = make_shared<PhaseShifterUni>((string(name()) + "/PS1").c_str()); + + connect_submodules(); +} + +void MZIActiveUni::connect_submodules() +{ + // Parametrizing the instances + + // cast as unidirectional devices + WaveguideUni * const wg1 = dynamic_cast<WaveguideUni *>(m_wg1.get()); + WaveguideUni * const wg2 = dynamic_cast<WaveguideUni *>(m_wg2.get()); + WaveguideUni * const wg3 = dynamic_cast<WaveguideUni *>(m_wg3.get()); + DirectionalCouplerUni * const dc1 = dynamic_cast<DirectionalCouplerUni *>(m_dc1.get()); + DirectionalCouplerUni * const dc2 = dynamic_cast<DirectionalCouplerUni *>(m_dc2.get()); + PhaseShifterUni * const ps = dynamic_cast<PhaseShifterUni *>(m_ps.get()); + + + // Top waveguides are divided by two to preserve + // the same length in the arms + wg1->m_length_cm = m_length_cm/2; + wg1->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg1->m_neff = m_neff; + wg1->m_ng = m_ng; + + wg2->m_length_cm = m_length_cm/2; + wg2->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg2->m_neff = m_neff; + wg2->m_ng = m_ng; + + wg3->m_length_cm = m_length_ref_cm; + wg3->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg3->m_neff = m_neff; + wg3->m_ng = m_ng; + + dc1->m_dc_loss = m_dc_loss_dB; + dc2->m_dc_loss = m_dc_loss_dB; + + ps->m_sensitivity = m_ps_sens_rad_v; + ps->m_attenuation_dB = m_ps_loss_dB; + + // Connecting the blocks + + dc1->p_in1(p_in1); + dc1->p_in2(p_in2); + dc1->p_out1(*m_dc1_o1); + dc1->p_out2(*m_dc1_o2); + + // Top arm + wg1->p_in(*m_dc1_o1); + wg1->p_out(*m_ps_in); + + ps->p_in(*m_ps_in); + ps->p_out(*m_ps_out); + ps->p_vin(p_vin); + + wg2->p_in(*m_ps_out); + wg2->p_out(*m_dc2_i1); + + // Bot arm + wg3->p_in(*m_dc1_o2); + wg3->p_out(*m_dc2_i2); + + dc2->p_in1(*m_dc2_i1); + dc2->p_in2(*m_dc2_i2); + dc2->p_out1(p_out1); + dc2->p_out2(p_out2); +} + +void MZIActiveBi::init() +{ + // forward + m_dc1_p2_out = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out1_0").c_str()); + m_dc1_p3_out = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out2_0").c_str()); + m_dc2_p0_in = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in1_0").c_str()); + m_dc2_p1_in = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in2_0").c_str()); + m_ps_p0_in = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_in_0").c_str()); + m_ps_p1_out = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_out_0").c_str()); + + // backward + m_dc1_p2_in = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out1_1").c_str()); + m_dc1_p3_in = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_out2_1").c_str()); + m_dc2_p0_out = make_shared<spx::oa_signal_type>((string(name()) + "/DC2_in1_1").c_str()); + m_dc2_p1_out = make_shared<spx::oa_signal_type>((string(name()) + "/DC1_in2_1").c_str()); + m_ps_p0_out = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_in_1").c_str()); + m_ps_p1_in = make_shared<spx::oa_signal_type>((string(name()) + "/PS1_out_1").c_str()); + + // devices + m_wg1 = make_shared<WaveguideBi>((string(name()) + "/WG1").c_str()); + m_wg2 = make_shared<WaveguideBi>((string(name()) + "/WG2").c_str()); + m_wg3 = make_shared<WaveguideBi>((string(name()) + "/WG3").c_str()); + m_dc1 = make_shared<DirectionalCouplerBi>((string(name()) + "/DC1").c_str()); + m_dc2 = make_shared<DirectionalCouplerBi>((string(name()) + "/DC2").c_str()); + m_ps = make_shared<PhaseShifterBi>((string(name()) + "/PS1").c_str()); + + connect_submodules(); +} + +void MZIActiveBi::connect_submodules() +{ + // Parametrizing the instances + + // cast as unidirectional devices + WaveguideBi * const wg1 = dynamic_cast<WaveguideBi *>(m_wg1.get()); + WaveguideBi * const wg2 = dynamic_cast<WaveguideBi *>(m_wg2.get()); + WaveguideBi * const wg3 = dynamic_cast<WaveguideBi *>(m_wg3.get()); + DirectionalCouplerBi * const dc1 = dynamic_cast<DirectionalCouplerBi *>(m_dc1.get()); + DirectionalCouplerBi * const dc2 = dynamic_cast<DirectionalCouplerBi *>(m_dc2.get()); + PhaseShifterBi * const ps = dynamic_cast<PhaseShifterBi *>(m_ps.get()); + + + // Top waveguides are divided by two to preserve + // the same length in the arms + wg1->m_length_cm = m_length_cm/2; + wg1->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg1->m_neff = m_neff; + wg1->m_ng = m_ng; + + wg2->m_length_cm = m_length_cm/2; + wg2->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg2->m_neff = m_neff; + wg2->m_ng = m_ng; + + wg3->m_length_cm = m_length_ref_cm; + wg3->m_attenuation_dB_cm = m_attenuation_dB_cm; + wg3->m_neff = m_neff; + wg3->m_ng = m_ng; + + dc1->m_dc_loss = m_dc_loss_dB; + dc2->m_dc_loss = m_dc_loss_dB; + + ps->m_sensitivity = m_ps_sens_rad_v; + ps->m_attenuation_dB = m_ps_loss_dB; + + // Connecting the blocks + + { // forward + dc1->p0_in(p0_in); + dc1->p1_in(p1_in); + dc1->p2_out(*m_dc1_p2_out); + dc1->p3_out(*m_dc1_p3_out); + + // Top arm + wg1->p0_in(*m_dc1_p2_out); + wg1->p1_out(*m_ps_p0_in); + + ps->p0_in(*m_ps_p0_in); + ps->p1_out(*m_ps_p1_out); + ps->p_vin(p_vin); + + wg2->p0_in(*m_ps_p1_out); + wg2->p1_out(*m_dc2_p0_in); + + // Bot arm + wg3->p0_in(*m_dc1_p3_out); + wg3->p1_out(*m_ps_p1_out); + + dc2->p0_in(*m_dc2_p0_in); + dc2->p1_in(*m_ps_p1_out); + dc2->p2_out(p2_out); + dc2->p3_out(p3_out); + } + + { // backward + dc1->p0_out(p0_out); + dc1->p1_out(p1_out); + dc1->p2_in(*m_dc1_p2_in); + dc1->p3_in(*m_dc1_p3_in); + + // Top arm + wg1->p0_out(*m_dc1_p2_in); + wg1->p1_in(*m_ps_p0_out); + + ps->p0_out(*m_ps_p0_out); + ps->p1_in(*m_ps_p1_in); + ps->p_vin(p_vin); + + wg2->p0_out(*m_ps_p1_in); + wg2->p1_in(*m_dc2_p0_out); + + // Bot arm + wg3->p0_out(*m_dc1_p3_in); + wg3->p1_in(*m_ps_p1_in); + + dc2->p0_out(*m_dc2_p0_out); + dc2->p1_out(*m_ps_p1_in); + dc2->p2_in(p2_in); + dc2->p3_in(p3_in); + } +}
\ No newline at end of file diff --git a/src/devices/mzi_active.h b/src/devices/mzi_active.h new file mode 100644 index 0000000..890322e --- /dev/null +++ b/src/devices/mzi_active.h @@ -0,0 +1,179 @@ +#pragma once + +#include "specs.h" +#include <systemc.h> +#include <waveguide.h> +#include <phaseshifter.h> +#include <directional_coupler.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +//TODO: pass element's characteristics as argument, for instance, pass an example of waveguide, +// or an example of DC such that it copies. Requires: constructors in each element to build +// by copying. + +class MZIActiveBase : public spx_module { +public: + // Member variables + double m_ps_sens_rad_v; + double m_length_cm; + double m_length_ref_cm; + double m_attenuation_dB_cm; + double m_neff; + double m_ng; + double m_dc_loss_dB; // loss of the splitter/merger + double m_ps_loss_dB; // loss of the phase shifter + + virtual void init() = 0; + virtual void connect_submodules() = 0; + + // Member submodules + shared_ptr<WaveguideBase> m_wg1, m_wg2, m_wg3; + shared_ptr<DirectionalCouplerBase> m_dc1, m_dc2; + shared_ptr<PhaseShifterBase> m_ps; + + /** Constructor for Waveguide + * + * @param name name of the module + * @param m_ps_sens_rad_v sensitivity of phase shifter + * @param m_length_cm length of the phase-shifter arm + * @param m_length_ref_cm length of the reference arm + * @param m_attenuation_dB_cm loss per cm of wgs + * @param m_neff neff of wgs + * @param m_ng ng of wgs + * @param m_dc_loss_dB insertion loss of the y-junctions + * @param m_ps_loss_dB insertion loss of the phase shifter + * */ + MZIActiveBase(sc_module_name name, + double ps_sens_rad_v = 1, + double length_cm = 0, double length_ref_cm = 0, + double attenuation_dB_cm = 0, + double neff = 2.2, double ng = 2.6, double dc_loss_dB = 0, + double ps_loss_dB = 0) + : spx_module(name) + , m_ps_sens_rad_v(ps_sens_rad_v) + , m_length_cm(length_cm) + , m_length_ref_cm(length_ref_cm) + , m_attenuation_dB_cm(attenuation_dB_cm) + , m_neff(neff) + , m_ng(ng) + , m_dc_loss_dB(dc_loss_dB) + , m_ps_loss_dB(ps_loss_dB) + { + } +}; + +class MZIActiveUni : public MZIActiveBase { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p_in1, p_in2; + /** The optical output ports. */ + spx::oa_port_out_type p_out1, p_out2; + /** The electrical input ports */ + spx::ea_port_in_type p_vin; + + // Wires + shared_ptr<spx::oa_signal_type> m_dc1_o1, m_dc1_o2, m_dc2_i1, m_dc2_i2; + shared_ptr<spx::oa_signal_type> m_ps_in, m_ps_out; + + virtual void init(); + virtual void connect_submodules(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param m_ps_sens_rad_v sensitivity of phase shifter + * @param m_length_cm length of the phase-shifter arm + * @param m_length_ref_cm length of the reference arm + * @param m_attenuation_dB_cm loss per cm of wgs + * @param m_neff neff of wgs + * @param m_ng ng of wgs + * @param m_dc_loss_dB insertion loss of the y-junctions + * @param m_ps_loss_dB insertion loss of the phase shifter + * */ + MZIActiveUni(sc_module_name name, + double ps_sens_rad_v = 1, + double length_cm = 0, + double length_ref_cm = 0, + double attenuation_dB_cm = 0, + double neff = 2.2, + double ng = 2.6, + double dc_loss_dB = 0, + double ps_loss_dB = 0) + : MZIActiveBase(name, + ps_sens_rad_v, + length_cm, + length_ref_cm, + attenuation_dB_cm, + neff, + ng, + dc_loss_dB, + ps_loss_dB) + { + + } +}; + +typedef MZIActiveUni MZIActive; + +class MZIActiveBi : public MZIActiveBase { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p0_in; + spx::oa_port_in_type p1_in; + spx::oa_port_in_type p2_in; + spx::oa_port_in_type p3_in; + + /** The optical output ports. */ + spx::oa_port_out_type p0_out; + spx::oa_port_out_type p1_out; + spx::oa_port_out_type p2_out; + spx::oa_port_out_type p3_out; + + /** The electrical input ports */ + spx::ea_port_in_type p_vin; + + // Wires + shared_ptr<spx::oa_signal_type> m_dc1_p2_out, m_dc1_p3_out, m_dc2_p0_in, m_dc2_p1_in; + shared_ptr<spx::oa_signal_type> m_dc1_p2_in, m_dc1_p3_in, m_dc2_p0_out, m_dc2_p1_out; + shared_ptr<spx::oa_signal_type> m_ps_p0_in, m_ps_p1_in, m_ps_p0_out, m_ps_p1_out; + + virtual void init(); + virtual void connect_submodules(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param m_ps_sens_rad_v sensitivity of phase shifter + * @param m_length_cm length of the phase-shifter arm + * @param m_length_ref_cm length of the reference arm + * @param m_attenuation_dB_cm loss per cm of wgs + * @param m_neff neff of wgs + * @param m_ng ng of wgs + * @param m_dc_loss_dB insertion loss of the y-junctions + * @param m_ps_loss_dB insertion loss of the phase shifter + * */ + MZIActiveBi(sc_module_name name, + double ps_sens_rad_v = 1, + double length_cm = 0, + double length_ref_cm = 0, + double attenuation_dB_cm = 0, + double neff = 2.2, + double ng = 2.6, + double dc_loss_dB = 0, + double ps_loss_dB = 0) + : MZIActiveBase(name, + ps_sens_rad_v, + length_cm, + length_ref_cm, + attenuation_dB_cm, + neff, + ng, + dc_loss_dB, + ps_loss_dB) + { + + } +}; diff --git a/src/devices/octane_cell.cpp b/src/devices/octane_cell.cpp new file mode 100644 index 0000000..1b5c59c --- /dev/null +++ b/src/devices/octane_cell.cpp @@ -0,0 +1,91 @@ +#include <octane_cell.h> + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +void OctaneCell::init() +{ + if(m_isCompact) + init_compact(); + else + init_full(); +} + +void OctaneCell::init_full() +{ + // Physical parameters of the components + double lambda = 1550e-9; // the wavelength used by the circuit + double neff = 2.2; + double ng = 2.2; + double loss_per_cm = 0; // in waveguide + double loss_merger = 0; + double length_for_2pi = 100*lambda/(neff); + + // Constructing the instances + m_wg1 = make_unique<Waveguide>(__modname("WG_", 0), length_for_2pi, loss_per_cm, neff, ng); + m_wg2 = make_unique<Waveguide>(__modname("WG_", 1), length_for_2pi, loss_per_cm, neff, ng); + m_wg3 = make_unique<Waveguide>(__modname("WG_", 2), length_for_2pi, loss_per_cm, neff, ng); + m_wg4 = make_unique<Waveguide>(__modname("WG_", 3), length_for_2pi, loss_per_cm, neff, ng); + + m_merger = make_unique<Merger>(__modname("Y_", 0), loss_merger); + + m_pcm = make_unique<PCMElement>(__modname("PCM_", 0), m_meltEnergy, m_nStates, 0); + + m_pd = make_unique<Detector>(__modname("PD_", 0)); + + // Connecting + m_wg1->p_in(p_in_r); + m_wg1->p_out(m_merg_in1); + + m_wg2->p_in(p_in_c); + m_wg2->p_out(m_merg_in2); + + m_merger->p_in1(m_merg_in1); + m_merger->p_in2(m_merg_in2); + m_merger->p_out(m_merg_out); + + m_wg3->p_in(m_merg_out); + m_wg3->p_out(m_pcm_in); + + m_pcm->p_in(m_pcm_in); + m_pcm->p_out(m_pcm_out); + + // m_wg4->p_in(m_pcm_in); + m_wg4->p_in(m_pcm_out); + m_wg4->p_out(m_pd_in); + + m_pd->p_in(m_pd_in); + m_pd->p_readout(p_readout); +} + +void OctaneCell::init_compact() +{ + // Physical parameters of the components + double lambda = 1550e-9; // the wavelength used by the circuit + double neff = 2.2; + double ng = 2.2; + double loss_per_cm = 0; // in waveguide + double loss_merger = 0; + double length_for_2pi = 100*lambda/(neff); + + (void)ng; + (void)loss_per_cm; + (void)length_for_2pi; + + m_merger = make_unique<Merger>(__modname("Y_", 0), loss_merger); + + m_pcm = make_unique<PCMElement>(__modname("PCM_", 0), m_meltEnergy, m_nStates, 0); + + m_pd = make_unique<Detector>(__modname("PD_", 0)); + + // Connecting + m_merger->p_in1(p_in_r); + m_merger->p_in2(p_in_c); + m_merger->p_out(m_merg_out); + + m_pcm->p_in(m_merg_out); + m_pcm->p_out(m_pcm_out); + + m_pd->p_in(m_pcm_out); + m_pd->p_readout(p_readout); +}
\ No newline at end of file diff --git a/src/devices/octane_cell.h b/src/devices/octane_cell.h new file mode 100644 index 0000000..aadcd20 --- /dev/null +++ b/src/devices/octane_cell.h @@ -0,0 +1,61 @@ +#pragma once + +#include <systemc.h> +#include <waveguide.h> +#include <pcm_device.h> +#include <merger.h> +#include <detector.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +/** The minimal octane cell composed of merger, waveguides, PCM and a detector. */ +/** For now, all photonic parameters will be internal and constant. The only */ +/** one that can be changed is the PCM levels.*/ + +//TODO: pass element's characteristics as argument, for instance, pass an example of waveguide, +// or an example of DC, PCM such that it copies. Requires: constructors in each element to build +// by copying. + +class OctaneCell : public sc_module { +public: + // Ports + /** The optical input ports. */ + sc_port<sc_signal_in_if<OpticalSignal>> p_in_r, p_in_c; + /** The electrical output port. */ + sc_out<double> p_readout; + + // Member variables + double m_meltEnergy; + bool m_isCompact; + int m_nStates; + + // Wires + sc_signal<OpticalSignal> m_merg_in1, m_merg_in2, m_merg_out, m_pcm_in, m_pcm_out, m_pd_in; + + // Member submodules + unique_ptr<Waveguide> m_wg1, m_wg2, m_wg3, m_wg4; + unique_ptr<Merger> m_merger; + unique_ptr<PCMElement> m_pcm; + unique_ptr<Detector> m_pd; + + // This function should be called right after + // Instantiation (either declaration or make_shared or new) + void init(); + void init_compact(); + void init_full(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param meltEnergy energy needed to amorphize by one step + * @param nStates number of internal states supported by the PCM + * */ + OctaneCell(sc_module_name name, double meltEnergy = 10e-12, int nStates = 63, bool isCompact = false) + : sc_module(name) + , m_meltEnergy(meltEnergy) + , m_isCompact(isCompact) + , m_nStates(nStates) + { + } + +}; diff --git a/src/devices/octane_matrix.cpp b/src/devices/octane_matrix.cpp new file mode 100644 index 0000000..d8126f3 --- /dev/null +++ b/src/devices/octane_matrix.cpp @@ -0,0 +1,365 @@ +#include <octane_matrix.h> + + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +#define __outname(SUFFIX, IDX, IDY) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX) + "_" + to_string(IDY)).c_str()) + +using std::size_t; +using std::pow; + +void OctaneMatrix::init_ports() +{ + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing input ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_in_r.clear(); + p_in_c.clear(); + + // cout << "Initializing input ports of " << name() << endl; + + for(size_t i = 0; i < m_rows; i++) + p_in_r.push_back(make_unique<port_in_type>(__modname("IN_R_", i))); + + for(size_t i = 0; i < m_columns; i++) + p_in_c.push_back(make_unique<port_in_type>(__modname("IN_C_", i))); + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing output port matrix + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_readout.clear(); // clearing before start + + // cout << "Initializing output ports of " << name() << endl; + + vector<unique_ptr<sc_out<double>>> temp_vector; + + for(size_t i = 0; i < m_rows; i++) + { + temp_vector.clear(); // moving to new row + for(size_t j = 0; j < m_columns; j++) + { + temp_vector.push_back(make_unique<sc_out<double>>(__outname("PD_", i, j))); + } + p_readout.push_back(std::move(temp_vector)); // adding new row + } + + // cout << "Successful port initialization of " << name() << endl; + +} + +void OctaneMatrix::init() +{ + + double crossing_loss = 0; + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing circuit elements (waveguides, DCs, octanecell) + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + m_optical_connect_horizontal.clear(); + m_optical_connect_vertical.clear(); + m_segment.clear(); + m_cross.clear(); + + // cout << "Initializing submodules of " << name() << endl; + + // Optical wires + unsigned long num_of_nets_horizontal = 3*m_rows*(m_columns-1); + unsigned long num_of_nets_vertical = (m_rows-1)*m_columns; + + for(unsigned long i = 0; i < num_of_nets_horizontal; i++) + m_optical_connect_horizontal.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("wh_", i))); + for(unsigned long i = 0; i < num_of_nets_vertical; i++) + m_optical_connect_vertical.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("wv_", i))); + + // Crossings + for(unsigned long i = 0; i < m_rows; i++) + { // Last column doesn't have crossings, thus the -1 in the loop + for(unsigned long j = 0; j < (m_columns - 1); j++) + { + m_cross.push_back(make_unique<Crossing>(__outname("X_", i, j), crossing_loss)); + } + } + + // Octane segments + bool term_row = false; + bool term_col = false; + double coupling_through_row = 0; + double coupling_through_col = 0; + + for(unsigned long i = 0; i < m_rows; i++) + { + for(unsigned long j = 0; j < m_columns; j++) + { + if(isLastRow(i) && isLastCol(j)) + { + // Last element + term_col = true; + term_row = true; + coupling_through_row = 0; + coupling_through_col = 0; + } + else if(isLastRow(i)) + { + // Term row + term_col = false; + term_row = true; + coupling_through_row = (1.0 - 1.0/(m_columns - j)); + coupling_through_col = 0; + } + else if(isLastCol(j)) + { + // Term col + term_col = true; + term_row = false; + coupling_through_row = 0; + coupling_through_col = (1.0 - 1.0/(m_rows - i)); + } + else + { + // Inner element + term_col = false; + term_row = false; + coupling_through_row = (1.0 - 1.0/(m_columns - j)); + coupling_through_col = (1.0 - 1.0/(m_rows - i)); + } + auto temp_segment = make_unique<OctaneSegment> + ( + __outname("SEG_", i, j), + term_row, term_col, + coupling_through_row, coupling_through_col, + m_meltEnergy, m_nStates, m_isCompact + ); + temp_segment->init(); // mandatory initialization + m_segment.push_back(std::move(temp_segment)); + } + } + + // cout << "Successful module initialization of " << name() << endl; + + // Modules are constructed in their vectors, time to connect ! + connect_submodules(); + +} + +void OctaneMatrix::connect_submodules() +{ + + // cout << "Connecting submodules of " << name() << endl; + + // If there is only one element in the matrix + // the pins are connected directly + if((m_rows == 1) && (m_columns == 1)) + { + // cout << name() << "contains only one element. " << endl; + + m_segment.at(0)->p_in[0]->bind(*p_in_r.at(0)); + m_segment.at(0)->p_in[1]->bind(*p_in_c.at(0)); + m_segment.at(0)->p_readout->bind(*p_readout[0][0]); + + // cout << "Successful connection of " << name() << endl; + return; + } + + // Column vector + if(m_columns == 1) + { + // cout << name() << "contains only one column. " << endl; + + for(unsigned long i = 0; i < m_rows; i++) + { + // in segment, in row is p_in[0] + m_segment.at(i)->p_in[0]->bind(*p_in_r.at(i)); + + if(isFirstRow(i)) // should connect to the port + m_segment.at(i)->p_in[1]->bind(*p_in_c.at(0)); + else // connects to intermediate net which is output of the previous block + m_segment.at(i)->p_in[1]->bind(*m_optical_connect_vertical.at(i-1)); + + if(!isLastRow(i)) // there's only one output in vector segment + m_segment.at(i)->p_out[0]->bind(*m_optical_connect_vertical.at(i)); + + m_segment.at(i)->p_readout->bind(*p_readout[i][0]); + + } + // cout << "Successful connection of " << name() << endl; + return; + } + + // Row vector + if(m_rows == 1) + { + // cout << name() << "contains only one row. " << endl; + + for(unsigned long j = 0; j < m_columns; j++) + { + if(isFirstCol(j)) + { + // in segment, in row is p_in[0] + m_segment.at(j)->p_in[0]->bind(*p_in_r.at(0)); + m_segment.at(j)->p_in[1]->bind(*m_optical_connect_horizontal.at(3*j+1)); + m_segment.at(j)->p_out[0]->bind(*m_optical_connect_horizontal.at(3*j)); + + // in1, out1 are horizontal connections + m_cross.at(j)->p_in1.bind(*m_optical_connect_horizontal.at(3*j)); + m_cross.at(j)->p_in2.bind(*p_in_c.at(j)); + m_cross.at(j)->p_out1.bind(*m_optical_connect_horizontal.at(3*j+2)); + m_cross.at(j)->p_out2.bind(*m_optical_connect_horizontal.at(3*j+1)); + } + else if(!isLastCol(j)) + { // middle element + m_segment.at(j)->p_in[0]->bind(*m_optical_connect_horizontal.at(3*j-1)); + m_segment.at(j)->p_in[1]->bind(*m_optical_connect_horizontal.at(3*j+1)); + m_segment.at(j)->p_out[0]->bind(*m_optical_connect_horizontal.at(3*j)); + + // in1, out1 are horizontal connections + m_cross.at(j)->p_in1.bind(*m_optical_connect_horizontal.at(3*j)); + m_cross.at(j)->p_in2.bind(*p_in_c.at(j)); + m_cross.at(j)->p_out1.bind(*m_optical_connect_horizontal.at(3*j+2)); + m_cross.at(j)->p_out2.bind(*m_optical_connect_horizontal.at(3*j+1)); + } + else + { // last column here + m_segment.at(j)->p_in[0]->bind(*m_optical_connect_horizontal.at(3*j-1)); + m_segment.at(j)->p_in[1]->bind(*p_in_c.at(j)); + } + m_segment.at(j)->p_readout->bind(*p_readout[0][j]); + } + // cout << "Successful connection of " << name() << endl; + return; + } + + // Getting here means it's an actual matrix with more than one element + unsigned long cur_pos; + unsigned long cur_cross; + unsigned long seg_out_h; + unsigned long seg_out_v; + for(unsigned long i = 0; i < m_rows; i++) + { + for(unsigned long j = 0; j < m_columns; j++) + { + cur_pos = m_columns*i + j; + cur_cross = (m_columns-1)*i + j; + seg_out_h = 3*(m_columns-1)*i+3*j; + seg_out_v = m_columns*i+j; + // cout << "cur_pos: " << cur_pos << endl; + // cout << "cur_cross: " << cur_cross << endl; + // cout << "out_h: " << seg_out_h << endl; + // cout << "out_v: " << seg_out_v << endl; + + if(isFirstRow(i) && isFirstCol(j)) + { + // cout << "first element" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*p_in_r.at(i)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_segment.at(cur_pos)->p_out[1]->bind(*m_optical_connect_vertical.at(seg_out_v)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*p_in_c.at(j)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(isFirstRow(i) && !isLastCol(j)) + { + // cout << "first row not last col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_segment.at(cur_pos)->p_out[1]->bind(*m_optical_connect_vertical.at(seg_out_v)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*p_in_c.at(j)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(isFirstRow(i) && isLastCol(j)) + { + // cout << "first row last col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*p_in_c.at(j)); + // no row output + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_vertical.at(seg_out_v)); + } + else if(!isFirstRow(i) && !isLastRow(i) && isFirstCol(j)) + { + // cout << "middle row first col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*p_in_r.at(i)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_segment.at(cur_pos)->p_out[1]->bind(*m_optical_connect_vertical.at(seg_out_v)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(!isFirstRow(i) && !isLastRow(i) && !isLastCol(j)) + { + // cout << "middle row middle col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_segment.at(cur_pos)->p_out[1]->bind(*m_optical_connect_vertical.at(seg_out_v)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(!isFirstRow(i) && !isLastRow(i) && isLastCol(j)) + { + // cout << "middle row last col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + // no row output + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_vertical.at(seg_out_v)); + } + else if(isLastRow(i) && isFirstCol(j)) + { + // cout << "last row first col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*p_in_r.at(i)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(isLastRow(i) && !isLastCol(j)) + { + // cout << "last row middle col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + m_segment.at(cur_pos)->p_out[0]->bind(*m_optical_connect_horizontal.at(seg_out_h)); + + m_cross.at(cur_cross)->p_in1.bind(*m_optical_connect_horizontal.at(seg_out_h)); + m_cross.at(cur_cross)->p_in2.bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + m_cross.at(cur_cross)->p_out1.bind(*m_optical_connect_horizontal.at(seg_out_h+2)); + m_cross.at(cur_cross)->p_out2.bind(*m_optical_connect_horizontal.at(seg_out_h+1)); + } + else if(isLastRow(i) && isLastCol(j)) + { + // cout << "last row last col" << endl; + m_segment.at(cur_pos)->p_in[0]->bind(*m_optical_connect_horizontal.at(seg_out_h-1)); + m_segment.at(cur_pos)->p_in[1]->bind(*m_optical_connect_vertical.at(seg_out_v-m_columns)); + } + else + cout<< endl << endl << "This condition should not appear" << endl << endl; + + m_segment.at(cur_pos)->p_readout->bind(*p_readout[i][j]); + + } + } +}
\ No newline at end of file diff --git a/src/devices/octane_matrix.h b/src/devices/octane_matrix.h new file mode 100644 index 0000000..afcf06a --- /dev/null +++ b/src/devices/octane_matrix.h @@ -0,0 +1,99 @@ +#pragma once + +#include <systemc.h> +#include <octane_segment.h> +#include <crossing.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +// TODO: remove these (are in spx_module.h) +typedef sc_signal_in_if<OpticalSignal > if_in_type; +typedef sc_signal_out_if<OpticalSignal > if_out_type; +typedef sc_port<if_in_type> port_in_type; +typedef sc_port<if_out_type> port_out_type; + + +/** Building block for the OCTANE circuit + * The current implementation includes all + * intermediate components allowing modelling of imperfections, + * but at the cost of increased module count. + * + * A simpler way would be to disregard all intermediate waveguides + * and model their effects on `octane_cell` + * + * Maybe I could add an argument to init(bool isCompact), defining + * if the model will be compact or not. +*/ + +//TODO: pass element's characteristics as argument, for instance, pass an example of waveguide, +// or an example of DC, PCM such that it copies. Requires: constructors in each element to build +// by copying. + +class OctaneMatrix : public sc_module { +public: + // Ports + /** The optical input ports. */ + vector<unique_ptr<port_in_type>> p_in_r; + vector<unique_ptr<port_in_type>> p_in_c; + + /** The optical output ports. */ + vector<unique_ptr<port_out_type>> p_out; + + /** The electric output ports in matrix format*/ + // TODO: verify if a matrix library should be used + vector<vector<unique_ptr<sc_out<double>>>> p_readout; + + // Define the size of the matrix + std::size_t m_rows; + std::size_t m_columns; + + bool m_isCompact; + + // For internal octane_cell + double m_meltEnergy; + int m_nStates; + + // Wires + vector<unique_ptr<sc_signal<OpticalSignal>>> m_optical_connect_horizontal; + vector<unique_ptr<sc_signal<OpticalSignal>>> m_optical_connect_vertical; + + // Member submodules + vector<unique_ptr<Crossing>> m_cross; + vector<unique_ptr<OctaneSegment>> m_segment; + + //Init() must be called after declaration + void init(); + void init_ports(); + void connect_submodules(); + + // auxiliar functions for readability + // they were written with 0 to N-1 indexing in mind! + inline bool isFirstRow(unsigned long i){return (i==0);} + bool isFirstCol(unsigned long j){return (j==0);} + bool isLastRow(unsigned long i){return (i==m_rows-1);} + bool isLastCol(unsigned long j){return (j==m_columns-1);} + + /** Constructor for Octane Matrix + * + * @param name name of the module + * @param meltEnergy energy needed to amorphize by one step + * @param nStates number of internal states supported by the PCM + * */ + OctaneMatrix(sc_module_name name, std::size_t rows, std::size_t columns, + double meltEnergy = 10e-12, int nStates = 63, bool isCompact = false) + : sc_module(name) + , m_rows(rows) + , m_columns(columns) + , m_isCompact(isCompact) + , m_meltEnergy(meltEnergy) + , m_nStates(nStates) + { + + assert(rows > 0); + assert(columns > 0); + + init_ports(); // At least the ports should always be + // in the constructor + } + +}; diff --git a/src/devices/octane_segment.cpp b/src/devices/octane_segment.cpp new file mode 100644 index 0000000..32dcd84 --- /dev/null +++ b/src/devices/octane_segment.cpp @@ -0,0 +1,396 @@ +#include <octane_segment.h> + + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + +using std::size_t; +using std::pow; + +void OctaneSegment::init_ports() +{ + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing input ports + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_in.clear(); + + // cout << "Initializing input ports of " << name() << endl; + // 0 is row, 1 is column + p_in.push_back(make_unique<port_in_type>("IN_R")); + p_in.push_back(make_unique<port_in_type>("IN_C")); + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing output ports based on termination + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + p_out.clear(); + + // cout << "Initializing output ports of " << name() << endl; + // If there is only one output, the vector has only one element + if (m_term_row && m_term_col) + { + ; // no optical output + } + else if(m_term_row) + { + // Last row, so only has row outputs + p_out.push_back(make_unique<port_out_type>("OUT_R")); + } + else if(m_term_col) + { + // Last column, so only has only column outputs + p_out.push_back(make_unique<port_out_type>("OUT_C")); + } + else + { + // Internal elements have both outputs + p_out.push_back(make_unique<port_out_type>("OUT_R")); + p_out.push_back(make_unique<port_out_type>("OUT_C")); + } + p_readout = make_unique<sc_out<double>>("OUT_PD"); + + // cout << "Successful port initialization of " << name() << endl; + +} + +void OctaneSegment::init() +{ + if(m_isCompact) + init_compact(); + else + init_full(); + +} + +void OctaneSegment::init_full() +{ + // Physical parameters of the components + double lambda = 1550e-9; // the wavelength used by the circuit + double neff = 2.2; + double ng = 2.2; + double loss_per_cm = 0; // in waveguide + double dc_loss = 0; + double length_for_2pi = 100*lambda/(neff); + double length_for_pi_2 = length_for_2pi/4; + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing circuit elements (waveguides, DCs, octanecell) + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + m_wg.clear(); + m_dc.clear(); + m_optical_connect.clear(); + + // cout << "Initializing submodules of " << name() << endl; + + m_oct = make_unique<OctaneCell>(__modname("OCT_", 0), m_meltEnergy, m_nStates, m_isCompact); + m_oct->init(); + + if (m_term_row && m_term_col) + { + // Last element + for(size_t i = 0; i < 2; i++) + { + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", i), length_for_2pi, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", i))); + } + } + else if(m_term_row) + { + // Last row + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_R_", 0), m_coupling_through_row, dc_loss)); + for(size_t i = 0; i < 4; i++) + { + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", i), length_for_2pi, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", i))); + } + // Adjusting the last waveguide to give pi/2 shift + // compensating for the lack of DC + m_wg.at(3)->m_length_cm = length_for_2pi + length_for_pi_2; + // We have one extra wire with respect to waveguides + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 4))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + } + else if(m_term_col) + { + // Last column + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_C_", 0), m_coupling_through_col, dc_loss)); + for(size_t i = 0; i < 4; i++) + { + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", i), length_for_2pi, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", i))); + } + // Adjusting the last waveguide to give pi/2 shift + // compensating for the lack of DC + m_wg.at(3)->m_length_cm = length_for_2pi + length_for_pi_2; + // We have one extra wire with respect to waveguides + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 4))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + } + else + { + // Internal element + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_R_", 0), m_coupling_through_row, dc_loss)); + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_C_", 0), m_coupling_through_col, dc_loss)); + for(size_t i = 0; i < 6; i++) + { + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", i), length_for_2pi, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", i))); + } + // We have two extra wires with respect to waveguides + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 6))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 7))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 1))); // terminator + } + + // cout << "Successful module initialization of " << name() << endl; + + // Modules are constructed in their vectors, time to connect ! + connect_submodules_full(); + +} + +void OctaneSegment::connect_submodules_full() +{ + + // cout << "Connecting submodules of " << name() << endl; + + if (m_term_row && m_term_col) + { + // Last element + m_wg.at(0)->p_in(*p_in.at(0)); + m_wg.at(0)->p_out(*m_optical_connect.at(0)); + + m_wg.at(1)->p_in(*p_in.at(1)); + m_wg.at(1)->p_out(*m_optical_connect.at(1)); + + m_oct->p_in_r(*m_optical_connect.at(0)); + m_oct->p_in_c(*m_optical_connect.at(1)); + + } + else if(m_term_row) + { + // Last row + m_wg.at(0)->p_in(*p_in.at(0)); // input at row + m_wg.at(0)->p_out(*m_optical_connect.at(0)); + + m_dc.at(0)->p_in1(*m_optical_connect.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(5)); // Terminated + m_dc.at(0)->p_out1(*m_optical_connect.at(1)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(2)); // Cross + + m_wg.at(1)->p_in(*m_optical_connect.at(1)); + m_wg.at(1)->p_out(*p_out.at(0)); // Out row + + m_wg.at(2)->p_in(*m_optical_connect.at(2)); + m_wg.at(2)->p_out(*m_optical_connect.at(3)); + + // Side without DC (must be conmpensated with pi/2) + m_wg.at(3)->p_in(*p_in.at(1)); + m_wg.at(3)->p_out(*m_optical_connect.at(4)); + + m_oct->p_in_r(*m_optical_connect.at(3)); + m_oct->p_in_c(*m_optical_connect.at(4)); + } + else if(m_term_col) + { + // Last column + m_wg.at(0)->p_in(*p_in.at(1)); // input at col + m_wg.at(0)->p_out(*m_optical_connect.at(0)); + + m_dc.at(0)->p_in1(*m_optical_connect.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(5)); // Terminated + m_dc.at(0)->p_out1(*m_optical_connect.at(1)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(2)); // Cross + + m_wg.at(1)->p_in(*m_optical_connect.at(1)); + m_wg.at(1)->p_out(*p_out.at(0)); // Out column + + m_wg.at(2)->p_in(*m_optical_connect.at(2)); + m_wg.at(2)->p_out(*m_optical_connect.at(3)); + + // Side without DC (must be conmpensated with pi/2) + m_wg.at(3)->p_in(*p_in.at(0)); + m_wg.at(3)->p_out(*m_optical_connect.at(4)); + + m_oct->p_in_r(*m_optical_connect.at(4)); + m_oct->p_in_c(*m_optical_connect.at(3)); + } + else + { + // Internal element + m_wg.at(0)->p_in(*p_in.at(0)); // input at row + m_wg.at(0)->p_out(*m_optical_connect.at(0)); + + m_dc.at(0)->p_in1(*m_optical_connect.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(8)); // Terminated + m_dc.at(0)->p_out1(*m_optical_connect.at(1)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(2)); // Cross + + m_wg.at(1)->p_in(*m_optical_connect.at(1)); + m_wg.at(1)->p_out(*p_out.at(0)); // Out row + + m_wg.at(2)->p_in(*m_optical_connect.at(2)); + m_wg.at(2)->p_out(*m_optical_connect.at(3)); + + m_wg.at(3)->p_in(*p_in.at(1)); + m_wg.at(3)->p_out(*m_optical_connect.at(4)); + + m_dc.at(1)->p_in1(*m_optical_connect.at(4)); + m_dc.at(1)->p_in2(*m_optical_connect.at(9)); // Terminated + m_dc.at(1)->p_out1(*m_optical_connect.at(5)); // Through + m_dc.at(1)->p_out2(*m_optical_connect.at(6)); // Cross + + m_wg.at(4)->p_in(*m_optical_connect.at(5)); + m_wg.at(4)->p_out(*p_out.at(1)); // Out col + + m_wg.at(5)->p_in(*m_optical_connect.at(6)); + m_wg.at(5)->p_out(*m_optical_connect.at(7)); + + m_oct->p_in_r(*m_optical_connect.at(3)); + m_oct->p_in_c(*m_optical_connect.at(7)); + } + m_oct->p_readout(*p_readout); + + // cout << "Successful connection of " << name() << endl; + +} + +void OctaneSegment::init_compact() +{ + // Physical parameters of the components + double lambda = 1550e-9; // the wavelength used by the circuit + double neff = 2.2; + double ng = 2.2; + double loss_per_cm = 0; // in waveguide + double dc_loss = 0; + double length_for_2pi = 100*lambda/(neff); + double length_for_pi_2 = length_for_2pi/4; + + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + // Initializing circuit elements (waveguides, DCs, octanecell) + // --------------------------------------------------------------------------// + // --------------------------------------------------------------------------// + + m_wg.clear(); + m_dc.clear(); + m_optical_connect.clear(); + + // cout << "Initializing submodules of " << name() << endl; + + m_oct = make_unique<OctaneCell>(__modname("OCT_", 0), m_meltEnergy, m_nStates, m_isCompact); + m_oct->init(); + + if (m_term_row && m_term_col) + { + ; + } + else if(m_term_row) + { + // Last row + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_R_", 0), m_coupling_through_row, dc_loss)); + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), length_for_pi_2, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 0))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 1))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + } + else if(m_term_col) + { + // Last column + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_C_", 0), m_coupling_through_col, dc_loss)); + m_wg.push_back(make_unique<Waveguide>(__modname("WG_", 0), length_for_pi_2, loss_per_cm, neff, ng)); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 0))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 1))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + } + else + { + // Internal element + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_R_", 0), m_coupling_through_row, dc_loss)); + m_dc.push_back(make_unique<DirectionalCoupler>(__modname("DC_C_", 0), m_coupling_through_col, dc_loss)); + // We have two extra wires with respect to waveguides + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 0))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("W_", 1))); + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 0))); // terminator + m_optical_connect.push_back(make_unique<sc_signal<OpticalSignal>>(__modname("TERM_", 1))); // terminator + } + + // cout << "Successful module initialization of " << name() << endl; + + // Modules are constructed in their vectors, time to connect ! + connect_submodules_compact(); + +} + +void OctaneSegment::connect_submodules_compact() +{ + + // cout << "Connecting submodules of " << name() << endl; + + if (m_term_row && m_term_col) + { + // Last element + m_oct->p_in_r(*p_in.at(0)); + m_oct->p_in_c(*p_in.at(1)); + + } + else if(m_term_row) + { + // Last row + m_dc.at(0)->p_in1(*p_in.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(2)); // Terminated + m_dc.at(0)->p_out1(*p_out.at(0)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(0)); // Cross + + // Side without DC (must be conmpensated with pi/2) + m_wg.at(0)->p_in(*p_in.at(1)); + m_wg.at(0)->p_out(*m_optical_connect.at(1)); + + m_oct->p_in_r(*m_optical_connect.at(0)); + m_oct->p_in_c(*m_optical_connect.at(1)); + } + else if(m_term_col) + { + // Last column + m_dc.at(0)->p_in1(*p_in.at(1)); + m_dc.at(0)->p_in2(*m_optical_connect.at(2)); // Terminated + m_dc.at(0)->p_out1(*p_out.at(0)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(0)); // Cross + + // Side without DC (must be conmpensated with pi/2) + m_wg.at(0)->p_in(*p_in.at(0)); + m_wg.at(0)->p_out(*m_optical_connect.at(1)); + + m_oct->p_in_r(*m_optical_connect.at(1)); + m_oct->p_in_c(*m_optical_connect.at(0)); + } + else + { + // Internal element + m_dc.at(0)->p_in1(*p_in.at(0)); + m_dc.at(0)->p_in2(*m_optical_connect.at(2)); // Terminated + m_dc.at(0)->p_out1(*p_out.at(0)); // Through + m_dc.at(0)->p_out2(*m_optical_connect.at(0)); // Cross + + m_dc.at(1)->p_in1(*p_in.at(1)); + m_dc.at(1)->p_in2(*m_optical_connect.at(3)); // Terminated + m_dc.at(1)->p_out1(*p_out.at(1)); // Through + m_dc.at(1)->p_out2(*m_optical_connect.at(1)); // Cross + + m_oct->p_in_r(*m_optical_connect.at(0)); + m_oct->p_in_c(*m_optical_connect.at(1)); + } + m_oct->p_readout(*p_readout); + + // cout << "Successful connection of " << name() << endl; + +}
\ No newline at end of file diff --git a/src/devices/octane_segment.h b/src/devices/octane_segment.h new file mode 100644 index 0000000..ea28f03 --- /dev/null +++ b/src/devices/octane_segment.h @@ -0,0 +1,98 @@ +#pragma once + +#include <systemc.h> +#include <waveguide.h> +#include <directional_coupler.h> +#include <octane_cell.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +// TODO: remove these (are in spx_module.h) +typedef sc_signal_in_if<OpticalSignal > if_in_type; +typedef sc_signal_out_if<OpticalSignal > if_out_type; +typedef sc_port<if_in_type> port_in_type; +typedef sc_port<if_out_type> port_out_type; + + +/** Building block for the OCTANE circuit + * The current implementation includes all + * intermediate components allowing modelling of imperfections, + * but at the cost of increased module count. + * + * A simpler way would be to disregard all intermediate waveguides + * and model their effects on `octane_cell` + * + * Maybe I could add an argument to init(bool isCompact), defining + * if the model will be compact or not. +*/ + +//TODO: pass element's characteristics as argument, for instance, pass an example of waveguide, +// or an example of DC, PCM such that it copies. Requires: constructors in each element to build +// by copying. + +class OctaneSegment : public sc_module { +public: + // Ports + /** The optical input ports. */ + vector<unique_ptr<port_in_type>> p_in; + + /** The optical output ports. */ + vector<unique_ptr<port_out_type>> p_out; + + /** The electric output port*/ + unique_ptr<sc_out<double>> p_readout; + + // Member variables + // Defines the number of ports and elements + bool m_term_row; + bool m_term_col; + bool m_isCompact; + + // Defines the coupling in the DCs (should change as more elements are added) + double m_coupling_through_row; + double m_coupling_through_col; + + // For internal octane_cell + double m_meltEnergy; + int m_nStates; + + + // Wires + vector<unique_ptr<sc_signal<OpticalSignal>>> m_optical_connect; + + // Member submodules + vector<unique_ptr<Waveguide>> m_wg; + vector<unique_ptr<DirectionalCoupler>> m_dc; + unique_ptr<OctaneCell> m_oct; + + // init() should be called after declaration + void init(); + void init_compact(); + void init_full(); + void init_ports(); + void connect_submodules_full(); + void connect_submodules_compact(); + + /** Constructor for Octane Segment + * + * @param name name of the module + * @param meltEnergy energy needed to amorphize by one step + * @param nStates number of internal states supported by the PCM + * */ + OctaneSegment(sc_module_name name, bool term_row, bool term_col, + double coupling_through_row, double coupling_through_col, + double meltEnergy, int nStates, bool isCompact = false) + : sc_module(name) + , m_term_row(term_row) + , m_term_col(term_col) + , m_isCompact(isCompact) + , m_coupling_through_row(coupling_through_row) + , m_coupling_through_col(coupling_through_col) + , m_meltEnergy(meltEnergy) + , m_nStates(nStates) + { + init_ports(); // At least the ports should always be + // in the constructor + } + +}; diff --git a/src/devices/pcm_device.cpp b/src/devices/pcm_device.cpp new file mode 100644 index 0000000..dc018da --- /dev/null +++ b/src/devices/pcm_device.cpp @@ -0,0 +1,150 @@ +#include <algorithm> +#include <iostream> + +#include <pcm_device.h> + +#define ZERO_PULSE_THRESHOLD (1e-10) + +using namespace std; + +void PCMElement::on_input_changed() +{ + // setting the transmission for the initial state + update_transmission_local(); + + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "Emelt = " << m_meltEnergy*1e9 << " nJ" << endl; + cout << "Nstates = " << m_nStates << "" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name(); + cout << endl; + cout << endl; + } + + m_last_pulse_power = 0; + bool in_window = false; + vector<pulse_sample_t> vector_of_samples; + uint32_t cur_wavelength_id; + m_memory_in[0] = 0; + double total_in_power = 0; + + while (true) { + // Wait next input change + wait(); + + // Read signal and store field in memory for that wavelength + auto s = p_in->read(); + cur_wavelength_id = s.m_wavelength_id; + m_memory_in[cur_wavelength_id] = s.m_field; + + // Summing powers of all wavelengths + total_in_power = 0; + for (auto lambdaID_field : m_memory_in) + { + total_in_power += norm(lambdaID_field.second); + } + + // Storage of optical state on the input + const sc_time &now = sc_time_stamp(); + + bool last_was_zero = m_last_pulse_power < ZERO_PULSE_THRESHOLD; + bool current_is_zero = total_in_power < ZERO_PULSE_THRESHOLD; + bool rising_edge = last_was_zero && !current_is_zero; + if (!rising_edge) + { + // Enter here when: + // - a signal is received on top of another one (pulse intersect) + // - a signal ends (power falls to 0) + + // Record the event + vector_of_samples.emplace_back(now.to_seconds(), total_in_power); + } + else + { + // Enter here when: + // - a signal starts (power rises from 0 to non-0 value) + // This case is necessarily the first rise + + // Will unroll events and check if there was a phase change before according to + // the vector and advance the state accordingly to get the transmission + // cout << "\t\t first rise detected" << endl; + in_window = phase_change(vector_of_samples, true); + if (!in_window) + vector_of_samples.clear(); + + // Record the current event + vector_of_samples.emplace_back(now.to_seconds(), total_in_power); + } + + m_last_pulse_power = total_in_power; + + // Write attenuated signal to output + s *= m_transmission; + s.getNewId(); + + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + } +} + +bool PCMElement::phase_change(const vector<pulse_sample_t> &samples, const bool &local) +{ + // Here should decide between sending the + // sim off to external or resolving inside + + // Should set new values for m_state and m_transmission + + double energy_absorbed = 0; + double dur_pulse = 0; + // will return true if we shouldn't erase the vector + bool in_influence_window = false; + + if (samples.empty()) + return true; + + if (sc_time_stamp().to_seconds() - samples.back().first < influence_time_ns*1e-9) + return true; + + if (local) + { + // Integrating power along each duration + // pulse 0 stays at pow[0] for tim[1]-tim[0] seconds + // the last sample is zero in power, just here to get the final time + for (unsigned int i = 0; i < samples.size()-1; i++) + { + dur_pulse = samples[i+1].first - samples[i].first; + energy_absorbed += samples[i].second * dur_pulse; + } + // if enough energy and not saturated + if ((energy_absorbed > m_meltEnergy) && (m_state < m_nStates)) + { + // cout << sc_time_stamp() << ": \n\t" << name() << " phase changed with " + // << energy_absorbed*1e6 << " uJ" << endl; + m_state++; + // cout << "\tnew state: " << m_state << endl; + update_transmission_local(); + } + } + else // external phase change simulation + { + // send the simulation to external software + cout << "TODO: implement handles to external sim" + << endl; + } + return in_influence_window; +} + +void PCMElement::update_transmission_local() +{ + double sp = 0.85; + double ep = 0.95; + double speed = 3; + m_transmission = sp+(ep-sp)*tanh(speed*m_state/m_nStates); + // cout << "\tnew trans_power: " << m_transmission << "- W/W" << endl; + + // For field need to do a square root + m_transmission = sqrt(m_transmission); +} diff --git a/src/devices/pcm_device.h b/src/devices/pcm_device.h new file mode 100644 index 0000000..4510591 --- /dev/null +++ b/src/devices/pcm_device.h @@ -0,0 +1,60 @@ +#pragma once + +#include <vector> + +#include <systemc.h> + +#include <spx_module.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +#include "specs.h" +#include "spx_module.h" + +class PCMElement : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in; + spx::oa_port_out_type p_out; + + // Timed ports writers + OpticalOutputPort m_out_writer; + + // Auxiliary struct - stores tuples of value-time + typedef pair<double,double> pulse_sample_t; + + // Member variables + double m_meltEnergy; + int m_state; + int m_nStates; + double m_transmission = 1; + double influence_time_ns = 1; + + /** Current information about the optical input, before any attenuation and phase shift. */ + double m_last_pulse_power; + std::map<uint32_t,OpticalSignal::field_type> m_memory_in; + + + // Processes + void on_input_changed(); + bool phase_change(const vector<pulse_sample_t> &vec, const bool &local); + void update_transmission_local(); + + + // Constructor + PCMElement(sc_module_name name, + double meltEnergy = 0, + int nStates = 0, + int state = 0) + : spx_module(name) + , m_out_writer("out_delayed_writer", p_out) + , m_meltEnergy(meltEnergy) + , m_state(state) + , m_nStates(nStates) + { + SC_HAS_PROCESS(PCMElement); + + SC_THREAD(on_input_changed); + sensitive << p_in; + } +}; diff --git a/src/devices/phaseshifter.cpp b/src/devices/phaseshifter.cpp new file mode 100644 index 0000000..620243d --- /dev/null +++ b/src/devices/phaseshifter.cpp @@ -0,0 +1,198 @@ +#include "specs.h" +#include <phaseshifter.h> + +using namespace std; + +void PhaseShifterUni::on_port_in_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + m_memory_in[0] = 0; // initializing for nan wavelength + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission_power = " << pow(transmission_field,2) << " W/W" << endl; + cout << "sensitivity = " << m_sensitivity << " rad/V" << endl; + cout << "initial phase delay = " << m_phaseshift_rad << " rad @1.55)" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name(); + cout << endl << "\t^" << endl; + cout << "\t|" << endl << "\t"; + cout << (dynamic_cast<spx::ea_signal_type *>(p_vin.get_interface()))->name(); + cout << endl; + cout << endl; + } + while (true) { + // Wait for a new input signal + wait(); + + // Read the input signal and apply attenuation + OpticalSignal s = p_in->read(); + + + // Store field value in memory + m_memory_in[s.m_wavelength_id] = s.m_field; + + // Get new ID for output event + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_sensitivity * p_vin->read()); + + // Write to ouput port after zero delay + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + } +} + +void PhaseShifterUni::on_port_vin_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + + uint32_t cur_wavelength_id = 0; + OpticalSignal::field_type cur_field = (complex<double>)0; + + while (true) + { + wait(); + // Read the new phase-delay + m_phaseshift_rad = m_sensitivity * p_vin->read(); + + // writes the signals of all wavelengths with the new phase shift + for(auto id_field : m_memory_in) + { + cur_wavelength_id = id_field.first; + cur_field = id_field.second; + auto s = OpticalSignal(cur_field, cur_wavelength_id); + + // Get a new ID for the signal + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_phaseshift_rad); + + // Write to ouput port after zero delay + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + } + } +} + + +void PhaseShifterBi::on_p0_in_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + m_memory_p0[0] = 0; // initializing for nan wavelength + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission_power = " << pow(transmission_field,2) << " W/W" << endl; + cout << "sensitivity = " << m_sensitivity << " rad/V" << endl; + cout << "initial phase delay = " << m_phaseshift_rad << " rad @1.55)" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_out.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_out.get_interface()))->name(); + cout << " <-- "; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_in.get_interface()))->name(); + cout << endl << "\t^" << endl; + cout << "\t|" << endl << "\t"; + cout << (dynamic_cast<spx::ea_signal_type *>(p_vin.get_interface()))->name(); + cout << endl; + cout << endl; + } + while (true) { + // Wait for a new input signal + wait(); + + // Read the input signal and apply attenuation + OpticalSignal s = p0_in->read(); + + + // Store field value in memory + m_memory_p0[s.m_wavelength_id] = s.m_field; + + // Get new ID for output event + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_sensitivity * p_vin->read()); + + // Write to ouput port after zero delay + m_p1_writer.delayedWrite(s, SC_ZERO_TIME); + } +} + +void PhaseShifterBi::on_p1_in_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + m_memory_p1[0] = 0; // initializing for nan wavelength + + while (true) { + // Wait for a new input signal + wait(); + + // Read the input signal and apply attenuation + OpticalSignal s = p1_in->read(); + + // Store field value in memory + m_memory_p1[s.m_wavelength_id] = s.m_field; + + // Get new ID for output event + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_sensitivity * p_vin->read()); + + // Write to ouput port after zero delay + m_p0_writer.delayedWrite(s, SC_ZERO_TIME); + } +} + +void PhaseShifterBi::on_port_vin_changed() +{ + auto transmission_field = pow(10.0, - m_attenuation_dB / 20); + + uint32_t cur_wavelength_id = 0; + OpticalSignal::field_type cur_field = (complex<double>)0; + + while (true) + { + wait(); + // Read the new phase-delay + m_phaseshift_rad = m_sensitivity * p_vin->read(); + + // writes the signals of all wavelengths with the new phase shift + for(auto id_field : m_memory_p0) + { + cur_wavelength_id = id_field.first; + cur_field = id_field.second; + auto s = OpticalSignal(cur_field, cur_wavelength_id); + + // Get a new ID for the signal + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_phaseshift_rad); + + // Write to ouput port after zero delay + m_p1_writer.delayedWrite(s, SC_ZERO_TIME); + } + for(auto id_field : m_memory_p1) + { + cur_wavelength_id = id_field.first; + cur_field = id_field.second; + auto s = OpticalSignal(cur_field, cur_wavelength_id); + + // Get a new ID for the signal + s.getNewId(); + + // Apply transmission and phase-shift + s *= polar(transmission_field, m_phaseshift_rad); + + // Write to ouput port after zero delay + m_p0_writer.delayedWrite(s, SC_ZERO_TIME); + } + } +} diff --git a/src/devices/phaseshifter.h b/src/devices/phaseshifter.h new file mode 100644 index 0000000..04ac803 --- /dev/null +++ b/src/devices/phaseshifter.h @@ -0,0 +1,166 @@ +#pragma once + +#include "specs.h" +#include <systemc.h> + +#include <spx_module.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +/** An electrically-controllable phase shifter. */ +class PhaseShifterBase : public spx_module { +public: + // Member variables + /** The phaseshift applied to the pulse, in rad. */ + double m_phaseshift_rad = 0; + + /** The attenuation of the device in dB. */ + double m_attenuation_dB; + + /** The responsivity of the phase-shifter in rad/V. */ + double m_sensitivity = 1; + + /** Constructor for PhaseShifter + * + * @param name name of the module + * */ + PhaseShifterBase(sc_module_name name, double attenuation_dB = 0) + : spx_module(name) + , m_phaseshift_rad(0) + , m_attenuation_dB(attenuation_dB) + {} +}; + +class PhaseShifterUni : public PhaseShifterBase { +public: + // Ports + /** The electrical input port. */ + spx::ea_port_in_type p_vin; + /** The optical input port. */ + spx::oa_port_in_type p_in; + /** The optical output port. */ + spx::oa_port_out_type p_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out_writer; + + /** Memory of the current input value for all wavelengths. */ + std::map<uint32_t,OpticalSignal::field_type> m_memory_in; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * Will use the current m_phaseshift_rad at that moment. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p_in + * */ + void on_port_in_changed(); + + /** Auxiliary process of the module. + * + * Updates @param m_phaseshift_rad and creates a new event + * using the current optical input, sending it to the output + * after zero time. + * + * **SystemC type:** thread + * + * **Sensitivity list:** v_in + * */ + void on_port_vin_changed(); + + /** Constructor for PhaseShifter + * + * @param name name of the module + * */ + PhaseShifterUni(sc_module_name name, double attenuation_dB = 0) + : PhaseShifterBase(name, attenuation_dB) + , m_out_writer("out_delayed_writer", p_out) + { + SC_HAS_PROCESS(PhaseShifterUni); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + SC_THREAD(on_port_vin_changed); + sensitive << p_vin; + } +}; + +typedef PhaseShifterUni PhaseShifter; + +class PhaseShifterBi : public PhaseShifterBase { +public: + // Ports + /** The electrical input port. */ + spx::ea_port_in_type p_vin; + + /** The optical input ports. */ + spx::oa_port_in_type p0_in; + spx::oa_port_in_type p1_in; + + /** The optical output ports. */ + spx::oa_port_out_type p0_out; + spx::oa_port_out_type p1_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_p0_writer; + OpticalOutputPort m_p1_writer; + + /** Memory of the current input value for all wavelengths. */ + std::map<uint32_t,OpticalSignal::field_type> m_memory_p0; + std::map<uint32_t,OpticalSignal::field_type> m_memory_p1; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * Will use the current m_phaseshift_rad at that moment. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p0_in + * */ + void on_p0_in_changed(); + void on_p1_in_changed(); + + /** Auxiliary process of the module. + * + * Updates @param m_phaseshift_rad and creates a new event + * using the current optical input, sending it to the output + * after zero time. + * + * **SystemC type:** thread + * + * **Sensitivity list:** v_in + * */ + void on_port_vin_changed(); + + /** Constructor for PhaseShifter + * + * @param name name of the module + * */ + PhaseShifterBi(sc_module_name name, double attenuation_dB = 0) + : PhaseShifterBase(name, attenuation_dB) + , m_p0_writer("p0_out_delayed_writer", p0_out) + , m_p1_writer("p1_out_delayed_writer", p1_out) + { + SC_HAS_PROCESS(PhaseShifterBi); + + SC_THREAD(on_p0_in_changed); + sensitive << p0_in; + SC_THREAD(on_p1_in_changed); + sensitive << p1_in; + SC_THREAD(on_port_vin_changed); + sensitive << p_vin; + } +}; diff --git a/src/devices/probe.cpp b/src/devices/probe.cpp new file mode 100644 index 0000000..f9054e9 --- /dev/null +++ b/src/devices/probe.cpp @@ -0,0 +1,164 @@ +#include <ios> +#include <probe.h> +#include <cstdlib> // system() + +#define __modname(SUFFIX, IDX) \ + ((""s + this->name() + "_" + SUFFIX + to_string(IDX)).c_str()) + + +void Probe::on_port_in_changed() +{ + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "Signal: " << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name() << endl; + cout << "trace power: " << m_trace_power << endl; + cout << "trace modulus: " << m_trace_modulus << endl; + cout << "trace phase: " << m_trace_phase << endl; + cout << "trace wavelength: " << m_trace_wavelength << endl; + cout << endl; + } + + while (true) { + // Wait for a new input signal + wait(); + + // Check if enabled first + if (!enable.read().to_bool()) + { + wait(enable.posedge_event()); + continue; + } + + auto &s = p_in->read(); + // cout << name() << ": " << s << endl; + if (!isnan(s.getWavelength())) + { + if (m_trace_power) + m_trace_sig_power.write(s.power()); + if (m_trace_modulus) + m_trace_sig_modulus.write(s.modulus()); + if (m_trace_phase) + m_trace_sig_phase.write(s.phase()); + if (m_trace_wavelength) + // m_trace_sig_wavelength.write(299792458.0 / s.m_wavelength); + m_trace_sig_wavelength.write(s.getWavelength()); + } + } +} + +void PowerProbe::on_port_in_changed() +{ + //m_trace_sig.write(0); + + while (true) { + // Wait for a new input signal + wait(); + auto &s = p_in->read(); + if (!isnan(s.getWavelength())) + m_trace_sig_power.write(s.power()); + } +} + +void PhaseProbe::on_port_in_changed() +{ + //m_trace_sig.write(0); + + while (true) { + // Wait for a new input signal + wait(); + auto &s = p_in->read(); + if (!isnan(s.getWavelength())) + m_trace_sig_phase.write(s.phase()); + } +} + +//__modname(SUFFIX, IDX) +void MLambdaProbe::setTraceFile(sc_trace_file *Tf) +{ + m_Tf = Tf; + if (!m_Tf) + return; + + unique_ptr<sc_signal<double>> temp_signal; + std::vector<unique_ptr<sc_signal<double>>> temp_vector; + + // for each wavelength, create three signals: + // power, abs, phase, that will monitor these characteristics in + // the input optical signals that arrive + int i = 0; + for(const auto &wl : m_lambdas) + { + stringstream ss; + // int wl_nm = floor(wl*1e9); + // int wl_sub_nm = round((wl*1e9 - wl_nm) * 1e6); + // ss << wl_nm << "nm" << wl_sub_nm; + ss << setprecision(6) << std::fixed << wl*1e9; + string wl_str = ss.str(); + // Necessary because '.' is interpreted by traces as the hierarchical separator + std::replace(wl_str.begin(), wl_str.end(), '.', '_'); + + temp_vector.clear(); + + temp_signal = make_unique<sc_signal<double>>(__modname("trace_pow", i*3)); + sc_trace(m_Tf, *temp_signal, (string(this->name()) + ".power@" + wl_str).c_str()); + temp_vector.push_back(std::move(temp_signal)); + + temp_signal = make_unique<sc_signal<double>>(__modname("trace_mod", i*3)); + sc_trace(m_Tf, *temp_signal, (string(this->name()) + ".abs@" + wl_str).c_str()); + temp_vector.push_back(std::move(temp_signal)); + + temp_signal = make_unique<sc_signal<double>>(__modname("trace_phase", i*3)); + sc_trace(m_Tf, *temp_signal, (string(this->name()) + ".phase@" + wl_str).c_str()); + temp_vector.push_back(std::move(temp_signal)); + + //m_lambda_signals[m_lambdas[i]] = std::move(temp_vector); + //or + m_lambda_signals.insert({wl, std::move(temp_vector)}); + + ++i; + } +} +void MLambdaProbe::on_port_in_changed() +{ + // std::map< int, std::vector< unique_ptr<sc_signal<double> > > > wl_map; + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << " ready ("; + for (const auto &p : m_lambda_signals) + { + cout << p.first << ", "; + } + cout << "\b\b)" << endl; + cout << "Signal: " << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name() << endl; + cout << endl; + } + //m_trace_sig.write(0); + + while (true) { + // Wait for a new input signal + wait(); + + // Check if enabled first + if (!enable.read().to_bool()) + { + wait(enable.posedge_event()); + continue; + } + + auto &s = p_in->read(); + auto search = m_lambda_signals.find(s.getWavelength()); + + if (search != m_lambda_signals.end()) + { + //cout << name() << " received supported signal (lambda = " << s.getWavelength() << "m)" << endl; + m_lambda_signals[s.getWavelength()][0]->write(s.power()); + m_lambda_signals[s.getWavelength()][1]->write(s.modulus()); + m_lambda_signals[s.getWavelength()][2]->write(s.phase()); + } + else { + cout << name() << " received unsupported signal (lambda = " << s.getWavelength() << "m)" << endl; + } + } +}
\ No newline at end of file diff --git a/src/devices/probe.h b/src/devices/probe.h new file mode 100644 index 0000000..10865ba --- /dev/null +++ b/src/devices/probe.h @@ -0,0 +1,131 @@ +#pragma once + +#include <systemc.h> +#include <fstream> +#include <map> +#include <set> + +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +/* +This class defines an ideal optical probe. It will sample the signal at its input and +write it in a file. Any OpticalSignal can be its input, without caring for optical +splitting, as it is completely ideal. +*/ +class Probe : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in; + + // Processes + virtual void on_port_in_changed(); + + // Member variables + + // If given a valid trace file as argument + // Will not save to txt and rather use VCD tracing + sc_trace_file *m_Tf; + sc_signal<double> m_trace_sig_power; + sc_signal<double> m_trace_sig_modulus; + sc_signal<double> m_trace_sig_phase; + sc_signal<double> m_trace_sig_wavelength; + + sc_signal<sc_logic> enable; + bool m_trace_power; + bool m_trace_modulus; + bool m_trace_phase; + bool m_trace_wavelength; + + // Constructor + Probe(sc_module_name name, const bool &trace_power = true, const bool &trace_modulus = true + , const bool &trace_phase = true, const bool &trace_wavelength = true) + : spx_module(name) + , m_Tf(nullptr) + , enable((string(this->name()) + "_enable").c_str(), sc_logic(1)) + , m_trace_power(trace_power) + , m_trace_modulus(trace_modulus) + , m_trace_phase(trace_phase) + , m_trace_wavelength(trace_wavelength) + { + SC_HAS_PROCESS(Probe); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } + + // Constructor overloading + Probe(sc_module_name name, sc_trace_file *Tf, const bool &trace_power = true, const bool &trace_modulus = true + , const bool &trace_phase = true, const bool &trace_wavelength = true) + : Probe(name, trace_power, trace_modulus, trace_phase, trace_wavelength) + { + setTraceFile(Tf); + } + + void setTraceFile(sc_trace_file *Tf) + { + if (m_Tf == Tf) + return; + m_Tf = Tf; + if (m_Tf) + { + if (m_trace_power) sc_trace(m_Tf, m_trace_sig_power, (string(this->name()) + ".power").c_str()); + if (m_trace_modulus) sc_trace(m_Tf, m_trace_sig_modulus, (string(this->name()) + ".abs").c_str()); + if (m_trace_phase) sc_trace(m_Tf, m_trace_sig_phase, (string(this->name()) + ".phase").c_str()); + if (m_trace_wavelength) sc_trace(m_Tf, m_trace_sig_wavelength, (string(this->name()) + ".wavelength").c_str()); + } + } +}; + +class PowerProbe : public Probe { +public: + virtual void on_port_in_changed(); +}; + +class PhaseProbe : public Probe { +public: + virtual void on_port_in_changed(); +}; + +class MLambdaProbe : public spx_module { +public: + // Ports + spx::oa_port_in_type p_in; + + // Processes + virtual void on_port_in_changed(); + + // Member variables + + // If given a valid trace file as argument + // Will not save to txt and rather use VCD tracing + sc_signal<sc_logic> enable; + sc_trace_file *m_Tf; + + sc_signal<double> m_trace_sig_power; + sc_signal<double> m_trace_sig_modulus; + sc_signal<double> m_trace_sig_phase; + + std::set<double> m_lambdas; + std::map< double, std::vector< unique_ptr<sc_signal<double> > > > m_lambda_signals; + + void setTraceFile(sc_trace_file *Tf); + void prepare(); + + // Constructor + MLambdaProbe(sc_module_name name, set<double> lambdas = {}) + : spx_module(name) + , enable((string(this->name()) + "_enable").c_str(), sc_logic(1)) + , m_Tf(nullptr) + , m_lambdas(lambdas) + { + SC_HAS_PROCESS(MLambdaProbe); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } + + // TODO: overload the constructor for the case of not informing lambdas, need to check all components +}; diff --git a/src/devices/ring.cpp b/src/devices/ring.cpp new file mode 100644 index 0000000..42f901e --- /dev/null +++ b/src/devices/ring.cpp @@ -0,0 +1,8 @@ +#include <waveguide.h> + +//void Ring::on_port_tuning_changed() +//{ +// // Tuning of the ring parameters... +// // e.g. wg.neff = ... +//} + diff --git a/src/devices/ring.h b/src/devices/ring.h new file mode 100644 index 0000000..0633c13 --- /dev/null +++ b/src/devices/ring.h @@ -0,0 +1,99 @@ +#pragma once + +#include <systemc.h> + +#include <waveguide.h> +#include <directional_coupler.h> +#include <optical_output_port.h> +#include <optical_signal.h> + +/** A unidirectional waveguide module. */ +class Ring : public sc_module { +public: + // Ports + /** The optical input port. */ + sc_port<sc_signal_in_if<OpticalSignal>> p_in; + /** The optical output port. */ + sc_port<sc_signal_out_if<OpticalSignal>> p_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out_writer; + + sc_signal<OpticalSignal> sig_in; + sc_signal<OpticalSignal> sig_internal_0; + sc_signal<OpticalSignal> sig_internal_1; + sc_signal<OpticalSignal> sig_out; + DirectionalCoupler dc; + Waveguide wg; + + // Member variables + /** The attenuation inside the waveguide, in dB/cm. */ + double m_attenuation_dB_cm = 0.2; + /** The effective index of the waveguide */ + double m_neff = 2.2111; + /** The group index of the waveguide */ + double m_ng = 2.2637; + + /** The coupling from waveguide to ring, in dB. */ + double m_cross_power_dB = 10*std::log10(0.5) - 0.15; + double m_through_power_dB = 10*std::log10(0.5) - 0.15; + double m_cross_phase_rad = M_PI / 2; + double m_through_phase_rad = M_PI / 2; + + /** The radius of the ring in um. */ + double m_radius_um = 0; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p_in + * */ + void on_port_in_changed(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param length_cm length of the waveguide in cm + * */ + Ring(sc_module_name name, double length_cm = 30e-6) + : sc_module(name) + , m_out_writer("out_delayed_writer", p_out) + , dc((std::string(name) + ".DC1").c_str()) + , wg((std::string(name) + ".WG1").c_str()) + { + dc.p_in1(sig_in); + dc.p_in2(sig_internal_1); + dc.p_out1(sig_out); + dc.p_out2(sig_internal_0); + + wg.p_in(sig_internal_0); + wg.p_out(sig_internal_1); + + p_in(sig_in); + p_out(sig_out); + + setLength(length_cm); + + SC_HAS_PROCESS(Waveguide); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } + + void setRadius(double radius_um) { + wg.setLength(2 * M_PI * radius_um * 1e-4); + m_radius_um = radius_um; + } + + void setLength(double length_cm) { + setRadius(length_cm * 1e4 / 2 / M_PI); + } +}; diff --git a/src/devices/splitter.cpp b/src/devices/splitter.cpp new file mode 100644 index 0000000..e6464f6 --- /dev/null +++ b/src/devices/splitter.cpp @@ -0,0 +1,41 @@ +#include <splitter.h> +#include <specs.h> + +void Splitter::on_port_in_changed() +{ + const double transmission = pow(10.0, - m_attenuation_dB / 20); + const double S12 = transmission * sqrt(m_split_ratio); + const double S13 = transmission * sqrt(1 - m_split_ratio); + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "transmission = " << pow(transmission, 2) << " W/W" << endl; + cout << "splitting ratio_power = 0.5" << " W/W" << endl; + cout << "\t " << (dynamic_cast<spx::oa_signal_type *>(p_out1.get_interface()))->name() << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name() << " -->" << endl; + cout << "\t " << (dynamic_cast<spx::oa_signal_type *>(p_out2.get_interface()))->name(); + cout << endl; + cout << endl; + } + + while (true) { + // Wait for a new input signal + wait(); + + // Read input signal + const auto &s = p_in->read(); + + // Apply device's transmission + auto s1 = S12 * s; + auto s2 = S13 * s; + + // Get new IDs + s1.getNewId(); + s2.getNewId(); + + // Write to output ports after delay + m_out1_writer.delayedWrite(s1, SC_ZERO_TIME); + m_out2_writer.delayedWrite(s2, SC_ZERO_TIME); + } +} diff --git a/src/devices/splitter.h b/src/devices/splitter.h new file mode 100644 index 0000000..1d5e730 --- /dev/null +++ b/src/devices/splitter.h @@ -0,0 +1,88 @@ +#pragma once + +#include <systemc.h> + +#include <spx_module.h> +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +/** A 1x2 waveguide splitter module. + * + * This device copies any output to its two output, with power values defines by the + * split ratio and attenuation. + * */ +class Splitter : public spx_module { +public: + // Ports + /** The optical input port. */ + spx::oa_port_in_type p_in; + /** The first optical output port. */ + spx::oa_port_out_type p_out1; + /** The second optical output port. */ + spx::oa_port_out_type p_out2; + +private: + // Timed ports writers + /** Timed writer to the first output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out1_writer; + /** Timed writer to the second output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out2_writer; + +public: + // Member variables + /** The power transmission from input to branch 1, exclusive of losses + * + * The power transmission from input to branch 2 is (1 - m_split_ratio) + * */ + double m_split_ratio; + + /** The delay for an output to propagate to the outputs in ns. + * + * The delay is the same for both outputs. + * */ + // static double m_delay_ns; + /** The attenuation of the signal in dB. + * + * The attenuation is applied before the split (although it does not really make a + * difference. */ + double m_attenuation_dB; + + // Processes + /** Main process of the module. + * + * It copies the input to both output, after attenuation and delay. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p_in + * */ + void on_port_in_changed(); + + // Constructor + /** Constructor for Splitter + * + * @param name name of the module + * @param split_ratio ratio of the light which goes to the first ouptut + * (pre-attenuation) + * */ + Splitter(sc_module_name name, double split_ratio = 0.5, double attenuation_dB = 0) + : spx_module(name) + , m_out1_writer("out1_delayed_writer", p_out1) + , m_out2_writer("out2_delayed_writer", p_out2) + , m_split_ratio(split_ratio) + , m_attenuation_dB(attenuation_dB) + { + SC_HAS_PROCESS(Splitter); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } +}; diff --git a/src/devices/spx_module.h b/src/devices/spx_module.h new file mode 100644 index 0000000..0a4506f --- /dev/null +++ b/src/devices/spx_module.h @@ -0,0 +1,36 @@ +#pragma once + +#include "optical_signal.h" +#include "specs.h" + +#include <systemc.h> +#include <string> + +using std::string; +using namespace std::string_literals; + +class spx_module : public sc_module { +public: + typedef spx_module this_type; + typedef spx::oa_value_type sigval_type; + typedef spx::oa_signal_type sig_type; + typedef spx::oa_if_in_type if_in_type; + typedef spx::oa_if_out_type if_out_type; + typedef spx::oa_port_in_type port_in_type; + typedef spx::oa_port_out_type port_out_type; + + enum ModuleFlags { + NON_LINEAR = 0b0001, + TIME_VARIANT = 0b0010, + FREQUENCY_DEPENDENT = 0b0100, + }; + + ModuleFlags flags = FREQUENCY_DEPENDENT; + + virtual void init() {} + virtual string describe() const { return ""s; } + + spx_module(sc_module_name name) + : sc_module(name) + {} +};
\ No newline at end of file diff --git a/src/devices/subcircuit_instance.h b/src/devices/subcircuit_instance.h new file mode 100644 index 0000000..5c835e8 --- /dev/null +++ b/src/devices/subcircuit_instance.h @@ -0,0 +1,25 @@ +#pragma once + +#include <memory> +#include <vector> + +#include "specs.h" +#include "spx_module.h" + +using std::vector; +using std::string; +using std::shared_ptr; + +class SubcircuitInstance: public spx_module { +public: + // vector<shared_ptr<spx::oa_port_inout_type>> ports; + vector<shared_ptr<sc_object>> signals; + vector<shared_ptr<sc_object>> modules; + + SubcircuitInstance(sc_module_name name) + : spx_module(name) + {} + + ~SubcircuitInstance() + {} +};
\ No newline at end of file diff --git a/src/devices/time_monitor.cpp b/src/devices/time_monitor.cpp new file mode 100644 index 0000000..0c4ab91 --- /dev/null +++ b/src/devices/time_monitor.cpp @@ -0,0 +1,152 @@ + +#include "time_monitor.h" +#include "optical_output_port.h" +#include "specs.h" +#include "sysc_utils.h" +#include <vector> +#include <set> + +using namespace std; + +string vec2str(const vector<bool> &v) +{ + string str; + for (const auto &x : v) + if (x) + str += "1|"; + else + str += "0|"; + + return str; +} + +void TimeMonitor::on_trigger() { + auto first = system_clock::now(); + auto last = first; + + set<OpticalOutputPort *> oops = sc_get_all_module_by_type<OpticalOutputPort>(); + sc_time delay = sc_time(m_poll_period, SC_SEC); + + //wait(1, SC_PS); + while (true) { + // Wait for next polling event + if (!sc_pending_activity()) + { + // cout << "a" << endl; + wait(); + } + else if (sc_pending_activity_at_current_time() && delay.value() != 0) + { + // cout << "b" << endl; + wait(delay); + } + else if (sc_pending_activity_at_current_time() && delay.value() == 0) + { + //cout << "c" << endl; + wait(delay); + } + else if (sc_pending_activity_at_future_time() && delay.value() != 0) + { + // cout << "d" << endl; + wait(delay); + } + else if (sc_pending_activity_at_future_time() && delay.value() == 0) + { + // cout << "e" << endl; + wait(sc_time_to_pending_activity()); + } + else + cerr << "How did you get here ?" << endl; + + + // Get current wall clock + auto now = system_clock::now(); + std::chrono::duration<double> elapsed_seconds = now - last; + + // Report if it's time to do it + if (elapsed_seconds.count() >= m_wallclock_period) + { + #if 0 + double error_sum = 0; + double max_error = 0; + double emitted_power_sum = 0; + double max_emitted_power = 0; + string max_error_name; + complex<double> culprit_emitted; + double culprit_emitted_pow = 0.0; + complex<double> culprit_wanted; + vector<bool> is_empty(oops.size(), false); + int i = 0; + int max_i = 0; + for (const auto &oop : oops) + { + is_empty[i] = oop->isempty(); + error_sum += oop->current_err_fd(); + max_error = max(max_error, oop->current_err_fd()); + if (max_error == oop->current_err_fd()) + // if (strcmp(oop->name(), "crow.dc_0.out1_delayed_writer") == 0) + { + max_error_name = oop->name(); + // deprecated following lines on 05/04: + // culprit_emitted = oop->m_emitted_val_fd; + // culprit_emitted_pow = oop->m_emitted_val_fd.power(); + culprit_wanted = oop->m_cur_val_fd; + max_i = i; + } + // deprecated following lines on 05/04: + // emitted_power_sum += oop->m_emitted_val_fd.power(); + // max_emitted_power = max(max_emitted_power, oop->m_emitted_val_fd.power()); + i++; + } + #endif + + std::chrono::duration<double> elapsed_seconds_since_start = now - first; + last = now; + auto t = sc_time_stamp().to_seconds(); + cout << "---------------------------------------------------------------------------" << endl; + cerr << "Time monitor incompatible with new multi-wavelength features" << endl; + exit(1); + cout << "Current simulation time (after "<< (int)(elapsed_seconds_since_start.count()) <<"s): " << endl + << "\tIn NS: " << t*1e9 << endl + << "\tIn PS: " << t*1e12 << endl + << "\tDelta-cycles at current time: " << sc_delta_count_at_current_time() << endl + //<< "\tCumulated error in ports: " << error_sum << endl + //<< "\tIsempty: " << vec2str(is_empty) << endl + //<< "\tCulprit: " << max_error_name << endl + //<< "\tCulprit i: " << max_i << endl + //<< setprecision(12) + //<< "\tWanted: " << norm(culprit_wanted) << "W " << arg(culprit_wanted) << "rad" << endl + //<< "\tEmitted: " << norm(culprit_emitted) << "W " << arg(culprit_emitted) << "rad" << endl + //<< "\tDiff: " << abs(norm(culprit_emitted) - norm(culprit_wanted)) << "W " << abs(arg(culprit_emitted) - arg(culprit_wanted)) << "rad" << endl + //<< "\tMax error in ports: " << max_error << endl + //<< "\tCumulated power output of all ports: " << emitted_power_sum << endl + //<< "\tMax port power output: " << max_emitted_power << endl + << "\tSimulation speed (NS/s): " << t*1e9 / elapsed_seconds_since_start.count() << endl; + + // if (max_error < specsGlobalConfig.oop_configs[0]->m_abs_tol_power) + if (false) + { + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // DOES NOT WORK IF CODE IS COMPILED WITH -O3 ??? + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + specsGlobalConfig.drop_all_events = true; + wait(SC_ZERO_TIME); + while (true) { + volatile bool isempty = true; + for (const auto &oop : oops) { + isempty &= oop->isempty(); + if (oop->isempty()) + { + cout << oop->name() << " didnt empty its queue" << endl; + } + } + if (isempty) + break; + wait(SC_ZERO_TIME); + } + specsGlobalConfig.drop_all_events = false; + wait(sc_time::from_value(1)); + } + } + } +} diff --git a/src/devices/time_monitor.h b/src/devices/time_monitor.h new file mode 100644 index 0000000..62d5626 --- /dev/null +++ b/src/devices/time_monitor.h @@ -0,0 +1,26 @@ +#pragma once + +#include <systemc.h> +#include <chrono> +#include <iostream> + +using std::chrono::system_clock; +using std::shared_ptr; +using std::make_shared; + +class TimeMonitor : public sc_module { +public: + double m_poll_period; // in seconds + double m_wallclock_period; // in seconds + + void on_trigger(); + + TimeMonitor(sc_module_name name, double poll_period=100e-12, double wallclock_period = 2) + : sc_module(name), + m_poll_period(poll_period), + m_wallclock_period(wallclock_period) + { + SC_HAS_PROCESS(TimeMonitor); + SC_THREAD(on_trigger); + } +}; diff --git a/src/devices/value_list_source.cpp b/src/devices/value_list_source.cpp new file mode 100644 index 0000000..a10c1a1 --- /dev/null +++ b/src/devices/value_list_source.cpp @@ -0,0 +1,69 @@ +#include "specs.h" +#include "value_list_source.h" + +using std::cout; +using std::cerr; +using std::endl; + +void VLSource::runner() +{ + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "value list (" << m_values_queue.size() << " values)"; + if (false) + { + cout << ":" << endl; + for (const auto& val : m_values_queue) + cout << "\t" << "@" << sc_time(val.first, SC_SEC) << ": " << val.second << endl; + } + cout << endl; + cout << "--> " << + (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name() << endl; + cout << endl; + } + + // Wait for enable signal + if (! enable.read().to_bool()) + { + // cout << name() << " waiting for enable" << endl; + wait(enable.posedge_event()); + cout << name() << " was enabled" << endl; + } + + // Emitting 0 at enable (will only go through if there is no value at t=0) + if (m_values_queue.cbegin() == m_values_queue.cend() + || sc_time(m_values_queue.front().first, SC_SEC).value() > 0) + { + cout << "@" << sc_time_stamp() << ", " << name() << " emitted: " << spx::oa_value_type(0) << endl; + m_out_writer.delayedWrite(spx::oa_value_type(0), SC_ZERO_TIME); + } + + auto it = m_values_queue.cbegin(); + while (it != m_values_queue.cend()) + { + sc_time now = sc_time_stamp(); + sc_time next_emit; + if (sc_time(it->first, SC_SEC) < now) + { + cout << name() << ": invalid time for signal emission !" << endl; + ++it; + continue; + } + sc_time delay = sc_time(it->first, SC_SEC) - now; + + // Wait until next output time + wait(delay); + + auto s = it->second; + + // Write value to output + m_out_writer.delayedWrite(s, SC_ZERO_TIME); + cout << "@" << sc_time_stamp() << ", " << name() << " emitted: " << s << endl; + + ++it; + } + + // cout << name() << " completed" << endl; + while(true) { wait(); } +} diff --git a/src/devices/value_list_source.h b/src/devices/value_list_source.h new file mode 100644 index 0000000..a7ddb6e --- /dev/null +++ b/src/devices/value_list_source.h @@ -0,0 +1,140 @@ +#pragma once + +#include <systemc.h> +#include <fstream> +#include <algorithm> +#include <vector> +#include <utility> + +#include "strutils.h" +#include "optical_output_port.h" +#include "optical_signal.h" +#include "specs.h" +#include "spx_module.h" + +using std::vector; +using std::pair; + +class VLSource : public spx_module { +public: + typedef pair<double, spx::oa_value_type> time_value_pair_type; + // Ports + spx::oa_port_out_type p_out; + + // Timed ports writers + OpticalOutputPort m_out_writer; + + // Signal to emit + vector<time_value_pair_type> m_values_queue; + + // Source emission control + spx::ed_signal_type enable; + + // Processes + void runner(); + + VLSource(sc_module_name name, const vector<time_value_pair_type> &values = {}) + : spx_module(name) + , m_out_writer((string(this->name()) + "_out_writer").c_str(), p_out) + , m_values_queue(values) + { + sortValues(); + enable = sc_logic(0); + + SC_HAS_PROCESS(VLSource); + SC_THREAD(runner); + } + + void setValues(const vector<time_value_pair_type> &values) + { + m_values_queue = values; + sortValues(); + } + + void setValues(const string &filename) + { + std::ifstream file(filename); + + if (!file.is_open()) { + cerr << "Error: Cannot open the file." << endl; + exit(1); + } + + string line; + + getline(file, line); + + int lineNumber = 0; + while (getline(file, line)) { + ++lineNumber; + istringstream s(line); + string field; + + double t, P, WL; + + // Skip if line is empty or starts with ; + if (line.empty()) + { + cout << "Skipping empty line." << endl; + continue; + } + // Skip if line starts with ; + if (line[0] == ';') + { + cout << "Skipping commented line: " << line << endl; + continue; + } + + // Read first field (time) + getline(s, field, ','); + strutils::trim(field); + + // Skip header line if it exists + if(lineNumber == 1 && field == "time") + { + cout << "Skipping header line: " << line << endl; + continue; + } + + try { + t = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for time: \"" << field << "\"" << endl; + exit(1); + } + + // Read second field (P) + getline(s, field, ','); + try { + P = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for power: \"" << field << "\"" << endl; + exit(1); + } + + // Read wavelength (WL) + std::getline(s, field); + try { + WL = stod(field); + } catch (std::invalid_argument const& ex) { + cerr << "Invalid value for wavelength: \"" << field << "\"" << endl; + exit(1); + } + + cout << t << ": " << P << "W @" << WL*1e9 << "nm" << endl; + m_values_queue.emplace_back(t, OpticalSignal(sqrt(P), WL)); + } + + file.close(); + sortValues(); + } + + void sortValues() + { + auto cmp = [](const time_value_pair_type& p1, const time_value_pair_type& p2) { + return p1.first < p2.first; + }; + // Stable sort will keep initial ordering for values with identical time + std::stable_sort(m_values_queue.begin(), m_values_queue.end(), cmp); + } +}; diff --git a/src/devices/waveguide.cpp b/src/devices/waveguide.cpp new file mode 100644 index 0000000..dc20ec7 --- /dev/null +++ b/src/devices/waveguide.cpp @@ -0,0 +1,280 @@ +#include "specs.h" +#include <waveguide.h> + +using namespace std; + +void WaveguideUni::on_port_in_changed() +{ + const double c = 299792458.0; + + // attenuation in dB + m_attenuation_dB = m_attenuation_dB_cm * m_length_cm; + + // transmission in field: 10^(-dB/20) + const double transmission = pow(10.0, - m_attenuation_dB / 20.0); + + // vg = c/ng + // => delay = L / vg = (L * ng) / c + const double group_delay_ns = 1e9 * m_length_cm * 1e-2 / (c / m_ng); + + // precalculate 2 * pi * L + double phase_delay_factor = 2.0 * M_PI * m_length_cm * 1e-2; + + // ng = neff - lambda * dneff/dlambda + // => dneff/dlambda = (neff - ng)/lambda + double dneff_dlambda = (m_neff - m_ng) / 1.55e-6; + + // Parameters relative to dispersion + double d2neff_dlambda2_over_2 = -1 * c * m_D / (2 * 1.55e-6); + double dng_dlambda = c*m_D; + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "length = " << m_length_cm/100 << " m" << endl; + cout << "neff = " << m_neff << "" << endl; + cout << "ng = " << m_ng << "" << endl; + cout << "transmission_power = " << pow(transmission, 2) << " W/W" << endl; + cout << "group delay = " << group_delay_ns << " ns" << endl; + // cout << "dneff/dlambda = " << dneff_dlambda*1e-6 << " um^-1" << endl; + cout << "phase delay = " << 1e9 * m_length_cm * 1e-2 / (c / m_neff) << " ns"; + cout << " (" + << fmod((2 * M_PI * m_neff / 1.55e-6) * m_length_cm * 1e-2, 2 * M_PI) + << "rad @1.55)" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p_out.get_interface()))->name(); + cout << endl; + cout << endl; + } + + if(m_D == 0) + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay = phase_delay_factor * neff / s.getWavelength(); + + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_out_writer.delayedWrite(s, sc_time(group_delay_ns, SC_NS)); + } + } + else // dispersion has a defined value + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6) + + d2neff_dlambda2_over_2 * pow(s.getWavelength() - 1.55e-6, 2); + const double ng = m_ng + dng_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay_disp = phase_delay_factor * neff / s.getWavelength(); + const double group_delay_ns_disp = 1e9 * m_length_cm * 1e-2 / (c / ng); + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay_disp); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_out_writer.delayedWrite(s, sc_time(group_delay_ns_disp, SC_NS)); + } + } +} + + +void WaveguideBi::on_p0_in_changed() +{ + const double c = 299792458.0; + + // attenuation in dB + m_attenuation_dB = m_attenuation_dB_cm * m_length_cm; + + // transmission in field: 10^(-dB/20) + const double transmission = pow(10.0, - m_attenuation_dB / 20.0); + + // vg = c/ng + // => delay = L / vg = (L * ng) / c + const double group_delay_ns = 1e9 * m_length_cm * 1e-2 / (c / m_ng); + + // precalculate 2 * pi * L + double phase_delay_factor = 2.0 * M_PI * m_length_cm * 1e-2; + + // ng = neff - lambda * dneff/dlambda + // => dneff/dlambda = (neff - ng)/lambda + double dneff_dlambda = (m_neff - m_ng) / 1.55e-6; + + // Parameters relative to dispersion + double d2neff_dlambda2_over_2 = -1 * c * m_D / (2 * 1.55e-6); + double dng_dlambda = c*m_D; + + if (specsGlobalConfig.verbose_component_initialization) + { + cout << name() << ":" << endl; + cout << "length = " << m_length_cm/100 << " m" << endl; + cout << "neff = " << m_neff << "" << endl; + cout << "ng = " << m_ng << "" << endl; + cout << "transmission_power = " << pow(transmission, 2) << " W/W" << endl; + cout << "group delay = " << group_delay_ns << " ns" << endl; + // cout << "dneff/dlambda = " << dneff_dlambda*1e-6 << " um^-1" << endl; + cout << "phase delay = " << 1e9 * m_length_cm * 1e-2 / (c / m_neff) << " ns"; + cout << " (" + << fmod((2 * M_PI * m_neff / 1.55e-6) * m_length_cm * 1e-2, 2 * M_PI) + << "rad @1.55)" << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_in.get_interface()))->name(); + cout << " --> "; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_out.get_interface()))->name(); + cout << endl; + cout << (dynamic_cast<spx::oa_signal_type *>(p0_out.get_interface()))->name(); + cout << " <-- "; + cout << (dynamic_cast<spx::oa_signal_type *>(p1_in.get_interface()))->name(); + cout << endl; + cout << endl; + } + + if(m_D == 0) + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p0_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay = phase_delay_factor * neff / s.getWavelength(); + + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_p1_out_writer.delayedWrite(s, sc_time(group_delay_ns, SC_NS)); + } + } + else // dispersion has a defined value + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p0_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6) + + d2neff_dlambda2_over_2 * pow(s.getWavelength() - 1.55e-6, 2); + const double ng = m_ng + dng_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay_disp = phase_delay_factor * neff / s.getWavelength(); + const double group_delay_ns_disp = 1e9 * m_length_cm * 1e-2 / (c / ng); + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay_disp); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_p1_out_writer.delayedWrite(s, sc_time(group_delay_ns_disp, SC_NS)); + } + } +} + +void WaveguideBi::on_p1_in_changed() +{ + const double c = 299792458.0; + + // attenuation in dB + m_attenuation_dB = m_attenuation_dB_cm * m_length_cm; + + // transmission in field: 10^(-dB/20) + const double transmission = pow(10.0, - m_attenuation_dB / 20.0); + + // vg = c/ng + // => delay = L / vg = (L * ng) / c + const double group_delay_ns = 1e9 * m_length_cm * 1e-2 / (c / m_ng); + + // precalculate 2 * pi * L + double phase_delay_factor = 2.0 * M_PI * m_length_cm * 1e-2; + + // ng = neff - lambda * dneff/dlambda + // => dneff/dlambda = (neff - ng)/lambda + double dneff_dlambda = (m_neff - m_ng) / 1.55e-6; + + // Parameters relative to dispersion + double d2neff_dlambda2_over_2 = -1 * c * m_D / (2 * 1.55e-6); + double dng_dlambda = c*m_D; + + if(m_D == 0) + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p1_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay = phase_delay_factor * neff / s.getWavelength(); + + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_p0_out_writer.delayedWrite(s, sc_time(group_delay_ns, SC_NS)); + } + } + else // dispersion has a defined value + { + while (true) { + // Wait for a new input signal + wait(); + // Read the input signal + auto s = p1_in->read(); + + // calculate phase-delay + const double neff = m_neff + dneff_dlambda * (s.getWavelength() - 1.55e-6) + + d2neff_dlambda2_over_2 * pow(s.getWavelength() - 1.55e-6, 2); + const double ng = m_ng + dng_dlambda * (s.getWavelength() - 1.55e-6); + + const double phase_delay_disp = phase_delay_factor * neff / s.getWavelength(); + const double group_delay_ns_disp = 1e9 * m_length_cm * 1e-2 / (c / ng); + // Apply transmission function + const OpticalSignal::field_type S12 = polar(transmission, phase_delay_disp); + s *= S12; + + // Get new ID for output event + s.getNewId(); + + // Write to ouput port after group delay + m_p0_out_writer.delayedWrite(s, sc_time(group_delay_ns_disp, SC_NS)); + } + } +}
\ No newline at end of file diff --git a/src/devices/waveguide.h b/src/devices/waveguide.h new file mode 100644 index 0000000..c686bba --- /dev/null +++ b/src/devices/waveguide.h @@ -0,0 +1,154 @@ +#pragma once + +#include <systemc.h> + +#include <optical_output_port.h> +#include <optical_signal.h> +#include "specs.h" +#include "spx_module.h" + +/** The base class for the waveguide module */ +class WaveguideBase : public spx_module { +public: + // Member variables + + /** The length of the waveguide in cm. */ + double m_length_cm; + /** The attenuation of the waveguide in dB. */ + double m_attenuation_dB; + /** The attenuation inside the waveguide, in dB/cm */ + double m_attenuation_dB_cm; + + /** The effective index of the waveguide */ + double m_neff; + /** The group index of the waveguide */ + double m_ng; + /** The group velocity dispersion Dlambda of the waveguide in [s/m/m] */ + double m_D; + + /** Constructor for WaveguideBase + * + * @param name name of the module + * @param length_cm length of the waveguide in cm + * */ + WaveguideBase(sc_module_name name, double length_cm = 1e-2, + double attenuation_dB_cm = 0, double neff = 2.2111, + double ng = 2.2637, double D = 0) + : spx_module(name) + , m_length_cm(length_cm) + , m_attenuation_dB_cm(attenuation_dB_cm) + , m_neff(neff) + , m_ng(ng) + , m_D(D) + { + // nothing to do + } + + inline void setLength(double length_cm) { + if (length_cm < 0) { + std::cerr << "Error: waveguide length < 0" << std::endl; + } + m_length_cm = length_cm; + } +}; + + +/** A unidirectional waveguide module */ +class WaveguideUni : public WaveguideBase { +public: + // Ports + /** The optical input port. */ + spx::oa_port_in_type p_in; + /** The optical output port. */ + spx::oa_port_out_type p_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_out_writer; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p_in + * */ + void on_port_in_changed(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param length_cm length of the waveguide in cm + * */ + WaveguideUni(sc_module_name name, double length_cm = 1e-2, + double attenuation_dB_cm = 0, double neff = 2.2111, + double ng = 2.2637, double D = 0) + : WaveguideBase(name, length_cm, attenuation_dB_cm, neff, ng, D) + , m_out_writer("out_delayed_writer", p_out) + { + SC_HAS_PROCESS(WaveguideUni); + + SC_THREAD(on_port_in_changed); + sensitive << p_in; + } +}; + +typedef WaveguideUni Waveguide; + +/** A bidirectional waveguide module */ +class WaveguideBi : public WaveguideBase { +public: + // Ports + /** The optical input ports. */ + spx::oa_port_in_type p0_in; + spx::oa_port_in_type p1_in; + + /** The optical output ports. */ + spx::oa_port_out_type p0_out; + spx::oa_port_out_type p1_out; + + // Timed ports writers + /** Timed writer to the output port. + * + * @sa DelayedWriter + * */ + OpticalOutputPort m_p0_out_writer; + OpticalOutputPort m_p1_out_writer; + + // Processes + /** Main process of the module. + * + * It copies the input to the output, after attenuation and delay. + * + * **SystemC type:** thread + * + * **Sensitivity list:** p0_in or p1_in + * */ + void on_p0_in_changed(); + void on_p1_in_changed(); + + /** Constructor for Waveguide + * + * @param name name of the module + * @param length_cm length of the waveguide in cm + * */ + WaveguideBi(sc_module_name name, double length_cm = 1e-2, + double attenuation_dB_cm = 0, double neff = 2.2111, + double ng = 2.2637, double D = 0) + : WaveguideBase(name, length_cm, attenuation_dB_cm, neff, ng, D) + , m_p0_out_writer("p0_out_delayed_writer", p0_out) + , m_p1_out_writer("p1_out_delayed_writer", p1_out) + { + SC_HAS_PROCESS(WaveguideBi); + + SC_THREAD(on_p0_in_changed); + sensitive << p0_in; + SC_THREAD(on_p1_in_changed); + sensitive << p1_in; + } +}; |
