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 组样例全部通过,结果与参考乘积一致。

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image1.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image10.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image11.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image12.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image13.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image2.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image3.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image4.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image5.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image6.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image7.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image8.png

16位有符号时序乘法器设计 Verilog Vivado 设计文档图片 image9.png

部分代码

以下展示顶层模块 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

注册为本站会员,充值100得150,详情咨询客服

目前为止共有 *** 位优秀的会员加入! 立刻加入会员