开源 FPGA 10|FPGA 的 CI/CD:GitHub Actions 自动综合+仿真+时序报告
____ ___ ____ ____
/ ___|_ _| / ___| _ \
| | | | | | | | | |
| |___ | | | |___| |_| |
\____|___| \____|____/
让每一次 git push 都不破坏时序
系列第 10 篇(收官) · 工具版本:GitHub Actions / Docker / Yosys 0.40 / nextpnr-ice40 0.7 / cocotb 1.9 上一篇:MicroLED 驱动控制器:从需求到 FPGA 实现
0. 这一篇要解决什么问题
前九篇把工具学会了,现在要问一个工程问题:怎么保证每次修改都不破坏已有的功能和时序?
单人项目靠自律。多人项目靠流程。FPGA 设计的一个典型痛点是:某同学改了一个模块,综合时间 10 分钟,等综合结束发现时序违例,才发现是 3 天前引入的——这时 git bisect 就很麻烦了。
CI/CD(持续集成/持续交付) 解决这个问题:每次 PR(或 commit),自动跑综合、仿真、时序分析,不达标则阻止合并。
本篇目标:
- 理解 FPGA CI/CD 的价值和挑战
- 构建完整 GitHub Actions 流水线(lint → synth → sim → timing)
- 自动解析 Yosys/nextpnr 报告,生成 PR comment
- 时序不达标自动 fail PR
- 分析成本和实际使用建议
本篇不覆盖:
- GitLab CI/CD(原理相同,YAML 格式略有差异)
- 硬件在环测试(HIL,需要物理开发板,在本地 runner 上跑)
- Vivado/Quartus 的 CI 集成(商业工具 License 问题复杂)
1. 为什么 FPGA 需要 CI/CD
没有 CI/CD 的典型痛苦:
Day 1: Alice 修改 FIR 滤波器,手动综合 OK,推送代码
Day 2: Bob 修改 SPI 控制器,综合发现时序违例(Slack = -2.1ns)
Bob: "是我改的问题还是 Alice 改的?"
两人各自 checkout,综合验证,花了 3 小时
Day 3: 发现是 Alice 修改后关键路径变长了
但 Alice 的本地综合没有触发(因为她目标频率设的是 25MHz,
而项目实际要求 50MHz……)
有 CI/CD:
Alice 推送 → CI 自动跑 50MHz 综合 → Slack 报告
"-0.3ns,FAIL" → PR 被阻止 → Alice 当天修复
FPGA CI/CD 的价值:
- 防止时序退化:每次修改都验证时序,不累积负债
- 文档化资源消耗:每次 PR 自动记录 LUT/FF/BRAM 变化
- 强制跑仿真:不让”改了但没测”的代码进入主干
- 团队协作:明确的自动化标准,减少口头约定
2. Docker 镜像准备
使用 hdl/containers 项目的预构建镜像,或者自建:
# Dockerfile.fpga-ci
# 包含 Yosys + nextpnr-ice40 + nextpnr-ecp5 + icestorm + iverilog + cocotb
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
# 安装基础依赖
RUN apt-get update && apt-get install -y \
build-essential \
python3 python3-pip \
iverilog \
gnat \
wget curl git \
libreadline-dev \
libffi-dev \
libeigen3-dev \
tcl-dev \
&& rm -rf /var/lib/apt/lists/*
# 从 OSS CAD Suite 安装预编译工具(最快)
RUN wget -qO oss-cad.tgz \
https://github.com/YosysHQ/oss-cad-suite-build/releases/download/2024-03-01/oss-cad-suite-linux-x64-20240301.tgz \
&& tar xf oss-cad.tgz -C /opt \
&& rm oss-cad.tgz
ENV PATH="/opt/oss-cad-suite/bin:${PATH}"
# 安装 cocotb
RUN pip3 install cocotb cocotb-bus pytest
# 验证安装
RUN yosys --version && nextpnr-ice40 --version
CMD ["/bin/bash"]
# 构建并推送到 GitHub Container Registry(ghcr.io)
docker build -f Dockerfile.fpga-ci -t ghcr.io/your-org/fpga-ci:latest .
docker push ghcr.io/your-org/fpga-ci:latest
# 也可以直接用 hdl/containers 的现成镜像
# ghcr.io/hdl/pkg/nextpnr:ice40 (包含 ice40 + ECP5 支持)
3. 完整 GitHub Actions Workflow
# .github/workflows/fpga-ci.yml
# FPGA CI/CD:lint → synth → sim → timing
name: FPGA CI
on:
push:
branches: [ main, develop ]
paths:
- 'src/**'
- 'tests/**'
- 'constraints/**'
- '.github/workflows/fpga-ci.yml'
pull_request:
branches: [ main ]
paths:
- 'src/**'
- 'tests/**'
- 'constraints/**'
# 允许 Actions 写 PR comments
permissions:
contents: read
pull-requests: write
issues: write
jobs:
# ─────────────────────────────────────────
# Job 1: Lint(语法检查,最快,先跑)
# ─────────────────────────────────────────
lint:
name: "Lint (Verilog 语法检查)"
runs-on: ubuntu-latest
container:
image: ghcr.io/hdl/pkg/nextpnr:ice40
steps:
- uses: actions/checkout@v4
- name: Verilog 语法检查(iverilog)
run: |
for f in src/*.v; do
echo "Checking $f..."
iverilog -Wall -t null -g2005 $f
done
- name: Verilog 语法检查(verilator lint)
run: |
verilator --lint-only -Wall \
--top-module top \
src/top.v src/pwm_gen16.v \
src/spi_slave_ctrl.v src/i2c_master.v \
src/fault_detect.v
# ─────────────────────────────────────────
# Job 2: Synthesis(综合,生成资源报告)
# ─────────────────────────────────────────
synthesis:
name: "Synthesis (Yosys + iCE40HX8K)"
needs: lint
runs-on: ubuntu-latest
container:
image: ghcr.io/hdl/pkg/nextpnr:ice40
steps:
- uses: actions/checkout@v4
- name: Yosys 综合
id: synth
run: |
mkdir -p build
yosys -p "
read_verilog -sv src/top.v src/pwm_gen16.v \
src/spi_slave_ctrl.v src/i2c_master.v src/fault_detect.v;
synth_ice40 -top top -json build/design.json;
stat -json > build/synth_stat.json
" 2>&1 | tee build/synth.log
# 提取资源数字
LUT=$(grep "SB_LUT4" build/synth.log | grep -oP '\d+' | head -1)
FF=$(grep "SB_DFF\|SB_DFFE" build/synth.log | grep -oP '\d+' | paste -sd '+' | bc)
BRAM=$(grep "SB_RAM40_4K" build/synth.log | grep -oP '\d+' | head -1)
echo "lut=$LUT" >> $GITHUB_OUTPUT
echo "ff=$FF" >> $GITHUB_OUTPUT
echo "bram=${BRAM:-0}" >> $GITHUB_OUTPUT
echo "### 综合结果" >> $GITHUB_STEP_SUMMARY
echo "| 资源 | 使用量 | 总量 | 利用率 |" >> $GITHUB_STEP_SUMMARY
echo "|------|--------|------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| LUT4 | $LUT | 7680 | $(echo "scale=1; $LUT * 100 / 7680" | bc)% |" >> $GITHUB_STEP_SUMMARY
echo "| FF | $FF | 7680 | $(echo "scale=1; $FF * 100 / 7680" | bc)% |" >> $GITHUB_STEP_SUMMARY
echo "| BRAM | ${BRAM:-0} | 32 | - |" >> $GITHUB_STEP_SUMMARY
- name: 上传综合产物
uses: actions/upload-artifact@v4
with:
name: synthesis-outputs
path: build/
- name: 输出综合摘要
run: |
echo "LUT: ${{ steps.synth.outputs.lut }}"
echo "FF: ${{ steps.synth.outputs.ff }}"
echo "BRAM: ${{ steps.synth.outputs.bram }}"
outputs:
lut: ${{ steps.synth.outputs.lut }}
ff: ${{ steps.synth.outputs.ff }}
bram: ${{ steps.synth.outputs.bram }}
# ─────────────────────────────────────────
# Job 3: Simulation(cocotb 仿真)
# ─────────────────────────────────────────
simulation:
name: "Simulation (cocotb + iverilog)"
needs: lint
runs-on: ubuntu-latest
container:
image: ghcr.io/hdl/pkg/nextpnr:ice40
steps:
- uses: actions/checkout@v4
- name: 安装 cocotb
run: pip3 install cocotb pytest
- name: 运行 cocotb 仿真测试
run: |
cd tests/
timeout 300 make SIM=icarus TOPLEVEL=pwm_gen16 MODULE=test_pwm \
COCOTB_RESULTS_FILE=results.xml 2>&1 | tee sim.log
# 检查是否超时
if [ $? -eq 124 ]; then
echo "❌ 仿真超时(300 秒)"
exit 1
fi
env:
PYTHONPATH: "."
- name: 上传 JUnit 测试报告
uses: actions/upload-artifact@v4
if: always()
with:
name: simulation-results
path: tests/results.xml
- name: 解析测试结果
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: tests/results.xml
comment_title: "🧪 仿真测试结果"
# ─────────────────────────────────────────
# Job 4: Timing(时序分析,最重要)
# ─────────────────────────────────────────
timing:
name: "Timing (nextpnr @ 50 MHz)"
needs: synthesis
runs-on: ubuntu-latest
container:
image: ghcr.io/hdl/pkg/nextpnr:ice40
steps:
- uses: actions/checkout@v4
- name: 下载综合产物
uses: actions/download-artifact@v4
with:
name: synthesis-outputs
path: build/
- name: nextpnr 布局布线 + 时序分析
id: timing
run: |
mkdir -p build
nextpnr-ice40 \
--hx8k \
--package ct256 \
--json build/design.json \
--pcf constraints/hx8k.pcf \
--asc build/design.asc \
--freq 50 \
--placer heap \
--seed 42 \
2>&1 | tee build/timing.log
# 提取 Fmax 和 Slack
FMAX=$(grep "Max frequency" build/timing.log | \
grep -oP '\d+\.\d+(?= MHz)' | head -1)
SLACK=$(grep "Slack:" build/timing.log | \
grep -oP '[+-]?\d+\.\d+(?= ns)' | head -1)
PASS_FAIL=$(grep "Max frequency" build/timing.log | \
grep -oP 'PASS|FAIL' | head -1)
echo "fmax=$FMAX" >> $GITHUB_OUTPUT
echo "slack=$SLACK" >> $GITHUB_OUTPUT
echo "result=$PASS_FAIL" >> $GITHUB_OUTPUT
echo "### 时序分析结果" >> $GITHUB_STEP_SUMMARY
echo "| 指标 | 值 |" >> $GITHUB_STEP_SUMMARY
echo "|------|-----|" >> $GITHUB_STEP_SUMMARY
echo "| 目标频率 | 50 MHz |" >> $GITHUB_STEP_SUMMARY
echo "| 实测 Fmax | ${FMAX} MHz |" >> $GITHUB_STEP_SUMMARY
echo "| Slack | ${SLACK} ns |" >> $GITHUB_STEP_SUMMARY
echo "| 结果 | ${PASS_FAIL} |" >> $GITHUB_STEP_SUMMARY
# 时序不达标则 fail
if [ "$PASS_FAIL" = "FAIL" ]; then
echo "❌ 时序违例:Fmax = ${FMAX} MHz(目标 50 MHz),Slack = ${SLACK} ns"
exit 1
fi
echo "✅ 时序通过:Fmax = ${FMAX} MHz,Slack = +${SLACK} ns"
- name: 上传 bitstream
uses: actions/upload-artifact@v4
if: success()
with:
name: bitstream
path: build/design.asc
outputs:
fmax: ${{ steps.timing.outputs.fmax }}
slack: ${{ steps.timing.outputs.slack }}
result: ${{ steps.timing.outputs.result }}
# ─────────────────────────────────────────
# Job 5: PR Comment(汇总报告)
# ─────────────────────────────────────────
report:
name: "PR 报告"
needs: [synthesis, simulation, timing]
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && always()
steps:
- name: 生成 PR Comment
uses: actions/github-script@v7
with:
script: |
const synth_lut = '${{ needs.synthesis.outputs.lut }}' || 'N/A';
const synth_ff = '${{ needs.synthesis.outputs.ff }}' || 'N/A';
const synth_bram = '${{ needs.synthesis.outputs.bram }}' || 'N/A';
const fmax = '${{ needs.timing.outputs.fmax }}' || 'N/A';
const slack = '${{ needs.timing.outputs.slack }}' || 'N/A';
const timing_ok = '${{ needs.timing.outputs.result }}' === 'PASS';
const sim_ok = '${{ needs.simulation.result }}' === 'success';
const badge_timing = timing_ok ?
'' :
'';
const badge_sim = sim_ok ?
'' :
'';
const body = `## 🔧 FPGA CI 报告
${badge_timing} ${badge_sim}
### 综合资源占用(iCE40HX8K)
| 资源 | 本次 | 总量 | 利用率 |
|------|------|------|--------|
| LUT4 | ${synth_lut} | 7680 | ~${Math.round(synth_lut/7680*100)}% |
| FF | ${synth_ff} | 7680 | ~${Math.round(synth_ff/7680*100)}% |
| BRAM | ${synth_bram} | 32 | - |
### 时序分析(目标 50 MHz)
| 指标 | 值 |
|------|-----|
| 最大频率 Fmax | **${fmax} MHz** |
| Slack | ${slack} ns |
| 结论 | ${timing_ok ? '✅ PASS' : '❌ FAIL(需要修复时序)'} |
> 使用 \`--seed 42\` 固定随机种子,保证 CI 结果稳定。
`;
// 查找已有的 CI comment,有则更新,无则新建
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.find(c =>
c.body.includes('🔧 FPGA CI 报告') &&
c.user.type === 'Bot'
);
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}
4. Yosys 报告解析脚本
#!/bin/bash
# parse_synth_report.sh — 从 Yosys 输出解析资源占用
LOG="$1" # Yosys 综合日志路径
# 提取各种单元数量
parse_cell() {
local cell_name="$1"
grep "$cell_name" "$LOG" | grep -oP '\s+\d+' | tr -d ' ' | tail -1
}
LUT4=$(parse_cell "SB_LUT4")
DFF=$(parse_cell "SB_DFF$")
DFFE=$(parse_cell "SB_DFFE")
BRAM=$(parse_cell "SB_RAM40_4K")
DSP=$(parse_cell "SB_MAC16")
PLL=$(parse_cell "SB_PLL40")
TOTAL_FF=$((${DFF:-0} + ${DFFE:-0}))
echo "==========================="
echo " Yosys 综合资源报告"
echo "==========================="
printf " %-12s %6s / %-6s (%.1f%%)\n" "LUT4" "${LUT4:-0}" "7680" \
"$(echo "scale=1; ${LUT4:-0} * 100 / 7680" | bc)"
printf " %-12s %6s / %-6s (%.1f%%)\n" "FF" "$TOTAL_FF" "7680" \
"$(echo "scale=1; $TOTAL_FF * 100 / 7680" | bc)"
printf " %-12s %6s / %-6s\n" "BRAM" "${BRAM:-0}" "32"
printf " %-12s %6s / %-6s\n" "DSP" "${DSP:-0}" "8"
printf " %-12s %6s / %-6s\n" "PLL" "${PLL:-0}" "2"
echo "==========================="
# parse_timing_report.sh — 从 nextpnr 输出解析时序
LOG="$1"
TARGET_MHZ="${2:-50}"
FMAX=$(grep "Max frequency" "$LOG" | grep -oP '\d+\.\d+(?= MHz)' | head -1)
SLACK=$(grep "Slack:" "$LOG" | grep -oP '[+-]?\d+\.\d+(?= ns)' | head -1)
CRIT_PATH=$(grep "Info: curr total" -A 20 "$LOG" | tail -15)
echo "==========================="
echo " nextpnr 时序分析报告"
echo "==========================="
echo " 目标频率: ${TARGET_MHZ} MHz"
echo " 实测 Fmax: ${FMAX} MHz"
echo " Slack: ${SLACK} ns"
if grep -q "PASS at ${TARGET_MHZ}" "$LOG"; then
echo " 结论: ✅ PASS"
exit 0
else
echo " 结论: ❌ FAIL(时序违例)"
echo ""
echo " 关键路径(最后 15 行):"
echo "$CRIT_PATH"
exit 1
fi
5. Badge 生成
在 README.md 中嵌入自动更新的 badge:
<!-- README.md -->
# My FPGA Project
[](https://github.com/your-org/your-repo/actions/workflows/fpga-ci.yml)
<!-- 动态 badge(需要 shields.io 从 gist 读取) -->



在 CI 中更新 badge 数据(写入 GitHub Gist):
# 在 report job 中添加:
- name: 更新 Badge(写入 Gist)
uses: schneegans/dynamic-badges-action@v1.7.0
with:
auth: ${{ secrets.GIST_TOKEN }}
gistID: YOUR_GIST_ID
filename: timing-badge.json
label: timing
message: "${{ needs.timing.outputs.fmax }} MHz"
color: ${{ needs.timing.outputs.result == 'PASS' && 'brightgreen' || 'red' }}
6. 成本分析
GitHub Actions 免费额度(2024):
公开仓库: 无限制(免费)
私有仓库: 2000 分钟/月(免费 tier)
典型 FPGA CI 任务耗时:
Job | 典型耗时 | 说明
────────────┼──────────┼──────────────────────
lint | ~1 min | iverilog + verilator
synthesis | ~3 min | Yosys synth_ice40(~500 LUT)
simulation | ~5 min | cocotb + iverilog(10 个测试)
timing | ~5 min | nextpnr(iCE40HX8K)
report | ~1 min | GitHub Script
────────────┼──────────┼──────────────────────
每次 PR 合计 | ~15 min |
私有仓库每月 2000 分钟 = 133 次 PR/month
对于个人项目或小团队,完全够用。
如果需要更多分钟:
- 用自托管 Runner(自己的服务器,时间不计入 GitHub 额度)
- 升级 GitHub Teams:每用户 3000 分钟/月
7. 三大避坑
🚧 避坑 #1:Docker image 体积
OSS CAD Suite 解压后约 3.5 GB。如果每次 CI 都从头下载,单次 CI 光下载就要 5 分钟,成本翻倍。解决方案: (1) 把 OSS CAD Suite 打包进自定义 Docker 镜像,推送到 GitHub Container Registry(ghcr.io),用
image:字段直接引用(GitHub Actions 会缓存 layer); (2) 或者用actions/cache缓存/opt/oss-cad-suite目录(按版本号做 cache key)。 推荐方案 (1):一次构建,到处复用,CI 环境稳定可复现。
🚧 避坑 #2:nextpnr seed 不固定导致 CI flaky
nextpnr 的模拟退火放置算法是随机的。不固定
--seed,同样的代码不同次运行可能得到不同的时序结果:今天 PASS,明天 FAIL(纯因为运气不同,代码没变)。这会导致 “flaky CI”——开发者不相信 CI 结果,开始习惯性地重跑。解决:永远在 CI 中加--seed 42(或任意固定整数),保证结果确定性。本地开发时可以不加 seed(让工具自己探索最优);CI 用固定 seed 保证稳定。
🚧 避坑 #3:仿真超时设置
cocotb 测试如果有无限循环(比如等待一个永远不会来的信号),会挂住 CI,耗尽 runner 时间配额,直到 GitHub 强制终止(默认 6 小时)。两个措施: (1) 在 Makefile 的
make命令前加timeout 300(5 分钟超时),超时则返回 exit code 124; (2) 在 cocotb 测试里加@cocotb.test(timeout_time=1000, timeout_unit='ns')装饰器,给每个测试设置最大仿真时间。 不要假设仿真测试”一定会结束”。
8. 自托管 Runner(本地跑更快)
# 修改 runs-on 使用本地 Runner
jobs:
synthesis:
runs-on: self-hosted # 用你自己的 Mac mini 或 Linux 服务器
# 其余不变
# 在本地机器上安装 GitHub Actions Runner
# (适合有 Mac mini 或 NAS 的人,Kevin 你有 Mac mini ✓)
mkdir actions-runner && cd actions-runner
curl -o actions-runner-osx-arm64.tar.gz -L \
https://github.com/actions/runner/releases/download/v2.317.0/actions-runner-osx-arm64-2.317.0.tar.gz
tar xzf actions-runner-osx-arm64.tar.gz
# 注册到你的 GitHub 仓库(需要 PAT Token)
./config.sh --url https://github.com/your-org/your-repo \
--token YOUR_TOKEN
# 启动 Runner(后台运行)
./run.sh &
自托管 Runner 的好处:
- 不计入 GitHub 的分钟额度(完全免费)
- Mac mini 的综合速度比 GitHub 的 Ubuntu runner 快 2-3 倍
- 可以访问本地开发板做硬件在环测试
9. 验证步骤
# 1. Fork 或创建测试仓库
git init my-fpga-ci-test
cd my-fpga-ci-test
# 2. 复制 src/ 和 constraints/(从第 09 篇的 MicroLED 项目)
mkdir src constraints tests .github/workflows
# 3. 放置 workflow 文件
cp fpga-ci.yml .github/workflows/
# 4. 初次推送,触发 CI
git add .
git commit -m "feat: add FPGA CI/CD pipeline"
git push origin main
# 5. 在 GitHub Actions 页面观察各 job 执行
# https://github.com/your-org/your-repo/actions
# 6. 创建一个破坏时序的 PR,验证 CI 能 fail
# 在 pwm_gen16.v 里加一个不必要的大组合逻辑链
# → PR 应该被 timing job FAIL 阻止
# 7. 验证 PR comment 自动生成
# PR 页面应看到 "🔧 FPGA CI 报告" comment,包含资源和时序表格
10. 系列回顾:10 篇解决了什么问题
《开源 FPGA 实战》系列走完了。下面是一张完整的回顾表:
| # | 主题 | 核心问题 | 关键工具/技术 | 交付物 |
|---|---|---|---|---|
| 01 | iCEstick 开箱点灯 | 开源工具链第一步怎么走 | Yosys + nextpnr-ice40 + iceprog | 点灯 + UART Hello |
| 02 | 时序收敛实战 | Slack 为负怎么修 | nextpnr 约束、关键路径分析 | 从 FAIL 到 PASS 的完整流程 |
| 03 | 形式验证 | 怎么证明 RTL 永不出错 | SymbiYosys + SMT 求解器 | FIFO 不溢出的形式证明 |
| 04 | LiteX SoC 深潜 | 怎么用 Python 搭一个 CPU SoC | LiteX + VexRiscv + DDR3 | 跑 Linux 的软核 SoC |
| 05 | cocotb 仿真 | 怎么用 Python 写测试台 | cocotb + iverilog/verilator | 替代 Verilog testbench 的 Python 测试框架 |
| 06 | ECP5 + ULX3S | 中大规模 FPGA 的外设集成 | TMDS encoder + SD SPI + ESP32 | HDMI + SD 卡 + ESP32 同时运行 |
| 07 | 高云 Tang Nano 9K | 国产 FPGA 开源工具链 | apicula + nextpnr-gowin + OSER10 | 国产 FPGA 开源流程全通 |
| 08 | Bambu HLS | C→RTL 不花 License 费 | Bambu HLS + Vitis HLS 对比 | FIR 滤波器 C→Verilog→iCE40 |
| 09 | MicroLED 控制器 | FPGA 原型验证真实工程问题 | SPI + 16×PWM + I2C + 故障检测 | ~500 LUT MicroLED 驱动原型 |
| 10 | CI/CD 流水线 | 怎么防止修改破坏时序 | GitHub Actions + Docker | PR 自动综合+仿真+时序报告 |
11. 后续学习路线
走完这 10 篇,你已经能用完整的开源工具链做真实的 FPGA 项目了。下面是几条值得继续探索的方向:
A. 更大的 FPGA:Xilinx UltraScale+
iCE40 和 ECP5 是入门器件。真实的高性能应用(HBM AI 推理、100G 网络、PCIe Gen5)需要 Xilinx/AMD 的 UltraScale+。
开源工具对 Xilinx 的支持还很有限,但:
nextpnr-xilinx(实验阶段,7 系列)- F4PGA / SymbiFlow(Xilinx 7 系列开源流,Google 赞助,可用于 Arty A7)
- Vivado 的免费版(WebPack)可以跑 Artix-7,功能有限但够用
B. RISC-V SoC 开发
第 04 篇用 LiteX + VexRiscv 搭了基础 SoC。进阶方向:
- CVA6(Ariane):更完整的 RV64GC 核,有 MMU,能跑 Linux
- BOOM(Berkeley Out-of-Order Machine):乱序执行,性能接近商业核
- Rocket Chip:可配置 RISC-V 生成器,学术届用的最广
- OpenTitan:Google 开源的安全 SoC(带 Root-of-Trust)
C. ASIC 流片
有了 FPGA 原型(第 09 篇),下一步可以考虑真实流片:
- Efabless MPW:Sky130 工艺,开源项目免费,用 OpenROAD + Magic
- IHP SG13G2:130nm BiCMOS,IHP(德国研究所)开放 PDK
- TSMC/SMIC MPW:通过中介(efabless、imec)拼片,约 $5K-20K
- 工具链:OpenROAD(综合+布局布线)→ Magic(DRC/LVS)→ Klayout(GDS)
D. 形式化验证进阶
第 03 篇介绍了 SymbiYosys 基础。进阶方向:
- JasperGold / Questa Formal:商业形式验证工具(学习用途有学术授权)
- CBMC / ESBMC:C 语言形式验证(配合 HLS 用)
- TLA+:系统级协议验证(适合 SoC 总线协议)
参考资料
| 资源 | 链接 / 说明 |
|---|---|
| hdl/containers(预构建 Docker 镜像) | hdl/containers,包含 yosys/nextpnr/cocotb |
| OSS CAD Suite | YosysHQ/oss-cad-suite-build |
| GitHub Actions 文档 | docs.github.com/actions |
| nextpnr —seed 说明 | nextpnr README,“Seed and Reproducibility” 章节 |
| cocotb timeout 装饰器 | cocotb.readthedocs.io/en/stable/triggers.html |
| F4PGA(Xilinx 7 系列开源 P&R) | f4pga.org,Arty A7 支持 |
| CVA6 RISC-V 核 | openhwgroup/cva6 |
| Efabless OpenMPW | efabless.com/open_shuttle_program |
| OpenROAD ASIC 工具链 | openroad.readthedocs.io |
| EnricoMi/publish-unit-test-result-action | GitHub Actions 测试报告发布 Action |
系列完结。感谢跟读到这里的每一位。硬件开源时代已经来临——工具是免费的,器件是买得到的,社区是开放的。去做些有趣的东西吧。🦞