实验二 算术逻辑单元的设计与实现
实验资料 实验源码

实验目的

  1. 掌握全加器和行波进位加法器的结构;
  2. 熟悉加减法运算及溢出的判断方法;
  3. 掌握算术逻辑单元(ALU)的结构;
  4. 熟练使用 SystemVerilog HDL 的行为建模和结构化建模方法对 ALU 进行 描述实现;
  5. 为“单周期 MIPS 处理器的设计与实现”奠定基础。

实验内容

基于 SystemVerilog HDL 设计并实现一个 4 位 ALU 单元。整个工程的顶层 模块如图 2-4 所示,输入/输出端口如表 2-2 所示。注意,顶层模块由两个子模 块组成,其中,一个是 ALU 单元,另一个是 7 段数码管动态显示扫描单元。同 学们只需要实现 ALU 单元即可,动态显示扫描单元在工程中直接提供。

完成上述 ALU 单元的设计,必需满足如下几点要求: 1. ALU 单元的输入 A 和 B 均是补码形式。 2. 实现加法和减法时,不能使用“+”和“-”两种运算符,且只能通过一个行波 进位加法器和其它必要的逻辑电路实现。 3. 可以使用“*”运算符实现乘法,但该运算符在只适用无符号数的乘法,有 符号数的乘法需要同学们考虑如何处理。 4. 实现算术右移时,可以使用运算符“>>>”。

实验步骤

  1. 解压缩 ALU_4bits_stu.rar,打开教师提供的工程文件 ALU_4bits.xpr。目前工程中已经提供了 7 段数码管动态扫描电路的网表文件 ( xseg7_d.edif ) 和 端 口 声 明 文 件 ( xseg7_d_stub.sv ), 以 及 顶 层 文 件 (ALU_4bits.sv)。前两个有关 7 段数码管的文件无需做任何修改。同学们只 需设计实现图 2-4 中的 ALU 单元;
  2. 在工程中,添加文件 fulladder.sv,实现全加器;
1
2
3
4
5
6
7
8
9
10
11
`timescale 1ns / 1ps
module fulladder(
input logic A,
input logic B,
input logic Cin,
output logic S,
output logic Cout
);
assign S = A^B^Cin;
assign Cout = A&B|(Cin&(A^B));
endmodule
  1. 在工程中,添加文件 rca.sv,调用全加器,实现 4 位行波进位加法器;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
`timescale 1ns / 1ps
module rca(
input logic [3:0]A,
input logic [3:0]B,
input logic Cin,
output logic [3:0]S,
output logic Cout
);
logic [3:0]tempCout;
fulladder fa1(A[0],B[0],Cin,S[0],tempCout[0]);
fulladder fa2(A[1],B[1],tempCout[0],S[1],tempCout[1]);
fulladder fa3(A[2],B[2],tempCout[1],S[2],tempCout[2]);
fulladder fa4(A[3],B[3],tempCout[2],S[3],tempCout[3]);
assign Cout=tempCout[3];
endmodule
  1. 在工程中,添加文件 alu.sv,通过调用行波进位加法器和添加必要的逻辑电路实现实验指导书中表2-3所示的所有运算操作。根据顶层文件对 ALU 单元的调用形式;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
`timescale 1ns / 1ps
module alu(
input logic [3:0]A,
input logic [3:0]B,
input logic [3:0]aluop,
output logic [7:0]alures,
output logic ZF,//isZero
output logic OF//overflow
);
logic Cout1;
logic [3:0]re1;
rca r1(A,B,0,re1,Cout1);
logic Cout2;
logic [3:0]re2;
rca r2(A,~B+1,0,re2,Cout2);
logic Cout3;
logic [3:0]re3;
rca r3(~A+1,B,0,re3,Cout3);
logic Cout4;
logic [3:0]re4;
rca r4(~A+1,~B+1,0,re4,Cout4);
always @(*) begin
alures=0;
ZF=0;
OF=0;
case (aluop)
4'b0000:alures=A&B;
4'b0001:alures=A|B;
4'b0010:alures=A^B;
4'b0011:begin
alures=~(A&B);
alures[7:4]=4'b0000;
end
4'b0100:begin
alures=~A;
alures[7:4]=4'b0000;
end
4'b0101:alures=A<<B[2:0];
4'b0110:alures=A>>B[2:0];
4'b0111:begin
alures=$signed(A)>>>B[2:0];
alures[7:4]=4'b0000;
end
4'b1000:alures=A*B;
4'b1001:alures=$signed(A)*$signed(B);
4'b1010:begin
if(A[3]==0) begin
if(B[3]==0) alures=re1;
else alures=re2;
end
else begin
if(B[3]==0) alures=re3;
else alures=re4;
end
// alures=~(alures-1);
alures[7:4]=4'b0000;
if(Cout1==1) OF=1;
else OF=0;
end
4'b1011:begin
alures=re1;
end
4'b1100:begin
if(A[3]==0) begin
if(B[3]==0) alures=re2;
else alures=re1;
end
else begin
if(B[3]==0) alures=re4;
else alures=re3;
end
// alures=~(alures-1);
alures[7:4]=4'b0000;
if(Cout2==1) OF=1;
else OF=0;
end
4'b1101:begin
alures=re2;
end
4'b1110:alures=($signed(A)<$signed(B))?1:0;
4'b1111:alures=(A<B)?1:0;
default: ;
endcase
if(alures==0) ZF=1;
else ZF=0;
end
endmodule
  1. 在顶层文件 ALU_4bits.sv 通过结构化建模;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
`define SIM
module ALU_4bits(
input sys_clk,
input sys_rst_n,
input [3 : 0] A,
input [3 : 0] B,
input [3 : 0] aluop,
output ov,
output zero,
output logic [3 : 0] an,
output logic [7 : 0] a_to_g
);

