可重构仪器设计文件编写指南
最后更新:2026-08-17
面向对象:从未接触过可重构仪器的人或 AI。本文只讲"怎么写一份能编译通过的设计文件",不涉及硬件原理。
一个设计文件就是把两样东西写出来:
modules):像搭积木一样选模块、命名、可选地指定版本和物理槽位。connections):把一个模块的输出端口接到另一个模块的输入端口。一个最小的、能编译的文件只有这么长:
modules:
- name: dds1
type: DDS
version: null
parameters: {}
- name: osc1
type: Oscilloscope
version: null
parameters: {}
connections:
- from: dds1.sin_out
to: osc1.in0
文件必须是 UTF-8 编码的 YAML。顶层只有两个键,两个都必须存在:
modules: [...] # 模块清单(数组)——可以为空 [],空则仅生成基础设施(无仪器模块)
connections: [...] # 连接清单(数组)——可以为空 []
modules 是数组,每个元素是一个模块条目。connections 是数组,每个元素是 {from: 模块.端口, to: 模块.端口}。connections 可以写 [];modules 也可以写 [](此时只生成传输层/时钟等基础设施,不含任何仪器模块)。modules 数组的每个元素)- name: adc1 # 必填:实例名,全文件唯一
type: AD402x # 必填:模块类型,必须是第 4 节表中的 type
version: null # 必填键:null 表示用最新版;写具体版本号表示锁定版本
parameters: {} # 必填键:模块参数,不填参数就写空对象 {}
phy_slot: SB1 # 可选:物理槽位,纯逻辑模块不需要
ports: [...] # 可省略、会被忽略(见 2.6)
| 字段 | 是否必需 | 规则 |
|---|---|---|
name | 必需 | 实例名。建议只用小写字母、数字、下划线(会变成 C 标识符和 TCL 名字,不能有连字符/空格/点)。全文件内唯一 |
type | 必需 | 必须与第 4 节表中的 type 完全一致(大小写敏感)。写了表里没有的类型 → 编译报错 |
version | 键必须存在 | 值写 null(或空字符串)→ 自动用仓库最新版本;写 "0.2.1" 这类点分数字字符串 → 锁定该版本。注意:version: 这个键本身不能删,删了模块会被静默跳过(控制台打印 import failed);若该模块还在 connections 里被引用,则整个编译直接失败 |
parameters | 键必须存在 | 不传参数就写 {}。传了模块不认识的参数名 → 编译报错(见 2.4) |
phy_slot | 可选 | 物理槽位,见第 5 节。纯逻辑模块写不写都会被忽略 |
ports | 可省略 | 编译器导入时完全忽略这个字段,端口一律由模块类型决定(见 3.1)。不写、少写、写错都不影响编译,可以不写 |
version 取值示例- name: dds1
type: DDS
version: null # 用最新版(推荐,最省心)
- name: dds2
type: DDS
version: "1.2.1" # 锁定 1.2.1(必须是在仓库里存在的版本,否则报错)
version: null里的null是 YAML 的空值字面量,不是字符串。也可以写version: ""或直接version:(留空)。
parameters 取值示例- name: osc1
type: Oscilloscope
parameters:
n_channels: 4 # 通道数
addr_width: 12 # 采集深度(寻址位宽)
- name: scan1
type: ScanController
parameters:
n_channels: 2
max_npoints: 4096
哪些模块有哪些参数,见第 4 节表。规则:
parameters 只能写 {},多写一个键就报错。n_channels 对 Oscilloscope / ScanController / AWG(如用其 setter)必须是 2 的幂(1、2、4、8……),否则报错。DigiMux 的 n_channels 可以是任意正整数(1、2、3……都可以)。phy_slot 取值规则SB1 ~ SB8。ADDA_HF_AXI)→ J2 或 J3。SB9)不会报错,但该模块的物理引脚不会被连接——属于"静默失败",要小心。ports模块的真实端口是由模块类型决定的(在 PL IP 里定义),设计文件里的 ports 字段导入时被忽略。编译器在导出 Outputs/design.yaml 时会自己重新生成完整端口信息。所以:
ports(包括 test_design.yaml 里那些 ports 都是历史产物,不是必需的)。connections 里能连哪些端口,取决于第 4 节表中该类型的端口,而不是你在 ports 里写了什么。connections)端口是每个模块类型的固定属性(见第 4 节表)。连接时只能引用表中列出的端口名,写成 模块实例名.端口名。
一条连接必须把一个【输出】端口和一个【输入】端口连在一起:
connections:
- from: dds1.sin_out # 输出端口
to: osc1.in0 # 输入端口
from / to 谁在前不影响结果(写成 from: osc1.in0, to: dds1.sin_out 同样有效,导出时会归一化为"输出在前")。建议仍按"输出在前、输入在后"书写,便于阅读。target port is not an out port)。target is already driven。from / to 的值必须含一个 .(模块.端口)。格式不对 → 编译失败。modules 里定义过。.(按第一个 . 分割)。端口方向:→表示输出(可作为from),←表示输入(可作为to)。
"物理槽位"列为空的 = 纯逻辑模块,无phy_slot。
| type | 输出端口(→) | 输入端口(←) | 物理槽位 | parameters |
|---|---|---|---|---|
AD402x | dataout | — | SB1~SB8 | 无 |
AD4005 | dataout | — | SB1~SB8 | 无 |
AD5791 | — | datain | SB1~SB8 | 无 |
AD5541 | — | datain | SB1~SB8 | 无 |
ADDA_HF_AXI | adc_in | dac_out | J2 / J3 | 无 |
DigiIOHF | digital_in | digital_out | SB1~SB8 | 无 |
PhaseAnalyzer | phase_out freq_out locked clk_out | — | SB1~SB8 | 无 |
PulseCounter | — | pulse_in | 无 | 无 |
DigiMux | sig_out | sig_in0 … sig_in{n-1} | 无 | n_channels(默认 2,任意正整数) |
DDS | phase_out sin_out cos_out signal_out trigger_out | freq_ext amplitude_ext offset_ext trigger_in | 无 | 无 |
LockIn_HF | x_data y_data r_data p_data | ref_phase signal_in | 无 | 无 |
PID | channel_y | channel_x | 无 | 无 |
LowPass | dataout | datain | 无 | 无 |
Oscilloscope | — | in0 … in{n-1} | 无 | n_channels(默认 2,须 2 的幂)、addr_width(默认 10) |
ScanController | motiv | in0 … in{n-1} | 无 | n_channels(默认 1,须 2 的幂)、max_npoints(默认 1024) |
AWG | waveform_out | trigger_in | 无 | 无(波形缓冲固定 1024 点) |
补充说明:
n(如 sig_in{n-1}、in{n-1})由 parameters.n_channels 决定,端口名从 0 开始编号。AD402x / AD4005 / AD5791 / AD5541 / PID / LowPass)内部还有一个 trig 输入端口,由编译器自动接到 1MHz 时钟,不能也不需要在设计文件里连接。PhaseAnalyzer 虽有 clk_out 等输出端口,但它本身不带输入端口——它常作为信号源接给别的模块。phy_slot)槽位是硬件板卡上的物理插口,编译器据此把模块的引脚映射到 FPGA 的物理引脚:
| 槽位 | 位置 | 适用模块 |
|---|---|---|
SB1 ~ SB8 | SPI 母板 | AD402x、AD4005、AD5791、AD5541、DigiIOHF、PhaseAnalyzer |
J2、J3 | 高速子卡 | ADDA_HF_AXI |
phy_slot。# 一个带物理 IO 的闭环:AD402x 采样 → PID 处理 → AD5791 输出
modules:
- name: adc1
type: AD402x
version: null
parameters: {}
phy_slot: SB1
- name: pid1
type: PID
version: null
parameters: {}
- name: dac1
type: AD5791
version: null
parameters: {}
phy_slot: SB2
connections:
- from: adc1.dataout
to: pid1.channel_x
- from: pid1.channel_y
to: dac1.datain
modules:
- name: dds1
type: DDS
version: null
parameters: {}
- name: lockin1
type: LockIn_HF
version: null
parameters: {}
- name: osc1
type: Oscilloscope
version: null
parameters:
n_channels: 4
addr_width: 12
connections:
- from: dds1.sin_out
to: lockin1.signal_in
- from: dds1.cos_out
to: lockin1.ref_phase
- from: lockin1.x_data
to: osc1.in0
- from: lockin1.y_data
to: osc1.in1
modules:
- name: ddfa1
type: ADDA_HF_AXI
version: null
parameters: {}
phy_slot: J2
- name: scan1
type: ScanController
version: null
parameters:
n_channels: 2
max_npoints: 4096
- name: dds1
type: DDS
version: null
parameters: {}
connections:
- from: ddfa1.adc_in
to: scan1.in0
- from: dds1.sin_out
to: ddfa1.dac_out
| 症状 | 原因 | 解决 |
|---|---|---|
控制台打印 ... import failed 且该模块没进设计 | 模块条目的 version 或 parameters 键缺失;或类型名写错 | 补上 version: null 和 parameters: {};核对 type 与表一致。若该模块被 connections 引用,后续还会报 Module or port not found |
报错 Module or port not found in connection ... | connections 里引用了不存在的模块或端口;或该模块因 version/parameters 键缺失被跳过 | 核对实例名、类型端口名(第 4 节表);检查被引用模块是否真的加载成功 |
报错 target port is not an out port | 一条连接的两个端口是同一类(两个都是输出,或两个都是输入) | 保证每条连接是"一个输出 + 一个输入" |
报错 target is already driven | 两个输出同时接同一个输入 | 让每个输入只保留一个 from |
报错 n_channels must be power of 2 | Oscilloscope / ScanController 的 n_channels 不是 2 的幂 | 改为 1、2、4、8…… |
报错 Version ... not found in repository | version 写的版本在仓库里不存在 | 改成 null 用最新版,或改成仓库里存在的版本 |
| 编译通过,但某模块没有物理引脚 | phy_slot 填了不存在的槽位(如 SB9) | 核对槽位:SPI 类 SB1~SB8,ADDA_HF 用 J2/J3 |
version 想省略不写 | 不行 | 必须写 version: 键(值可为 null) |