RTL可拓展设计:VerilogHDL实现数据深度与位宽可配置的余弦函数发生器

@

参考资料

Github代码仓库

使用方法

文件构成

  • 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的数据手册。

posted @ 2026-09-18 10:03  LogicField  阅读(4)  评论(0)    收藏  举报