Board Model Module
The adidt.model package provides the unified BoardModel abstraction
that both the manual board-class workflow and the XSA pipeline produce.
A single BoardModelRenderer renders any BoardModel to DTS using
per-component Jinja2 templates.
Overview
A BoardModel describes the complete hardware composition of a board:
what components exist, how they connect via JESD204 links, and what FPGA
configuration applies. Three workflows converge on this model:
XSA Pipeline Manual Board Class Direct Construction
───────────── ────────────────── ────────────────────
XsaPipeline.run() daq2.to_board_model(cfg) BoardModel(name=...,
└─ AD9081Builder ad9081_fmc components=[...],
.build_model() .to_board_model(cfg) jesd_links=[...])
│ │ │
└────────────────────────┼────────────────────────┘
▼
BoardModel
│
BoardModelRenderer
.render()
│
▼
dict[str, list[str]]
(DTS node strings)
│
DtsMerger
.merge()
│
▼
.dts / .dtso files
The model is editable after construction. You can modify component configs, JESD parameters, or metadata before rendering:
from adidt.xsa.builders.fmcdaq2 import FMCDAQ2Builder
from adidt.model.renderer import BoardModelRenderer
# Build from XSA topology
model = FMCDAQ2Builder().build_model(topology, cfg, "zynqmp_clk", 71, "gpio")
# Edit before rendering
clock = model.get_component("clock")
clock.config["vcxo_hz"] = 100_000_000
# Render to DTS
nodes = BoardModelRenderer().render(model)
Supported boards
All six XSA builders produce BoardModel instances:
Builder |
Clock Chip |
Converters |
|---|---|---|
|
AD9523-1 |
AD9680 (ADC) + AD9144 (DAC) |
|
AD9528 |
AD9680 (ADC) + AD9152 (DAC) |
|
HMC7044 |
AD9172 (DAC) |
|
HMC7044 |
AD9081 MxFE (ADC + DAC) |
|
AD9528 |
ADRV9009 (transceiver, RX + TX + ORX) |
|
HMC7044 + ADF4382 |
AD9084 (RX transceiver) |
Board classes with to_board_model():
adidt.boards.daq2(ZCU102, ZC706)
Component factories
The easiest way to create components. Each factory returns a
pre-configured ComponentModel — no template filenames needed:
from adidt.model import BoardModel, components
model = BoardModel(
name="my_board",
platform="rpi5",
components=[
components.adis16495(spi_bus="spi0", cs=0, interrupt_gpio=25),
],
)
Available factories (from adidt.model import components):
Simple SPI:
components.adis16495— ADIS16495/16497 IMUClock chips:
components.hmc7044,components.ad9523_1,components.ad9528ADCs / DACs:
components.ad9680,components.ad9144,components.ad9152,components.ad9172Transceivers:
components.ad9081,components.ad9084
Context builders
Lower-level functions that produce template context dicts. Use these when you need full control over every field, or when no factory exists for your device:
from adidt.model.contexts import build_ad9523_1_ctx
ctx = build_ad9523_1_ctx(
label="clk0_ad9523",
cs=0,
spi_max_hz=10_000_000,
vcxo_hz=125_000_000,
)
# ctx is ready to pass to ad9523_1.tmpl
API Reference
Board Model
Dataclass definitions for the unified board model.
- class adidt.model.board_model.ComponentModel(role: str, part: str, template: str, spi_bus: str, spi_cs: int, config: dict[str, ~typing.Any]=<factory>)
Bases:
objectOne physical device on the board (clock chip, converter, etc.).
- role
Logical role on the board —
"clock","adc","dac", or"transceiver".- Type:
str
- part
Part number string, e.g.
"ad9523_1","ad9680".- Type:
str
- template
Jinja2 template filename used to render this component, e.g.
"ad9523_1.tmpl".- Type:
str
- spi_bus
SPI bus label, e.g.
"spi0","spi1".- Type:
str
- spi_cs
SPI chip-select index.
- Type:
int
- config
Template context dict — the same dicts that
adidt.model.contextsfunctions produce.- Type:
dict[str, Any]
- role: str
- part: str
- template: str
- spi_bus: str
- spi_cs: int
- config: dict[str, Any]
- class adidt.model.board_model.JesdLinkModel(direction: str, jesd_label: str, xcvr_label: str, core_label: str, dma_label: str | None, link_params: dict[str, int]=<factory>, xcvr_config: dict[str, ~typing.Any]=<factory>, jesd_overlay_config: dict[str, ~typing.Any]=<factory>, tpl_core_config: dict[str, ~typing.Any]=<factory>, dma_clocks_str: str | None = None)
Bases:
objectOne JESD204 link (RX or TX) with its associated FPGA IP labels and config.
- direction
"rx"or"tx".- Type:
str
- jesd_label
AXI JESD204 IP label, e.g.
"axi_ad9680_jesd204_rx".- Type:
str
- xcvr_label
ADXCVR IP label, e.g.
"axi_ad9680_adxcvr".- Type:
str
- core_label
TPL core IP label, e.g.
"axi_ad9680_tpl_adc_tpl_core".- Type:
str
- dma_label
AXI DMA IP label, e.g.
"axi_ad9680_dma".- Type:
str | None
- link_params
JESD framing parameters — keys
F,K,M,L,Np,S.- Type:
dict[str, int]
- xcvr_config
ADXCVR template context dict (for
adxcvr.tmpl).- Type:
dict[str, Any]
- jesd_overlay_config
JESD overlay template context dict (for
jesd204_overlay.tmpl).- Type:
dict[str, Any]
- tpl_core_config
TPL core template context dict (for
tpl_core.tmpl).- Type:
dict[str, Any]
- direction: str
- jesd_label: str
- xcvr_label: str
- core_label: str
- dma_label: str | None
- link_params: dict[str, int]
- xcvr_config: dict[str, Any]
- jesd_overlay_config: dict[str, Any]
- tpl_core_config: dict[str, Any]
- dma_clocks_str: str | None = None
- class adidt.model.board_model.FpgaConfig(platform: str, addr_cells: int, ps_clk_label: str, ps_clk_index: int | None, gpio_label: str)
Bases:
objectPlatform-level FPGA configuration.
- platform
Target platform string, e.g.
"zcu102","vcu118".- Type:
str
- addr_cells
Number of address cells —
1for vcu118/zc706,2for zcu102/vpk180.- Type:
int
- ps_clk_label
Processing-system clock label, e.g.
"zynqmp_clk","clkc".- Type:
str
- ps_clk_index
PS clock index (e.g.
71), orNonewhen the platform has no PS clock index.- Type:
int | None
- gpio_label
GPIO controller label, e.g.
"gpio","gpio0".- Type:
str
- platform: str
- addr_cells: int
- ps_clk_label: str
- ps_clk_index: int | None
- gpio_label: str
- class adidt.model.board_model.BoardModel(name: str, platform: str, components: list[ComponentModel] = <factory>, jesd_links: list[JesdLinkModel] = <factory>, fpga_config: FpgaConfig | None = None, metadata: dict[str, ~typing.Any]=<factory>, extra_nodes: list[str] = <factory>)
Bases:
objectUnified board model that both the manual and XSA workflows produce.
A
BoardModelis an editable snapshot of the complete hardware composition. After construction (from either workflow), callers may inspect and modify components, JESD links, and metadata before passing the model toBoardModelRendererfor DTS rendering.- name
Board/design name, e.g.
"fmcdaq2_zcu102".- Type:
str
- platform
Target platform, e.g.
"zcu102".- Type:
str
- components
Physical devices (clock chips, converters, etc.).
- Type:
list[adidt.model.board_model.ComponentModel]
- jesd_links
JESD204 link definitions (RX and TX).
- Type:
list[adidt.model.board_model.JesdLinkModel]
- fpga_config
Platform-level FPGA configuration.
- Type:
adidt.model.board_model.FpgaConfig | None
- metadata
Free-form dict for rendering metadata —
date,config_source,base_dts_include, etc.- Type:
dict[str, Any]
- name: str
- platform: str
- components: list[ComponentModel]
- jesd_links: list[JesdLinkModel]
- fpga_config: FpgaConfig | None = None
- metadata: dict[str, Any]
- extra_nodes: list[str]
- get_component(role: str) ComponentModel | None
Return the first component matching role, or
None.
- get_components(role: str) list[ComponentModel]
Return all components matching role.
- get_jesd_link(direction: str) JesdLinkModel | None
Return the first JESD link matching direction, or
None.
- get_jesd_links(direction: str) list[JesdLinkModel]
Return all JESD links matching direction.
- to_dts(output_path: str, config_source: str = 'board_model') str
Render this model to a standalone DTS file.
Convenience method that renders via
BoardModelRendererand writes the output with SPDX header and metadata.- Parameters:
output_path – Path to write the DTS file.
config_source – Config source string for the metadata header.
- Returns:
The output_path string.
- to_dict() dict[str, Any]
Serialize the model to a plain dict (JSON-compatible).
Useful for debugging, logging, and sharing configurations.
- classmethod from_dict(data: dict[str, Any]) BoardModel
Deserialize a
BoardModelfrom a dict (as produced byto_dict()).- Parameters:
data – Dict with keys matching
BoardModelfields.- Returns:
A new
BoardModelinstance.
Renderer
Render a BoardModel to DTS node strings using per-component templates.
- class adidt.model.renderer.BoardModelRenderer
Bases:
objectRenders a
BoardModelinto DTS node strings.The output dict has the same shape that
build()returns:{"clkgens": [...], "jesd204_rx": [...], "jesd204_tx": [...], "converters": [...]}
This allows the existing
DtsMergerto consume the output without changes.
Context Builder Functions
Shared template-context builders for board components.
Each function returns a plain dict ready to pass to a Jinja2 template.
These are used by both the XSA pipeline (via board builders) and the
manual board-class workflow (via to_board_model()).
- adidt.model.contexts.fmt_hz(hz: int) str
Format hz as a human-readable frequency string (e.g. ‘245.76 MHz’).
- adidt.model.contexts.coerce_board_int(value: Any, key_path: str) int
Convert value to int; raise ValueError with key_path context on failure.
- adidt.model.contexts.build_ad9523_1_ctx(*, label: str = 'clk0_ad9523', cs: int = 0, spi_max_hz: int = 10000000, vcxo_hz: int = 125000000, gpio_controller: str = 'gpio0', sync_gpio: int | None = None, status0_gpio: int | None = None, status1_gpio: int | None = None, channels: list[dict] | None = None) dict
Build context dict for
ad9523_1.tmpl.- Parameters:
label – DT node label.
cs – SPI chip select.
spi_max_hz – SPI max frequency.
vcxo_hz – VCXO frequency.
gpio_controller – GPIO controller label.
sync_gpio – GPIO index for sync pin, or None to omit.
status0_gpio – GPIO index for status0 pin, or None to omit.
status1_gpio – GPIO index for status1 pin, or None to omit.
channels – List of channel dicts with keys
id,name,divider,freq_str. If None, uses FMCDAQ2 defaults.
- adidt.model.contexts.build_ad9680_ctx(*, label: str = 'adc0_ad9680', cs: int = 2, spi_max_hz: int = 1000000, use_spi_3wire: bool = False, clks_str: str, clk_names_str: str, sampling_frequency_hz: int = 1000000000, rx_m: int = 2, rx_l: int = 4, rx_f: int = 1, rx_k: int = 32, rx_np: int = 16, jesd204_top_device: int = 0, jesd204_link_ids: list[int] | None = None, jesd204_inputs: str = '', gpio_controller: str = 'gpio0', powerdown_gpio: int | None = None, fastdetect_a_gpio: int | None = None, fastdetect_b_gpio: int | None = None) dict
Build context dict for
ad9680.tmpl.
- adidt.model.contexts.build_ad9144_ctx(*, label: str = 'dac0_ad9144', cs: int = 1, spi_max_hz: int = 1000000, clk_ref: str | None = None, jesd204_top_device: int = 1, jesd204_link_ids: list[int] | None = None, jesd204_inputs: str = '', gpio_controller: str = 'gpio0', txen_gpio: int | None = None, reset_gpio: int | None = None, irq_gpio: int | None = None) dict
Build context dict for
ad9144.tmpl.
- adidt.model.contexts.build_adxcvr_ctx(*, label: str, sys_clk_select: int, out_clk_select: int, clk_ref: str | None = None, use_div40: bool = True, div40_clk_ref: str | None = None, clock_output_names_str: str, use_lpm_enable: bool = True, jesd_l: int | None = None, jesd_m: int | None = None, jesd_s: int | None = None, jesd204_inputs: str | None = None, is_rx: bool = True) dict
Build context dict for
adxcvr.tmpl.
- adidt.model.contexts.build_jesd204_overlay_ctx(*, label: str, direction: str, clocks_str: str, clock_names_str: str, clock_output_name: str | None = None, f: int, k: int, jesd204_inputs: str, converter_resolution: int | None = None, converters_per_device: int | None = None, bits_per_sample: int | None = None, control_bits_per_sample: int | None = None) dict
Build context dict for
jesd204_overlay.tmpl.
- adidt.model.contexts.build_tpl_core_ctx(*, label: str, compatible: str, direction: str, dma_label: str | None, spibus_label: str | None = None, jesd_label: str | None = None, jesd_link_offset: int, link_id: int, pl_fifo_enable: bool = False, sampl_clk_ref: str | None = None, sampl_clk_name: str | None = None) dict
Build context dict for
tpl_core.tmpl.
- adidt.model.contexts.fmt_gpi_gpo(controls: list) str
Format a list of int/hex values as a space-separated hex string for DTS.
- adidt.model.contexts.build_hmc7044_channel_ctx(pll2_hz: int, channels_spec: list) list
Pre-compute freq_str for each HMC7044 channel.
- adidt.model.contexts.build_hmc7044_ctx(*, label: str, cs: int, spi_max_hz: int, pll1_clkin_frequencies: list, vcxo_hz: int, pll2_output_hz: int, clock_output_names: list, channels: list[dict] | None, raw_channels: str | None = None, jesd204_sysref_provider: bool = True, jesd204_max_sysref_hz: int = 2000000, pll1_loop_bandwidth_hz=None, pll1_ref_prio_ctrl=None, pll1_ref_autorevert: bool = False, pll1_charge_pump_ua=None, pfd1_max_freq_hz=None, sysref_timer_divider=None, pulse_generator_mode=None, clkin0_buffer_mode=None, clkin1_buffer_mode=None, clkin2_buffer_mode: str | None = None, clkin3_buffer_mode: str | None = None, oscin_buffer_mode=None, gpi_controls=None, gpo_controls=None, sync_pin_mode=None, high_perf_mode_dist_enable: bool = False, clkin0_ref: str | None = None) dict
Build context dict for
hmc7044.tmpl.
- adidt.model.contexts.build_ad9528_ctx(*, label: str = 'clk0_ad9528', cs: int = 0, spi_max_hz: int = 10000000, vcxo_hz: int = 122880000, gpio_lines: list[dict] | None = None, channels: list[dict] | None = None) dict
Build context dict for
ad9528.tmpl.If channels is None, uses FMCDAQ3 default channels.
- adidt.model.contexts.build_ad9528_1_ctx(*, label: str = 'clk0_ad9528', cs: int = 0, spi_max_hz: int = 10000000, vcxo_hz: int = 122880000, gpio_lines: list[dict] | None = None, channels: list[dict] | None = None) dict
Build context dict for
ad9528_1.tmpl(ADRV9009 variant).If channels is None, uses standard ADRV9009 default channels.
- adidt.model.contexts.build_ad9152_ctx(*, label: str = 'dac0_ad9152', cs: int = 1, spi_max_hz: int = 1000000, clk_ref: str | None = None, jesd_link_mode: int = 4, jesd204_top_device: int = 1, jesd204_link_ids: list[int] | None = None, jesd204_inputs: str = '', gpio_controller: str = 'gpio0', txen_gpio: int | None = None, irq_gpio: int | None = None) dict
Build context dict for
ad9152.tmpl.
- adidt.model.contexts.build_ad9172_device_ctx(*, label: str = 'dac0_ad9172', cs: int = 1, spi_max_hz: int = 1000000, clk_ref: str = 'hmc7044 2', dac_rate_khz: int, jesd_link_mode: int, dac_interpolation: int, channel_interpolation: int, clock_output_divider: int, jesd_link_ids: list[int] | None = None, jesd204_inputs: str = '') dict
Build context dict for
ad9172.tmpl.
- adidt.model.contexts.build_ad9081_mxfe_ctx(*, label: str, cs: int, gpio_label: str, reset_gpio: int | None = None, sysref_req_gpio: int, rx2_enable_gpio: int, rx1_enable_gpio: int, tx2_enable_gpio: int, tx1_enable_gpio: int, dev_clk_ref: str, rx_core_label: str, tx_core_label: str, rx_link_id: int, tx_link_id: int, dac_frequency_hz: int, tx_cduc_interpolation: int, tx_fduc_interpolation: int, tx_converter_select: str, tx_lane_map: str, tx_link_mode: int, tx_m: int, tx_f: int, tx_k: int, tx_l: int, tx_s: int, adc_frequency_hz: int, rx_cddc_decimation: int, rx_fddc_decimation: int, rx_converter_select: str, rx_lane_map: str, rx_link_mode: int, rx_m: int, rx_f: int, rx_k: int, rx_l: int, rx_s: int, spi_max_hz: int = 5000000) dict
Build context dict for
ad9081_mxfe.tmpl.
- adidt.model.contexts.build_adrv9009_device_ctx(*, phy_family: str, phy_compatible: str, trx_cs: int, spi_max_hz: int = 25000000, gpio_label: str, trx_reset_gpio: int, trx_sysref_req_gpio: int, trx_clocks_value: str, trx_clock_names_value: str, trx_link_ids_value: str, trx_inputs_value: str, trx_profile_props_block: str, is_fmcomms8: bool, trx2_cs: int | None = None, trx2_reset_gpio: int | None = None, trx1_clocks_value: str | None = None) dict
Build context dict for
adrv9009.tmpl.
- adidt.model.contexts.build_adf4382_ctx(*, label: str = 'adf4382', cs: int, spi_max_hz: int = 1000000, clks_str: str | None = None, clock_output_names_str: str | None = None, power_up_frequency: int | None = None, spi_3wire: bool = True, charge_pump_microamp: int | None = None, output_power: int | None = None) dict
Build context dict for
adf4382.tmpl.
- adidt.model.contexts.build_ad9084_ctx(*, label: str, cs: int, spi_max_hz: int = 5000000, gpio_label: str, reset_gpio: int | None = None, dev_clk_ref: str, dev_clk_scales: str | None = None, firmware_name: str | None = None, subclass: int = 1, side_b_separate_tpl: bool = False, jrx0_physical_lane_mapping: str | None = None, jtx0_logical_lane_mapping: str | None = None, jrx1_physical_lane_mapping: str | None = None, jtx1_logical_lane_mapping: str | None = None, hsci_label: str | None = None, hsci_auto_linkup: bool = False, link_ids: str = '', jesd204_inputs: str = '') dict
Build context dict for
ad9084.tmpl.
- adidt.model.contexts.build_adis16495_ctx(*, label: str = 'imu0', device: str = 'adis16495', compatible: str = 'adi,adis16495-1', cs: int = 0, spi_max_hz: int = 2000000, spi_cpol: bool = True, spi_cpha: bool = True, gpio_label: str = 'gpio', interrupt_gpio: int | None = None, irq_type: str = 'IRQ_TYPE_EDGE_FALLING') dict
Build context dict for
adis16495.tmpl.The ADIS16495 is a 6-DOF IMU connected via SPI. This context builder produces a minimal device tree node with SPI mode, interrupt, and compatible string.
- adidt.model.contexts.build_adxl345_ctx(*, label: str = 'accel0', device: str = 'adxl345', compatible: str = 'adi,adxl345', cs: int = 0, spi_max_hz: int = 5000000, spi_cpol: bool = True, spi_cpha: bool = True, gpio_label: str = 'gpio', interrupt_gpio: int | None = None, irq_type: str = 'IRQ_TYPE_LEVEL_HIGH') dict
Build context dict for
adxl345.tmpl.
- adidt.model.contexts.build_ad7124_ctx(*, label: str = 'adc0', device: str = 'ad7124', compatible: str = 'adi,ad7124-8', cs: int = 0, spi_max_hz: int = 5000000, gpio_label: str = 'gpio', interrupt_gpio: int | None = None, irq_type: str = 'IRQ_TYPE_EDGE_FALLING', channels: list[dict] | None = None) dict
Build context dict for
ad7124.tmpl.- Parameters:
channels – List of channel dicts with keys
idand optionalname. If None, creates 8 default channels.
Component Factories
Pre-configured component factories for common ADI devices.
Each factory returns a ComponentModel
with the correct role, part name, and template already set. Pass
device-specific parameters as keyword arguments — they are forwarded to
the matching context builder.
Usage:
from adidt.model.components import adis16495, ad9680, hmc7044
model = BoardModel(
name="my_board",
platform="rpi5",
components=[
adis16495(spi_bus="spi0", cs=0, interrupt_gpio=25),
],
)
- adidt.model.components.adis16495(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
ADIS16495 6-DOF IMU.
Common kwargs:
label,spi_max_hz,compatible,gpio_label,interrupt_gpio,spi_cpol,spi_cpha.
- adidt.model.components.adxl345(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
ADXL345 3-axis accelerometer.
Common kwargs:
label,spi_max_hz,compatible,gpio_label,interrupt_gpio.
- adidt.model.components.ad7124(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
AD7124 24-bit precision ADC.
Common kwargs:
label,spi_max_hz,compatible,gpio_label,interrupt_gpio,channels.
- adidt.model.components.hmc7044(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
HMC7044 14-channel clock distributor.
Common kwargs:
label,spi_max_hz,pll1_clkin_frequencies,vcxo_hz,pll2_output_hz,clock_output_names,channels.
- adidt.model.components.ad9523_1(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
AD9523-1 clock generator.
Common kwargs:
label,spi_max_hz,vcxo_hz,gpio_controller,sync_gpio,channels.
- adidt.model.components.ad9528(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
AD9528 clock generator.
Common kwargs:
label,spi_max_hz,vcxo_hz,channels.
- adidt.model.components.ad9680(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
AD9680 dual-channel ADC.
Common kwargs:
label,spi_max_hz,clks_str,clk_names_str,sampling_frequency_hz,rx_m,rx_l,rx_f,rx_k,rx_np.
- adidt.model.components.ad9144(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
AD9144 quad-channel DAC.
Common kwargs:
label,spi_max_hz,clk_ref,jesd204_top_device,jesd204_link_ids,jesd204_inputs.
- adidt.model.components.ad9152(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
AD9152 dual-channel DAC.
Common kwargs:
label,spi_max_hz,clk_ref,jesd_link_mode.
- adidt.model.components.ad9172(spi_bus: str = 'spi0', cs: int = 0, **kwargs) ComponentModel
AD9172 RF DAC.
Common kwargs:
label,spi_max_hz,clk_ref,dac_rate_khz,jesd_link_mode,dac_interpolation,channel_interpolation,clock_output_divider.