ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

从RTL到GDSII:用Makefile串起IC设计全流程自动化

从RTL到GDSII:用Makefile串起IC设计全流程自动化 做IC设计这些年我见过不少新人入职后的第一道坎不是看不懂RTL而是不知道怎么把整个流程“串”起来。RTL写好了要仿真仿真过了要综合综合完要查时序时序过了还要跑回归——每一步背后都是一大堆EDA工具命令、脚本、路径配置。如果你只会手动敲十几条vcs、dc_shell的命令效率会非常低而且特别容易出错。所以业内有个不成文的共识IC设计工程师尤其是数字前端工程师必须会写Makefile。这不是什么加分项而是基本功。这篇文章我会把完整IC设计流程从头到尾拆一遍再把Makefile在流程里的典型用法掰开揉碎最后给出一套可以直接抄的仿真和综合管理方案。不论你是刚入行的新人、正在准备IC数字设计面试的学生还是想系统梳理流程的中级工程师这篇文章都值得你花十几分钟读完。1. 从RTL到GDSIIIC设计完整流程里每个环节在做什么1.1 前端、中端、后端的三段式分工IC设计不是一条单线的流水线业内习惯分成前端Front-end、中端物理实现前和后端Back-end三个阶段。前端负责把产品定义变成可综合的RTL代码中端负责把RTL变成门级网表并保证时序和可测试性后端负责把网表变成可以送去流片的版图GDSII。很多公司把逻辑综合、DFT、STA归到“中端”也有的叫“物理实现前端”而把布局布线、物理验证归到“后端”。划分不同但工作内容是固定的。1.2 每个环节的输入、输出和关键工具我用一个表格把完整的链条列出来方便你对照理解环节核心工作输入输出代表性工具规格定义产品需求、性能指标、功耗预算市场需求设计规格书无文档架构设计微架构、总线拓扑、性能建模设计规格书架构文档/模型SystemC、自研脚本RTL编码写Verilog/SystemVerilog代码架构文档RTL源码Vim/VS Code/Emacs功能验证UVM验证环境、断言、覆盖率收集RTL、验证计划覆盖率报告VCS/QuestaSim/Xcelium逻辑综合RTL到门级网表满足时序约束RTL、SDC约束门级网表Design Compiler/Genus形式验证RTL与网表逻辑等价性检查RTL、网表等价性报告Formality/ConformalDFT设计扫描链插入、ATPG、BIST门级网表测试向量Tessent/DFTMAX静态时序分析检查建立时间、保持时间网表、SDC时序报告PrimeTime/Tempus物理设计布局规划、放置、CTS、布线网表、SDCGDSIIInnovus/ICC2物理验证DRC/LVS/ERC规则检查GDSII验证结果Calibre签核IR-drop、电迁移、功耗完整性GDSII签核报告RedHawk/Voltus1.3 各环节的推进逻辑这个表看起来很复杂但背后有一条主线设计约束SDC是贯穿前后端的“宪法”。综合要读它STA要读它后端物理设计也要读它。SDC里定义了时钟频率、输入输出延时、false path、multicycle path等关键信息一个项目能不能按时序收敛从SDC的质量就能看出七八成。另外一个容易被忽视的环节是形式验证。综合工具在优化组合逻辑时可能改变电路结构如果等价性检查不做后面后端改出来的网表出了问题都找不到源头。所以即使DC报了“时序干净”Formality没过谁也不敢往下推。1.4 架构设计里的AXI为什么是高频考点热搜词里有“AXI协议数字IC设计面试”说明这是面试官非常爱问的方向。AXI是ARM AMBA总线家族的核心协议在架构设计阶段就要确定总线拓扑RTL设计阶段每个需要访问内存或外设的模块都要按照AXI的读地址通道、写地址通道、读数据通道、写数据通道等机制来编写。它跟流程的关系是什么就是“架构设计”和“RTL编码”这两个环节的实际落点。你把AXI的outstanding、burst、out-of-order这些概念搞清楚了再去看总线的RTL实现和验证环境的sequence会顺畅很多。2. 流程自动化的底层需求为什么IC工程师必须会Makefile2.1 没有Makefile的日子我都在干什么我2017年刚入行的时候跑一个UVM仿真需要敲这么一串vcs -sverilog -debug_accessall -f filelist.f -o simv ./simv UVM_TESTNAMEbase_test -l test_base.log文件多的时候编译一次要等七八分钟。最坑的是改了一个RTL文件之后我经常自己都忘了改过哪些文件保险起见干脆从头编译一遍。一天下来真正花在设计上的时间没多少全在等编译和敲命令。后来我用了一个简单的Makefile把编译、仿真、回归封装成target情况立刻不一样了。改完代码make run它检测到RTL变化后只重新编译必要部分批量跑用例make regress全自动收集日志。那种感觉像是从手工小作坊换成了流水线。2.2 Makefile解决的核心问题增量构建和依赖关系Makefile的核心机制是依赖关系加时间戳判断。目标文件比依赖文件新就跳过比依赖文件旧就重新生成。这就解决了两个问题一是避免重复劳动二是保证每次构建都是基于正确的依赖关系。我经常打一个比方贴好了瓷砖但水管要改装修队不会把整个屋子拆了重装只拆水管相关的部分。Makefile干的就是这个“只拆该拆的部分”的活。芯片项目动辄几十万行RTL如果不做增量编译每改一行代码就全量编译一次一天的时间都不够等编译。2.3 在IC流程里Makefile能管哪些事我在实际工作中把Makefile用在这些地方编译仿真可执行文件vcs/questa/xcelium批量跑回归用例收集pass/fail结果调综合脚本把dc_shell、Genus的命令封装成一行make syn生成带时间戳的日志目录方便回溯对接CI系统提交代码后自动触发编译回归所以Makefile不是“软件工程师专用的构建工具”而是IC设计流程自动化的黏合剂。哪个环节有重复操作哪个环节就能用Makefile把它管起来。3. Makefile核心机制拆解目标、依赖、命令和时间戳博弈3.1 一条规则是怎么工作的Makefile最基本的结构是规则ruletarget: prerequisites command第一行冒号前是目标冒号后是依赖下一行以Tab缩进是生成目标要执行的命令。Make执行时的决策逻辑只有三步目标文件不存在执行命令。依赖文件比目标文件新执行命令。否则什么都不做直接跳过。注意第2点这里有“时间戳博弈”的味道。Make对比的是文件的修改时间不是内容。如果你改了一个文件但时间戳没有更新比如用touch -d改回了旧时间Make会认为这个文件没有变化从而跳过编译。反过来如果只是touch了一下文件内容没改Make也会老老实实地重新编译。理解这一点后面排查“为什么改了代码没重新编译”的问题就会很快。3.2 变量、自动变量和三种赋值方式变量是Makefile的骨架VCS vcs RTL_DIR ../rtl TB_DIR ../tb SIMV ./simv引用变量用$(变量名)。这里有一个容易搞混的点就是、:、?三者的区别赋值符含义展开时机典型陷阱递归展开使用时才展开循环引用会死循环:简单展开定义时立即展开变量必须先用后定义?条件赋值未定义才赋值常用来给默认值追加赋值根据已有变量类型注意和/:的兼容实际项目中我建议优先用:。它的行为最可预测不容易出现“变量值在某个target里变了另一个target也用受影响”的诡异问题。自动变量是写rule时的高频工具$当前目标名$^所有依赖文件列表$第一个依赖文件$*目标中%匹配的部分3.3 wildcard、patsubst 这几个函数是日常主力仿真工程里文件列表经常需要动态获取这时候就要用函数RTL_SRC $(wildcard $(RTL_DIR)/*.sv) TB_SRC $(wildcard $(TB_DIR)/*.sv) OBJ $(patsubst %.sv,%.o,$(RTL_SRC))$(wildcard 模式)匹配所有符合模式的文件返回空格分隔的文件列表$(patsubst 模式,替换,文本)替换模式常用于把.sv换成.o$(notdir 路径)去掉绝对路径只保留文件名这两个函数配合起来可以省掉大量手动维护文件名列表的时间。3.4 伪目标不声明会有什么后果clean、all、run这些目标并不生成同名文件所以必须声明为伪目标.PHONY: all clean run regress我见过不止一次项目目录里多了一个叫clean的文件夹然后执行make cleanMake显示“clean已是最新”什么都不做。这就是没声明伪目标导致的。用.PHONY声明之后Make不再检查同名文件每次都会执行命令。3.5 最常见的报错“没有指明目标并且找不到makefile”热搜词里有“make没有指明目标并且找不到makefile”这是一句让所有新手头皮发麻的报错完整英文是make: *** No targets specified and no makefile found. Stop.我总结这个报错的几个常见原因当前目录根本没有Makefile。你可能cd错了目录。文件名不对。Make默认按顺序找GNUmakefile、makefile、Makefile三个名字如果你起了makefile.txt之类的名字它是不会认的。用了-f参数但路径写错。make -f /path/to/xxx.mk路径不对一样报这个错。环境变量MAKEFILES有干扰但这个比较少见。排查命令就三把斧头pwd ls -la | grep -i make find .. -name Makefile在IC项目里还有一个典型场景你站在项目根目录但Makefile在sim/子目录里。这时候要么进子目录再make要么用make -C sim all要么在顶层Makefile里写一层转发。4. 可复用的仿真Makefile从单目录到多用例回归的完整写法4.1 一份能直接用的仿真Makefile下面这份Makefile是我用来管理一个RTL仿真项目的模板直接抄下来改改路径就能用# 仿真配置 VCS : vcs SIMV : ./simv RTL_DIR : ../rtl TB_DIR : ../tb LOG_DIR : ../log RTL_SRC : $(wildcard $(RTL_DIR)/*.sv) TB_SRC : $(wildcard $(TB_DIR)/*.sv) FILELIST : ../filelist.f # 测试用例列表 TESTS : base_test read_test write_test SEED : 123 # 默认目标 all: compile # 编译目标 compile: $(VCS) -sverilog -debug_accessall \ -f $(FILELIST) \ -o $(SIMV) # 运行单个用例 run: compile $(SIMV) UVM_TESTNAME$(TEST) ntb_random_seed$(SEED) \ -l $(LOG_DIR)/$(TEST).log # 回归所有用例 regress: mkdir -p $(LOG_DIR) for t in $(TESTS); do \ echo Running $$t; \ $(SIMV) UVM_TESTNAME$$t ntb_random_seed$(SEED) \ -l $(LOG_DIR)/$$t.log || echo FAIL: $$t; \ done # 清理 clean: rm -rf simv simv.daidir csrc ucli.key $(LOG_DIR) .PHONY: all compile run regress clean用法很简单make compile # 只编译 make run TESTbase_test # 跑单个用例 make regress # 回归所有用例 make clean # 清理编译产物4.2 为什么把编译和运行分开注意我把compile和run分开run依赖compile。这是故意的因为仿真工程里编译很耗时运行单个用例很快。如果run目标每次都重新编译那调试用例的时候会非常痛苦。拆开之后你可以只在第一次编译之后反复make run TESTxxx脚本会自己判断要不要重新编译。4.3 多目录或多testbench工程怎么组织当验证环境变复杂比如不同testbench在不同目录或者需要跑多种seed的穷举回归时我一般这样调整TESTS : base_test read_test write_test SEEDS : 1 2 3 4 5 regress: mkdir -p $(LOG_DIR) for t in $(TESTS); do \ for s in $(SEEDS); do \ echo Running $$t seed $$s; \ $(SIMV) UVM_TESTNAME$$t ntb_random_seed$$s \ -l $(LOG_DIR)/$$t.$$s.log || echo FAIL: $$t seed $$s; \ done; \ done这样一次回归就能覆盖多个测试用例的多种随机种子日志按用例名.seed.log命名查问题的时候一目了然。4.4 仿真Makefile的高频踩坑记录Tab和空格搞混Make要求命令必须以Tab缩进四个空格会直接报missing separator。在Vim里可以用set list看一下Tab显示为^I。忘记加.PHONYclean不执行或者run显示已是最新都是没声明伪目标。改了RTL但make没反应检查文件时间戳。如果是git checkout来的文件时间戳可能比目标文件旧touch一下就好。make -j4并行跑回归日志混乱多个simv同时写同一个log目录文件名会冲突。解决方案是每个seed/job用独立目录或者直接用--jobserver-auth让make管理。5. 工程化进阶多目录源码编译的Makefile组织方案5.1 什么时候必须上多目录Makefile芯片项目动辄几十个IP每个IP又有rtl/、verif/、syn/目录。如果所有编译规则都堆在一个Makefile里会变成几千行的巨型文件维护成本极高。这时候正确的做法是顶层一个入口子模块自己管自己。常见的目录结构长这样project/ ├── Makefile ├── filelist/ │ ├── rtl.f │ └── tb.f ├── rtl/ ├── tb/ ├── sim/ │ ├── Makefile │ └── script/ ├── syn/ │ ├── Makefile │ └── script/ │ ├── dc.tcl │ └── top.sdc └── log/5.2 递归Make的核心写法顶层Makefile只做一件事把命令转发给子目录。# 顶层 Makefile SUBDIRS : sim syn all: for dir in $(SUBDIRS); do \ $(MAKE) -C $$dir all; \ done clean: for dir in $(SUBDIRS); do \ $(MAKE) -C $$dir clean; \ done .PHONY: all clean子目录各自的Makefile可以独立运行互不干扰。这样sim/Makefile只管仿真syn/Makefile只管综合职责清晰。这里有个关键细节子make必须用$(MAKE)不要用make。因为make只是GNU Make的可执行文件名而$(MAKE)不仅是命令还会自动传递顶层make的参数和变量。如果你在顶层用了make -j4但没有用$(MAKE)转发子目录的make会丢失并行信息速度慢很多。5.3 典型仿真子目录Makefile写法sim/Makefile和单目录版本的差别不大但路径要小心TOP_DIR : $(abspath ..) RTL_DIR : $(TOP_DIR)/rtl TB_DIR : $(TOP_DIR)/tb LOG_DIR : $(TOP_DIR)/log RTL_SRC : $(wildcard $(RTL_DIR)/*.sv) TB_SRC : $(wildcard $(TB_DIR)/*.sv) compile: $(VCS) -sverilog \ -f $(TOP_DIR)/filelist/rtl.f \ -f $(TOP_DIR)/filelist/tb.f \ -o $(TOP_DIR)/sim/simv .PHONY: compile用$(abspath ..)和$(TOP_DIR)来统一锚定路径避免在Makefile里到处写硬编码的相对路径。否则你从不同目录执行make相对路径解析的结果完全不同找文件找不到的时候非常崩溃。5.4 将综合流程也纳入Makefile管理仿真之外综合脚本也可以用Makefile封装# syn/Makefile DC : dc_shell DC_SCRIPT : ./script/dc.tcl LOG_DIR : $(TOP_DIR)/log syn: mkdir -p $(LOG_DIR) $(DC) -f $(DC_SCRIPT) | tee $(LOG_DIR)/syn_$(shell date %Y%m%d_%H%M%S).log .PHONY: syn这样make syn就能在跑综合前自动创建带时间戳的日志文件综合输出不会覆盖历史记录。需要回溯某次综合结果时直接去log/目录找对应时间的文件就行。5.5 多目录编译的常见坑子目录Makefile找不到文件多半是路径解析问题用$(abspath ...)统一锚定。变量没传到子make命令行变量是传递的但Makefile里定义的变量需要显式export。两个子目录target同名互相影响比如sim/Makefile和syn/Makefile都有clean顶层循环调用时没问题但如果你在某个子目录手误执行了make clean另一个子目录不会受影响这其实也是件好事。6. IC数字设计面试考点流程题与Makefile题的高频问题热搜词里“ic数字设计面试题”“axi协议数字ic设计面试”“makefile”同时出现说明面试官在这三个方向都有考察。我结合自己带人和被面的经验挑几个高频问题说说答题思路。6.1 流程类高频问题问题1请简要描述IC设计完整流程。这是一道送分题但也是最容易答乱的一道题。我的建议是按“输入端-工具-输出端”的框架来回答前端规格定义、架构设计、RTL编码、功能验证中端逻辑综合、形式验证、DFT、STA后端布局规划、放置、时钟树综合、布线、物理验证、签核问题2SDC约束文件里一般包含哪些内容时钟定义create_clock、时钟不确定性set_clock_uncertainty、输入输出延时set_input_delay/set_output_delay、伪路径set_false_path、多周期路径set_multicycle_path。答完这些还可以补充一句SDC是整个时序收敛的核心综合、STA、后端都要基于同一份SDC来工作。问题3什么时候需要做形式验证至少两个场景一是综合后验证RTL和门级网表逻辑等价二是后端做ECO之后验证ECO前后的逻辑行为一致。问题4梳清时钟树综合的目的。CTS是为了让时钟信号到达各个寄存器的延迟尽量一致减少时钟偏斜保证时序收敛。6.2 Makefile类高频问题问题1Makefile中和:的区别。候选答案是递归展开变量的值在使用时才展开可能出现递归引用性能也更差:是简单展开在定义时立即展开行为更可预测推荐使用。问题2.PHONY作用是什么声明伪目标告诉Make这个目标不生成实际文件每次都执行命令避免和同名文件冲突。问题3如何用Makefile实现增量编译核心是Make的时间戳比较机制目标比依赖旧就重新执行命令目标比依赖新就跳过。实际工程中RTL或TB文件一修改对应的仿真可执行文件就会重新编译。问题4多目录项目如何组织Makefile用递归make顶层Makefile通过$(MAKE) -C 子目录逐个调用子目录的Makefile每个子目录管理自己的构建逻辑职责清晰。6.3 答题时的一个加分动作面试官考察流程题很多时候不是为了听你背流程而是看你对上下游关系的理解。比如你提到逻辑综合如果能顺带一句“综合后的网表需要做形式验证确保和RTL等价才能进入后端”这就比单纯背流程高出一个档次。每个环节都说出“输入从哪来、输出去哪、谁来检查质量”整道题的答法就立体了。7. 最后分享几个调试Makefile的实用习惯我用了这么多年Makefile真正让效率拉开差距的其实是几个调试习惯。第一个是make -n。这个参数只打印将要执行的命令不真正执行是检查Makefile逻辑是否正确的最快方式。我在改完Makefile之后尤其是改动了变量和依赖关系后一定会先跑一遍make -n看看它到底会执行哪些命令。如果发现它要执行的命令和我想的不一样说明依赖关系写错了趁没启动编译赶紧改。第二个是make --debugv。它会输出详细的决策过程包括每个目标的时间戳比较结果。遇到“为什么这个目标执行了/没执行”的问题这个命令会直接告诉你答案。第三个习惯是制造一个“总入口”。我通常会在每个Makefile顶部写一个help目标把最常用的命令列出来help: echo 可用命令: echo make compile - 编译仿真可执行文件 echo make run - 运行单个用例需指定 TEST echo make regress - 跑所有回归用例 echo make clean - 清理编译产物不管是我自己隔几个月重新打开项目还是交接给同事这个help都能让人在30秒内知道这份Makefile能干哪些事省下大量沟通成本。最后再提一句我在实际使用中发现很多新人把Makefile当成“背语法”问我怎样才能写出标准的Makefile。我的答案一直是不要背规则先去写一个五行的Makefile管住一次编译再写一个二十行的管住一次回归遇到问题了去查手册踩过坑自然就记住了。Makefile是为流程服务的工具你把IC设计流程的每个环节想清楚了Makefile的写法其实是水到渠成的事。
返回列表