基于FPGA的高效乘法器

1、设计思路

  二进制的乘法运算与十进制的乘法运算相似,如下图所示,二进制数据6’b110010乘以二进制数据4’b1011,得到乘积结果10’b1000100110。

在这里插入图片描述

图1 二进制乘法运算

  仔细观察上图发现,乘数最低位为1(上图紫色数据位),则得到紫色数据,乘数第1位为1,将被乘数左移1位,得到橙色数据,然后乘数的第2位是0,0乘以被乘数为0,则舍弃。乘数的第3位为1,则将被乘数左移3位,得到红色数据。然后将紫色、橙色、红色数据相加,得到乘积。

  这就是二进制乘法运算思路,乘法的运算时间与乘数的位宽有关。乘数为1时需要左移的位数与数据位的权重其实有关,但是FPGA实现这样的运算并不算特别简单,还能不能简化?

  当乘数或者被乘数为0时,直接输出0即可,不需要运算。

  当乘数和被乘数均不等于0时,乘积的初始值为0,每个时钟周期把乘数右移一位,被乘数左移一位,如果乘数最低位为1,则乘积等于乘积加上此时被乘数的值,当乘数为1时,计算完成,输出乘积的运算结果。

  计算流程如下图所示,其实就是将图1的运算拆分,每次只需要判断乘数的最低位是否为1,从而确定乘积是否需要加上被乘数,乘数每右移一次,被乘数就必须左移一次,这样来能保证乘积不变。当乘数变为1时,移位结束,此时乘数最低位为1,被乘数加上乘积后作为运算结果,完成运算。

在这里插入图片描述

图2 简化的移位相加运算

2、代码设计

  由此,就可以编写FPGA代码了,为了模块通用,位宽全部进行参数化设计,增加开始计算信号和模块忙闲指示信号,以及乘积计算完成的有效指示信号。

  端口信号如下表所示:

表1 端口信号列表

信号I/O位宽含义
clkI1系统时钟
rst_nI1系统复位,低电平有效
startI1开始运算,高电平有效
multiplicandIMULT_D被乘数
multiplierIMULT_R乘数
productOMULT_D+ MULT_R乘积
product_vldO1乘积有效指示信号,高电平有效
rdyO1模块空闲指示信号,高电平有效。

  当开始计算信号有效且乘数与被乘数均不等于0且模块不处于运算状态时,把开始计算信号start_f拉高,运算状态标志信号flag初始值为0,当检测到开始运算start_f有效时拉高,当乘数为1时结束运算,flag信号拉低,对应代码如下所示:

    //开始计算信号有效且乘数和被乘数均不等于0;assign start_f = (~flag) && (start && (multiplicand != 0) && (multiplier != 0));//运算标志信号,always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值为0;flag <= 1'b0;endelse if(start_f)begin//开始运算时拉高flag <= 1'b1;endelse if(multiplier_r == 1)begin//运算结束时拉低;flag <= 1'b0;endend

  然后就是对乘数和被乘数信号的处理,如下所示。初始值均为0,当开始运算时,将输入的乘数和被乘数保存到相应寄存器中,如果flag信号有效,则每个时钟周期把乘数右移1位,把被乘数左移1位。

    always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值为0;multiplicand_r <= {{MULT_D + MULT_R}{1'b0}};multiplier_r <= {{MULT_R}{1'b0}};endelse if(start_f)begin//当计算开始时;multiplicand_r <= multiplicand;//将被乘数加载到被乘数寄存器中。multiplier_r <= multiplier;//将乘数加载到乘积寄存器中。endelse if(flag)begin//正常计算标志信号有效时,被乘数左移一位,乘数右移一位。multiplicand_r <= multiplicand_r << 1;multiplier_r <= multiplier_r >> 1;endend

  之后就是乘积的运算,出数字为0,当开始信号有效时,不管乘数和被乘数的状态是什么,将乘积寄存器设置为0。在之后的运算中,如果flag有效并且乘数最低位为1,则把乘积寄存器的值与被乘数寄存器的值相加,得到乘积寄存器数据。

    //计算乘法运算结果,开始信号有效时,将乘积清零。//当乘数寄存器最低位为1时,加上此时被乘数的值。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值为0;product_r <= {{MULT_D + MULT_R}{1'b0}};endelse if(start)//当乘数或者被乘数为0时,乘积输出0.product_r <= {{MULT_D + MULT_R}{1'b0}};else if(flag && multiplier_r[0])begin//如果乘积的最低位为1,则把乘积的高位数据与被乘数相加。product_r <= product_r + multiplicand_r;endend

  最后就是乘积运算的输出,如果开始信号有效时,乘数和被乘数其中一个为0,则乘积输出0,拉高乘积有效指示信号。如果在计算乘积的过程中(flag为高电平)且乘数等于1,则表示计算完成,把乘积寄存器值加上此时被乘数的值作为乘积输出,并且把乘积有效指示信号拉高一个时钟周期。乘积有效指示信号在其余时间均为0。

    //输出乘积和乘积有效指示信号;always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值为0;product <= {{MULT_D + MULT_R}{1'b0}};product_vld <= 1'b0;endelse if((~flag) && (start && ((multiplicand == 0) || (multiplier == 0))))beginproduct <= {{MULT_D + MULT_R}{1'b0}};//如果开始计算时,乘数或者被乘数为0,则直接输出0;product_vld <= 1'b1;endelse if(flag && (multiplier_r == 1))begin//计算完成时,把计算结果输出,且乘积有效指示信号拉高;product <= product_r + multiplicand_r;product_vld <= 1'b1;endelse begin//其余时间把有效指示信号拉低;product_vld <= 1'b0;endend

  最后就是模块忙闲指示信号,当开始信号有效或者模块处于计算状态时拉低,其余时间拉高,上游模块检测到该信号后就可以拉高start信号,开始下一次运算。注意该信号只能使用组合逻辑电路生成,并且上游只能通过时序电路检测该信号状态。

    //生成模块忙闲指示信号;always@(*)begin//当开始信号有效或者标志信号有效时,模块处于工作状态;if(start || flag)rdy = 1'b0;else//否则模块处于空闲状态;rdy = 1'b1;end

  代码就这么多,相对比较简单,参考代码如下:

