设计 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
。除此之外:
ctrl
需要 4 位宽。 IE input [3:0] ctrl
- 组合块需要在敏感列表中声明所有元素或使用auto-sensitivity。使用
always @(ctrl)
时,模拟器将不会使用 iA
或 iB
中的更改。而是使用 always @*
代替 auto-sensitivity。对于组合逻辑,你应该始终使用 auto-sensitivity,除非你仅限于 1995 版的 IEEE Std 1364(很可能你不是),在这种情况下你需要 always @(ctrl or iA or iB or iCin)
。 Auto-sensitivity(@*
或 @(*)
)于 2001 年添加到标准中。
- 您不应将 non-blocking 赋值 (
<=
) 与组合逻辑一起使用,而应使用阻塞赋值 (=
)。 Non-blocking 应该用于分配 flip-flops 和锁存器。
out
和 oCarry
没有在每个条件下都被分配一个已知值,这将它们推断为锁存器。锁存逻辑设计容易出现时序问题,需要谨慎使用。大多数 FPGA 的锁存器数量有限,有些有一些。更改编码风格可以删除推断的锁存器,主要有两种方法。
在每个条件下分配输出。确保在 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
在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
oZero
也是推断闩锁。只能赋值给1,没有路径给0。你可以:
always @* oZero = (out==0);
- 将 always 块与下面的
oZero = (out==0);
合并 endcase
assign oZero = (out==0); // Make sure 'oZero' is a wire (not reg)
其他建议(可选)
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 );
您在 iA*iB
和 iA/iB
错误的地方提出了建议。在verilog中,没有错。综合工具可能存在问题,因为 32 位 x 32 位乘法器/除法器往往会占用大量资源。您可能需要在单独的模块中流水线操作。 FPGA可能有预定义模块,所以查资料sheet.
我为 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
。除此之外:
ctrl
需要 4 位宽。 IEinput [3:0] ctrl
- 组合块需要在敏感列表中声明所有元素或使用auto-sensitivity。使用
always @(ctrl)
时,模拟器将不会使用iA
或iB
中的更改。而是使用always @*
代替 auto-sensitivity。对于组合逻辑,你应该始终使用 auto-sensitivity,除非你仅限于 1995 版的 IEEE Std 1364(很可能你不是),在这种情况下你需要always @(ctrl or iA or iB or iCin)
。 Auto-sensitivity(@*
或@(*)
)于 2001 年添加到标准中。 - 您不应将 non-blocking 赋值 (
<=
) 与组合逻辑一起使用,而应使用阻塞赋值 (=
)。 Non-blocking 应该用于分配 flip-flops 和锁存器。 out
和oCarry
没有在每个条件下都被分配一个已知值,这将它们推断为锁存器。锁存逻辑设计容易出现时序问题,需要谨慎使用。大多数 FPGA 的锁存器数量有限,有些有一些。更改编码风格可以删除推断的锁存器,主要有两种方法。在每个条件下分配输出。确保在 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
在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
oZero
也是推断闩锁。只能赋值给1,没有路径给0。你可以:always @* oZero = (out==0);
- 将 always 块与下面的
oZero = (out==0);
合并endcase
assign oZero = (out==0); // Make sure 'oZero' is a wire (not reg)
其他建议(可选)
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 );
您在
iA*iB
和iA/iB
错误的地方提出了建议。在verilog中,没有错。综合工具可能存在问题,因为 32 位 x 32 位乘法器/除法器往往会占用大量资源。您可能需要在单独的模块中流水线操作。 FPGA可能有预定义模块,所以查资料sheet.