当前位置:首页 > 随笔 > 正文内容

基于Verilog的32位有符号流水线乘法器设计与实现

访客 随笔 2026年7月28日 1

基于Verilog的32位有符号流水线乘法器设计与实现

1.二进制乘法运算机制

1.1无符号数乘法运算

以4位乘法器为例,计算2×6:
	0 0 0 0 0 0 1 0     (2)
X	0 0 0 0 0 1 1 0     (6)
    --------------------- 
	0 0 0 0 0 0 0 0     (0)
	0 0 0 0 0 1 0 0     (4)
	0 0 0 0 1 0 0 0     (8)
	0 0 0 0 0 0 0 0     (0)
	0 0 0 0 0 0 0 0     (0)
	0 0 0 0 0 0 0 0     (0)
	0 0 0 0 0 0 0 0     (0)
	0 0 0 0 0 0 0 0     (0)
    ---------------------	
	0 0 0 0 1 1 0 0     (12)
注意:4位×4位乘法器的最大结果位宽为8位(4+4)。在计算时,需要将乘数和被乘数通过符号位扩展到相应位宽。对于无符号乘法,虽然扩展的符号位没有实际作用,但对于有符号运算则是必需的。考虑到模块需要同时支持有符号和无符号运算,建议统一进行位宽扩展。

1.2有符号数乘法运算

计算-2 × -6:
	1 1 1 1 1 1 1 0     (-2)
 X	1 1 1 1 1 0 1 0     (-6)
    --------------------- 
	0 0 0 0 0 0 0 0		
	1 1 1 1 1 1 0 0		
	0 0 0 0 0 0 0 0		
	1 1 1 1 0 0 0 0		
	1 1 1 0 0 0 0 0     
	1 1 0 0 0 0 0 0     
	1 0 0 0 0 0 0 0     
	0 0 0 0 0 0 0 0     
    ---------------------	
	0 0 0 0 1 1 0 0     (12) 

1.3混合符号乘法运算

有符号×无符号(-2 × 6):
 	1 1 1 1 1 1 1 0     (-2)
 X	0 0 0 0 0 1 1 0     (6)
    --------------------- 
	0 0 0 0 0 0 0 0		
 	1 1 1 1 1 1 0 0		
	1 1 1 1 1 0 0 0		
	0 0 0 0 0 0 0 0		
	0 0 0 0 0 0 0 0     
	0 0 0 0 0 0 0 0     
	0 0 0 0 0 0 0 0     
	0 0 0 0 0 0 0 0     
    ---------------------	
	1 1 1 1 0 1 0 0     (-12)
无符号×有符号(2 × -6):
 	0 0 0 0 0 0 1 0     (2)
 X	1 1 1 1 1 0 1 0     (-6)
    --------------------- 
	0 0 0 0 0 0 0 0		
 	0 0 0 0 0 1 0 0		
	0 0 0 0 0 0 0 0		
	0 0 0 1 0 0 0 0		
	0 0 1 0 0 0 0 0     
	0 1 0 0 0 0 0 0     
	1 0 0 0 0 0 0 0     
	0 0 0 0 0 0 0 0     
    ---------------------	
	1 1 1 1 0 1 0 0     (-12)

2.乘法运算优化策略

通过对上述四种运算方式的分析,可以总结出一个重要规律:符号位本身不影响运算流程,仅影响最终结果的截取位置。 以无符号×有符号为例进行说明:bin(00000010)表示十进制2,而bin(11111010)作为有符号数时表示-6,作为无符号数时表示250。因此,2×250的结果若只取低8位,作为有符号数则为bin(11110100) = -12。这表明有符号乘法可以转换为无符号乘法进行计算,最后通过控制截位位置即可得到正确结果。 以下以-1000 × -1200为例说明16位乘法的简化过程:

2.1位宽扩展处理

dec(-1000) = bin(11111111111111111111110000011000) = dec(4294966296)
dec(-1200) = bin(11111111111111111111101101010000) = dec(4294966096)

2.2乘数分解与移位运算

