FPGA使用查表法实现正弦和余弦函数 - 变形方法2

FPGA使用查表法实现正弦和余弦函数

FPGA使用查表法实现正弦和余弦函数 - 变形方法1

在前两篇文章中,设置的输入角度范围分别是[0,2*pi)[0,pi/2)。但根据三角函数公式,其实我们还可以将[0,2*pi)分成8段,这样就能将输入角度范围继续缩小到[0,pi/4)了。

1、修改matlab代码

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Zheng Wei, 2022/12/06
%% 
%% 输入角度范围:[0,pi/4); 输入数据量化位数:10bit;
%% 输出结果范围:[0,1];    输出数据量化位数:16bit;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

clc;        % 清理命令行
close all;  % 关闭所有图形窗口 
clear all;  % 清理所有工作区变量


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 参数定义
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
in_quantify_bit = 10;        % 输入数据量化位数(无符号数)
alpha = 0:(pi/4/2^in_quantify_bit):(pi/4-pi/4/2^in_quantify_bit);  % 弧度

out_quantify_bit = 16;       % 输出数据量化位数(有符号数)


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 获取三角函数值
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
cos_alpha = cos(alpha);  % 获得cos函数值
sin_alpha = sin(alpha);  % 获得sin函数值
                                                                               
cos_alpha_q = round(cos_alpha *(2^(out_quantify_bit-1)-1));  % 16bit量化
sin_alpha_q = round(sin_alpha *(2^(out_quantify_bit-1)-1));  % 16bit量化

% figure(1); subplot(2,1,1);plot(alpha,cos_alpha);subplot(2,1,2);plot(alpha,cos_alpha_q);
% figure(2); subplot(2,1,1);plot(alpha,sin_alpha);subplot(2,1,2);plot(alpha,sin_alpha_q);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 生成coe文件,第一行定义数据格式, 16代表ROM的数据格式为16进制。
%% 高16bit代表sin值,低16bit代表cos值,各自最高的1bit是符号位。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% 因为都是正数,所以不需要取补码
% for i = 1:1:(2^in_quantify_bit)
%     if (cos_alpha_q(i)<0)
%        cos_alpha_q(i) = cos_alpha_q(i) + 2^(out_quantify_bit); % 负数用补码表示
%     else
%        cos_alpha_q(i) = cos_alpha_q(i); % 正数的补码为原码
%     end
%                                                                                          
%     if (sin_alpha_q(i)<0)
%        sin_alpha_q(i) = sin_alpha_q(i) + 2^(out_quantify_bit); % 负数用补码表示
%     else
%        sin_alpha_q(i) = sin_alpha_q(i); % 正数的补码为原码
%     end
% end

cos_alpha_q = dec2hex(cos_alpha_q); 
sin_alpha_q = dec2hex(sin_alpha_q);

% 将cos和sin拼接为32bit(各自16bit),此数据输出到FPGA
fid=fopen('cos_sin_lut_3.coe','wt');
fprintf( fid, 'MEMORY_INITIALIZATION_RADIX = 16;\n');                     
fprintf( fid, 'MEMORY_INITIALIZATION_VECTOR =\n');

for i = 1:1:(2^in_quantify_bit)
    s=strcat(sin_alpha_q(i,1),sin_alpha_q(i,2),sin_alpha_q(i,3),sin_alpha_q(i,4),cos_alpha_q(i,1),cos_alpha_q(i,2),cos_alpha_q(i,3),cos_alpha_q(i,4));
    fprintf(fid,'%c%c%c%c%c%c%c%c',s); 
    if (i < 2^in_quantify_bit)
        fprintf(fid,',\n'); 
    else
        fprintf(fid,';\n');  % 分号(;) 结束标志位
    end    
end
fclose(fid);

2、将重新生成的coe文件写入ROM核中
3、通过查表得到角度所对应的正弦值和余弦值

/*----------------------------------------------------------------------
    Author:  Zheng Wei
    Date:    2022-12-06
    Version: 1.0
    Description: It is a cos_sin_lut_3 testbench.
-----------------------------------------------------------------------*/

