0
\$\begingroup\$

I am working on a project wherein I need to convert a Finite State Machine coded in Verilog into a ROM. In order to do this I need to create a memory file for the ROM version of the FSM which stores the Next State and Output Values at Addresses comprised of the Current State and Input Values. My thought for how to build this file was to take the FSM in question and add "current_state" input, and "next_state" outputs, and then to make small modification to the fsm such that it uses those. However, I have had multiple issues with this tesbench that I can't seem to figure out so I figured I would throw together a smaller fsm to show what I am doing and hopefully get some insight as to what I am doing incorrectly. I believe my main issue is that the clock signal doesn't appear to be triggering at all (at least when I debug, the block which should be activiating at the posedge clock doesn't appear to ever trigger). The FSM in question I am attempting to model has something like 15 Current State and Input bits, and 76 Next State and Output bits, so rather than use any of the code from that I just threw together this smaller FSM (I get the same behavior so the issue isn't state machine specific and is likely more just due to my bad testbench).

So here is the FSM unit under test. It's a basic FSM I lifted from an old textbook:

    module mealy_traditional(input wire clk, 
                        input wire reset, 
                        input wire a, 
                        input wire b, 
                        output wire y);
    
    //Symbollic State Definition
    localparam [1:0]    state0 = 2'b00, 
                        state1 = 2'b01, 
                        state2 = 2'b10;
    //signal declaration
    reg [1:0]   stateCurrent, 
                stateNext;
    
    //state register
    always @(posedge clk, posedge reset)
        if(reset)
            stateCurrent <= state0;
        else
            stateCurrent <= stateNext;
    
    //_______________________________________________________Next State Logic
    always @* begin
        stateNext = stateCurrent
        //______________________________________________________START_CASE
        case(stateCurrent)
            //___________________________________________________state0
            state0:
                if(a)
                    if(b)
                        stateNext = state2;
                    else
                        stateNext = state1;
                else
                    stateNext = state0;
            //___________________________________________________state1
            state1:
                if(a)
                    stateNext = state0;
                else
                    stateNext = state1;
            //___________________________________________________state2
            state2: stateNext = state0;
            //__________________________________________________default
            default: stateNext = state0;
        endcase
    end
    //Mealy Output Logic, function of both current state and current inputs
    assign y = (stateCurrent == state0) & a & b;
endmodule

And here is the same FSM modified (I believe) to accept the current state as an input and to assign the next state output to a wire output.

    module mealy_traditional(input wire clk, 
                        input wire reset, 
                        input wire [1:0] state_current, 
                        input wire a, 
                        input wire b, 
                        output wire [1:0] state_next, 
                        output wire y);
    
    //Symbollic State Definition
    localparam [1:0]    state0 = 2'b00, 
                        state1 = 2'b01, 
                        state2 = 2'b10;
    //signal declaration
    reg [1:0]   stateCurrent, 
                stateNext;
    
    //state register
    always @(posedge clk, posedge reset)
        if(reset)
            stateCurrent <= state0;
        else
            stateCurrent <= state_current;
    
    //_______________________________________________________Next State Logic
    always @* begin
        stateNext = stateCurrent;
        //______________________________________________________START_CASE
        case(stateCurrent)
            //___________________________________________________state0
            state0:
                if(a)
                    if(b)
                        stateNext = state2;
                    else
                        stateNext = state1;
                else
                    stateNext = state0;
            //___________________________________________________state1
            state1:
                if(a)
                    stateNext = state0;
                else
                    stateNext = state1;
            //___________________________________________________state2
            state2: stateNext = state0;
            //__________________________________________________default
            default: stateNext = state0;
        endcase
    end
    //Mealy Output Logic, function of both current state and current inputs
    assign y = (stateCurrent == state0) & a & b;
    assign state_next = stateNext;
endmodule

So here's where the issue almost certainly lies, the testbench. Now I know that given this FSM's limited inputs it would be very easy to just manually hit the fsm with each input combination and display the output, however, obviously with the other FSM that has close to 100 bits of IO, that's not really an option. Therefore I decided to create a for loop to just iterate through all possible input bit combinations and then generate the outputs to save into the memory file. The problem I run into is that it doesn't appear that the clock input cycles at all. Currently I am trying the following testbench code

