2

我有一个 16 位单周期、非常稀疏的 MIPS 实现,我一直在 Verilog 中工作。除了分支延迟一个完整的时钟周期之外,一切正常。

always @(posedge clock) begin
    // Necessary to add this in order to ensure PC => PC_next
    iaddr <= pc_next 
end

上面的代码用于更新程序计数器/指令地址,它来自一个模块,PCLogic:

module PCLogic(
        pc_next,    // next value of the pc
        pc,     // current pc value
        signext,    // from sign extend circuit
        branch, // beq instruction
        alu_zero,   // zero from ALU, used in cond branch
        reset       // reset input
        );

output [15:0] pc_next;
input [15:0] pc;
input [15:0] signext;  // From sign extend circuit
input branch;
input alu_zero;
input reset;

reg [15:0] pc_next; 

    always @(pc or reset) begin
        if (reset == 1)
            pc_next = 0;
        else if (branch == 1 && alu_zero == 1)
            pc_next = pc+2+(signext << 1);
        else
            pc_next = pc+2;
    end

endmodule

iaddr是一个简单的 16 位寄存器,用于存储程序计数器。

我不明白为什么这个电路可能有问题,但由于某种原因,整个电路延迟了一个时钟周期,直到它分支(例如,如果我在 0x16 处有一条 BEQ 指令总是跳转,它将执行0x18 处的下一条指令,然后跳转到相对偏移量,但从 0x20 开始)。

我几乎可以感觉到解决方案就在我面前,但我不知道我在语义上缺少什么。如果我删除总是隐含的偏移量问题+2,除非存在真正的“气泡”或硬件引起的无操作,但延迟仍然存在。

有人可以向我解释导致延迟的原因以及发生的原因吗?

4

2 回答 2

2

答案是在 PCLogic 模块中使用状态会导致额外的传播延迟。通过删除 PCLogic 中的寄存器,我们删除了模块本身中的隐式状态步骤,将其传播减少到可以忽略不计的 0。

因此,答案是将块pc_next计算更改为always @(pc)基于声明性表达式的块:

wire [15:0] pc_next = (reset == 1)? 0 : (branch == 1 && alu_zero == 1)? pc+2+(signext << 1) : pc+2;

通过将我们的电路变成一个组合电路,我们不再需要存储状态,从而减少了我们过程中的“缓冲区”。PC 现在可以在 (T) 时间内更新,而不是 (2T)。

于 2015-12-02T04:12:45.537 回答
2

编写组合电路的另一种方法:

reg [15:0] pc_next; 

always @* begin
    if (reset == 1)
        pc_next = 0;
    else if (branch == 1 && alu_zero == 1)
        pc_next = pc+2+(signext << 1);
    else
        pc_next = pc+2; // latch will be inferred without this
end

当您的组合电路变得更加复杂时,您将需要它,因为当有很多嵌套的 if-else 时,分配语句很难阅读。

请注意这一点

pc_next = pc+2; // latch will be inferred without this

组合块应该有一个默认值。当条件语句中没有定义默认值时,它将保留其值并导致不正确的行为。组合块不得包含值。

有关意外闩锁的更多信息,请参阅

于 2015-12-02T04:35:58.757 回答