PGA数字信号处理之CORDIC实现NCO
0 2023-03-22

,本文就讲一下用verilog实现cordic算法的NCO代码。

上文讲到cordic的计算公式为:

X1=cos(θ)(X0-Y0*tan(θ))Y1=cos(θ)(Y0+X0*tan(θ))其中cos(θ)可以根据最终的迭代次数直接得到,但是上式中还有一个tan(θ)的乘法操作,FPGA在实现cordic算法时会对其进行进一步优化,将tan(θ)的取值设置为1,0.5,0.25.... (这些值在verilog数字硬件中就对应为1/2^0,1/2^1,1/2^2 ...)cordic的旋转角度为对应的角度值,这样在verilog中tan(θ)的乘法操作可以使用移位直接实现,cordic中就没有乘法操作了。最终可以得到下表:

序号

正切值

弧度值

角度值

正弦值

gain

0

1

0.785398

45

0.707107

0.707107

1

0.5

0.463648

26.56505

0.894427

0.632456

2

0.25

0.244979

14.03624

0.970143

0.613572

3

0.125

0.124355

7.125016

0.992278

0.608834

4

0.0625

0.062419

3.576334

0.998053

0.607648

5

0.03125

0.03124

1.789911

0.999512

0.607352

6

0.015625

0.015624

0.895174

0.999878

0.607278

7

0.007813

0.007812

0.447614

0.999969

0.607259

8

0.003906

0.003906

0.223811

0.999992

0.607254


表中第一列为序号,表征cordic的迭代次数,第二类为tan(θ)的值,第三列第四列是正切值求得的弧度值和角度值,第五列是角度对应的正弦值,最后一列是增益,就是cos(θ)的乘积。

Verilog代码实现NCO的方法:1、对角度值选取合适的量化位宽;2、输出量化位宽选择;3、根据精度选取迭代次数;4、确定增益及增益量化位宽;*最大误差就是最终的正切角度。*注意角度溢出的处理。代码如下:
端口:





















// ============================================================// File Name: cm_nco_cordic// VERSION  : V1.0// DATA     : 2022/11/20// Author   : FPGA干货分享// ============================================================// 功能:NCO生成代码 cordic// delay : // ============================================================  `timescale 1ns/100psmodule cm_nco_cordic #( parameter             C_DATA_WITH     = 11 )( input  wire                     I_sys_clk   , /// 工作时钟 100M input  wire [14:0]              I_angle     , /// 角度值,2*13 = π/4 output reg  [C_DATA_WITH-1:0]   O_sin_out   , /// 输出正弦值 output reg  [C_DATA_WITH-1:0]   O_cos_out /// 输出余弦值); 

参数化增益,这里使用4096对应45度:














// ============================================================// parameter// ============================================================localparam  C_ATAN0 = 13'd4096 ; ///弧度量化 localparam  C_ATAN1 = 13'd2418 ; ///弧度量化 localparam  C_ATAN2 = 13'd1278 ; ///弧度量化 localparam  C_ATAN3 = 13'd649  ; ///弧度量化 localparam  C_ATAN4 = 13'd326  ; ///弧度量化 localparam  C_ATAN5 = 13'd163  ; ///弧度量化 localparam  C_ATAN6 = 13'd81   ; ///弧度量化 localparam  C_ATAN7 = 13'd41   ; ///弧度量化 localparam  C_ATAN8 = 13'd20   ; ///弧度量化 localparam  C_GAIN  = 2**(C_DATA_WITH-1) -1;

代码首先对象限信息进行打拍,方便cos,sin值得输出:















always @(posedge I_sys_clk) begin S_ang_d1 <= I_angle[14:13]; S_ang_d2 <= S_ang_d1; S_ang_d3 <= S_ang_d2; S_ang_d4 <= S_ang_d3; S_ang_d5 <= S_ang_d4; S_ang_d6 <= S_ang_d5; S_ang_d7 <= S_ang_d6; S_ang_d8 <= S_ang_d7; S_ang_d9 <= S_ang_d8; S_ang_d10 <= S_ang_d9; end 

