基于FPGA的CORDIC旋转模式实现sin和cos运算_采用EGo1板卡上板验证【FPGA数字信号处理第5讲】

一、概述

本文在前文《CORDIC旋转模式实现sin和cos运算》的基础上,将CORDIC旋转模式算法部署到EGo1 FPGA开发板上,实现sin和cos运算,并实现数码管显示运算结果,显示结果保留小数点后三位,并且经过四舍五入运算。

由于算法的理论介绍、仿真验证在前文以及详细介绍过,这里不再赘述,而是针对在EGo1开发板上进行部署的方案进行详解。

二、系统搭建

软件开发环境为Vivado 2018.3,使用Verilog HDL进行描述,最终在EGo1开发板上实现并验证。

硬件只需要用到EGo1开发板,如图1所示,板载的FPGA芯片型号为Xilinx Artix-7系列XC7A35T-1CSG324,具有33280个逻辑单元、41600个寄存器、1800kb的BRAM和90个DSP等逻辑资源。该开发板外设丰富,可以实现较复杂的数字逻辑设计。

图1 EGo1开发板实物图

本设计使用8个拨码开关作为角度输入信号,并且以8421BCD码的格式进行输入。即SW7~SW4代表角度的高位,SW3~SW0代表角度的低位,输入角度最大值为99度。使用拨码开关的最低位控制sin运算和cos运算,拨码开关为0时,进行sin运算;拨码开关为1时,进行cos运算。8个数码管显示的内容包括:指示sin或者cos运算,显示输入角度,显示sin、cos的计算结果,保留小数点后三位。

三、模块设计

(1)CORDIC旋转模式计算模块(cordic_rotate.v):使用Verilog HDL实现了CORDIC旋转模式算法,输入角度范围为0~360°,并乘以2^16进行量化,输出sin和cos运算结果,结果乘以了2^24进行量化。该算法的理论、Verilog设计和仿真可以参考前文《CORDIC旋转模式实现sin和cos运算》。

(2)二进制转8421BCD码模块(bin_to_8421bcd.v):实现了将二进制数转8421BCD码,输入为二进制数,输出为8421BCD码格式的数据,方便后续的数码管显示。

(3)数码管显示模块(nixie_disp.v):实现了数码管的动态显示,将输入角度和计算的sin、cos结果显示到数码管上。

(4)顶层模块(sin_cos_EGo1.v):本模块实例化了上述子模块,并生成各个子模块所需要的输入信号,实现在EGo1板卡上部署CORDIC旋转模式算法,实现sin和cos运算。

顶层模块代码如下:

复制代码
//Description:使用CORDIC旋转模式 ,在EGo1上实现sin和cos运算, 并使用数码管指示运算结果



module sin_cos_EGo1

(

      input      wire                     clk_100m                   ,//100MHz clock

      input      wire                     rst_n                    ,//reset, active low

      input      wire       [7:0]      alpha_bcd           ,//输入角度, 用拨动开关表示输入角度, 8421BCD码格式,输入角度范围为0~99度

      input      wire                     sin_cos_choose   ,//选择sin还是cos运算, 用拨码开关选择

      output    wire       [3:0]      cs1                             ,//数码管1片选信号

      output    wire       [7:0]      seg1                            ,//数码管1段选信号

      output    wire       [3:0]      cs2                             ,//数码管2片选信号

      output    wire       [7:0]      seg2                            //数码管2段选信号

);   



//--------------------------------------------\

//-----------------main code-----------------

//--------------------------------------------/

//复位后延时1ms

reg        [16:0]           cnt_1ms              ;

wire                           cnt_1ms_flag             ;

assign cnt_1ms_flag = (cnt_1ms == 17'd100_000) ? 1'b1 : 1'b0 ;

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n)

             cnt_1ms <= 17'd0 ;

      else if(cnt_1ms_flag)

             cnt_1ms <= 17'd100_000 ;

      else

             cnt_1ms <= cnt_1ms + 1'b1 ;

end



//对角度信号打一拍

reg        [7:0]      alpha_bcd_r        ;

wire                    alpha_bcd_change     ;//角度改变标志信号

