浮点分频器硬件实现细节

时间:2013-06-13 23:05:26

标签: algorithm math floating-point hardware verilog

我正在尝试在硬件中实现32位浮点硬件分频器,我想知道我是否可以就不同算法之间的一些权衡取得任何建议?

我的浮点单元目前支持乘法和加法/减法,但我不打算将它切换到融合乘法 - 加法(FMA)浮点架构,因为这是一个嵌入式平台,我试图最小化区域使用。

1 个答案:

答案 0 :(得分:3)

很久很久以前,我遇到了这个简洁易行的浮动/定点分割算法,用于那个时期的军用FPU:

  1. 输入必须是无符号的并且已移位x < y,且两者都在范围< 0.5 ; 1 >

    请勿忘记存储班次sh = shx - shy与原始标志的差异

  2. 找到f(通过迭代)所以y*f -> 1 ....之后x*f -> x/y这是除法结果

    < / LI>
  3. x*f移回sh并恢复结果符号(sig=sigx*sigy)

    可以像这样轻松计算x*f

    z=1-y
    (x*f)=(x/y)=x*(1+z)*(1+z^2)*(1+z^4)*(1+z^8)*(1+z^16)...(1+z^2n)
    

    ,其中

    n = log2(num of fractional bits for fixed point, or mantisa bit size for floating point)
    

    当固定位宽数据类型的z^2n为零时,您也可以停止。

  4. [Edit2]有一点时间和心情,所以32位IEEE 754 C ++实现

    我删除了旧的(bignum)示例,以避免混淆未来的读者(如果需要,它们仍然可以在编辑历史中访问)

    //---------------------------------------------------------------------------
    // IEEE 754 single masks
    const DWORD _f32_sig    =0x80000000;    // sign
    const DWORD _f32_exp    =0x7F800000;    // exponent
    const DWORD _f32_exp_sig=0x40000000;    // exponent sign
    const DWORD _f32_exp_bia=0x3F800000;    // exponent bias
    const DWORD _f32_exp_lsb=0x00800000;    // exponent LSB
    const DWORD _f32_exp_pos=        23;    // exponent LSB bit position
    const DWORD _f32_man    =0x007FFFFF;    // mantisa
    const DWORD _f32_man_msb=0x00400000;    // mantisa MSB
    const DWORD _f32_man_bits=       23;    // mantisa bits
    //---------------------------------------------------------------------------
    float f32_div(float x,float y)
        {
        union _f32          // float bits access
            {
            float f;        // 32bit floating point
            DWORD u;        // 32 bit uint
            };
        _f32 xx,yy,zz; int sh; DWORD zsig; float z;
        //      result signum        abs value
        xx.f=x; zsig =xx.u&_f32_sig; xx.u&=(0xFFFFFFFF^_f32_sig);
        yy.f=y; zsig^=yy.u&_f32_sig; yy.u&=(0xFFFFFFFF^_f32_sig);
        // initial exponent difference sh and normalize exponents to speed up shift in range
        sh =0;
        sh-=((xx.u&_f32_exp)>>_f32_exp_pos)-(_f32_exp_bia>>_f32_exp_pos); xx.u&=(0xFFFFFFFF^_f32_exp); xx.u|=_f32_exp_bia;
        sh+=((yy.u&_f32_exp)>>_f32_exp_pos)-(_f32_exp_bia>>_f32_exp_pos); yy.u&=(0xFFFFFFFF^_f32_exp); yy.u|=_f32_exp_bia;
        // shift input in range
        while (xx.f> 1.0f) { xx.f*=0.5f; sh--; }
        while (xx.f< 0.5f) { xx.f*=2.0f; sh++; }
        while (yy.f> 1.0f) { yy.f*=0.5f; sh++; }
        while (yy.f< 0.5f) { yy.f*=2.0f; sh--; }
        while (xx.f<=yy.f) { yy.f*=0.5f; sh++; }
        // divider block
        z=(1.0f-yy.f);
        zz.f=xx.f*(1.0f+z);
        for (;;)
            {
            z*=z; if (z==0.0f) break;
            zz.f*=(1.0f+z);
            }
        // shift result back
        for (;sh>0;) { sh--; zz.f*=0.5f; }
        for (;sh<0;) { sh++; zz.f*=2.0f; }
        // set signum
        zz.u&=(0xFFFFFFFF^_f32_sig);
        zz.u|=zsig;
        return zz.f;
        }
    //---------------------------------------------------------------------------
    

    我想保持简单,所以它还没有优化。例如,您可以用指数*=0.5替换所有*=2.0inc/dec ...如果您与float运算符/上的FPU结果进行比较,这将会有点不太精确,因为大多数FPU以80位内部格式计算,而这种实现仅在32位上。

    正如您所看到的,我正在使用FPU +,-,*。通过使用像

    这样的快速sqr算法,可以加快速度

    特别是如果你想使用大位宽......

    不要忘记实现规范化和/或溢出/下溢校正。