Verilog生活-乘法器

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]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值