assign   alpha_bcd_change = (alpha_bcd_r != alpha_bcd ) ? 1'b1 : 1'b0 ;

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n)

             alpha_bcd_r <= 8'd0 ;

      else

             alpha_bcd_r <= alpha_bcd ;

end



//得到输入角度有效信号标志 和输入角度

reg                      in_flag_temp             ;

reg        [7:0]      in_alpha_temp           ;

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n) begin

             in_flag_temp <= 1'b0 ;

             in_alpha_temp <= 8'd0 ;

      end

      else if(cnt_1ms == 17'd100_000 - 1'b1) begin //复位后1ms

             in_flag_temp <= 1'b1 ;

             in_alpha_temp <= (alpha_bcd[7:4]<<3) + (alpha_bcd[7:4]<<1) + alpha_bcd[3:0] ;//alpha_bcd[7:4]*10 + alpha_bcd[3:0] ;//将BCD码转换为二进制码

      end

      else if(alpha_bcd_change)begin //角度改变

             in_flag_temp <= 1'b1 ;

             in_alpha_temp <= (alpha_bcd[7:4]<<3) + (alpha_bcd[7:4]<<1) + alpha_bcd[3:0] ;//alpha_bcd[7:4]*10 + alpha_bcd[3:0] ;//将BCD码转换为二进制码

      end

      else begin

             in_flag_temp <= 1'b1 ;

             in_alpha_temp <= in_alpha_temp ;

      end

end



//对于输入角度再乘以2^16

reg                      in_flag          ;

reg        [31:0]    in_alpha       ;

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n) begin

             in_flag <= 1'b0 ;

             in_alpha <= 8'd0 ;

      end

      else if(in_flag_temp) begin

             in_flag <= 1'b1 ;

             in_alpha <= {8'h00,in_alpha_temp,16'h0000};//输入角度均为正数

      end

      else begin

             in_flag <= 1'b0 ;

             in_alpha <= in_alpha ;

      end

end



//Description:旋转模式 Cordic算法实现sin、cos运算

wire                                  out_flag        ;

wire      signed    [31:0]    out_cos         ;

wire      signed    [31:0]    out_sin         ;

cordic_rotate cordic_rotate_inst

(

      .clk_in          (clk_100m    )      ,//clock

      .rst_n            (rst_n            )      ,//reset

      .in_flag         (in_flag        )      ,//输入角度有效信号标志

      .in_alpha      (in_alpha      )      ,//输入角度  输入范围为0~360°      并且乘以2^16之后的结果 位宽最大位25位

      .out_flag       (out_flag      )      ,//输出信号有效标志信号

      .out_cos        (out_cos       )      ,//输出余弦值cos 乘以2^24的结果

      .out_sin        (out_sin        )      //输出正弦值sin 乘以2^24的结果

);



//计算结果保留小数点后三位,只需要将sin和cos放大1000倍即可, 不需要放大2^24倍

//首先确定sin和cos的正负

reg                      flag_r1         ;

reg                      sin_sign        ;

reg        [24:0]    sin_data        ;//最大值为1*2^24

reg                      cos_sign              ;

reg        [24:0]    cos_data              ;//最大值为1*2^24

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n)

             flag_r1 <= 1'b0 ;

      else if(out_flag)

             flag_r1 <= 1'b1 ;

      else

             flag_r1 <= 1'b0 ;

end

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n) begin

             sin_sign <= 1'b0  ;

             sin_data <= 25'd0 ;

      end

      else if(out_flag) begin

             if(out_sin[31])begin //负数

                    sin_sign <= 1'b1 ;

                    sin_data <= ~out_sin[24:0] + 1'b1 ;//保留符号位, 数值部分取反+1

             end

             else begin //正数

                    sin_sign <= 1'b0 ;

                    sin_data <= out_sin[24:0] ;

             end

      end

      else begin

             sin_sign <= sin_sign ;

             sin_data <= sin_data ;

      end

end

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n) begin

             cos_sign <= 1'b0  ;

             cos_data <= 25'd0 ;

      end

      else if(out_flag) begin

             if(out_cos[31])begin //负数

                    cos_sign <= 1'b1 ;

                    cos_data <= ~out_cos[24:0] + 1'b1 ;//保留符号位, 数值部分取反+1

             end

             else begin //正数

                    cos_sign <= 1'b0 ;

                    cos_data <= out_cos[24:0] ;

             end

      end

      else begin

             cos_sign <= cos_sign ;

             cos_data <= cos_data ;

      end

