2.6.3 常用数字处理算法的Verilog实现

1.加法器的Verilog实现

串行加法器

组合逻辑的加法器可以利用真值表,通过与门和非门简单地实现。假设 和 表示两个加数, 表示和, 表示来自低位的进位, 表示向高位的进位。每个全加器都执行如下的逻辑表达式:

这样可以得到加法器的一种串行结构。因此,式(2.1)所示的加法器也被称为串行加法器。如图2-20给出了一个4位串行加法器的结构示意图。

图2-20 串行加法器的结构示意图

在无线通信的信号处理中,常常要用到多位数字量的加法运算。如果用串行加法器实现,速度较慢,而并行加法器就能满足要求,并且结构并不复杂。现在普遍使用的并性加法器是超前进位加法器,只是在几个全加器的基础上增加了一个超前进位形成逻辑,以减少由于逐步进位信号的传递所造成的时延。图2-21给出了一个4位并行加法器的结构示意图。

图2-21 串行加法器的示意图

在4位并行加法器的基础上,可以递推出16位、32位和64位的快速并行加法器。

流水线加法器

在使用了并行加法器后,仍旧只有在输出稳定后才能输入新的数进行下一次计算,即计算的节拍必须大于运算电路的延迟;此外,许多门级电路和布线的延迟会随着位数的增加而累加,因此加法器的频率还是受到了限制。但如果采用流水线,就有可能将一个算术操作分解为一些小规模的基本操作,将进位和中间值存储在寄存器中,并在下一个时钟周期内继续运算,这样就可以提高电路的利用效率。将流水线规则应用于FPGA中,只需要很少或根本不需要额外的成本。这是因为每个逻辑单元都包含两个触发器,大多数情况下这两个触发器或者没有用到,或者用于存储布线资源,那么就可以利用其来实现流水线结构。如果采用了流水线后,加法器的速度仍然不能满足需要的话,可以采用第3章中将会提到的串并转换来进一步提高计算的并行度。        由于一个slice中有两个触发器,还需要有1个触发器来作为进位输出,那么采用 级流水线,就可以构造一个最大位数为 位的加法器。下面给出一个16位流水线加法器的代码。例2-24 16位2级流水线加法器的Verilog设计module adder16_2(cout ,sum ,clk ,cina ,cinb ,cin) ;input [15 :0 ]cina ,cinb ;input clk ,cin ;output [15 :0 ] sum;output cout ;reg cout ;reg cout1 ;reg[7 :0 ] sum1 ;reg[15 :0 ] sum;always @(posedge clk) begin // 低8 位相加;{cout1 , sum1} = {cina [7], cina [ 7 : 0 ]} + {cinb[7], cinb [ 7 : 0 ]} +cin ;endalways @(posedge clk) begin // 高8 位相加,并连成16位{cout ,sum} = {{cina [15], cina [15 :8 ] }+ {cinb [15], cinb[15 :8]} + cout1 , sum1} ;endendmodule上述程序经过synplify Pro综合后,得到如图2-22所示的RTL级结构图。

2-22 16位加法器的RTL结构图

在ModelSim 6.2b中完成仿真,其结果如图2-23所示,正确地实现了16比特加法。

图2-23 16位加法器的RTL结构图

2.乘法器的Verilog实现

串行乘法器

两个N位二进制数x 、y 的乘积,最简单的方法就是利用移位操作来实现,用公式可以表示为:

                                                                                                      (2.3)

这样输入量随着k的位置连续地变化,然后累加

。例2-25 用Verilog实现一个8位串行乘法器module ade (clk, x, y, p);input clk;input [7:0] x, y;output [15:0] p;reg [15:0] p;parameter s0=0, s1=1, s2=2;reg [2:0] count;reg [1:0] state;reg [15:0] p1, t; // 比特位加倍reg [7:0] y_reg;always @(posedge clk) begincase (state)s0 : begin // 初始化y_reg <= y;state <= s1;count = 0;p1 <= 0;t <= {{8{x[7]}},x};ends1 : begin // 处理步骤if (count == 7) //判断是否处理结束state <= s2;else beginif (y_reg[0] == 1)p1 <= p1 + t;y_reg <= y_reg >> 1; //移位t <= t << 1;count <= count + 1;state <= s1;endends2 : beginp <= p1;state <= s0;endendcaseendendmodule上述程序在Synplify Pro中综合后,得到如图2-24所示的RTL级结构示意图。

