【问题标题】:How to get a square root for 32 bit input in one clock cycle only?如何仅在一个时钟周期内获得 32 位输入的平方根?
【发布时间】:2016-01-07 09:50:29
【问题描述】:

我想在 Verilog 中设计一个可合成的模块,它只需要一个周期来计算给定 32 位输入的平方根。

【问题讨论】:

  • 如果你真的只想要1T 的时钟,那么我能想到的唯一方法是2^32 or 2^31 or 2^30 x 2 Byte 在某些ROM 大小2-8GB 中预先计算的LUT 表取决于有符号/无符号类型,如果你想要忽略 LSB。我知道的任何计算方法都需要很少的迭代(近似)或高达16T 进行二分搜索。如果存在不同的1T 方法,则可能是基于 PCA 并需要不同的 LUT 表。 (抱歉此评论的多次编辑...)
  • 我必须多次使用这个模块,如果我使用 LUT 表会增加开销
  • 我投票结束这个问题,因为它与 Verilog 无关
  • 整个计算需要一个时钟周期,还是通过能够平均每个周期一个结果的管道?
  • @EML 这里关于 SO 的大部分问题都与 Verilog 无关。

标签: algorithm integer verilog sqrt


【解决方案1】:

[Edit1]修复代码

最近发现即使测试确定一切正常,结果也会出现问题,因此我深入挖掘并发现我的方程式中有一个愚蠢的错误,并且由于与我的 pgm 环境的名称冲突,测试得到误报,所以我忽略了它前。现在它在所有情况下都可以正常工作。

我能想到的最好的事情(除了近似或大的LUT)是二分搜索没有乘法,这里是C++ 代码:

//---------------------------------------------------------------------------
WORD u32_sqrt(DWORD xx) // 16 T
    {
    DWORD x,m,a0,a1,i;
    const DWORD lut[16]=
        {
        //     m*m
        0x40000000,
        0x10000000,
        0x04000000,
        0x01000000,
        0x00400000,
        0x00100000,
        0x00040000,
        0x00010000,
        0x00004000,
        0x00001000,
        0x00000400,
        0x00000100,
        0x00000040,
        0x00000010,
        0x00000004,
        0x00000001,
        };
    for (x=0,a0=0,m=0x8000,i=0;m;m>>=1,i++)
        {
        a1=a0+lut[i]+(x<<(16-i));
        if (a1<=xx) { a0=a1; x|=m; }
        }
    return x;
    }
//---------------------------------------------------------------------------

标准二分查找sqrt(xx)x 的位从MSB 设置为LSB,从而得到x*x &lt;= xx 的结果。幸运的是,我们可以通过简单地将事物重写为递增被乘数来避免乘法...在每次迭代中,旧的 x*x 结果可以像这样使用:

x1 = x0+m
x1*x1 = (x0+m)*(x0+m) = (x0*x0) + (2*m*x0) + (m*m)

其中x0 是上次迭代中x 的值,x1 是实际值。 m 是实际处理位的权重。 (2*m)(m*m) 是常量,可以用作 LUT 和位移,因此无需相乘。只需要添加。遗憾的是,迭代绑定到顺序计算禁止并行化,因此结果最多为16T

代码中a0代表最后一个x*xa1代表实际迭代的x*x

如您所见,sqrt 是在 16 x (BitShiftLeft,BitShiftRight,OR,Plus,Compare) 中完成的,其中位移位和 LUT 可以硬连线。

如果与其他门相比,你有超快的门,你可以将输入时钟乘以 16 并将其用作 SQRT 模块的内部时序。类似于在旧的英特尔 CPU/MCU 中有 MC 时钟作为源 CPU 时钟的划分的旧日子......这样你可以得到1T 计时(或倍数取决于倍率)。

【讨论】:

  • 所以...您更改了标签以创建合法问题,然后在 OP 想要 Verilog 时用 C++ 回答问题?那是犹太洁食吗?也许您现在应该删除 Verilog 标记?那么 OP 对单周期解决方案的要求又如何呢——你是说 16 个周期如果足够快就可以成为一个周期?
  • @EML 如前所述,这在1T 中并没有真正回答,语言标签意味着在 Verilog (VHDL) 语言中首选回答,但这并不意味着这是严格地绑定到该语言。我也可以绘制电路而不是 C++ 代码,但我太懒了。我更改了标签,因为我认为我选择的标签比语言本身更重要,并且应该为此吸引合适的人。只有当您知道它是做什么以及如何完成时,电路部分才会完成。但是如果你把它改写成 VHDL,它会更合适,可能会得到更多的选票。
  • 感谢您的回答
  • Verilog 和 VHDL 是不同的语言,都是 HDL,但 VHDL 不代表 Verilog HDL。
  • 摩根是对的,它代表VHSIC HDL(一个首字母缩略词中的首字母缩略词,太棒了!),它代表V ery High Speed I集成C电路H硬件D描述 Language... 这很可能是有史以来最糟糕的首字母缩略词。
【解决方案2】:

有转换为对数、减半和转换回的过程。
有关如何实现“组合”logantilog 的想法,请参阅Michael Dunn's EDN article,其中显示了优先级编码器、桶形移位器和查找表,以及 System 中的三个日志变体Verilog 用于下载。
(优先编码器、桶形移位器和查找表对于“一步巴比伦/Heron/Newton/-Raphson”看起来很有希望。但这可能仍需要 128K x 9 位查找表。)

