3. Testbench

In this chapter, we write the testbench for the Listing 2.1. This testbench contains several features of MyHDL which are enough to start writing the testbenches. Also, conversion of MyHDL testbench to HDL testbench is discussed.

3.1. Mod-m counter

  • In this section, we will write a testbench for Mod m coutner. For this, the Listing 2.1 is modified as below,

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    # mod_m_counter.py
    
    from myhdl import *
    
    period = 20 # clk frequency = 50 MHz
    
    @block
    def mod_m_counter(clk, reset_n, complete_tick, count, M, N):
        """ M = max count
            N = minimum bits required to represent M
        """
    
        count_reg = Signal(intbv(0)[N:0])
        count_next = Signal(intbv(0)[N:0])
    
        @always(clk.posedge, reset_n.negedge)
        def logic_reg():
            if reset_n == 0 :
                count_reg.next = 0
            else :
                count_reg.next = count_next
    
        @always_comb
        def logic_next():
            if count_reg == M-1 :
                count_next.next = 0
            else :
                count_next.next = count_reg + 1
    
    
        # optional complete_tick
        if complete_tick is not None:
            @always_comb
            def complete_tick_logic():
                if count_reg == M-1 :
                    complete_tick.next = 1
                else :
                    complete_tick.next = 0
    
        # optional count
        if count is not None:
            @always_comb
            def out_val():
                count.next = count_reg
    
        return instances()  # return all instances
    
    
    # testbench
    @block
    def mod_m_counter_tb():
        N = 3
        M = 5
    
        clk = Signal(bool(0))
        reset_n = Signal(bool(0))
        complete_tick = Signal(bool(0))
        count = Signal(intbv(0)[N:0])
    
        mod_m_counter_inst = mod_m_counter(clk, reset_n, complete_tick, count, M, N)
    
        # int is required
        @always(delay(int(period/2)))
        def clk_signal():
            clk.next = not clk
    
        @instance  # reset signal
        def reset_signal():
            reset_n.next = 0
            yield delay(period)
            reset_n.next = 1
    
        return instances()
    
    
    def main():
        N = 3
        M = 5
    
        clk = Signal(bool(0))
        reset_n = Signal(bool(0))
        complete_tick = Signal(bool(0))
        count = Signal(intbv(0)[N:0])
    
        mod_m_counter_v = mod_m_counter(clk, reset_n, complete_tick, count, M, N)
        mod_m_counter_v.convert(hdl="Verilog", initial_values=True)
    
        # test bench
        tb = mod_m_counter_tb()
        tb.config_sim(trace=True)
        tb.run_sim(15*period) # run for 15 clock cycle
    
    if __name__ == '__main__':
        main()
    
  • If we run the above code, then a vcd file, i.e. mod_m_counter_tb.vcd’, will be generated, which can be open in gtkwave. Following is the results saved in the .vcd file,

    ../_images/mod_m_counter_tb.png

    Fig. 3.1 Simulation results of mod_m_counter.py

3.2. Saving data to file

  • Sometimes we want to save the testbench-data in the file for further analysis of the data. In the below listing, the code in the above Section 3.1 is slightly modified to save the data in the file and printed on the screen.

    Important

    • Note that, the signals at Lines 55-61 are declared as ‘intbv’ instead of ‘bool’, because the ‘bool’ signal is saved as ‘True/False’ which may not be desirable for the analysis of the data.
    • Lines 79-93 prints the data on screen and save the data in the file ‘mod_m_counter.csv’.
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    # mod_m_counter.py
    
    from myhdl import *
    
    period = 20 # clk frequency = 50 MHz
    
    @block
    def mod_m_counter(clk, reset_n, complete_tick, count, M, N):
        """ M = max count
            N = minimum bits required to represent M
        """
    
        count_reg = Signal(intbv(0)[N:0])
        count_next = Signal(intbv(0)[N:0])
    
        @always(clk.posedge, reset_n.negedge)
        def logic_reg():
            if reset_n == 0 :
                count_reg.next = 0
            else :
                count_reg.next = count_next
    
        @always_comb
        def logic_next():
            if count_reg == M-1 :
                count_next.next = 0
            else :
                count_next.next = count_reg + 1
    
    
        # optional complete_tick
        if complete_tick is not None:
            @always_comb
            def complete_tick_logic():
                if count_reg == M-1 :
                    complete_tick.next = 1
                else :
                    complete_tick.next = 0
    
        # optional count
        if count is not None:
            @always_comb
            def out_val():
                count.next = count_reg
    
        return instances()  # return all instances
    
    
    # testbench
    @block
    def mod_m_counter_tb():
        N = 3
        M = 5
    
        # 'intbv' is used instead of 'bool' as 'false' will be displayed
        # in the saved-data for 'bool'.
        # clk = Signal(bool(0))
        clk = Signal(intbv(0)[1:0])
        reset_n = Signal(intbv(0)[1:0])
        complete_tick = Signal(intbv(0)[1:0])
        count = Signal(intbv(0)[N:0])
    
        mod_m_counter_inst = mod_m_counter(clk, reset_n, complete_tick, count, M, N)
    
    
        # int is required
        @always(delay(int(period/2)))
        def clk_signal():
            clk.next = not clk
    
    
        @instance  # reset signal
        def reset_signal():
            reset_n.next = 0
            yield delay(period)
            reset_n.next = 1
    
    
        # print simulation data on screen and file
        file_data = open("mod_m_counter.csv", 'w') # file for saving data
        # print header on screen
        print("{0},{1},{2}".format("reset_n", "complete_tick", "count"))
        # print header to file
        print("{0},{1},{2}".format("reset_n", "complete_tick", "count")
                    , file=file_data)
        # print data on each clock
        @always(clk.posedge)
        def print_data():
            # print on screen
            print("{0}, {1}, {2}".format(reset_n, complete_tick, count))
            # print in file
            print("{0}, {1}, {2}".format(reset_n, complete_tick, count)
                    , file=file_data)
    
    
        return instances()
    
    
    def main():
        N = 3
        M = 5
    
        clk = Signal(bool(0))
        reset_n = Signal(bool(0))
        complete_tick = Signal(bool(0))
        count = Signal(intbv(0)[N:0])
    
        mod_m_counter_v = mod_m_counter(clk, reset_n, complete_tick, count, M, N)
        mod_m_counter_v.convert(hdl="Verilog", initial_values=True)
    
        # test bench
        tb = mod_m_counter_tb()
        tb.config_sim(trace=True)
        tb.run_sim(15*period) # run for 15 clock cycle
    
    
    if __name__ == '__main__':
        main()
    
  • Following is the data which is saved in the file ‘mod_m_counter.csv’

    reset_n,complete_tick,count
    0, 0, 0
    1, 0, 0
    1, 0, 1
    1, 0, 2
    1, 0, 3
    1, 1, 4
    1, 0, 0
    1, 0, 1
    1, 0, 2
    1, 0, 3
    1, 1, 4
    1, 0, 0
    1, 0, 1
    1, 0, 2
    1, 0, 3
    