`timescale 1ns / 1ns
module mealy_traditional_tb;
    reg             clk = 1'b0;
    reg             reset = 1'b0;
    reg [3:0]       in;
    wire [2:0]      out;
    integer         i = 0;
    integer         maxValue = 16;


    //Instantiation
    mealy_traditional 
        uut(.clk(clk), 
            .reset(reset),
            .state_current(in[3:2]),
            .a(in[1]), 
            .b(in[0]), 
            .state_next(out[2:1]),
            .y(out[0]));
    initial begin
        
        reset = 1;
        #10
        reset = 0;
        #10
        for(i = 0; i < maxValue; i = i + 1) begin
            in = i;
            clk = 1;
            clk = 0;
            clk = 1;
            clk = 0;
            $display("%b : %b", in, out);
        end
        $finish;
    end
endmodule

initially I had tried to create a standalone clock cycle by using an

always #10 clk = ~clk;

And then putting delays inside the for loop, which verilog did NOT like (wouldn't even compile, threw syntax errors).

So as it stands right now, this testbench code does compile and the simulator fires up, however it's pretty clear that the else branch of the state register update in the FSM is not getting accessed as when I set break points in there they never trigger and the output of the state machine appears to be just the baseline state from a system reset. (as follows)

# 0000 : 00x
# 0001 : 00x
# 0010 : 00x
# 0011 : 00x
# 0100 : 00x
# 0101 : 00x
# 0110 : 00x
# 0111 : 00x
# 1000 : 00x
# 1001 : 00x
# 1010 : 00x
# 1011 : 00x
# 1100 : 00x
# 1101 : 00x
# 1110 : 00x
# 1111 : 00x

I would have thought manually forcing the clock cycle in the for loop would have cause the finite state machine to update but it appears to not have. Is there a way for me to ensure that the clock cycle fires in such a way?

\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Yes, the problem is in your testbench code. It is always a good idea to show the simulation time when you $display values as it helps to debug your problem. Doing so shows that all your output occurs at the same time (20ns):

              20 0000 : 00x
              20 0001 : 00x
              20 0010 : 00x
              20 0011 : 00x
              20 0100 : 00x
              20 0101 : 00x
              20 0110 : 00x
              20 0111 : 00x
              20 1000 : 00x
              20 1001 : 00x
              20 1010 : 00x
              20 1011 : 00x
              20 1100 : 00x
              20 1101 : 00x
              20 1110 : 00x
              20 1111 : 00x

The first column is the simulation time. The problem is that no time elapses inside the for loop. One way to fix that is to add # delays in the loop as follows:

initial begin
    reset = 1;
    #10
    reset = 0;
    #10
    for(i = 0; i < maxValue; i = i + 1) begin
        in = i;
        #5 clk = 1;
        #5 clk = 0;
        #5 clk = 1;
        #5 clk = 0;
        $display($time,, "%b : %b", in, out);
    end
    $finish;
end

This prints:

          40 0000 : 000
          60 0001 : 000
          80 0010 : 010
         100 0011 : 101
         120 0100 : 010
         140 0101 : 010
         160 0110 : 000
         180 0111 : 000
         200 1000 : 000
         220 1001 : 000
         240 1010 : 000
         260 1011 : 000
         280 1100 : 000
         300 1101 : 000
         320 1110 : 000
         340 1111 : 000

As you can see, the simulation time and out are changing value, and out no longer has unknowns (x).


Here is another way to write the testbench code. It uses the more traditional always block for the clock, which you were trying to get working. It also assures the input is synchronous with the clock.

initial begin
    $monitor($time,, "%b : %b", in, out);
    reset = 1;
    #10
    reset = 0;
    #10
    for (i = 0; i < maxValue; i = i + 1) begin
        @(posedge clk) in <= i;
    end
    $finish;
end

always #10 clk = ~clk;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.