在 Sipeed 的 Tang Nano 20K 开发板上,你完全可以绕开高云(GOWIN)那套闭源 IDE:一个名为 APIO 的项目把 Yosys、GTKWave 等零散的开源工具打包成统一工作流,而项目描述文件只需要两行。

APIO 是聚合器,不是新工具链
APIO 本身并不做 Verilog 综合,也不做布局布线。它更像 FPGA 版的 PlatformIO------作者明确说这个项目正是受 PlatformIO 启发。它负责更新你需要的各种工具、自带一套私有库,并在所支持的 FPGA 之间提供一致的工作流。除了命令行,它还能以 Visual Studio Code 扩展的形式安装。
代价是依赖冲突。由于自带私有库,APIO 可能和系统库打架:作者遇到的例子是 ~/.apio/bin/_internal 下的 libreadline.so.8 让一切无法工作,把它改名移走或直接删掉即可解决。另一个坑是,即使你系统里已经装了 Yosys 或 GTKWave,APIO 也坚持用自己那份:作者本机的 GTKWave 版本就和 APIO 自带的不是同一个,脱离 APIO 直接读波形文件会报错。补救办法是从 VS Code 里 APIO 面板的 Tools/Misc 菜单打开一个 shell。

两行配置,一条流水线
每个 APIO 项目都要一个 apio.ini。作者点灯的极简版本只有:
env:default
board = sipeed-tang-nano-20k
top-module = led
也可以用 apio create 命令新建项目,用 apio create -h 看哪些参数必须给。不过在 VS Code 里没有顺手的入口,最实际的做法还是打开 APIO 面板 → Tools → APIO Shell 再执行。

开源与厂商工具,各有取舍
开源工具在 Verilog 环节明显更强:可以用相当成熟的 linter 检查代码正确性,能格式化源码,还有强大的仿真驱动。GOWIN 那套也不差,但它的仿真器绑定在一个第三方仿真包上,而后者在被收购之后,似乎已经没法按 GOWIN 的方式继续获取了。
反过来,想用 GOWIN 的 IP(从生成时钟这种小事,到造一个 CPU 这种大事)就很难塞进 APIO;GOWIN 还能把逻辑分析仪嵌进设计里做运行时观测,APIO 也不容易照搬。你会失去好用的图形化约束编辑器------不过约束本来就能写成文本,作者甚至做了个表格,GitHub 上还有一份轻度测试过的 Excel 版,以及一个 VS Code 约束编辑器扩展。至于谁的合成结果更好,只能实测:速度和资源占用各有所长,而换不换聚合器,最终结果都不会有实质差别。
上手硬件:27 MHz 与串口
示例工程用 27 MHz 时钟,O0 位于 FPGA 的 10 号引脚。插上 Nano 20K 会出现一个串口,在 Linux 下可用 ls -l /dev/serial/by-id 找到。串口经过的是负责烧录的那颗单片机,它一直等待特殊字符序列 XC Enter,敲进去就会得到 "TangNano20K />" 提示符。固件版本旧的话可以按 Sipeed 的说明升级,但务必照表格选对固件------有些看起来更新、实际并不兼容。在提示符下输入 pll_clk O0=27M -s 即可输出时钟;不带参数则显示状态。
默认下载目标是 flash,作者想改成 SRAM,于是用环境配置:
apio
default-env = ram
common
board = sipeed-tang-nano-20k
top-module = led
env:ram
programmer-cmd = openFPGALoader -b tangnano20k ${BIN_FILE}
env:flash
programmer-cmd = openFPGALoader -b tangnano20k -f ${BIN_FILE}
问题是 VS Code 扩展没法切换环境,要烧 flash 只能回到 shell 跑 apio upload --env flash。另外,GOWIN IDE 支持临时禁用某些调试文件,APIO 没有对应功能,作者的办法是把 foo.v、foo-test.v 改名成 .option 结尾再软链。
先仿真,再上板
testbench 是驱动「真实」Verilog 的测试文件。APIO 要求文件名必须以 _tb.v(SystemVerilog 则 _tb.sv)结尾,dumpfile 由它自己接管,但 dumpvars 必须自己写,否则仿真文件是空的;结尾还要有 $finish,不然仿真永不停止。作者把被测模块设成 10 Hz、1 秒间隔,而不是 27 MHz------后者会生成巨大且难处理的文件。顺带一提,他让 ChatGPT 写了一个,除时间参数外几乎与手写的完全一致。
仿真只能走这么远。要看真实运行时芯片内部的信号,还得回到 GOWIN 工具,那是下一篇文章的事。