1
\$\begingroup\$

I am working on a finite state machine that is written in Verilog and being simulated in ModelSim. This FSM is for implementation of a SISC processor. In this implementation, we are using arithmetic and branch instructions. Below is the control module in question:

module ctrl (clk, rst_f, opcode, mm, stat, rf_we, alu_op, wb_sel, pc_sel, pc_write, pc_rst, br_sel, rb_sel, ir_load);

  input clk, rst_f;
  input[3:0] stat, mm, opcode;
  output rf_we, wb_sel, pc_sel, pc_write, pc_rst, br_sel, rb_sel, ir_load;
  output [1:0] alu_op;
  reg rf_we, wb_sel, pc_sel, pc_write, pc_rst, br_sel, rb_sel, ir_load;
  reg [1:0] alu_op;


  // states
  parameter start0 = 0, start1 = 1, fetch = 2, decode = 3, execute = 4, mem = 5, writeback = 6;

  // opcodes
  parameter NOOP = 0, LOD = 1, STR = 2, SWP = 3, BRA = 4, BRR = 5, BNE = 6, BNR = 7, ALU_OP = 8, HLT=15;

  // addressing modes
  parameter am_imm = 8;

  // state registers
  reg [2:0]  present_state, next_state;
  reg [3:0]  branchTest;

  initial
    present_state = start0;

always @(posedge clk or negedge rst_f)
begin
if(!rst_f)
    next_state = 1;
present_state = next_state;
end

always @(mm or stat)
begin
    branchTest = mm & stat;
end

always @(present_state or next_state)
begin
case (present_state)
0: //Startup
    next_state = start1;  

1: //Reset state - cycle setup step
    begin                  
        wb_sel = 0;
        rf_we = 0;
        alu_op[0] = 0;
        alu_op[1] = 0;
        pc_sel = 0;
        pc_write = 0;
        pc_rst = 1;
        br_sel = 0;
        rb_sel = 0;
        ir_load = 0;
        next_state = fetch;  
    end
2: //fetch step - This will be retrieve instruction from memory
    begin
        pc_write = 0;
        pc_rst = 0;
        ir_load = 1;
        pc_write = 1;
        wb_sel = 0;
        rf_we = 0;
        alu_op[0] = 0;
        alu_op[1] = 0;
        br_sel = 0;
        rb_sel = 0;
        pc_sel = 0; 
        next_state = decode;  
    end
3: //decode step - Interpret instruction and decide what signals to turn on
    begin
        pc_write = 0;
        ir_load = 0;
        case (opcode)   
        NOOP: next_state = execute;
        LOD:  next_state = execute;//Code for load instructions
        STR:  next_state = execute;//Code for store instructions
        SWP:  next_state = execute;//Code for swap instruction
        BRA:  begin if(branchTest != 0) //Code for branch if equal instruction
            begin
                br_sel = 0;
                pc_sel = 1;
                pc_write = 1;
                next_state = fetch;
            end
            else
                next_state = execute;
            end
        BRR:  begin if(branchTest != 0) //Code for branch if equal relative instruction
            begin
                br_sel = 1;
                pc_sel = 1;
                pc_write = 1;
                next_state = fetch;
            end
            else
                next_state = execute;
            end
        BNE:  begin if(branchTest == 0) //Code for branch if not equal instruction
            begin
                br_sel = 0;
                pc_sel = 1;
                pc_write = 1;
                next_state = fetch;
            end
            else
                next_state = execute;
            end
        BNR:  begin if(branchTest == 0) //Code for branch if not equal relative instruction
            begin
                br_sel = 1;
                pc_sel = 1;
                pc_write = 1;
                next_state = fetch;
            end
            else
                next_state = execute;
            end
        ALU_OP: begin
                next_state = execute;
            end
        endcase
    end
4: //execute step
    begin
        if(opcode != ALU_OP)
        begin
            alu_op[1] = 1;
        end
        else
        begin
            alu_op[1] = 0;
            alu_op[0] = mm[3];
        end
        next_state = mem;  
    end
5: //memory step
    begin  
        next_state = writeback;
    end
6: //writeback step
    begin     
        if(opcode == ALU_OP)
            begin
                rf_we = 1;
            end
        next_state = fetch;
    end
endcase  
end 

// Halt on HLT instruction

  always @ (opcode)
  begin
    if (opcode == HLT)
    begin 
      #5 $display ("Halt."); //Delay 5 ns so $monitor will print the halt instruction
      $stop;
    end
  end


endmodule

The arithmetic functions work as expected. However, when the branch instructions are executed, the change in outputs seem to be delayed by many cycles. I have a monitor statement in another module that is outputting the various values from the simulation. The pertinent part of output is as follows:

# R1:00000001 R2:00000002 R3:00000002 R4:00000002 R5:ff000019 IR:80331002 PC:001b ir_load:0 pc_write:0 br_addr: 4125 IM:7100fffd br_sel:0 pc_sel:0 OP-CODE:8 mm:0000 bTest:0000 alu_op:00 rf_we:0 write_data:00000002 state:4 next:5 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000002 R4:00000002 R5:ff000019 IR:80331002 PC:001b ir_load:0 pc_write:0 br_addr: 4125 IM:7100fffd br_sel:0 pc_sel:0 OP-CODE:8 mm:0000 bTest:0000 alu_op:00 rf_we:0 write_data:00000001 state:5 next:6 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000002 R4:00000002 R5:ff000019 IR:80331002 PC:001b ir_load:0 pc_write:0 br_addr: 4125 IM:7100fffd br_sel:0 pc_sel:0 OP-CODE:8 mm:0000 bTest:0000 alu_op:00 rf_we:1 write_data:00000001 state:6 next:2 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:80331002 PC:001b ir_load:1 pc_write:1 br_addr: 4125 IM:7100fffd br_sel:0 pc_sel:0 OP-CODE:8 mm:0000 bTest:0000 alu_op:00 rf_we:0 write_data:00000001 state:2 next:3 (STAT: 0000)
**# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:7100fffd PC:001c ir_load:0 pc_write:0 br_addr:   25 IM:60000016 br_sel:0 pc_sel:0 OP-CODE:7 mm:0001 bTest:0000 alu_op:00 rf_we:0 write_data:00000001 state:3 next:4 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:7100fffd PC:001c ir_load:0 pc_write:0 br_addr:   25 IM:60000016 br_sel:0 pc_sel:0 OP-CODE:7 mm:0001 bTest:0000 alu_op:10 rf_we:0 write_data:00000000 state:4 next:5 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:7100fffd PC:001c ir_load:0 pc_write:0 br_addr:   25 IM:60000016 br_sel:0 pc_sel:0 OP-CODE:7 mm:0001 bTest:0000 alu_op:10 rf_we:0 write_data:00000000 state:5 next:6 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:7100fffd PC:001c ir_load:0 pc_write:0 br_addr:   25 IM:60000016 br_sel:0 pc_sel:0 OP-CODE:7 mm:0001 bTest:0000 alu_op:10 rf_we:0 write_data:00000000 state:6 next:2 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:7100fffd PC:001c ir_load:1 pc_write:1 br_addr:   25 IM:60000016 br_sel:0 pc_sel:0 OP-CODE:7 mm:0001 bTest:0000 alu_op:00 rf_we:0 write_data:00000000 state:2 next:3 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:60000016 PC:001d ir_load:0 pc_write:1 br_addr:   22 IM:f0000000 br_sel:1 pc_sel:1 OP-CODE:6 mm:0000 bTest:0000 alu_op:00 rf_we:0 write_data:00000000 state:3 next:2 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:60000016 PC:0016 ir_load:1 pc_write:1 br_addr:   44 IM:8822ffff br_sel:0 pc_sel:0 OP-CODE:6 mm:0000 bTest:0000 alu_op:00 rf_we:0 write_data:00000000 state:2 next:3 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:8822ffff PC:0017 ir_load:0 pc_write:1 br_addr:   22 IM:51000005 br_sel:0 pc_sel:1 OP-CODE:8 mm:1000 bTest:0000 alu_op:00 rf_we:0 write_data:00000000 state:3 next:2 (STAT: 0000)**
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:8822ffff PC:0016 ir_load:1 pc_write:1 br_addr:   21 IM:8822ffff br_sel:0 pc_sel:0 OP-CODE:8 mm:1000 bTest:0000 alu_op:00 rf_we:0 write_data:00000000 state:2 next:3 (STAT: 0000)
# R1:00000001 R2:00000002 R3:00000001 R4:00000002 R5:ff000019 IR:8822ffff PC:0017 ir_load:0 pc_write:0 br_addr:   22 IM:51000005 br_sel:0 pc_sel:0 OP-CODE:8 mm:1000 bTest:0000 alu_op:00 rf_we:0 write_data:00000000 state:3 next:4 (STAT: 0000)

In the lines marked with ** in the output, there are two commands (OPCODE) being used: 7 and 6. OPCODE 7 comes before OPCODE 6 in the output provided, and when OPCODE 7 is run, given the conditions it was passed, should change the state of the output, return to the FETCH step and branch the program counter (PC). However, it seems to evaluate to a different condition, instead move onto EXECUTE, and finish out the cycle. On the OPCODE 6, which should always evaluate true and branch, it indeed branches as I would expect. However, on the next OPCODE after 6, it seems to also produce a branch result, though it is not a branch instruction. Given the nature of the instructions being passed, I think it is only a fluke that it was going to branch to the same place regardless, so it doesn't totally mess up the instructions (both OPCODE 7 and 6 branch to the same place).

What I am surmising is - the OPCODE 7 did indeed test true for the branch, but instead of setting all its variables during the current cycle and moving to FETCH, it goes through all the steps and waits until the FSM goes back to FETCH and then changes (this creates the return FETCH seen during OPCODE 6). Since the following OPCODE is also a branch, it also generates its conditions, but not until the FSM returns to FETCH. During the next instruction, it executes the branch criteria from OPCODE 6, but fortunately returns to itself and then continues on its merry way.

So my question is am I barking up the wrong tree or am I not setting my outputs and next state properly in the if statements that are in DECODE state of my FSM?

I have a passing familiarity with HDLs, and have worked with VHDL before, but this is my first go at Verilog. This question is a bit long winded, so if any clarification is needed, please feel free to ask.

\$\endgroup\$
  • \$\begingroup\$ You have Verilog and a decent simulator. Why don't you use it to debug your own problem? The behavior should be deterministic, so just trace the bad behavior. \$\endgroup\$ – Elliot Alderson Mar 20 at 22:06
1
\$\begingroup\$

You need to learn about modeling combinational logic versus sequential logic in Verilog. Sequential blocks should be using non-blocking assignments <= instead of = to variables. Combinational blocks need complete sensitivity lists. You can use the shortcut @(*).

\$\endgroup\$
  • \$\begingroup\$ Yes, I think you are correct. I changed my assignments over in my sequential component to non-blocking, and then the states started to assign properly. That then revealed that my branch instructions were backwards on using immediate or relative. Changed those over and the output is as I would expect now. Thank you for the assistance. Also went and cleaned up the redundancy, as I was still in the middle of testing things. \$\endgroup\$ – Daedaliss Mar 20 at 22:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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