logic [7:0]alures;
logic [3:0]alures0;
logic [3:0]alures1;

assign alures0=alures[3:0];
assign alures1=alures[7:4];

alu myALU(
.A(A),
.B(B),
.aluop(aluop),
.alures(alures),
.ZF(zero),
.OF(ov)
);

x7seg_d U1(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.Y0 (alures0),
.Y1 (alures1),
.an (an),
.a_to_g (a_to_g)
);

endmodule
  1. 添加测试文件 ALU_4bits_tb.sv,对所实现的 ALU 单元进行行为仿真(注意: 仅对 alu.sv 进行仿真即可,不要仿真整个系统)。仿真过程中必须采用自动化 测试的方法(注:读取文件时,如果不使用绝对路径,请将存放测试向量的 文本文件拷贝到“工程路径/ALU_4bits.sim/sim_1/ behave/xsim”目录下,否则 读取文件将失败);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
`timescale 1ns / 1ps
module alu_tb(
);
logic [3 : 0] A;
logic [3 : 0] B;
logic [3 : 0] aluop;
logic [7 : 0] alures;
logic [7 : 0] aluresexpected;
logic [21 : 0] stim[15:0];
logic OFexpected;
logic ZFexpected;
logic ZF;
logic OF;
integer i;
alu DUT(.A(A),.B(B),.alures(alures),.aluop(aluop),.ZF(ZF),.OF(OF));

initial begin
$readmemb("alu_test.txt",stim);
for(i=0;i<16;i=i+1)begin
{A,B,aluop,aluresexpected,OFexpected,ZFexpected}=stim[i];#20;
if(alures==aluresexpected) $display($time," Test A=%b, B=%b, aluop=%b, alures=%b, aluresecpected=%b, OF=%b, ZF=%b Pass!",A,B,aluop,alures,aluresexpected,OFexpected,ZFexpected);
else $display($time," Test A=%b, B=%b, aluop=%b, alures=%b, aluresecpected=%b, OF=%b, ZF=%b Wrong!",A,B,aluop,alures,aluresexpected,OFexpected,ZFexpected);
end
end

endmodule
  1. 如果第 6 步行为仿真通过,则对工程进行综合、实现、生成 bin 文件。注意, 约束文件已在工程中提供,不需要再添加;

  2. 登录远程 FPGA 硬件云平台,直接导入教师提供的验证平台文件(4 位算术逻 辑单元(ALU).epl),无需进行绘制,验证平台如图 2-6 所示。其中操作数 A、 B 以及操作类型控制信号 aluop 通过多位输入控件进行控制;使用 4 位七段 数码管显示 ALU 的运算结果(最低位数码管用于显示非乘法结果或乘法结果 module alu( input [3 : 0] A, input [3 : 0] B, input [3 : 0] aluop, output logic [7 : 0] alures, output logic ZF, output logic OF ); 的低 4 位,次低位数码管显示乘法结果的高 4 位,剩下两位数码管在本实验 中不被点亮);使用 2 个 LED 灯显示溢出标志 ov 和零标志 zero。烧写所生成 的 bin 文件,然后运行实验,通过调整操作数 A、B 和 aluop 的值验证所设计 ALU 的正确性。

实验结果

仿真结果

2

波形图

3

FPGA

4

实验思考

  1. 画出实现加/减法运算的逻辑电路原理图,并说明为什么加/减法可以只 使用一个加法器进行实现?

对于无符号数加法

1

上图为全加器的逻辑电路原理图,本实验的行波进位加法器由全加器构成,通过前一个全加器的Cout与后一个全加器的Cin进行连接。对于无符号数加法,就是将两个无符号数A,B输入即可得到结果。

对于无符号数减法

将-B变为+(-B),所以在加法器的输入端输入A,~B+1即可。

对于有符号数加减法

与无符号数减法同理,只不过最高位变为符号位,计算方法相同。根据不同情况调整输入即可。

减法可以看成加上一个数的相反数,即A-B=A+(-B),而-B的补码是B的补码取反加1,所以可以使用一个加法器实现。

  1. 给出有符号数加/减法溢出的判断规则?

通过全加器的Cout来判断,当Cout为1时一定溢出,当Cout为0时不一定溢出,此时要判断这个0是否为原本为1的最高位再+1得来,如果是这种情况,则说明溢出,否则不溢出。