【发布时间】:2015-11-14 11:29:32
【问题描述】:
我在 Verilog 中实现 MIPS 数据路径(行为),当我模拟我的代码时,行为是意外的。 这里显示了 BEQ 和 BNE(相等/不相等时的分支)指令的情况。 ALU_Out 判断两个寄存器是否相等。这些都不重要,因为我的问题基本上是如何真正跳过我的病情。因此,我从波形中的信号来看,ALU_out 是 零,但是 if (ALU_Out == 32'd1) 内部发生的任何事情都会执行一次。这样我的电脑就变成了 PC+6。现在如果我的 ALU_Out 实际上等于 1,PC 变为 PC+6+6(它是 1 ==> 变为 13)我还在这个 if 语句中集成了一些 kinna 标志并确保它执行一次。 即使我在 if 中添加了 else,我的标志也清楚地表明 if 在 else 之前已经执行过一次。 同样的事情发生在 BNE 的情况下,并且在 if 语句执行一次后检查所需的条件。 你能告诉我这段代码有什么问题吗? 非常感谢。
if (ALU_op == 6'd30) //BEQ
begin
ALU_out = ((ALU_in1) == (ALU_in2));
if (ALU_out == 32'd1)
begin
PC = PC + Imm_32;
end
end
if (ALU_op == 6'd31) //BNE
begin
ALU_out = ((ALU_in1) == (ALU_in2));
if (ALU_out == 0)
begin
PC = PC + Imm_32;
end
end
更新: 仍然没有对非阻塞赋值做任何事情,但我确实修改了更新 PC 的方式,将 next_PC 添加到我的代码中。If 语句问题消失了。尽管如此,问题是当发生跳转或分支时,下一个 PC 被正确计算,但分支目标处的指令不会被相同的时钟获取! please look at this photo
这应该是一个单周期 MIPS 处理器,因此应该在一个周期内获取并执行一条指令。这就是为什么不希望由非阻塞分配引起的延迟!
always @(*)
begin
IR <= Instruction;
end
always @(posedge clk)
begin
PC = next_PC;
OverFlow = 0;
end
// Decode + Operand Fetch //
always @ (IR)
begin
Op_code = IR[31:26];
if (Op_code == 6'd0) //R-type Instructions
begin
Func = IR[5:0];
if (Func == 6'b100000) //ADD
begin
ALU_op = 6'd1; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
read_addr2 = IR [20:16]; //Rt
end //end of ADD
if (Func == 6'b100001) //ADDU
begin
ALU_op = 6'd2; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
read_addr2 = IR [20:16]; //Rt
end //end of ADDU
/* some code here skipped to make the code shorter*/
if (Op_code == 6'b000100) //BEQ (Branch if Equal)
begin
ALU_op = 6'd30; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
read_addr2 = IR [20:16]; //Rt
Imm_32 = {{16{IR[15]}},IR [15:0]}; //Offset 2nd operand: imm-32 (Sign Extended Imm_16)
//BT = PC + Imm_32;
end
if (Op_code == 6'b000101) //BNE (Branch if NOT Equal)
begin
ALU_op = 6'd31; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
read_addr2 = IR [20:16]; //Rt
Imm_32 = {{16{IR[15]}},IR [15:0]}; //Offset 2nd operand: imm-32 (Sign Extended Imm_16)
end
if (Op_code == 6'b000001)
begin
if ( IR [20:16] == 5'b00001) //BGEZ (Branch on Greater than or Equal to Zero)
begin
ALU_op = 6'd33; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
Imm_32 = {{16{IR[15]}},IR [15:0]}; //Offset 2nd operand: imm-32 (Sign Extended Imm_16)
end
if (IR [20:16] == 5'b10000) //BLTZAL (Branch on less than Zero And Link)
begin
ALU_op = 6'd34; //as numbered in LAB manual
read_addr1 = IR [25:21]; //Rs
Imm_32 = {{16{IR[15]}},IR [15:0]}; //Offset 2nd operand: imm-32 (Sign Extended Imm_16)
end
end
if (Op_code == 6'b000010) //J (Jump)
begin
ALU_op = 6'd35; //as numbered in LAB manual
Imm_32 = {PC [31:26],IR [25:0]}; //Jump Address
end
if (Op_code == 6'b000011) //JAL (Jump And Link)
begin
ALU_op = 6'd36; //as numbered in LAB manual
Imm_32 = {PC [31:26],IR [25:0]}; //Jump Address
end
end // end of DECODE
// Execution & Write Back//
always @ (ALU_op, ALU_in1, ALU_in2)
begin
next_PC = PC+1;
wr_En = 0;
DM_wrEn_0 = 0;
DM_wrEn_1 = 0;
DM_wrEn_2 = 0;
DM_wrEn_3 = 0;
if (ALU_op == 6'd1) //ADD
begin
ALU_out = ALU_in1 + ALU_in2;
write_addr = IR [15:11]; //Rd
if( ( (ALU_in1[31]) && (ALU_in2[31]) && (!ALU_out[31]) )||( (!ALU_in1[31]) && (!ALU_in2[31]) && (ALU_out[31]) ) )
OverFlow = 1'b1;
wr_En = 1;
write_data = ALU_out;
end
if (ALU_op == 6'd2) //ADDU
begin
ALU_out = ALU_in1 + ALU_in2;
write_addr = IR [15:11]; //Rd
wr_En = 1;
write_data = ALU_out;
end
/* some code here skipped to make it shorter*/
if (ALU_op == 6'd30) //BEQ
begin
ALU_out = ((ALU_in1) == (ALU_in2));
if (ALU_out == 32'd1)
begin
//PC = PC + Imm_32;
next_PC = PC + Imm_32;
BT = 32'd56;
//PC = BT;
end
end
if (ALU_op == 6'd31) //BNE
begin
ALU_out = ((ALU_in1) == (ALU_in2));
if (ALU_out == 0)
begin
//PC = PC + Imm_32;
next_PC = PC + Imm_32;
BT = 32'd90;
end
end
if (ALU_op == 6'd33) //BGZE
begin
ALU_out = ((ALU_in1) >= 0);
if (ALU_out)
begin
//PC = PC + Imm_32;
next_PC = PC + Imm_32;
end
end
if (ALU_op == 6'd34) //BLTZAL
begin
ALU_out = ((ALU_in1) < 0);
if (ALU_out)
begin
write_addr = 5'd31; //$ra ($31)
write_data = PC;
wr_En = 1;
//PC = PC + Imm_32;
next_PC = PC + Imm_32;
end
end
if (ALU_op == 6'd32) //JR
begin
//PC = ALU_in1;
next_PC = ALU_in1;
end
if (ALU_op == 6'd35) //J
begin
//PC = Imm_32;
next_PC = Imm_32;
end
if (ALU_op == 6'd36) //JAL
begin
write_addr = 5'd31; //$ra
wr_En = 1;
write_data = PC;
//PC = Imm_32;
next_PC = Imm_32;
end
end //end of EXE and WB always block
【问题讨论】:
-
这是在组合过程 (
always @*) 还是顺序 (always @(posedge clk)) 中?如果正确编写PC = PC + Imm_32;应该真的是PC <= PC + Imm_32; -
你试过
ALU_out = ((ALU_in1) == (ALU_in2)) ? 1 : 0。万一,ALU_OUT 中的任何先前的1仍然存在..?一个替代可能是if( ((ALU_in1) == (ALU_in2)) ) begin ALU_out<=1;PC<=PC+...; end。尝试使用非阻塞分配和一些调试消息。 -
Morgan 可能是正确的,您将组合逻辑和顺序逻辑混合在一个块中,这会导致不良行为。您可能应该将
PC的实际分配分开到它自己的顺序块中,并组合确定nextPC。 -
@sharvil111 感谢您的回复。我实际上也尝试过!我的电脑还是变成了 7 (1+6)
-
@Morgan 感谢您的回复。实际上我有 2 个 always 块:always @(posedge clk) 在其中获取指令并确定其对应的 ALU_Op(这是解码部分的输入)下一个总是对此 ALU_Op 敏感(以及操作数以防万一两个连续的指令是相同的)。现在,这个 if 总是发生在这个里面
标签: if-statement mips verilog