Verilog | 除法--试商法

news/2024/11/23 8:00:10/

试商法

采用试商法实现除法运算,对于32位的除法,需要至少32个时钟周期才能得到除法结果。下面是试商法的一般过程。

设被除数是m,除数是n,商保存在s中,被除数的位数是k,其计算步骤如下(为了便于说明,在此处将所有数据的最低位称为第1位,而不称为第0位)。

1、取出被除数的最高位m[k],使用被除数的最高位减去除数n,如果结果大于等于0,则商的s[k]为1,反之为0。

2、如果上一步得出的结果是0,表示当前的被减数小于除数,则取出被除数剩下的值的最高位m[k-1],与当前被减数组合做为下一轮的被减数;如果上一步得出的结果是1,表示当前的被减数大于除数,则利用上一步中减法的结果与被除数剩下的值的最高位m[k-1]组合做为下一轮的被减数。然后,设置k等于k-1。

3、新的被减数减去除数,如果结果大于等于0,则商的s[k]为1,否则s[k]为0,后面的步骤重复2-3,直到k等于1。

其他

  • 该方法对于有符号数和无符号数除法均适用,对于有符号数需要处理符号位,最后在运算结果中添加符号位
  • 在处理器中,除法运算需要多个周期才能完成,需要注意暂停流水线

除法器设计

单步运算设计

单步除法计算时,单步被除数位宽(信号 dividend)需比原始除数(信号 divisor)位宽多 1bit 才不至于溢出。

为了便于流水,输出端需要有寄存器来存储原始的除数(信号 divisor 和 divisor_kp)和被除数信息(信号 dividend_ci 和 dividend_kp)。

单步的运算结果就是得到新的 1bit 商数据(信号 merchant)和余数(信号 remainder)。

为了得到最后的除法结果,新的 1bit 商数据(信号 merchant)还需要与上一周期的商结果(merchant_ci)进行移位累加。

// parameter M means the actual width of divisor
module    divider_cell#(parameter N=5,parameter M=3)(input                     clk,input                     rstn,input                     en,input [M:0]               dividend,input [M-1:0]             divisor,input [N-M:0]             merchant_ci , //上一级输出的商input [N-M-1:0]           dividend_ci , //原始除数output reg [N-M-1:0]      dividend_kp,  //原始被除数信息output reg [M-1:0]        divisor_kp,   //原始除数信息output reg                rdy ,output reg [N-M:0]        merchant ,  //运算单元输出商output reg [M-1:0]        remainder   //运算单元输出余数);always @(posedge clk or negedge rstn) beginif (!rstn) beginrdy            <= 'b0 ;merchant       <= 'b0 ;remainder      <= 'b0 ;divisor_kp     <= 'b0 ;dividend_kp    <= 'b0 ;endelse if (en) beginrdy            <= 1'b1 ;divisor_kp     <= divisor ;  //原始除数保持不变dividend_kp    <= dividend_ci ;  //原始被除数传递if (dividend >= {1'b0, divisor}) beginmerchant    <= (merchant_ci<<1) + 1'b1 ; //商为1remainder   <= dividend - {1'b0, divisor} ; //求余endelse beginmerchant    <= merchant_ci<<1 ;  //商为0remainder   <= dividend ;        //余数不变endend // if (en)else beginrdy            <= 'b0 ;merchant       <= 'b0 ;remainder      <= 'b0 ;divisor_kp     <= 'b0 ;dividend_kp    <= 'b0 ;endendendmodule

顶层模块

将单步计算的余数(信号 remainder)和原始被除数(信号 dividend)对应位的 1bit 数据重新拼接,作为新的单步被除数输入到下一级单步除法计算单元。

其中,被除数、除数、及商的数据信息也要在下一级运算单元中传递。

//parameter N means the actual width of dividend
//using 29/5=5...4
module    divider_man#(parameter N=5,parameter M=3,parameter N_ACT = M+N-1)(input                     clk,input                     rstn,input                     data_rdy ,  //数据使能input [N-1:0]             dividend,   //被除数input [M-1:0]             divisor,    //除数output                    res_rdy ,output [N_ACT-M:0]        merchant ,  //商位宽:Noutput [M-1:0]            remainder ); //最终余数wire [N_ACT-M-1:0]   dividend_t [N_ACT-M:0] ;wire [M-1:0]         divisor_t [N_ACT-M:0] ;wire [M-1:0]         remainder_t [N_ACT-M:0];wire [N_ACT-M:0]     rdy_t ;wire [N_ACT-M:0]     merchant_t [N_ACT-M:0] ;//初始化首个运算单元divider_cell      #(.N(N_ACT), .M(M))u_divider_step0( .clk              (clk),.rstn             (rstn),.en               (data_rdy),//用被除数最高位 1bit 数据做第一次单步运算的被除数,高位补0.dividend         ({{(M){1'b0}}, dividend[N-1]}),.divisor          (divisor),                  .merchant_ci      ({(N_ACT-M+1){1'b0}}),   //商初始为0.dividend_ci      (dividend[N_ACT-M-1:0]), //原始被除数//output.dividend_kp      (dividend_t[N_ACT-M]),   //原始被除数信息传递.divisor_kp       (divisor_t[N_ACT-M]),    //原始除数信息传递.rdy              (rdy_t[N_ACT-M]),.merchant         (merchant_t[N_ACT-M]),   //第一次商结果.remainder        (remainder_t[N_ACT-M])   //第一次余数);genvar               i ;generatefor(i=1; i<=N_ACT-M; i=i+1) begin: sqrt_stepxdivider_cell      #(.N(N_ACT), .M(M))u_divider_step(.clk              (clk),.rstn             (rstn),.en               (rdy_t[N_ACT-M-i+1]),.dividend         ({remainder_t[N_ACT-M-i+1], dividend_t[N_ACT-M-i+1][N_ACT-M-i]}),   //余数与原始被除数单bit数据拼接.divisor          (divisor_t[N_ACT-M-i+1]),.merchant_ci      (merchant_t[N_ACT-M-i+1]),.dividend_ci      (dividend_t[N_ACT-M-i+1]),//output.divisor_kp       (divisor_t[N_ACT-M-i]),.dividend_kp      (dividend_t[N_ACT-M-i]),.rdy              (rdy_t[N_ACT-M-i]),.merchant         (merchant_t[N_ACT-M-i]),.remainder        (remainder_t[N_ACT-M-i]));end // block: sqrt_stepxendgenerateassign res_rdy       = rdy_t[0];assign merchant      = merchant_t[0];  //最后一次商结果作为最终的商assign remainder     = remainder_t[0]; //最后一次余数作为最终的余数endmodule

testbench

取被除数位宽为 5,除数位宽为 3,testbench 中加入自校验,描述如下:

`timescale 1ns/1nsmodule test ;parameter    N = 5 ;parameter    M = 3 ;reg          clk;reg          rstn ;reg          data_rdy ;reg [N-1:0]  dividend ;reg [M-1:0]  divisor ;wire         res_rdy ;wire [N-1:0] merchant ;wire [M-1:0] remainder ;//clockalways beginclk = 0 ; #5 ;clk = 1 ; #5 ;end//driverinitial beginrstn      = 1'b0 ;#8 ;rstn      = 1'b1 ;#55 ;@(negedge clk ) ;data_rdy  = 1'b1 ;dividend  = 25;      divisor      = 5;#10 ;   dividend  = 16;      divisor      = 3;#10 ;   dividend  = 10;      divisor      = 4;#10 ;   dividend  = 15;      divisor      = 1;repeat(32)    #10   dividend   = dividend + 1 ;divisor      = 7;repeat(32)    #10   dividend   = dividend + 1 ;divisor      = 5;repeat(32)    #10   dividend   = dividend + 1 ;divisor      = 4;repeat(32)    #10   dividend   = dividend + 1 ;divisor      = 6;repeat(32)    #10   dividend   = dividend + 1 ;end//对输入延迟,便于数据结果同周期对比,完成自校验reg  [N-1:0]   dividend_ref [N-1:0];reg  [M-1:0]   divisor_ref [N-1:0];always @(posedge clk) begindividend_ref[0] <= dividend ;divisor_ref[0]  <= divisor ;endgenvar         i ;generatefor(i=1; i<=N-1; i=i+1) beginalways @(posedge clk) begindividend_ref[i] <= dividend_ref[i-1];divisor_ref[i]  <= divisor_ref[i-1];endendendgenerate//自校验reg  error_flag ;always @(posedge clk) begin# 1 ;if (merchant * divisor_ref[N-1] + remainder != dividend_ref[N-1] && res_rdy) beginb      //testbench 中可直接用乘号而不考虑运算周期error_flag <= 1'b1 ;endelse beginerror_flag <= 1'b0 ;endend//module instantiationdivider_man  #(.N(N), .M(M))u_divider(.clk              (clk),.rstn             (rstn),.data_rdy         (data_rdy),.dividend         (dividend),.divisor          (divisor),.res_rdy          (res_rdy),.merchant         (merchant),.remainder        (remainder));//simulation finishinitial beginforever begin#100;if ($time >= 10000)  $finish ;endendendmodule // test

仿真结果
在这里插入图片描述


http://www.ppmy.cn/news/533658.html

相关文章

Qt读写csv文件

第一种办法&#xff1a;使用QStringList及QTextStream实现CSV文件读写 适用于&#xff1a;对于小型数据量的CSV文件。 优点&#xff1a; 实现简单&#xff1a;使用QStringList和QTextStream实现CSV读写操作非常简单&#xff0c;能够快速上手&#xff1b;代码量少&#xff1a…

为什么独立站做不好?80%的人都走过这5个弯路

最近接触了不少卖家&#xff0c;发现不少独立站因为犯下一些常见的错误&#xff0c;导致最终失败&#xff0c;但是这些都是完全可以避免的。 那些新手卖家常见的问题就是许多人对独立站运营没有基本概念&#xff0c;同时并没有把脑袋中的零散的技巧串起来。要知道独立站运营是…

CSS 字体英文

&#xff08;内容来自网络&#xff09; CSS font-family 字体 宋体 SimSun&#xff08;浏览器默认&#xff09; 黑体 SimHei 微软雅黑 Microsoft Yahei 微软正黑体 Microsoft JhengHei 楷体 …

Vivado使用技巧之外部编辑器配置

目录 一、前言 二、编辑器配置 2.1 环境变量添加 2.2 环境变量验证 2.3 Vivado设置 2.4 配置验证 2.5 解决Vivado配置失败问题 一、前言 Vivado自带的默认编辑器功能受限&#xff0c;不如第三方编辑器Eclipse&#xff0c;Notepad&#xff0c;Sublime功能强大。因此&…

HDFS的工作原理是怎么样的?是如何实现HA模式?

原文链接&#xff1a;http://www.ibearzmblog.com/#/technology/info?id714dcb3957e29185493239b269a9ef65 前言 HDFS是能够提供一个分布式文件存储的系统&#xff0c;在大型数据文件的存储中&#xff0c;能够提供高吞吐量的数据访问&#xff0c;那么它是如何实现数据文件的…

电机调速执行

一、建立思维导图&#xff0c;将功能分析近而转换成技术要点&#xff0c;逐步实现。 二、编码器 1、机械编码器 &#xff08;1&#xff09;机械编码器是什么&#xff0c;张啥样&#xff1f; 如下图&#xff0c;这个就是我们生活应用中常见的机械编码器&#xff0c;我们又叫旋…

MySQL数据表查询

&#x1f607;作者介绍&#xff1a;一个有梦想、有理想、有目标的&#xff0c;且渴望能够学有所成的追梦人。 &#x1f386;学习格言&#xff1a;不读书的人,思想就会停止。——狄德罗 ⛪️个人主页&#xff1a;进入博主主页 &#x1f5fc;专栏系列&#xff1a;进入MySQL知识专…

博客背景图片

转载于:https://www.cnblogs.com/naijun/p/10665597.html