图2-24 串行乘法器的RTL结构图

图2-25给出了串行乘法器模块在ModelSim中的仿真结果,验证了功能的正确性。

图2-25 串行乘法器的局部仿真结果示意图

从仿真结果可以看出,上述串行乘法器,速度比较慢,时延很大,但这种乘法器的优点是所占用的资源是所有类型乘法器中最少的,在低速的信号处理中有着广泛的应用。

流水线乘法器

一般的快速乘法器通常采用逐位并行的迭代阵列结构,将每个操作数的N位都并行地提交给乘法器。但是一般对于FPGA来讲,进位的速度快于加法的速度,这种阵列结构并不是最优的。所以可以采用多级流水线的形式,将相邻的两个部分乘积结果再加到最终的输出乘积上,即排成一个二叉树形式的结构,这样对于N位乘法器需要log2(N)级来实现。一个8位乘法器,如图2-26所示。

图2-26流水线乘法器结构图

例2-26 用Verilog HDL实现一个4位的流水线乘法器module mul_addtree(mul_a, mul_b, mul_out, clk, rst_n);parameter MUL_WIDTH = 4;parameter MUL_RESULT = 8;input [MUL_WIDTH-1 : 0] mul_a;input [MUL_WIDTH-1 : 0] mul_b;input clk;input rst_n;output [MUL_RESULT-1 : 0] mul_out;reg [MUL_RESULT-1 : 0] mul_out;reg [MUL_RESULT-1 : 0] stored0;reg [MUL_RESULT-1 : 0] stored1;reg [MUL_RESULT-1 : 0] stored2;reg [MUL_RESULT-1 : 0] stored3;reg [MUL_RESULT-1 : 0] add01;reg [MUL_RESULT-1 : 0] add23;always @ (posedge clk or negedge rst_n)beginif(!rst_n)begin //初始化寄存器变量mul_out <= 8'b0000_0000;stored0 <= 8'b0000_0000;stored1 <= 8'b0000_0000;stored2 <= 8'b0000_0000;stored3 <= 8'b0000_0000;add01 <= 8'b0000_0000;add23 <= 8'b0000_0000;endelsebegin //实现移位相加stored3 <= mul_b[3]?{1'b0,mul_a,3'b0}: 8'b0;stored2 <= mul_b[2]?{2'b0,mul_a,2'b0}: 8'b0;stored1 <= mul_b[1]?{3'b0,mul_a,1'b0}: 8'b0;stored0 <= mul_b[0]?{4'b0,mul_a}: 8'b0;add01 <= stored1 + stored0;add23 <= stored3 + stored2;mul_out <= add01 + add23;endendendmodule上述程序在Synplify Pro软件中综合后,得到如图2-27所示的RTL级结构示意图。

图2-27 流水线乘法器的RTL结构示意图

图2-28给出了流水线乘法器模块在ModelSim中的仿真结果,验证了功能的正确性。

图2-28 流水线乘法器的局部仿真结果示意图

从仿真结果可以看出,上述流水线乘法器比串行加法器的速度快很多,在非高速的信号处理中有着广泛的应用。至于高速信号的乘法一般需要利用FPGA芯片中内嵌的硬核DSP单元来实现。3.无符号除法器的Verilog实现两个无符号二进制数(如正整数)相除的时序算法是通过“减并移位”的思想来实现的,即从被除数中重复地减去除数,直到已检测到余数小于除数。这样可以通过累计减法运算的次数而得到商;而余数是在减法运算结束时被除数中的剩余值。当除数较小时,这种基本电路都必须进行多次减法,因此效率都不高。图2-29给出了一种更加高效的除法器基本结构[3]。在进行两个数相除的运算时,通常采用的步骤是调整除数与被除数使其最高位相对齐,然后反复地从被除数中减去除数,并将除数向被除数的最低位移动,且在高效除法器结构中可以并行运行这些操作步骤。而在具体的硬件实现中,是通过将被除数寄存器的内容不断地向除数的最高位移动来完成除法运算的。

图2-29 8位被除数,4位除数的无符号二进制字自调整除法器

