HLS / Chisel 实现CORDIC算法双曲系统

CORDIC(坐标旋转数字算法)是一种计算三角、双曲和其他数学函数的有效方法。它是一种数字算法,每次运算均产生一次结果输出。这使我们能够根据应用需求调整算法精度;增加运算迭代次数可以得到更精确的结果。

CORDIC是只使用加法、减法、移位和查找表实现的简单算法,这种算法在FPGA中实现效率高,在硬件算法实现中经常用到。

CORDIC算法双曲系统原理

( c o s h β , s i n h β ) (cosh \beta,sinh \beta) (coshβ,sinhβ) 定义了右半直角双曲线 x 2 − y 2 = 1 x^2-y^2 = 1 x2y2=1,所以有:

{ x P = cosh ⁡ β y P = sinh ⁡ β \left\{\begin{array}{l} x_{\mathrm{P}}=\cosh \beta\\ y_{\mathrm{P}}=\sinh \beta \end{array}\right. {xP=coshβyP=sinhβ

如下图我们假设 β \beta β为自定义角度概念,然后旋转 θ \theta θ角度到下一个点的这个过程我们做了一个旋转操作。(注意实际的角度非此大小,我们只是引入旋转概念)

在这里插入图片描述

同时有关于双曲三角函数的计算公式如下:

{ cosh ⁡ ( θ ± β ) = cosh ⁡ θ cosh ⁡ β ± sinh ⁡ θ sinh ⁡ β sinh ⁡ ( θ ± β ) = sinh ⁡ θ cosh ⁡ β ± cosh ⁡ θ sinh ⁡ β \left\{\begin{array}{l} \cosh (\theta \pm \beta)=\cosh \theta \cosh \beta \pm \sinh \theta \sinh \beta \\ \sinh (\theta \pm \beta)=\sinh \theta \cosh \beta \pm \cosh \theta \sinh \beta \end{array}\right. {cosh(θ±β)=coshθcoshβ±sinhθsinhβsinh(θ±β)=sinhθcoshβ±coshθsinhβ

则可以令 cosh ⁡ β = X i , Y i = sinh ⁡ β \cosh \beta=\mathbf{X}_{\mathrm{i}}, \mathbf{Y}_{\mathrm{i}}=\sinh \beta coshβ=Xi,Yi=sinhβ ,从中可以看出,将向量 ( X i , Y i ) (X_i,Y_i) (XiYi)旋转 θ \theta θ角,得到一个新的向量 ( X i + 1 , Y i + 1 ) (X_{i+1},Y_{i+1}) (Xi+1Yi+1) ,即上图的旋转,那么有:

{ X i + 1 = cosh ⁡ ( θ + β ) = cosh ⁡ θ cosh ⁡ β + sinh ⁡ θ sinh ⁡ β = X i cosh ⁡ θ + Y i shnh ⁡ θ Y i + 1 = sinh ⁡ ( θ + β ) = sinh ⁡ θ cosh ⁡ β + cosh ⁡ θ sinh ⁡ β = X i sinh ⁡ θ + Y i cosh ⁡ θ \left\{\begin{array}{l} X_{i+1}=\cosh (\theta+\beta)=\cosh \theta \cosh \beta+\sinh \theta \sinh \beta=X_{i} \cosh \theta+Y_{i} \operatorname{shnh} \theta \\ Y_{i+1}=\sinh (\theta+\beta)=\sinh \theta \cosh \beta+\cosh \theta \sinh \beta=X_{i} \sinh \theta+Y_{i} \cosh \theta \end{array}\right. {Xi+1=cosh(θ+β)=coshθcoshβ+sinhθsinhβ=Xicoshθ+YishnhθYi+1=sinh(θ+β)=sinhθcoshβ+coshθsinhβ=Xisinhθ+Yicoshθ

其中 θ \theta θ为旋转角度,化为矩阵表达形式, 平面旋转定义如下:

[ X i + 1 Y i + 1 ] = [ cosh ⁡ θ sinh ⁡ θ sinh ⁡ θ cosh ⁡ θ ] [ X i Y i ] \left[\begin{array}{l} \mathbf{X}_{\mathrm{i+1}} \\ \mathbf{Y}_{\mathrm{i+1}} \end{array}\right]=\left[\begin{array}{ll} \cosh \theta & \sinh \theta \\ \sinh \theta & \cosh \theta \end{array}\right]\left[\begin{array}{l} \mathbf{X}_{\mathrm{i}} \\ \mathbf{Y}_{\mathrm{i}} \end{array}\right] [Xi+1Yi+1]=[coshθsinhθsinhθcoshθ][XiYi]

使用迭代的方法, 旋转角度可在多步完成, 每一步的旋转只完成其中的一小部分,多步之后将会完成一个平面旋转。旋转等式如上定义,旋转方法如下:

  • 从一个起始位置开始旋转
  • 每次旋转的一个先验固定的角度
  • 每次旋转都朝向目标位置前进,不断调整方向逼近旋转目标

硬件优化

为了方便FPGA硬件计算,将如上的乘法转化为移位运算,则我们可以提取 cosh ⁡ θ n \cosh \theta_{n} coshθn 因子得到单步旋转等式为:

[ X n + 1 Y n + 1 ] = [ cosh ⁡ θ n sinh ⁡ θ n sinh ⁡ θ n cosh ⁡ θ n ] [ X n Y n ] = cosh ⁡ θ n [ 1 tanh ⁡ θ n tanh ⁡ θ n 1 ] [ X n Y n ] \begin{aligned} {\left[\begin{array}{l} \mathbf{X}_{n+1} \\ \mathbf{Y}_{n+1} \end{array}\right]=} & {\left[\begin{array}{ll} \cosh \theta_{n} & \sinh \theta_{n} \\ \sinh \theta_{n} & \cosh \theta_{n} \end{array}\right]\left[\begin{array}{l} \mathbf{X}_{n} \\ \mathbf{Y}_{n} \end{array}\right]=} \\ & \cosh \theta_{n}\left[\begin{array}{cc} 1 & \tanh \theta_{n} \\ \tanh \theta_{n} & 1 \end{array}\right]\left[\begin{array}{l} \mathbf{X}_{n} \\ \mathbf{Y}_{n} \end{array}\right] \end{aligned} [Xn+1Yn+1]=[coshθnsinhθnsinhθncoshθn][XnYn]=coshθn[1tanhθntanhθn1][XnYn]

然后令 θ n = tanh ⁡ − 1 ( 2 − n ) \theta_{n}=\tanh ^{-1}\left(2^{-n}\right) θn=tanh1(2n) tanh ⁡ θ n = ( 2 − n ) \tanh\theta_{n}=(2^{-n}) tanhθn=(2n)可以使得乘法转化为移位计算

由于 tanh ⁡ − 1 ( 2 − 0 ) = ∞ \tanh ^{-1}\left(2^{-0}\right)=\infty tanh1(20)=, 其迭代应该从 n=1开始。为了区分旋转角度,加上方向因子 ∑ n = 1 ∞ σ n θ n = θ , σ n = ± 1 \sum_{n=1}^{\infty} \sigma_{n} \theta_{n}=\theta, \sigma_{n}=\pm 1 n=1σnθn=θ,σn=±1, 即所有迭代角之和必须等于旋转角度。

[ X n + 1 Y n + 1 ] = cosh ⁡ θ n [ 1 S n 2 − n S n 2 − n 1 ] [ X n Y n ] \left[\begin{array}{l} \mathbf{X}_{n+1} \\ \mathbf{Y}_{n+1} \end{array}\right]=\cosh \theta_{n}\left[\begin{array}{cc} 1 & S_{n} 2^{-n} \\ S_{n} 2^{-n} & 1 \end{array}\right]\left[\begin{array}{l} X_{n} \\ Y_{n} \end{array}\right] [Xn+1Yn+1]=coshθn[1Sn2nSn2n1][XnYn]

再通过 N 次迭代后:

[ X j Y j ] = ∏ n = 1 N cosh ⁡ θ n [ 1   S n 2 − n S n 2 − n 1 ] [ X i Y i ] = K ∗ ∏ n = 1 N [ 1   S n 2 − n S n 2 − n 1 ] [ X i Y i ] \begin{gathered} {\left[\begin{array}{l} \mathbf{X}_{\mathrm{j}} \\ \mathbf{Y}_{\mathrm{j}} \end{array}\right]=\prod_{\mathrm{n}=1}^{N} \cosh \theta_{\mathrm{n}}\left[\begin{array}{cc} 1 & \mathrm{~S}_{\mathrm{n}} 2^{-\mathrm{n}} \\ \mathrm{S}_{\mathrm{n}} 2^{-\mathrm{n}} & 1 \end{array}\right]\left[\begin{array}{l} \mathbf{X}_{\mathrm{i}} \\ \mathbf{Y}_{\mathrm{i}} \end{array}\right]=} \\ \mathbf{K}^{*} \prod_{\mathrm{n}=1}^{N}\left[\begin{array}{cc} 1 & \mathrm{~S}_{\mathrm{n}} 2^{-\mathrm{n}} \\ \mathrm{S}_{\mathrm{n}} 2^{-\mathrm{n}} & 1 \end{array}\right]\left[\begin{array}{l} \mathbf{X}_{\mathrm{i}} \\ \mathbf{Y}_{\mathrm{i}} \end{array}\right] \end{gathered} [XjYj]=n=1Ncoshθn[1Sn2n Sn2n1][XiYi]=Kn=1N[1Sn2n Sn2n1][XiYi]

将比例因子从迭代公式中提取出来, 定义 K 为增益因子, 具体增益K 取决于迭代次数, 对于所有的初始 向量和所有的旋转角度而言, K 是一个常数。通常把 K 称作聚焦常数,常数P为K的倒数,则有:

K = 1 / P = ∏ n = 1 N cosh ⁡ ( tanh ⁡ − 1 ( 2 − n ) ) = ∏ n = 1 ∞ 1 − 2 − 2 n ≈ 0.8297816 \begin{gathered} K=1 / P=\prod_{n=1}^{N} \cosh \left(\tanh ^{-1}\left(2^{-n}\right)\right)= \\ \prod_{n=1}^{\infty} \sqrt{1-2^{-2 n}} \approx 0.8297816 \end{gathered} K=1/P=n=1Ncosh(tanh1(2n))=n=1122n 0.8297816

则每一次迭代我们可以只计算如下的公式,最后再乘上K,注意如下出现的z是对于角度的变化:

{ x i + 1 = x i + σ i ⋅ y i ⋅ 2 − i y i + 1 = y i + σ i ⋅ x i ⋅ 2 − i z i + 1 = z i − σ i ⋅ tanh ⁡ − 1 2 − i \left\{\begin{array}{l} x_{i+1}=x_{i}+\sigma_{i} \cdot y_{i} \cdot 2^{-i} \\ y_{i+1}=y_{i}+\sigma_{i} \cdot x_{i} \cdot 2^{-i} \\ z_{i+1}=z_{i}-\sigma_{i} \cdot \tanh ^{-1} 2^{-i} \end{array}\right. xi+1=xi+σiyi2iyi+1=yi+σixi2izi+1=ziσitanh12i

同时可以注意到如下的旋转的角度由于极限所以会限制旋转的大小只能在如下角度范围内。

θ max ⁡ = ∑ j = 1 ∞ tanh ⁡ − 1 ( 2 − j ) ≈ 1.0554693737354848 \theta_{\max }=\sum_{j=1}^{\infty} \tanh ^{-1}\left(2^{-j}\right) \approx 1.0554693737354848 θmax=j=1tanh1(2j)1.0554693737354848

计算模式

双曲系统有旋转模式和向量模式两种计算模式。

旋转模式中,(x,y)在(1,0)点,Z为初始化需要旋转的角度(即与目标点的差距), 当Z旋转变为 0时,则旋转完成,用来计算双曲正余弦函数,其推导公式如下:

{ x n = K n ( x 1 cosh ⁡ z 1 + y 1 sinh ⁡ z 1 ) y n = K n ( y 1 cosh ⁡ z 1 + x 1 sinh ⁡ z 1 ) z n = 0 K n = ∏ i = 1 n − 1 1 − 2 − 2 i σ i = { + 1 , z i ⩾ 0 − 1 , z i < 0 z 0 ∈ [ − π 3 , π 3 ] \begin{aligned} &\left\{\begin{array}{l} x_{n}=K_{n}\left(x_{1} \cosh z_{1}+y_{1} \sinh z_{1}\right) \\ y_{n}=K_{n}\left(y_{1} \cosh z_{1}+x_{1} \sinh z_{1}\right) \\ z_{n}=0 \\ K_{n}=\prod_{i=1}^{n-1} \sqrt{1-2^{-2 i}} \end{array}\right.\\ &\sigma_{i}=\left\{\begin{array}{l} +1, z_{i} \geqslant 0 \\ -1, z_{i}<0 \end{array}\right. \end{aligned}\\ z_0∈[-\frac \pi 3 , \frac \pi 3] xn=Kn(x1coshz1+y1sinhz1)yn=Kn(y1coshz1+x1sinhz1)zn=0Kn=i=1n1122i σi={+1,zi01,zi<0z0[3π,3π]

向量模式跟旋转模式差不多,其区别是旋转的方向取决于Y而不是 Y的符号, 即将输入向量 Y变为 0 , 计算相应的 tanh ⁡ − 1 \tanh ^{-1} tanh1 (反双曲正切)值。几何解释相当于起点在目标向量(x,y)处,z记录角度的,旋转到x轴就可以累计得到z为向量与x轴的夹角;最终旋转到x轴时,x坐标即此向量的模,即长度

{ x n = K n x 1 2 − y 1 2 y n = 0 z n = z 1 + tanh ⁡ − 1 ( y 1 / x 1 ) K n = ∏ i = 1 n − 1 1 − 2 − 2 i σ i = { + 1 , y i ⩽ 0 − 1 , y i > 0 − 0.784 < t a n h ( z n ) = y 1 x 1 < 0.784 \begin{aligned} &\left\{\begin{array}{l} x_{n}=K_{n} \sqrt{x_{1}^{2}-y_{1}^{2}} \\ y_{n}=0 \\ z_{n}=z_{1}+\tanh ^{-1}\left(y_{1} / x_{1}\right) \\ K_{n}=\prod_{i=1}^{n-1} \sqrt{1-2^{-2 i}} \end{array}\right.\\ &\sigma_{i}=\left\{\begin{array}{l} +1, y_{i} \leqslant 0 \\ -1, y_{i}>0 \end{array}\right. \end{aligned} \\ -0.784<tanh({z_n}) =\frac {y_1} {x_1}<0.784 xn=Knx12y12 yn=0zn=z1+tanh1(y1/x1)Kn=i=1n1122i σi={+1,yi01,yi>00.784<tanh(zn)=x1y1<0.784

可以看到如上都有输入输出 x 0 , y 0 , z 0 x_0,y_0,z_0 x0,y0,z0限制:

  • 旋转模式中,由于 θ max ⁡ = ∑ j = 1 ∞ tanh ⁡ − 1 ( 2 − j ) ≈ 1.055 \theta_{\max }=\sum_{j=1}^{\infty} \tanh ^{-1}\left(2^{-j}\right) \approx 1.055 θmax=j=1tanh1(2j)1.055可以知道必然其旋转不会超过 [ − 1.055 , 1.055 ] [-1.055, 1.055] [1.055,1.055] ,即本输入一定限制 z 0 ∈ [ − π 3 , π 3 ] z_0∈[-\frac \pi 3,\frac \pi 3] z0[3π,3π]
  • 向量模式中,注意 − 0.784 < t a n h ( z n ) = y 0 x 0 < 0.784 -0.784<tanh({z_n}) =\frac {y_0} {x_0}<0.784 0.784<tanh(zn)=x0y0<0.784

HLS实践向量计算模式

#define THETA_TYPE float
#define COS_SIN_TYPE float
#define NUM_ITERATIONS 50

// The cordic_phase array holds the angle for the current rotation
// The data type of cordic & the iterations number of cordic
THETA_TYPE cordic_phase_h[] = {0.5493061443340548, 0.25541281188299536, 0.12565721414045303, 0.06258157147700301, 0.03126017849066699, 0.01562627175205221, 0.007812658951540421, 0.003906269868396826, 0.0019531274835325498, 0.000976562810441036, 0.0004882812888051128, 0.0002441406298506386, 0.00012207031310632982, 6.103515632579122e-05, 3.05175781344739e-05, 1.5258789063684237e-05, 7.62939453139803e-06, 3.8146972656435034e-06, 1.907348632814813e-06, 9.53674316406539e-07, 4.768371582031611e-07, 2.38418579101567e-07, 1.192092895507818e-07, 5.960464477539069e-08, 2.980232238769532e-08, 1.4901161193847656e-08, 7.450580596923828e-09, 3.725290298461914e-09, 1.862645149230957e-09, 9.313225746154785e-10, 4.656612873077393e-10, 2.3283064365386963e-10, 1.1641532182693481e-10, 5.820766091346741e-11, 2.9103830456733704e-11, 1.4551915228366852e-11, 7.275957614183426e-12, 3.637978807091713e-12, 1.8189894035458565e-12, 9.094947017729282e-13, 4.547473508864641e-13, 2.2737367544323206e-13, 1.1368683772161603e-13, 5.684341886080802e-14, 2.842170943040401e-14, 1.4210854715202004e-14, 7.105427357601002e-15, 3.552713678800501e-15, 1.7763568394002505e-15, 8.881784197001252e-16, 4.440892098500626e-16, 2.220446049250313e-16, 1.1102230246251565e-16, 5.551115123125783e-17, 2.7755575615628914e-17, 1.3877787807814457e-17, 6.938893903907228e-18, 3.469446951953614e-18, 1.734723475976807e-18, 8.673617379884035e-19, 4.336808689942018e-19, 2.168404344971009e-19, 1.0842021724855044e-19, 5.421010862427522e-20, 2.710505431213761e-20, 1.3552527156068805e-20, 6.776263578034403e-21, 3.3881317890172014e-21, 1.6940658945086007e-21, 8.470329472543003e-22, 4.235164736271502e-22, 2.117582368135751e-22, 1.0587911840678754e-22, 5.293955920339377e-23, 2.6469779601696886e-23, 1.3234889800848443e-23, 6.617444900424222e-24, 3.308722450212111e-24, 1.6543612251060553e-24, 8.271806125530277e-25, 4.1359030627651384e-25, 2.0679515313825692e-25, 1.0339757656912846e-25, 5.169878828456423e-26, 2.5849394142282115e-26, 1.2924697071141057e-26, 6.462348535570529e-27, 3.2311742677852644e-27, 1.6155871338926322e-27, 8.077935669463161e-28, 4.0389678347315804e-28, 2.0194839173657902e-28, 1.0097419586828951e-28, 5.048709793414476e-29, 2.524354896707238e-29, 1.262177448353619e-29, 6.310887241768095e-30, 3.1554436208840472e-30, 1.5777218104420236e-30};

void cordic_hv(COS_SIN_TYPE x, COS_SIN_TYPE y, COS_SIN_TYPE &x_out, THETA_TYPE &z_out)
{
    // Set the initial vector that we wil rorate
    // current_cos = I; current = Q
    THETA_TYPE theta = 0;
    
    // This loop iteratively rotates the initial vector to find the
    // sine and cosine vlaue corresponding to the input theta angle
    for(int j = 1;j <= NUM_ITERATIONS; j++){
    
        // Save ther current_cos, so that it can be used in the sine calculation
        COS_SIN_TYPE temp_x = x;
        
        // Perform ther roation
        if(y < 0){
            x = x + y >> j;
            y = y + temp_x >> j;
            theta = theta - cordic_phase_h[j-1];
        } else{
            x = x - y >> j;
            y = y - temp_x >> j;
            theta = theta + cordic_phase_h[j-1];
        }
    }
    
    // Set ther final value
    x_out = x;
    z_out = theta
}

Chisel实践向量计算模式

定点数定义

首先定义定点数,注意在chisel.experimental包里有定点数的类

import chisel3._
import chisel3.util._
import chisel3.experimental._
import scala.collection.immutable
import scala.math._

/* 定点数的位宽定义 */ 
trait HasDataConfig {
  val DataWidth = 24
  val BinaryPoint = 10
}

源码实现

然后实现cordic算法计算 输出x和arctanh:

  • 其中注意有调试的接口将每一级流水线的计算值输出
import chisel3._
import chisel3.util._
import chisel3.experimental._
import scala.collection.immutable
import scala.math._


class cordic_hv extends Module with HasDataConfig with Cordic_Const_Data{
  /*
   * @x : 输入的向量横轴坐标 定点数类型
   * @y : 输入的向量横轴坐标 定点数类型 取值范围 y/x 属于(-1,1)
   * @x_out : 输出的x n次迭代的结果 定点数类型
   * @arctanh : 输出的arctanh(y/x)反三角函数 定点数类型
   * details: 利用cordic双曲坐标系的向量模式迭代得到双曲反三角函数的近似值,注意输入坐标
              在第一四象限,迭代次数不超过50,在25次时,K`值的变化已经超过了float的范围
  **/
  val io = IO(new Bundle {
    val x: FixedPoint = Input(FixedPoint(DataWidth.W, BinaryPoint.BP))
    val y: FixedPoint = Input(FixedPoint(DataWidth.W, BinaryPoint.BP))
    val x_out: FixedPoint = Output(FixedPoint(DataWidth.W, BinaryPoint.BP))
    val arctanh: FixedPoint = Output(FixedPoint(DataWidth.W, BinaryPoint.BP))
    /* Debug */
    //    val sin_o: Vec[FixedPoint] = Output(Vec(NUM_ITERATIONS, FixedPoint(DataWidth.W, BinaryPoint.BP)))
    //    val cos_o: Vec[FixedPoint] = Output(Vec(NUM_ITERATIONS, FixedPoint(DataWidth.W, BinaryPoint.BP)))
    //    val theta_o: Vec[FixedPoint] = Output(Vec(NUM_ITERATIONS, FixedPoint(DataWidth.W, BinaryPoint.BP)))
  })

  /* 旋转度数表 */
  val temp_seq: immutable.Seq[Double] = immutable.Seq(
    0.5493061443340548, 0.25541281188299536, 0.12565721414045303, 0.06258157147700301, 0.03126017849066699,
    0.01562627175205221, 0.007812658951540421, 0.003906269868396826, 0.0019531274835325498, 0.000976562810441036,
    0.0004882812888051128, 0.0002441406298506386, 0.00012207031310632982, 6.103515632579122e-05, 3.05175781344739e-05,
    1.5258789063684237e-05, 7.62939453139803e-06, 3.8146972656435034e-06, 1.907348632814813e-06, 9.53674316406539e-07,
    4.768371582031611e-07, 2.38418579101567e-07, 1.192092895507818e-07, 5.960464477539069e-08, 2.980232238769532e-08,
    1.4901161193847656e-08, 7.450580596923828e-09, 3.725290298461914e-09, 1.862645149230957e-09, 9.313225746154785e-10,
    4.656612873077393e-10, 2.3283064365386963e-10, 1.1641532182693481e-10, 5.820766091346741e-11, 2.9103830456733704e-11,
    1.4551915228366852e-11, 7.275957614183426e-12, 3.637978807091713e-12, 1.8189894035458565e-12, 9.094947017729282e-13,
    4.547473508864641e-13, 2.2737367544323206e-13, 1.1368683772161603e-13, 5.684341886080802e-14, 2.842170943040401e-14,
    1.4210854715202004e-14, 7.105427357601002e-15, 3.552713678800501e-15, 1.7763568394002505e-15, 8.881784197001252e-16,
    4.440892098500626e-16, 2.220446049250313e-16, 1.1102230246251565e-16, 5.551115123125783e-17, 2.7755575615628914e-17,
    1.3877787807814457e-17, 6.938893903907228e-18, 3.469446951953614e-18, 1.734723475976807e-18, 8.673617379884035e-19,
    4.336808689942018e-19, 2.168404344971009e-19, 1.0842021724855044e-19, 5.421010862427522e-20, 2.710505431213761e-20,
    1.3552527156068805e-20, 6.776263578034403e-21, 3.3881317890172014e-21, 1.6940658945086007e-21, 8.470329472543003e-22,
    4.235164736271502e-22, 2.117582368135751e-22, 1.0587911840678754e-22, 5.293955920339377e-23, 2.6469779601696886e-23,
    1.3234889800848443e-23, 6.617444900424222e-24, 3.308722450212111e-24, 1.6543612251060553e-24, 8.271806125530277e-25,
    4.1359030627651384e-25, 2.0679515313825692e-25, 1.0339757656912846e-25, 5.169878828456423e-26, 2.5849394142282115e-26,
    1.2924697071141057e-26, 6.462348535570529e-27, 3.2311742677852644e-27, 1.6155871338926322e-27, 8.077935669463161e-28,
    4.0389678347315804e-28, 2.0194839173657902e-28, 1.0097419586828951e-28, 5.048709793414476e-29, 2.524354896707238e-29,
    1.262177448353619e-29, 6.310887241768095e-30, 3.1554436208840472e-30, 1.5777218104420236e-30)
  val inits_cordic_phase_h: immutable.Seq[FixedPoint] = temp_seq.map(t => FixedPoint.fromDouble(t, DataWidth.W, BinaryPoint.BP))
  val cordic_phase_h: Vec[FixedPoint] = VecInit(inits_cordic_phase_h)

  /* 初始化计算的寄存器数组,形成NUM_ITERATIONS级流水 */
  val current_x: Vec[FixedPoint] = RegInit(VecInit(Seq.fill(NUM_ITERATIONS)(FixedPoint.fromDouble(0.0, DataWidth.W, BinaryPoint.BP)))) // cos
  val current_y: Vec[FixedPoint] = RegInit(VecInit(Seq.fill(NUM_ITERATIONS)(FixedPoint.fromDouble(0.0, DataWidth.W, BinaryPoint.BP)))) // sin
  val current_z: Vec[FixedPoint] = RegInit(VecInit(Seq.fill(NUM_ITERATIONS)(FixedPoint.fromDouble(0.0, DataWidth.W, BinaryPoint.BP)))) // 目标角度

  for (i <- 1 to NUM_ITERATIONS) {
    /*
     * x[i] = K`(x[i-1] + sigma * 2^(-i) * y[i-1])
     * y[i] = K`(y[i-1] + sigma * 2^(-i) * x[i-1])
     * z[i] = z[i-1] - sigma * arctanh(2^(-i))
    **/
    if (i == 1) {
      /* 流水线第一级直接对(io.x,io.y)点做运算 */
      when(io.y < FixedPoint.fromDouble(0.0, DataWidth.W, BinaryPoint.BP)) {
        current_x(i - 1) := io.x + (io.y >> i)
        current_y(i - 1) := io.y + (io.x >> i)
        current_z(i - 1) := -cordic_phase_h(i-1)
      }.otherwise {
        current_x(i - 1) := io.x - (io.y >> i)
        current_y(i - 1) := io.y - (io.x >> i)
        current_z(i - 1) := cordic_phase_h(i-1)
      }
    } else {
      when(current_y(i - 2) < FixedPoint.fromDouble(0.0, DataWidth.W, BinaryPoint.BP)) {
        current_x(i - 1) := current_x(i - 2) + (current_y(i - 2) >> i) // 移位替代乘法
        current_y(i - 1) := current_y(i - 2) + (current_x(i - 2) >> i)
        current_z(i - 1) := current_z(i - 2) - cordic_phase_h(i - 1)
      }.otherwise {
        current_x(i - 1) := current_x(i - 2) - (current_y(i - 2) >> i)
        current_y(i - 1) := current_y(i - 2) - (current_x(i - 2) >> i)
        current_z(i - 1) := current_z(i - 2) + cordic_phase_h(i-1)
      }
    }

    /* Debug */
    //    io.cos_o(i) := current_x(i)
    //    io.sin_o(i) := current_y(i)
    //    io.theta_o(i) := current_theta(i)
  }

  io.x_out := current_x(NUM_ITERATIONS - 1)
  io.arctanh := current_z(NUM_ITERATIONS - 1)
}

定义伴生对象工厂方法

最后我们定义两个个伴生对象工厂方法来方便直接调用函数,无需多加连线

/*
 * 定义这个类的伴生对象,并定义一个工厂方法来简化模块的例化和连线。
**/
object cordic_hv{
  def apply(x: FixedPoint, y: FixedPoint):(FixedPoint, FixedPoint) = {
    /*
    * @x : 输入的向量横轴坐标 定点数类型
    * @y : 输入的向量横轴坐标 定点数类型 取值范围 y/x 属于(-1,1)
    * @x_out : 输出的x n次迭代的结果 定点数类型
    * @arctanh : 输出的arctanh(y/x)反三角函数 定点数类型
    * details: 利用cordic双曲坐标系的向量模式迭代得到双曲反三角函数的近似值,注意输入坐标
    *          在第一四象限,迭代次数不超过50,在25次时,K`值的变化已经超过了float的范围
    **/
    val cordic_cv = Module(new cordic_hv)
    cordic_cv.io.x := x
    cordic_cv.io.y := y
    (cordic_cv.io.x_out, cordic_cv.io.arctanh)
  }
}
object cordic_arctanh{
  def apply(x: FixedPoint, y: FixedPoint):FixedPoint = {
    /*
    * @x : 输入的向量横轴坐标 定点数类型
    * @y : 输入的向量横轴坐标 定点数类型 取值范围 y/x 属于(-1,1)
    * @arctanh : 输出的arctanh(y/x)反三角函数 定点数类型
      details: 利用cordic双曲坐标系的向量模式迭代得到双曲反三角函数的近似值,注意输入坐标
               在第一四象限,迭代次数不超过50,在25次时,K`值的变化已经超过了float的范围
    **/
    val (x_out, arctanh) = cordic_hv(x, y)
    arctanh
  }
}

执行

object cordic_hv_app extends App {
  println(getVerilogString(new cordic_hv))
  (new chisel3.stage.ChiselStage).emitVerilog(new cordic_hv)
}

测试

import org.scalatest._
import chisel3._
import chisel3.experimental.BundleLiterals._
import chiseltest._
import chisel3.experimental._
import scala.math._

class Cordic_HV_Tester extends FlatSpec with HasDataConfig with ChiselScalatestTester with Matchers {
  behavior of "mytest2"
  it should "do something" in{
    val NUM_ITERATIONS = 20
    test(new cordic_hv){ c =>
      c.io.x.poke(FixedPoint.fromDouble(cosh(70*3.1415926/180),32.W, 20.BP))
      c.io.y.poke(FixedPoint.fromDouble(sinh(70*3.1415926/180),32.W, 20.BP))
      for(i <- 0 until NUM_ITERATIONS*2){
        println(s"*****************${i}******************")
        println(s"x_out ${c.io.x_out.peek}")
        println(s"arctanh ${c.io.arctanh.peek}\n")
        //         for(j <- 0 until NUM_ITERATIONS){
        //            println(s"sin_o  (${j}) ${c.io.sin_o(j).peek}")
        //            println(s"cos_o  (${j}) ${c.io.cos_o(j).peek}")
        //            println(s"theta_o(${j}) ${c.io.theta_o(j).peek}")
        //         }

        c.clock.step(1)
      }
    }
  }
}

参考论文

[1]陈石平, 李全, 莫丽兰,等. 基于CORDIC的反双曲正切函数的FPGA实现[J]. 计算机工程与科学, 2009, 31(5):3.

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AlwaysDayOne

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值