设计 32 位算术逻辑单元 (ALU)

Design 32 bit arithmetic logic unit (ALU)

我为 ALU 编写了这个编码器。该 ALU 由 ctrl 信号控制,并执行一些工作,例如加、减和或...当输出为零时,oZero 信号应该处于活动状态。

我在标记的行中有一些错误。我的错误是什么?

module ALU_32 (iA, iB ,iCin ,ctrl, oCarry,oZero, out);    
input [31:0] iA,iB;
output [31:0] out;
input iCin,ctrl;
output oCarry,oZero;
reg [31:0] out;
reg oCarry;
reg oZero;
always@ (ctrl)
    begin 
        case(ctrl)
            4'b0: out<=iA&iB;
            4'b0001: out<=iA|iB;
            4'b0010: {oCarry ,out}<=iA+iB;
            4'b0011: out<=iA~|iB;    //error
            4'b0100: 
            begin 
                if(iA==iB)
                    out<=32'b1;
            end  
            4'b0101: out<=iA-iB;  //error
            4'b0110:    //error
            begin 
                if(iA<iB)
                    out<=32'b1;
                else
                    out<=32'b0;
                end
            4'b0111:   out<=iA*iB;     //error
            4'b1000: out<=iA/iB;        //error

        end
       always@(out)
           begin
               if(out==0)
                   oZero<=1;
               end

      endmodule

~| 您在 4'b0011: out<=iA~|iB; 中使用的运算符被您的 IDE 视为归约运算符而不是 NOR 运算。要解决这个问题,您可以使用例如以下构造:

out <= ~(iA | iB);

第二个问题是您忘记在 case 构造的末尾使用 endcase 关键字。

检查 edaplayground 以查看应用到您的代码的更改。

正如 Qiu 指出的那样,iA~|iB 应该是 ~(iA|iB) 而你少了一个 endcase。除此之外:

  1. ctrl 需要 4 位宽。 IE input [3:0] ctrl
  2. 组合块需要在敏感列表中声明所有元素或使用auto-sensitivity。使用 always @(ctrl) 时,模拟器将不会使用 iAiB 中的更改。而是使用 always @* 代替 auto-sensitivity。对于组合逻辑,你应该始终使用 auto-sensitivity,除非你仅限于 1995 版的 IEEE Std 1364(很可能你不是),在这种情况下你需要 always @(ctrl or iA or iB or iCin)。 Auto-sensitivity(@*@(*))于 2001 年添加到标准中。
  3. 您不应将 non-blocking 赋值 (<=) 与组合逻辑一起使用,而应使用阻塞赋值 (=)。 Non-blocking 应该用于分配 flip-flops 和锁存器。
  4. outoCarry 没有在每个条件下都被分配一个已知值,这将它们推断为锁存器。锁存逻辑设计容易出现时序问题,需要谨慎使用。大多数 FPGA 的锁存器数量有限,有些有一些。更改编码风格可以删除推断的锁存器,主要有两种方法。

    1. 在每个条件下分配输出。确保在 case-statement:

      中声明了默认条件
      always @* begin
        case(ctrl)
        4'b0000 :
          begin
            out = iA&iB;
            oCarry = 1'b0;
          end
        // conditions assigning both 'out' and 'oCarry'
        default:
          begin
            out = 32'0;
            oCarry = 1'b0;
          end
        endcase
      end
      
    2. 在case-statement之前将值赋给默认值。 case-statement 将覆盖默认值。

      always @* begin
        // default value
        out = 32'd0;
        oCarry = 1'b0;
        // calculate value, override default
        case(ctrl)
          4'b0000 : out = iA&iB;
          // ...
          4'b0010: {oCarry ,out} = iA+iB;
          // ...
          4'b1000: out = iA/iB;
        endcase
      end
      
  5. oZero也是推断闩锁。只能赋值给1,没有路径给0。你可以:

    • always @* oZero = (out==0);
    • 将 always 块与下面的 oZero = (out==0); 合并 endcase
    • assign oZero = (out==0); // Make sure 'oZero' is a wire (not reg)

其他建议(可选)

  1. ANSI 样式 header。添加到 IEEE Std 1364-2001 中,并得到所有现代模拟器和合成器的支持。它比 IEEE Std 1364-1995 的 Non-ANSI 样式更紧凑。

    module ALU_32 (    
      input [31:0] iA, iB,
      input        iCin,
      input [3:0]  ctrl,
      output reg  oCarry, oZero,
      output reg [31:0] out );
    
  2. 您在 iA*iBiA/iB 错误的地方提出了建议。在verilog中,没有错。综合工具可能存在问题,因为 32 位 x 32 位乘法器/除法器往往会占用大量资源。您可能需要在单独的模块中流水线操作。 FPGA可能有预定义模块,所以查资料sheet.