cordic迭代:






















































































































 /// 第一次循环 x0=C_GAIN  y0=0/// x_n = x - y/(2**n)///   y_n = y + x/(2**n)///   ang = ang - angele[i]always @(posedge I_sys_clk) begin S_x_1 <= C_GAIN ; S_y_1 <= C_GAIN ; S_a_1 <= {1'b0,I_angle[12:0]}-C_ATAN0; end /// 第二次循环///    if(S_a_1<0):/// x_n = x + y/(2**1)///        y_n = y - x/(2**1)///        ang = ang + angele[i]/// else:///        x_n = x - y/(2**1)///        y_n = y + x/(2**1)///        ang = ang - angele[i] always @(posedge I_sys_clk) if(S_a_1[13]) begin S_x_2 <= S_x_1 + {S_y_1[C_DATA_WITH],S_y_1[C_DATA_WITH:1]} ; S_y_2 <= S_y_1 - {S_x_1[C_DATA_WITH],S_x_1[C_DATA_WITH:1]} ; S_a_2 <= S_a_1 + C_ATAN1 ; end else begin S_x_2 <= S_x_1 - {S_y_1[C_DATA_WITH],S_y_1[C_DATA_WITH:1]} ; S_y_2 <= S_y_1 + {S_x_1[C_DATA_WITH],S_x_1[C_DATA_WITH:1]} ; S_a_2 <= S_a_1 - C_ATAN1 ; end /// 第三次循环///    if(S_a_2<0):/// x_n = x + y/(2**2)///        y_n = y - x/(2**2)///        ang = ang + angele[i]/// else:///        x_n = x - y/(2**2)///        y_n = y + x/(2**2)///        ang = ang - angele[i] always @(posedge I_sys_clk) if(S_a_2[13]) begin S_x_3 <= S_x_2 + {{2{S_y_2[C_DATA_WITH]}},S_y_2[C_DATA_WITH:2]} ; S_y_3 <= S_y_2 - {{2{S_x_2[C_DATA_WITH]}},S_x_2[C_DATA_WITH:2]} ; S_a_3 <= S_a_2 + C_ATAN2 ; end else begin S_x_3 <= S_x_2 - {{2{S_y_2[C_DATA_WITH]}},S_y_2[C_DATA_WITH:2]} ; S_y_3 <= S_y_2 + {{2{S_x_2[C_DATA_WITH]}},S_x_2[C_DATA_WITH:2]} ; S_a_3 <= S_a_2 - C_ATAN2 ; end /// 第四次循环///    if(S_a_3<0):/// x_n = x + y/(2**3)///        y_n = y - x/(2**3)///        ang = ang + angele[i]/// else:///        x_n = x - y/(2**3)///        y_n = y + x/(2**3)///        ang = ang - angele[i] always @(posedge I_sys_clk) if(S_a_3[13]) begin S_x_4 <= S_x_3 + {{3{S_y_3[C_DATA_WITH]}},S_y_3[C_DATA_WITH:3]} ; S_y_4 <= S_y_3 - {{3{S_x_3[C_DATA_WITH]}},S_x_3[C_DATA_WITH:3]} ; S_a_4 <= S_a_3 + C_ATAN3 ; end else begin S_x_4 <= S_x_3 - {{3{S_y_3[C_DATA_WITH]}},S_y_3[C_DATA_WITH:3]} ; S_y_4 <= S_y_3 + {{3{S_x_3[C_DATA_WITH]}},S_x_3[C_DATA_WITH:3]} ; S_a_4 <= S_a_3 - C_ATAN3 ; end  always @(posedge I_sys_clk) if(S_a_4[13]) begin S_x_5 <= S_x_4 + {{4{S_y_4[C_DATA_WITH]}},S_y_4[C_DATA_WITH:4]} ; S_y_5 <= S_y_4 - {{4{S_x_4[C_DATA_WITH]}},S_x_4[C_DATA_WITH:4]} ; S_a_5 <= S_a_4 + C_ATAN4 ; end else begin S_x_5 <= S_x_4 - {{4{S_y_4[C_DATA_WITH]}},S_y_4[C_DATA_WITH:4]} ; S_y_5 <= S_y_4 + {{4{S_x_4[C_DATA_WITH]}},S_x_4[C_DATA_WITH:4]} ; S_a_5 <= S_a_4 - C_ATAN4 ; end always @(posedge I_sys_clk) if(S_a_5[13]) begin S_x_6 <= S_x_5 + {{5{S_y_5[C_DATA_WITH]}},S_y_5[C_DATA_WITH:5]} ; S_y_6 <= S_y_5 - {{5{S_x_5[C_DATA_WITH]}},S_x_5[C_DATA_WITH:5]} ; S_a_6 <= S_a_5 + C_ATAN5 ; end else begin S_x_6 <= S_x_5 - {{5{S_y_5[C_DATA_WITH]}},S_y_5[C_DATA_WITH:5]} ; S_y_6 <= S_y_5 + {{5{S_x_5[C_DATA_WITH]}},S_x_5[C_DATA_WITH:5]} ; S_a_6 <= S_a_5 - C_ATAN5 ; end ///数据溢出特殊处理assign S_x_6_abs = S_x_6[C_DATA_WITH] ? (~S_x_6 + 1) : S_x_6 ;assign S_y_6_abs = S_y_6[C_DATA_WITH] ? (~S_y_6 + 1) : S_y_6 ; 