在设计除法器结构的过程中应特别小心。在图中的减法运算步骤中,必须要将除数和被除数对齐,这取决于它们的相应大小和每个字的最高一位1的相对位置。同样,被除数寄存器也应向左扩展一位,以适应可能出现的已调整的除法寄存器内容初始值超过被除数寄存器中相应4位值的情况,在这种情况下,在进行减法操作之前,应从被除数的最高位移出一个1。例如(1100)2与(0111)2相除,应首先将被除数向左移,为下一步减法运算调整好被除数。因此,该机器的控制器会变得更加复杂,而且要包括能使除数和被除数移动的控制信号,如图2-29所示。        该物理结构将调整除数字与被除数的8位数据通道中的最左边4位对齐。在操作中,被除数字不断地从右向左移动,而且每一步都要从已调整的被除数的相应位中减去除数,这种操作取决于除数是否比被除数选定部分的对应值小。调整机器使得从被除数中减去的不是除数而是除数与2的幂的最大乘积,这样在当除数较小时就可以消去一些需要重复进行的减法运算。这种调整被称为是自调整的,因为它在一个除法运算序列的开始就能自动判断是否需要调整除数或被除数,这取决于它们最左非0位的相对位置。在除法运算中经常性地启动对两个字的调整使得其最高位为1的方法效率较低,因为这可能会需要更多的移位。所采用的方法是在一开始就将除数移到被除数的最左非0位(而不是被除数的最低位)。        有两种需要对数据通路字进行初始调整的情况:(1)被除数最左4位的值小于除数的值(例如(1100)2除以(1110)2),(2)除数的最低位为0,同时除数字节可以向左移动,而且仍然可以去除被除数(例如(1100)2除以(0101)2)。对于前者,应将被除数依次向左移动1位直到扩展1位的被除数的最左5位等于或大于除数,或者直到不能再移位为止。而对于后者,则应将除数向左移直到再移动所得到的字节不能去除被除数字节的最左4位为止(不包括扩展位)。余数位在除法运算序列结束时的物理位置取决于被除数是否进行了移位调整。因此,可将调整移位记录下来,并用来控制状态机调整在执行序列结束后的余数值。        图2-30给出了自调整除法器的状态转移图[3]。在一个给定状态下,从一个状态节点出发的支路中所使用的控制标记仅适于该支路,而在其他没有明确使用该标记离开当前状态的支路中被视为是不成立的。在任何离开一个状态节点的支路中都没有出现的标记被认为是无关紧要的。只有S_idle状态下才会给出复位信号,而在其他的所有状态上复位信号均被视为是异步动作。       该机器的状态与它的动作有关。S_Adivr状态下Shift_divisor的动作是将除法调整到被除数的最高非0位;S_Adivr状态下Shift_dividend的动作将调整被除数寄存器以进行减法运算;S_div状态下同时进行实际的减法运算和许多移位操作。状态S_Adivn和S_Adivr下变量Max将检测所允许的最大移位何时发生。

图2-30 自调整除法器的状态转移图

