Verilog生活-乘法器
基础部分的讲解请见:从零开始的 Verilog 生活-优快云博客
实验指导书连接详见: 百度网盘 请输入提取码 提取码: nnen
本次开始探究乘法器的实现,将直接从代码讲起,请参阅代码段中的注释
multiply.v
`timescale 1ns / 1ps
module multiply(
input clk, // 时钟
input mult_begin, // 乘法开始信号
input [31:0] mult_op1, // 乘法源操作数1
input [31:0] mult_op2, // 乘法源操作数2
input [31:0] addend, // 输入的加数
input cin, // 输入的进位
output [63:0] product, // 最终结果
output cout, // 向高位的进位
output mult_end // 乘法结束信号
);
// 乘法正在运算信号和结束信号
reg mult_valid;
assign mult_end = mult_valid & ~(|multiplier); // 乘法结束信号:乘数全0
always @(posedge clk)
begin
if (!mult_begin || mult_end)
begin
mult_valid <= 1'b0;//设置为二进制数的0,位宽为1
end
else
begin
mult_valid <= 1'b1;//设置为二进制数的1,位宽为1
end
end
// 两个源操作取绝对值,正数的绝对值为其本身,负数的绝对值为取反加1
wire op1_sign; // 操作数1的符号位
wire op2_sign; // 操作数2的符号位
wire [31:0] op1_absolute; // 操作数1的绝对值
wire [31:0] op2_absolute; // 操作数2的绝对值
assign op1_sign = mult_op1[31];
assign op2_sign = mult_op2[31];
assign op1_absolute = op1_sign ? (~mult_op1 + 1) : mult_op1;
assign op2_absolute = op2_sign ? (~mult_op2 + 1) : mult_op2;
// 加载被乘数,运算时每次左移两位
reg [63:0] multiplicand;
always @ (posedge clk)
begin
if (mult_valid)
begin
multiplicand <= {multiplicand[61:0], 2'b0};//记录左移两位后的值
end
else if (mult_begin)
begin
multiplicand <= {32'd0, op1_absolute};//初始阶段将原始被乘数赋值
end
end
// 加载乘数,运算时每次右移两位
reg [31:0] multiplier;
always @ (posedge clk)
begin
if (mult_valid)
begin
//{}拼接运算符,实现了算数位的移动
multiplier <= {2'b00, multiplier[31:2]};
end
else if (mult_begin)
begin
multiplier <= op2_absolute;
end
end
//多使用一个寄存器,用于存储移动一位后的结果
// 部分积:乘数末两位为11,由被乘数左移两位得到;乘数末两位为00,部分积为0
wire [63:0] partial_product1;
wire [63:0] partial_product2;
assign partial_product2 = multiplier[1] ? {multiplicand[62:0], 1'b0} : 64'd0;
assign partial_product1 = multiplier[0] ? multiplicand : 64'd0;
// 累加器
reg [63:0] product_temp;
always @ (posedge clk)
begin
if (mult_valid)
begin
product_temp <= product_temp + partial_product1 + partial_product2;
end
else if (mult_begin)
begin
product_temp <= 64'd0; // 乘法开始,乘积清零
end
end
// 乘法结果的符号位和乘法结果
reg product_sign;
always @ (posedge clk)
begin
if (mult_valid)
begin
product_sign <= op1_sign ^ op2_sign;
end
end
// 若乘法结果为负数,则需要对结果取反+1
wire [63:0] product_final;
assign product_final = product_sign ? (~product_temp + 1) : product_temp;
//乘法的结果与加法相加
reg [63:0] final_result; //最终结果
reg final_cout; //最终进位
always @ (posedge clk)
begin
if (mult_end) //乘法结束后
begin
{final_cout, final_result} <= product_final + addend + cin; //加法运算
end
end
//这里的输出product一定不能直接使用两次
assign product = final_result;
assign cout = final_cout;
endmodule
testbench.v
`timescale 1ns / 1ps module tb; // Inputs reg clk; reg mult_begin; reg [31:0] mult_op1; reg [31:0] mult_op2; reg [31:0] addend; // 加数 reg cin; // 进位输入 // Outputs wire [63:0] product; // 乘积 wire cout; // 进位输出 wire mult_end; // 乘法结束信号 // Instantiate the Unit Under Test (UUT) multiply uut ( .clk(clk), .mult_begin(mult_begin), .mult_op1(mult_op1), .mult_op2(mult_op2), .addend(addend), .cin(cin), .product(product), .cout(cout), .mult_end(mult_end) ); initial begin // Initialize Inputs clk = 0; mult_begin = 0; mult_op1 = 0; mult_op2 = 0; addend = 0; cin = 0; // Wait 100 ns for global reset to finish #100; //自行添加测试样例 // Test Case 1: 正数 × 正数 mult_begin = 1; mult_op1 = 32'H00001111; // 被乘数 mult_op2 = 32'H00001111; // 乘数 addend = 32'H00000010; // 加数 cin = 0; // 进位输入 #600; // 等待乘法完成 mult_begin = 0; #500; // 等待一段时间 end // 生成时钟信号 always #5 clk = ~clk; endmodule
multiply_display
这一部分的命名一定要设计好,建议在开始设计前,将所要利用到的变量名列举好,不容易混乱
module multiply_display(
//时钟与复位信号
input clk,
input resetn, //后缀"n"代表低电平有效
//拨码开关,用于选择输入数
input input_sel1, //0:输入为乘数1;1:输入为乘数2
input input_sel2, //0:输入为加数
input cin, //输入的进位
input sw_begin, //乘法开始信号
//乘法结束信号
output led_end, //乘法结束信号
output led_cout,//向高位的进位
//触摸屏相关接口,不需要更改
output lcd_rst,
output lcd_cs,
output lcd_rs,
output lcd_wr,
output lcd_rd,
inout[15:0] lcd_data_io,
output lcd_bl_ctr,
inout ct_int,
inout ct_sda,
output ct_scl,
output ct_rstn
);
//-----{调用乘法器模块}begin
wire mult_begin;
wire cin;
reg [31:0] mult_op1;
reg [31:0] mult_op2;
reg [31:0] addend;
wire [63:0] product;
wire mult_end;
wire cout;//向高位的借位
assign mult_begin = sw_begin;
assign led_end = mult_end;
assign let_cout = cout;
//实例化乘法器模块
multiply multiply_module (
.clk (clk ),
.mult_begin(mult_begin),
.mult_op1 (mult_op1 ),
.mult_op2 (mult_op2 ),
.addend (addend ),
.cin (cin ),
.product (product ),
.mult_end (mult_end ),
.cout (cout )
);
reg [63:0] product_r; //存储乘法结果
always @(posedge clk)
begin
if (!resetn)
begin
product_r <= 64'd0;
end
else if (mult_end)
begin
product_r <= product;
end
end
//-----{调用乘法器模块}end
//---------------------{调用触摸屏模块}begin--------------------//
//-----{实例化触摸屏}begin
//此小节不需要更改
reg display_valid;
reg [39:0] display_name;
reg [31:0] display_value;
wire [5 :0] display_number;
wire input_valid;
wire [31:0] input_value;
lcd_module lcd_module(
.clk (clk ), //10Mhz
.resetn (resetn ),
//调用触摸屏的接口
.display_valid (display_valid ),
.display_name (display_name ),
.display_value (display_value ),
.display_number (display_number),
.input_valid (input_valid ),
.input_value (input_value ),
//lcd触摸屏相关接口,不需要更改
.lcd_rst (lcd_rst ),
.lcd_cs (lcd_cs ),
.lcd_rs (lcd_rs ),
.lcd_wr (lcd_wr ),
.lcd_rd (lcd_rd ),
.lcd_data_io (lcd_data_io ),
.lcd_bl_ctr (lcd_bl_ctr ),
.ct_int (ct_int ),
.ct_sda (ct_sda ),
.ct_scl (ct_scl ),
.ct_rstn (ct_rstn )
);
//-----{实例化触摸屏}end
//-----{从触摸屏获取输入}begin
//根据实际需要输入的数修改此小节,
//建议对每一个数的输入,编写单独一个always块
//当input_sel1、2为00时,表示输入数为乘数1
always @(posedge clk)
begin
if (!resetn)
begin
mult_op1 <= 32'd0;
end
else if (input_valid && !input_sel1 && !input_sel1)//00输入乘数1
begin
mult_op1 <= input_value;
end
end
//当input_sel1、2为10时,表示输入数为乘数2
always @(posedge clk)
begin
if (!resetn)
begin
mult_op2 <= 32'd0;
end
else if (input_valid && input_sel1 && !input_sel2)//10输入乘数2
begin
mult_op2 <= input_value;
end
end
//当input_sel1、2为01时,表示输入数为乘数2
always @(posedge clk)
begin
if (!resetn)
begin
addend <= 32'd0;
end
else if (input_valid && !input_sel1 && input_sel2)//10输入乘数2
begin
addend <= input_value;
end
end
//-----{从触摸屏获取输入}end
//-----{输出到触摸屏显示}begin
//根据需要显示的数修改此小节,
//触摸屏上共有44块显示区域,可显示44组32位数据
//44块显示区域从1开始编号,编号为1~44,
always @(posedge clk)
begin
case(display_number)
6'd1 :
begin
display_valid <= 1'b1;
display_name <= "M_OP1";
display_value <= mult_op1;
end
6'd2 :
begin
display_valid <= 1'b1;
display_name <= "M_OP2";
display_value <= mult_op2;
end
6'd3 : //显示加数
begin
display_valid <= 1'b1;
display_name <= "ADDEN";
display_value <= addend;
end
6'd4 : // 显示乘法结果的高32位
begin
display_valid <= 1'b1;
display_name <= "PRO_H";
display_value <= product_r[63:32];
end
6'd5 : // 显示乘法结果的低32位
begin
display_valid <= 1'b1;
display_name <= "PRO_L";
display_value <= product_r[31:0];
end
default : // 默认情况
begin
display_valid <= 1'b0;
display_name <= 40'd0;
display_value <= 32'd0;
end
endcase
end
//-----{输出到触摸屏显示}end
//----------------------{调用触摸屏模块}end---------------------//
endmodule
multiply.xdc
这一部分参照实验书与箱子对应的接口即可,变量名注意一定要与display模块定义的相同,而不是multiply模块
#对每个信号绑定试验箱上的位置
set_property PACKAGE_PIN AC19 [get_ports clk]
set_property PACKAGE_PIN Y3 [get_ports resetn]
set_property PACKAGE_PIN AC21 [get_ports input_sel1]
set_property PACKAGE_PIN AD24 [get_ports input_sel2]
set_property PACKAGE_PIN AC22 [get_ports cin]
set_property PACKAGE_PIN AC23 [get_ports sw_begin]
set_property PACKAGE_PIN H7 [get_ports led_end]
set_property PACKAGE_PIN D5 [get_ports led_cout]
#给每个信号定义标准电压为3.3伏
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports resetn]
set_property IOSTANDARD LVCMOS33 [get_ports input_sel1]
set_property IOSTANDARD LVCMOS33 [get_ports input_sel2]
set_property IOSTANDARD LVCMOS33 [get_ports cin]
set_property IOSTANDARD LVCMOS33 [get_ports sw_begin]
set_property IOSTANDARD LVCMOS33 [get_ports led_end]
set_property IOSTANDARD LVCMOS33 [get_ports led_cout]
#lcd
set_property PACKAGE_PIN J25 [get_ports lcd_rst]
set_property PACKAGE_PIN H18 [get_ports lcd_cs]
set_property PACKAGE_PIN K16 [get_ports lcd_rs]
set_property PACKAGE_PIN L8 [get_ports lcd_wr]
set_property PACKAGE_PIN K8 [get_ports lcd_rd]
set_property PACKAGE_PIN J15 [get_ports lcd_bl_ctr]
set_property PACKAGE_PIN H9 [get_ports {lcd_data_io[0]}]
set_property PACKAGE_PIN K17 [get_ports {lcd_data_io[1]}]
set_property PACKAGE_PIN J20 [get_ports {lcd_data_io[2]}]
set_property PACKAGE_PIN M17 [get_ports {lcd_data_io[3]}]
set_property PACKAGE_PIN L17 [get_ports {lcd_data_io[4]}]
set_property PACKAGE_PIN L18 [get_ports {lcd_data_io[5]}]
set_property PACKAGE_PIN L15 [get_ports {lcd_data_io[6]}]
set_property PACKAGE_PIN M15 [get_ports {lcd_data_io[7]}]
set_property PACKAGE_PIN M16 [get_ports {lcd_data_io[8]}]
set_property PACKAGE_PIN L14 [get_ports {lcd_data_io[9]}]
set_property PACKAGE_PIN M14 [get_ports {lcd_data_io[10]}]
set_property PACKAGE_PIN F22 [get_ports {lcd_data_io[11]}]
set_property PACKAGE_PIN G22 [get_ports {lcd_data_io[12]}]
set_property PACKAGE_PIN G21 [get_ports {lcd_data_io[13]}]
set_property PACKAGE_PIN H24 [get_ports {lcd_data_io[14]}]
set_property PACKAGE_PIN J16 [get_ports {lcd_data_io[15]}]
set_property PACKAGE_PIN L19 [get_ports ct_int]
set_property PACKAGE_PIN J24 [get_ports ct_sda]
set_property PACKAGE_PIN H21 [get_ports ct_scl]
set_property PACKAGE_PIN G24 [get_ports ct_rstn]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_rst]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_cs]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_rs]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_wr]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_rd]
set_property IOSTANDARD LVCMOS33 [get_ports lcd_bl_ctr]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[4]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[5]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[6]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[7]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[8]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[9]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[10]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[11]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[12]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[13]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[14]}]
set_property IOSTANDARD LVCMOS33 [get_ports {lcd_data_io[15]}]
set_property IOSTANDARD LVCMOS33 [get_ports ct_int]
set_property IOSTANDARD LVCMOS33 [get_ports ct_sda]
set_property IOSTANDARD LVCMOS33 [get_ports ct_scl]
set_property IOSTANDARD LVCMOS33 [get_ports ct_rstn]
59

被折叠的 条评论
为什么被折叠?