module mult #(parameter			MULT_D		        =		8		        ,//被乘数位宽;parameter			MULT_R		        =		4		         //乘数位宽;
)(input									        clk		        ,//系统时钟信号;input									        rst_n	        ,//系统复位信号,低电平有效;input                                           start           ,//开始运算信号,高电平有效;input               [MULT_D - 1 : 0]            multiplicand    ,//被乘数;input               [MULT_R - 1 : 0]            multiplier      ,//乘数;output  reg         [MULT_D + MULT_R - 1 : 0]   product         ,//乘积输出;output  reg                                     product_vld     ,//乘积有效指示信号,高电平有效;output  reg                                     rdy              //模块忙闲指示信号,高电平表示空闲;
);reg                                             flag            ;reg                 [MULT_D - 1 : 0]            multiplier_r    ;//乘数的寄存器reg                 [MULT_D + MULT_R - 1 : 0]   multiplicand_r  ;//被乘数的寄存器。reg                 [MULT_D + MULT_R - 1 : 0]   product_r       ;//乘积寄存器;wire                                            start_f         ;//开始计算信号有效且乘数和被乘数均不等于0;assign start_f = (~flag) && (start && (multiplicand != 0) && (multiplier != 0));//运算标志信号,always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值为0;flag <= 1'b0;endelse if(start_f)begin//开始运算时拉高flag <= 1'b1;endelse if(multiplier_r == 1)begin//运算结束时拉低;flag <= 1'b0;endendalways@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值为0;multiplicand_r <= {{MULT_D + MULT_R}{1'b0}};multiplier_r <= {{MULT_R}{1'b0}};endelse if(start_f)begin//当计算开始时;multiplicand_r <= multiplicand;//将被乘数加载到被乘数寄存器中。multiplier_r <= multiplier;//将乘数加载到乘积寄存器中。endelse if(flag)begin//正常计算标志信号有效时,被乘数左移一位,乘数右移一位。multiplicand_r <= multiplicand_r << 1;multiplier_r <= multiplier_r >> 1;endend//计算乘法运算结果,开始信号有效时,将乘积清零。//当乘数寄存器最低位为1时,加上此时被乘数的值。always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值为0;product_r <= {{MULT_D + MULT_R}{1'b0}};endelse if(start)//当乘数或者被乘数为0时,乘积输出0.product_r <= {{MULT_D + MULT_R}{1'b0}};else if(flag && multiplier_r[0])begin//如果乘积的最低位为1,则把乘积的高位数据与被乘数相加。product_r <= product_r + multiplicand_r;endend//输出乘积和乘积有效指示信号;always@(posedge clk or negedge rst_n)beginif(rst_n==1'b0)begin//初始值为0;product <= {{MULT_D + MULT_R}{1'b0}};product_vld <= 1'b0;endelse if((~flag) && (start && ((multiplicand == 0) || (multiplier == 0))))beginproduct <= {{MULT_D + MULT_R}{1'b0}};//如果开始计算时,乘数或者被乘数为0,则直接输出0;product_vld <= 1'b1;endelse if(flag && (multiplier_r == 1))begin//计算完成时,把计算结果输出,且乘积有效指示信号拉高;product <= product_r + multiplicand_r;product_vld <= 1'b1;endelse begin//其余时间把有效指示信号拉低;product_vld <= 1'b0;endend//生成模块忙闲指示信号;always@(*)begin//当开始信号有效或者标志信号有效时,模块处于工作状态;if(start || flag)rdy = 1'b0;else//否则模块处于空闲状态;rdy = 1'b1;endendmodule