end

//计算sin和cos值乘以1000

reg                      flag_r2                ;

reg                      sin_sign_mult            ;

reg        [39:0]    sin_data_mult            ;//最大值为1000*2^24, 为了防止溢出, 位宽取40

reg                      cos_sign_mult            ;

reg        [39:0]    cos_data_mult           ;//最大值为1000*2^24, 为了防止溢出, 位宽取40

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n)

             flag_r2 <= 1'b0 ;

      else if(flag_r1)

             flag_r2 <= 1'b1 ;

      else

             flag_r2 <= 1'b0 ;

end

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n) begin

             sin_sign_mult <= 1'b0  ;

             sin_data_mult <= 40'd0 ;

      end

      else if(flag_r1)begin

             sin_sign_mult <= sin_sign ;

             sin_data_mult <= (sin_data << 10) - (sin_data << 4) - (sin_data << 3) ;//sin_data*1000 = sin_data*1024 - sin_data*24 = sin_data << 10 - sin_data << 4 - sin_data << 3 ;

      end

      else begin

             sin_sign_mult <= sin_sign_mult ;

             sin_data_mult <= sin_data_mult ;

      end

end

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n) begin

             cos_sign_mult <= 1'b0  ;

             cos_data_mult <= 40'd0 ;

      end

      else if(flag_r1) begin

             cos_sign_mult <= cos_sign ;

             cos_data_mult <= (cos_data << 10) - (cos_data << 4) - (cos_data << 3) ;//cos_data*1000 = cos_data*1024 - cos_data*24 = cos_data << 10 - cos_data << 4 - cos_data << 3 ;

      end

      else begin

             cos_sign_mult <= cos_sign_mult ;

             cos_data_mult <= cos_data_mult ;

      end

end

//sin和cos值乘以1000后再除以2^24, 右移24,这里截断低24位 最大值位1000 只需要保留10bit即可

reg                      flag_r3                ;

reg                      sin_sign_div        ;

reg        [9:0]      sin_data_div       ;//最大值为1000 位宽取10

reg                      cos_sign_div              ;

reg        [9:0]      cos_data_div              ;//最大值为1000  位宽取10

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n)

             flag_r3 <= 1'b0 ;

      else if(flag_r2)

             flag_r3 <= 1'b1 ;

      else

             flag_r3 <= 1'b0 ;

