verilog编程之乘法器的实现

news2024/11/18 19:40:52

知识储备
首先来回顾一下乘法是如何在计算机中实现的。
假设现在有两个32位带符号定点整数x和y,我们现在要让x和y相乘,然后把乘积存放在z中,大家知道,两个32位数相乘,结果不会超过64位,因此z的长度应该为64位。
z = x * y中,x是被乘数,在Verilog代码中 multiplicand表示,y是乘数,在代码中用multiplier表示。因为x和y都是带符号数,所以应该是用补码乘法,但是如果对x和y求绝对值,让两个绝对值相乘,然后再判断正负,效果和补码乘法是相同。后面给出的Verilog代码就是基于这种思路编写的。两个32位整数相乘,实际上是进行了32次加法操作。下面以两个4位二进制数相乘来说明乘法实现的过程。

从上图中可以看到,被乘数x为1000,乘数y为1001,上面的乘法过程是手工运算的一个步骤,而计算机在做乘法时就是模拟上述手工运算的执行过程。因为是两个4位数相乘,所以结果应该是四个数加和得到的。先判断y的最低位是0还是1,如果是1,则需要把x加到部分积上,若为0,则需要把0加到部分积上(实际上加0的这个过程计算机并不执行,因为加0对部分积没有任何影响),x左移一位,之后再让y右移一位,若y为0,则循环结束,否则继续此循环过程。流程图如下。

流程图中,x因为需要左移,所以32位长度的x应该用一个64位寄存器来存储,这样才能保证x左移后不会发生高位丧失。

代码实现与分析
multiply.v文件如下

