Verilog——Vidado中基于ROM IP的sin函数实现

Verilog——Vidado中基于ROM IP的sin函数实现

基本原理

  • 使用FPGA的内部ROM记录sin函数数值;

  • 为节省逻辑单元,仅记录步长0.1°,第一象限[0°,90.0°]范围的sin函数数值;

  • 其他象限的函数值由第一象限数值简单计算得到。

具体步骤

一. 生成ROM初始化数据文件(Matlab)

  • FPGA内部ROM实际上是FPGA上电时自动初始化写入数据得到,因此首先使用Matlab生成ROM初始化的数据文件(.coe);

  • 由于0.0 <= sin(x) <= 1.0,将[0, 1.0]线性映射至[0, 16383],其中16383 = 2^14 - 1;

  • Matlab代码如下:

clc;
close all;

%初始化文件,文件名称:sine.coe
fid = fopen('sine.coe', 'wt');
fprintf(fid, 'MEMORY_INITIALIZATION_RADIX = 10;\n');
fprintf(fid, 'MEMORY_INITIALIZATION_VECTOR =\n');
%比例[0:1] => [0:16383]
SCALE = 16383;
%步长
step = (pi/2) / 900;
%计算,写入
angle = 0.0;
for i = 1 : 901
    data = round(sin(angle) * SCALE);
    
    fprintf(fid, '%d', data);
    if(i < 901)
        fprintf(fid, ',');
    else
        fprintf(fid, ';');
    end
    
    angle = angle + step;
end
%关闭文件
fclose(fid);

disp('finish');

二. 生成ROM IP

在Vivado的IP Catalog中搜索Block Memory Generator,双击运行

  1. Basic参数设置

  1. Port A Options参数设置

3.在Other Options中勾选Load Init File,导入之前在Matlab中生成的.coe文件

三. sin函数实现代码

`timescale 1ns / 1ps

module sin(
    input               clk200M ,//时钟
    input       [11:0]  angle   ,//input angle ∈ [0,3599] <=> 0.0°~359.9°
    output reg  [15:0]  result   //output result = sin(angle)
);

localparam  ANGLE90     = 900   ;
localparam  ANGLE180    = 1800  ;
localparam  ANGLE270    = 2700  ;
localparam  ANGLE360    = 3600  ;

reg  [ 9:0] rom_addr;
wire [15:0] rom_data;
reg  [ 2:0] quadrant;
//实例化ip_rom
ip_rom ip_rom_inst (
    .clka           (clk200M       ),    // input wire clka
    .addra          (rom_addr      ),  // input wire [9 : 0] addra
    .douta          (rom_data      )  // output wire [15 : 0] douta
);

//quadrant
always @(*) begin
    if(angle < ANGLE90)//第一象限
        quadrant = 1;
    else if((angle >= ANGLE90) && (angle < ANGLE180))//第二象限
        quadrant = 2;
    else if((angle >= ANGLE180) && (angle < ANGLE270))//第三象限
        quadrant = 3;
    else if((angle >= ANGLE270) && (angle < ANGLE360))//第四象限
        quadrant = 4;
    else
        quadrant = 0;
end 

//rom_addr
always @(*) begin
    case(quadrant)
        1: rom_addr = angle;
        2: rom_addr = ANGLE180 - angle;
        3: rom_addr = angle - ANGLE180;
        4: rom_addr = ANGLE360 - angle;
        default: rom_addr = 'dz;
    endcase
end

//result
always @(*) begin
    case(quadrant)
        1,2: result = rom_data;
        3,4: result = ~rom_data + 1;
        default result = 'dz;
    endcase
end
endmodule

四. 仿真代码

`timescale 1ns / 1ps

module tb_sin();

reg         clk200M ;
reg  [11:0] angle   ;
wire [15:0] result  ;

sin sin_inst(
    .clk200M    (clk200M    ),//时钟
    .angle      (angle      ),//input angle ∈ [0,3600] <=> 0.0°~360.0°
    .result     (result     )//output result = sin(angle)
);
//clk200M
initial begin
    clk200M = 1'b0;
    forever begin
        #2.5;
        clk200M = ~clk200M;
    end
end

integer i;
//main
initial begin
    repeat(100) begin
        for(i = 0; i <= 3599; i = i + 1) begin
            angle = i;
            #5;
        end
    end
end



endmodule

五. 仿真结果

posted @ 2022-04-28 14:31  fxz_abc  阅读(1172)  评论(0编辑  收藏  举报