end

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n) begin

             sin_sign_div <= 1'b0  ;

             sin_data_div <= 10'd0 ;

      end

      else if(flag_r2) begin

             sin_sign_div <= sin_sign_mult ;

             sin_data_div <= sin_data_mult[23] ? (sin_data_mult[33:24]+1'b1) : sin_data_mult[33:24] ;//截断低24位, 等效于右移24位, 即除以2^24 考虑四舍五入

      end

      else begin

             sin_sign_div <= sin_sign_div ;

             sin_data_div <= sin_data_div ;

      end

end

always @(posedge clk_100m or negedge rst_n) begin

      if(!rst_n) begin

             cos_sign_div <= 1'b0  ;

             cos_data_div <= 40'd0 ;

      end

      else if(flag_r2)begin

             cos_sign_div <= cos_sign_mult ;

             cos_data_div <= cos_data_mult[23] ? (cos_data_mult[33:24]+1'b1) : cos_data_mult[33:24] ;//截断低24位, 等效于右移24位, 即除以2^24 考虑四舍五入

      end

      else begin

             cos_sign_div <= cos_sign_div ;

             cos_data_div <= cos_data_div ;

      end

end



//二进制转8421BCD码模块

//理论上sin和cos的符号标志信号也要延时bin转bcd码所需的时钟周期数

//因为数码管动态扫描的时钟周期为1ms, 基本不影响视觉效果,这里为了简便就不做延时了

wire      [15:0]    sin_data_bcd              ;

wire      [31:0]    bcd_temp_sin            ;

assign sin_data_bcd = bcd_temp_sin[15:0] ;

bin_to_8421bcd bin_to_8421bcd_sin

(

      .clk_100m    (clk_100m           )      ,//100MHz

      .rst_n            (rst_n                   )      ,//reset

      .in_flag         (flag_r3               )      ,//二进制输入数据有效

      .bin_in          (sin_data_div             )      ,//二进制数输入

      .out_flag       (                           )      ,//输出标志信号

      .data_bcd     (bcd_temp_sin           )      //输出32位的BCD码

);

wire      [15:0]    cos_data_bcd             ;

wire      [31:0]    bcd_temp_cos            ;

assign cos_data_bcd = bcd_temp_cos[15:0] ;

bin_to_8421bcd bin_to_8421bcd_cos

(

      .clk_100m    (clk_100m           )      ,//100MHz

      .rst_n            (rst_n                   )      ,//reset

      .in_flag         (flag_r3               )      ,//二进制输入数据有效

      .bin_in          (cos_data_div            )      ,//二进制数输入

      .out_flag       (                           )      ,//输出标志信号

      .data_bcd     (bcd_temp_cos          )      //输出32位的BCD码

);



//数码管显示模块

wire                    data_sign      ;//待显示计算结果的符号

wire      [15:0]    data_bcd             ;//待显示计算结果的数值

assign data_sign = sin_cos_choose ? cos_sign_div : sin_sign_div ;

assign data_bcd  = sin_cos_choose ? cos_data_bcd : sin_data_bcd ;

nixie_disp   nixie_disp_inst

(

      .clk_100m       (clk_100m                         )      ,//100MHz

      .rst_n            (rst_n                                 )      ,

      .data_disp          ({{sin_cos_choose+4'hc},alpha_bcd,{data_sign+4'ha},data_bcd} )       ,//待显示的数据

      .point_disp   (8'b0000_1000                  )      ,//小数点显示

      .cs1             (cs1                                      )      ,//数码管1片选信号

      .seg1            (seg1                                     )      ,//数码管1段选信号

      .cs2             (cs2                                      )      ,//数码管2片选信号

      .seg2            (seg2                                     )      //数码管2段选信号

);



endmodule

四、上板验证

对工程进行全编译,工程总结界面如图2所示。

图2 工程总结界面

具体的逻辑资源消耗如图3所示,查找表LUT的使用数量为1260个,占总数的6.06%;寄存器FF的使用数量为718个,占总数的1.73%,整体逻辑资源使用较少。

图3 逻辑资源消耗

将bit流文件下载到EGo1开发板中,板卡的具体操作如图4所示

图4 具体操作示意图

首先验证sin运算,输入角度分别为0°、30°、60°、90°、99°时的现象分别如图5所示,计算结果分别为-0.000、0.500、0.866、1.000、0.988,符合预期。

图5 sin运算验证结果

对于cos运算,输入角度分别为0°、30°、60°、90°、99°时的现象分别如图6所示,计算结果分别为1.000、0.866、0.500、0.000、0.156,符合预期。

图6 cos运算验证结果

五、获取方法

加入知识星球【FPGA设计入门与提高】: https://t.zsxq.com/ytILk

目前(2026-09-27)知识星球包括的内容如下:

相关推荐
博览鸿蒙6 天前
FPGA开发使用的语言
fpga开发·fpga
明德扬6 天前
机器人多摄像头如何做FPGA多路视频聚合?
学习·fpga开发·fpga
JoYER_cc7 天前
FPGA 调试实录:LED 闪烁程序从踩坑到仿真验证的完整经验总结
fpga开发
啄缘之间7 天前
1.【SVA】什么是SVA?
经验分享·学习·测试用例·verilog·sva
ktd0077 天前
复旦微开发环境搭建流程备忘录
fpga开发
ERROR:997 天前
陈工,试试Agent开发FPGA
fpga开发
威嵌神州7 天前
复旦微 FMQL100TAI 开发 8 问:仿真器、系统、接口高频问题解答
人工智能·嵌入式硬件·fpga开发
北京青翼科技7 天前
青翼基于PCIe的VU13P FPGA信号处理板
fpga开发·fpga开发板·图像处理卡·pcie板卡
szxinmai主板定制专家7 天前
NXP LS1046A 硬件方案: LS1046A+FPGA 异构加速架构实践
大数据·图像处理·人工智能·嵌入式硬件·fpga开发