Search code examples
verilogsystem-verilogiverilog

How does verilog treat input values to if statements in always_ff blocks


I'm currently working on a pipelined MIPS cpu using Icarus Verilog and have come across some very strange behaviour when using an if statement within an always_ff loop. I'm currently testing this implementation of a PC block:

module PC (

    input logic clk,
    input logic rst,

    input logic[31:0] PC_JVal,
    input logic jump_en,
    input logic branch_en,
    input logic PC_Stall,

    output logic [31:0] PC_Out,
    output logic fetch_stall,

    output logic active,
    output logic [2:0] check

);

// Active is completely dependent on the value of the PC.

// JUMP_EN --> PC = JVAL
// BRANCH_EN --> PC = PC + JVAL
// PC_Stall --> PC = PC

reg [31:0] PC;
logic [31:0] branchSignExt = (PC_JVal[15] == 1) ? {16'hFFFF, PC_JVal[15:0]} : {16'h0000, PC_JVal[15:0]};
logic start;

assign fetch_stall = PC_Stall;
assign active = (PC != 0) ? 1 : 0;

assign PC_Out = (active == 0) ? 0 : ( (PC_Stall == 1) ? PC + 4 : ( (jump_en == 1) ? PC_JVal : ( (branch_en == 1) ? PC + branchSignExt : PC + 4 ) ) );

initial begin

    PC = 0;
    start = 0;

    check = 0;

end

always_ff @ (posedge clk) begin

    check[1] <= ~check[1];

    if (rst) begin
        start <= 1;
    end 
    else if (active) begin

        
        
        if (PC_Stall) begin
            PC <= PC;
            check[0] <= ~check[0];
        end
        else if (jump_en) begin
            PC <= PC_JVal;
        end
        else if (branch_en) begin
            PC <= PC + branchSignExt;
        end
        else begin
            PC <= PC + 4;
        end
        
    end

end

always_ff @ (negedge rst) begin
    if (start) begin
        PC <= 32'hBFBFFFFC;
        start <= 0;
    end
end

endmodule

And am running the following testbench:

module PC_TB ();

    logic clk;
    logic rst;

    logic[31:0] PC_JVal;
    logic jump_en;
    logic branch_en;
    logic PC_Stall;

    logic [31:0] PC_Out;
    logic fetch_stall;
    logic active;
    logic [2:0] check;

    initial begin

        $dumpfile("PC_TB.vcd");
        $dumpvars(0, PC_TB);

        clk = 0;
        jump_en = 0;
        PC_Stall = 0;
        branch_en = 0;
        rst = 0;
        
        repeat(100) begin
            #50; clk = ~clk;
        end

        $fatal(1, "Timeout");

    end

    initial begin
        
        @ (posedge clk);
        @ (posedge clk);
        @ (posedge clk);
        rst = 1;
        @ (posedge clk);
        @ (posedge clk);
        @ (posedge clk);
         rst = 0;
        @ (posedge clk);
        @ (posedge clk);
         @ (posedge clk);
        PC_Stall = 1;
        @ (posedge clk);
        PC_Stall = 0;
        @ (posedge clk);
        
        @ (posedge clk);
        
        

    end

    PC PC(.clk(clk), .rst(rst), .PC_JVal(PC_JVal), .jump_en(jump_en), .branch_en(branch_en), .PC_Stall(PC_Stall), .PC_Out(PC_Out), .fetch_stall(fetch_stall), .active(active), .check(check));

endmodule

The issue I'm having is that how the if statement checking for PC_Stall is evaluated seems to alternate between clock cycles and I have no clue why.

I get the following VCD output when running it with the test bench as is (not the desired output), with the PC Stall not really happening (the PC value should remain for 2 cycles, but here it is only for one.)

Stall lasts 1 Cycle

Then by just shifting the point at which the PC_Stall is asserted forward by one cycle, results in Stall lasting 3 cycles, even though its only asserted for 1.

Stall lasts 3 cycles

I've been really stuck on this and genuinely have no idea what is wrong, and I would appreciate the help.


Solution

  • So it appears to be a compiler issue regarding how conditionals are treated when both inputs to said conditionals change and the conditionals themselves are executed on a positive clock edge.

    The issue was fixed by adding a small delay just before said conditional, to give the values time to update or something, not sure and this seems like quite a botched solution, it works though.