verilog 如何将输入值处理为 always_ff 块中的 if 语句

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

我目前正在使用 Icarus Verilog 开发流水线 MIPS cpu,并且在 always_ff 循环中使用 if 语句时遇到了一些非常奇怪的行为。我目前正在测试这个 PC 块的实现:

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

并且是 运行 以下测试台:

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

我遇到的问题是,如何评估检查 PC_Stall 的 if 语句似乎在时钟周期之间交替,我不知道为什么。

当运行它与测试台保持原样(不是期望的输出)时,我得到以下 VCD 输出,PC 停顿并没有真正发生(PC 值应该保持 2 个周期,但在这里只有一个。)

Stall lasts 1 Cycle

然后通过将 PC_Stall 断言的点向前移动一个周期,导致 Stall 持续 3 个周期,即使它只断言 1.

Stall lasts 3 cycles

我一直被困在这个问题上,真的不知道哪里出了问题,非常感谢您的帮助。

iverilog 还没有很好地支持 SystemVerilog 功能。如果你在其他模拟器上编译你的代码,比如 edaplayground 上的 VCS,你会得到编译错误。例如:

Error-[ICPD] Illegal combination of drivers
  Illegal combination of procedural drivers
  Variable "check" is driven by an invalid combination of procedural drivers. 
  Variables written on left-hand of "always_ff" cannot be written to by any 
  other processes, including other "always_ff" processes.
  This variable is declared at : logic [2:0] check;
  The first driver is at : always_ff @(posedge clk) begin
  check[1] <= (~check[1]);
   ...
  The second driver is at : check = 0;

您必须修复所有此类错误。

请注意,如果您注册一个免费帐户,edaplayground 上可以使用多个模拟器。

因此,当所述条件的两个输入都发生变化并且条件本身在正时钟边沿执行时,关于如何处理条件似乎是一个编译器问题。

问题已通过在所述条件之前添加一个小延迟来解决,以便为值提供更新时间或其他东西,不确定,这似乎是一个拙劣的解决方案,但它有效。

这可能不是一个完整的答案,但我有理由相信您在 PC_Stall 和 active 之间存在循环依赖。通常当我有奇怪的行为时,这是因为我有一个奇怪的循环依赖,如您的代码中所述。