迭代结果要与增益相乘,x*0.6 = x*622/1024,这里我们是基于xilinx的dsp48写的乘法器,大家可以根据需要替换这个乘法器即可:













































 /// x*0.6 = x*622/1024cm_dsp48e1 #( .C_DATA_WITH_A      (C_DATA_WITH+1 ), .C_DATA_WITH_B      (12 ), .C_DATA_WITH_C      (12 ), .C_DATA_WITH_D      (25 ))U0_cm_dsp48e1( .I_CLK              (I_sys_clk      ) , // clk .I_RST              ('d0            ) , // RST .I_A                (S_x_6_abs      ) , // [29:0]  .I_B                (12'd622        ) , // [17:0]  .I_C                (12'd512        ) , // [47:0]  .I_D                (25'd0          ) , // [24:0]  .I_PCIN             (48'd0          ) , // [47:0] 只能直连PCOUT .I_ALUMODE          (4'd0           ) , // [3:0]  .I_INMODE           (5'b00101       ) , // [4:0] d+a1 .I_OPMODE           (7'b0110101     ) , // [6:0]  .O_P                (S_x_dsp        ) , // [47:0] .O_PCOUT            (               ) // [47:0] 只能直连PCIN ); cm_dsp48e1 #( .C_DATA_WITH_A      (C_DATA_WITH+1 ), .C_DATA_WITH_B      (12 ), .C_DATA_WITH_C      (12 ), .C_DATA_WITH_D      (25 ))U1_cm_dsp48e1( .I_CLK              (I_sys_clk      ) , // clk .I_RST              ('d0            ) , // RST .I_A                (S_y_6_abs      ) , // [29:0]  .I_B                (12'd622        ) , // [17:0]  .I_C                (12'd512        ) , // [47:0]  .I_D                (25'd0          ) , // [24:0]  .I_PCIN             (48'd0          ) , // [47:0] 只能直连PCOUT .I_ALUMODE          (4'd0           ) , // [3:0]  .I_INMODE           (5'b00101       ) , // [4:0] d+a1 .I_OPMODE           (7'b0110101     ) , // [6:0]  .O_P                (S_y_dsp        ) , // [47:0] .O_PCOUT            (               ) // [47:0] 只能直连PCIN ); 

