通过固定常量执行整数硬件除法的最快方法是什么?

时间:2014-07-16 16:19:14

标签: algorithm division system-verilog

我有一个16位数,我想要除以100.假设它是50000.目标是获得500.但是,我试图避免推断我的FPGA上的分频器,因为它们打破了时序要求。结果不一定准确;近似值可以。

我尝试过硬件乘法0.01但不支持实数。我现在正在看流水线分频器,但我希望它不会出现这种情况。

4 个答案:

答案 0 :(得分:12)

概念上:乘以655(= 65536/100)然后向右移16位。当然,在硬件方面,右移是免费的。

如果你需要它更快,你可以将除法线作为两个幂(移位)的除法之和。例如,

1/100 ~= 1/128                  = 0.0078125
1/100 ~= 1/128 + 1/256          = 0.01171875
1/100 ~= 1/128 + 1/512          = 0.009765625
1/100 ~= 1/128 + 1/512 + 1/2048 = 0.01025390625
1/100 ~= 1/128 + 1/512 + 1/4096 = 0.010009765625
etc.

在C代码中,上面的最后一个例子是:

uint16_t divideBy100 (uint16_t input)
{
    return (input >> 7) + (input >> 9) + (input >> 12);
}

答案 1 :(得分:4)

假设

  • 整数除法旨在截断,而不是舍入(例如599 / 100 = 5)
  • 可以在FPGA中使用16x16乘法器(固定值为on 一个输入)

然后您可以通过实现16x16无符号乘法器来获得精确值,其中一个输入为0xA3D7,另一个输入为您的16位数。将0x8000添加到32位乘积,结果为高10位。

在C代码中,算法看起来像这样

uint16_t divideBy100( uint16_t input )
{
    uint32_t temp;

    temp = input;
    temp *= 0xA3D7;     // compute the 32-bit product of two 16-bit unsigned numbers
    temp += 0x8000;     // adjust the 32-bit product since 0xA3D7 is actually a little low
    temp >>= 22;        // the upper 10-bits are the answer

    return( (uint16_t)temp );
}

答案 2 :(得分:1)

通常,您可以乘以逆和移位。编译器一直这样做,即使对于软件也是如此 这是一个为您执行此操作的页面:http://www.hackersdelight.org/magic.htm
在你的情况下,似乎乘以0x431BDE83,然后右移17。

这是一个解释:Computing the Multiplicative Inverse for Optimizing Integer Division

答案 3 :(得分:0)

乘以倒数通常是一种很好的方法,正如您所指出的那样,虽然不支持实数。您需要使用固定点而不是浮点数。

Verilog没有定点的定义,但它只是使用字长,你可以决定有多少位是整数,有多少是小数。

二进制的

0.01(0.0098876953125)为0_0000001010001。这个字长越大,精度越高。

// 1Int, 13Frac
wire ONE_HUNDREDTH = 14'b0_0000001010001 ; 

input  a         [15:0];    //Integer (no fractional bits)
output result [15+14:0];    //13 fractional bits inherited form ONE_HUNDREDTH
output result_int [15:0];   //Integer result

always @* begin
  result     = ONE_HUNDREDTH * a;
  result_int = result >>> 13;
end

使用ruby gem fixed_point进行实数到二进制转换。

ruby​​ irb会话(通过gem install fixed_point安装了fixed_point):

require 'fixed_point'
#Unsigned, 1 Integer bit, 13 fractional bits
format  = FixedPoint::Format.new(0, 1, 13)
fix_num = FixedPoint::Number.new(0.01, format )
 => 0.0098876953125
fix_num.to_b
 => "0.0000001010001"