[VHDL]

玩转VHDL-004除法器-附函数解读

[复制链接]
2004|3
手机看帖
扫描二维码
随时随地手机跟帖
ucx|  楼主 | 2017-9-19 16:52 | 显示全部楼层 |阅读模式

除法器逻辑复杂,其特点决定了在FPGA内,甚至CPU(除51外),均无除法器硬核。本节给出实时除法器RT_Divider的完整代码,可不经修改直接使用。

RT_Divider实现通用的定点数除法,功能为:分子÷分母=商…余数。分子与分母的比特宽度可根据实际运用情况合理选取,余数的比特宽同分母。商的比特宽度默认值为分子宽度-分母宽度,也可以根据实际情况自由选取。除法器完成除法运算需要的周期数为商的比特宽度。在除法运算结束后,商和余数同时出现在端口;若分子或分母数值发生改变则触发一次新的运算;若在除法运算期间分子或分母发生改变,将一直等到本次运算结束再触发下一次运算;若运算期间发生多次变化,则先前的变化值均被忽略,最后一次的变化值触发下一轮运算。如果除法实际运算值相对于商的宽度发生溢出,则商和余数的结果无价值。

除法器的使用可固定为如下形式:

  定义std_vector类型的分子N、分母D、商Q和余数R

  g1: RT_Divider genericmap(NW=>N’length, DW=>D’length, QW=>Q’length)
port map(clock=>clock, numerator=>N, denominator=>D, quotient=>Q, remainder=>R);

由于RT_Divider使用了ucx_2008pkg中大部分函数(function)和过程(procedure),所以把包含将来可能会用到的函数或过程的文件ucx_2008pkg.vhd连同RT_Divider.vhd一起在附近内给出。

为了阐明RT_Divider的设计逻辑,很有必要对ucx_2008pkg的部分函数或过程做一次系统解释。

自定义包ucx_2008pkg主要是一些重载函数和过程的定义。函数(function)和过程(procedure)均类似于C语言的函数,不过是过程更像带指针或引用参数的函数。为行文方便,在不引起混淆的情况下,以后用函数统称函数或过程

001贴中,以STDZ函数为例说明本文推荐的编程风格的一个特点是把常用函数统一放在自定义包内,并拷贝到Quartus II按照目录\quartus\libraries\vhdl\ucxLib文件夹下(此文件夹不存在,需要新建)。这样,在以后的设计文件头部加上:

Library ucxLib; Use ucxLib.ucx_2008pkg.all;
即可。从而使vhd文件看起来更像C语言,以减少代码量并提高可读性。

自定义包部分说明列表:语法表述均类似于IncDec

std_vector: std_logic_vector相同,修改原因是个人觉得类型名字过长。

STDZ(b) : standardization;把boolean类型转化为std_logic类型。

IncDec(cn, inc, dec):increase decrease;当inc时(incboolean型时表示inc=true,为std_logic型时表示inc=’1’,以下关于条件的表述均类同)cn1计数,否则dec时减1计数;dec默认无效(IncDec(cn, inc)IncDec(cn, inc, false)IncDec(cn, inc, ‘0’)等效,以下关于默认无效的表述均与此类似,默认有效则反之)。

DecInc(cn, dec, inc):当dec时减1计数,否则inc时加1计数;inc默认无效。

RstIncDec(cn, rst,inc, dec)reset increase decrease;当rstcn赋全0值,否则inc+1,否则dec-1dec默认无效。

SetIncDec(cn, set,inc, dec)SetDecInc:当setcn赋全1值,其余与上述类似。

ResetSet(q, rst, set):当rstq赋全0值,否则当setq赋全1值,set默认无效。

SetReset(q, set, rst):当setq赋全1值,否则当rstq赋全0值,rst默认无效。

LoadValue(q, v, en):当enqv值。

LoadInc(cn, v, ld,inc)load increase;当ldcnv值,否则inc+1inc默认有效。

LoadDec(cn, v, ld,dec):与LoadInc类似。

LOG2(n)n为常整数,返回值为log2(n)的整数部分。此函数主要供编译器使用。

b_and(v)v类型为std_vector,返回std_logic型,是v的所有比特and后的结果;即当输入v为全1时,返回’1’,否则返回’0’

b_or(v):与b_and类似,不同的是返回v的所有比特or运算的结果。

b_xor(v), b_xnor(v):与上类同。

b_nand(v):等效notb_and(v)

b_nor(v):等效notb_or(v)

descend(v):把std_vector型的变量v以降序到0的形式返回。
例:signal v : std_vector(2 to 8);descend(v)(1)的值为v(7)

descend(v, n):若n>=v’length,则将v左侧补0n宽度,以降序形式返回;否则截取v右侧n位以降序形式返回。

ascend(v):把变量v以升序的形式返回。

ascend(v, n):若n>=v’length,则将v右侧补0n宽度,以升序形式返回;否则截取v左侧n位以升序形式返回。
例:signal v : std_vector(2 to 8);ascend (v, 10)值为v&”000”; ascend (v, 10)(5 to 9)v(7) &v(8) &”000”

OneOf(a, b, sa):二选一逻辑,当sa时返回a,否则返回b

OneOf(a, b, c, sa, sb):三选一,当sa时返回a,否则sb时返回b,否则返回c

OneOf(v0,v1,v2,v3,sv):四选一,当sv=0时返回v0sv=1时返回v1sv=2时返回v2,其他返回v3

