#include <systemc>

using namespace sc_core;
using namespace sc_dt;
using namespace std;

SC_MODULE(Adder) {
    sc_out<sc_logic> S, Cout;
    sc_in<sc_logic> A, B, Cin;
    SC_CTOR(Adder) {
        SC_METHOD(add);
        sensitive << A << B << Cin;
    }
private:
    void add() { 
        S.write(A.read() ^ B.read() ^ Cin.read());
        Cout.write(A.read() & B.read() | Cin.read() & (A.read() ^ B.read()));
    }
};

SC_MODULE(Adder2Bits) {
    sc_in<sc_logic> A0, B0, A1, B1, Cin;
    sc_out<sc_logic> S0, S1, Cout;
    SC_CTOR(Adder2Bits): adder0("adder0"), adder1("adder1") {
        adder0.A(A0);
        adder0.B(B0);
        adder0.Cin(Cin);
        adder0.S(S0);
        adder0.Cout(carry);
        adder1.Cin(carry);
        adder1.A(A1);
        adder1.B(B1);
        adder1.S(S1);
        adder1.Cout(Cout);
    }
private:
    Adder adder0, adder1;
    sc_signal<sc_logic> carry;
};

SC_MODULE(Testbench){
    sc_out<sc_logic> TA0, TB0, TA1, TB1, TCin;
    SC_CTOR(Testbench) {
        SC_THREAD(testprocess);
    }
private:
    void testprocess() {
        TA0.write(SC_LOGIC_0);
        TA1.write(SC_LOGIC_0);
        TB0.write(SC_LOGIC_0);
        TB1.write(SC_LOGIC_0);
        TCin.write(SC_LOGIC_0);
        wait(10, SC_NS);
        TA0.write(SC_LOGIC_1);
        TA1.write(SC_LOGIC_0);
        TB0.write(SC_LOGIC_1);
        TB1.write(SC_LOGIC_1);
        TCin.write(SC_LOGIC_1);
        wait(10, SC_NS);
        TA0.write(SC_LOGIC_0);
        TA1.write(SC_LOGIC_0);
        TB0.write(SC_LOGIC_0);
        TB1.write(SC_LOGIC_0);
        TCin.write(SC_LOGIC_0);
    }
};

int sc_main(int argc, char *argv[]){
    sc_signal<sc_logic> A1, A0, B1, B0, Cin;
    sc_signal<sc_logic> S0, S1, Cout;
    Adder2Bits adder2Bits("adder2Bits");
    Testbench testbench("testbench");
    adder2Bits.A1(A1);
    adder2Bits.A0(A0);
    adder2Bits.B1(B1);
    adder2Bits.B0(B0);
    adder2Bits.Cin(Cin);
    adder2Bits.S0(S0);
    adder2Bits.S1(S1);
    adder2Bits.Cout(Cout);
    testbench.TA0(A0);
    testbench.TA1(A1);
    testbench.TB1(B1);
    testbench.TB0(B0);
    testbench.TCin(Cin);

    // Record (trace) signals for verification
    auto tf = sc_create_vcd_trace_file("trace");
    tf->set_time_unit(1, SC_NS);
    sc_trace(tf, A0, "A0");
    sc_trace(tf, A1, "A1");
    sc_trace(tf, B0, "B0");
    sc_trace(tf, B1, "B1");
    sc_trace(tf, Cin, "Cin");
    sc_trace(tf, S0, "S0");
    sc_trace(tf, S1, "S1");
    sc_trace(tf, Cout, "Cout");

    // Start the simulation for 200ns 
    sc_start(200, SC_NS);

    sc_close_vcd_trace_file(tf);
    return 0;
}