虽然没有“verilog”,但
Tole Sutikno: "An Optimized Square Root Algorithm for Implementation in FPGA Hardware" 显示了修改后的(二进制)逐位算法的组合实现。

【讨论】:

  • log 方法听起来很有希望(+1),但很难说它是否比我的更快,因为仍有迭代 + LUT 在硬件上尝试并比较会很好。 .
【解决方案3】:

2018 年,T. Bagala、A. Fibich、M. Hagara, P. Kubinec、O. Ondráček、V. Štofanik 和 R. Stojanović 撰写了基于二项式级数的单时钟平方根算法及其 FPGA 实现

本地振荡器以 50MHz 运行 [... 对于 16 位输入尾数,] [硬件] 实验的值与模拟的值相同 [...] 获得的延迟平均值分别为 892 ps 和 906 ps。

(没有说明 50MHz.9ns 之间的差异或引用的 ps 分辨率和使用 10Gsps 范围。如果它大约是 18 个周期(由于流水线而不是循环?)/~900*ns*,单时钟平方根... 的解释保持开放 - 可能是 1每个周期的结果。)
该论文接下来没有披露有关二项式级数评估的详细信息。
虽然方程式也以一般形式呈现,但我的猜测是,更多位所需的硬件数量很快就会变得令人望而却步。

【讨论】:

  • 刚刚发现您的新帖子 +1 可能 0.9ps 只是输入和输出之间的传播延迟......而 50MHz 只是他们使用的时钟,即使限制更高(或有限)通过噪音和高频问题而不是门速度)你有任何链接吗?甚至可能是我认为捷克语的名字的原始语言......很想读它......
  • 我在semanticscholar找到它;目前不知道我在哪里看到的内容 - 我可能已经访问了我的母校。
  • 我早些时候发现了这一点:ieeexplore.ieee.org/document/8406022 但我的同事没有访问权限,他刚刚将论文发回给我,它看起来比你的要好得多(从可理解性的角度来看)看起来他们是利用:sqrt(1+x) = 1 + (x^1)/2 - (x^2)/8 + (x^3)/16 - 5*(x^4)/128 + .... 以及已经计算的子结果数字没有改变的事实......
【解决方案4】:

我得到了代码 在这里

    module sqrt(
input[31:0]a,
output[15:0]out
    );
reg [31:0]temp;
reg[14:0]x;

always@(a)
begin
if(a<257)x=4;
if(a>256 && a<65537)x=80;
if(a>65536 && a<16777217)x=1000;
if(a>16777216 && a<=4294967295)x=20000;
temp=(x+(a/x))/2;
temp=(temp+(a/temp))/2;
temp=(temp+(a/temp))/2;
temp=(temp+(a/temp))/2;
temp=(temp+(a/temp))/2;
temp=(temp+(a/temp))/2;
temp=(temp+(a/temp))/2;
end

assign out=temp;
endmodule

【讨论】:

  • 任务 sqrt;输入[31:0]a;输出[15:0]输出;注册 [31:0] 温度,温度 1,温度 2,温度 3,温度 4,温度 5,温度 6;注册 [14:0] x;如果(a256 && a65536 && a16777216 && a
  • 此评论不是解释。如果您想发布更新的解决方案,只需点击 this 答案下方的 edit
  • 不使用七次除法运算 (a/temp) 使这个实现几乎无法使用?我的理解是,整数 sqrt 和整数 div 的真正硬件实现是使用类似的算法实现的(从最高一位到最低一位的结果中测试位),并且应该具有相似的延迟。
  • 正确缩进你的代码,并编辑它以添加解释。 cmets 中的代码应放在反引号 `likethis` 之间以使其可读
【解决方案5】:

在硬件中执行此操作的常用方法是使用CORDIC。一般实现允许计算各种超越函数(cos/sin/tan)和...平方根,具体取决于您如何初始化和操作 CORDIC。

这是一种迭代算法,因此要在一个循环中完成,您可以将循环展开为所需精度所需的尽可能多的迭代,并将实例链接在一起。

特别是如果你在矢量模式下操作 CORDIC,用 [x, 0] 初始化它并旋转 45 度,[x', y'] 最终输出将是一个乘法常数。即 sqrt(x) = x' * sqrt(2) * K

【讨论】:

  • one clock cycle only?怎么样
  • 如果你展开循环,正如我所说的那样,它是一个循环。因此,如果您有一个对算法进行一次迭代的块,您可以简单地将输入链接到输出(stage->stage->stage->....)。每个阶段都以可预测的方式接近答案,因此您只需根据所需的精度要求拥有尽可能多的阶段。
  • 造成可怕的长延迟和慢时钟,因为所有其他迭代算法都转换为组合电路。 (恰好一个迭代有一种例外。)
  • 嗯,好的。问题是如何在一个周期内计算 sqrt。这里的所有答案都是 a) 迭代或 b) 使用分隔符。平方根是硬件中的重要数学运算。您可以选择小型(上面提供的选项)或快速(大型预计算表)。你不能两者兼得。
猜你喜欢
  • 2017-02-23
  • 2012-10-24
  • 1970-01-01
  • 2018-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多