#include <systemc>

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

template <int N>
SC_MODULE(counterNbit) {
    sc_in_clk clk ;
    sc_in<bool> reset, enable;
    sc_out<sc_uint<N>> counter_out;
    SC_CTOR(counterNbit): count(0) {
        SC_METHOD(incr_count);
        sensitive << clk.pos() << reset.pos();
    }
    static const int number_of_bits = N;
private:
    sc_uint<N> count;
    void incr_count() {
        if (reset.read()) {
            count = 0;
            counter_out.write(count);
        }
        else {
            if (enable.read()) {
                count = count + 1;
                counter_out.write(count);
            }
        }
    }
};

SC_MODULE(Testbench) {
    sc_in_clk clk;
    sc_out<bool> reset, enable;
    SC_CTOR(Testbench){
        SC_THREAD(testprocess);
        sensitive << clk.pos();
    }
private:
    void testprocess() {
        enable.write(true);
        reset.write(true);
        wait(25, SC_NS);
        reset.write(false);
        wait(200, SC_NS);
        enable.write(false);
        wait(50, SC_NS);
        reset.write(true);
        wait(50, SC_NS);
        reset.write(false);
    }
};

int sc_main(int argc, char *argv[]) {
    sc_clock clock("clock", 20, SC_NS);
    sc_signal<bool> reset, enable;
    Testbench testbench("testbench");
    testbench.reset(reset);
    testbench.enable(enable);
    testbench.clk(clock);

    counterNbit<2> counter4("counter4");
    sc_signal<sc_uint<counter4.number_of_bits>> count4;
    counter4.clk(clock);
    counter4.reset(reset);
    counter4.enable(enable);
    counter4.counter_out(count4);

    counterNbit<3> counter8("counter8");
    sc_signal<sc_uint<counter8.number_of_bits>> count8;
    counter8.clk(clock);
    counter8.reset(reset);
    counter8.enable(enable);
    counter8.counter_out(count8);
    
    // Record (trace) signals for verification
    auto tf = sc_create_vcd_trace_file("trace");
    tf->set_time_unit(1, SC_NS);
    sc_trace(tf, clock, "clock");
    sc_trace(tf, reset, "reset");
    sc_trace(tf, enable, "enable");
    sc_trace(tf, count4, "count4");
    sc_trace(tf, count8, "count8");

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

    sc_close_vcd_trace_file(tf);
    cin.get();
    return 0;
}