将十进制数按2的幂次进行分解:
dec(4294966096) = 1×2^31 + 1×2^30 + 1×2^29 + 1×2^28 + 
1×2^27 + 1×2^26 + 1×2^25 + 1×2^24 + 1×2^23 + 1×2^22 + 
1×2^21 + 1×2^20 + 1×2^19 + 1×2^18 + 1×2^17 + 1×2^16 + 
1×2^15 + 1×2^14 + 1×2^13 + 1×2^12 + 1×2^11 + 0×2^10 + 
1×2^9 + 1×2^8 + 0×2^7 + 1×2^6 + 0×2^5 + 1×2^4 + 0×2^3 + 
0×2^2 + 0×2^1 + 0×2^0
由此可得:
dec(4294966296) × dec(4294966096) = dec(4294966296)×2^31 + dec(4294966296)×2^30 + 
dec(4294966296)×2^29 + dec(4294966296)×2^28 + dec(4294966296)×2^27 +
dec(4294966296)×2^26 + dec(4294966296)×2^25 + dec(4294966296)×2^24 + 
dec(4294966296)×2^23 + dec(4294966296)×2^22 + dec(4294966296)×2^21 + 
dec(4294966296)×2^20 + dec(4294966296)×2^19 + dec(4294966296)×2^18 + 
dec(4294966296)×2^17 + dec(4294966296)×2^16 + dec(4294966296)×2^15 + 
dec(4294966296)×2^14 + dec(4294966296)×2^13 + dec(4294966296)×2^12 + 
dec(4294966296)×2^11 + 0×2^10 + dec(4294966296)×2^9 + dec(4294966296)×2^8 + 
0×2^7 + dec(4294966296)×2^6 + 0×2^5 + dec(4294966296)×2^4 + 0×2^3 + 
0×2^2 + 0×2^1 + 0×2^0
虽然公式看起来复杂,但将指数视为左移运算符后,乘法运算就转化为移位和加法的组合。

2.3流水线加法结构

对于表达式 num = a + b + c + d + e + f + g + h,可以采用树形流水线结构进行优化:

2.4结果截取

dec(4294966296) × dec(4294966096) = hex(FFFFF76800124F80)
其中16位×16位乘法器的最大位宽为32位,取低32位:
hex(00124F80) = dec(1200000)
至此,一个完整的乘法运算流程得以实现。

3.Verilog硬件实现

3.1 16位流水线乘法器