3.3. Conversion : MyHDL testbench to HDL testbench

  • Some changes are required in above listing to convert the MyHDL testbench into HDL testbench, as shown below. Execute the below code to generate the testbench.

    Note

    • ‘@instance’ decorator is used with “def clk_signal():”, as ‘delay’ argument is not supported for testbench conversion (Lines 66-70).
    • ‘.format’ option of Python3-print statement is not supported by MyHDL (Lines 91-99).
    • The conversion statement (Line 119) is placed above the simulation statements (Lines 122-123).
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    # mod_m_counter.py
    
    from myhdl import *
    
    period = 20 # clk frequency = 50 MHz
    
    @block
    def mod_m_counter(clk, reset_n, complete_tick, count, M, N):
        """ M = max count
            N = minimum bits required to represent M
        """
    
        count_reg = Signal(intbv(0)[N:0])
        count_next = Signal(intbv(0)[N:0])
    
        @always(clk.posedge, reset_n.negedge)
        def logic_reg():
            if reset_n == 0 :
                count_reg.next = 0
            else :
                count_reg.next = count_next
    
        @always_comb
        def logic_next():
            if count_reg == M-1 :
                count_next.next = 0
            else :
                count_next.next = count_reg + 1
    
    
        # optional complete_tick
        if complete_tick is not None:
            @always_comb
            def complete_tick_logic():
                if count_reg == M-1 :
                    complete_tick.next = 1
                else :
                    complete_tick.next = 0
    
        # optional count
        if count is not None:
            @always_comb
            def out_val():
                count.next = count_reg
    
        return instances()  # return all instances
    
    
    # testbench
    @block
    def mod_m_counter_tb():
        N = 3
        M = 5
    
        # 'intbv' is used instead of 'bool' as 'false' will be displayed
        # in the saved-data for 'bool'.
        # clk = Signal(bool(0))
        clk = Signal(intbv(0)[1:0])
        reset_n = Signal(intbv(0)[1:0])
        complete_tick = Signal(intbv(0)[1:0])
        count = Signal(intbv(0)[N:0])
    
        mod_m_counter_inst = mod_m_counter(clk, reset_n, complete_tick, count, M, N)
    
    
        @instance
        def clk_signal():
            while True:
                clk.next = not clk
                yield delay(period//2)
    
    
        @instance  # reset signal
        def reset_signal():
            reset_n.next = 0
            yield delay(period)
            reset_n.next = 1
    
    
        # print simulation data on screen and file
        file_data = open("mod_m_counter.csv", 'w') # file for saving data
        # # print header on screen
        print("{0},{1},{2}".format("reset_n", "complete_tick", "count"))
        # # print header to file
        print("{0},{1},{2}".format("reset_n", "complete_tick", "count"),
                   file=file_data)
        # print data on each clock
        @always(clk.posedge)
        def print_data():
            # print on screen
            # print.format is not supported in MyHDL 1.0
            # print("{0}, {1}, {2}".format(reset_n, complete_tick, count))
            print(reset_n, ",", complete_tick, ",",  count, sep='')
    
            # print in file
            # print.format is not supported in MyHDL 1.0
            # print("{0}, {1}, {2}".format(reset_n, complete_tick, count)
                    # , file=file_data)
            print(reset_n, ",", complete_tick, ",",  count, sep='', file=file_data)
    
    
        return instances()
    
    
    def main():
        N = 3
        M = 5
    
        clk = Signal(bool(0))
        reset_n = Signal(bool(0))
        complete_tick = Signal(bool(0))
        count = Signal(intbv(0)[N:0])
    
        mod_m_counter_v = mod_m_counter(clk, reset_n, complete_tick, count, M, N)
        mod_m_counter_v.convert(hdl="Verilog", initial_values=True)
    
        # test bench
        tb = mod_m_counter_tb()
        tb.convert(hdl="Verilog", initial_values=True)
        # keep following lines below the 'tb.convert' line
        # otherwise error will be reported
        tb.config_sim(trace=True)
        tb.run_sim(15*period) # run for 15 clock cycle
    
    
    if __name__ == '__main__':
        main()
    
  • Following is the generated Verilog code for the testbench,

    Note

    In the above listing, the Python code prints the data on screen and save in the file. But in the resultant Verilog code, the data will not be saved in the file (but will be printed on the screen).

    // File: mod_m_counter_tb.v
    // Generated by MyHDL 1.0dev
    // Date: Mon Oct 16 11:44:15 2017
    
    
    `timescale 1ns/10ps
    
    module mod_m_counter_tb (
    
    );
    
    reg [0:0] reset_n = 0;
    wire [2:0] count;
    reg [0:0] complete_tick = 0;
    reg [0:0] clk = 0;
    reg [2:0] mod_m_counter_1_count_reg = 0;
    reg [2:0] mod_m_counter_1_count_next = 0;
    
    always @(posedge clk, negedge reset_n) begin: MOD_M_COUNTER_TB_MOD_M_COUNTER_1_LOGIC_REG
        if ((reset_n == 0)) begin
            mod_m_counter_1_count_reg <= 0;
        end
        else begin
            mod_m_counter_1_count_reg <= mod_m_counter_1_count_next;
        end
    end
    
    
    always @(mod_m_counter_1_count_reg) begin: MOD_M_COUNTER_TB_MOD_M_COUNTER_1_LOGIC_NEXT
        if (($signed({1'b0, mod_m_counter_1_count_reg}) == (5 - 1))) begin
            mod_m_counter_1_count_next = 0;
        end
        else begin
            mod_m_counter_1_count_next = (mod_m_counter_1_count_reg + 1);
        end
    end
    
    
    always @(mod_m_counter_1_count_reg) begin: MOD_M_COUNTER_TB_MOD_M_COUNTER_1_COMPLETE_TICK_LOGIC
        if (($signed({1'b0, mod_m_counter_1_count_reg}) == (5 - 1))) begin
            complete_tick = 1;
        end
        else begin
            complete_tick = 0;
        end
    end
    
    assign count = mod_m_counter_1_count_reg;
    
    initial begin: MOD_M_COUNTER_TB_CLK_SIGNAL
        while (1'b1) begin
            clk <= (!clk);
            # (20 / 2);
        end
    end
    
    initial begin: MOD_M_COUNTER_TB_RESET_SIGNAL
        reset_n <= 0;
        # 20;
        reset_n <= 1;
    end
    
    always @(posedge clk) begin: MOD_M_COUNTER_TB_PRINT_DATA
        $write("%h", reset_n);
        $write(" ");
        $write(",");
        $write(" ");
        $write("%h", complete_tick);
        $write(" ");
        $write(",");
        $write(" ");
        $write("%h", count);
        $write("\n");
        $write("%h", reset_n);
        $write(" ");
        $write(",");
        $write(" ");
        $write("%h", complete_tick);
        $write(" ");
        $write(",");
        $write(" ");
        $write("%h", count);
        $write("\n");
    end
    
    endmodule
    
  • Below is the simulation results for the above testbench, which is same as Fig. 3.1 generated by MyHDL testbench.

    ../_images/mod_m_counter_verilog_tb.png

    Fig. 3.2 Simulation results of mod_m_counter.v

3.4. Conclusion

In this chapter, we wrote the testbench for the Mod-m counter. First we wrote a testbench which saves the data in the .vcd file. Next, we learn the method to save the data in the file. Lastly, we modified the MyHDL testbench to convert it into HDL testbench.