Yosys JSON 网表回读:write_json 与 read_json 往返检查
更新时间:2026-10-31。本文是开源 EDA 排错系列的 FAQ 速查页,相关入口见 Yosys JSON 网表回读与开源 EDA 排错中心。这里讨论 JSON 文件重新进入 Yosys 后能观察到什么,不把它当作原始 Verilog 工程的无损备份。
本文要回答的问题
write_json后怎样清空当前设计并执行read_json?- 参数化位宽、常量、属性和层次在 JSON 中表现为什么字段?
- 回读后的
stat、check与write_verilog能证明到哪一步? - 为什么重新写出的 Verilog 不会逐行恢复输入源码?
一、Yosys JSON 网表回读保存的是当前设计状态
write_json 把当前 RTLIL 设计序列化为 JSON,包括模块、端口、线网、单元、连接和属性。它不会保存 Verilog 注释、宏定义、排版或完整的 elaboration(层次展开)历史。因此,回读检查应围绕字段和连接展开,而不是比较两个文本文件是否相同。
蓝色表示读入,橙色表示导出,灰色是主动清空,绿色是回读后的检查。中间插入 design -reset 很重要:如果不清空,后续命令可能仍在操作内存里的旧设计,无法形成干净的 JSON 回读路径。
JSON 中常见字段可以这样定位。不同设计阶段产生的单元类型会不同,不应把本表中的 $dff、$mux 当作所有输入的固定结果。
| 字段区域 | 本次观察内容 | 排查用途 |
|---|---|---|
modules | top 与参数派生后的 lane 模块 | 确认模块是否进入文件 |
ports、netnames | 五位 d、q 及内部线网 | 检查位宽和名称 |
parameter_default_values | WIDTH=5、RESET_VALUE=00101 | 检查派生参数 |
attributes | keep、keep_hierarchy、mark_debug | 检查属性是否随对象保存 |
cells、connections | 一个寄存器、两个多路器及层次实例 | 检查当前网表结构 |
二、最小往返命令与参数化样例
实验模块 lane 有参数 WIDTH 和 RESET_VALUE,顶层把二者设为 5 和 5'b00101。实例带 keep_hierarchy,内部线网带 keep,顶层观察线带 mark_debug。这样一个文件就能同时覆盖参数、常量、属性和层次。
module lane #(
parameter WIDTH = 5,
parameter [WIDTH-1:0] RESET_VALUE = {WIDTH{1'b0}}
) (
input wire clk, rst, en,
input wire [WIDTH-1:0] d,
output reg [WIDTH-1:0] q
);
(* keep = 1 *) wire [WIDTH-1:0] next_q;
assign next_q = en ? d : q;
always @(posedge clk) begin
if (rst) q <= RESET_VALUE;
else q <= next_q;
end
endmodule完整命令序列如下。proc 把过程转换为网表单元,opt_clean 清理未使用对象;随后立即导出,避免把后续优化结果误当成前端原始状态。
read_verilog -sv roundtrip.v
hierarchy -check -top top
proc
opt_clean
write_json roundtrip.json
design -reset
read_json roundtrip.json
hierarchy -check -top top
stat
check
write_verilog roundtrip_out.v回读后的统计显示派生模块仍有 1 个 $dff 和 2 个 $mux,顶层仍有 1 个层次实例;check 报告 0 个问题。这些结果说明当前样例能够被导入、检查并再次写出。
check 的 0 问题只覆盖该命令 obvious problems(明显结构问题)的检查范围。它不是仿真对照,也不是形式等价证明;要确认功能没有被修改,仍需针对原设计与回读设计运行等价性检查或相同时序激励。
三、字段证据:参数已派生,属性仍能回读
本次 hierarchy 在导出前已展开参数,通用的 lane 被移除,JSON 接收的是 $paramod... 派生模块。因此,WIDTH=5 和 RESET_VALUE=00101 出现在派生模块的参数默认值中;重新写出的 Verilog 使用固定五位端口和 5'h05,而不是恢复最初的参数声明文本。
-- selected JSON fields --
"parameter_default_values": {
"RESET_VALUE": "00101",
"WIDTH": "00000000000000000000000000000101"
"keep": "00000000000000000000000000000001",
"keep_hierarchy": "00000000000000000000000000000001",
"mark_debug": "true",
-- round-trip Verilog fields --
module \$paramod$91b81f62bdb2214549427fafbe36dad3af5beb54\lane (clk, rst, en, d, q);
assign _0_ = rst ? (* full_case = 32'd1 *) (* src = "roundtrip.v:15.13-15.16|roundtrip.v:15.9-18.25" *) 5'h05 : next_q;
module top(clk, rst, en, d, q);
(* mark_debug = "true" *)
(* keep_hierarchy = 32'd1 *)
\$paramod$91b81f62bdb2214549427fafbe36dad3af5beb54\lane u_lane (
result=JSON read-back completed; stat/check/write_verilog finished for this sample这段记录还显示,三个实验属性都存在于 JSON,其中 mark_debug 和 keep_hierarchy 也出现在回读写出的 Verilog 中。位字符串前的零用于对齐内部位宽,不应直接按字符串长度推断端口宽度。
如果实际问题表现为“回读后参数不见了”,先确认导出发生在 hierarchy 之前还是之后。已经派生的设计保留的是该次展开结果;若希望保留通用模块及其参数表达,需要单独检查导出前的设计状态,不能要求派生网表反向恢复所有源码形式。
四、怎样判断回读是否满足用途
不同用途对“成功”的要求不同。脚本只想搬移当前网表时,模块可导入、连接可检查可能已经足够;若下游依赖某个名字或属性,就必须逐项验证该字段,而不能只看 Yosys 是否以退出码 0 结束。
| 目标 | 应检查的证据 | 本次结论边界 |
|---|---|---|
| 在同一 Yosys 版本回读当前样例 | 导入日志、层次、stat、check | 已完成,0 个明显结构问题 |
| 保留指定位宽和常量 | JSON 参数字段、输出端口、常量 | 五位与 00101 可观察 |
| 保留指定属性 | 对应对象的 attributes | 三个样例属性可观察 |
| 恢复原始 Verilog 文本 | 参数声明、注释、宏与格式 | 不属于该往返的目标 |
| 证明任意设计功能等价 | 独立形式等价或仿真结果 | 本文没有作此声明 |
还应固定 Yosys 版本和导出阶段。版本升级、额外优化、扁平化或属性清理都可能改变可观察对象。遇到差异时,保存导出前后的 stat、目标对象的 JSON 片段和回读日志,比只比较最终 Verilog 更容易定位信息在哪一步变化。
五、回读异常时按阶段缩小范围
read_json 直接报解析错误时,先确认文件是否完整写出、是否混入了其他日志文本。JSON 后端应独占输出文件,不要把命令重定向、进度信息或手工说明拼进同一个文件。若文件来自另一台机器,还要确认传输过程没有截断;重新导出一份并比较文件大小和末尾结构,比在解析器报错行附近猜字段更直接。
文件能解析但 hierarchy -check 报缺模块时,应查看导出时该模块是否仍在设计中。本文的通用 lane 被派生模块替代,如果下游脚本仍只寻找短名 lane,即使 JSON 完全合法也会得到“找不到”的结果。此时应列出 modules 的实际键,并检查脚本是否需要识别派生名,而不是立即重写输入 Verilog。
结构能回读、某个属性却消失时,要分别检查属性附着在模块、单元还是线网上。只搜索整个文件里的字符串容易把同名的其他对象当成目标。更有效的方法是先按模块和对象名定位,再看该对象的 attributes;同时确认导出前它还存在。opt_clean 或后续优化可能删除未使用线网,属性不会脱离宿主对象单独保留。
如果单元数量与预期不同,也不要仅用 stat 总数判定回读损坏。导出前后的命令序列必须一致,才适合比较计数;一方多跑了优化、展平或技术映射,单元类型自然可能改变。可临时在 write_json 前和 read_json 后各放一次 stat,先排除导出前已经发生的差异,再检查导入环节。
最后把结论写成具体字段清单,例如“两个模块均导入、指定实例存在、三个属性存在、检查为 0 问题”。这种表述既便于复跑,也避免把一次成功扩大成所有版本、所有单元类型和所有属性都得到保证。若下游工具不是 Yosys,还需另外验证它对 JSON 字段和位字符串编码的支持,本文的 Yosys 自回读结果不能替代那一项兼容性测试。
代码获取
本文输入、Yosys 命令和运行脚本已整理到公开仓:https://github.com/derekzhuo/geek-doc.cn/tree/main/experiments/ts-yosys-json-write-read-back。进入目录执行 bash run.sh,会重新生成 JSON 与回读 Verilog,并打印关键字段。
实验环境
- 机器/系统:Mac / macOS(Darwin 25.5.0,x86_64),Docker 容器运行
- 工具版本:Yosys 0.33 (git sha1 2584903a060),镜像
geek-eda:0.2 - 日期:2026-10-31
- 完整命令:
bash run.sh;脚本执行read_verilog -sv、hierarchy -check -top top、proc、opt_clean、write_json、design -reset、read_json、stat、check和write_verilog
这一篇在知识体系里的位置
本篇属于开源 EDA 排错簇的“网表交换与字段核对”节点,重点是 Yosys JSON 网表回读的可观察证据。参数展开产生的派生名称与 generate 作用域命名都应按当前设计阶段理解;后者见 Yosys generate 作用域名:未命名块的 genblk 层级与显式标签。其他前端与网表问题可回到 Yosys JSON 网表回读相关排错入口。
总结
Yosys JSON 网表回读可按 write_json → design -reset → read_json 构造干净往返。本次 Yosys 0.33 样例中,派生后的五位参数、00101 复位常量、三个属性和层次实例均有可观察记录,回读后 stat 给出 1 个 $dff 与 2 个 $mux,check 报告 0 个问题,并成功写出 Verilog。结论只适用于当前样例和所列字段,不代表任意设计通用形式等价,也不意味着能恢复原始源码文本。