例2-27 用Verilog实现一个被除数为8位,除数为4位的高效除法器module divider(clock,reset,word1,word2,Start,quotient,remainder,Ready,Error);parameter L_divn = 8;parameter L_divr = 4;parameter S_idle = 0, S_Adivr = 1, S_Adivn = 2, S_div = 3, S_Err =4;parameter L_state = 3, L_cnt = 4, Max_cnt = L_divn - L_divr;input [L_divn-1 : 0] word1; //被除数数据通道input [L_divr-1 : 0] word2; //除数数据通道input Start, clock, reset;output [L_divn-1 : 0] quotient; //商output [L_divn-1 : 0] remainder; //余数output Ready, Error;reg [L_state-1 : 0] state, next_state;reg Load_words, Subtract, Shift_dividend, Shift_divisor;reg [L_divn-1 : 0] quotient;reg [L_divn : 0] dividend; //扩展的被除数reg [L_divr-1 : 0] divisor;reg [L_cnt-1 : 0] num_shift_dividend, num_shift_divisor;reg [L_divr : 0] comparison;wire MSB_divr = divisor[L_divr-1];wire Ready = ((state == S_idle) && !reset);wire Error = (state == S_Err);wire Max = (num_shift_dividend== Max_cnt + num_shift_divisor);wire sign_bit = comparison[L_divr];always@(state  or dividend or divisor or MSB_divr) begin //从被除数中减去除数case(state)S_Adivr: if(MSB_divr == 0)comparison = dividend[L_divn : L_divn - L_divr] +{1'b1, ~(divisor << 1)} + 1'b1;elsecomparison = dividend[L_divn : L_divn - L_divr] +{1'b1, ~divisor[L_divr-1 : 0]} + 1'b1;default: comparison = dividend[L_divn : L_divn - L_divr] +{1'b1, ~divisor[L_divr-1 : 0]} + 1'b1;endcaseend//将余数移位来对应于整体的移位assign remainder = (dividend[L_divn-1 : L_divn-L_divr]) - num_shift_divisor;always@(posedge clock) beginif(reset)state <= S_idle;elsestate <= next_state;end//次态与控制逻辑always@(state or word1 or word2 or state or comparison or sign_bit or Max) beginLoad_words = 0;Shift_dividend = 0;Shift_divisor = 0;Subtract = 0;case(state)S_idle: case(Start)0: next_state = S_idle;1: if(word2 == 0)next_state = S_Err;else if(word1) beginnext_state = S_Adivr;Load_words = 1;endelsenext_state = S_idle;endcaseS_Adivr: case(MSB_divr)0: if(sign_bit == 0) beginnext_state = S_Adivr;Shift_divisor = 1; //可移动除数endelse if(sign_bit == 1) beginnext_state = S_Adivn; //不可移动除数end1: next_state = S_div;endcaseS_Adivn: case({Max, sign_bit})2'b00: next_state = S_div;2'b01: beginnext_state = S_Adivn;Shift_dividend = 1;end2'b10: beginnext_state = S_idle;Subtract = 1;end2'b11: next_state = S_idle;endcaseS_div: case({Max, sign_bit})2'b00: beginnext_state = S_div;Subtract = 1;end2'b01: next_state = S_Adivn;2'b10: beginnext_state = S_div;Subtract = 1;end2'b11: beginnext_state = S_div;Shift_dividend = 1;endendcasedefault: next_state = S_Err;endcaseendalways@(posedge clock)begin //寄存器,数据通道操作if(reset)begindivisor <= 0;dividend <= 0;quotient <= 0;num_shift_dividend <= 0;num_shift_divisor <= 0;endelse if(Load_words == 1) begindividend <= word1;divisor <= word2;quotient <= 0;num_shift_dividend <= 0;num_shift_divisor <= 0;endelse if(Shift_divisor) begindivisor <= divisor << 1;num_shift_divisor <= num_shift_divisor + 1;endelse if(Shift_dividend) begindividend <= dividend << 1;quotient <= quotient << 1;num_shift_dividend <= num_shift_dividend + 1;endelse if(Subtract) begindividend[L_divn : L_divn-L_divr] <= comparison;quotient[0] <= 1;endendendmodule上述程序经过Synplify综合后得到如图2-31所示的RTL级结构。

图2-31 无符号高效除法器的RTL级结构图

在ModelSim中经过仿真,其仿真结果如图2-31所示。仿真中输入了两组数据,且在输出信号Ready为高时输出相应的商和余数。当被除数为57,除数为6时,可以看到商和余数分别为9和3;当被除数为98,除数为9时,可以看到商和余数分别为10和8。这表明上述程序的正确性。

图2-32 无符号高效除法器的仿真波形

4. CORDIC算法的Verilog实现

CORDIC算法的原理

