import os
import myhdl
from myhdl import *


module = 'counter_4bit'
testbench = 'tb_%s' % module

build_cmd = "iverilog -o %s.vvp %s.v %s.v" % (testbench, module, testbench)


def tb_counter_4bit():
    clk = Signal(bool(0))
    rst = Signal(bool(0))
    updown = Signal(bool(0))
    load = Signal(bool(0))
    data = Signal(modbv(val=0, min=0, max=15)[4:])
    data_out = Signal(modbv(val=0, min=0, max=15)[4:])

    # DUT
    print(build_cmd)
    if os.system(build_cmd):
        raise Exception("Error running build command")

    dut = Cosimulation(
        "vvp -m myhdl %s.vvp -lxt2" % testbench,
        clk=clk,
        rst=rst,    
        updown=updown,
        load=load,
        data=data,
        data_out=data_out
    )

    @always(delay(4))
    def clkgen():
        clk.next = not clk

    @instance
    def check():
        print("initialize")
        clk.next = 0
        rst.next = 0
        load.next = 0
        updown.next = 1
        data.next = 0
        
        yield clk.posedge
        yield clk.posedge

        print("... OK")
        print("test 1: reset")
        yield clk.negedge
        rst.next = 1
        yield clk.posedge
        yield clk.negedge
        rst.next = 0
        yield clk.posedge
        
        assert data_out == 0, f"assert failed with data_out {data_out} != 0"

        yield clk.posedge
        yield clk.posedge

        
        print("... OK")
        print("test 2: load")

        yield clk.posedge
        load.next = 1
        data.next = Signal(modbv(13)[4:])
        yield clk.negedge
        yield clk.posedge
        yield clk.negedge
        assert data_out == 13, f"assert failed with data_out {data_out} != {13}"

        load.next = 0
        yield clk.posedge
        yield clk.negedge


        print("... OK")
        print("test 3: inc")

        yield clk.negedge
        rst.next = 1
        yield clk.posedge
        yield clk.negedge
        rst.next = 0
        soll = 0

        for i in range(4):
            assert data_out == soll, f"assert failed with data_out {int(data_out)} != {i}"

            yield clk.posedge
            soll += 1
            yield clk.negedge

        print("... OK")
        print("test 4: dec")

        soll = 13

        yield clk.posedge
        updown.next = 0
        load.next = 1
        data.next = Signal(modbv(soll)[4:])
        yield clk.negedge
        yield clk.posedge
        yield clk.negedge
        load.next = 0

        for i in range(4):
            assert data_out == soll, f"assert failed with data_out {int(data_out)} != {soll}"

            yield clk.posedge
            soll -= 1
            yield clk.negedge


        print("... OK")

        for _ in range(10):
            yield clk.posedge

        raise StopSimulation

    return instances()


def simulate():
    sim = Simulation(tb_counter_4bit())
    sim.run()
