目录
实验目标及任务
实验目标:
1、了解8位有符号、无符号乘法器的实现原理。
2、使用Verilog实现8位无符号乘法器和有符号乘法器。
实验任务:
1、设计无符号乘法器,将两个8位无符号数相乘,得到一个16位无符号数。
2、设计有符号乘法器,将两个8位有符号数相乘,得到一个16位有符号数。
3、设计按键输入转换电路,设计七段显示器显示和控制电路: 通过8个按键输入两个4位的二进制乘数A和B,两个乘数和乘积的结果,分别送给多个七段显示器进行显示。
实验内容与结果记录
(在完成实验基础上额外完成步骤3-5 )
步骤1-2:
源代码:
module MULTU(
input clk, //乘法器时钟信号
input reset,
input [ 15:0] a, //输入a(被乘数)
input [15:0] b, //输入b(乘数)
output [ 31:0] z //乘积输出z
);
reg [ 31:0 ] temp; //申请寄存器
reg [ 31:0 ] stored0;
reg [ 31:0 ] stored1;
reg [ 31:0 ] stored2;
reg [ 31:0 ] stored3;
reg [31 :0] stored4;
reg [31 :0] stored5 ;
reg [ 31:0 ] stored6;
reg [ 31:0 ] stored7;
reg [ 31:0 ] stored8;
reg [ 31:0 ] stored9;
reg [ 31:0 ] stored10;
reg [ 31:0 ] stored11;
reg [31 :0] stored12;
reg [31 :0] stored13 ;
reg [ 31:0 ] stored14;
reg [ 31:0 ] stored15;
reg [ 31:0 ] add0_1;
reg [ 31:0 ] add2_3;
reg [ 31:0 ] add4_5;
reg [ 31:0 ] add6_7;
reg [ 31:0 ] add8_9;
reg [ 31:0 ] add10_11;
reg [ 31:0 ] add12_13;
reg [ 31:0 ] add14_15;
reg [ 31:0 ] add0t1_2t3;
reg [ 31:0 ] add4t5_6t7;
reg [ 31:0 ] add8t9_10t11;
reg [ 31:0 ] add12t13_14t15;
reg [ 31:0 ] add0t3_4t7;
reg [ 31:0 ] add8t11_12t15;
always @( posedge clk or negedge reset)
begin
if(reset) begin //reset 置零
temp <= 0;
stored0 <= 0 ;
stored1 <= 0 ;
stored2<= 0 ;
stored3<= 0 ;
stored4 <= 0 ;
stored5<= 0 ;
stored6<= 0 ;
stored7 <= 0 ;
stored8 <= 0 ;
stored9 <= 0 ;
stored10<= 0 ;
stored11<= 0 ;
stored12 <= 0 ;
stored13<= 0 ;
stored14<= 0 ;
stored15 <= 0 ;
add0_1<= 0;
add2_3<= 0;
add4_5 <= 0;
add6_7 <= 0;
add8_9<= 0;
add10_11<= 0;
add12_13 <= 0;
add14_15<= 0;
add0t1_2t3<= 0;
add4t5_6t7<= 0;
add8t9_10t11<= 0;
add12t13_14t15<= 0;
add0t3_4t7<= 0;
add8t11_12t15<= 0;
end
else begin //通过字符拼接方式表示出中间相乘值,并相加
stored0=(b[0]?{16'b0,a}:32'b0);
stored1=(b[1]?{15'b0,a,1'b0}:32'b0);
stored2=(b[2]?{14'b0,a,2'b0}:32'b0);
stored3=(b[3]?{13'b0,a,3'b0}:32'b0);
stored4=(b[4]?{12'b0,a,4'b0}:32'b0);
stored5=(b[5]?{11'b0,a,5'b0}:32'b0);
stored6=(b[6]?{10'b0,a,6'b0}:32'b0);
stored7=(b[7]?{9'b0,a,7'b0}:32'b0);
stored8=(b[8]?{8'b0,a,8'b0}:32'b0);
stored9=(b[9]?{7'b0,a,9'b0}:32'b0);
stored10=(b[10]?{6'b0,a,10'b0}:32'b0);
stored11=(b[11]?{5'b0,a,11'b0}:32'b0);
stored12=(b[12]?{4'b0,a,12'b0}:32'b0);
stored13=(b[13]?{3'b0,a,13'b0}:32'b0);
stored14=(b[14]?{2'b0,a,14'b0}:32'b0);
stored15=(b[15]?{1'b0,a,15'b0}:32'b0);
add0_1= (stored0 + stored1) ;
add2_3= (stored2 + stored3) ;
add4_5= (stored4 + stored5) ;
add6_7= (stored6 + stored7);
add8_9= (stored8 + stored9 );
add10_11= (stored10 + stored11) ;
add12_13= (stored12 + stored13 );
add14_15= (stored14 + stored15);
add0t1_2t3=( add0_1 + add2_3);
add4t5_6t7= (add4_5 + add6_7);
add8t9_10t11=( add8_9 + add10_11);
add12t13_14t15=( add12_13 + add14_15);
add0t3_4t7= (add0t1_2t3+add4t5_6t7);
add8t11_12t15= (add8t9_10t11+add12t13_14t15);
temp=(add0t3_4t7+add8t11_12t15);
end
end
assign z =temp;
endmodule
激励文件:
module MULTUTEST;
parameter CLKPER = 10; //时钟
reg clk;
reg reset;
reg [15:0] a, b;
wire [31:0] z;
//初始化
MULTU multu1(
.clk(clk),
.reset(reset),
.a(a),
.b(b),
.z(z)
);
// 时钟
always begin
#CLKPER clk = ~clk;
end
// 输入input
initial begin
clk = 0;
reset = 1;
#CLKPER;
reset = 0;
#CLKPER;
#CLKPER;
a = 16'd4; b = 16'd4; //Test case 1: Multiplication of 4*4
#CLKPER;
#CLKPER;
a = 16'd10; b = 16'd15; //Test case 2: Multiplication of 10*15
#CLKPER;
#CLKPER;
a = 16'd255; b = 16'd255; //Test case 3: Multiplication of 255*255
#CLKPER;
#CLKPER;
a = 16'd1024; b = 16'd2048; //Test case 4: Multiplication of 1024*2048
#CLKPER;
$finish;
end
endmodule
模拟仿真图像:
(步骤1-2不需要下载到开发板,因此没有引脚约束文件和图片结果)
步骤3-5:
源代码:(模拟仿真时注意看最后两行的注释)
module MULTU(
input clk, //乘法器时钟信号
input [3:0] a, //输入a(被乘数)
input [3:0] b, //输入b(乘数)
output [7:0] z //乘积输出z
);
reg [ 7:0 ] temp; //申请寄存器
reg [ 7:0 ] stored0;
reg [ 7:0 ] stored1;
reg [ 7:0 ] stored2;
reg [ 7:0 ] stored3;
reg [ 7:0 ] add0_1;
reg [ 7:0 ] add2_3;
always @( posedge clk)
begin
stored0= b[0]?{4'b0,a}:8'b0;
stored1= b[1]?{3'b0,a,1'b0}:8'b0;
stored2= b[2]?{2'b0,a,2'b0}:8'b0;
stored3= b[3]?{1'b0,a,3'b0}:8'b0;
add0_1= stored1 + stored0 ;
add2_3= stored2 + stored3 ;
temp= add0_1 + add2_3;
end
assign z = temp;
endmodule
//控制两个数码管动态显示的模块。seg0[3:0]和seg1[3:0]分别是两个8421BCD码。
//如果是控制8个数码管显示,需要修改信号an为8位,作为8个数码管的位选控制。
//现在已完成修改8位
module ShowTwoSeg7(input clk,input [3:0] seg0,input [3:0] seg1,
input [3:0] seg2,input [3:0] seg3,input [3:0] seg4,input [3:0] seg5,
input [3:0] seg6,input [3:0] seg7,output reg [7:0] an, output reg [7:0] seg,output reg [7:0] segg);
reg [2:0]state;
reg [3:0] bcd;
always @(posedge clk) begin
state <= state + 1;
end
always @(*) begin
case(state)
3'b000: begin
an = 8'b00000001;
bcd = seg7;
end
3'b001: begin
an = 8'b00000010;
bcd = seg6;
end
3'b010: begin
an = 8'b00000100;
bcd = seg5;
end
3'b011: begin
an = 8'b00001000;
bcd = seg4;
end
3'b100: begin
an = 8'b00010000;
bcd = seg3;
end
3'b101: begin
an = 8'b00100000;
bcd = seg2;
end
3'b110: begin
an = 8'b01000000;
bcd = seg1;
end
3'b111: begin
an = 8'b10000000;
bcd = seg0;
end
endcase
end
//8421BCD码bcd与一位数码管的8段A、B、C、D、E、F、G、DP之间的对应关系
always @(*) begin
if(an> 8'b00001000) begin//可能遇到seg一个信号不能同时绑定左右两边数码管,多做一份segg作为右边数码管信号
case(bcd)
4'h0: seg = 8'hfc;
4'h1: seg = 8'h60;
4'h2: seg = 8'hda;
4'h3: seg = 8'hf2;
4'h4: seg = 8'h66;
4'h5: seg = 8'hb6;
4'h6: seg = 8'hbe;
4'h7: seg = 8'he0;
4'h8: seg = 8'hfe;
4'h9: seg = 8'hf6;
// 4'ha: seg = 8'hee;
// 4'hb: seg = 8'h3e;
// 4'hc: seg = 8'h9c;
// 4'hd: seg = 8'h7a;
// 4'he: seg = 8'h9e;
// 4'hf: seg = 8'h8e;
default: seg = 8'h00; //八段全熄灭
endcase
end
else begin
case(bcd)
4'h0: segg = 8'hfc;
4'h1: segg = 8'h60;
4'h2: segg = 8'hda;
4'h3: segg = 8'hf2;
4'h4: segg = 8'h66;
4'h5: segg = 8'hb6;
4'h6: segg = 8'hbe;
4'h7: segg = 8'he0;
4'h8: segg = 8'hfe;
4'h9: segg = 8'hf6;
// 4'ha: seg = 8'hee;
// 4'hb: seg = 8'h3e;
// 4'hc: seg = 8'h9c;
// 4'hd: seg = 8'h7a;
// 4'he: seg = 8'h9e;
// 4'hf: seg = 8'h8e;
default: segg = 8'h00; //八段全熄灭
endcase
end
end
initial begin
state=0;
end
endmodule
//4位二进制转8421
module bin_to_bcd(
input [3:0] bin, // 输入的二进制数
output reg [3:0] tens, // BCD码的十位
output reg [3:0] ones // BCD码的个位
);
// 此处实现二进制到BCD的转换
always @(bin) begin
// 初始化BCD数字
tens = 0;
ones = 0;
// 计算十位和个位
if (bin > 9) begin
tens = bin / 10;
ones = bin % 10;
end else begin
ones = bin;
end
end
endmodule
//8位二进制转8421,给乘积用的
module bin_to_bcd_eight(
input [7:0] bin, // 输入的二进制数
output reg [3:0] thous,//BCD码的千位
output reg [3:0] huns,//BCD码的百位
output reg [3:0] tens, // BCD码的十位
output reg [3:0] ones // BCD码的个位
);
// 此处实现二进制到BCD的转换
always @(bin) begin
// 初始化BCD数字
thous = 0;
huns = 0;
tens = 0;
ones = 0;
// 计算百位十位个位,因为最多15*15=225,压根没必要计算千位
if (bin > 99) begin
huns = bin / 100;
tens = (bin / 10) % 10;
ones = bin % 10;
end
else if (bin > 9) begin
tens = bin / 10;
ones = bin % 10;
end
else begin
ones = bin;
end
end
endmodule
//数码管动态扫描所需要的时钟模块
module clk_div(
input clk0,
output reg clk
);
parameter N = 32'd51200, WIDTH = 32 - 1;
reg [WIDTH : 0] number = 0;
always @(posedge clk0) begin//原本给的代码监听应该写错了,是clk0才对
if (number == N - 1) begin
number <=0;
clk <= ~clk;
end
else begin
number <= number + 1;
end
end
initial begin//这里需要补充初始化clk,不然全程X,反转都是X
clk=0;
end
endmodule
module fourbit_bcd(clk,A,B,an,seg,segg);
input clk; // 输入时钟,需要分频后进行动态扫描控制。
input[3:0] A; // 输入乘数A
input[3:0] B; //输入乘数B
//output reg an1, an2; // 显示乘数A和乘数B的两个数码管的位选控制端
output wire[7:0] an; // 八个数码管的位选控制端
wire[7:0] mul1,mul2; // 乘数A和乘数B。乘数A的十、个位分时送给mul1,B给mul2
wire[15:0] mul; //乘积的千、百、十、个位分时送给mul。进行动态显示
wire[7:0] result; //乘积
wire clk_digit;//数码管动态扫描所需时钟
output wire[7:0] seg;//段选信号
output wire[7:0] segg;//右侧数码管段选信号
//乘数转8421
bin_to_bcd d1(.bin(A),.tens(mul1[7:4]),.ones(mul1[3:0]));
bin_to_bcd d2(.bin(B),.tens(mul2[7:4]),.ones(mul2[3:0]));
//获取乘积
MULTU multu(.clk(clk),.a(A),.b(B),.z(result));
//乘积转8421
bin_to_bcd_eight res(.bin(result),.thous(mul[15:12]),.huns(mul[11:8]),.tens(mul[7:4]),.ones(mul[3:0]));
//获取数码管动态扫描所需clk_digit
clk_div clkdiv1(.clk0(clk),.clk(clk_digit));
//获取数码管段选信号
ShowTwoSeg7 da1(.clk(clk_digit),.seg0(mul1[7:4]),.seg1(mul1[3:0]),.seg2(mul2[7:4]),.seg3(mul2[3:0]),
.seg4(mul[15:12]),.seg5(mul[11:8]),.seg6(mul[7:4]),.seg7(mul[3:0]),.an(an),.seg(seg),.segg(segg));
//(这里上面的.clk(clk_digit),仿真时使用clk才能看到效果,因为clk_digit是ms级的时钟,
//仿真是ns级的,用clk_digit仿真时看不出效果,所以仿真时可以暂时用clk代替。)
endmodule
激励文件:
module fourbit_bcd_tb();
parameter CLKPER = 10; //时钟
reg clk;
reg [3:0] A;
reg [3:0] B;
wire [7:0] an;
wire [7:0] seg;//段选信号
wire [7:0] segg;//右侧数码管段选信号
//初始化
fourbit_bcd fb(
.clk(clk),
.A(A),
.B(B),
.an(an),
.seg(seg),
.segg(segg)
);
// 时钟
always begin
#CLKPER clk = ~clk;
end
initial begin
clk = 0;
A=4'b0010;//2*2
B=4'b0010;
#CLKPER;
#CLKPER;
#CLKPER;
A=4'b0100;//4*4
B=4'b0100;
#CLKPER;
#CLKPER;
#CLKPER;
A=4'b1011;//11*11
B=4'b1011;
#CLKPER;
#CLKPER;
#CLKPER;
end
endmodule
引脚约束文件:
set_property PACKAGE_PIN P17 [get_ports clk]
set_property PACKAGE_PIN P2 [get_ports {A[0]}]
set_property PACKAGE_PIN P3 [get_ports {A[1]}]
set_property PACKAGE_PIN P4 [get_ports {A[2]}]
set_property PACKAGE_PIN P5 [get_ports {A[3]}]
set_property PACKAGE_PIN R1 [get_ports {B[0]}]
set_property PACKAGE_PIN N4 [get_ports {B[1]}]
set_property PACKAGE_PIN M4 [get_ports {B[2]}]
set_property PACKAGE_PIN R2 [get_ports {B[3]}]
set_property PACKAGE_PIN G2 [get_ports an[7]]
set_property PACKAGE_PIN C2 [get_ports an[6]]
set_property PACKAGE_PIN C1 [get_ports an[5]]
set_property PACKAGE_PIN H1 [get_ports an[4]]
set_property PACKAGE_PIN G1 [get_ports an[3]]
set_property PACKAGE_PIN F1 [get_ports an[2]]
set_property PACKAGE_PIN E1 [get_ports an[1]]
set_property PACKAGE_PIN G6 [get_ports an[0]]
set_property PACKAGE_PIN B4 [get_ports {seg[7]}]
set_property PACKAGE_PIN A4 [get_ports {seg[6]}]
set_property PACKAGE_PIN A3 [get_ports {seg[5]}]
set_property PACKAGE_PIN B1 [get_ports {seg[4]}]
set_property PACKAGE_PIN A1 [get_ports {seg[3]}]
set_property PACKAGE_PIN B3 [get_ports {seg[2]}]
set_property PACKAGE_PIN B2 [get_ports {seg[1]}]
set_property PACKAGE_PIN D5 [get_ports {seg[0]}]
set_property PACKAGE_PIN D4 [get_ports {segg[7]}]
set_property PACKAGE_PIN E3 [get_ports {segg[6]}]
set_property PACKAGE_PIN D3 [get_ports {segg[5]}]
set_property PACKAGE_PIN F4 [get_ports {segg[4]}]
set_property PACKAGE_PIN F3 [get_ports {segg[3]}]
set_property PACKAGE_PIN E2 [get_ports {segg[2]}]
set_property PACKAGE_PIN D2 [get_ports {segg[1]}]
set_property PACKAGE_PIN H2 [get_ports {segg[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {clk}]
set_property IOSTANDARD LVCMOS33 [get_ports {A[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {A[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {A[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {A[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {B[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {B[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {B[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {B[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports an[7]]
set_property IOSTANDARD LVCMOS33 [get_ports an[6]]
set_property IOSTANDARD LVCMOS33 [get_ports an[5]]
set_property IOSTANDARD LVCMOS33 [get_ports an[4]]
set_property IOSTANDARD LVCMOS33 [get_ports an[3]]
set_property IOSTANDARD LVCMOS33 [get_ports an[2]]
set_property IOSTANDARD LVCMOS33 [get_ports an[1]]
set_property IOSTANDARD LVCMOS33 [get_ports an[0]]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {seg[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segg[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segg[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segg[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segg[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segg[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segg[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segg[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {segg[0]}]
模拟仿真图像:
下载到开发板上的图片结果: