16位有符号时序乘法器设计 Verilog Vivado
名称:16位有符号时序乘法器设计 Verilog Vivado
软件:Vivado
语言:Verilog
功能介绍
本工程实现一个 16 位有符号时序乘法器,输入为 signed [15:0] 的 in_a 和 in_b,输出为 signed [31:0] 的 product_out,并通过 done 信号指示运算完成。模块在 start 脉冲到来后锁存输入,按照逐位移位累加方式完成乘法计算,支持正数、负数以及正负混合运算。 工程配套 Vivado 项目文件、Verilog 顶层模块和 testbench,可用于学习可综合时序算法的 RTL 写法。测试内容覆盖 0*0、1*1、255*38、负数符号翻转、-123*456、321*-789、-32768 边界值以及 32767*32767 等典型场景,并加入随机测试向量,适合验证有符号乘法的数据通路和完成握手机制。运行环境
开发环境为 Xilinx Vivado,工程文件为 mul16_seq.xpr,主设计语言为 Verilog。项目包含 mul16_seq.v、testbench.v 以及 Vivado 仿真工程配置,可直接打开工程后运行行为级仿真,查看状态机、累加器、移位寄存器、done 和 product_out 的时序变化。设计思路
设计采用经典的移位累加乘法算法,并通过两状态有限状态机控制运算流程。启动信号有效时,模块先记录两个 16 位有符号输入的符号,利用补码取反加一得到操作数绝对值,再将一个操作数扩展到 32 位作为被乘数,另一个操作数装入 16 位乘数寄存器。随后进入计算状态,每个时钟周期检查乘数最低位,最低位为 1 时把当前被乘数累加到部分积寄存器中,同时执行被乘数左移和乘数右移。 为了处理有符号结果,模块使用输入符号位异或得到最终符号,并在完成 16 次逐位运算后组合为 32 位有符号乘积。bit_counter 用于记录已经处理的位数,acc_next 组合逻辑用于提前计算当前周期的累加值,从而在最后一拍直接得到正确结果。该结构不依赖乘法器 IP,便于学习时序乘法、符号处理、状态机和寄存器级数据通路的实现方法。模块结构
顶层模块为 mul16_seq,负责输入锁存、符号判断、移位累加、计数和结果输出;内部由 S_IDLE 空闲状态和 S_CALC 计算状态组成。multiplicand_reg 保存扩展后的被乘数,multiplier_reg 保存待处理的乘数位,acc_reg 保存部分乘积,bit_counter 统计 16 位运算进度,result_sign 保存最终符号。工程同时包含 tb_mul16_seq 测试平台,用于产生时钟、启动信号和多组有符号输入并检查结果。演示视频
演示视频展示了 16 位有符号时序乘法器工程的运行过程,可结合 Vivado 工程进行操作步骤和结果观察。仿真图/仿真说明/设计文档图片
设计文档包含工程结构、程序编译、RTL 结构、Testbench 和仿真说明,并配有对应设计文档图片。仿真测试包括 10 组固定测试用例和 50 组随机测试用例,覆盖零值、正负号组合、16 位边界输入及最大正数乘法;测试输出显示 60 组样例全部通过,结果与参考乘积一致。












