MESA

MESA (Manifest driven Example System for All-platforms) is the runtime helper that every shipped ChipScoPy example notebook uses to locate its programming and probes files, validate that the connected device matches the expected design, and program the device with the correct flow (flat or segmented).

Each design ships with a manifest.json that captures the platform’s debug cores, programming flow, and supported example IDs. MESA discovers those manifests, resolves any extends chain, and exposes a small typed Python API. Users write one notebook; MESA adapts it to whichever hardware platform the manifest targets.

Quick start

Every notebook in chipscopy/examples/ uses the same setup-cell shape, built around resolve_example_design(). It returns an ExampleDesign that hides whether the notebook is running in MESA mode (manifest-driven) or direct mode (user-supplied PDI / LTX), so the rest of the example is identical in both cases.

import os
from chipscopy import create_session, delete_session
from chipscopy.examples.mesa import resolve_example_design

CS_URL = os.getenv("CS_SERVER_URL", "TCP:localhost:3042")
HW_URL = os.getenv("HW_SERVER_URL", "TCP:localhost:3121")
HW_PLATFORM = "vck190"
EXAMPLE_ID = "ila_and_vio"

PROGRAMMING_FILE = ""  # Direct mode: set to your own PDI to skip MESA
PROBES_FILE = ""

example_design = resolve_example_design(
    HW_PLATFORM,
    EXAMPLE_ID,
    programming_file=PROGRAMMING_FILE,
    probes_file=PROBES_FILE,
)
example_design.print_summary()

session = create_session(cs_server_url=CS_URL, hw_server_url=HW_URL)
device = session.devices.filter_by(family=example_design.device_family).get()

if not example_design.verify_device_idcode(device):
    raise RuntimeError("Device IDCODE does not match the manifest design.")

example_design.program_device(device)
device.discover_and_setup_cores(ltx_file=example_design.probes_file)

# ... your example logic here ...

delete_session(session)

The HW_PLATFORM and EXAMPLE_ID constants are the only things a notebook author needs to set; everything else is derived from the manifest.

ExampleDesign paths (flat vs segmented)

ExampleDesign exposes string fields programming_file and probes_file for the usual notebook pattern (assign to PROGRAMMING_FILE / PROBES_FILE and pass them to APIs such as device.discover_and_setup_cores(ltx_file=...)). It also exposes programming_files and probes_files: ordered lists of resolved programming paths and any matching LTX paths. probes_files may be empty or shorter than programming_files when no LTX is present (LTX is for debug setup, not for device.program).

For programming_flow: "flat", each list has at most one element matching the scalar fields when paths exist. For "segmented", programming_files is [boot, PLD] in that order; the scalars are set to the PLD pair when an LTX is resolved so ILA/VIO and other PL-side debug use the correct LTX without per-notebook branching. If no segmented LTX is found, probes_file falls back to any LTX returned by chipscopy.get_design_files(), or is empty. Boot and PLD PDIs are always required for segmented programming; LTX files are optional and are resolved with the same manifest glob patterns when present.

To retarget another board, change HW_PLATFORM (or override it via the environment) - any platform that has a manifest will work:

HW_PLATFORM=vck190 python my_example.py
HW_PLATFORM=vpk120 python my_example.py
HW_PLATFORM=vcu128 python my_example.py

Working with the manifest

The ExampleDesign.manifest attribute returns a typed Manifest (or None in direct mode). Use it to gate optional capabilities cleanly instead of hard-coding per-platform branches in the notebook.

manifest = example_design.manifest

if manifest and manifest.has_core("memory"):
    memory = device.memory[0]

if manifest and manifest.has_all_cores("ila", "vio"):
    ila = device.ila_cores[0]

if manifest and manifest.has_feature("memory", "2d_eye_scan"):
    run_2d_eye_scan(device)

For platform-specific knobs (quad names, ILA instance paths, NoC node names), use Manifest.get_core_config() so the notebook reads the value from the manifest rather than embedding it in code:

quad_names = manifest.get_core_config("ibert_gty", "quad_names", [])
node_names = manifest.get_core_config("noc_perfmon", "node_names", [])

ila_instance = manifest.get_core_instance("ila", index=0)
if ila_instance:
    ila_name = ila_instance["name"]
    probe_prefix = ila_instance.get("probe_prefix", "")

Manifest structure

Each design directory under examples/designs/<platform>/<design> contains a manifest.json like this:

