我有一个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
,除非存在真正的“气泡”或硬件引起的无操作,但延迟仍然存在,否则解决了偏移问题。
有人可以向我解释导致延迟的原因及其发生的原因吗?
答案 0 :(得分:2)
答案是在PCLogic模块中使用状态会导致额外的传播延迟。通过删除PCLogic中的寄存器,我们删除模块本身中的隐式状态步骤,将其传播降低到可忽略的0。
所以答案是根据声明性表达式将pc_next
块由always @(pc)
块计算为1:
wire [15:0] pc_next = (reset == 1)? 0 : (branch == 1 && alu_zero == 1)? pc+2+(signext << 1) : pc+2;
通过将电路更改为组合电路,我们不再需要存储状态,从而减少了我们工艺中的“缓冲区”。 PC现在可以在(T)时间而不是(2T)更新。
答案 1 :(得分: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时,很难读取assign语句。
注意这个
pc_next = pc+2; // latch will be inferred without this
组合块应具有默认值。如果条件语句中没有定义默认值,它将保留其值并导致不正确的行为。组合块不能保留值。
有关意外锁定的详细信息,请参阅this。