板子上有两颗大芯片:一颗 Zynq UltraScale+ RFSoC,旁边一颗 Kintex UltraScale FPGA,中间用八条 高速串行线连起来。哪一颗单独都不够大,装不下我们想跑的全部东西。于是计划是:把第二颗芯片变成 第一颗的”扩展仓”——一处放额外逻辑去跑的地方,加上它旁边那块内存,全都跨链路够得着。整个过程 由 AI 在 Python2Verilog 框架下完成,我只在几个关键节点给方向。
这一篇讲的是,第二颗芯片怎么成了第一颗的算力和内存,以及中间那一段——每一次仿真都通过,硬件 却依旧一动不动。
两颗芯片,一条接缝
两颗芯片之间,只有那八条串行线。第一颗芯片想从第二颗那里得到的任何东西——放一个模块过去跑、 从远端内存里读一个值——都得走这条接缝、再回来。于是整个设计归结成一个问题:这条接缝诚实吗? 如果一个字节出去、回来变了样,建在它上面的一切就都不能信。
这条链路是我们自己写的,刻意做得很薄。板子上厂商自带的那个链路模块没能用起来,于是我们没去靠 一套沉重的协议,而是自己做了一条,让它保持简单。它传的是一帧帧带边界的消息,而每条消息最前面 的那一小段,就说明了它是什么。有的是内存请求:读或写第二颗芯片的内存。有的是控制消息,甚至能 跨着这条链路把远端那颗芯片重启,不用去够任何一根线。还有的就只是给解码器的原始数据——根本没有 一条单独的”开始计算”命令,因为解码器就坐在通路里,一条消息到了,它本身就是解码器的输入。链路 薄,每条消息最前面的那一小段决定接下来发生什么。
我们只用一种真正算数的方式来定这件事:在真实硬件上,对着基准,逐字节比。不是一个自己跟自己自 洽的仿真,而是真芯片,对着一个已知正确的答案核。
三样活,每一样都在真实硬件上核过
到最后,第二颗芯片替第一颗干着三样不同的活,而每一样都用同一种方式验过——在板子上,不是在仿 真里,对着一个已知答案:
- 一次数据往返。数据跨链路发到第二颗芯片,再回来。逐字节相符。
- 一个解码器放在远端跑。解码器坐在第二颗芯片上,软信息跨链路进去,算完,硬判决交回来。它和基 准模型比对零错误——而且中间不复位,连着来两次仍然零错,证明它能自己重新就绪。
- 远程内存,读回来。一个值写进远端内存再读回来,和写进去的一模一样,换一个深地址再来一遍,还 是一样。
这就是这里”扩展仓”的意思。第二颗芯片是一条管道、一个算力节点、一块内存,而这三样都可信,靠的 是同一个道理:每一样都在硬件本身上对着基准核过,不是在一个可能悄悄跟着错误一起自洽的模型上。
所有东西都在骗人的那一段
走到这一步并不顺,而中间这一段值得讲,因为那教训比这块板子活得更久。
链路有一个方向死活不通。接收端盯着一串合法的标记,连续看到足够多,就宣布自己锁定了。一个方向 立刻就锁上了,另一个方向永远锁不上。
于是就查,每一步都有干净的结果。厂商自带的例程在仿真里锁定了。自环仿真一帧数据逐位精确。按真 实接线连好的跨芯片仿真,两个方向都锁定,逐字节精确。把好设计和坏设计做布线后对比,逐引脚完全 相同。物理信号干净得挑不出毛病。电路每一层都查过、都清白,硬件却还是老样子。
这些检查里的每一个,都是一个统计出来的数字——一个锁定、一个通过、一个百分比——而统计把信号本 身是什么给藏了起来。所以 AI 趁链路声称锁定的时候,把导线上连续几个原始字直接抓回来。抓回来是 一个干干净净的循环图案:一个裸时钟,一个方波,上面什么数据都没驮。
让这件事如此以假乱真的,是一个又小又刁的巧合。切成帧之后,一个纯时钟图案,每一帧那两位同步头 都落在合法值上。于是它满足锁定、满足眼图、满足合法率——同时不带一丁点信息。一个毫无意义的循环 图案,冒充着一条健康的链路。
既然导线上跑的是自由振荡的时钟,那就说明没有人在驱动它。于是 AI 回头把通道接线直接量了一遍, 只认两颗芯片上印着的物理名字,不认工具内部的序号,往每条通道上发一个不一样的编码标记,好把它 们区分开。结果,和所有人一直信着的那份记录,正好相反。
陷阱就在这里。发送端确实在发正确的数据——只是发给了一条没人在听的通道。而所有人盯着的那个输 入,连着一条从未配置的通道,于是自己振荡起来。每一次仿真都照着同一份记录连线。记录内部自洽, 所以每一次仿真都复现了同一个错误连接,然后通过。一层又一层的验证彼此一致,是因为它们都读了同 一个共享的错误假设。
还有个扎心的细节:那份记录甚至带着一句”已上硅验证”的注记。注记是真的,但它背后的”验证”只是第 一条通道的巧合锁定。答案其实一直画在原理图上。把设计指向正确的两条通道,链路一次就通,逐字节 精确。
而且,这个反接不只是一份记录写反了。它是刻在板子本身里的:两颗芯片之间的那些通道,在铜线上就 是反着连的,改不了。这才是厂商那个现成链路模块在这块板子上根本走不通的真正原因。它非要两颗芯 片先对上通道的顺序,才肯把这些通道合到一起,而铜线上的顺序既然是反的,它就永远锁不上。这正是 我们一开始就自己写一条薄链路的原因:它压根不用那种方式去合并通道,所以通道顺序反没反,它根本 不在乎。
留下什么
当电路每一层都查过、都清白,硬件却依旧一动不动,剩下该怀疑的,就是测量本身。那张反的记录能活 过一整天,只有一个原因:每一次仿真都在老老实实照着它连线。电路从没骗人,骗人的是那张地图。
信硬件,别信那张描述它的文件。用物理名字去读接线,别信表里的序号。而且,别说一条链路通过了, 除非你从它上面解出真实载荷、逐字节核对过——锁定、眼图、一个读作合格的指标,都能被一个什么也不 带的信号满足。
接缝诚实了,剩下的就顺了,第二颗芯片就位,成了第一颗的算力和内存:一块板子,两颗 FPGA,还有一 条你真能信得过的链路。
备注
- 硬件:一块定制板,把一颗 AMD Zynq UltraScale+ RFSoC(XCZU67DR)和一颗 AMD Kintex UltraScale FPGA(XCKU115)配在一起,用八条高速串行链路相连。
- 链路层、远程算力与远程内存的逻辑,以及全部排查,都由 AI 在 Python2Verilog 框架下完成。文中提 到的一切,都是在板子上读到的,或在同一次会话里对着基准核对过的。
- 本文接续《一天,把 Linux 跑上一块 RFSoC 板子》。