打印
[VHDL]

玩转VHDL-005 Sin函数发生器

[复制链接]
1642|3
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
ucx|  楼主 | 2017-9-21 16:23 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 ucx 于 2017-9-21 16:33 编辑

002004中依次介绍了计数器、分频器和除法器,本帖讲述Sin函数发生器。先介绍这些功能是考虑到这些模块不需要专业背景知识,每一位帖友均能准确把握其功能。

提到Sin函数的实现,首先想到的是查表法。这种方法快速直观,但是若要求RAM资源消耗不超过FPGA内一块RAM的容量(Cyclone IIIIV系列,一块RAM的容量是9216比特),角度分辨率<360°/65536,函数值误差不超过±1/32768,那么如何实现呢?显然,纯粹的查表法已无能为力。CPU实现Sin函数是通过泰勒级数展开,这在FPGA内并不适用。

那么,是否可以把查表法与级数展开法结合起来使用呢?答案是肯定的,本帖提供的模块SinQ18b正是基于这一方法实现的。在讲述SinQ18b之前,先介绍下此模块的功能和使用方法。此功能模块,可不经任何修改,直接使用于任何角度分辨率不大于360°/2^18且函数值误差精度不高于±1/32768情况。

模块实体头部:

Entity SinQ18b is Port(
         clock                            : in std_logic;
         x0in                             : in std_vector(0 to 17);
         q4Sin                           : out std_vector(0 to17)
         );
End SinQ18b;

其中clock为模块时钟,对通常的FPGA保证可工作在150M以上,用EP4CE6E22I7综合后频率上限为181.95Mx0in18位角度输入,实际角度值=360°÷2^18 × x0in。如果实际运用不需要这么高的角度分辨率时,可以将低位补全0,或者补中值(类似于”100..0”,补足的数中除最高位为1其余为0)。输出q4Sinsin值输出,内部用流水线设计,相对于输入迟滞4个时钟周期。流水线设计保证了clock速率,同时实现每个时钟节拍输出一次sin值功能。输出用18位补码表示,函数功能可描述为q4Sin=(2^17-1)×sin(360°÷2^18 × x0in)。式中的放大因子为2^17-1而不采用2^17是基于适合DA输出之考虑。当实际使用sin值不足18位时,可截取q4Sin使用,或者四舍五入法截取。

SinQ18b原理
设角度值为x >=0y=sin(x)。令x=xn + δx,把y一阶泰勒展开为ysin(xn) +sin`(xn)×δx。若x16位无符号数x[15..0]表示,则x的高8x[15..8]可作为xn使用,低8x[7..0]可作为δx带入。那么xn共有256个值,可用256个地址的ROM来存储sin(xn)sin`(xn)值。表格存储x值在[090°)范围,再通过x在四个象限的对称性通过折转可扩展到[0360°)范围。SinQ18b18位角度输入正是基于此考虑,x0in的前两位(或者说高两位)表示象限位置。

