开源 EDA 环境 Dockerfile 模板:iverilog/verilator/yosys 一行起环境
更新时间:2026-11-05。模板卡,属于 芯片设计入门系列。同一个 RTL 工程,新人入组第一天装环境往往要耗掉半天:iverilog、verilator、yosys 三个工具各自的依赖、版本差异、PATH 配置,任何一步踩坑都会卡在「仿真器跑不起来」而不是「设计对不对」。这份开源 EDA 环境 Dockerfile 模板把三件套连同冒烟脚本一起烤进镜像,让环境准备收敛成一句
docker build。
同主题还有一篇 EDA 容器环境搭建:从 docker run 开始的消费方指南,两篇的分工要说清楚:那一篇站在消费方视角,讲镜像已经存在时怎么 docker run、怎么挂载工程目录、怎么进容器交互;本篇站在生产方视角,讲这个镜像的 Dockerfile 应该怎么写、两条基底路线怎么选。两篇配合,覆盖「造镜像」和「用镜像」的完整闭环。
本文要回答的问题
- 团队标准镜像上加项目层,和从公共 debian 基底从零 apt 安装,两条 Dockerfile 路线各适合什么场景?
- 一份装齐 iverilog/verilator/yosys 的 Dockerfile 逐行应该怎么写?
- 镜像构建出来后,怎么用一个冒烟脚本证明三件套都真的能跑,而不是只看版本号?
- 受限网络下拉不动 Docker Hub 镜像、容器内工具打印奇怪告警,分别怎么处理?
一、开源 EDA 环境 Dockerfile 模板的使用场景与两条基底路线
这套模板面向的是「三到十人的 RTL 小团队」:工程本身不复杂,但每个人的工作站系统不一(macOS、Ubuntu、老版 CentOS 都有),手工装齐开源 EDA 三件套的维护成本随人数线性增长。容器化之后,Dockerfile 进版本库,环境本身成为被评审、被回溯的工程资产,而不是口口相传的装机笔记。
基底有两条可选路线,日常工作流是同一条四步闭环,颜色按角色区分:蓝是准备输入、紫是构建规划、橙是执行构建、绿是冒烟通过。
读这张图抓住一个判断标准:环境可用的证据是冒烟通过,不是 --version 能打印。版本号只能证明二进制存在,证明不了它能编译你的 RTL、链接器和数据文件都齐全。两条基底路线的取舍如下表。
| 维度 | 路线 A:团队标准镜像加项目层 | 路线 B:公共 debian 基底从零 apt |
|---|---|---|
| FROM 来源 | 本地已有的 geek-eda:0.1(Ubuntu 24.04,内置三件套) | Docker Hub 的 debian:bookworm-slim |
| 外网依赖 | 构建期不需要外网,镜像本地已缓存 | 需拉取基底镜像并访问 debian apt 源 |
| 构建耗时 | 秒级,只有 WORKDIR 与 COPY 两层 | 约 3–8 分钟,取决于 apt 源速度 |
| 版本确定性 | 与团队标准镜像逐字节一致,版本锁定在基底 | 跟随发行版打包版本,bookworm 为 iverilog 12.0 / verilator 5.020 / yosys 0.33 |
| 适用场景 | 内网、受限网络、CI 节点、团队统一环境 | 对外分发、开源工程、能稳定访问外网的环境 |
两条路线不是替代关系:团队内部日常工作走 A,把工程放进公开仓时附上 B 作为外部贡献者的参考实现。本次实测在受限网络下完成,实证路线是 A;B 的 Dockerfile 同仓提供,写法经过逐行核对但构建耗时取决于网络条件。
二、模板一:团队标准镜像上加项目层
路线 A 的完整 Dockerfile 如下,与公开仓中的文件逐字一致。基底 geek-eda:0.1 已经内置 iverilog 12.0、verilator 5.020、yosys 0.33,项目层只做工作目录、工程文件和默认命令三件事。
# 团队标准 EDA 镜像上加项目层的 Dockerfile 模板(本机实证版)
# 基底 geek-eda:0.1 已内置 iverilog 12.0 / verilator 5.020 / yosys 0.33(Ubuntu 24.04)
# 构建:docker build -t geek-eda-mini:local .
# 冒烟:docker run --rm -v "$PWD/src":/work -w /work geek-eda-mini:local bash smoke.sh
#
# 从公共 debian 基底从零装三件套的等价写法见同目录 Dockerfile.public
# (需要外网访问 apt 源与 Docker Hub,内网/受限网络以本文件为准)。
FROM geek-eda:0.1
WORKDIR /work
# 把工程脚本与 RTL 烤进镜像层;日常调试也可用 -v 挂载覆盖 /work
COPY src/ /work/
# 建镜像后默认做一次三件套版本核对,镜像可直接当版本探针用
CMD ["bash", "-lc", "iverilog -V | head -1; verilator --version; yosys -V"]逐行看四个决策点。第一,FROM geek-eda:0.1 而不是随手 latest:标签钉死小版本,团队任何人重建出来的镜像工具版本都相同,避免「我机器上能综合」这类不可复现问题。第二,WORKDIR /work 给后续 COPY 和运行时挂载一个固定锚点,挂载卷与镜像内目录同名,路径心智负担最小。第三,COPY src/ /work/ 把 RTL、testbench 和冒烟脚本烤进镜像层,意味着即使不挂任何卷,docker run <镜像> bash smoke.sh 也能跑——镜像自身就是可分发的最小实验包;日常调试时再用 -v 挂载宿主目录覆盖它,烤进去的副本相当于兜底。第四,默认 CMD 做三件套版本核对,让镜像同时充当版本探针,排查「容器里到底是什么版本」时不用再手敲一长串命令。
三、模板二:公共 debian 基底从零 apt 安装
路线 B 的参考实现是同仓的 Dockerfile.public,适合开源工程对外发布:贡献者不需要你的私有团队镜像,只要能访问 Docker Hub 和 debian apt 源就能复现环境。
# 公共基底参考写法:从干净 debian 一行 apt 装齐三件套
# 适用:能正常访问 Docker Hub 与 debian apt 源的环境。
# 受限网络拉不动 docker.io/library 镜像时,改用同目录 Dockerfile(本地团队基底)。
# 构建:docker build -f Dockerfile.public -t geek-eda-mini:public .
FROM debian:bookworm-slim
ENV DEBIAN_FRONTEND=noninteractive
# 一层完成 apt 安装与清理,避免缓存层撑大镜像
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
iverilog \
verilator \
yosys \
make \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
CMD ["bash", "-lc", "iverilog -V | head -1; verilator --version; yosys -V"]这份模板有三个值得照抄的细节。DEBIAN_FRONTEND=noninteractive 抑制 apt 在构建期弹交互式时区/配置对话框,无人值守的 CI 里不加这行会直接卡住。--no-install-recommends 只装硬依赖、不装「推荐」包,debian 的推荐包动辄上百个文档和 X11 库,对命令行 EDA 工具全无用处。最关键的是整段安装与 rm -rf /var/lib/apt/lists/* 收在同一个 RUN 层里:Docker 的镜像层只增不改,如果在单独一层删 apt 缓存,旧层里的缓存仍然占着体积,合并成一层才能让清理真正生效。make 和 ca-certificates 两个小依赖别漏——前者承接 仿真工程Makefile通用模板 那套构建骨架,后者是日后访问 https 源的前提。
四、构建、挂载与冒烟的数据流
两份 Dockerfile 共用同一套运行方式:构建出本地镜像,运行时把宿主的 src/ 目录挂载进容器 /work,由容器内三件套执行冒烟脚本。数据流向如下。
读这张图注意两条输入在容器工作区汇合:镜像提供工具(iverilog/verilator/yosys),挂载卷提供工程(RTL 与脚本),两者在 /work 叠合。这就是「烤进镜像」与「挂载覆盖」可以共存的原因——不挂卷时用镜像内的副本兜底,挂了卷就以宿主当前代码为准。对应的三条命令如下。
# 1. 构建项目镜像(路线 A,基底本地已缓存,秒级完成)
docker build -t geek-eda-mini:local .
# 2. 挂载工程目录,容器内跑三件套冒烟
docker run --rm -v "$PWD/src":/work -w /work geek-eda-mini:local bash smoke.sh
# 3. 不带参数启动,执行默认 CMD 做版本核对
docker run --rm geek-eda-mini:local冒烟脚本本身也是模板的一部分,set -euo pipefail 保证任何一步失败立即中止,不会出现前段报错、末行仍打印 PASS 的假通过。
#!/usr/bin/env bash
# 容器内冒烟:iverilog 编译执行 + verilator lint + yosys 综合,三件套各跑一次
set -euo pipefail
echo "== iverilog 编译并执行 =="
iverilog -g2012 -o hello.vvp hello.v tb_hello.v
vvp hello.vvp
echo "== verilator lint =="
verilator --lint-only -Wall hello.v
echo "== yosys 综合 =="
yosys -p "read_verilog hello.v; synth -top hello; stat"
echo "SMOKE PASS"被综合的 hello.v 是一个带异步复位的 4 位计数器,三个工具共用同一份 RTL:iverilog 配自检 testbench 跑仿真,五个使能拍后校验 cnt === 5;verilator 用 -Wall 做静态 lint;yosys 走完整 synth 后打印 stat 资源统计。一份 RTL 同时检验仿真、 lint、综合三条工具链,这是冒烟设计的取巧之处——第一个 iverilog 实验的完整按键流程可以对照 iverilog 第一个仿真实验。
五、容器内实测记录
下面两段实录来自同一次 2026-11-05 的真实运行,先构建再冒烟。第一段是构建过程:因为基底 geek-eda:0.1 已在本地缓存,metadata 请求与三层构建全部 0.0 秒完成,镜像 ID 正常写出。
读这段构建日志抓三个点:load metadata 与 FROM 两步都是 0.0 秒,证明基底完全走本地缓存、没有触发任何外网请求;构建上下文只有 1.60kB,说明 .dockerignore 之外没有把杂项文件送进 daemon;COPY src/ /work/ 之后直接导出镜像,项目层薄到可以忽略不计。这正是路线 A 在受限网络下的价值——构建是否成功与网络状况彻底脱钩。
第二段是运行尾部:yosys 综合出 10 个 cell(含 4 个 $_DFF_PN0_ 触发器,对应计数器的 4 位),末行打印 SMOKE PASS,随后默认 CMD 给出三工具版本。
这段输出里藏着一个需要正名的现象:iverilog -V 在版本号 Icarus Verilog version 12.0 (stable) 之后,又打印了两行 Unable to get version from ... ivlpp/ivl。这是 Debian 打包版 iverilog 的已知无害告警——驱动程序尝试向两个后端子进程再要一次版本字符串而未获响应,主版本号本身正常打印,编译、仿真全流程也不受影响。看到这两行不要误判为安装损坏去重装,判定工具是否健康以冒烟脚本为准。
六、注意事项
受限网络下的基底策略要提前定,不要赌加速器。本次实测环境直连 Docker Hub 拉 debian:bookworm-slim 时,metadata 请求长时间挂起,即使配置了三个镜像加速器仍无明显改善。结论是:内网或网络受限时,路线选择要在写 Dockerfile 之前就定下来,预先把团队标准镜像分发到各节点(离线导出 docker save / 导入 docker load 也是一条路),把路线 B 留给确实能访问外网的场景。加速器配置可以作为缓解手段,但不能写进团队流程当依赖。
挂载路径用绝对锚点,脚本里别写死宿主路径。-v "$PWD/src":/work 中宿主侧用 shell 展开当前目录,容器侧固定 /work,配合 -w /work 让工作目录与挂载点重合;冒烟脚本里全部使用相对路径,换一台机器、换一个盘符都不需要改脚本。
镜像内产物与宿主产物要隔离。冒烟生成的 hello.vvp 是容器内 Linux 二进制,若挂载卷把它写回宿主 macOS 目录,它在宿主上不可执行,也容易被误提交进版本库。工程仓的 .gitignore 应覆盖这类产物,保持「源码随挂载卷走、制品留在容器里」的边界。
公共基底路线的版本会随发行版漂移。debian:bookworm-slim 的包版本由发行版冻结,安全更新时小版本可能变化。需要严格锁版本的团队,要么在 apt 命令里写死 =版本号,要么以路线 A 的团队镜像做二次分发——公开仓里两份文件并存,就是为了让这个取舍显式化,而不是藏在某个开发者的本机配置里。
实验环境
- 机器/系统:Mac(Docker Desktop 24.0.7);容器基底 geek-eda:0.1 = Ubuntu 24.04.5 LTS,内置 iverilog 12.0 / verilator 5.020 / yosys 0.33
- 工具版本:Icarus Verilog version 12.0 (stable) / Verilator 5.020 2024-01-01 rev (Debian 5.020-1) / Yosys 0.33 (git sha1 2584903a060)
- 日期:2026-11-05
- 完整命令:
docker build -t geek-eda-mini:local .、docker run --rm -v "$PWD/src":/work -w /work geek-eda-mini:local bash smoke.sh、docker run --rm geek-eda-mini:local
代码获取
完整实验工程(Dockerfile、Dockerfile.public、hello.v、tb_hello.v、smoke.sh、README)在公开仓 experiments/tpl-dockerfile-eda-env。克隆后在该目录执行 docker build -t geek-eda-mini:local . 构建镜像,再执行 docker run --rm -v "$PWD/src":/work -w /work geek-eda-mini:local bash smoke.sh,末行打印 SMOKE PASS 即复现本文全部结果;联网环境可改用 docker build -f Dockerfile.public -t geek-eda-mini:public . 验证公共基底路线。
这一篇在知识体系里的位置
本篇是模板卡,与 EDA 容器环境搭建:从 docker run 开始的消费方指南 构成「生产方造镜像 / 消费方用镜像」的一对:本篇产出的镜像正是那一篇里 docker run 的对象。容器落地之后,工程内部的构建节奏交给 仿真工程Makefile通用模板 的 iverilog/verilator 双后端 Makefile;第一个手把手仿真实验看 iverilog 第一个仿真实验。
总结
开源 EDA 环境 Dockerfile 模板的核心是三个取舍:基底走「团队标准镜像加项目层」还是「公共 debian 从零 apt」,取决于网络条件与分发对象,受限网络下前者把构建与外网彻底脱钩、秒级完成;镜像内只放 WORKDIR、COPY、版本探针 CMD 三类项目层内容,工程文件烤进镜像兜底、运行时挂载卷覆盖;环境是否可用以 SMOKE PASS 为准——一份 4 位计数器 RTL 同时压过 iverilog 仿真、verilator lint、yosys 综合三条工具链,版本号旁的两行无害告警不必惊慌。