算术逻辑单元(ALU)是 CPU 的核心部件,实现算术及逻辑运算。
实验目的
熟悉用算术、逻辑的 Verilog 编程,练习多模块自顶向下编程方法。
实验内容
实验1
设计一个 4 位算术逻辑单元,输入信号为:
两组 4 位数据输入信号(A3—A0,B3—B0),一个进位输入信号 Cin;
数据输出信号为:4 位数据信号(F3—F0), 一个进位输出信号 Cout。
以上数据均为无符号正整数。功能控制信号有:S1、S0、M。
当 M=0 时为逻辑运算,M=1 时为算术运算,S1、S0 的功能如表 1 所示。
实验2
在 BASYS3 开发板上实现上述设计,SW 选择可以自己确定。
例如,SW[15]对应:M,
SW[14:13]对应:S1 和 S0,
SW[12]、SW[3:0]、SW[7:4]分别对应:Cin、A[3:0]、B[3:0],
左侧第 2 个七段数码管的小数点表示 Cin,最右侧的小数点表示 Cout。
当 SW 为 1 时,其上面的 LED 点亮,否则熄灭。
开发板上的 4 个七段数码管用于显示十六进制的输入数据和输出数据。
如, S1=0,S0=0,M=0,A=1, F=E,显示为:
如, S1=0,S0=1,M=0,A=1, B=0,F=0,显示为:
如, S1=0,S0=0,M=1(加法),
A=1,B=2,(B 的小数点) Cin=1,(F 的小数点) Cout=0,F=4,显示为:
参考示意图
实验方案
整体思路:
→输入(SW 拨码开关)
→ALU 运算模块(逻辑/算术运算+运算结果整合)
→输出模块(七段数码管分时复用+数字转七段数码管)
→输出(七段数码管+LED 灯)
代码分析
输入(Basys3_Master.xdc)
## Switches
set_property PACKAGE_PIN V17 [get_ports {A[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {A[0]}]
set_property PACKAGE_PIN V16 [get_ports {A[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {A[1]}]
set_property PACKAGE_PIN W16 [get_ports {A[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {A[2]}]
set_property PACKAGE_PIN W17 [get_ports {A[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {A[3]}]
set_property PACKAGE_PIN W15 [get_ports {B[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {B[0]}]
set_property PACKAGE_PIN V15 [get_ports {B[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {B[1]}]
set_property PACKAGE_PIN W14 [get_ports {B[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {B[2]}]
set_property PACKAGE_PIN W13 [get_ports {B[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {B[3]}]
set_property PACKAGE_PIN W2 [get_ports {Cin}]
set_property IOSTANDARD LVCMOS33 [get_ports {Cin}]
set_property PACKAGE_PIN U1 [get_ports {S[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {S[0]}]
set_property PACKAGE_PIN T1 [get_ports {S[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {S[1]}]
set_property PACKAGE_PIN R2 [get_ports {M}]
set_property IOSTANDARD LVCMOS33 [get_ports {M}]
开关分配
- SW[3:0] <- A[3:0]
- SW[7:4] <- B[3:0]
- SW[14:13] <- S[1:0]
- SW [15] <- M
- SW [12] <- Cin
ALU 运算模块(mainDesign.sv)
变量定义
module mainDesign(
input logic [1:0] S, //运算功能控制器
input logic [3:0] A, //A输入
input logic [3:0] B, //B输入
input logic M, //逻辑or算术运算选择器
input logic Cin, //进位输入信号
input logic CLK100MHZ, //时钟频率
output logic [1:0] SL, //SW开关LED输出,下同
output logic [3:0] AL,
output logic [3:0] BL,
output logic ML,
output logic CinL,
output logic [7:0] a2g, //七位数码管-阴极
output logic [3:0] AN //七位数码管-阳极
);
logic [4:0] temp; //临时运算结果储存
logic nf; //负数标志
logic zf; //零标志
logic cf; //进位标志
logic ovf; //溢出标志
logic Cout; //进位输出信号
logic [3:0] F; //运算结果返回值
logic [15:0] x; //模块返回值
ALU模块
//ALU模块-运算部分
always_comb begin
cf=0;
ovf=0;
temp=5'b00000;
case(M)
1'b0: begin //M=0 逻辑运算
case(S)
2'b00: F= ~A; //F= not A
2'b01: F=A&B; //F= A and B
2'b10: F=A|B; //F= A or B
2'b11: F=A^B; //F= A xor B
default: F=A;
endcase
end
1'b1: begin //M=1 算术运算
case(Cin)
1'b0: begin //Cin=0
case(S)
2'b00: begin //F=A+B+0
temp={1'b0,A}+{1'b0,B}+{1'b0,1'b0};
F=temp[3:0];
cf=temp[4];
ovf=F[3]^A[3]^B[3]^cf;
end
2'b01: begin //F=A-B-0
temp={1'b0,A}-{1'b0,B}-{1'b0,1'b0};
F=temp[3:0];
cf=temp[4];
ovf=F[3]^A[3]^B[3]^cf;
end
endcase
end
1'b1: begin //Cin=1
case(S)
2'b00: begin //F=A+B+1
temp={1'b0,A}+{1'b0,B}+{1'b0,1'b1};
F=temp[3:0];
cf=temp[4];
ovf=F[3]^A[3]^B[3]^cf;
end
2'b01: begin //F=A-B-1
temp={1'b0,A}-{1'b0,B}-{1'b0,1'b1};
F=temp[3:0];
cf=temp[4];
ovf=F[3]^A[3]^B[3]^cf;
end
endcase
end
endcase
end
endcase
nf=F[3];
if(F==4'b0000) zf=1;
else zf=0;
end
- 运算规则基于实验内容建立
- ALU 整体通过 always 语句实现
- 分支结构通过 case 语句实现
- 参考项目: ppt 中的示例工程以及 GitHub 项目:
jrmoulton/Simple-ALU: A calculator in Verilog for the Basys3 FPGA (github.com)
LED 模块&返回模块
//LED模块-开关信号控制LED灯点亮
assign AL=A;
assign BL=B;
assign SL=S;
assign ML=M;
assign CinL=Cin;
//返回模块-运算结果返回给输出模块
assign x[15:12]=A;
assign x[11:8]=B;
assign x[3:0]=F;
assign Cout=cf;
assistDesign X7(.x(x),.Cin(Cin),.clk(CLK100MHZ),.a2g(a2g),.AN(AN),.Cout(Cout));
endmodule
输出模块
七段数码管分时复用(assistDesign.sv)
module assistDesign(
input logic [15:0] x,
input logic Cin,
input logic Cout,
input logic clk,
input logic clr,
output logic [7:0] a2g,
output logic [3:0] AN //数码管使能
);
logic [1:0] s; //选择哪个数码管
logic [7:0] digit;
logic [19:0] clkdiv;
assign s = clkdiv[19:18]; // count every 10.4ms
//4个数码管 4选1 (MUX44)
always_comb
case(s)
0: begin
if(Cout==0) digit=x[3:0]; //分配给F
else digit=x[3:0]+'h10; //进位标志
end
1: digit='hff; //显示等号
2: begin
if(Cin==0) digit=x[11:8]; //分配给B
else digit=x[11:8]+'h10; //进位标志
end
3: digit=x[15:12]; //分配给A
default: digit=x[3:0];
endcase
//4个数码管轮流点亮
always_comb
case(s)
0: AN=4'b1110;
1: AN=4'b1101;
2: AN=4'b1011;
3: AN=4'b0111;
default: AN=4'b1110;
endcase
//时钟分频器(20位二进制计数器)
always @(posedge clk, posedge clr)
if(clr == 1) clkdiv <= 0;
else clkdiv <= clkdiv + 1;
//实例化 7段数码管
Dec7Seg s7(.x(digit),.a2g(a2g));
endmodule
整体参考实验 2 相关,详见注释
数字转七段数码管(Dec7Seg.sv)
module Dec7Seg(
input logic [7:0] x,
output logic [7:0] a2g
);
assign AN = 4'b0000;
always_comb
case (x)
'h00: a2g = 8'b00000011; //数字并且不显示小数点,下同
'h01: a2g = 8'b10011111;
'h02: a2g = 8'b00100101;
'h03: a2g = 8'b00001101;
'h04: a2g = 8'b10011001;
'h05: a2g = 8'b01001001;
'h06: a2g = 8'b01000001;
'h07: a2g = 8'b00011111;
'h08: a2g = 8'b00000001;
'h09: a2g = 8'b00001001;
'h0a: a2g = 8'b00010001;
'h0b: a2g = 8'b11000001;
'h0c: a2g = 8'b01100011;
'h0d: a2g = 8'b10000101;
'h0e: a2g = 8'b01100001;
'h0f: a2g = 8'b01110001;
'h10: a2g = 8'b00000010; //数字并且显示小数点,下同
'h11: a2g = 8'b10011110;
'h12: a2g = 8'b00100100;
'h13: a2g = 8'b00001100;
'h14: a2g = 8'b10011000;
'h15: a2g = 8'b01001000;
'h16: a2g = 8'b01000000;
'h17: a2g = 8'b00011110;
'h18: a2g = 8'b00000000;
'h19: a2g = 8'b00001000;
'h1a: a2g = 8'b00010000;
'h1b: a2g = 8'b11000000;
'h1c: a2g = 8'b01100010;
'h1d: a2g = 8'b10000100;
'h1e: a2g = 8'b01100000;
'h1f: a2g = 8'b01110000;
'hff: a2g = 8'b11101101; //显示等号
default: a2g = 8'b00000000; //完全显示
endcase
endmodule
- 整体参考实验 2 相关和 GitHub 项目:
nganinho/Basys3_7segments (github.com) - 由于需要控制单个小数点,于是取消了小数点位由 DP 变量同一控制,而将其
整合入了 a2g 数组和其余数码管一同控制,依旧是高电平熄灭低电平点亮 - 等号的显示由特殊数值’hff 控制
输出(Basys3_Master.xdc)
## LEDs
set_property PACKAGE_PIN U16 [get_ports {AL[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AL[0]}]
set_property PACKAGE_PIN E19 [get_ports {AL[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AL[1]}]
set_property PACKAGE_PIN U19 [get_ports {AL[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AL[2]}]
set_property PACKAGE_PIN V19 [get_ports {AL[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AL[3]}]
set_property PACKAGE_PIN W18 [get_ports {BL[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {BL[0]}]
set_property PACKAGE_PIN U15 [get_ports {BL[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {BL[1]}]
set_property PACKAGE_PIN U14 [get_ports {BL[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {BL[2]}]
set_property PACKAGE_PIN V14 [get_ports {BL[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {BL[3]}]
set_property PACKAGE_PIN P3 [get_ports {CinL}]
set_property IOSTANDARD LVCMOS33 [get_ports {CinL}]
set_property PACKAGE_PIN N3 [get_ports {SL[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SL[0]}]
set_property PACKAGE_PIN P1 [get_ports {SL[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {SL[1]}]
set_property PACKAGE_PIN L1 [get_ports {ML}]
set_property IOSTANDARD LVCMOS33 [get_ports {ML}]
##7 segment display
set_property PACKAGE_PIN W7 [get_ports {a2g[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a2g[7]}]
set_property PACKAGE_PIN W6 [get_ports {a2g[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a2g[6]}]
set_property PACKAGE_PIN U8 [get_ports {a2g[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a2g[5]}]
set_property PACKAGE_PIN V8 [get_ports {a2g[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a2g[4]}]
set_property PACKAGE_PIN U5 [get_ports {a2g[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a2g[3]}]
set_property PACKAGE_PIN V5 [get_ports {a2g[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a2g[2]}]
set_property PACKAGE_PIN U7 [get_ports {a2g[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a2g[1]}]
set_property PACKAGE_PIN V7 [get_ports {a2g[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {a2g[0]}]
set_property PACKAGE_PIN U2 [get_ports {AN[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AN[0]}]
set_property PACKAGE_PIN U4 [get_ports {AN[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AN[1]}]
set_property PACKAGE_PIN V4 [get_ports {AN[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AN[2]}]
set_property PACKAGE_PIN W4 [get_ports {AN[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {AN[3]}]
总结与思考
- 在 ALU 中定义的输入输出变量需要和约束文件中定义的名称保持一致
- 在实验二中所有的小数点由 DP 变量统一控制点亮/熄灭,而本实验中则需要单独控制第二位和第四位的小数点,所以需要将原DP整合进入a2g数组统一控制,a2g 的长度从原来的 7 位二进制数变为 8 位二进制数
- 由于增加了小数点和等号的显示,实例化时需要返回 2 位十六进制数才能完整表示全部情况
- 收获:ALU 模块中 always 语句,case 语句,if 语句的使用,进行简单的逻辑与算术运算;输出模块中重写数码管约束文件和实例化文件