部分代码
以下展示顶层模块 mul16_seq 的部分代码,完整源码请下载压缩包查看。
module mul16_seq (
input wire clk, // 时钟信号,上升沿有效
input wire start, // 启动信号,高电平一个时钟周期
input wire signed [15:0] in_a, // 乘数 A(有符号 16 位)
input wire signed [15:0] in_b, // 乘数 B(有符号 16 位)
output reg done, // 运算完成标志,高电平表示结果已经计算完毕
output reg signed [31:0] product_out // 乘积输出(有符号 32 位)
);
//============================================================
// 一、内部寄存器定义
//============================================================
// 将其中一个操作数的"绝对值"扩展为 32 位,方便做左移和累加
reg [31:0] multiplicand_reg; // 被加数寄存器(正数),相当于"部分被乘数"
// 另一个操作数的"绝对值"保存在 16 位寄存器中,逐位右移
reg [15:0] multiplier_reg; // 乘数寄存器(正数),每一位决定是否累加
// 累加器,用来保存当前的部分和(始终为非负数)
reg [31:0] acc_reg; // 部分乘积寄存器(正数)
// 计数器,用来记录已经处理了多少位(最多 16 位)
reg [4:0] bit_counter; // 范围足够覆盖 0~16
// 记录结果的符号:0 表示正数或 0,1 表示负数
reg result_sign; // 最终结果的符号位
// 简单的状态机定义
localparam S_IDLE = 2'b00; // 空闲状态,等待 start
localparam S_CALC = 2'b01; // 计算状态,进行移位与累加
reg [1:0] state; // 当前状态
// 用于保存"下一拍累加器的值",方便在最后一拍直接输出结果
reg [31:0] acc_next; // 下一个周期的部分乘积(正数)
//============================================================
// 二、组合逻辑:根据当前最低位决定是否累加
//============================================================
// 说明:
// - 在计算状态下,如果乘数寄存器的最低位为 1,
// 则需要把当前的 multiplicand_reg 加到部分和 acc_reg 中;
// - 如果最低位为 0,则保持 acc_reg 不变。
// - 这里使用组合逻辑提前计算 acc_next,便于在最后一拍使用。
//============================================================
always @(*) begin
if (state == S_CALC && multiplier_reg[0] == 1'b1) begin
acc_next = acc_reg + multiplicand_reg; // 需要累加
end else begin
acc_next = acc_reg; // 不需要累加
end
end
//============================================================
// 三、时序逻辑:状态机与寄存器更新
//============================================================
// 说明:
// - 所有寄存器都在时钟上升沿更新
// - 无异步复位,为了简化仿真,初始值在 initial 代码块中给出
// - 实际工程中可根据需要增加复位信号
//============================================================
initial begin
state = S_IDLE; // 上电后默认处于空闲状态
multiplicand_reg = 32'd0;
multiplier_reg = 16'd0;
acc_reg = 32'd0;
bit_counter = 5'd0;
result_sign = 1'b0;
product_out = 32'sd0;
done = 1'b0;
end
// 主状态机与运算过程
always @(posedge clk) begin
case (state)
//----------------------------------------------------
// 空闲状态:等待 start 信号
//----------------------------------------------------
S_IDLE: begin
done <= 1'b0; // 空闲时 done 置 0
bit_counter <= 5'd0; // 计数器清零
if (start == 1'b1) begin
// 计算最终结果的符号:两个操作数符号位异或
result_sign <= in_a[15] ^ in_b[15];
// 对输入取绝对值,只对幅值做无符号乘法
// 说明:这里使用补码取反加一的方式求绝对值
// 例如:-3 的补码为 ...1101,取绝对值后得到 3
if (in_b[15] == 1'b1) begin
multiplicand_reg <= {16'd0, (~in_b + 16'sd1)}; // |in_b|
end else begin
multiplicand_reg <= {16'd0, in_b}; // 正数直接使用
end
if (in_a[15] == 1'b1) begin
multiplier_reg <= (~in_a + 16'sd1); // |in_a|
end else begin
multiplier_reg <= in_a; // 正数直接使用
end
// 清空累加器
acc_reg <= 32'd0;
// 进入计算状态
state <= S_CALC;
end
end
//----------------------------------------------------
// 计算状态:每个时钟周期处理一位
//----------------------------------------------------
S_CALC: begin
// 首先更新累加器(是否加 multiplicand_reg 由 acc_next 决定)
acc_reg <= acc_next;
// 准备下一位运算:被加数左移一位,乘数右移一位
multiplicand_reg <= multiplicand_reg << 1;
multiplier_reg <= multiplier_reg >> 1;
// ... 以下代码略,完整源码请下载压缩包查看
代码文件(付费下载)
📥
付费后此处显示完整工程压缩包下载链接。
1、代码文件需要付费后才可见。
2、支付问题请联系微信公众号客服。
3、优质Verilog/VHDL代码资源,所见即所得。
Verilog/VHDL资源下载 » 16位有符号时序乘法器设计 Verilog Vivado
2、支付问题请联系微信公众号客服。
3、优质Verilog/VHDL代码资源,所见即所得。
Verilog/VHDL资源下载 » 16位有符号时序乘法器设计 Verilog Vivado