类似myhdl的Python转HDL
参考
Vitis HLS 例子
myhdl文档
官方文档
在线 Playground
GitHub
Verilog规则和常用模板
1. 背景:硬件描述语言的困境
传统的硬件描述语言------Verilog 和 VHDL------诞生于上世纪 80 年代,其核心设计目标是"仿真"而非"综合"。几十年下来,工程师们积累了大量"综合安全子集"的经验,形成了各种 lint 规则和编码规范,本质上是在用外力约束语言本身的危险特性。
这带来了几个长期痛点:
- 意外生成 latch(忘记在 always 块中给信号赋默认值)
- 阻塞赋值(
=)和非阻塞赋值(<=)的混用陷阱 - 代码复用困难,参数化模块写起来极为繁琐
- 测试基础设施薄弱,与现代软件工程工具链脱节
Amaranth HDL(前身是 nMigen)正是为了解决这些问题而生的。
2. Amaranth 是什么
Amaranth 是一个以 Python 为宿主语言的硬件描述语言(HDL),由 whitequark 主导开发,以两条款 BSD 协议开源。它不是把 Python 编译成硬件,而是用 Python 的类和对象来构建一棵描述数字逻辑的 AST(抽象语法树),再由工具链将其转换为 Verilog-2001,最终送入各家 FPGA 的综合器。
整个工具链由四个部分组成:
Amaranth 语言 → 标准库 → 仿真器 → 构建系统
- 语言:Python 库,提供 Signal、Module、ClockDomain 等核心原语
- 标准库:FIFO、仲裁器、流接口等常用组件
- 仿真器:纯 Python 实现,事件驱动,支持多时钟域
- 构建系统:对接 Yosys+nextpnr 等 FOSS 工具链,以及 Lattice、Xilinx 等厂商工具
3. 在线 Playground:零门槛上手
官方提供了一个基于 WebAssembly 的在线 Playground,在浏览器里就能运行完整的 Amaranth 代码------包括仿真。它的技术底座是把 Yosys 编译为 WASM,再通过 wasmtime-py 驱动,做到了真正的本地运算,不需要后端服务器。
这对学习和快速验证逻辑非常有价值。打开页面即可编写代码、运行仿真、观察波形,不需要安装任何东西。
4. 核心概念详解
4.1 Signal:比特的容器
python
from amaranth.hdl import *
# 4 位无符号信号,复位值为 0
counter = Signal(4)
# 1 位信号(默认)
enable = Signal()
# 有符号信号
offset = Signal(shape=signed(8))
Signal 是 Amaranth 的基本数据单元,对应 Verilog 里的 wire/reg。它的位宽和符号性在构造时确定,后续操作会自动进行类型推断。
4.2 Module:逻辑的容器
python
m = Module()
# 组合逻辑(对应 always @(*) 或 assign)
with m.If(enable):
m.d.comb += output.eq(a & b)
with m.Else():
m.d.comb += output.eq(0)
# 时序逻辑(对应 always @(posedge clk))
with m.If(enable):
m.d.sync += counter.eq(counter + 1)
这里有一个非常重要的设计决策:Amaranth 里组合逻辑和时序逻辑使用不同的赋值域 (m.d.comb 和 m.d.sync),从语法层面消除了 Verilog 里阻塞/非阻塞赋值混用的可能性。
4.3 Elaboratable:可综合的模块单元
Amaranth 中每个可综合的设计单元都是一个实现了 elaborate() 方法的类:
python
class Counter(Elaboratable):
def __init__(self, width=8):
self.width = width
# 端口定义在 __init__ 里
self.en = Signal()
self.count = Signal(width)
def elaborate(self, platform):
m = Module()
with m.If(self.en):
m.d.sync += self.count.eq(self.count + 1)
return m
这种面向对象的设计带来了软件工程里习以为常的好处:参数化、继承、组合------都是原生 Python 的能力,不需要任何特殊语法扩展。
4.4 FSM:有限状态机
Amaranth 对 FSM 有一流的原生支持:
python
with m.FSM():
with m.State("IDLE"):
with m.If(start):
m.next = "RUNNING"
with m.State("RUNNING"):
m.d.sync += data.eq(data + 1)
with m.If(done):
m.next = "IDLE"
不需要手动管理状态编码,工具链会自动处理。
4.5 时钟域
python
# 创建名为 "fast" 的时钟域
m.domains.fast = ClockDomain("fast")
# 在 fast 域中做时序逻辑
m.d.fast += reg.eq(value)
多时钟域是 Amaranth 的一等公民,跨域信号可以用标准库里的同步器处理。
5. 一个完整示例:4 位环形计数器
python
from amaranth.hdl import *
from amaranth.sim import *
class RingCounter(Elaboratable):
"""4 位环形计数器,输出始终只有一位为高"""
def __init__(self):
self.en = Signal() # 使能
self.out = Signal(4, reset=1) # 输出,复位后第 0 位为 1
def elaborate(self, platform):
m = Module()
with m.If(self.en):
# 循环左移
m.d.sync += self.out.eq(
self.out[1:] | (self.out[0] << 3)
)
return m
# 仿真
dut = RingCounter()
sim = Simulator(dut)
def bench():
yield dut.en.eq(1)
for _ in range(8):
yield
val = yield dut.out
print(f"out = {val:04b}")
sim.add_clock(1e-6) # 1 MHz
sim.add_sync_process(bench)
with sim.write_vcd("ring.vcd"):
sim.run()
运行后输出:
out = 0010
out = 0100
out = 1000
out = 0001
out = 0010
out = 0100
out = 1000
out = 0001
仿真产生的 .vcd 文件可以用 GTKWave 打开查看波形。
6. 与 Verilog 互操作
Amaranth 不要求你推翻现有代码库。你可以把 Verilog 模块包装进来:
python
from amaranth.hdl.ir import Instance
# 引用一个已有的 Verilog 模块
uart_core = Instance("uart_top",
i_clk=ClockSignal(),
i_rst=ResetSignal(),
i_tx_data=tx_data,
o_rx_data=rx_data,
)
m.submodules.uart = uart_core
反过来,Amaranth 设计也可以导出为 Verilog 接入已有的 Verilog/SystemVerilog 流程:
python
from amaranth.back.verilog import convert
verilog_src = convert(Counter(), ports=[counter.en, counter.count])
7. 构建与上板
以 Lattice iCE40 为例(使用 iCEBreaker 开发板):
python
from amaranth_boards.icebreaker import ICEBreakerPlatform
class Blinky(Elaboratable):
def elaborate(self, platform):
m = Module()
led = platform.request("led", 0)
timer = Signal(24)
m.d.sync += timer.eq(timer + 1)
m.d.comb += led.o.eq(timer[-1])
return m
ICEBreakerPlatform().build(Blinky(), do_program=True)
一行 build() 调用会依次完成:生成 Verilog → Yosys 综合 → nextpnr 布局布线 → icepack 打包 → iceprog 下载。
支持的 FPGA 家族涵盖:Lattice iCE40、ECP5、Nexus、MachXO 系列,AMD/Xilinx Virtex/Spartan 系列,Intel/Altera,以及 Quicklogic EOS S3 等。
8. 安装
bash
pip install amaranth amaranth-boards
# 需要系统安装 Yosys 和 nextpnr(或使用 WASM 版本)
WASM 版本的 Yosys 通过 amaranth-yosys 包提供,无需本地安装即可在 Python 环境中使用综合功能,这也是 Playground 的技术基础。
9. 设计哲学
Amaranth 的核心设计原则可以归结为一句话:让正确的代码容易写,让错误的代码难以写。
具体体现在:
- 组合逻辑自动有默认值,不会产生意外 latch
- 时序逻辑强制使用非阻塞语义,赋值域隔离
- 所有运算符的位宽规则与 Python 整数语义一致,没有 Verilog 那样的"截断陷阱"
- 模块化和参数化直接复用 Python 的类系统,无需学习额外语法
代价是:相比 Verilog,Amaranth 的仿真速度较慢(纯 Python 实现),对于性能敏感的大规模仿真,仍建议导出 Verilog 后接入 Verilator 或 Icarus。