STDV_INT(v):把std_vector型的v转换为整数返回,常用于索引。

INT_STDV(n, w):把整数n转化为比特宽wstd_vector型返回。
以上函数中,部分完成的功能在包std_logic_unsignedstd_logic_arith中已有定义。在此重新定义是为了简化或清晰。
除法器.rar (4.42 KB)

相关帖子

ucx|  楼主 | 2017-9-19 16:53 | 显示全部楼层

下面正式进入RT_Divider的讲解。实现除法的原理类似于手除法:①根据商的位数QW和分母DW的位数在分子前补适当个数0成为DW+QW位;②从分子头部开始选取NW+1位得dividend;③比较dividend与分母den的大小关系,如果dividend>=den则商1并从dividend中减去den,否则商0;④减后的差左移一位,取分子中下一位补到差的最低位,得新的dividend值;⑤如果计算结束,则装在商的值并从dividend取出余数输出,否则转到步骤③。从RT_Divider的原理可以看出共消耗QW个节拍,再加上延时处理,实际共消耗QW+2个节拍。

其实体头部如下:

Entity RT_Divider is generic(

         NW : integer := 16; DW: integer := 8; QW : integer := 0);

port(

         clock                                     : instd_logic;

         numerator                          : in std_vector(NW-1downto 0);

         denominator                      : in std_vector(DW-1downto 0);

         quotient                             :out std_vector(OneOf(QW-1, NW-DW-1, QW>0) downto 0);

         remainder                           : out std_vector(DW-1downto 0)

         );

End RT_Divider;

其中NW表示分子numerator的比特宽度,DW表示分母denominator宽度,QW表示表示商quotient的宽度。端口定义如同端口名。

实体结构如下:

constant m                                   :integer := quotient'length+denominator'length;

constant n                                    :integer := quotient'length;

signal den                                                                :std_vector(0 to DW-1);

signal num                                                                :std_vector(0 to NW-1);

signal div                                                                 :std_vector(0 to m-1);

signal dividend                                                        :std_vector(0 to DW);

signal cnt                                                         :std_vector(0 to LOG2(n));

signal quot                                                               :std_vector(quotient'range);

signal minus_en,calculated                        :std_logic;

signal bgn,calcing,start,ld_result   :std_logic;

Begin

dividend <= div(0 to DW);

minus_en <= calcing and dividend >= den;

Process(clock) begin

         if rising_edge(clock)then

31.             bgn <=b_nor(cnt) and not bgn and (denominator/=den or numerator/=num);

                   LoadValue(den,denominator, bgn);

                   LoadValue(num,numerator, bgn);

                   LoadDec(cnt,INT_STDV(n, cnt'length), bgn, b_or(cnt));

35.             start <=bgn;

                   calcing<= b_or(cnt);

                   ShiftLeft(div,'0');

                   LoadValue(div(0to DW-1), descend(dividend-den, DW), minus_en);

                   LoadValue(div,descend(num, m), start);

40.             ShiftLeft(quot,minus_en);

                   ResetSet(quot,start);

                   calculated <=STDZ(cnt = 1);

                   ld_result<= calculated;

                   LoadValue(quotient,quot, ld_result);

45.             LoadValue(remainder,div(0 to DW-1), ld_result);

         end if;

End process;

End myFavor;

打开Divider.vhd可见dividend<= div(0 to DW);在第27行,对bgn信号赋值行为第31行。信号bgnbegin的缩写,表示开始计算。

31b_nor(cnt)and not bgn表示计算结束denominator/=den or numerator/=num表示输入分子或分母较上一轮运算发生改变。

3233表示把分母和分子分别装载到dennum

27实现截取NW+1位功能,相当于步骤②。

373839实现步骤③和④功能。

4445完成步骤⑤。

RT_Divider功能模块的正确性可以通过仿真软件自行验证。在此不画蛇添足。

RT_Divider除法器是串行除法器,其优点是消耗的资源较少,且资源增长随位数线性增长。缺点是需要消耗更多时间,前面已经提到需要QW个时钟节拍。算法原理中②③④步骤表明每次试商1个比特,如果对逻辑作适当修改,每次试商2个比特即有0,1,2,3四种试商值,那么一次计算只需要QW/2个时钟节拍,命名此除法器为RT2_Divider。类似地可以一次试商4个比特得RT4_Divider,一次计算又缩小一半,为QW/4个时钟节拍。RT4_Divider相对于RT_Divider资源消耗略有增大,clock最高时钟略有降低。
如果想获得每个时钟进行一次除法运算,有两种方法。方法一是我们常说的查表法,思路是通过分母查表获得倒数然后再和分子相乘。查表法的优点是可以获得每个时钟一次运算的吞吐量,缺点是消耗过多的资源,且资源随位数呈指数增长,很难超过16位。那么方法二是并行使用多个RT4_Divider,对于32位除法,只需使用8RT4_Divider,其资源消耗略高于RT_Divider8倍。

使用特权

评论回复
dybestboy| | 2019-1-14 21:14 | 显示全部楼层
你好~你用的仿真软件是什么?

使用特权

评论回复
ucx|  楼主 | 2019-1-31 22:52 | 显示全部楼层
dybestboy 发表于 2019-1-14 21:14
你好~你用的仿真软件是什么?

QUARTUS软件中集成的free版modelsim

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

ucx

28

主题

85

帖子

5

粉丝