`timescale 1ns / 1ps
//*************************************************************************
//   > 文件名: multiply.v
//   > 描述  :乘法器模块,低效率的迭代乘法算法,使用两个乘数绝对值参与运算
//   > 作者  : LOONGSON
//   > 日期  : 2016-04-14
//*************************************************************************
module multiply(              // 乘法器
    input         clk,        // 时钟
    input         mult_begin, // 乘法开始信号
    input  [31:0] mult_op1,   // 乘法源操作数1
    input  [31:0] mult_op2,   // 乘法源操作数2
    output [63:0] product,    // 乘积
    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;     //mult_valid 赋值成0,说明现在没有进行有效的乘法运算
        end
        else
        begin
            mult_valid <= 1'b1;
       //     test <= 1'b1;
        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[62:0],1'b0};  //被乘数x每次左移一位。
        end
        else if (mult_begin) 
        begin   // 乘法开始,加载被乘数,为乘数1的绝对值
            multiplicand <= {32'd0,op1_absolute};
        end
    end

    //加载乘数,运算时每次右移一位,相当于y
    reg  [31:0] multiplier;
    
    always @ (posedge clk)  //③
    begin
    if(mult_valid)
    begin       //如果正在进行乘法,则乘数每时钟右移一位
         multiplier <= {1'b0,multiplier[31:1]}; //相当于乘数y右移一位
    end
    else if(mult_begin)
    begin   //乘法开始,加载乘数,为乘数2的绝对值
        multiplier <= op2_absolute;
        end
    end
    // 部分积:乘数末位为1,由被乘数左移得到;乘数末位为0,部分积为0
    wire [63:0] partial_product;
    assign partial_product = multiplier[0] ? multiplicand:64'd0;        //若此时y的最低位为1,则把x赋值给部分积partial_product,否则把0赋值给partial_product
    
    //累加器
    reg [63:0] product_temp;        //临时结果
    always @ (posedge clk)  //④//clk信号从0变为1时,激发此段语句的执行,但语句的执行需要时间
    begin
        if (mult_valid)
        begin
            product_temp <= product_temp + partial_product;
        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
    
    assign product = product_sign ? (~product_temp+1) : product_temp;
endmodule


要看懂这段程序,很重要的一点是要弄明白Verilog语言中always语句的并发执行,这和我们以前接触过的高级语言不同,Verilog代码中的语句可以不按顺序执行,这个有点像多线程,也就是说多个任务同时进行。
上面的代码中共有5个always语句,每个always语句都是时钟信号clk的上跳沿触发,也就是说当clk从0变为1的时候,会触发always语句的执行。
下面介绍一下每个always语句的功能。
1.第一个always块

 always @(posedge clk)   //①
    begin
        if (!mult_begin || mult_end)    //如果没有开始或者已经结束了
        begin
            mult_valid <= 1'b0;     //mult_valid 赋值成0,说明现在没有进行有效的乘法运算
        end
        else
        begin
            mult_valid <= 1'b1;
       //     test <= 1'b1;
        end
    end

如果乘法还没开始(mult_begin == 0)或者乘法已经结束(mult_end == 1),则乘法有效信号(mult_valid)赋值0,也就是说此时没有进行有效的乘法。否则,乘法有效信号(mult_valid)赋值1。
2.第二个always块

always @ (posedge clk)  //②
    begin
        if (mult_valid)
        begin    // 如果正在进行乘法,则被乘数每时钟左移一位
            multiplicand <= {multiplicand[62:0],1'b0};  //被乘数x每次左移一位。
        end
        else if (mult_begin) 
        begin   // 乘法开始,加载被乘数,为乘数1的绝对值
            multiplicand <= {32'd0,op1_absolute};
        end
    end

如果乘法有效(mult_valid == 1),则被乘数(multiplicand)左移一位。如果乘法无效且乘法刚开始(mult_begin == 1),那么初始化被乘数(multiplicand)为mult_op1的绝对值。
3.第三个always块

 always @ (posedge clk)  //③
    begin
    if(mult_valid)
    begin       //如果正在进行乘法,则乘数每时钟右移一位
         multiplier <= {1'b0,multiplier[31:1]}; //相当于乘数y右移一位
    end
    else if(mult_begin)
    begin   //乘法开始,加载乘数,为乘数2的绝对值
        multiplier <= op2_absolute;
        end
    end

如果乘法有效(mult_valid == 1),乘数右移一位。如果乘法无效且乘法刚开始(mult_begin == 1),初始化乘数(multiplier)为mult_op2的绝对值。

4.第四个always块

 always @ (posedge clk)  //④//clk信号从0变为1时,激发此段语句的执行,但语句的执行需要时间
    begin
        if (mult_valid)
        begin
            product_temp <= product_temp + partial_product;
        end      
        else if (mult_begin)
        begin
        product_temp <= 64'd0;
        end
     end

如果乘法有效,则让临时结果(product_temp)加上部分积(partial_product),如果乘法无效且乘法刚开始(mult_begin == 1),那么初始化临时结果为0。部分积的内容在程序中的第71行


如果乘数y的最低位为0,则把0赋值给部分积,否则把乘数x赋值给部分积。
5.第五个always块

 always @ (posedge clk)  // 乘积⑤
    begin
        if (mult_valid)
        begin
              product_sign <= op1_sign ^ op2_sign;
        end
    end 

如果乘法有效,则计算乘积的符号,计算方法为乘数的符号和被乘数的符号进行异或。

编写思路
程序仿真开始时,bestbench.v文件会对输入信号进行初始化。使得mult_begin为1,并且给出两个操作数mult_op1和mult_op2分别作为乘数和被乘数。时钟信号clk每5ns变化一次,也就是说五个always块每隔10ns被触发一次。对mult_op1和mult_op2进行分解,分解出他们的符号和绝对值,后面的运算是让mult_op1和mult_op2的绝对值进行运算,相当于是两个无符号数的乘法。当乘法信号有效后,也就是说乘法开始之后,把x的绝对值赋值给一个64位的reg型变量multiplicand,把y的绝对值赋值给一个32位reg型变量multiplier,根绝multiplier最低位是0还是1,决定着64位wire型变量partial_product赋值0还还是赋值multiplicand。临时结果product_temp加上部分积之后再把加的结果赋值给自己,根据mult_op1和mult_op2的符号计算乘积结果的符号。最终的乘积结果(product)是wire型变量,用assign赋值,每当临时结果(product_temp)发生改变时,product也立即发生变化。

提示
reg型变量必须通过过程赋值语句赋值!不能通过assign语句赋值!而wire型数据不能放在过程块内赋值。

最后附上本实验用到的所有文件:

https://www.cnblogs.com/lures/p/14525787.html

可以使用百度云进行免费下载
链接:https://pan.baidu.com/s/1W3yFQ0kzJQfnkI4VyKyyJg
提取码:83lj

如果本篇文章对你有所帮助,欢迎使用CSDN下载来支持我
链接:https://download.csdn.net/download/weixin_43074474/13728746
链接:https://blog.csdn.net/weixin_43074474/article/details/90473709

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1417632.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

总结6(循环(for))

循环 定义&#xff1a; 某些代码会被重复执行 分类&#xff1a; for 1.格式 for(1; 2; 3) 语句A; 2.执行的流程&#xff08;1,2,A,3 2,A,3 2,A,3..........&#xff09; 单个for循环的使用 多个for循环的嵌套使用 1). for&#xff08;1; 2; 3&#xff09; for&#xff0…

用通俗易懂的方式讲解:一种全新的大模型检索增强生成方法

如何使大型语言模型更加事实、正确和可靠&#xff1f; 检索增强生成&#xff08;RAG&#xff09;是一种有效的方法&#xff0c;可以缓解大型语言模型的基本局限性&#xff0c;如幻觉和缺乏最新知识。 然而&#xff0c;如果您曾尝试过RAG&#xff0c;您会同意我所说的RAG易于原…

【每日一题】4.LeetCode——环形链表

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

RK3568平台开发系列讲解(Linux系统篇)互斥锁使用

🚀返回专栏总目录 文章目录 一、互斥锁API二、使用互斥锁的步骤三、互斥锁使用规则四、使用案例沉淀、分享、成长,让自己和他人都能有所收获!😄 一、互斥锁API 在Linux中,你可以使用互斥锁(Mutex)来实现多线程或多进程之间的互斥访问。互斥锁用于确保在同一时间只有一…

24. 两两交换链表中的节点(力扣LeetCode)

文章目录 24. 两两交换链表中的节点题目描述解题思路只使用一个临时节点使用两个临时节点 24. 两两交换链表中的节点 题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff0…

STM32学习笔记(二) —— 调试串口

我们在调试程序时&#xff0c;经常会使用串口打印相关的调试信息&#xff0c;但是单片机串口不能直接与 PC 端的 USB 接口通讯&#xff0c;需要用到一个USB转串口的芯片来充当翻译的角色。我们使用的开发板上有这个芯片&#xff0c;所以在打印调试信息的时候直接使用USB线连接开…

每日一题——LeetCode1365.有多少小于当前数字的数字

方法一 暴力循环 对于数组里的没一个元素都遍历一遍看有多少元素小于当前元素 var smallerNumbersThanCurrent function(nums) {let n nums.length;let ret [];for (let i 0; i < n; i) {let count 0;for (let j 0; j < n; j) {if (nums[j] < nums[i]) {count…

【C语言】数组的应用:三子棋游戏

由于代码较长&#xff0c;为了增加可读性&#xff0c;我们把代码分别写到game.h&#xff0c;game.c&#xff0c;test.c&#xff0c;里面&#xff0c;其中game.h用来声明函数&#xff0c;实现函数功能的代码在game.c&#xff0c;测试游戏的代码在test.c 为了方便后续的更改&…

qt学习:http+访问百度智能云api实现车牌识别

目录 登录到百度智能云&#xff0c;找到文字识别 完成操作指引 开通 查看车牌识别的api文档 ​编辑​编辑 查看自己应用的api key 查看回应的数据格式 编程步骤 ui界面编辑 添加模块&#xff0c;头文件和定义变量 新建两个类&#xff0c;一个图像Image类&#xff0c…

深度学习之卷积神经网络进阶版

上一讲我们介绍了卷积神经网络和多层感知机&#xff0c;也就是全链接网络。他们在网络架构上是串行的结构&#xff0c;也就是在每一层与每一层之间&#xff0c;前面一层的输出&#xff0c;是后面一层的输入。 在神经网络里面&#xff0c;我们可能会有更加复杂的结构&#xff0…

Pycharm连接云算力远程服务器(AutoDL)训练深度学习模型全过程

前言&#xff1a;在上一篇windows搭建深度学习环境中&#xff0c;我试图使用笔记本联想小新air14的mx350显卡训练一个图像检测的深度学习模型&#xff0c;但是训练时长大概需要几天时间远超我的预期&#xff0c;所以我便选择租用GPU进行训练&#xff0c;在对多家平台对比后找到…

[BT]小迪安全2023学习笔记(第19天:Web开发-.NET项目)

第19天 名词解释 .NET 是一种由Microsoft开发的软件框架&#xff0c;用于构建和运行Windows操作系统上的应用程序。它提供了一个广泛的工具和库集合&#xff0c;支持多种编程语言&#xff0c;包括C#、VB.NET、F#等。.NET的主要目标是提供一种一致的开发平台&#xff0c;使开发…

《HTML 简易速速上手小册》第1章:HTML 入门(2024 最新版)

文章目录 1.1 HTML 简介与历史&#xff08;&#x1f609;&#x1f310;&#x1f47d;踏上神奇的网页编程之旅&#xff09;1.1.1 从过去到现在的华丽蜕变1.1.2 市场需求 —— HTML的黄金时代1.1.3 企业中的实际应用 —— 不只是个网页1.1.4 职业前景 —— 未来属于你 1.2 基本 H…

SQL注入:二次注入

SQL注入系列文章&#xff1a; 初识SQL注入-CSDN博客 SQL注入&#xff1a;联合查询的三个绕过技巧-CSDN博客 SQL注入&#xff1a;报错注入-CSDN博客 SQL注入&#xff1a;盲注-CSDN博客 目录 什么是二次注入&#xff1f; 二次注入演示 1、可以注册新用户 2、可以登录->…

数据湖技术之平台建设篇2

数据湖技术之平台建设篇1&#xff0c;主要介绍了湖仓平台建设的前三个主要工作&#xff0c;本次主要继续上次的建设工作介绍&#xff0c;聊一聊一站式湖仓服务平台的相关管理能力建设以及针对小文件的处理。 一. 一站式湖仓服务平台的相关管理能力 主要是将相关能力落地到平台…

day03-今日佳人功能实现

课程说明 首页功能说明系统架构说明实现今日佳人功能实现推荐用户的列表接口增加缓存功能整合前端联调测试 1、首页 在用户登录成功后&#xff0c;就会进入首页&#xff0c;首页中有今日佳人、推荐好友、探花、搜附近等功能。 2、系统架构 在开发完SSO系统中的登录功能后&…

【Spring实战】31 Spring Boot3 集成 Gateway 微服务网关

文章目录 1. 定义2. 功能3. 示例代码1) 创建一个业务服务2&#xff09;创建一个网关服务3&#xff09;启动服务4&#xff09;验证 4. 代码参考结语 1. 定义 Spring Cloud Gateway 是一个基于 Spring Framework 的开源网关服务&#xff0c;用于构建微服务架构中的 API 网关。它…

SpringCloud LoadBalancer

SpringCloud LoadBalancer 1.什么是LoadBalancer LoadBalancer&#xff08;负载均衡器&#xff09;是一种网络设备或软件机制&#xff0c;用于分发传入的网络流量负载请求到多个后端目标服务器上&#xff0c;从而实现系统资源的均衡利用和提高系统的可用性和性能。 负载均衡器…

前端工程化之:webpack1-7(入口和出口)

前置知识 node 环境下 "./" 代表的意思&#xff1a; 模块化代码中&#xff0c;比如 require("./") &#xff0c;表示当前 js 文件所在的目录。在路径处理中&#xff0c; "./" 表示 node 运行目录。__dirname&#xff1a;所有情况下&#xff0c;…

Python tkinter (8) ——Spinbox控件

Python的标准Tk GUI工具包的接口 tkinter系列文章 python tkinter窗口简单实现 Python tkinter (1) —— Label标签 Python tkinter (2) —— Button标签 Python tkinter (3) —— Entry标签 Python tkinter (4) —— Text控件 Python tkinter (5) 选项按钮与复选框 Pyt…