Export solved configurations to pyadi-dt

pyadi-jif can export a solved system as the versioned adi.jif-dt contract. The contract contains solved electrical intent—JESD links, semantic clock requirements, rates, dividers, and FPGA settings—but deliberately excludes physical clock output channels and device-tree labels.

pyadi-dt owns that physical placement and joins each semantic ID to a board profile before generating or modifying a device tree. This prevents requested clock order inside the solver from being mistaken for HMC7044 or AD952x output channel numbering.

Solve and export an AD9680 system

The complete runnable example is checked by the normal example test suite:

examples/ad9680_jif_dt_contract.py
"""Solve AD9680 clocks and export the versioned pyadi-jif -> pyadi-dt contract."""

import adijif

system = adijif.system("ad9680", "hmc7044", "xilinx", 125_000_000)
system.converter.sample_clock = 1_000_000_000
system.converter.decimation = 1
system.converter.set_quick_configuration_mode(str(0x88))
system.converter.K = 32
system.fpga.setup_by_dev_kit_name("zc706")
system.fpga.force_qpll = 1

solution = system.solve()
contract = system.export_config(format="adi.jif-dt", solution=solution)

# The JSON contains semantic requirements only. A pyadi-dt board profile maps
# them onto physical output channels and device-tree labels.
print(contract.to_json(), end="")

Run it directly or redirect the portable result to a file:

python examples/ad9680_jif_dt_contract.py > ad9680.jif-dt.json

The important API boundary is:

solution = system.solve()
contract = system.export_config(format="adi.jif-dt", solution=solution)
contract.to_json_file("ad9680.jif-dt.json")

Passing the existing solution avoids solving twice. If it is omitted, export_config() solves the current system first.

What the producer emits

For the AD9680 example, the contract contains one ad9680.rx link and four semantic clock requirements:

  • ad9680.device-clock

  • ad9680.sysref

  • ad9680.fpga-ref

  • ad9680.fpga-link

Each clock has a solved rate and divider, but no output_index or dt_label. Those values depend on the carrier and FPGA design, not the solver.

The output begins with:

{
  "clock_requirements": [
    {
      "divider": 3,
      "id": "ad9680.device-clock",
      "rate_hz": 1000000000,
      "role": "converter-device",
      "sink": "AD9680",
      "source": "HMC7044"
    }
  ],
  "producer": {
    "name": "pyadi-jif",
    "version": "0.1.6"
  },
  "schema": "adi.jif-dt",
  "version": "1.0"
}

Consumer-side binding

pyadi-dt loads the JSON with its strict JifDtContract projection, then uses a separate JifDtBindings object to map semantic requirements to physical clock channels and XSA/device-tree labels. See the pyadi-dt interface contract documentation for binding examples, validation order, and compatibility rules.

The producer and consumer both enforce contract version 1.0. Unsupported versions, inconsistent JESD transport/lane rates, duplicate IDs, fractional hertz, and non-JSON solver objects fail before a device tree can be changed.

Current topology support

system.export_config() initially supports one non-nested ADC or DAC converter. Nested transceivers and MxFE systems require an explicit follow-up extension so their multiple links retain stable identities rather than relying on computed dictionary key parsing.