`timescale 1ns / 1ps

module tb;
                                                 
                                                  
    reg                         i_sys_clk       ; 
    reg                         i_sys_rst       ;
                                                 
    reg             [12:0]      r_rom_phase     ;
    reg             [12:0]      r_rom_phase_ff  ;
    reg             [12:0]      r_rom_phase_2ff ;
    reg             [12:0]      r_rom_phase_3ff ;
    reg             [09:0]      r_rom_addr      ;
    wire            [15:0]      w_cos_rom_val   ; // ROM readout value [0, pi/4)
    wire            [15:0]      w_sin_rom_val   ; // ROM readout value [0, pi/4)
    reg             [16:0]      r_cos_val       ; // transform output value[0, 2*pi)
    reg             [16:0]      r_sin_val       ; // transform output value[0, 2*pi)
    wire            [15:0]      o_cos_val       ; // final output value, drop the least 1bit
    wire            [15:0]      o_sin_val       ; // final output value, drop the least 1bit    
                                                                                             
                                                                                             
                                                                                               
    //  clock generate module
    parameter  period = 6.67;           // 150MHz
    initial begin
        i_sys_clk = 1'b0;
        forever     #(period/2)  i_sys_clk = ~i_sys_clk;
    end
                                                                         
	//-------------------------------------------------------------------
	
	//  system initialization
	task task_sysinit;
		begin		 
            r_rom_phase = 12'd0;
		end
	endtask
	
	//  system reset
	task task_reset;
        begin
            i_sys_rst = 1'b1;
            repeat(20)  @(negedge i_sys_clk);
            i_sys_rst = 1'b0;
        end
    endtask                                                      
                                                          
                                                          
                                                          
    initial begin
        task_sysinit;
	    task_reset  ;
                                            
        repeat(10)  @(posedge i_sys_clk);                                                                            
                                          
                                         
        repeat(100000) @(posedge i_sys_clk);
		                                  
		$stop;                          
    end
                                                                                          
                                                                                         
    always @(posedge i_sys_clk or posedge i_sys_rst) begin
        if (i_sys_rst) begin
            r_rom_phase <= 13'd0;       
        end                  
        else begin           
            r_rom_phase <= r_rom_phase + 13'd1;
        end
    end                                                                                      
                                                                                         
    always @(posedge i_sys_clk) begin
        r_rom_phase_ff  <= r_rom_phase;   
        r_rom_phase_2ff <= r_rom_phase_ff;
        r_rom_phase_3ff <= r_rom_phase_2ff;         
    end    
                                                                                         
    always @(posedge i_sys_clk or posedge i_sys_rst) begin
        if (i_sys_rst) begin
            r_rom_addr <= 10'd0;       
        end                  
        else if(r_rom_phase[10]) begin
            r_rom_addr <= ~r_rom_phase[9:0]; // alpha = n*pi/2 - beta
        end
        else begin
            r_rom_addr <= r_rom_phase[9:0];  // alpha = n*pi/2 + beta
        end
    end
                                                                                          
                                                                                         
    always @(posedge i_sys_clk) begin          
        case (r_rom_phase_3ff[12:10])
            3'b000: begin // [0, pi/4)
                r_cos_val <=  {1'b0, w_cos_rom_val};
                r_sin_val <=  {1'b0, w_sin_rom_val};
            end
                                                              
            3'b001: begin // [pi/4, pi/2)
                r_cos_val <=  {1'b0, w_sin_rom_val};
                r_sin_val <=  {1'b0, w_cos_rom_val};
            end                                                 
                                                             
            3'b010: begin // [pi/2, 3pi/4)
                r_cos_val <= ~{1'b0, w_sin_rom_val} + 1'b1;
                r_sin_val <=  {1'b0, w_cos_rom_val};
            end
                                                             
            3'b011: begin // [3pi/4, pi)
                r_cos_val <= ~{1'b0, w_cos_rom_val} + 1'b1;
                r_sin_val <=  {1'b0, w_sin_rom_val};
            end
                                                             
            3'b100: begin // [pi, 5*pi/4)
                r_cos_val <= ~{1'b0, w_cos_rom_val} + 1'b1;
                r_sin_val <= ~{1'b0, w_sin_rom_val} + 1'b1;
            end
                                                             
            3'b101: begin // [5*pi/4, 3*pi/2)
                r_cos_val <= ~{1'b0, w_sin_rom_val} + 1'b1;
                r_sin_val <= ~{1'b0, w_cos_rom_val} + 1'b1;
            end
                                                             
            3'b110: begin // [3*pi/2, 7*pi/4)
                r_cos_val <=  {1'b0, w_sin_rom_val};
                r_sin_val <= ~{1'b0, w_cos_rom_val} + 1'b1;
            end
                                                             
            3'b111: begin // [7*pi/4, 2*pi)
                r_cos_val <=  {1'b0, w_cos_rom_val};
                r_sin_val <= ~{1'b0, w_sin_rom_val} + 1'b1;
            end                              
        endcase
    end
                                                                                        
    assign  o_cos_val = r_cos_val[16:1];                                                                                   
    assign  o_sin_val = r_sin_val[16:1];                                                                                   
                                                                                         
//-----------------------------------------------------------------------------------------------                                                         
                                                            
    cos_sin_lut_3  u_cos_sin_lut_3(
        .clka   ( i_sys_clk                    ),  // input wire clka
        .addra  ( r_rom_addr                   ),  // input wire [9 : 0] addra
        .douta  ({w_sin_rom_val, w_cos_rom_val})   // output wire [31 : 0] douta
    );
                                                                                                 
                                                                                                  
                                                                                                 
endmodule

4、仿真结果


5、关键代码分析

  1. 使用10bit数据来量化pi/4,即pi/4 = 2^10,也即r_rom_phase = 13'b0_0100_0000_0000
  2. 所以我们只需要根据r_rom_phase[12:10]来判断输入角度处于8段的哪一段
  3. 然后根据三角函数公式调整最终输出值。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
NCO(Numerically Controlled Oscillator)是一种通过数字控制的振荡器。在FPGA(Field Programmable Gate Array)中实现NCO可以通过查表实现查表是一种将预先计算好的数值存储在查找表中,通过查表获取结果的方法。在NCO中,查表可以用来获取正弦余弦函数的离散点值,以实现振荡信号生成实现NCO的关键步骤如下: 1. 首先,定义NCO的参数,如采样率、输出频率、相位等。这些参数将影响最终输出的波形效果。 2. 创建一个查找表,将频率为Fs的连续正弦余弦函数进行离散化,存储在表中。表的大小可以根据所需精度和存储资源进行调整。 3. 根据输出频率和相位参数,计算在查找表中对应的离散点。相位参数确定了在查找表中的起始点。 4. 根据采样率和输出频率,计算每个采样点在查找表中的步长。这个步长决定了两个相邻离散点之间的距离。 5. 在FPGA中,使用时钟信号来驱动NCO模块。根据每个时钟周期的步长,逐个取出查找表中的对应离散点值,作为输出信号。 通过查表实现NCO的优点是简单高效,因为预先进行了计算,并且存储在查找表中,可以直接获取结果。同时,查表可以实现较高的波形精度,因为离散化的点越多,波形的近似程度越高。 总之,通过查表实现NCO在FPGA中可以实现高效、精确的数字振荡器功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jjzw1990

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值