在vivado上使用verilog语言设计32位ALU,包含16种不同的算数、逻辑、比较、移位运算。
32位ALU
- 顶层设计
- 输入:
- n0 ,第一个数据输入,32比特
- in1 ,第二个数据输入,32比特
- op ,ALU计算类型码,11比特
- 输出
- out ,输出,32比特
- overflow ,溢出标志,1比特
- zero ,零标志,1比特
- carryout ,进位标志,1比特
算数运算
运算 | OP编码 | 具体含义 |
---|
add | 00000100000 | 有符号数加法 |
addu | 00000100001 | 无符号数加法 |
sub | 00000100010 | 有符号数减法 |
subu | 00000100011 | 无符号数减法 |
逻辑运算
运算 | OP编码 | 具体含义 |
---|
and | 00000100100 | 按位与 |
or | 00000100101 | 按位或 |
xor | 00000100110 | 异或 |
nor | 00000100111 | 或非 |
比较运算
运算 | OP编码 | 具体含义 |
---|
slt | 00000101010 | slt $1,$2,$3 意为If($2<$3) $1=1 else $1=0;有符号数的比较 |
altu | 00000101011 | sltu $1,$2,$3 意为If($2<$3) $1=1 else $1=0;无符号数的比较 |
移位运算
运算 | OP编码 | 具体含义 |
---|
sll | 00000000000 | sll $1,$2,10 意为$1=$2<<10;左移10位 |
srl | 00000000010 | srl $1,$2,10 意为$1=$2>>10;右移10位 |
sra | 00000000011 | sra $1,$2,10 意为$1=$2>>10;注意符号位保留 |
sllv | 00000000100 | sllv $1,$2,$3 意为$1=$2<<$3 |
srlv | 00000000110 | srlv $1,$2,$3 意为$1=$2>>$3 |
srav | 00000000111 | srav $1,$2,$3 意为$1=$2>>$3;注意符号位的保留 |
设计代码
module alu_32(
reset, in0, in1, op, out, overflow, zero, carryout
);
input reset;
input[31:0] in0,in1;
input[10:0] op;
output[31:0] out;
output overflow,zero,carryout;
reg[31:0] out;
reg overflow,zero,carryout;
always@(*)
begin
if(reset)
begin
out=0;
overflow=0;
zero=0;
carryout=0;
end
else
alutask( in0, in1, op, out, overflow, zero, carryout);
end
task alutask;
input[31:0] in0,in1;
input[10:0] op;
output[31:0] out;
output overflow,zero,carryout;
reg[31:0] out;
reg overflow,zero,carryout;
begin
overflow=0;
carryout=0;
case( op )
11'b00000100000:
begin
out=$signed(in0)+$signed(in1);
overflow=in0[31]&in1[31] ^ in0[30]&in1[30];
end
11'b00000100001:
begin
if(in0[31]==1||in1[31]==1)
out=32'bx;
else
{carryout,out}=$unsigned(in0)+$unsigned(in1);
end
11'b00000100010:
begin
out=$signed(in0)-$signed(in1);
overflow=in0[31]&in1[31] ^ in0[30]&in1[30];
end
11'b00000100011:
begin
if(in0[31]==1||in1[31]==1)
out=32'bx;
else
{carryout,out}=$unsigned(in0)-$unsigned(in1);
end
11'b00000100100: out=in0&in1;
11'b00000100101: out=in0|in1;
11'b00000100110: out=in0^in1;
11'b00000100111: out=~(in0|in1);
11'b00000101010: out=( $signed(in0)<$signed(in1) )? 1:0;
11'b00000101011: out=(in0<in1)? 1:0;
11'b00000000000: out=in0<<10;
11'b00000000010: out=in0>>10;
11'b00000000011: out=in0>>>10;
11'b00000000100: out=in0<<in1;
11'b00000000110: out=in0>>in1;
11'b00000000111: out=in0>>>in1;
endcase
zero=out==0;
end
endtask
endmodule
仿真文件
module addtest;
reg reset;
reg [31:0] in0,in1;
reg[10:0] op;
wire[31:0] out;
wire overflow,zero,carryout;
alu_32 unit(
.reset(reset),
.in0(in0),
.in1(in1),
.op(op),
.out(out),
.overflow(overflow),
.zero(zero),
.carryout(carryout)
);
initial
begin
#10 reset=1;
#10 reset=0;in0=32'd1; in1=32'd2;
for(op=11'b00000100000;op<11'b00000100111;op=op+1)
#20;
#20 op=11'b00000101010;
#20 op=11'b00000101011;
#20 op=11'b00000000000;
#20 op=11'b00000000010;
#20 op=11'b00000000011;
#20 op=11'b00000000100;
#20 op=11'b00000000110;
#20 op=11'b00000000111;
#10 reset=1;
#10 reset=0;in0=-32'd1; in1=32'd2;
for(op=11'b00000100000;op<11'b00000100111;op=op+1)
#20;
#20 op=11'b00000101010;
#20 op=11'b00000101011;
#20 op=11'b00000000000;
#20 op=11'b00000000010;
#20 op=11'b00000000011;
#20 op=11'b00000000100;
#20 op=11'b00000000110;
#20 op=11'b00000000111;
#100 $finish;
end
initial
$monitor ($time,,,"reset=%b in0=%b in1=%b op=%b out=%b overflow=%b zero=%b carryout=%b ",
reset,in0,in1,op,out,overflow,zero,carryout);
endmodule
电路图

仿真波形图

附上monitor监视器的监视结果(monitor语句)

扩展:由于vivado自带的加法器非常简单,如果直接使用该加法器的话,会造成加法结果延迟时间较长,因此可以考虑使用自己写的32位超前进位加法器,但是在组织上会复杂一些。
关于有无符号数溢出问题可以参见:有无符号数溢出判断
另:欢迎大家对博客内容批评指正。