verilog编程之乘法器的实现

2024-01-29 15:36

本文主要是介绍verilog编程之乘法器的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

知识储备
首先来回顾一下乘法是如何在计算机中实现的。
假设现在有两个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,   // 乘法源操作数1input  [31:0] mult_op2,   // 乘法源操作数2output [63:0] product,    // 乘积output        mult_end   // 乘法结束信号
);//乘法正在运算信号和结束信号reg mult_valid;assign mult_end = mult_valid & ~(|multiplier); //乘法结束信号:乘数全0always @(posedge clk)   //①beginif (!mult_begin || mult_end)    //如果没有开始或者已经结束了beginmult_valid <= 1'b0;     //mult_valid 赋值成0,说明现在没有进行有效的乘法运算endelsebeginmult_valid <= 1'b1;//     test <= 1'b1;endend//两个源操作取绝对值,正数的绝对值为其本身,负数的绝对值为取反加1wire        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)  //②beginif (mult_valid)begin    // 如果正在进行乘法,则被乘数每时钟左移一位multiplicand <= {multiplicand[62:0],1'b0};  //被乘数x每次左移一位。endelse if (mult_begin) begin   // 乘法开始,加载被乘数,为乘数1的绝对值multiplicand <= {32'd0,op1_absolute};endend//加载乘数,运算时每次右移一位,相当于yreg  [31:0] multiplier;always @ (posedge clk)  //③beginif(mult_valid)begin       //如果正在进行乘法,则乘数每时钟右移一位multiplier <= {1'b0,multiplier[31:1]}; //相当于乘数y右移一位endelse if(mult_begin)begin   //乘法开始,加载乘数,为乘数2的绝对值multiplier <= op2_absolute;endend// 部分积:乘数末位为1,由被乘数左移得到;乘数末位为0,部分积为0wire [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时,激发此段语句的执行,但语句的执行需要时间beginif (mult_valid)beginproduct_temp <= product_temp + partial_product;end      else if (mult_begin)beginproduct_temp <= 64'd0;endend//乘法结果的符号位和乘法结果reg product_sign;    //乘积结果的符号always @ (posedge clk)  // 乘积⑤beginif (mult_valid)beginproduct_sign <= op1_sign ^ op2_sign;endend //若乘法结果为负数,则需要对结果取反+1assign 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

这篇关于verilog编程之乘法器的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/657370

相关文章

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja

使用Python和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1