尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

一文跑通:使用OpenROAD实现RISC-V芯片从RTL到GDS

一文跑通:使用OpenROAD实现RISC-V芯片从RTL到GDS 如果我说现在完全不用碰商业EDA就能把一个RISC-V小核从RTL一路跑到GDS并导出版图很多人第一反应是不相信。OpenROAD这个开源工具链就是专门为这件事而生的——它把数字后端最关键的几个步骤逻辑综合、布局规划、标准单元布局、时钟树综合、布线和GDS导出全部收进一套开源流程里。我最早是在OpenLane里间接用到它后来发现OpenROAD本身就是一个独立的RTL-to-GDSII全流程工具。这篇文章是我用OpenROAD从零跑通第一个小SoC的完整记录从工具安装、PDK准备到SDC约束、分阶段配置再到最后输出GDS每一步都写清楚配置细节和参数理由。适合刚想入坑开源芯片设计、又不想一上来就被商业工具链劝退的同学。1. 为什么是OpenROAD开源数字后端的一站式答案说实话做数字IC设计的人对“开源”这两个字的态度一直很微妙。前端仿真、综合可以靠开源工具勉强撑起来但一走到布局布线几乎所有人都会劝你老老实实买商业工具。原因很简单place and route涉及大量算法优化和工艺细节早年开源工具连基本时序收敛都做不到更不用说DRC干净。OpenROAD算是真正把这个缺口补上的项目。它最初来自DARPA的芯片设计计划目标很直白让开源工具链能在一夜之间完成RTL到GDSII的设计迭代。项目名字里的“ROAD”不是马路是“Realization Of A Design”——一个设计从想法到物理实现的全过程。它把Yosys的综合结果接进来自己完成布局、时钟树、布线最后通过KLayout的库生成GDS。我用它跑了一个很小的SoC一个PicoRV32的32位RISC-V核加上UART、GPIO和一块SRAM宏。整条链路跑通的体验比我想象中顺但中间也踩了不少坑。对比商业工具动辄几十万的license费用OpenROAD至少让个人开发者、学生和小团队第一次有了“亲手做一个芯片物理实现”的可能性。它的适合人群很清楚想了解数字后端完整流程的学生OpenROAD能让你看到每一阶段的中间结果想在流片前做快速评估的团队用它做早期floorplan和时序预估非常方便对EDA工具本身感兴趣的开发者它的代码结构比商业工具友好太多。如果你只是想跑通流程、拿到GDS而不是做一颗超高主频的处理器OpenROAD完全够用。我这次的目标就是跑通、出GDS、时序基本干净。2. 开工前的三件事工具链、PDK与设计输入2.1 安装OpenROADDocker最快源码构建最稳安装OpenROAD有四种常见方式按省事程度排我建议的顺序是Docker、官方Release二进制、包管理器、源码编译。Docker是最省心的官方在Docker Hub上有现成镜像docker pull openroad/openroad docker run -it openroad/openroad bash进去之后直接执行openroad -version能输出版本号就说明环境OK。这个镜像里所有依赖都装好了适合第一次接触的人先跑通流程。如果你不想用容器GitHub Releases页面每个版本都有编译好的Linux二进制包下载解压后把bin目录加进PATH就能用。macOS用户可以用brew install openroad但Homebrew上的版本往往不是最新的跑新PDK时可能遇到兼容性问题。源码编译是让我最安心的一条路。依赖不少主要需要CMake 3.15、GCC 9、Boost、Tcl、SWIG、Eigen、Lemon、OR-Tools。在Ubuntu 22.04上核心依赖一条命令装完sudo apt install cmake g libboost-all-dev tcl-dev swig \ libeigen3-dev liblemon-dev libgsl-dev libx11-dev然后从GitHub拉源码、编译git clone --recursive https://github.com/The-OpenROAD-Project/OpenROAD.git cd OpenROAD mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERELEASE make -j$(nproc)编译过程大概十几分钟取决于机器。装完后建议把openroad软链到/usr/local/bin后面脚本调用方便。提示如果你是在虚拟机或远程服务器里装先把网络环境确认好。我踩过一次很无语的坑——在一台远程Linux机器上编OpenROAD编译到一半依赖下载失败排查了半天发现是双网卡环境下网络配置有问题大量并发下载时连接不稳定。后来配好链路聚合才解决。这类基础环境问题跟芯片设计本身无关但卡你一整天很常见。2.2 PDK选型为什么我推荐从sky130开始PDKProcess Design Kit是工艺厂提供的设计套件包含标准单元库、IO单元、器件模型等。开源设计能选的主流PDK有三个PDK工艺节点特点SkyWater 130nm (sky130)130nm社区资料最多OpenLane默认支持教程丰富GlobalFoundries 180nm (GF180MCU)180nmefabless维护偏向MCU场景IHP SG13G2130nm有SiGe HBT适合混合信号探索对于第一个SoC设计我强烈建议选sky130。不是因为它的130nm工艺有多先进而是因为这个PDK的社区生态最成熟你遇到任何问题都能在网上找到答案。sky130的PDK数据放在GitHub的efabless/open_pdks仓库里。安装流程是git clone https://github.com/efabless/open_pdks.git cd open_pdks ./configure --enable-sky130-pdk make sudo make install安装完成后需要一个环境变量指向PDK根目录export PDK_ROOT/usr/local/share/pdksopen_pdks会生成一个完整的sky130A目录里面包含我们最关心的几个子目录。后续OpenROAD读库时需要找到这几个关键文件标准单元Liberty时序库sky130A/libs.ref/sky130_fd_sc_hd/liberty/typical/sky130_fd_sc_hd__tt_025C_1v80.lib标准单元LEF物理库sky130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef工艺Tech LEFsky130A/libs.ref/sky130_fd_sc_hd/techlef/sky130_fd_sc_hd.tlef这些路径很长建议在脚本里用变量固定好免得每次敲到崩溃。2.3 准备一个“能跑通”的RTL设计有了工具和PDK还得有一个能跑的设计。我选的是一颗PicoRV32小核外加UART和GPIO的小SoC。PicoRV32是Clifford Wolf写的极简RISC-V核整个CPU几千行Verilog综合速度非常快非常适合做OpenROAD的练手对象。选择这个设计的原因很实际它足够小第一次跑流程时每一个阶段都能快速出结果方便我理解每个阶段做了什么。如果你一上来就拿来一个带Cache、带中断控制器的大SoC综合时间翻倍不说遇到问题时根本分不清是RTL问题还是工具配置问题。拿到RTL后第一件要做的事是确保它是可综合的。OpenROAD不会帮你检查这一点。我习惯先用Yosys做一次快速综合验证yosys -p read_verilog rtl/*.v; synth; stat正常跑完会输出门数和面积统计。如果这里报错或者生成了大量意外的Latch先回头改RTL再继续。我这次SoC里还带了一块SRAM宏PicoSoC的源码里其实用的是cell-based的双端口RAM。如果你想用真实的SRAM硬宏需要单独准备宏的LEF和Liberty文件OpenROAD把它当黑盒处理floorplan阶段再手动放到指定位置。3. 目录布局与SDC约束跑通之前先把这些写明白3.1 项目目录怎么组织工具链准备好了RTL也有了接下来是搭项目目录。我强烈建议从一开始就建立一个规范的结构不要所有文件堆在一个目录。前期你也许觉得无所谓跑到第三第四次迭代你就会感谢这个决定。openroad_soc/ ├── rtl/ # RTL源文件 │ ├── picorv32.v │ ├── simpleuart.v │ └── soc_top.v ├── constraints/ │ └── soc.sdc # 时序约束 ├── config/ │ └── pdn.cfg # 电源网络配置 ├── scripts/ │ ├── flow.tcl # OpenROAD主流程脚本 │ └── reports.tcl # 报告生成脚本 ├── results/ │ ├── synthesis/ │ ├── floorplan/ │ ├── placement/ │ ├── cts/ │ ├── route/ │ └── gds/ └── logs/results下面按阶段分子目录每跑完一步就把中间结果写到对应目录。这样做最大的好处是某个阶段出错时可以只重跑那一步不用从头开始。3.2 哪些库文件要备齐OpenROAD启动前至少需要这几类文件缺一个流程都走不动Liberty文件标准单元在不同PVT条件下的时序、功耗模型。SKY130的typical条件是25度、1.8V。LEF文件标准单元的物理尺寸、pin位置、布线障碍等信息。Tech LEF工艺层的布线规则包括每层金属的最小线宽、间距、通孔规则。SDC约束时钟、IO时序、false path等设计约束。门级网表综合后的verilog网表。如果你用OpenROAD自带的synth命令做综合它会生成网表如果你用自己的综合工具就在主流程里读入。其中Tech LEF最容易漏。很多人只读了standard cell的LEF就开始跑跑到详细布线阶段报一堆DRC违例回头排查才发现Tech LEF没读。3.3 手写SDC的三个关键点SDCSynopsys Design Constraints是时序约束的标准格式OpenSTA会用它来做时序分析。第一次写SDC不需要很复杂抓住三个关键点就行时钟、IO延迟、非相关路径。我的示例constraints/soc.sdcset clk_period 20.0 # 1. 定义时钟20ns周期对应50MHz create_clock -name clk -period $clk_period [get_ports {clk}] # 2. IO时序约束 set_input_delay [expr $clk_period * 0.2] -clock clk \ [remove_from_collection [all_inputs] [get_ports {clk} rst_n]] set_output_delay [expr $clk_period * 0.2] -clock clk [all_outputs] # 3. 异步复位设为false path set_false_path -from [get_ports {rst_n}]时钟周期我起初设的是20ns也就是50MHz。对于130nm工艺和PicoRV32这种小核这个频率余量很充足第一次跑时序能干净收尾的概率大。等你流程熟练了再改成10ns甚至更紧张去挑战极限。IO延时先给20%的周期做保守估计。真实的IO时序要根据外部器件的建立保持时间算但练手阶段没必要扣那么细重点是让OpenROAD知道输入输出不能随便乱约束。异步复位必须设false path否则STA会把复位信号的时序路径全部检查一遍结果既慢又一堆伪违例。这个很多人一开始不知道设了之后报告一下子清爽很多。4. 从综合到GDSOpenROAD七个阶段逐段拆解4.1 综合把RTL变成门级网表OpenROAD本身支持调用Yosys做逻辑综合命令就是synth但我在实际使用中更喜欢先在Yosys里独立完成综合再把网表交给OpenROAD做物理设计。这样分工更清晰RTL层面的问题在Yosys阶段解决物理设计的问题在OpenROAD阶段解决。Yosys综合命令yosys -p read_verilog -sv rtl/*.v; \ synth -top soc_top; \ dfflibmap -liberty sky130_fd_sc_hd_tt.lib; \ abc -liberty sky130_fd_sc_hd_tt.lib; \ write_verilog results/synthesis/soc_synth.v综合完成后生成的网表会作为OpenROAD主流程的输入。OpenROAD主流程脚本scripts/flow.tcl从读库开始# 0. 版本与全局设置 set PDK_ROOT /usr/local/share/pdks set LIB_FILE $PDK_ROOT/sky130A/libs.ref/sky130_fd_sc_hd/liberty/typical/sky130_fd_sc_hd__tt_025C_1v80.lib set LEF_FILE $PDK_ROOT/sky130A/libs.ref/sky130_fd_sc_hd/lef/sky130_fd_sc_hd.lef set TECH_LEF $PDK_ROOT/sky130A/libs.ref/sky130_fd_sc_hd/techlef/sky130_fd_sc_hd.tlef set NETLIST results/synthesis/soc_synth.v set SDC_FILE constraints/soc.sdc # 1. 读入工艺库 read_liberty -liberty $LIB_FILE read_lef $LEF_FILE read_lef $TECH_LEF # 2. 读入网表并链接设计 read_verilog $NETLIST link_design soc_top # 3. 读入时序约束 read_sdc $SDC_FILElink_design是关键一步它把网表里的标准单元名称和Liberty库里的单元做映射。如果这里报“cant find cell xxx”多半是Liberty库不全或者SRAM宏的库没读进去。4.2 Floorplan与电源网络先圈地再布线Floorplan就是给芯片定尺寸、规划模块位置的过程。OpenROAD的floorplan命令有两种用法# 方式一直接指定utilization、长宽比和die尺寸 floorplan -u 50 -r 1.0 -s 900x900 # 方式二现代版本的等效写法 # initialize_floorplan -utilization 50 -aspect_ratio 1.0 -core_space 20参数含义并不复杂-u 50表示标准单元要占core面积的50%剩下留给布线通道-r 1.0表示长宽比1:1-s 900x900表示die尺寸900微米乘900微米。不同版本参数名会有差异跑之前先执行floorplan -help确认一下你手上版本的写法。利用率50%是个保守值适合第一次跑。太小浪费面积太大布线时必爆congestion。我后来试过70%详细布线阶段就出现了一些DRC违例。如果是带SRAM宏的设计floorplan之后要把宏放到合适位置。宏的位置影响很大我习惯把SRAM贴着core的边放远离时钟输入引脚避免布线绕大圈macro_placement # 如果宏少也可以手动指位置更可控电源网络PDN在floorplan后立刻做。OpenROAD的pdngen命令读一个配置文件来生成电源环和电源条纹pdngen -cfg config/pdn.cfgpdn.cfg的写法版本间差异比较大最简单的做法是先不写配置文件直接默认参数生成一遍看看电源环长什么样再调整。对练手设计来说默认的电源网格完全够用。4.3 Placement全局布局与合法化Floorplan和电源网络就位后进入placement阶段。字面上是给每个标准单元找个位置实际包含两步全局布局global placement和详细布局detailed placement。global_placement detailed_placementglobal_placement用RePLAce算法把单元摊开考虑布线密度和时序但此时单元位置可能重叠。detailed_placement做合法化legalization把重叠的单元推到标准单元行上保证每个位置合法。这里有个必须检查的指标——overflow。跑完global placement后查看报告里的overflow数值report_placement_density如果overflow超过10%说明floorplan的利用率设太高或者宏布局不合理建议先回去调floorplan而不是硬往下走。硬跑下去CTS和布线阶段会很痛苦。4.4 CTS时钟不走歪的秘密CTSClock Tree Synthesis是很多初学者忽略但实际非常重要的阶段。时钟信号在芯片里要同时到达所有寄存器如果到达时间差太大建立时间路径就会出现严重违例。OpenROAD的CTS用TritonCTS实现命令很简单clock_tree_synthesis但背后做的事情不少。它会自动选择合适的buffer单元、构建时钟树、平衡各级时钟延迟。CTS之前最好指定clock buffer的类型否则工具可能用到性能不佳的单元clock_tree_synthesis -root_buf CLKBUF_X2 \ -buf CLKBUF_X2 -buf CLKBUF_X4-root_buf和-buf参数在不同版本里可能不同同样建议先查help。CTS跑完后report_clock_timing可以看时钟的skew和latency。我这次设计19级深度的时钟树skew控制在0.5ns以内对50MHz来说完全够用。CTS之后有一个细节很多教程不提要重新读入网表并做时序更新。因为CTS在网表里插入了大量buffer单元逻辑关系没变但物理连接变了。OpenROAD在执行后续步骤前会自动处理但如果手动拼接阶段脚本记得在CTS后保存一次DBwrite_db results/cts/soc_cts.db这样万一后面布线有问题可以直接从CTS阶段恢复不用重跑前四步。4.5 Routing全局布线与详细布线布线分两级全局布线global routing和详细布线detailed routing。set_routing_layers -signal 2-5 -clock 4-5 global_route detailed_routeset_routing_layers非常重要。sky130有6层金属我设定信号走metal2到metal5时钟走metal4和metal5。原因很实际metal1通常被标准单元内部的pin占据电源网络也占了一层信号从metal2往上走比较合理时钟网络走高层金属因为高层电阻小、延迟低适合长距离传输。global_route不做实际的物理连线而是把布线问题抽象成网格图规划每条net的走向。它在FastRoute引擎里完成。这一步会输出congestion报告重点关注有没有超过100%的拥塞区域——如果有代表那条通道的布线需求超过了物理容量详细布线阶段一定出问题。detailed_route用TritonRoute做真正的连线。它会按照设计规则检查每根线的间距、宽度、通孔位置。跑完后的报告会给出DRC违例数量Detailed routing violations: 0看到这个数字归零的那一刻整个流程算真正走通了。4.6 寄生提取与时序签核很多教程到布线就结束了这是不对的。布线完成后那块GDS只是“画得出来”能不能工作还要看时序。布线后的实际延迟和布局阶段的估算值差别很大必须做寄生提取再重新报时序。OpenROAD的寄生提取命令estimate_parasitics -global_routing report_checks -path_delay max -digits 3 report_checks -path_delay min -digits 3 report_worst_slackestimate_parasitics会基于布线结果提取RC寄生参数然后OpenSTA用这些参数重新做时序分析。如果report_worst_slack输出负值说明时序违例需要回到前面的阶段调参数。提示在跑estimate_parasitics之前报的时序数据是没有意义的那只是基于布局的粗略估算。一定要在布线后、寄生提取后再看才是接近真实芯片的信号延迟。4.7 GDS导出与DRC检查最后一步导出GDSfinish write_gds results/gds/soc.gds write_def results/def/soc.def write_db results/db/soc_final.dbfinish命令会做一些收尾工作包括金属填充metal fill。金属填充不是为了好看而是为了满足工艺厂的金属密度要求避免化学机械抛光时出现凹陷。对于科研用途或练手设计不做fill问题不大但如果你要去流片这步不能省。OpenROAD自带的DRC检查是基于TritonRoute的布线规则准确度有限。真正的signoff DRC要用Magic或者KLayout加载foundry的DRC deck来跑。我一般会在导出GDS后用KLayout加载sky130的drc规则文件再跑一遍确认klayout soc.gds -drc sky130A/libs.tech/klayout/sky130A.drc这一步能帮你发现很多OpenROAD没报出来的问题比如金属密度不足、某些特殊间距规则违例。5. 第一次跑设计最容易卡住的5个坑含完整排查链路5.1 版本不匹配导致的读库崩溃现象read_liberty或read_lef时报错或读入后link_design找不到标准单元。排查链路先看OpenROAD版本和PDK生成工具版本是否匹配。open_pdks更新后生成的LEF可能包含新版OpenROAD才认识的新语法反过来也一样。我刚装好时用OpenROAD 2.0配一个旧版open_pdks生成的sky130A一读LEF就崩。解决办法很简单把两个仓库都更新到最新。或者更稳妥的做法是用Docker镜像镜像里的OpenROAD和PDK版本是经过测试匹配的。5.2 Floorplan利用率设小导致placement overflow现象global placement跑完overflow高达20%以上。这是我最开始犯的错误——以为利用率越低越安全设了个30%。结果core面积太大单元之间距离过远全局布局时密度极不均匀反而出现局部拥塞。排查链路report_placement_density看overflow分布图发现某个区域红得发紫。回到floorplan把利用率适当提高。我实测50%到60%之间对sky130比较理想。5.3 CTS后没有时钟树现象clock_tree_synthesis秒结束report_clock_timing显示所有flip-flop的时钟延迟相同且接近0。这不是好事。CTS等于没做。最常见的原因是SDC里忘了定义时钟或者时钟名字和网表里不一致。排查链路先确认网表里时钟端口的名字。get_ports clk查不到的时候试试get_pins看看时钟是不是来自PLL或其他内部节点。如果时钟是内部产生的要在SDC里用create_clock -name clk_internal [get_pins pll/clk_out]这样定义在内部节点上。5.4 detailed_route高违例现象详细布线跑完报出几百上千个DRC违例。排查链路分三层先回溯global_route的congestion报告如果全局布线阶段就有大片黄色红色区域问题出在floorplan布局密度再看set_routing_layers是否限制了可用金属层——我试过只允许metal2-metal3结果违例爆炸最后检查Tech LEF是否读入了完整布线规则。如果三层都排查过还违例把利用率降5%重跑placement。5.5 “Timing unconstrained”——SDC没生效现象所有时序报告显示路径全部unconstrained或者WNS最差负裕量巨大无比。排查链路先确认read_sdc执行了且没有报错。很多人没发现SDC文件路径打错但OpenROAD只是warning没有fatal流程照跑只是所有约束静默失效。这时候在脚本里加一行打印foreach clk [get_clocks] { puts clock: [get_name $clk] period: [get_property $clk period] }如果输出为空说明SDC里的create_clock根本没匹配到任何节点检查时钟端口名字。6. 把OpenROAD接进自己的工作流脚本化与提速跑通一次流程后你想做的第一件事一定是怎么让我改一个参数就能自动重跑答案是脚本化。OpenROAD支持非交互式批处理模式openroad -exit scripts/flow.tcl 21 | tee logs/flow.log-exit参数让工具执行完脚本自动退出tee保留日志方便排查。进一步封装成Makefile目标细分synth: yosys -p read_verilog -sv rtl/*.v; synth -top soc_top; dfflibmap -liberty sky130_fd_sc_hd_tt.lib; abc -liberty sky130_fd_sc_hd_tt.lib; write_verilog results/synthesis/soc_synth.v floorplan: openroad -exit scripts/flow.tcl 21 | tee logs/flow.log clean: rm -rf results/* logs/*有了Makefile之后每次改RTL只需要make synth再make floorplan一气呵成。提速方面OpenROAD支持多线程。主流程脚本开头加set_thread_count 4write_db在各阶段保存DB是我的最大习惯之一。每跑完一个阶段都存一次后续任何环节出错都可以从最近的DB恢复不用从头跑全部流程。多corner的问题也稍微提一句。我上面全程只用了typical cornerTT25度1.8V这是练手设计的合理简化。但如果要做更严谨的时序签核需要至少跑fast和slow两个corner——用read_liberty把各corner的lib都读进来在read_sdc之后用set_corner切到不同corner分别分析。最后再分享一个我实测中觉得特别有用的做法。第一次跑通之后把每一步的report_checks、report_worst_slack、report_design_area这些输出都存成一份baseline。之后再调整任何参数都和baseline对比这样你能清楚知道自己改的东西带来的是正向还是负向变化。我靠这个习惯把WNS从最初的正0.3ns优化到了正1.2ns整个过程都很踏实。开源工具链最大的优势就是可重复、可控制把这份控制力发挥出来你也能在一个周末完成自己的第一个SoC物理实现。
返回列表