{
  "extends": "_base/versal_standard_cores.json",
  "design_info": {
    "design_name": "chipscopy_ced",
    "description": "ChipScoPy CED for VCK190",
    "hw_platform": "vck190",
    "programming_flow": "flat",
    "design_path": "vck190/production/chipscopy_ced",
    "family": "versal",
    "device": "xcvc1902",
    "board": "VCK190",
    "idcode": "0x14CA8093"
  },
  "debug_cores": {
    "ibert_gty": {
      "enabled": true,
      "quad_names": ["Quad_205", "Quad_204", "Quad_201", "Quad_105"]
    }
  },
  "supported_examples": [
    "ila_and_vio",
    "link_and_eye_scan",
    "ddr_example"
  ],
  "metadata": {
    "version": "1.0.0",
    "maintainer": "AMD ChipScoPy Team"
  }
}

Notable fields:

  • extends (optional) - relative path to a base manifest under _base/; child manifests deep-merge over the base.

  • programming_flow - must be "flat" (single PDI) or "segmented" (boot + PLD PDI for partial reconfiguration).

  • family - must match a ChipScoPy device family (versal, virtexuplus, etc.).

  • idcode / idcode_list - JTAG IDCODE(s) used by ExampleDesign.verify_device_idcode() to detect the wrong board before programming. Use idcode_list for multi-variant silicon (e.g. production + ES).

The full schema lives in chipscopy/examples/mesa/manifest_schema.json. Both the JSON schema and the Python-side ManifestValidator set additionalProperties: false for nested debug_cores entries, so typo’d keys (e.g. descripton) are rejected at validation time instead of silently behaving as no-ops at runtime.

Programming flows

ExampleDesign.program_device() and Manifest.program_device() both dispatch on the manifest’s programming_flow field through an internal flow table; adding a new flow is a one-line registration.

Flow

PDI files

Skip reset

Use case

flat

1 (monolithic)

No

Simple designs, full configuration

segmented

2 (boot + PLD)

Yes (PLD)

Partial reconfiguration, boot + dynamic

Segmented boot and PLD PDIs (and LTX paths during example resolution) use the manifest’s boot_pdi_pattern / pld_pdi_pattern with the same matching rules whenever MESA resolves files or runs the segmented program steps.

Management CLI (internal)

ChipScoPy maintainers use the private chipscopy.examples.mesa._cli module to validate, lint, and scaffold MESA manifests and example notebooks. End users do not need to call it; it is documented here as a reference for maintainers contributing new MESA examples.

# Validate every manifest under chipscopy/examples/designs.
python -m chipscopy.examples.mesa._cli validate --all

# Validate a specific manifest by path.
python -m chipscopy.examples.mesa._cli validate --manifest path/to/manifest.json

# Cross-check manifests vs. notebook filenames; flag dangling references
# and per-manifest lint warnings.
python -m chipscopy.examples.mesa._cli lint

# Add an example ID to all compatible manifests (use --dry-run first).
python -m chipscopy.examples.mesa._cli add-example \
    --example-id my_example --dry-run

# Scaffold a new MESA-powered example notebook.
python -m chipscopy.examples.mesa._cli new-example \
    --example-id my_example --template basic

# Print a compatibility report (platforms, examples, categories).
python -m chipscopy.examples.mesa._cli report

API reference

Notebook entry points

chipscopy.examples.mesa.resolve_example_design(hw_platform, example_id, *, programming_file='', probes_file='', device_family='versal')[source]

Resolve programming and probes files for an example notebook.

Two paths are supported, in this order:

  1. Direct mode: if programming_file is non-empty the MESA manifest lookup is skipped entirely. The notebook can run with any user-provided PDI/LTX combination - no manifest is required.

  2. MESA mode: otherwise the design is resolved from the MESA manifest for hw_platform and example_id. The platform’s design_path is converted into actual file paths via chipscopy.get_design_files(), and device_family is taken from the manifest.

Raises:

RuntimeError – In MESA mode when no compatible manifest is found.

Return type:

ExampleDesign

chipscopy.examples.mesa.get_manifest(platform, example_id=None)[source]

Get MESA manifest for platform and optional example ID.

Return type:

Optional[Manifest, None]

chipscopy.examples.mesa.get_all_platforms()[source]

Get list of all available platforms.

Return type:

List[str]

chipscopy.examples.mesa.get_designs_directory()[source]

