【问题标题】:Verilog: if statement unexpected behaviorVerilog:if语句意外行为
【发布时间】: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

a screen shot of my waveform

更新: 仍然没有对非阻塞赋值做任何事情,但我确实修改了更新 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 &lt;= PC + Imm_32;
  • 你试过ALU_out = ((ALU_in1) == (ALU_in2)) ? 1 : 0。万一,ALU_OUT 中的任何先前的1 仍然存在..?一个替代可能是if( ((ALU_in1) == (ALU_in2)) ) begin ALU_out&lt;=1;PC&lt;=PC+...; end。尝试使用非阻塞分配和一些调试消息。
  • Morgan 可能是正确的,您将组合逻辑和顺序逻辑混合在一个块中,这会导致不良行为。您可能应该将PC 的实际分配分开到它自己的顺序块中,并组合确定nextPC
  • @sharvil111 感谢您的回复。我实际上也尝试过!我的电脑还是变成了 7 (1+6)
  • @Morgan 感谢您的回复。实际上我有 2 个 always 块:always @(posedge clk) 在其中获取指令并确定其对应的 ALU_Op(这是解码部分的输入)下一个总是对此 ALU_Op 敏感(以及操作数以防万一两个连续的指令是相同的)。现在,这个 if 总是发生在这个里面

标签: if-statement mips verilog


【解决方案1】:

现在我们知道您希望它是单周期,这说明了很多。

首先,您需要确定您的寄存器是否用于与内存通信,您是将访问指令内存的地址放入寄存器还是生成的指令,因为现在使用您的 always @(*) IR &lt;= Instruction; endalways @(posedge clk) begin PC = nextPC end,您是做一个奇怪的两者结合。您需要执行以下任一操作:

always @(posedge clk) begin
  IR <= Instruction;
  PC <= nextPC;
end

并使用nextPC 或:

always @(posedge clk) begin
  PC <= nextPC;
end

assign IR = Instruction;

并使用PC 寻址指令内存。请注意,摩根关于从内存读取应该花费一个周期的评论通常是正确的,但是,我们将假设您有某种不需要锁存地址和输出/输入的组合读取内存。不过,在实际系统中,您确实应该准备好在地址和输入/输出总线上使用寄存器。

其他一些风格说明:

  • 最好不要编写自己的敏感度列表,使用always @(*),而不是always @(IR) 用于组合逻辑(使用always @(posedge clk) 用于顺序逻辑。
  • always @(*)(组合)块中使用阻塞分配(=),在always @(posedge clk)(顺序)块中使用NBA(&lt;=)。
  • 使用 case 语句而不是一长串 if .. if .. if .. 来解码操作码。它更容易阅读
  • 我建议将操作码替换为参数或宏,以便代码更容易阅读(例如,而不是 if (op == 6'b001001),你会得到 parameter ALU_ADD = 6'b001001; ... if (op == ALU_ADD),它更清晰;在 case 语句 case (op) ALU_ADD: ... ALU_SUB: ... ALU_OR: ... endcase 中更好)

【讨论】:

  • 非常感谢!顺便说一句,不错的提示:)好吧,我确实改变了IR的东西(我把它做成了一根电线并使用了assign语句)我仍然得到了相同的波形! :( 在分支或跳转的情况下,PC 会正确更改,但会获取以下指令而不是 PC 指向的位置(分支目标)!
  • 与我链接的第二个波形屏幕截图相同)
  • 哦,顺便说一句,我使用
  • @LadyMillionM 指令内存是什么样的?如果需要一个周期才能访问,这个模型必须改变。
  • IP 内核内存块-单端口 RAM,宽度 32 位,长度 1024。我很确定这里需要不到一个周期。当 if 语句(错误地)执行时,从 inst 内存中获取 inst 的延迟在我的波形中可见,大约为 100 ps(我的时钟周期现在是 20 ns)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-28
  • 2018-01-09
  • 2015-02-15
  • 2010-11-01
  • 2016-04-02
  • 1970-01-01
相关资源
最近更新 更多