RTL可拓展设计:VerilogHDL实现数据深度与位宽可配置的余弦函数发生器
@
参考资料
使用方法
文件构成
trigonometric_function/cos_function/cos_function_x1.v:ROM空间优化的余弦函数发生器trigonometric_function/cos_function/cos_function_x4.v:LUT规模优化的余弦函数发生器trigonometric_function/cos_function/Makefile:用于测试的make脚本文件trigonometric_function/cos_function/testbench.sv:测试激励文件trigonometric_function/cos_function/python/gen_cos_data.py:用于生成余弦函数数据的python脚本
主模块信号及其参数配置
虽然两种优化方向的余弦函数发生器的模块名稍有区别,但是参数配置、信号和信号的时序都是一样的。
// trigonometric_function/cos_function/cos_function_x1.v
module cos_function_x1 #(
parameter DEPTH_BITWIDTH = 8,
parameter DATA_BITWIDTH = 8
) (
input clk,
input rstn,
input [DEPTH_BITWIDTH-1:0] pword,
output reg [ DATA_BITWIDTH-1:0] cos
);
// trigonometric_function/cos_function/cos_function_x4.v
module cos_function_x4 #(
parameter DEPTH_BITWIDTH = 8,
parameter DATA_BITWIDTH = 8
) (
input clk,
input rstn,
input [DEPTH_BITWIDTH-1:0] pword,
output reg [ DATA_BITWIDTH-1:0] cos
);
以下是参数的相关解释:
| 参数 | 备注 |
|---|---|
DEPTH_BITWIDTH |
余弦函数发生器地址深度 |
DATA_BITWIDTH |
输出的数据位宽 |
需要注意的是,这里建议DEPTH_BITWIDTH和DATA_BITWIDTH相等,因为这样在经验上可以获得精度和空间利用的最高性价比。
以下是信号的相关解释:
| 方向 | 位宽 | 信号 | 备注 |
|---|---|---|---|
| 输入 | 1 | clk |
系统时钟信号 |
| 输入 | 1 | rstn |
同步复位信号,低有效 |
| 输入 | DEPTH_BITWIDTH |
pword |
余弦函数相位 |
| 输入 | DATA_BITWIDTH |
cos |
余弦函数输出 |
信号时序

这个的时序很简单,pword与cos同周期。
余弦函数数据生成
利用trigonometric_function/cos_function/python/gen_cos_data.py可以生成余弦函数数据,具体用法如下:
python gen_cos_data.py 深度位宽 数据位宽
这样就会在本目录下生成cos_data_x1.dat和cos_data_x4.dat两个数据文件,分别对应ROM空间优化版本和LUIT资源优化版本。
设计原理
本设计主要利用了查表的原理。
LUT优化的实现(cos_function_x4)如下:
// trigonometric_function/cos_function/cos_function_x4.v
module cos_function_x4 #(
parameter DEPTH_BITWIDTH = 8,
parameter DATA_BITWIDTH = 8
) (
input clk,
input rstn,
input [DEPTH_BITWIDTH-1:0] pword,
output reg [ DATA_BITWIDTH-1:0] cos
);
reg [DATA_BITWIDTH-1:0] cos_rom[0:2**DEPTH_BITWIDTH-1];
initial $readmemh("./python/cos_data_x4.dat", cos_rom);
always @(posedge clk) begin
if (!rstn) begin
cos <= {DATA_BITWIDTH{1'b0}};
end else begin
cos <= cos_rom[pword];
end
end
endmodule //cos_function_x4
很简单,就是用pword为地址读取cos_rom中的数据,然后直接输出。
以下为ROM优化版本(cos_function_x1)的实现:
// trigonometric_function/cos_function/cos_function_x1.v
module cos_function_x1 #(
parameter DEPTH_BITWIDTH = 8,
parameter DATA_BITWIDTH = 8
) (
input clk,
input rstn,
input [DEPTH_BITWIDTH-1:0] pword,
output reg [ DATA_BITWIDTH-1:0] cos
);
localparam DEPTH = 2 ** (DEPTH_BITWIDTH - 2);
localparam DATA_MAX_VALUE = 2 ** DATA_BITWIDTH;
reg [DATA_BITWIDTH-1:0] cos_rom[0:DEPTH-1];
initial $readmemh("./python/cos_data_x1.dat", cos_rom);
always @(posedge clk) begin
if (!rstn) begin
cos <= {DATA_BITWIDTH{1'b0}};
end else begin
if (pword < DEPTH) begin
cos <= cos_rom[pword];
end else if ((pword >= DEPTH) && (pword < 2 * DEPTH)) begin
cos <= DATA_MAX_VALUE - cos_rom[2*DEPTH-pword-1];
end else if ((pword >= 2 * DEPTH) && (pword < 3 * DEPTH)) begin
cos <= DATA_MAX_VALUE - cos_rom[pword-2*DEPTH];
end else begin
cos <= cos_rom[4*DEPTH-pword-1];
end
end
end
endmodule //cos_function_x1
在这个实现中,利用了余弦函数单周期内的特性求解,节省了$\frac{3}{4}$的ROM资源。
$\cos$函数的一个周期为$2\pi$,但是在这一个周期中可以将其分解为4段(暂不考虑边界情况):$(0,0.5\pi)$、$(0.5\pi,\pi)$、$(\pi,1.5\pi)$和$(1.5\pi,2\pi)$,这四个区间其实都可以用同一段区间的数据生成。假设$a\in(0,0.5\pi)$、$b\in(0.5\pi,\pi)$、$c\in(\pi,1.5\pi)$、$d\in(1.5\pi,2\pi)$,储存区$R$长度为$0.5\pi$,输出数据$D[a]=R[a]$,则易得以下式子:
$$ D[b]=0-R[\pi-b]$$
$$D[c]=R[c-\pi]$$
$$D[d]=R[2\pi-d]$$
然后看边界情况,如果下溢出(寻址到小于0的空间)就+1,如果上溢出(寻址到大于$a$的空间)就-1,就可以得到代码中的转换式了。
补充说明
cos_ram在具体用在某一块FPGA上的时候最好用IP进行替代,实测安路TD没有办法将这个储存区综合为ROM。使用ROM时时序基本跟用储存区是一样的,具体可以参考具体器件开发环境(如Vivado、Pango)相关IP的数据手册。
浙公网安备 33010602011771号