最后根据输入的象限得到最终输出:
























 always @(posedge I_sys_clk) if(S_ang_d10 == 2'd0) begin O_cos_out <= S_x_dsp[C_DATA_WITH+:10]; O_sin_out <= S_y_dsp[C_DATA_WITH+:10]; end else if(S_ang_d10 == 2'd1) begin O_cos_out <= -(S_y_dsp[C_DATA_WITH+:10]); O_sin_out <= S_x_dsp[C_DATA_WITH+:10]; end else if(S_ang_d10 == 2'd2) begin O_cos_out <= -(S_x_dsp[C_DATA_WITH+:10]); O_sin_out <= -(S_y_dsp[C_DATA_WITH+:10]); end else begin O_cos_out <= (S_y_dsp[C_DATA_WITH+:10]); O_sin_out <= -(S_x_dsp[C_DATA_WITH+:10]); end 

上边的象限判断,我们在FPGA数字信号处理之verilog实现NCO(代码及仿真)里边已经详细说过,这里就放一张图如下,不理解的可以看之前的文章。

最后对其仿真:


声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 相关技术文库
  • EDA
  • 仿真
  • CAD
  • 芯片
  • 运输车辆为什么选用AMT?AMT控制解决方案

    变速器是重型运输车辆的核心总成之一,在新能源与智能化的时代背景下,运输车辆的自动变速技术需求旺盛。

    前天
  • 为什么要电池均衡?锂电池主动均衡和被动均衡理解

    通过电池均衡,电池组中的每个单元都得以被有效监控并保持健康的荷电状态(State of Charge, SoC)。

    前天
  • 国六后处理系统的结构与保养指南

    如今国六排放标准已在多地实施,国六车型也不断推出,但由于构造不同,国六车相比国四国五,也更加“娇气”,所以在保养上面车友们也要更加上心。

    前天
  • 分析电控发动机常见故障原因

    汽车给我们的出行带来了很大的便利性,但是汽车有时候也会发生一些故障,最为严重的就是汽车发动机发生故障,那么汽车发动机常见的故障有哪些呢,本文将着重介绍这些故障原因及其解决措施。

    06-06
  • 分析IC失效问题及处理方法

      当IC失效或客户认为它失效的时候,我们该怎么办?做一次IC的失效分析还是做一个彻底的测试?这些都是在浪费时间。在解决IC失效问题上,什么才是最有效的失效分析

    05-30
  • 三种发动机不能启动的情况

    发动机不能启动的三种故障。

    05-30
  • 单片机按键去抖原理你知道吗?

    [导读]由上图可以看出理想波形与实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动的现象,抖动时间的长短和按键的机械特性有关,一般为5~10ms。通常我

    05-29
  • 一文重点分析ARM64的函数调用标准

    欢迎订阅奔跑吧linux社区微信公众号 本文节选自《奔跑吧Linux内核》第二版卷1第1.6章 函数调用标准(Procedure Call Standard,PCS)用来描述父/子函数是如何编译、链接的,特别是父函数和子函数之间调用关系的约定,如栈的布局、参数的传递等。每个处理器架

    05-29
  • 介绍高功率白光LED应用及LED芯片的散热能力

      就今天而言,白光LED仍旧存在着发光均一性不佳、封闭材料的寿命不长,而无法发挥白光LED被期待的应用优点。但就需求层面来看,不仅一般的照明用途,随着手机、L

    05-29
  • 3D存储芯片各种细节解读

      现在每一个闪存厂家都在向3DNAND技术发展,我们之前也报道过Intel3DNAND的一些信息。5月14日,Intel&Richmax举办了一场技术

    05-29
  • 浅谈使用MSSP模块实现I2C从模式

      网上有许多讲解单片机实现I2C主模式,但是从模式的很少。我现在就来讲讲PIC单片机使用MSSP模块实现I2C从模式。  有关I2C协议的具体介绍可以看《PI

    05-28
  • PIC单片机之探讨低功耗如何实现

      许多人说PIC单片机一大的优势就是低功耗,那我们就来讨论,讨论低功耗的实现。  1,睡眠(sleep)  睡眠方式是我们最常用的一种方式来降低功耗,但睡眠期

    05-28
下载排行榜
更多
广告