Digital Bridge工程小结
Digital-Bridge工程小结
自今年3月底,我全程高强度参与了 EPFL 脑机接口(Brain-Computer Interface, BCI)芯片的设计与开发工作。如今,项目已顺利收官。这是迄今为止我所参与过规模最大、复杂度最高的一次芯片流片任务,过程中经历了诸多挑战与反复。借此机会,我将这段宝贵的经验做一次系统性总结,以便今后进行自我复盘。
首先贴出该芯片的一些spec:
- 芯片面积为 $3.1\times3.2 mm^2$,其中数字部分占 $1.2\times3.2 mm^2$ ,包含定制的AFE(Analog Front End),(CIM)Compute-In-Memory模块以及台积电提供的Low power low leakage SRAM单元。
- 数字部分采用Digital-as-a-top流程进行设计,将各个CIM模块建模为包含时序信息的reference model,然后与其他模块进行联合仿真。
- 片上包含一个End-to-end的神经网络,用于脑皮层电图(ECoG)信号的分析。
- 加入了用于可测性的Scan chain,可以对神经网络的每一层单独进行调试。
参数化设计
三月中旬收到了消息得知最后的dealine是4月30日。当时隐约感到时间极其地紧迫,而整个RTL代码设计还停留在只有顶层模块和控制信号的阶段。由于算法那里的模型架构以及量化工作迟迟没能确定下来。我们直接选择了对神经网络的RTL实现进行参数化设计,以便后面调整起来方便。在进行综合之前,所有的模型参数都被存在一个名为NNparam.vh的文件中。综合前需要将所有的NNparam.vh的参数复制到顶层,否则会出现无法综合的问题,这里要确保顶层的参数定义与NNparam.vh的参数保持一致。
冗余操作在工作副本上执行
在github上进行工程版本管理时,由于git本身存在上传与下载文件大小的限制,可以创建两个文件夹,一个保存重要的代码以及script脚本文件,用于上传github便于版本同步。另一个则用于运行内存占用较大的操作,比如仿真产生的波形文件。一开始我们将Vivado上的工程文件全部上传做版本管理,但由于工程在不同组员之间互相分享时,各自运行会产生许许多多的临时文件,这对每次的push和pull造成了大量冗余数据传输。所以我们复制了原本的Vivado Project,在第二个Vivado Project全部引用第一个Vivado Project的文件,但仿真与debug全部在第二个Vivado Project里操作,而git pull与push在第一个Vivado Project里操作,这样每个组员可以在本地跑各自的仿真同时不会打乱原有的工程结构。这也符合最小化冗余的工程管理思想,将主干工程与工作副本相分离,既避免了干扰,使得代码管理更加清晰可靠,也不会因为个人调试操作污染主项目,引发无意义的版本冲突。
保持清晰的代码设计风格
代码不能写的太乱。大的system做出来了之后,很容易忘记之前实现的细节,应有一条脉络清晰的主线。
验证中发现的问题
shift溢出问题
神经网络的量化采用的最经典的Integer-Arithmatic only的方案,这意味着在每一次矩阵乘法运算之后,结果都要乘以一个M值之后进行右移。一开始的代码中有许多变量没有给够位宽,导致乘以M值之后发生溢出。在进行数值验证的过程中我们一直在解决代码中存在的溢出问题。第一天完成了前10个样本的验证,但在第12个样本处出现错误。第二天修复了第12个,但又在第13个出错,随后又陆续在第16和第19个样本中发现问题。经过一周的调试,前期仿真中存在的问题基本得到解决。在全部90个样本中,最终仅有3个样本的计算结果存在极其微小的数值差异,但所有样本的分类结果均与算法输出完全一致。所有的模块在运算完之后要回到初始的状态。
- CIM reference model写入的时候注意WBL与WWL之间的竞争冒险问题。在建模的时候,reference model中的时序信息(delay等)尽可能符合macro在cadence里的仿真。当建模很困难的时候,考虑加入一些处理output的数字电路,形成一个整体的macro,这样只需要建模最后数字信号的输出delay即可。比如sampling ADC的output缺乏同步的时钟信号,我们可以用数字控制模块产生的sampling pulse来作为同步的时钟写入时序信息。
- 综合后仿真不需要读入.sdf,因为在CTS之前不存在时钟树,clock gating产生的时钟信号会直接消失掉。
- Timing Constraint一定要尽可能完善,一定要根据当前的process去写。28,65。第一版综合出来的accumulator出现了严重的signal integrity的问题,主要原因是65nm LPHVT库clock buffer的驱动能力不够,后续添加了input driver的约束后成功修复了这个问题。
对于clock transition time的约束。因为一开始代码中的一些小问题导致我们误以为65nm LPHVT的工艺下,只要系统一做大,transition time就会变得非常高。一开始我用我从28nm HPC+工艺下的时序约束做综合,28nm下的时序约束,发现总是会爆出大量的transition time slack的问题(约有3ns左右,远远超出了设定的100ps的约束),这一度让我们以为是65nm LPHVT的库不适合做大系统。中间我还尝试把时钟约束中的transition time调为了5ns(后来发现这其实是掩耳盗铃的解法)。后面才发现,问题主要出在系统在设计的时候存在许多fanout非常大的net,
在工程的早期,我们可能对电路中一些具体的时序进行确定,一般而言这是一个trial&error的过程,一边进行用VCS在数字域进行仿真的同时,另一边用AMS在模拟域进行仿真。这里举一个例子,左图是这里对SRAM cell写的reference model,第四个时序代表着,当Write Word Line置高之后,它必须保持至少80ns才能让数据成功被写入进memory里去。我记得这一次流片,我们后仿出现的最后一个问题就是在这里,解决了它才让我们成功通过全部的后仿真
验证结构
End-to-end python与systemVerilog联合验证。
Jupyter Note以及assert方法。
用if语句控制开关assert。
设计时中提前考虑潜在的fanout与criticle path问题。
Verilog代码风格对综合后的netlist有很大的影响。
状态机的三段式写法。
同步reset与异步reset不能混用。
Designware与台积电的SRAM要单独例化在一个inst module里面。
有一个关键的计数器信号在reset的时候会复位到另一个寄存器上,又因为当时使用的是同步复位信号,导致复位时两个寄存器的datapath不存在任何延迟,从而爆出大量hold slack的问题。
综合之后一定要尽可能清掉所有的Spyglass warning。