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,双击运行
- Basic参数设置
- 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