Return the MESA designs directory.

Single source of truth for examples/designs/ lookup. Honours the CHIPSCOPY_EXAMPLES environment variable through chipscopy.get_examples_dir_or_die() so notebooks, the registry and CLI all agree on the same location.

Return type:

Path

chipscopy.examples.mesa.print_manifest_info(manifest, detailed=False)[source]

Print manifest information in a formatted way.

Return type:

None

ExampleDesign

class chipscopy.examples.mesa.ExampleDesign(manifest, design_files, programming_file, probes_file, device_family, hw_platform, example_id, programming_files=<factory>, probes_files=<factory>)[source]

Resolved design files for an example notebook.

Hides whether the notebook is running in MESA mode (manifest-driven) or direct mode (user-supplied PDI/LTX), so the rest of the notebook uses one consistent interface.

manifest

Loaded Manifest when MESA resolved the design, or None when the user supplied design files directly.

Type:

chipscopy.examples.mesa.mesa.Manifest | None

design_files

DesignFiles object from chipscopy.get_design_files() when MESA is used, None otherwise.

Type:

Any | None

programming_file

Path to the PDI / programming file used by the device.

Type:

str

probes_file

Path to the LTX / probes file used by the device.

Type:

str

device_family

Device family string suitable for session.devices.filter_by(family=...).

Type:

str

hw_platform

Hardware platform identifier (e.g. "vck190").

Type:

str

example_id

Example ID requested by the notebook.

Type:

str

programming_files

Ordered programming file paths (boot then PLD when segmented).

Type:

List[str]

probes_files

Ordered LTX paths when resolved (boot then PLD when both exist). May be shorter than programming_files or empty when no LTX is available; probe files are optional and not required for program_device().

Type:

List[str]

print_summary()[source]

Print the server-independent design details used by the notebook.

Return type:

None

program_device(device, **program_options)[source]

Program the device using the resolved design files.

In MESA mode this delegates to Manifest.program_device(), which handles flat vs. segmented programming flows. In direct mode it falls back to device.program(self.programming_file).

Return type:

None

property using_manifest: bool

True when this design was resolved through a MESA manifest.

Return type:

bool

verify_device_idcode(device)[source]

Verify the device JTAG IDCODE matches the resolved design.

Returns True in direct mode (no manifest constraint) and delegates to Manifest.verify_device_idcode() in MESA mode.

Return type:

bool

Manifest

class chipscopy.examples.mesa.Manifest(design_info, debug_cores, supported_examples, metadata, manifest_path)[source]

Type-safe MESA manifest representation.

Provides IDE autocomplete and type checking for manifest fields.

classmethod from_dict(data, manifest_path)[source]

Create Manifest from a dictionary that has already been merged with its bases.

Return type:

Manifest

classmethod from_file(path)[source]

Load and return a Manifest from path, resolving any extends chain.

Raises ManifestError for any load / parse / merge / required-field problem so callers can catch a single exception type.

Return type:

Manifest

get_all_enabled_cores_by_prefix(prefix, filter_fn=None)[source]

Get all enabled cores whose name starts with prefix (with optional filter).

Return type:

List[Tuple[str, DebugCore]]

get_all_ibert_cores(filter_fn=None)[source]

Get all enabled IBERT cores with optional filtering.

Return type:

List[Tuple[str, DebugCore]]

get_core_config(core_name, config_key, default=None)[source]

Get a platform-specific configuration value from a debug core.

For platform-specific hardware details like quad names, node names, and core instances. Algorithm parameters (scan patterns, step sizes, thresholds) should remain visible in notebooks for learning.

get_core_instance(core_name, index=0)[source]

Get a specific core instance by name and index.

Thin wrapper around get_core_config() that returns the entry from the core’s core_instances list at index.

Return type:

Optional[Dict, None]

get_enabled_cores()[source]

Get list of all enabled core names.

Return type:

List[str]

get_first_enabled_core_by_prefix(prefix, filter_fn=None, index=0)[source]

Get enabled core by prefix with optional filtering and indexing.

Parameters:
  • prefix (str) – Core name prefix to match (e.g., "ibert_", "ila", "vio").

  • filter_fn (Optional[Callable[[str, DebugCore], bool], None]) – Optional (core_name, core) -> bool for custom filtering.

  • index (int) – Index of matching core to return (default 0).

Return type:

Optional[Tuple[str, DebugCore], None]

Returns:

Tuple of (core_name, DebugCore) or None.

get_ibert_core(filter_fn=None, index=0)[source]

Get IBERT core (any type) with optional custom filtering and indexing.

Return type:

Optional[Tuple[str, DebugCore], None]

static get_mem_type_name(mem_type_val)[source]

Translate DDR memory type integer code to human-readable name.

Parameters:

mem_type_val (int) – Integer from ddr_node.get_property(["mem_type"])["mem_type"].

Return type:

str

Returns:

String name like "DDR4", "LPDDR4", "DDR5", "LPDDR5", or "Unknown(N)".

has_all_cores(*core_names)[source]

Check if all specified cores are present and enabled.

Return type:

bool

has_core(core_name)[source]

Check if a debug core is present and enabled.

Return type:

bool

has_feature(core_name, feature)[source]

Check if a specific feature is available for a core.

Return type:

bool

program_device(device, design_files, *, delay_after_program=0, show_progress_bar=True, progress=None, done=None)[source]

Program device according to the manifest’s programming flow.

Dispatches to the appropriate flow handler based on design_info.programming_flow. Adding a new flow is one entry in _FLOW_HANDLERS.

Return type:

None

supports_example(example_id)[source]

Check if this manifest supports a specific example.

Return type:

bool

verify_device_idcode(device)[source]

Verify device JTAG IDCODE matches manifest expectation.

Returns True if no IDCODE constraint is specified, if all IDCODEs are placeholders, or if the device IDCODE matches any configured value. Returns False only when a real IDCODE is specified and the device does not match.

Return type:

bool

class chipscopy.examples.mesa.DebugCore(enabled, description, features=<factory>, quad_names=None, memory_types=None, core_instances=None, node_names=None, trigger_base_address=None, probe_mapping=None, vio_hierarchy=None)[source]

Represents a debug core configuration.

class chipscopy.examples.mesa.DesignInfo(design_name, hw_platform, programming_flow, design_path, family, device, board=None, description=None, boot_pdi_pattern=None, pld_pdi_pattern=None, idcode=None, idcode_list=None)[source]

Represents design information.

class chipscopy.examples.mesa.ManifestMetadata(created_date, version, maintainer=None, last_updated=None, notes=None)[source]

Represents manifest metadata.

Registry and errors

class chipscopy.examples.mesa.ManifestRegistry[source]

MESA manifest registry for design discovery.

Manifests are loaded lazily and cached by file path. Each cached entry is keyed on the file’s mtime so notebook reruns automatically pick up edits without an explicit invalidate call.

find_manifest_files()[source]

Find all manifest.json files under the configured designs directory.

Return type:

List[Path]

get_all_platforms()[source]

Get sorted list of all available platforms across all manifests.

Return type:

List[str]

get_designs_directory()[source]

Get the designs directory path (delegates to get_designs_directory()).

Return type:

Path

get_manifest(platform, example_id=None)[source]

Get MESA manifest for platform and optional example_id.

Returns the first manifest whose hw_platform matches and that either supports the requested example or - if no example is given - is just the first match. Returns None when no manifest matches.

Return type:

Optional[Manifest, None]

invalidate_cache(platform=None)[source]

Drop cached manifests. Filters by platform when one is given.

Return type:

None

invalidate_directory_cache()[source]

Backward-compatible alias for invalidate_cache().

Return type:

None

exception chipscopy.examples.mesa.ManifestError[source]

Raised when a manifest cannot be loaded, parsed or merged.

Validation

chipscopy.examples.mesa.validate_manifest_file(filepath)[source]

Validate a manifest file (resolving any extends chain first).

Returns (is_valid, errors). Inheritance is resolved exactly as it will be at runtime so override-only typos are caught here.

Return type:

Tuple[bool, List[str]]

class chipscopy.examples.mesa.ManifestValidationReporter[source]

CLI-friendly reporter that wraps validate_manifest_file().

Adds non-schema lint warnings (missing recommended description / maintainer fields, empty supported_examples) on the fully merged manifest and formats the result for terminal output. All authoritative schema rules still live in ManifestValidator; this class only layers CLI presentation on top.

get_validation_report()[source]

Format collected errors and warnings for CLI output.

Return type:

str

validate_manifest(manifest_path)[source]

Validate one manifest file and collect lint warnings on the merged dict.

Return type:

bool