HLS / Chisel 实践CORDIC高性能计算复数平方根

CORDIC(坐标旋转数字算法)是一种计算三角、双曲和其他数学函数的数字算法,每次运算均产生一次结果输出。这使我们能够根据应用需求调整算法精度;增加运算迭代次数可以得到更精确的结果。CORDIC 是只使用加法、减法、移位和查找表实现的简单算法,这种算法在FPGA中实现效率高,在硬件算法实现中经常用到。

论文[1]介绍了一种通过CORDIC算法中CR、CV、HV三种模式拆分高性能计算复数平方根的方法,本文将使用Chisel 实现该方法,这里默认在以下几篇文章的CORDIC原理基础之上完成。

HLS / Chisel 实现CORDIC算法计算反三角函数(圆坐标系向量模式)
HLS / Chisel 实现CORDIC算法双曲系统
HLS / Chisel 利用CORDIC双曲系统实现平方根计算

原理

CORDIC背景

CORDIC算法有旋转和向量两个模式,分别可以在圆坐标系、线性坐标系和双曲线坐标系使用,从而可以演算出8种运算,而结合这8种运算也可以衍生出其他许多运算。下表展示了8种运算在CORDIC算法中计算的核心公式推导:

在这里插入图片描述

接下来我们将利用如上的CV、HV三种模式实现复数平方根

复数平方根

首先有如下的复数平方根公式 x + i y \sqrt{x+i y} x+iy 的结果推导,首先转换为极坐标模式 r c o s θ + i r s i n θ \sqrt{rcos\theta+i rsin\theta} rcosθ+irsinθ ,由De Moivre公式可以得到复数乘方的公式如下:

Z n = [ r ( c o s θ + i s i n θ ] n = r n [ c o s ( n θ ) + i s i n ( n θ ) ] Z^n = [r(cos\theta+isin\theta]^n = r^n[cos(n\theta)+isin(n\theta)] Zn=[r(cosθ+isinθ]n=rn[cos(nθ)+isin(nθ)]

则求平方根为:

Z 1 2 = r ( c o s θ 2 + s i n θ 2 ) Z^{\frac 1 2} = \sqrt r (cos{\frac \theta 2} + sin{\frac \theta 2}) Z21=r (cos2θ+sin2θ)

由于:

c o s θ 2 = ± 1 + c o s θ 2 , s i n θ 2 = ± 1 − c o s θ 2 cos{\frac \theta 2} = \pm \sqrt{\frac {1+cos\theta} 2},sin{\frac \theta 2} = \pm \sqrt{\frac {1-cos\theta} 2} cos2θ=±21+cosθ ,sin2θ=±21cosθ

x > 0 , θ ∈ [ − π 2 , π 2 ] x>0,\theta∈[-\frac \pi 2, \frac \pi 2] x>0,θ[2π,2π]

再将极坐标转化为原坐标系可以得到如下的推到结果:

x + i y = x 2 + y 2 + x 2 ± i x 2 + y 2 − x 2 \sqrt{x+i y}=\sqrt{\frac{\sqrt{x^{2}+y^{2}}+x}{2}} \pm i \sqrt{\frac{\sqrt{x^{2}+y^{2}}-x}{2}} x+iy =2x2+y2 +x ±i2x2+y2 x

CORDIC复数平方根高性能计算原理

  1. CV模式转化为极坐标形式

参考文章HLS / Chisel 实现CORDIC算法计算反三角函数(圆坐标系向量模式)

首先输入如下的数值

X 0 = x , Y 0 = y , Z 0 = 0 \mathrm{X}_{0}=x, \quad Y_{0}=y, \quad Z_{0}=0 X0=x,Y0=y,Z0=0

在此反三角函数计算模块可以得到如下的结果:

X = K x 2 + y 2 = r , Y = 0 , Z = tan ⁡ ( y / x ) − 1 X=K \sqrt{x^{2}+y^{2}} = r , \quad \mathrm{Y}=0, \quad Z=\tan (y / x)^{-1} X=Kx2+y2 =r,Y=0,Z=tan(y/x)1

其中K在CV模式的推导文章中有介绍,是一个固定的常数。

  1. HV_square_root模式计算

参考文章HLS / Chisel 利用CORDIC双曲系统实现平方根计算

将1中的结果 r 作如下处理:

a r e = ( r + x ) / 2 , a i m = ( r − x ) / 2 a_{re}=(r+x)/2,a_{im}=(r-x)/2 are=(r+x)/2aim=(rx)/2

即输入HV模块的值是:

X 0 = a + 1 , Y 0 = a − 1 , Z 0 = 0 \mathrm{X}_{0}=a+1, \quad Y_{0}=a-1, \quad Z_{0}=0 X0=a+1,Y0=a1,Z0=0

具体HV模块内部的计算参考以上引用文章和HLS / Chisel 实现CORDIC算法双曲系统 实现.

用两个最终的HV_square_root模块分别计算实部虚部结果得到如下:

X = x 2 + y 2 + x 2 Y = x 2 + y 2 − x 2 X = \sqrt{\frac{\sqrt{x^{2}+y^{2}}+x}{2}} \quad Y= \sqrt{\frac{\sqrt{x^{2}+y^{2}}-x}{2}} X=2x2+y2 +x Y=2x2+y2 x

具体的流程模块如下:

在这里插入图片描述

HLS实践

#define COS_SIN_TYPE float
#define NUM_ITERATIONS 50

typedef struct { COS_SIN_TYPE re, im; } Complex; 

THETA_TYPE cordic_K = 0.6072529350088814

void cordic_complex_square_root(Complex x, Complex &res)
{
    COS_SIN_TYPE p,q;
    p = divisor.re;
    q = divisor.im;
 
    /* CV模式 */
    for(int j = 0;j < NUM_ITERATIONS; j++){
        COS_SIN_TYPE temp_cos = p;
        if(y < 0){
            p = p - q >> j;
            q = q + temp_cos >> j;
        } else{
            x = x + y >> j;
            q = q - temp_cos >> j;
        }
    }
    
    // Set ther final sine and cosine values
    r = p * cordic_K_n;
    
    /* 中间处理 */
    COS_SIN_TYPE  x_next = (r + p) >> 1;
    COS_SIN_TYPE  y_next = (r - p) >> 1;
    
    /* HV 模块,调用之前文章实现了的函数*/
    cordic_hv_square_root(x_next , res.re);
    cordic_hv_square_root(y_next , res.im);
}

CHISEL实践

定点数定义

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

/* Cordic 的全局常数定义 */
trait Cordic_Const_Data extends HasDataConfig{
  /* 迭代次数配置 */
  val NUM_ITERATIONS = 20
}

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

源码实现

class cordic_complex_square_root extends Module with HasDataConfig with Cordic_Const_Data {
  /*
   * @io.op : 输入的被除数 定点数表示的复数Complex 输入范围R+
   * @io.res : 输出的复数除法结果 定点数表示的复数Complex 范围R
   * details: 利用cordic实现复数除法,周期为2*迭代次数NUM_ITERATIONS
  **/
  val io = IO(new Bundle {
    val op: Complex = Input(new Complex())
    val res: Complex = Output(new Complex())
//    val debug_cv_out: FixedPoint = Output(FixedPoint(DataWidth.W, BinaryPoint.BP))
  })

  val x = WireInit(io.op.re)
  val y = WireInit(io.op.im)

  /* CV模式求square(x^2+y^2) */
  /* 初始化计算的寄存器数组,形成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 cordic_K: FixedPoint = FixedPoint.fromDouble(0.6072529350088814, DataWidth.W, BinaryPoint.BP)
  /* 寄存最初输入的x值 */
  val reg_x: Vec[FixedPoint] = RegInit(VecInit(Seq.fill(NUM_ITERATIONS)(FixedPoint.fromDouble(0.0, DataWidth.W, BinaryPoint.BP)))) // cos
  for (i <- 0 until 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])
    **/
    if (i == 0) {
      reg_x(0) := x
      /* 流水线第一级直接对(io.x,io.y)点做运算 */
      when(y < FixedPoint.fromDouble(0.0, DataWidth.W, BinaryPoint.BP)) {
        current_x(i) := x - (y >> i)
        current_y(i) := y + (x >> i)
      }.otherwise {
        current_x(i) := x + (y >> i)
        current_y(i) := y - (x >> i)
      }
    } else {
      reg_x(i) := reg_x(i - 1)
      when(current_y(i - 1) < FixedPoint.fromDouble(0.0, DataWidth.W, BinaryPoint.BP)) {
        current_x(i) := current_x(i - 1) - (current_y(i - 1) >> i) // 移位替代乘法
        current_y(i) := current_y(i - 1) + (current_x(i - 1) >> i)
      }.otherwise {
        current_x(i) := current_x(i - 1) + (current_y(i - 1) >> i)
        current_y(i) := current_y(i - 1) - (current_x(i - 1) >> i)
      }
    }
  }

//  io.debug_cv_out := current_x(NUM_ITERATIONS - 1)*cordic_K
  val temp_cv_out = current_x(NUM_ITERATIONS - 1)*cordic_K

  /* HV模式求实数平方根 */
  val next_x = (temp_cv_out + reg_x(NUM_ITERATIONS - 1)) >> 1
  val next_y = (temp_cv_out - reg_x(NUM_ITERATIONS - 1)) >> 1
  io.res.re := cordic_hv_square_root(next_x)
  io.res.im := cordic_hv_square_root(next_y)
}

object cordic_complex_square_root{
  def apply(x: Complex): Complex = {
    /*
     * @io.op : 输入的被除数 定点数表示的复数Complex 输入范围R+
     * @io.res : 输出的复数除法结果 定点数表示的复数Complex 范围R
     * details: 利用cordic实现复数除法,周期为2*迭代次数NUM_ITERATIONS
    **/
    val unit = Module(new cordic_complex_square_root)
    unit.io.op := x
    unit.io.res
  }
}

测试

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

class Cordic_Complex_Square_Root_Tester extends FlatSpec with ChiselScalatestTester with Matchers {
  behavior of "mytest2"
  it should "do something" in {
    val NUM_ITERATIONS = 20
    test(new cordic_complex_square_root) { c =>
      val opre = 0.6
      val opim = 0.6
      c.io.op.re.poke(FixedPoint.fromDouble(opre, 32.W, 20.BP))
      c.io.op.im.poke(FixedPoint.fromDouble(opim, 32.W, 20.BP))
      // 对比组
//      val cv_out = pow(opre * opre + opim * opim, 0.5)
      val opposite_resre = pow((pow(opre * opre + opim * opim, 0.5) + opre) / 2, 0.5)
      val opposite_resim = pow((pow(opre * opre + opim * opim, 0.5) - opre) / 2, 0.5)

      for (i <- 0 to NUM_ITERATIONS * 2) {
        println(s"*****************${i}******************")
        println(s"res.re ${c.io.res.re.peek}")
        println(s"res.im ${c.io.res.im.peek}\n")
//        println(s"debug_cv_out ${c.io.debug_cv_out.peek}\n")
        c.clock.step(1)
      }

//      println(s"cv_out ${cv_out}")
      println(s"opposite_res.re ${opposite_resre}")
      println(s"opposite_res.im ${opposite_resim}")
    }

    test(new cordic_complex_square_root) { c =>
      val opre = 3.0
      val opim = -3.0
      c.io.op.re.poke(FixedPoint.fromDouble(opre, 32.W, 20.BP))
      c.io.op.im.poke(FixedPoint.fromDouble(opim, 32.W, 20.BP))
      // 对比组
      val opposite_resre = pow((pow(opre * opre + opim * opim, 0.5) + opre) / 2, 0.5)
      val opposite_resim = pow((pow(opre * opre + opim * opim, 0.5) - opre) / 2, 0.5)
      c.clock.step(41)

      println(s"res.re ${c.io.res.re.peek}")
      println(s"res.im ${c.io.res.im.peek}\n")

      println(s"opposite_res.re ${opposite_resre}")
      println(s"opposite_res.im ${opposite_resim}")
    }

  }
}

// 执行 sbt "testOnly Cordic_Complex_Square_Root_Tester"
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AlwaysDayOne

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

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

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

打赏作者

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

抵扣说明:

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

余额充值