🏭 第5课:从 Verilog 代码到 GDS 版图——芯片是怎么"造"出来的

写完 RTL 代码只是第一步——真正的挑战是把代码变成能在硅片上"刻"出来的物理版图。rv_control 完成了从 RTL 到 20MB GDS 文件的完整流程。

🎯 本课目标

理解 ASIC 物理设计的 6 个阶段:综合→布图→布局→时钟树→布线→收尾。知道每一阶段是做什么的,理解 200MHz 是怎么达到的。

一、从代码到硅片:全流程概览

RTL-to-GDS 六阶段流水线

flowchart LR RTL["📝 RTL设计
Verilog代码
~2400行"] -->|Yosys| SYN["🔧 综合 Synthesis
代码→门级网表
8,215个逻辑门"] SYN -->|OpenROAD| FP["📐 布图 Floorplan
确定芯片大小
520×520μm"] FP -->|OpenROAD| PLACE["📍 布局 Placement
摆放23,559个单元
密度0.58"] PLACE -->|OpenROAD| CTS["🕐 时钟树 CTS
插入422个时钟Buffer
skew=0.18ns"] CTS -->|OpenROAD| ROUTE["🔗 布线 Route
连接292mm金属线
151,281个通孔
DRC=0"] ROUTE -->|OpenROAD| FINAL["✅ 收尾 Finish
GDSII(20MB)+SPEF(24MB)
Setup/Hold clean"] style RTL fill:#3987e5,color:#fff style SYN fill:#199e70,color:#fff style FP fill:#c98500,color:#000 style PLACE fill:#e66767,color:#fff style CTS fill:#9085e9,color:#fff style ROUTE fill:#d55181,color:#fff style FINAL fill:#0ca30c,color:#fff

二、第1步:综合(Synthesis)—— "翻译"

Verilog 代码是给人看的(`always @(posedge clk)`),芯片需要的是逻辑门(AND、OR、NOT、Flip-Flop)。综合就是做这个翻译工作:

if (a & b) out = 1; else out = 0; → 翻译成 → 一个 AND 门 + 一个 MUX

rv_control 用了 Yosys(开源综合工具)做这件事,结果:8,215 个标准单元(6,485 个组合逻辑 + 1,727 个触发器)。

三、第2步:布图(Floorplan)—— "划地盘"

综合完后你有一堆逻辑门,但它们堆在一起,没有物理位置。布图就是给芯片划地盘:

rv_control 初始设计用了 1500×1500µm 的大片区域,后来缩小到 520×520µm——因为 23K 单元根本用不了那么大地方。小面积意味着更短的走线、更低的功耗。

四、第3步:布局(Placement)—— "摆家具"

有了地盘,现在要把 23,559 个标准单元一个一个摆到正确的位置。目标:让有关联的门尽量靠近,减少走线长度

这就像搬新家摆家具——沙发和茶几应该放一起(经常同时用),书柜可以放远一点。

rv_control 用了 density=0.58 的密度(58% 的面积放标准单元,42% 留给走线),最终实际利用率 16.47%——故意留了很多空余空间给布线。

五、第4步:时钟树综合(CTS)—— "同步心跳"

这是最关键也最容易出错的一步。

芯片里 23,559 个单元中,1,727 个是触发器——它们都需要在同一个时钟边沿同时更新。但时钟信号从芯片一角传到另一角需要时间(物理延迟不可避免),如果不对齐,就会出现"有的触发器先收到时钟、有的后收到"——这就是时钟偏移(clock skew)。

时钟树 —— 像树枝一样分发时钟

graph TD CLK["🕐 clk_i 输入"] --> B1["Clock Buffer 1"] CLK --> B2["Clock Buffer 2"] B1 --> L1["Leaf Buffer 1.1"] B1 --> L2["Leaf Buffer 1.2"] B2 --> L3["Leaf Buffer 2.1"] B2 --> L4["Leaf Buffer 2.2"] L1 --> FF1["FF"] L2 --> FF2["FF"] L3 --> FF3["FF"] L4 --> FF4["FF"] style CLK fill:#3987e5,color:#fff

CTS 插入 422 个时钟 Buffer + 93 个时钟反相器,确保时钟信号到达所有触发器的时间差控制在 0.18ns 以内。

六、第5步:布线(Route)—— "铺路"

单元都摆好了,现在要用金属线把它们连起来。芯片有多层金属(像多层高架桥),低层走短线,高层走长线。

rv_control 的布线结果:

总线长292,447 µm(约29cm!)通孔数151,281 个DRC 错误0天线违规0

DRC(Design Rule Check)是制造规则检查——金属线不能太近(会短路)、不能太细(会断)。rv_control 的 DRC=0 说明所有线都符合 45nm 工艺的制造规则。

七、第6步:收尾(Finish)—— "交卷"

布线完成后还要做几件事才能"交卷":

✅ rv_control 最终结果(真的跑通了!)
时钟200 MHz (5.0ns)Setup TNS0(slack +1.52ns)Hold TNS0(slack +0.95ps)总功耗6.82 mWGDS 文件20,531,800 字节

八、200MHz 怎么做到的?—— 工程实战技巧

从最初的 50MHz 目标到最终实现 200MHz,中间做了大量工程优化:

频率提升的关键技巧

flowchart TB A["🎯 目标: 200MHz (5ns周期)"] --> B["1. 缩小Die面积
1500→520μm
缩短走线=减少延迟"] A --> C["2. Hold margin后置
CTS不加Hold margin
Route阶段加5ps
避免过量Buffer"] A --> D["3. 异步False Path
uart_rx/gpio设为false path
不需要收敛时序"] A --> E["4. SRAM mock/shrink
避免黑盒阻塞流程
让PD全流程跑通"] A --> F["5. Max cap放宽
set_max_cap 20.0
364 violations剩余"] B & C & D & E & F --> G["✅ 200MHz闭合!
Setup/Hold/DRC=0"] style A fill:#3987e5,color:#fff style G fill:#0ca30c,color:#fff
💡 关键教训:Hold Slack 只有 0.95ps

不到 1 皮秒(1ps = 万亿分之一秒)——这非常临界。换一个 OpenROAD 版本、换一个随机种子,可能 hold 就会失败。真正的 tapeout 前需要留更大余量(至少 10-20ps)。

九、交付物:可以真实验证的 GDS

最终交付包含完整的 SHA256 校验,任何人都可以验证:

文件大小用途
6_final.gds20.5 MBGDSII版图(KLayout可打开)
6_final.def21.2 MB设计交换格式(单元位置+连线)
6_final.v2.4 MB门级网表(综合后的Verilog)
6_final.spef24.2 MB寄生参数(电阻+电容)