CORDIC算法可以将多种难以用硬件电路直接实现的复杂运算分解为统一的简单的移位、加迭代运算,而且结构规则、运算周期可以预测、适合于VLSI实现。许多数字信号处理算法,如DXT、FFT、复数滤波器、格形滤波器、基于Givens旋转的QR分解、奇异值/特征值分解、最小二乘求解以及线性系统求解等,都很容易用圆周旋转或双曲旋转来描述其基本的操作,因此都可以用CORDIC算法得到很好地实现。因此以CORDIC为核心的FPGA应用日益受到人们的重视。        CORDIC是用于计算广义矢量旋转的一种迭代方法。由J.D.Volder于1959年提出的[6],主要用于三角函数、双曲函数、指数和对数的运算[7,8]。该算法使得矢量的旋转和定向运算不需要三角函数表以及乘法、开方、反三角函数等复杂的运算,仅需要进行加减和移位即可。1971年,Walther提出了统一的CORDIC算法[9],引入了参数m将CORDIC实现的三种迭代模式:三角运算、双曲运算和线性运算统一于一个表达式下,形成目前所用到的CORDIC算法最基本的数学基础。该算法的基本思想是通过一系列固定的、与运算基数相关的角度不断偏摆以逼近所需的旋转角度,可由下列等式进行描述:

                        (2.4)                                                   (2.5)

X(n),Y(n) 和Z(n)为所期望得到的函数。根据m=1 、-1或0,可以将上面的运算分别称为圆周旋转运算、双曲旋转运算或线形旋转运算。其中:

                                                        (2.6)

使结果Z(n)的旋转称为旋转模式(rotation mode),使结果Y(n)=0的旋转称为向量模式(vector mode)。为了能达到所要求的结果,各旋转角 要满足下列条件:

                                                                         (2.7)                                                                 (2.8)

=0或1,

 和

是非负整数值。最通常的微转角选择方法为:当m=1 时,

 ;当m=-1 时

;当m=0时

 。此时每一级迭代运算可以简化为:

                                                 (2.9)                                                   (2.10)                                                       (2.11)

可以仅由加法、减法和移位来实现,本级的微转角旋转方向

 由上一级运算结果和所处的旋转模式决定。在所有级旋转之后须执行一次模校正运算,即乘以模校正因子

 ,一旦如上旋转一系列微转角之后,无论每个微转角的方向如何,对于确定的m值,当n趋向无穷大时,模校正因子趋近于一个极限值km 。因为CORDIC算法本身是一种逐位逼近算法,所以一般不论旋转级数n是多少,都直接应用其极限的二进制码作为模校正因子。对于不同的m值、工作模式和初始值,可以产生不同的结果,如表2-3所示。

表2-3 CORDIC在不同情况下的输出

CORDIC算法的Verilog实现

CORDIC算法的实现方式有两种:简单状态机法和高速全流水线处理器,前者主要采用折叠/迭代方式,后者采用展开/流水线式。1. 简单状态机结构如果计算时间不严格的话,可以采用图2-33所示的状态机。在每个周期内都将精确地计算一次式(2.9) ~ 式(2.11)所示的迭代。其中最复杂的就是两个筒形移位器。

图2-33 CORDIC算法状态机

2. 流水线CORDIC结构流水线CORDIC虽然占用的硬件资源较多,但是流水结构可以提高数据的吞吐率(Throughput)。对于大多数的DSP算法来说,存在很多同一条指令连续处理很长一段数据的情况,此时高吞吐率更有意义。从当前VLSI的发展趋势上来看,芯片内的门资源相对富裕,对流水线CORDIC的实现规模约束很小。此外,流水线CORDIC不存在迭代式CORDIC的反馈回路,使得单元结构更加规则,有利于VLSI实现。图2-34给出CORDIC算法的一般流水线结构:

图2-34 CORDIC算法的一般流水线结构

