ARTICLE DETAIL

资讯详情

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

Yosys开源综合实战:配置Xilinx库与UART模块验证

Yosys开源综合实战:配置Xilinx库与UART模块验证 1. 为什么我要从Vivado转向Yosys如果你做FPGA开发有一段时间了大概率经历过这样的场景装一个Vivado硬盘直接少掉50个G打开软件等三分钟综合一个小工程跑十分钟换个电脑又得重新申请License。我不是说Vivado不好它在Xilinx生态里的地位无可替代但问题是——很多时候我们只是想快速验证一个RTL模块能不能综合、资源大概多少、时序能不能收敛犯不着把整个重型武器库搬出来。Yosys就是在这个需求缝隙里长出来的东西。它是一个完全开源的RTL综合框架最初由Clifford Wolf开发现在由YosysHQ维护。核心能力是把Verilog代码转成门级网表支持多种后端输出格式包括BLIF、EDIF、JSON等。配合nextpnr做布局布线再加上IceStorm或Project X-Ray等比特流工具就能构成一条完整的开源FPGA工具链。但很多人对Yosys有个误解觉得它只能玩Lattice的iCE40或者ECP5这类小芯片。实际上Yosys完全有能力处理Xilinx器件的综合任务只是需要额外配置Xilinx的统一库文件。这篇文章要讲的就是怎么从零开始把Yosys跑起来怎么配置Xilinx库让它能综合Artix-7或者Zynq系列的代码以及在实际项目中我踩过哪些坑。适合谁看如果你是有一定Verilog基础、想了解开源工具链的FPGA开发者或者你手头有Xilinx的板子但不想每次都开Vivado跑综合再或者你纯粹对Yosys这个工具好奇想试试水那这篇内容应该能帮到你。我会尽量把每一步都写清楚包括那些官方文档里一笔带过但实际会卡住你的细节。2. Yosys的核心工作方式与综合流程拆解2.1 Yosys到底在做什么要理解Yosys先得搞清楚“综合”这两个字在FPGA流程里到底意味着什么。你写的Verilog代码是行为级的描述比如always (posedge clk) q d;这是给人看的。FPGA里面没有“always块”这种东西它只有查找表、触发器、进位链、Block RAM、DSP这些硬核资源。综合要做的就是把你写的行为级代码翻译成这些实际资源的连接关系生成一个门级网表。Yosys的工作方式跟商业综合工具在理念上有很大不同。Vivado的综合引擎是一个高度集成的大黑盒你给它RTL它给你网表中间发生了什么你不太看得见。Yosys则是把综合流程拆成了一连串可以单独调用的Pass每个Pass做一件具体的事。比如proc负责把always块转成进程网表opt做逻辑优化techmap做工艺映射abc做组合逻辑优化和映射。你可以按顺序执行这些Pass也可以在中途停下来看看中间结果。这种设计的好处是透明。你可以清楚地知道每一步做了什么出了问题也容易定位。坏处是你得知道正确的Pass顺序不然综合出来的东西可能完全不对。我刚开始用的时候就是随便跑synth命令结果综合出来的网表少了一堆逻辑后来才发现是某个Pass没执行到位。2.2 综合流程的完整链路一个典型的Yosys综合流程大致是这样的读取设计用read_verilog读入Verilog文件用read_liberty读入目标工艺库层次展开用hierarchy检查模块层次展开参数化模块进程转换用proc把always块转成多路选择器和触发器逻辑优化用opt系列命令做常量传播、死代码消除、资源共享等工艺映射用techmap把通用逻辑映射到目标库的基本单元组合逻辑优化用abc做逻辑最小化和LUT映射输出网表用write_edif或write_json输出结果对于Xilinx器件第5步和第6步需要特别注意。因为Xilinx的库文件格式跟Yosys默认支持的Liberty格式有些差异需要做一些转换工作。这也是为什么很多人卡在Xilinx库配置这一步。2.3 为什么选择Yosys而不是商业工具这个问题我被问过很多次。说实话如果你的项目是量产级别的用Vivado或者Quartus是更稳妥的选择它们的时序引擎经过大量验证对器件的支持也最完整。但Yosys在以下几个场景里有明显优势快速原型验证改一行代码想看看资源变化Yosys几秒钟就能出结果不用等Vivado慢慢启动CI/CD集成Yosys是命令行工具可以很方便地集成到持续集成流程里做自动化检查教学和学习综合流程完全透明适合理解FPGA综合到底在做什么资源受限环境在没有条件安装大型EDA工具的机器上也能跑我自己的用法是日常开发用Vivado做最终实现但中间做RTL迭代的时候用Yosys快速检查语法和大致资源。这样能省下大量等待时间。3. 环境搭建与Xilinx库配置实操3.1 Yosys的安装方式选择Yosys的安装有好几种途径我分别说一下各自的适用场景。方式一包管理器直接安装Ubuntu或者Debian系统上直接apt install yosys就能装上这是最省事的方法。但问题是仓库里的版本通常比较旧可能不支持一些新的特性。如果你只是想体验一下这个方法最快。方式二从源码编译这是我最推荐的方式虽然麻烦一点但能拿到最新版本而且可以自己控制编译选项。基本步骤是git clone https://github.com/YosysHQ/yosys.git cd yosys make config-gcc make -j$(nproc) sudo make install编译之前需要确保系统里装了必要的依赖build-essential、clang、bison、flex、libreadline-dev、tcl-dev、libffi-dev、git、pkg-config、python3。缺哪个补哪个编译报错信息一般会告诉你缺什么。注意如果你打算用ABC做逻辑优化编译时需要确保ABC子模块也被正确拉取。用make -j$(nproc)之前先执行git submodule update --init --recursive否则ABC可能编译不出来。方式三使用OSS CAD Suite这是YosysHQ提供的一站式打包方案里面包含了Yosys、nextpnr、ABC、GTKWave等全套工具。下载解压就能用不需要自己编译。缺点是包比较大而且版本更新不如源码及时。我个人的建议是如果你只是偶尔用用选方式三如果你打算长期使用并且需要最新特性选方式二。3.2 Xilinx库文件的获取与转换这是整个流程里最关键也最容易出问题的一步。Yosys本身不包含Xilinx的工艺库你需要自己从Vivado的安装目录里提取。Xilinx的库文件通常位于Vivado安装目录下的data/verilog/src/unisims和data/verilog/src/unimacro文件夹里。这些是Verilog格式的仿真模型不是Yosys直接能用的Liberty格式。你需要做一步转换。具体操作流程是这样的第一步找到Vivado安装路径下的库文件。以Vivado 2020.2为例路径大概是/tools/Xilinx/Vivado/2020.2/data/verilog/src/unisims/。第二步用Yosys自带的read_verilog命令读取这些文件然后用write_liberty输出Liberty格式。但实际操作中你会发现直接转换会报一堆错因为Xilinx的仿真模型里用了很多Yosys不支持的Verilog语法。第三步更实际的做法是使用社区已经转换好的库文件。GitHub上有几个项目专门做这件事比如yosys-xilinx或者symbiflow-arch-defs里包含的Xilinx库。这些库已经经过了预处理可以直接被Yosys读取。我目前用的是从symbiflow-arch-defs项目里提取的Artix-7库文件包含cells_sim.v和对应的Liberty文件。配置的时候在Yosys脚本里这样写read_verilog -lib /path/to/cells_sim.v read_liberty -lib /path/to/artix7.lib提示不同系列的Xilinx器件需要不同的库文件。Artix-7、Kintex-7、Zynq-7000虽然同属7系列但库文件有细微差异。用错了库可能导致综合结果不正确。3.3 一个最小可用的综合脚本环境配好之后你需要一个Yosys脚本文件来驱动整个流程。我一般会写一个.ys脚本内容大致如下# 读取设计文件 read_verilog top.v read_verilog uart_rx.v read_verilog uart_tx.v # 读取Xilinx库 read_verilog -lib /path/to/cells_sim.v read_liberty -lib /path/to/artix7.lib # 设置顶层模块 hierarchy -top top # 综合流程 synth_xilinx -top top -family xc7 # 输出结果 write_json top.json write_edif top.edif stat这里用的是synth_xilinx命令它是Yosys专门为Xilinx器件封装的一个综合脚本内部会按正确顺序调用各个Pass。比手动一步步跑要方便得多。-family xc7指定器件系列支持的有xc7、xcu、xc5等。跑完之后stat命令会输出资源使用统计包括用了多少LUT、多少FF、多少BRAM。这个统计虽然不如Vivado精确但用来做快速评估足够了。4. 用UART接收模块跑通完整综合流程4.1 为什么选UART作为验证案例UART接收模块是FPGA入门最经典的练手项目但它其实涵盖了数字设计的几个核心概念时钟分频、状态机、移位寄存器、边沿检测。用它来验证综合流程是否跑通非常合适因为逻辑规模不大综合速度快而且资源使用情况容易预期。我写了一个简化的UART接收模块参数化设计了波特率和时钟频率支持8位数据位、1位停止位、无校验位。代码大概长这样module uart_rx #( parameter CLK_FREQ 100_000_000, parameter BAUD_RATE 115200 )( input wire clk, input wire rst_n, input wire rx, output reg [7:0] data_out, output reg data_valid ); localparam BAUD_CNT CLK_FREQ / BAUD_RATE; reg [15:0] baud_cnt; reg [3:0] bit_cnt; reg [7:0] shift_reg; reg rx_d0, rx_d1; reg state; localparam IDLE 1b0; localparam RECV 1b1; wire rx_negedge rx_d1 ~rx_d0; always (posedge clk or negedge rst_n) begin if (!rst_n) begin rx_d0 1b1; rx_d1 1b1; end else begin rx_d0 rx; rx_d1 rx_d0; end end always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; baud_cnt 0; bit_cnt 0; shift_reg 0; data_out 0; data_valid 0; end else begin data_valid 0; case (state) IDLE: begin if (rx_negedge) begin state RECV; baud_cnt BAUD_CNT / 2; bit_cnt 0; end end RECV: begin if (baud_cnt 0) begin baud_cnt BAUD_CNT; shift_reg {rx, shift_reg[7:1]}; if (bit_cnt 7) begin state IDLE; data_out {rx, shift_reg[7:1]}; data_valid 1b1; end else begin bit_cnt bit_cnt 1; end end else begin baud_cnt baud_cnt - 1; end end endcase end end endmodule这个模块的逻辑很直接检测起始位下降沿然后按波特率周期采样数据位收满8位后输出有效信号。综合这个模块能很好地检验Yosys对基本时序逻辑和组合逻辑的处理能力。4.2 综合脚本的编写与执行针对这个UART模块我写了一个完整的综合脚本uart_synth.ys# 清理之前的状态 yosys -import # 读取设计 read_verilog uart_rx.v # 读取Xilinx库 read_verilog -lib /opt/xilinx_libs/cells_sim.v read_liberty -lib /opt/xilinx_libs/artix7.lib # 设置顶层 hierarchy -check -top uart_rx # 执行综合 synth_xilinx -top uart_rx -family xc7 -flatten # 输出统计 stat # 输出网表 write_json uart_rx.json write_edif uart_rx.edif执行命令很简单yosys -s uart_synth.ys跑完之后终端会输出一大段日志最后是资源统计。我第一次跑的时候看到LUT用了大概30个FF用了20个左右跟预期基本吻合。作为对比Vivado综合同样的代码大概用25个LUT和18个FF差异在合理范围内。4.3 综合结果的解读与验证Yosys输出的JSON网表可以用netlistsvg工具转成可视化电路图或者用json2verilog转回Verilog网表做进一步分析。我一般会做两件事第一检查资源统计是否合理。如果LUT数量远超预期可能是综合过程中某个优化Pass没生效或者代码里存在不必要的逻辑。比如我一开始写UART的时候把baud_cnt的位宽设成了32位综合出来FF数量直接翻倍。后来改成16位就正常了。第二用write_verilog输出综合后的网表跟原始RTL做对比。Yosys综合后的网表是门级描述里面全是LUT2、FDRE、MUXF7这类Xilinx原语。如果你看到网表里出现了$_AND_、$_DFF_P_这种通用单元说明工艺映射没做完整需要检查库文件是否正确加载。实操心得综合完成后一定要跑一次check命令它会检查网表里有没有悬空信号、多驱动冲突等问题。我遇到过好几次因为代码里某个信号没初始化综合出来的网表行为跟仿真不一致的情况。5. 常见报错与排查经验实录5.1 库文件加载失败这是新手遇到最多的坑。典型报错是Cant find module LUT6或者Unknown cell type FDRE。原因通常是库文件路径不对或者库文件格式不兼容。排查步骤确认read_verilog -lib和read_liberty -lib的路径是否正确检查库文件里是否真的包含报错的单元定义可以用grep搜一下确认库文件版本跟目标器件系列匹配我遇到过一种情况是库文件路径里有空格Yosys解析路径时出了问题。把库文件放到没有空格的路径下就好了。5.2 综合结果资源异常有时候综合能跑通但资源统计明显不对。比如一个简单的计数器综合出了几百个LUT。这种情况通常是以下原因位宽设置过大Verilog里reg的位宽默认是32位如果你没显式指定位宽综合工具会按32位处理。养成好习惯每个reg和wire都明确指定位宽。综合命令选择不当synth是通用综合命令不会做工艺映射。对Xilinx器件要用synth_xilinx对Lattice要用synth_ice40或synth_ecp5。优化Pass未执行手动跑Pass流程时漏掉了opt或abc导致逻辑没有优化。5.3 时序约束的处理Yosys本身不做时序分析它只负责综合。时序约束需要交给后面的布局布线工具处理。但综合阶段的一些决策会影响最终时序比如是否做资源共享、是否做寄存器重定时。如果你用Yosys综合然后交给Vivado做实现可以在Yosys脚本里加上-no-rw选项禁止资源共享这样综合结果更接近Vivado的行为。或者在Vivado里重新跑综合把Yosys的结果只作为参考。5.4 常见问题速查表问题现象可能原因解决方法找不到库单元库文件未加载或路径错误检查read_liberty路径确认库文件包含目标单元综合后仿真不一致寄存器未初始化或存在锁存器检查always块完整性避免不完整赋值资源使用远超预期位宽过大或优化未生效明确指定位宽使用synth_xilinx命令综合速度极慢设计规模大或优化级别过高减少优化Pass或分模块综合网表里出现通用单元工艺映射未完成确认库文件正确加载检查techmap是否执行6. 从综合到实现的衔接思路Yosys综合出来的EDIF网表可以导入Vivado做后续的布局布线。具体做法是在Vivado里创建一个工程添加综合后的EDIF文件作为设计源文件然后正常跑实现流程。这样你就能用Yosys做快速综合迭代用Vivado做最终实现和时序签核。另一种玩法是走完全开源的路线Yosys综合 nextpnr布局布线 开源比特流工具。但这条路目前对Xilinx 7系列的支持还不完善主要是比特流生成部分还有限制。如果你用的是Lattice的iCE40或者ECP5这条全开源路线已经相当成熟了。我自己的习惯是项目前期用Yosys快速验证RTL功能和大致资源等逻辑稳定了再导入Vivado做完整实现。这样既享受了开源工具的轻量和快速又保证了最终结果的可靠性。踩过几次坑之后我现在会在Yosys脚本里加上write_verilog输出综合后网表跟Vivado的综合结果做对比如果差异超过20%就说明有问题需要排查。最后分享一个小技巧Yosys的synth_xilinx命令支持-retime选项做寄存器重定时能在一定程度上改善时序。但这个选项对代码风格有要求不是所有设计都能受益。用之前最好先跑一遍不带-retime的版本做对比。
返回列表