我试图了解FPGA如何做SHA1散列。
作为参考,SHA1涉及进行一系列32位整数计算,以80“步”排列;这里是算法中间的4个代表性步骤,在C:
中x0 = rol(x13 ^ x8 ^ x2 ^ x0, 1);
e += rol(a,5) + (b^c^d) + x0 + 0x6ED9EBA1L;
b = rol(b,30);
x1 = rol(x14 ^ x9 ^ x3 ^ x1, 1);
c += rol(d,5) + (e^a^b) + x1 + 0x6ED9EBA1L;
e = rol(e,30);
x2 = rol(x13 ^ x10 ^ x4 ^ x2, 1);
b += rol(c,5) + (d^e^a) + x2 + 0x6ED9EBA1L;
d = rol(d,30);
x3 = rol(x13 ^ x11 ^ x5 ^ x3, 1)
a += rol(b,5) + (c^d^e) + x3 + 0x6ED9EBA1L;
c = rol(c,30);
总共有21个内部32位变量,并且算法一直将它们相互馈送。 'rol'随着旋转而移位(将位从一端移出另一端并进入另一端。)
现在,在我看来,计算x13 ^ x11 ^ x5 ^ x3需要32个LUT,c ^ d ^ e需要另外32个LUT,我不清楚如何计算添加所需的资源,但我猜的是96或128 LUT。轮换和分配通过互连完成。所以,让我们说192个LUT总计,80步,加上一些开销。完全展开后,我预计会有大约16,000个LUT,每个时钟周期的吞吐量为1个输入模块,延迟时间为80个时钟周期。 Xilinx Artix-7 XC7A50T包含8150个切片,每个切片有4个LUT,因此每个时钟周期的吞吐量为2个块,或者300 MHz时为600 Mhash / s(因为每个块为512位,所以300 Gbps)。这是合理的估计还是我离开了?
我无法找到任何完全展开的SHA1实现的引用,但这些人https://www.heliontech.com/fast_hash.htm声称具有828个LUT的“非常高性能”实现,每82个时钟周期吞吐量为1个块,因此,在XC7A50T上接近70 Gbps。这个数字是如此低得多,因为它们没有展开?
答案 0 :(得分:4)
现在,在我看来,计算
x13 ^ x11 ^ x5 ^ x3
需要32个LUT,c^d^e
需要另外32个LUT,我不清楚如何计算添加所需的资源,但我猜猜96或128个LUT。
如果XOR和加法都是独立的,那将是真的 - 但事实并非如此。 7系列FPGA上的每个LUT最多可以输入6个输入,因此合成器可以将一些XOR吸收到附加链中。
总而言之,路由和布局将是您最大的障碍。为了利用进位链,宽加法器中的所有位必须“垂直”布局。这导致管道自然地从左向右流动,但我怀疑XC7A50T有足够的列来适应单行中的整个管道。路由资源将是限制因素,而不是LUT。
答案 1 :(得分:2)
好的,我现在可以回答我自己的问题了。我已经设法在Verilog中组合了一个有效的SHA1实现。
https://github.com/ekuznetsov139/fpga
这实际上是一个WPA2 PMK生成器,而不仅仅是SHA1(SHA1在相同数据上循环执行8192次。)
我不会声称它是完美优化的,甚至没有特别好的编码 - 我已经在过去两周内在其他项目之间了解了我对Verilog的所有了解,并且有一半的时间用于获取数据通过PCI-Express与多个核心实例进行编组。但是我让它在模拟器中正常运行并且在实际的FPGA上成功运行,并且性能数据接近我原来的预测。有了Cyclone V目标,我一直看到每个核心大约有7,000个ALM,每个核心每个时钟点都能做一个哈希。一个ALM基本上是2个LUT(1个大或2个小)加上一些进位加法器硬件。那么,14,000个LUT。对于快速硅,Fmax似乎约为300 MHz,而对于慢速硅则更接近150 MHz。
我最初的估计中没有考虑的一件事是内部状态需要大量内存。 21个32位变量乘以80步是53760位,并且每个ALM有4个寄存器,仅此一个就需要比所有计算更多的资源。但编译器能够将大部分内容打包到内存单元中,即使我没有明确指示它。
但是,路由/布局是一个相当大的问题。我的芯片有113K ALM(301K LE)。我能够融入其中的最多是5份。利用率低于40%。这需要约8个小时的拟合。试着弄乱LogicLock看看我能不能做得更好。在300 MHz下同时运行5个副本,吞吐量为1.5 Ghash / s SHA1或90 Khash / s WPA2。这比我希望的要少一些(大约是GeForce 980 Ti吞吐量的1/3)。但至少能源效率要好得多。
编辑:看看Quartus标准版中的Design Partition Planner就会发现问题所在。编译器太聪明了,它正在合并每个内核的内部存储阵列,从而在内核之间创建了大量不必要的互连。即使没有完整的LogicLock,只需使用"允许移位寄存器跨层次结构合并"设置为" off",我已经成功完成了10个副本。让我们看看我能做到12 ...