3、模块仿真

  对应的TestBench如下所示:

`timescale 1 ns/1 ns
module test();localparam  CYCLE		        =   10          ;//系统时钟周期,单位ns,默认10ns;localparam  RST_TIME	        =   10          ;//系统复位持续时间,默认10个系统时钟周期;localparam  MULT_D              =   8           ;//被乘数位宽;localparam  MULT_R              =   4           ;//乘数位宽;reg			                        clk         ;//系统时钟,默认100MHz;reg			                        rst_n       ;//系统复位,默认低电平有效;reg                                 start       ;//开始运算信号,高电平有效;reg     [MULT_D - 1 : 0]            multiplicand;//被乘数;reg     [MULT_R - 1 : 0]            multiplier  ;//乘数;wire    [MULT_D + MULT_R - 1 : 0]   product     ;//乘积输出;wire                                product_vld ;//乘积有效指示信号,高电平有效;wire                                rdy         ;//模块忙闲指示信号,高电平表示空闲;//例化需要仿真的模块;mult #(.MULT_D         ( MULT_D        ),//被乘数位宽;.MULT_R         ( MULT_R        ) //乘数位宽;)u_mult (.clk            ( clk           ),//系统时钟,默认100MHz;.rst_n          ( rst_n         ),//系统复位,默认低电平有效;.start          ( start         ),//开始运算信号,高电平有效;.multiplicand   ( multiplicand  ),//被乘数;.multiplier     ( multiplier    ),//乘数;.product        ( product       ),//乘积输出;.product_vld    ( product_vld   ),//乘积有效指示信号,高电平有效;.rdy            ( rdy           ) //模块忙闲指示信号,高电平表示空闲;);//生成周期为CYCLE数值的系统时钟;initial beginclk = 0;forever #(CYCLE/2) clk = ~clk;end//生成复位信号;initial beginrst_n = 1;start = 0;multiplicand = 0;multiplier = 0;#2;rst_n = 0;//开始时复位10个时钟;#(RST_TIME*CYCLE);rst_n = 1;#(5*CYCLE);multiplicand = 4;multiplier = 15;start = 1'b1;#(CYCLE);start = 1'b0;#(CYCLE);repeat(30)begin//产生30组随机数据进行测试;@(posedge rdy);#(8*CYCLE);#1;multiplicand = {$random};//产生随机数据,作为被乘数;multiplier = {$random};//产生随机数据,作为乘数;start = 1'b1;#(CYCLE);start = 1'b0;end@(posedge rdy);#(8*CYCLE);$stop;//停止仿真;endendmodule

  简要截取仿真的一段数据进行查看,如下所示,start信号有效时,乘数为13,被乘数为92。之后被相应的寄存器暂存,然后flag信号为高电平时,每个时钟周期乘数寄存器右移一位,被乘数寄存器数据左移一位。

  如果乘数最低位为1,则乘积寄存器的值就会与被乘数的值相加,得到新的乘积寄存器值,最后当乘数为1时,蓝色的乘积信号就会把乘积寄存器的值460与被乘数的值736相加得到1196作为输出,完成乘法运算。

在这里插入图片描述

图3 仿真截图

  至此,该模块的设计到此结束,该模块的位宽全部进行了参数化处理,需要修改乘数和被乘数的位宽时,只需要修改位宽的参数即可,代码不需要做任何修改。

  该模块在后续设计中可能作为子模块出现,因为这种靠移位和加法的运算,在面对较大位宽的乘法运算时,可以得到更高的时钟频率。

  源文件可以在公众号后台回复“基于FPGA的乘法器“(不包括引号)获取。

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

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

相关文章

机器学习:什么是监督学习和无监督学习

目录 一、监督学习 &#xff08;一&#xff09;回归 &#xff08;二&#xff09;分类 二、无监督学习 聚类 一、监督学习 介绍&#xff1a;监督学习是指学习输入到输出&#xff08;x->y&#xff09;映射的机器学习算法&#xff0c;监督即理解为&#xff1a;已知正确答案…

Eureka基础知识总结(微服务)

Eureka Eureka是Netflix开发的一个服务发现框架&#xff0c;它提供了一种在微服务架构中注册和发现服务的方法。通过使用Eureka&#xff0c;我们可以实现负载均衡、故障转移和服务的自动注册等功能。本文将对Eureka的相关知识进行总结。 一、Eureka的基本概念 Eureka Server…

Arduino开发实例-LoRa通信(基于SX1278 LoRa)

LoRa通信(基于SX1278 LoRa) 文章目录 LoRa通信(基于SX1278 LoRa)1、硬件准备1.1 Semtech LoRa SX1278 收发器2、LoRa介绍2.1 什么是LoRa2.2 SX1278 LoRa 模块2.1 LoRa 频段(按国家/地区划分)3、硬件接线原理图4、代码实现4.1 数据发送与接收4.2 远距离控制LED实现4.2.1 硬…

leetcode-二叉树的前序遍历

144. 二叉树的前序遍历 递归方法 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def preorderTraversal(…

期末考试发等级发成绩,就用易查分!

期末考试后&#xff0c;学校老师如何发布私密成绩&#xff1f;易查分可以轻松创建等级、成绩查询系统&#xff0c;让家长仅看到自己孩子成绩。 支持查询后留言反馈&#xff0c;电子签名确认签收等高级功能&#xff0c;节省老师沟通时间&#xff0c;大大提升工作效率。 &#x1…

linux安装docker(入门一)

环境&#xff1a;centos 7(linux) 网站 官网: https://docs.docker.com/ Docker Hub 网站: https://hub.docker.com/ 容器官方概述 一句话概括容器&#xff1a;容器就是将软件打包成标准化单元&#xff0c;以用于开发、交付和部署。 容器镜像是轻量的、可执行的独立软件包 &…

蓝桥杯分糖果、最小化战斗力差距、小蓝零花钱

分糖果 问题描述 最近暑期特训算法班的同学们表现出色&#xff0c;他们的老师肖恩决定给他们分发糖果。肖恩购买了 个不同种类的糖果&#xff0c;用小写的阿拉伯字母表示。每个糖果必须分发给一个同学&#xff0c;并且每个同学至少要分到一个糖果。同学们的开心程度定义为他们所…

【百面机器学习】读书笔记(一)

本文系列主要作用就是读书笔记&#xff0c;自己看的话比较杂&#xff0c;没怎么归类过&#xff0c;所以现在跟着这个分类走一遍。本文主要内容为前两章&#xff0c;特征工程和模型评估。 如果我想起一些相关的内容也会做适当的补充&#xff0c;主打就是一个intuition&#xff…

OpenCV书签 #直方图算法的原理与相似图片搜索实验

1. 介绍 直方图算法&#xff08;Image Histogram Algorithm&#xff09; 通过统计图像中各个颜色值的分布情况来提供关于图像颜色特征的信息&#xff0c;它可以用来衡量两张图片在颜色分布上的相似度&#xff0c;进而可以用来进行图像相似度的比较&#xff0c;因此&#xff0c…

电脑录屏软件大比拼,哪个最适合你?

现如今&#xff0c;电脑录屏软件成为了许多用户记录、分享和教学的重要工具。从游戏玩家到专业制作人员&#xff0c;都需要高效的录屏软件。本文将介绍三款优秀的电脑录屏软件&#xff0c;通过详细的步骤和简洁的介绍&#xff0c;帮助用户轻松掌握这些工具的使用方法。 电脑录屏…

附1:k8s服务器初始化

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 关联文章&#xff1a; 《RKE快速搭建离线k8s集群并用rancher管理界面》 1.创建普通用户sre并赋予sudo权限 # adduser sre # ec…

【动态规划】879. 盈利计划

作者推荐 【动态规划】【广度优先搜索】【状态压缩】847 访问所有节点的最短路径 本文涉及知识点 动态规划汇总 LeetCode879. 盈利计划 集团里有 n 名员工&#xff0c;他们可以完成各种各样的工作创造利润。 第 i 种工作会产生 profit[i] 的利润&#xff0c;它要求 group[…

大模型独立解答30道国际奥数难题,能力接近金牌选手!

谷歌旗下的AI研究机构DeepMind和纽约大学的研究人员联合开发了一个AI模型——AlphaGeometry。 AlphaGeometr是一种神经符号模型,内置了大语言模型和符号推理引擎等功能,主要用于解决各种超难几何数学题&#xff0c;同时可以自动生成易于查看的解题原理。 为了验证AlphaGeomet…

React Router v6 改变页面Title

先说正事再闲聊 1、在路由表加个title字段 2、在index包裹路由 3、在App设置title 闲聊&#xff1a; 看到小黄波浪线了没 就是说默认不支持title字段了 出来的提示&#xff0c; 所以我本来是像下面这样搞的&#xff0c;就是感觉有点难维护&#xff0c;就还是用上面的方法了 …

Linux配置yum源以及基本yum指令

文章目录 一、yum介绍二、什么是软件包三、配置yum源四、一键配置yum源【三步走】五、yum指令搜索软件安装软件卸载软件 六、其他yum指令更新内核更新软件更新指定软件显示所有可更新的软件清单卸载指定包并自动移除依赖包删除软件包&#xff0c;以及软件包数据和配置文件 一、…

c中realloc重新分配大小时,空间增长方式的问题

今天在写leetcodeT15.三数之和的时候遇到了一个比较奇怪的问题&#xff1a; 部分代码&#xff1a; int size 5;//设置初始解的空间为5个 int ** res (int **)malloc(sizeof(int*)*size);//分配size大小的二维数组存结果 ....//检查空间够不够&#xff0c;不够重新分配空间 …

安全 专题

[实践总结] 日志注入问题&#xff08;log4j2&#xff09; [实践总结] Java 防止SQL注入的四种方案 [实践总结] 如何防护 order by 导致的SQL注入 [实践总结] 限制正则表达式匹配次数/时间 防止DoS攻击 [实践总结] java XML解析防止外部实体注入 [Ref] yaml.load的漏洞…

【vue-cli详细介绍】

vue-cli详细介绍 1. vue-cli2. 特点3. 安装Vue CLI4. 创建新项目5. CLI 插件6. GUI界面7. 构建和服务8. 配置9. 结语 1. vue-cli Vue CLI 是一套用于快速开发 Vue.js 应用程序的完整系统&#xff0c;它提供了从项目创建和管理到编码、打包、部署的整个流程的工具&#xff0c;V…

快速上手MyBatis Plus:简化CRUD操作,提高开发效率!

MyBatisPlus 1&#xff0c;MyBatisPlus入门案例与简介1.1 入门案例步骤1:创建数据库及表步骤2:创建SpringBoot工程步骤3:勾选配置使用技术步骤4:pom.xml补全依赖步骤5:添加MP的相关配置信息步骤6:根据数据库表创建实体类步骤7:创建Dao接口步骤8:编写引导类步骤9:编写测试类 1.2…

Redis常见类型及常用命令

目录 常见的数据类型 一、String类型 1、简介 2、常用命令 &#xff08;1&#xff09;新建key &#xff08;2&#xff09;设值取值 ​编辑 &#xff08;3&#xff09;批量操作 &#xff08;4&#xff09;递增递减 3、原子性操作 4、数据结构 二、list类型 1、list常…