当前位置:   article > 正文

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

求余计算的rtl实现

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
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

以下是参数的相关解释:

参数备注
DEPTH_BITWIDTH余弦函数发生器地址深度
DATA_BITWIDTH输出的数据位宽

需要注意的是,这里建议DEPTH_BITWIDTHDATA_BITWIDTH相等,因为这样在经验上可以获得精度和空间利用的最高性价比。

以下是信号的相关解释:

方向位宽信号备注
输入1clk系统时钟信号
输入1rstn同步复位信号,低有效
输入DEPTH_BITWIDTHpword余弦函数相位
输入DATA_BITWIDTHcos余弦函数输出

信号时序

请添加图片描述

这个的时序很简单,pwordcos同周期。

余弦函数数据生成

利用trigonometric_function/cos_function/python/gen_cos_data.py可以生成余弦函数数据,具体用法如下:

python gen_cos_data.py 深度位宽 数据位宽
  • 1

这样就会在本目录下生成cos_data_x1.datcos_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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

很简单,就是用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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

在这个实现中,利用了余弦函数单周期内的特性求解,节省了 3 4 \frac{3}{4} 43的ROM资源。

cos ⁡ \cos cos函数的一个周期为 2 π 2\pi 2π,但是在这一个周期中可以将其分解为4段(暂不考虑边界情况): ( 0 , 0.5 π ) (0,0.5\pi) (0,0.5π) ( 0.5 π , π ) (0.5\pi,\pi) (0.5π,π) ( π , 1.5 π ) (\pi,1.5\pi) (π,1.5π) ( 1.5 π , 2 π ) (1.5\pi,2\pi) (1.5π,2π),这四个区间其实都可以用同一段区间的数据生成。假设 a ∈ ( 0 , 0.5 π ) a\in(0,0.5\pi) a(0,0.5π) b ∈ ( 0.5 π , π ) b\in(0.5\pi,\pi) b(0.5π,π) c ∈ ( π , 1.5 π ) c\in(\pi,1.5\pi) c(π,1.5π) d ∈ ( 1.5 π , 2 π ) d\in(1.5\pi,2\pi) d(1.5π,2π),储存区 R R R长度为 0.5 π 0.5\pi 0.5π,输出数据 D [ a ] = R [ a ] D[a]=R[a] D[a]=R[a],则易得以下式子:

D [ b ] = 0 − R [ π − b ] D[b]=0-R[\pi-b] D[b]=0R[πb]

D [ c ] = R [ c − π ] D[c]=R[c-\pi] D[c]=R[cπ]

D [ d ] = R [ 2 π − d ] D[d]=R[2\pi-d] D[d]=R[2πd]

然后看边界情况,如果下溢出(寻址到小于0的空间)就+1,如果上溢出(寻址到大于 a a a的空间)就-1,就可以得到代码中的转换式了。

补充说明

cos_ram在具体用在某一块FPGA上的时候最好用IP进行替代,实测安路TD没有办法将这个储存区综合为ROM。使用ROM时时序基本跟用储存区是一样的,具体可以参考具体器件开发环境(如Vivado、Pango)相关IP的数据手册。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/625444
推荐阅读
相关标签
  

闽ICP备14008679号