平台:vivado
算法:booth
输入输出为补码形式
乘法器代码:
module multi (
input signed [31:0] op1,
input signed [31:0] op2,
output reg signed [63:0] result,
input clk, //时钟
input start, //开始标志 可以是拨动一个拨码开关
input ret_n, //复位标志 低电平复位
output reg done,
output reg auxiliary //辅助位
);
//增加开始标志和结束标志的乘法器 输入的数是补码 要按补码的乘法来做
reg signed [32:0] acc;
reg signed [32:0] op1_t;
reg signed [31:0] op2_t;
reg signed [32:0] op1_to; //被乘数的相反数的补码
reg [5:0] k;
initial begin
acc = 33'b0;
op1_t = 33'b0;
op2_t = 32'b0;
op1_to = 33'b0;
k = 6'b0;
done = 1'b0;
auxiliary = 1'b0;
result = 64'b0;
end
always @(posedge clk or negedge ret_n) begin
if(!ret_n) begin //如果为0 复位 非阻塞 同时赋值
result <= 64'b0;
done <= 1'b0;
acc <= 33'b0;
auxiliary <= 1'b0;
op1_t <= 33'b0;
op2_t <= 32'b0;
op1_to <= 33'b0;
k <= 6'b0;
end else begin
if (start && !done) begin
if (k == 0) begin
op1_t <= {op1[31], op1};
op2_t <= op2;
op1_to <= ~{op1[31], op1} + 1'b1;
k <= k + 1;
#1 $display("op1t=%d",op1_t);
end else if (k < 32) begin
#1 $display("au=%d op2t=%d,op2t[0]=%d -op1t=%d",auxiliary,op2_t,op2_t[0],-op1_t);
case ({auxiliary, op2_t[0]})
2'b10: acc <= acc + op1_t;
2'b01: acc <= acc - op1_t;
default: acc <= acc;
endcase
#1 //$display("accadd %d",acc);/// ?????为什么 加上这个输出就对了???
//重点不是输出 是#1 等待1单位时间
// 右移
auxiliary <= op2_t[0];
op2_t <= {acc[0], op2_t[31:1]};
acc <= {acc[32], acc[32:1]};
k <= k + 1;
#1 $display("rightmove: k=%d acc=%d ",k,acc);
end else begin
case ({auxiliary, op2_t[0]})
2'b10: acc <= acc + op1_t;
2'b01: acc <= acc - op1_t;
default: acc <= acc;
endcase
#1 $display("result acc=%d op2t=%d op2t[31:1]=%d",acc,op2_t,op2_t[31:1]);
// result <= {acc[32],1'b0,acc[30:0], op2_t[31:1]};
result <= {acc, op2_t[31:1]}; //这里还是有一个问题 为什么可以识别两个符号位
done <= 1'b1;
k <= 6'b0;
end
end else begin
// done <= 1'b0;
end
end
end
endmodule
acc不能使用非阻塞赋值 acc加改为=也可
测试文件
`timescale 1ns / 1ps
module tb_multi;
// 定义信号
reg start_switch;
reg ret_n_switch;
wire done_led;
reg [31:0] op1;
reg [31:0] op2;
wire [63:0] result;
reg clk;
// 实例化顶层模�?
multi uut (
.start(start_switch),
.ret_n(ret_n_switch),
.done(done_led),
.op1(op1),
.op2(op2),
.result(result),
.clk(clk)
);
// 时钟生成
initial begin
clk = 0;
forever #5 clk = ~clk; // 10ns 时钟周期
end
// 测试序列
initial begin
// 初始化信�?
start_switch = 0;
ret_n_switch = 0;
op1 = 32'd20;
op1=~op1;
op1=op1+1;
op1[31]=1;
// op1=~op1;
// op1=op1+1;
op2 = 32'd30;
#20; // 等待�?段时间,确保复位完成
// 释放复位信号
ret_n_switch = 1;
#20;
// 启动乘法运算
start_switch = 1;
// #320;
// start_switch = 0;
// 等待运算完成
wait (done_led);
#20;
// 显示结果
$display("op1 = %d, op2 = %d, result = %d", op1, op2, result);
end
endmodule
外围调用模块 (实验箱有lcd屏)
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2024/10/22 14:55:24
// Design Name:
// Module Name: display_multiply
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
/*2025年3月23日15点45分 by 某人*/
//////////////////////////////////////////////////////////////////////////////////
module mul_display(
input clk,
input resetn,
input input_sel,
input sw_cin,
output led_cout,
// 实验箱拨码开关输入
input wire start_switch, // 对应 start 信号
input wire ret_n_switch, // 对应 ret_n 信号
// 实验箱 LED 输出
output wire done_led, // 对应 done 信号
//lcd 触摸屏相关接口,不需要更改
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
);
//-----{调用乘法法模块}
reg [31:0] mul_a;
reg [31:0] mul_b;
//wire adder_cin;
wire [63:0] mul_result;
//wire adder_cout;
multi multiply_module(//需要改引脚名称
.op1(mul_a),
.op2(mul_b),
.result(mul_result),
.clk(clk), // 假设 clk 是时钟信号,需要在顶层模块处理
.start(start_switch),
.ret_n(ret_n_switch),
.done(done_led),
.auxiliary()
);
//assign adder_cin = sw_cin;
//assign led_cout = adder_cout;
//---------------------{调用触摸屏模块}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),
.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_sel 为 0 时,表示输入数为加数 1,即 operand1
always @(posedge clk)
begin
if (!resetn)
begin
mul_a<= 32'd0;
end
else if(input_valid && !input_sel)
begin
mul_a <= input_value;
end
end
//当 input_sel 为 1 时,表示输入数为加数 2,即 operand2
always @(posedge clk)
begin
if (!resetn)
begin
mul_b <= 32'd0;
end
else if(input_valid && input_sel)
begin
mul_b <= 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 <= "MUL_1";
display_value <= mul_a;
end
6'd2 :
begin
display_valid <= 1'b1;
display_name <= "MUL_2";
display_value <= mul_b;
end
6'd3 :
begin
display_valid <= 1'b1;
display_name <= "HIGH_";
display_value <= mul_result[63:32];
end
6'd4 :
begin
display_valid <= 1'b1;
display_name <= "LOW__";
display_value <= mul_result[31:0];
end
default:
begin
display_valid <= 1'b0;
display_name <= 40'd0;
display_value <= 32'd0;
end
endcase
end
//-----{输出到触摸屏显示}end
//----------------------{调用触摸屏模块}end---------------------//
endmodule
问题:最后result<={acc,op2_t[31:1]} acc有两位符号位 为什么结果也是对的