一、概述
本文在前文《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)知识星球包括的内容如下:
