芯片设计全流程解析:从RTL到流片的工程实践

发布时间:2026/8/29 8:39:06
芯片设计全流程解析:从RTL到流片的工程实践 如果有一天你的团队放出这句话——“我们造了一颗芯片而且它很快”别急着羡慕。“快”这个字在芯片项目里至少有三层含义芯片运行起来性能很快从立项到流片的设计迭代很快以及系统上电后启动和对外响应很快。这三层含义对应的是完全不同的技术栈却经常被放在同一句话里。这篇文章想帮你拆清楚当一个人说“我们造了一颗芯片”时他背后到底经历了什么。尤其是这两年从公开报道里可以看到 OpenAI 之类的大公司也在用更短的时间推进自研芯片而国内的 RK3588、STM32、ESP32 等芯片生态又非常成熟很多嵌入式工程师会误以为“造芯片”和“写单片机程序”差不多。实际上一颗芯片从 RTL 代码到实物中间隔着远比你想象中更长的流程。如果你正准备接触 SoC 设计、芯片后端、芯片测试或者只是好奇一块开发板上的主控芯片是怎么被“造”出来的这篇文章可以给你一张完整的地图。文章会以一个典型的边缘 AI SoC 为例从需求定义、RTL 设计、仿真验证、逻辑综合、物理实现、封装测试到启动流程全部走一遍并且给出可以直接参考的代码和命令。1. 这篇文章真正要解决的问题先泼一盆冷水芯片研发不是“写代码然后点编译”这么简单。普通嵌入式开发中你写好一个 STM32 工程编译下载如果没跑起来大概率是逻辑写错了或者时钟配置不对。但在芯片项目里一旦进入流片阶段任何 Bug 都无法像软件一样热修复。一次流片要付出的成本从几十万美元到上千万美元不等而且制造周期以月为单位。所以“快”必须建立在“正确”和“可验证”的基础上。这篇文章要解决的核心问题有三个概念问题一颗芯片从无到有到底要经过哪些阶段前端、后端、验证、测试这些岗位分别负责什么实操问题如果你想在一个教学项目或公司预研项目里把一颗简单的 SoC 立起来最小可用的工具链和流程是什么工程问题为什么有的团队造芯快有的团队造芯慢“快”的杠杆来自哪里不要急着把“造芯片”理解成一件只有巨头才能做的事。现在公开可用的 EDA 工具、开源 IP、FPGA 原型平台已经很成熟一个 10 人以内的小团队也完全有能力在有限时间内完成一颗低复杂度 SoC 的设计和验证。难的不是“能不能做”而是“你能不能对每一行 RTL、每一个时序约束、每一项测试负责到底”。2. 芯片项目里的“快”到底指什么很多人一听到“芯片快”第一反应是主频高。比如 RK3588 能做到多核高性能是因为采用了先进工艺和复杂的微架构。但你真正关心“造一颗芯片”的时候主频只是最后呈现在参数表里的一个数字它代表的是后端物理设计是否收敛、时序是否满足约束、芯片能否在目标频率下稳定跑完软件。这里把“快”拆成三个层面2.1 运行性能快这是用户能感知的“快”。CPU 主频、AI 算力、内存带宽、总线上数据搬运速度都属这一类。运行性能快通常不是某一个模块的功劳而是架构、时钟树、存储层次、接口协议共同配合的结果。比如一颗边缘 SoC 要跑神经网络推理光靠 CPU 肯定不够需要加一个 NPU 加速器并且保证数据能在 DDR 和 NPU 之间快速流转。2.2 设计迭代快这是团队内部最关心的“快”。从写 RTL 到仿真通过再到 FPGA 原型验证是否能快速找到 Bug每次综合、布局布线是否能在数小时或一天内完成这取决于验证环境是否自动化、IP 是否可复用、EDA 工具链是否顺畅。很多造芯团队真正的瓶颈不在流片而在验证收敛太慢。2.3 系统启动快芯片出厂后还要经过固件启动流程才能跑 Linux 或 RTOS。SoC 启动涉及 BootROM、SPL、U-Boot、内核镜像加载等多个阶段。如果启动代码有问题芯片本身没问题也会表现为“板子起不来”。所以在芯片开发阶段要提前把启动流程在 FPGA 原型上跑通否则等芯片回来再调启动时间成本会非常高。理解了这三层“快”你再看这篇文章的标题就会知道它不是一句简单的技术炫耀而是一个系统工程的结果。3. 从需求到 RTL一颗芯片的起点造芯片的第一步不是写代码而是确定“这颗芯片用来干什么”。即使是一颗很小的 MCU也需要明确 CPU 内核、外设接口、存储控制器、时钟方案、功耗目标。对 SoC 来说通常还会划分出 CPU 域、加速器域和 IO 域。在一个典型的边缘 AI SoC 项目里系统架构可能长这样一个 RISC-V CPU 核心负责运行应用逻辑。一个 NPU 加速器负责卷积、池化等算子。一条 AXI 总线把 CPU、NPU、DDR 控制器和 DMA 连起来。若干低速外设如 UART、SPI、I2C、GPIO。中断控制器、定时器、看门狗。这些模块里CPU 核通常会选用开源或商业 IP比如 RISC-V 内核总线也可以直接复用 AXI IP自己需要重点设计的是加速器、外设控制器以及整个系统的地址映射和通信协议。也就是说造一颗 SoC不是从“晶体管”开始画而是从“搭积木”开始。RTL 设计是这一切的核心。下面用一个简单的任务队列模块作为示例。这个模块在 SoC 中很常见CPU 往队列里下发任务加速器从队列里取任务队列还要能上报当前深度。// 文件路径rtl/task_queue.sv module task_queue #( parameter DEPTH 4, parameter DATA_WIDTH 32 )( input logic clk, input logic rst_n, // 写入侧 input logic push_valid, input logic [DATA_WIDTH-1:0] push_data, // 读取侧 output logic pop_valid, output logic [DATA_WIDTH-1:0] pop_data, // 状态 output logic [$clog2(DEPTH):0] count ); logic [DATA_WIDTH-1:0] mem [DEPTH]; logic [$clog2(DEPTH)-1:0] wr_ptr, rd_ptr; logic [$clog2(DEPTH):0] cnt; assign count cnt; assign pop_valid (cnt ! 0); assign pop_data mem[rd_ptr]; always_ff (posedge clk or negedge rst_n) begin if (!rst_n) begin wr_ptr 0; rd_ptr 0; cnt 0; end else begin case ({push_valid (cnt ! DEPTH), pop_valid}) 2b10: begin mem[wr_ptr] push_data; wr_ptr wr_ptr 1; cnt cnt 1; end 2b01: begin rd_ptr rd_ptr 1; cnt cnt - 1; end 2b11: begin mem[wr_ptr] push_data; wr_ptr wr_ptr 1; rd_ptr rd_ptr 1; end default: ; endcase end end endmodule这段代码只做一件事实现一个环形任务队列支持同时写入和读出。要注意的是push_valid (cnt ! DEPTH)保证队列满时不会覆盖pop_valid保证队列空时不会读空数据。这样的模块在 SoC 里往往只是一个小角色但它的状态判断正确性会直接影响加速器会不会跑飞。RTL 设计阶段最重要的纪律是“可综合性”。你在 SystemVerilog 里写的每条语句最终都要被综合成门级网表。因此不能随手写#5延时也不能写只能在仿真库里使用的任务和函数。写代码之前先想好每个模块的边界和时序比写完之后再改要快得多。4. 验证为什么芯片流片前必须“抠细节”芯片后端和制造之前最耗时、也最影响“快”的环节是验证。业内有个常被引用的估算一个 SoC 项目里验证工程师和设计工程师的比例往往接近 2:1 甚至更高。因为流片后修复一个逻辑 Bug 的成本可能是流片前修复成本的几十倍。验证不是“随便跑几个测试看看结果对不对”而是要回答三个问题功能对不对在正常输入下输出是否符合 spec边界对不对队列满、队列空、时钟域交叉、复位释放等边界场景是否安全覆盖率够不够所有分支、状态、条件是否都被测试到了通用的验证手段有两种动态仿真和 FPGA 原型验证。动态仿真就是在仿真工具里给 RTL 代码加激励观察波形和输出。最简单的流程是写一个 testbench用 Icarus Verilog 或 Verilator 跑回归。以刚才的 task_queue 为例仿真命令看起来像这样# 安装 Icarus Verilog 之后在项目根目录执行 iverilog -g2012 -o tb_task_queue \ rtl/task_queue.sv \ testbench/task_queue_tb.sv # 运行仿真 vvp tb_task_queue如果需要更快的仿真性能可以使用 Verilator 把 SystemVerilog 转换成 C 模型。这种方式的回归速度比传统事件驱动仿真快很多适合做大规模验证和跑软件测试环境。verilator --cc --exe --build -j 4 \ -Wall \ rtl/task_queue.sv \ testbench/task_queue_tb.cpp \ --top-module task_queue ./obj_dir/Vtask_queue仿真通过并不代表芯片一定没问题。仿真速度太慢无法运行完整的 Linux 启动或复杂的 AI 推理程序所以还要做 FPGA 原型验证。FPGA 原型验证的基本思路是把 RTL 放到 FPGA 上跑用真实的外设、真实的软件去验证系统功能。很多团队在流片前会用 FPGA 开发板提前验证 SoC 启动流程因为等芯片回来再发现启动路径有 bug时间损失是不可接受的。验证环节有一个常见误区认为测试跑得越多越好。实际上更重要的是“覆盖率驱动的验证”。你需要统计行覆盖率、分支覆盖率、条件覆盖率、有限状态机覆盖率。覆盖率不到 90% 以上就贸然流片风险很高。合理做法是在每个里程碑跑一次回归加覆盖率统计收敛后才进入下一阶段。5. 从综合到后端知道你的代码变成了什么很多写 RTL 的工程师对“综合”有误解以为综合就是“把 SystemVerilog 转成标准单元网表剩下交给工具”。实际上综合只是起点后面还有布局、时钟树综合、布线、时序收敛、功耗分析、信号完整性分析。先解释几个概念标准单元也就是芯片里的逻辑门比如与非门、触发器。综合工具会把 RTL 映射到标准单元库。综合Synthesis把 RTL 转换成门级网表同时做逻辑优化、面积优化和时序优化。布局布线Place Route把门级网表放到芯片版图上确定每个单元的位置并连接金属连线。时钟树综合CTS把时钟信号分发到所有触发器尽量减小时钟偏移。芯片后端指的是从综合网表到 GDSII 版图的物理设计过程。这一步和软件“后端开发”完全不是一个概念目录一看到“芯片后端”四个字应该先意识到这是物理设计范畴。时序约束是连接前端和后端的桥梁。你和后端团队沟通时最核心的文件是 SDCSynopsys Design Constraints。比如你希望 task_queue 模块工作时钟是 100MHz就要在 SDC 里定义时钟# 文件路径constr/task_queue.sdc create_clock -name clk -period 10.0 [get_ports clk] set_clock_uncertainty 0.200 [get_clocks clk] set_input_delay -max 1.0 -clock clk [get_ports push_data] set_output_delay -max 1.0 -clock clk [get_ports pop_data]create_clock告诉综合工具时钟频率是 100MHz也就是 10ns 周期。set_input_delay和set_output_delay描述外部逻辑相对时钟的时序边界。如果你忘了写 SDC工具会默认认为外部信号在时钟沿完全对齐这样的约束收紧或放松都会导致后端时序收敛困难。后端物理设计里最让人头疼的问题往往是“时序违例”。明明 RTL 仿真都通过了综合后频率却跑不上去。原因可能来自组合逻辑路径太长、触发器扇出过大、布线拥塞、甚至时钟树偏移。这时候有两种选择优化 RTL或者调整约束。比如在关键路径上加流水线寄存器或者在综合工具里设置更严格的逻辑优化选项。还需要注意SDC 约束不是越紧越好。如果一个根本不存在的 1ns 时钟约束被写进去工具会努力布一个实际上无法满足的时钟树导致面积和功耗严重增加。合理做法是先从架构上预估各模块的目标频率然后把约束交给综合工具在时序报告确认违规路径后再决定是改 RTL 还是改约束。6. 芯片制造与测试从晶圆到通电RTL、验证、后端都做完了接下来的物理世界部分很多人并不熟悉。造芯片不是在车间里“打印”而是把 GDSII 版图交给晶圆厂经过光刻、刻蚀、离子注入等步骤在硅片上制造出成百上千颗裸片。这个过程叫流片。流片结束后并不能直接把芯片装到板子上。晶圆上的裸片可能存在制造缺陷比如某条金属线断裂、某个晶体管短路所以需要测试。芯片测试分两类良率测试CPCircuit Probing在晶圆上直接对每颗裸片进行测试标记坏片。封装后测试FTFinal Test把好的裸片封装成芯片再次测试功能、时序、功耗、温漂等。测试时常用的技术有扫描链Scan Chain、内建自测BIST、JTAG 边界扫描等。芯片内部不能像 FPGA 那样随便接逻辑分析仪所以必须设计专门的可测性逻辑让测试机能够把测试向量灌进去、把响应抓出来。设计阶段如果不考虑可测性后面测试成本会非常高。芯片回来以后第一步是在评估板上点灯。这个阶段最接近嵌入式工程师的工作。你要写一个最小固件完成时钟初始化、PINMUX 配置、串口打印然后逐步验证 DDR、Flash、以太网、USB 等外设。如果你用的是 RISC-V 内核芯片支持 JTAG 调试接口可以通过 OpenOCD 把固件加载到 SRAM 或 Flash 里执行# 以 J-Link 连接开发板上的 RISC-V 调试核为例 # 实际 target 配置要根据芯片使用的内核和调试接口进行调整 openocd \ -f interface/ftdi/jlink.cfg \ -f target/riscv.cfg \ -c adapter speed 4000 \ -c transport select jtag \ -c init \ -c halt \ -c program app.elf verify reset exit这段命令做了四件事连接调试器、暂停目标、把 app.elf 烧进去并校验、复位后运行。调试口是芯片测试和软硬件协同定位的“生命线”所以开发自研芯片时一定要在早期就把 JTAG/SWD 调试模块规划好否则芯片回来之后你连“为什么没跑起来”都很难查。一颗 SoC 不是上电就能跑系统。以 Linux 启动为例典型流程是 BootROM → SPL → U-Boot → ATF → 内核 → 文件系统。BootROM 是芯片出厂固化的启动程序它负责从 Flash、SD 卡或 UART 加载下一级引导程序。这一套流程在流片前就应该在 FPGA 原型上验证过否则芯片回来再调最容易卡在“串口没输出”这种问题上。你平时在 RK3588 开发板上看到的 U-Boot 启动日志放到自研芯片上每一个环节都要自己移植和适配。7. “快”的工程杠杆并行化、自动化与 IP 复用回到这篇文章的题目。如果问“我们造了一颗芯片而且它很快”这里的“快”到底靠什么实现我认为有三个杠杆。第一个杠杆是IP 复用。一颗 SoC 里CPU 核、总线、DDR 控制器、USB 控制器这些成熟 IP 能买就买能开源的用开源不要什么都从零写。你团队的核心价值应该放在自定义加速器、系统架构差异化、功耗优化等真正能拉开差距的地方。把时间浪费在写一个通用 SPI 控制器上就是在拖慢整个项目。第二个杠杆是验证自动化。RTL 代码量越大靠手工测试就越不可靠。建议建立持续集成CI流程每次代码提交后自动运行 lint、编译仿真、跑回归用例、检查覆盖率并输出报告。这样设计人员可以尽早发现破坏性问题避免到项目后期才堆积大量回归 Bug。下面是一个极简的 CI 回归脚本示例可以放进 GitLab CI 或 Jenkins#!/bin/bash # 文件路径ci/run_regression.sh set -e DESIGN_RTL_DIRrtl TB_DIRtestbench # 找到所有 RTL 文件 RTL_FILES$(find ${DESIGN_RTL_DIR} -name *.sv | sort) echo Start compiling all RTL files and testbenches... iverilog -g2012 -o sim/regression \ ${RTL_FILES} \ ${TB_DIR}/*.sv echo Run simulation... vvp sim/regression echo Check key log keywords... grep -E TEST_PASSED|TEST_FAILED sim/regression.log if grep -q TEST_FAILED sim/regression.log; then echo Regression failed. exit 1 fi echo Regression passed.这个脚本虽然简单但它体现的是工程化思维把重复劳动交给机器把人从“点点点”里解放出来。当一个项目里有几十个模块、几百个测试用例时自动化回归是唯一可行的质量管理手段。第三个杠杆是云上 EDA 与并行综合。大规模 SoC 的综合和布局布线非常消耗计算资源。传统本地服务器跑一次后端流程可能需要一晚上但如果把任务拆到云端配合工具的多机并行能力就能把物理实现时间压缩到几小时。这也是很多设计团队追求的“快速迭代”能力之一。这里需要注意云上 EDA 有数据安全、许可证管理、网络传输等问题并不是所有团队都适合。但如果你已经达到每周甚至每天都要跑一次综合回归的阶段上云往往是值得投资的。8. 常见问题与排查思路造芯项目的问题往往不是“某一段代码运行失败”而是“结果不符合预期”。我把最典型的几个问题列成一张表方便你在项目里遇到类似情况时快速定位。问题现象可能原因排查方式解决方案仿真波形正确综合后频率上不去关键组合逻辑路径过长查看综合时序报告定位最大路径延迟在关键路径插入流水线寄存器或优化逻辑结构FPGA 原型上能用流片后不稳定时序约束不完善或时钟域交叉未处理检查 SDC 约束分析片上时钟偏移完善多层时钟约束处理异步信号同步芯片上电后串口无输出BootROM/启动引脚配置错误复位电路异常检查电源、时钟、复位引脚用逻辑分析仪看启动引脚电位核对硬件原理图检查 boot mode 引脚确认启动介质写入 Flash 后无法启动固件链接地址或镜像格式错误用调试器读内存确认跳转地址检查链接脚本、镜像头部确认启动介质偏移芯片功耗明显偏高时钟使能控制不当或模块空跑跑功耗分析检查模块活动率增加时钟门控优化电源域OpenOCD 连接不上目标芯片调试接口配置错误或复位被拉死检查 target cfg查看 OpenOCD 日志更换接口配置确保调试口供电和复位正常这些问题的共同点是出现时机往往在“看起来已经跑通”之后。所以要养成记录日志和仿真波形的习惯。尤其是时序报告、覆盖率报告、build 记录都应该有版本管理和归档。否则一旦出现任何回归你不知道是 RTL 改坏了还是约束文件变了还是 IP 版本升级了。9. 最佳实践与工程建议如果你所在团队正准备启动一个芯片项目或者你个人想把一颗小 SoC 作为学习项目跑通下面这些建议值得先收藏。9.1 把需求写成可验证的规格书芯片设计最忌讳“边写边想”。需求阶段就要明确CPU 用哪种架构总线怎么选支持哪些外设目标频率是多少功耗目标是多少每个需求都要对应一个可验收的测试用例。比如“UART 在 115200 波特率下收发正确”这就是一个可验证的需求“系统体验流畅”则不是。9.2 成立独立的验证小组小团队很容易让设计工程师自己验证自己的代码。短期看效率高长期看风险大。设计者会下意识地围绕自己的实现逻辑写测试很难发现设计意图上的偏差。哪怕只有一个人做全职验证也比让设计者自证清白要好。验证计划要提前写覆盖率要提前定目标。9.3 从第一天就考虑可测性和可调试性不要等芯片后端开始了才讨论“怎么测试”。设计阶段就要规划扫描链接入点、JTAG 调试接口、性能计数器、trace 模块。芯片回来后软件团队拥有的第一件武器就是调试接口。没有调试手段任何问题都只能靠猜。9.4 流片前必须跑一次完整的系统级回归这个建议听起来是废话但很多团队会犯同类错误模块级都通过但把整个 SoC 的 CPU、加速器、总线、DDR 模型连起来后却没有跑过完整的 Linux 启动、应用程序加载和压力测试。流片前跑一次“24 小时不间断压力”往往能发现很多互相踩脚的时序问题。9.5 版本管理要覆盖 RTL、约束、脚本和 IP 配置大量芯片项目的问题不是代码 Bug而是“配置文件混乱”。一个 SDC 文件名写错可能导致整条时钟路径分析错误。强烈建议把 RTL、约束、综合脚本、FPGA 工程、固件代码全部纳入版本管理并在每次流片/原型发布时打 tag记录对应的 IP 版本、工具版本和数据库版本。9.6 软件人员和硬件人员要提前对齐芯片设计进入验证阶段时软件团队就应该开始在模拟器或者 FPGA 原型上写驱动了。等芯片回来再让软件从头读手册会浪费掉整个软硬件联调的前两周。最佳实践是在 FPGA 原型验证阶段软件就跑通启动和最小外设驱动同时把 BSP 的坑提前踩完。10. 总结与后续学习方向这篇文章从“我们造了一颗芯片而且它很快”这句话出发把芯片项目的全流程拆成了需求定义、RTL 设计、仿真验证、逻辑综合、后端物理设计、制造测试和系统启动七个环节。你可能已经发现芯片行业的“快”不是某一个人的手艺超群而是架构复用、验证自动化和流程规范共同积累的结果。如果你刚接触芯片设计下一步可以先做两件事一是用开源的 RISC-V 核和 FPGA 开发板把一个最小 SoC 跑起来亲身体验“写 RTL、跑仿真、上板验证”的闭环二是找一个已有的 SoC 项目读它的验证环境和启动流程理解模块之间如何协作。只要能把 RTL 的每个模块都定义清楚验证覆盖率达到足够高的水平后端时序约束真正收敛“造一颗芯片”就不再是黑盒里的魔法而是一套你可以掌控的工程流程。

相关新闻