SinQ18b的存储表每个单元使用31个比特,前19个比特存储sin(xn)12个比特存储sin`(xn)值。总共资源消耗:1RAM+64LE+2个嵌入乘法器。
SinQ18b的输入和输出用18位升序形式表示(基于个人偏好),如果改成降序
         x0in                             :in std_vector(17 downto 0);
        q4Sin                           :out std_vector(17 downto 0)
程序不需其他任何改动而功能不变。
更简单地讲,SinQ18b的原理是查表与线性插值相结合。查表得到基值sin(xn)和线性插值斜率sin`(xn),计算sin`(xn)×δx为补偿值。补偿值的计算消耗29位乘法器。

SinQ18b代码
全部代码共三个文件:SinQ18b.vhdsin18b.mifRamRom.vhd,见附件sin.rar。其中RamRom.vhd是在Quartus IIMegaWizardPlug-In Manager向导生成的IP核基础上做通用封装而成。如果有同仁需要了解,我可以在回帖中再作介绍,在此不作说明。
另外,还需要一个自定义库文件ucx_2008pkg.vhd,已在004帖的附件中给出。

功能仿真
testBench文件top_IO.vht全部代码如下:

Library ieee;  Useieee.std_logic_1164.all, ieee.std_logic_unsigned.all;
Library ucxLib; Use ucxLib.ucx_2008pkg.all;
entity top is
end top;

architecture pure_sim of top is
         signal clk125M                   : std_logic := '0';
         signal angle                        : std_vector(0 to 17) :=(others=>'0');
         signal qSin                           : std_vector(0 to17);

begin

i1: entity work.SinQ18b Port Map(clock=>clk125M, x0in=>angle,q4Sin=>qSin);

Process(clk125M)   begin
         ifrising_edge(clk125M) then
                   angle <=angle + 1;
         end if;
End process;


Process begin
       wait for 4 ns;
       clk125M <= not clk125M;
End process;

End pure_sim;

仿真时钟选取为125M5ms时间,结果如下图:

                               


sin.rar

4.05 KB

sin

相关帖子

沙发
ucx|  楼主 | 2017-9-21 16:26 | 只看该作者

代码简析
sin18b.mif文件用C语言生成,把0度到90度分成256等份,基值部分是取sin值乘219 - 1 = 524287再四舍五入后的值,斜率部分是下一点与当前点的差值。用45°值举例说明。对于45°角,对应xn地址是0x80,用文本查看器打开sin18b.mif文件看到0x80地址存放数值为0x5A8278DCsin(45°)×524287四舍五入取整得0x5A827。同理(45+1/256)°对应的值是0x5B103。而0x5B103 - 0x5A827 = 0x8DC,正好是0x5A8278DC的低12位数值。

SinQ18b.vhd结构代码如下:

Architecture EP4C of SinQ18b is
12.   alias x                                            :std_vector(0 to 17) is x0in;
         Signal dlyTy                                   :std_vector(0 to 2);
         Alias ty                                          :std_logic is dlyTy(0);
15.   Signal y                                          :std_vector(0 to 15);
         Signal dlyYL,cof                           : std_vector(0 to 7);
         Signal sinQ                                    :std_vector(1 to 31);
         Signal compens                           : std_vector(0 to12);
         Signal PIP1V,vBase                     : std_vector(0 to 18);

Begin

21. gTable : entity work.RamRom Generic Map(AW=>8, DW=>31,mif=>"sin18b.mif", rom_m=>true)
22.             PortMap(clock=>clock, rdA=>y(0 to 7), q=>sinQ);
23. y <= bv_xor(x(1), x(2 to 17)) + (b_or(x(2 to 17)) and x(1));
24. PIP1V <= vBase + compens(0 to 11) + compens(12);

Process(clock) begin
         if rising_edge(clock)then
27.             dlyYL <= y(8to 15);
                   cof <=dlyYL;
                   vBase <=ascend(sinQ, 19);
30.             compens <=ascend(descend(sinQ, 12) * cof, 13);
                  ShiftLeft(dlyTy, x(0));
                   q4Sin <=bv_xor(ty, '0' & PIP1V(0 to 16)) + ty;
         end if;
End process;

End EP4C;

12x0in重命名,目的当把端口改为x0in: in std_vector(17 downto 0);的降序形式时不影响后面逻辑。
15定义的yx在第一象限对应的位置。

13定义的dlyTy,用于对x(0)的流水线延时,见行31x(0)=0表示在一二象限,sin>=0x(0)=1表示三四象限,sin值按照一二象限规则计算后取负即得。

2122中调用RamRomrdA为输入地址,q为查表输出,输出相对于地址输入延时两个时钟周期。AW=>8指明地址宽度8位,即256个地址。DW=>31指明数据宽度为31位。rom_m=>true指明使用ROM模式,即不需要写地址和写入数据。

23完成x处于第一象限时保持不变,处于第二象限时对称折反即取补运算。原理上可简化为y <= bv_xor(x(1), x(2 to 17)) + x(1);其中函数bv_xor(b,v)返回比特b与向量v作异或操作的结果,当b=0时返回vb=1时返回v的逐比特取反值。并考虑x(1 to 17)=0x10000时补运算溢出情况,所以用b_or(x(2 to 17)) and x(1)代替x(1)

24完成基值与补偿值相加,+compens(12)是四舍五入算法。

27,28,29,31均是流水线延时,完成时序对齐。31行的过程ShiftLeft(v, b)是对v的左移操作,低位移入b

32是取补输出。

小结
SinQ18b是高精度快速sin函数生成器。

使用特权

评论回复
板凳
山东电子小菜鸟| | 2017-9-26 18:21 | 只看该作者
学习了 谢谢分享

使用特权

评论回复
地板
ucx|  楼主 | 2017-9-27 09:55 | 只看该作者

谢版主夸奖,吾将努力不懈。正在酝酿下一帖:实时FFT,然后再介绍点小的功能函数,比如格雷计数,并行伪随机序列和CRC等

使用特权

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

本版积分规则

ucx

29

主题

89

帖子

5

粉丝