16位有符号乘法器实现代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2025/03/28 22:54:41
// Design Name: 
// Module Name: multiplier_16
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 16位有符号乘法器
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module multiplier_16
#(  
    parameter A_WIDTH  = 16 ,
    parameter A_SIGNED = 1  ,
    parameter B_WIDTH  = 16 ,
    parameter B_SIGNED = 1  
)
(
    input       clk ,
    input       rst ,
    input   [A_WIDTH-1:0]   data_a,
    input   [B_WIDTH-1:0]   data_b,
    output  [A_WIDTH + B_WIDTH - 1:0]   product
    );
    
    wire [32-1:0]    extend_a;
    wire [32-1:0]    extend_b;
    
    assign extend_a = A_SIGNED ? {{(32-A_WIDTH){data_a[A_WIDTH-1]}}, data_a} : 
                              {(32-A_WIDTH){1'b0}}, data_a};
    assign extend_b = B_SIGNED ? {{(32-B_WIDTH){data_b[B_WIDTH-1]}}, data_b} : 
                              {(32-B_WIDTH){1'b0}}, data_b};

    genvar  idx0;
    reg [31:0]  partial_sum0 [15:0];
    
    generate
        for (idx0 = 0; idx0 <= 15; idx0 = idx0 + 1) begin : gen_stage0
            always @(posedge clk) begin
                if (rst) begin
                    partial_sum0[idx0] <= 0;
                end else begin
                    case({extend_b[2*idx0+1], extend_b[2*idx0]})
                        2'b00: begin
                            partial_sum0[idx0] <= 0;
                        end
                        2'b01: begin
                            partial_sum0[idx0] <= extend_a << (2*idx0);
                        end
                        2'b10: begin
                            partial_sum0[idx0] <= ({extend_a[30:0], 1'b0}) << (2*idx0);
                        end
                        2'b11: begin
                            partial_sum0[idx0] <= ({extend_a[30:0], 1'b0} + extend_a) << (2*idx0);
                        end
                    endcase
                end
            end
        end       
    endgenerate
    
    genvar  idx1;
    reg [31:0]  partial_sum1 [7:0];
    
    generate
        for (idx1 = 0; idx1 <= 7; idx1 = idx1 + 1) begin : gen_stage1
            always @(posedge clk) begin
                if (rst) begin
                    partial_sum1[idx1] <= 0;
                end else begin
                    partial_sum1[idx1] <= partial_sum0[2*idx1] + partial_sum0[2*idx1+1];
                end
            end
        end       
    endgenerate
    
    genvar  idx2;
    reg [31:0]  partial_sum2 [3:0];
    
    generate
        for (idx2 = 0; idx2 <= 3; idx2 = idx2 + 1) begin : gen_stage2
            always @(posedge clk) begin
                if (rst) begin
                    partial_sum2[idx2] <= 0;
                end else begin
                    partial_sum2[idx2] <= partial_sum1[2*idx2] + partial_sum1[2*idx2+1];
                end
            end
        end       
    endgenerate
    
    genvar  idx3;
    reg [31:0]  partial_sum3 [1:0];
    
    generate
        for (idx3 = 0; idx3 <= 1; idx3 = idx3 + 1) begin : gen_stage3
            always @(posedge clk) begin
                if (rst) begin
                    partial_sum3[idx3] <= 0;
                end else begin
                    partial_sum3[idx3] <= partial_sum2[2*idx3] + partial_sum2[2*idx3+1];
                end
            end
        end       
    endgenerate
    
    reg [31:0]  final_result;
    
    always @(posedge clk) begin
        if (rst) begin
            final_result <= 0;
        end else begin
            final_result <= partial_sum3[0] + partial_sum3[1];
        end
    end
    
    assign product = final_result;
    
endmodule

3.2 32位流水线乘法器

32位有符号乘法器实现代码
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/09/11 22:21:12
// Design Name: 
// Module Name: multiplier_pp
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module multiplier
#(  
    parameter A_WIDTH  = 32 ,
    parameter A_SIGNED = 1  ,
    parameter B_WIDTH  = 32 ,
    parameter B_SIGNED = 1  
)
(
    input       clk ,
    input       rst ,
    input   [A_WIDTH-1:0]   data_a,
    input   [B_WIDTH-1:0]   data_b,
    output  [A_WIDTH + B_WIDTH - 1:0]   product
    );
    
    wire [64-1:0]    extend_a;
    wire [64-1:0]    extend_b;
    
    assign extend_a = A_SIGNED ? {{(64-A_WIDTH){data_a[A_WIDTH-1]}}, data_a} : 
                              {(64-A_WIDTH){1'b0}}, data_a};
    assign extend_b = B_SIGNED ? {{(64-B_WIDTH){data_b[B_WIDTH-1]}}, data_b} : 
                              {(64-B_WIDTH){1'b0}}, data_b};

    genvar  idx0;
    reg [63:0]  partial_sum0 [31:0];
    
    generate
        for (idx0 = 0; idx0 <= 31; idx0 = idx0 + 1) begin : gen_stage0
            always @(posedge clk) begin
                if (rst) begin
                    partial_sum0[idx0] <= 0;
                end else begin
                    case({extend_b[2*idx0+1], extend_b[2*idx0]})
                        2'b00: begin
                            partial_sum0[idx0] <= 0;
                        end
                        2'b01: begin
                            partial_sum0[idx0] <= extend_a << (2*idx0);
                        end
                        2'b10: begin
                            partial_sum0[idx0] <= ({extend_a[62:0], 1'b0}) << (2*idx0);
                        end
                        2'b11: begin
                            partial_sum0[idx0] <= ({extend_a[62:0], 1'b0} + extend_a) << (2*idx0);
                        end
                    endcase
                end
            end
        end       
    endgenerate
    
    genvar  idx1;
    reg [63:0]  partial_sum1 [15:0];
    
    generate
        for (idx1 = 0; idx1 <= 15; idx1 = idx1 + 1) begin : gen_stage1
            always @(posedge clk) begin
                if (rst) begin
                    partial_sum1[idx1] <= 0;
                end else begin
                    partial_sum1[idx1] <= partial_sum0[2*idx1] + partial_sum0[2*idx1+1];
                end
            end
        end       
    endgenerate
    
    genvar  idx2;
    reg [63:0]  partial_sum2 [7:0];
    
    generate
        for (idx2 = 0; idx2 <= 7; idx2 = idx2 + 1) begin : gen_stage2
            always @(posedge clk) begin
                if (rst) begin
                    partial_sum2[idx2] <= 0;
                end else begin
                    partial_sum2[idx2] <= partial_sum1[2*idx2] + partial_sum1[2*idx2+1];
                end
            end
        end       
    endgenerate
    
    genvar  idx3;
    reg [63:0]  partial_sum3 [3:0];
    
    generate
        for (idx3 = 0; idx3 <= 3; idx3 = idx3 + 1) begin : gen_stage3
            always @(posedge clk) begin
                if (rst) begin
                    partial_sum3[idx3] <= 0;
                end else begin
                    partial_sum3[idx3] <= partial_sum2[2*idx3] + partial_sum2[2*idx3+1];
                end
            end
        end       
    endgenerate
    
    genvar  idx4;
    reg [63:0]  partial_sum4 [1:0];
    
    generate
        for (idx4 = 0; idx4 <= 1; idx4 = idx4 + 1) begin : gen_stage4
            always @(posedge clk) begin
                if (rst) begin
                    partial_sum4[idx4] <= 0;
                end else begin
                    partial_sum4[idx4] <= partial_sum3[2*idx4] + partial_sum3[2*idx4+1];
                end
            end
        end       
    endgenerate
    
    reg [63:0]  final_result;
    
    always @(posedge clk) begin
        if (rst) begin
            final_result <= 0;
        end else begin
            final_result <= partial_sum4[0] + partial_sum4[1];
        end
    end
    
    assign product = final_result;
    
endmodule

相关文章

可以按小时收费的VPS

很多 VPS 提供商都支持 按小时计费(hourly billing),想短期试用 / 临时搭建节点、测试网络、短期项目等场景非常合适。下面是当前最主流且靠谱的按小时 VPS 选项,分别按不同需求场景整理: 1. Vultr(全球节点,包括日本) 按小时计费 可选机房:东京 / 大阪 / 洛杉矶 / 法兰克福 / 伦敦 … 支持 PayPal(部分情况),但更常用信用卡/PayPal+卡价格参考$...

在 iPhone 上下载国外App

地区/国家限制App Store 会根据 Apple ID 的国家或地区限制应用下载。如果你的 Apple ID 绑定的是中国大陆,就可能无法下载 OpenAI 官方的 ChatGPT 应用,因为它在大陆 App Store 不上架。解决办法:换成美国、加拿大、香港等地区的 Apple ID。或者在现有 Apple ID 上更改地区。注册一个国外 Apple ID(推荐)比如注册 美国区 Appl...

Node.js 中的异步编程:回调与 Promise

Node.js 是一个基于 JavaScript 构建的单线程、非阻塞运行环境,它通过异步编程机制来高效处理多个操作。在执行如文件读取、API 请求或数据库查询等任务时,Node.js 不会等待这些操作完成,而是使用回调函数和 Promise 来避免阻塞主线程。 回调方式实现异步 那么当异步操作完成后,Node.js 如何知道接下来要做什么呢?这就要用到 回调函数(callback)。 回调本质上...

Selenium自动化测试入门指南

Selenium自动化测试入门指南

什么是自动化测试? 自动化测试是指利用软件工具自动执行测试用例,模拟用户操作,如打开网页、点击链接、输入文本等,并验证结果是否符合预期。 其主要优点包括: 大幅减少人工成本 测试速度快 可以在非工作时间运行 支持持续集成和交付 然而,它也存在一些局限性,例如开发成本较高、不适合快速变化的项目、依赖稳定的UI界面等。 自动化测试的应用条件 适合引入自动化测试的情况包括: 手动测试耗时且需要大量...

MariaDB Galera集群故障快速恢复指南

OpenStack控制节点采用三节点MariaDB Galera集群架构。当数据库集群因故障重启时,有时会出现Galera集群无法正常启动的问题。虽然有多种方法可以恢复数据库服务,但如何实现快速启动同时确保数据完整性呢? 通过分析日志发现,MariaDB Galera集群节点宕机时会在日志中输出以下信息: [Note] WSREP: 新集群视图:全局状态: 874d8e7e-5980-11e8-8...

Android 中 EventBus 的通信机制与实现原理深度解析

EventBus 核心设计思想 EventBus 是一个基于观察者模式的事件总线框架,广泛应用于 Android 平台以实现组件解耦。它通过中心化的消息分发机制,使不同层级、不同线程的对象能够以"发布-订阅"方式通信,避免了传统接口回调或广播带来的强依赖问题。 核心角色说明 事件(Event):任意 Java 对象,作为数据载体,如网络状态变更通知、用户登录信息等。 发布者(Publi...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。