用verilog实现检测1的个数_[转]常用数字处理算法的Verilog实现相关推荐

  1. [转]常用数字处理算法的Verilog实现

      2.6.3 常用数字处理算法的Verilog实现 1.加法器的Verilog实现 串行加法器 组合逻辑的加法器可以利用真值表,通过与门和非门简单地实现.假设 和 表示两个加数, 表示和, 表示来自 ...

  2. 用verilog实现检测1的个数_入门指南:用Python实现实时目标检测(内附代码)

    全文共6821字,预计学习时长20分钟 来源:Pexels 从自动驾驶汽车检测路上的物体,到通过复杂的面部及身体语言识别发现可能的犯罪活动.多年来,研究人员一直在探索让机器通过视觉识别物体的可能性. ...

  3. 检测1的个数_冲床电永磁夹紧系统(PMCS),实现1分钟的快速换模~

    冲床电永磁夹紧系统 PMCS=Press Mag Clamp System ▼ 电永磁夹紧系统,是靠强力永久磁石(钕铁硼.铝镍钴)吸引, 固定模具的夹紧系统.模具尺寸不必统一,只需按下按钮即可瞬间夹紧 ...

  4. C语言——二分法查找一个数_数组

    C语言--二分法查找一个数_数组 问题描述: 针对一个按顺序排列的一维数组,用户输入一个数,如何辨别它是否存在?是数组中的第几位? 编程思想: 采用二分法,以最中间的数和用户输入的数进行比较,逐步缩小 ...

  5. (128)System Verilog下降沿检测实例

    (128)System Verilog下降沿检测实例 1.1 目录 1)目录 2)FPGA简介 3)System Verilog简介 4)System Verilog下降沿检测实例 5)结语 1.2 ...

  6. notepad++调用VIVADO语法检测工具进行verilog语法检测

    Notepad++ 色彩过于明显,已不再使用该编辑器 Notepad++安装NppExec插件 在notepad++界面中选择 插件>插件管理>安装NppExec 获取VIVADO语法检测 ...

  7. kmeans中的k的含义_聚类分析:kmeans 算法簇个数的确定

    kmeans算法是无监督聚类学习中最常见.最常用的算法之一,其基本原理如下: 1.随机初始化k个聚类中心点,并计算数据中每个点到k个点的距离: 2.将每个数据点分到距离聚类中心点最近的聚类中心中: 3 ...

  8. 模糊c均值聚类_六种常用的文本聚类方法介绍

    文本聚类算法介绍 分类和聚类都是文本挖掘中常使用的方法,他们的目的都是将相似度高的对象归类,不同点在于分类是采用监督学习,分类算法按照已经定义好的类别来识别一篇文本,而聚类是将若干文本进行相似度比较, ...

  9. fifo算法模拟_我是怎样学习算法的?(V1.0)

    我相信很多人都是因为找工作才去看数据结构与算法,我自己也是出于这个目的.我自己在学习数据结构和算法上走了很多弯路,原因就是没有系统地的去学习.看了很多书,刷了很多题,浪费了不少时间,所以希望看到这篇文 ...

最新文章

  1. 全套CRC校验的算法
  2. k8s部署ingress:使用heptio-contour部署ingress controller(通过sealos安装,非nginx-ingress类型)
  3. 大厂Java岗面试心得记录
  4. java 时间处理经典案例
  5. 第004讲 浮动窗口 表单及表单控件
  6. 【优化覆盖】基于matlab虚拟力算法求解无线网络传感覆盖优化问题【含Matlab源码 1187期】
  7. C++常见设计模式之工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)
  8. 20210610 线程数不断飙升问题定位
  9. Win 10.0.16299.15 禁用五笔输入法Shift切换中英文
  10. java logger 乱码_Log4j乱码
  11. python 古典密码第一弹(凯撒密码,Playfair密码,维吉尼亚密码)
  12. android 相册view,Android直接把当前View保存到相册
  13. 深入理解Java虚拟机开篇
  14. BI学习笔记之六 - 数据仓库介绍
  15. 零输入响应和零状态响应
  16. Appium TestNg Maven Android Eclipse java简单启动实例
  17. 滴滴亮相第15届“开源中国开源世界”高峰论坛,并获重要奖项
  18. NoiOpenjudge水题选刷之_搜索
  19. vue 高德地图 定位插件 地图实例插件 获取点击的地方的经纬度和具体地址
  20. 关于计算机的英语名言,关于玩游戏的名言 英语

热门文章

  1. 从 HTTP 瞎逼逼到 HTTP/2
  2. 说的话可以转换为文字的笔记APP
  3. 泛泰binx和ota升级包下载工具Android版[2013.6.7提供源代码]
  4. python爬虫scrapy爬取新闻标题及链接_18Python爬虫---CrawlSpider自动爬取新浪新闻网页标题和链接...
  5. JAVA多媒体网络教学计算机毕业设计Mybatis+系统+数据库+调试部署
  6. Spring源码学习第四天==>初识Refresh()
  7. c语言设计程序实现顺序冒泡_C语言学习 顺序程序设计
  8. 【解决方案】“博物馆热”背后,如何建设安防视频监控体系保障文物安全?
  9. 我们游戏后台架构学习
  10. css3动画实现3d旋转效果