{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "FPGA Boards Catalog",
  "description": "Schemas for entities (standalone boards, SoMs, carriers, kits, FMC cards) and their relationships. Each entity type has its own folder under board-repo (boards/, soms/, carriers/, kits/, fmc-cards/); relationships live under relationships/<type>/. The build script validates every file in those folders against the matching $def.",
  "$defs": {

    "standalone": {
      "type": "object",
      "title": "Standalone Board",
      "description": "A complete FPGA development board with on-board user I/O — not a SoM, not part of a kit.",
      "required": ["mpn", "name", "status", "url", "vendor", "price", "device"],
      "additionalProperties": false,
      "properties": {
        "mpn": { "$ref": "#/$defs/mpn" },
        "name": { "$ref": "#/$defs/name" },
        "status": { "$ref": "#/$defs/status" },
        "url": { "$ref": "#/$defs/url" },
        "vendor": { "$ref": "#/$defs/vendor" },
        "price": { "$ref": "#/$defs/price_or_null" },
        "device": { "$ref": "#/$defs/device" },
        "memory": { "type": "array", "title": "Memory", "description": "On-board DRAM. List one entry per distinct memory bank.", "items": { "$ref": "#/$defs/memory_interface" }, "minItems": 1 },
        "sram": { "type": "array", "title": "SRAM", "description": "On-board static / non-volatile RAM chips (SRAM, SSRAM, MRAM, FRAM, nvSRAM) — distinct from the DRAM banks in `memory`. One entry per chip.", "items": { "$ref": "#/$defs/sram_interface" }, "minItems": 1 },
        "flash": { "type": "array", "title": "Flash", "description": "On-board non-volatile flash memory. List one entry per distinct chip.", "items": { "$ref": "#/$defs/flash_interface" }, "minItems": 1 },
        "eeprom": { "type": "array", "title": "EEPROM", "description": "Small on-board EEPROMs (MAC address, board UID, board identification, user data). One entry per chip.", "items": { "$ref": "#/$defs/eeprom_chip" }, "minItems": 1 },
        "analog": { "type": "array", "title": "Analog", "description": "Discrete ADCs, DACs, RF transceivers, and AFEs soldered to the board. Excludes FPGA-internal XADC/SYSMON and anything on daughter cards.", "items": { "$ref": "#/$defs/analog_chip" }, "minItems": 1 },
        "sensors": { "type": "array", "title": "Sensors", "description": "On-board sensor chips (IMU, temperature, light, etc.).", "items": { "$ref": "#/$defs/sensor" }, "minItems": 1 },
        "audio": { "$ref": "#/$defs/audio" },
        "clocking": { "$ref": "#/$defs/clocking" },
        "pcie": { "type": "array", "title": "PCIe", "description": "PCIe interfaces on the board (Edge connector, slot, PCIe/104, iPass, MCIO, MiniPCIe). M.2 sockets and M.2 form-factor boards live in the separate `m2` array.", "items": { "$ref": "#/$defs/pcie_interface" }, "minItems": 1 },
        "m2": { "type": "array", "title": "M.2", "description": "M.2 sockets the board provides, or the board's own M.2 edge connector if it is itself an M.2 module.", "items": { "$ref": "#/$defs/m2_interface" }, "minItems": 1 },
        "high_speed_io": { "$ref": "#/$defs/high_speed_io" },
        "video": { "$ref": "#/$defs/video" },
        "display": { "$ref": "#/$defs/display" },
        "networking": { "$ref": "#/$defs/networking" },
        "serial": { "$ref": "#/$defs/serial" },
        "usb": { "type": "array", "title": "USB", "description": "USB interfaces accessible to the FPGA design.", "items": { "$ref": "#/$defs/usb_interface" }, "minItems": 1 },
        "usb_bridge": { "type": "array", "title": "USB UART/JTAG", "description": "USB-to-UART/JTAG bridge ports.", "items": { "$ref": "#/$defs/usb_bridge_interface" }, "minItems": 1 },
        "expansion": { "$ref": "#/$defs/expansion" },
        "storage": { "$ref": "#/$defs/storage" },
        "user_io": { "$ref": "#/$defs/user_io" },
        "wireless": { "$ref": "#/$defs/wireless" },
        "features": { "$ref": "#/$defs/features" }
      }
    },

    "som": {
      "type": "object",
      "title": "System-on-Module",
      "description": "A System-on-Module (SoM) — an FPGA + memory + flash on a small board designed to plug into a carrier via board-to-board mating connectors. Some SoMs include on-board debug peripherals (Ethernet, microSD, USB-UART) that are usable without a carrier.",
      "required": ["mpn", "name", "status", "url", "vendor", "price", "device"],
      "additionalProperties": false,
      "properties": {
        "mpn": { "$ref": "#/$defs/mpn" },
        "name": { "$ref": "#/$defs/name" },
        "status": { "$ref": "#/$defs/status" },
        "url": { "$ref": "#/$defs/url" },
        "vendor": { "$ref": "#/$defs/vendor" },
        "price": { "$ref": "#/$defs/price_or_null" },
        "device": { "$ref": "#/$defs/device" },
        "memory": { "type": "array", "title": "Memory", "description": "On-SoM DRAM. List one entry per distinct memory bank.", "items": { "$ref": "#/$defs/memory_interface" }, "minItems": 1 },
        "sram": { "type": "array", "title": "SRAM", "description": "On-SoM static / non-volatile RAM chips (SRAM, SSRAM, MRAM, FRAM, nvSRAM) — distinct from the DRAM banks in `memory`. One entry per chip.", "items": { "$ref": "#/$defs/sram_interface" }, "minItems": 1 },
        "flash": { "type": "array", "title": "Flash", "description": "On-SoM non-volatile flash memory.", "items": { "$ref": "#/$defs/flash_interface" }, "minItems": 1 },
        "eeprom": { "type": "array", "title": "EEPROM", "description": "On-SoM EEPROMs (typically MAC address, unique board ID). One entry per chip. Useful regardless of carrier — flattens into the kit at build time.", "items": { "$ref": "#/$defs/eeprom_chip" }, "minItems": 1 },
        "analog": { "type": "array", "title": "Analog", "description": "Discrete ADCs, DACs, RF transceivers, and AFEs soldered to the SoM.", "items": { "$ref": "#/$defs/analog_chip" }, "minItems": 1 },
        "sensors": { "type": "array", "title": "Sensors", "description": "On-SoM sensor chips.", "items": { "$ref": "#/$defs/sensor" }, "minItems": 1 },
        "audio": { "$ref": "#/$defs/audio" },
        "clocking": { "$ref": "#/$defs/clocking" },
        "m2": { "type": "array", "title": "M.2", "description": "M.2 sockets on the SoM (rare, but some SoMs include them for SSD storage or WiFi).", "items": { "$ref": "#/$defs/m2_interface" }, "minItems": 1 },
        "networking": { "$ref": "#/$defs/networking" },
        "serial": { "$ref": "#/$defs/serial" },
        "usb": { "type": "array", "title": "USB", "description": "USB interfaces accessible to the FPGA design.", "items": { "$ref": "#/$defs/usb_interface" }, "minItems": 1 },
        "usb_bridge": { "type": "array", "title": "USB UART/JTAG", "description": "USB-to-UART/JTAG bridge ports.", "items": { "$ref": "#/$defs/usb_bridge_interface" }, "minItems": 1 },
        "expansion": { "$ref": "#/$defs/expansion" },
        "storage": { "$ref": "#/$defs/storage" },
        "user_io": { "$ref": "#/$defs/user_io" },
        "wireless": { "$ref": "#/$defs/wireless" },
        "features": { "$ref": "#/$defs/features" }
      }
    },

    "carrier": {
      "type": "object",
      "title": "SoM Carrier",
      "description": "A carrier board that mates with one or more SoMs to provide user I/O. Has no FPGA of its own — the FPGA comes from the SoM. Compatibility with SoMs is recorded as som-mates relationships, not in the carrier file.",
      "required": ["mpn", "name", "status", "url", "vendor", "price"],
      "additionalProperties": false,
      "properties": {
        "mpn": { "$ref": "#/$defs/mpn" },
        "name": { "$ref": "#/$defs/name" },
        "status": { "$ref": "#/$defs/status" },
        "url": { "$ref": "#/$defs/url" },
        "vendor": { "$ref": "#/$defs/vendor" },
        "price": { "$ref": "#/$defs/price_or_null" },
        "flash": { "type": "array", "title": "Flash", "description": "On-carrier non-volatile flash. Less common than SoM-side flash (the SoM usually owns configuration + Linux rootfs flash), but some carriers carry their own on-board eMMC or QSPI for application data. One entry per chip. Flattened kits get the union of SoM-side and carrier-side flash.", "items": { "$ref": "#/$defs/flash_interface" }, "minItems": 1 },
        "eeprom": { "type": "array", "title": "EEPROM", "description": "On-carrier EEPROMs (rare — the SoM typically owns the identity EEPROM, but some carriers have their own user-data EEPROM).", "items": { "$ref": "#/$defs/eeprom_chip" }, "minItems": 1 },
        "analog": { "type": "array", "title": "Analog", "description": "Discrete ADCs, DACs, RF transceivers, and AFEs soldered to the carrier.", "items": { "$ref": "#/$defs/analog_chip" }, "minItems": 1 },
        "sensors": { "type": "array", "title": "Sensors", "description": "On-board sensor chips.", "items": { "$ref": "#/$defs/sensor" }, "minItems": 1 },
        "audio": { "$ref": "#/$defs/audio" },
        "clocking": { "$ref": "#/$defs/clocking" },
        "pcie": { "type": "array", "title": "PCIe", "items": { "$ref": "#/$defs/pcie_interface" }, "minItems": 1 },
        "m2": { "type": "array", "title": "M.2", "items": { "$ref": "#/$defs/m2_interface" }, "minItems": 1 },
        "high_speed_io": { "$ref": "#/$defs/high_speed_io" },
        "video": { "$ref": "#/$defs/video" },
        "display": { "$ref": "#/$defs/display" },
        "networking": { "$ref": "#/$defs/networking" },
        "serial": { "$ref": "#/$defs/serial" },
        "usb": { "type": "array", "title": "USB", "items": { "$ref": "#/$defs/usb_interface" }, "minItems": 1 },
        "usb_bridge": { "type": "array", "title": "USB UART/JTAG", "items": { "$ref": "#/$defs/usb_bridge_interface" }, "minItems": 1 },
        "expansion": { "$ref": "#/$defs/expansion" },
        "storage": { "$ref": "#/$defs/storage" },
        "user_io": { "$ref": "#/$defs/user_io" },
        "wireless": { "$ref": "#/$defs/wireless" },
        "features": { "$ref": "#/$defs/features" }
      }
    },

    "kit": {
      "type": "object",
      "title": "Kit (SoM + Carrier)",
      "description": "A bundled product comprising a SoM and a carrier (and possibly extras). All feature data is inherited from the composed entities at build time — kit files only contain identity, price, composition, and optional extras.",
      "required": ["mpn", "name", "status", "url", "vendor", "price", "composition"],
      "additionalProperties": false,
      "properties": {
        "mpn": { "$ref": "#/$defs/mpn" },
        "name": { "$ref": "#/$defs/name" },
        "status": { "$ref": "#/$defs/status" },
        "url": { "$ref": "#/$defs/url" },
        "vendor": { "$ref": "#/$defs/vendor" },
        "price": { "$ref": "#/$defs/price_or_null" },
        "composition": {
          "type": "object",
          "title": "Composition",
          "description": "MPN references to the SoM and carrier that compose this kit. Both must exist as entities in soms/ and carriers/. The build script flattens the kit by merging SoM + carrier features into a single resolved record.",
          "required": ["som", "carrier"],
          "additionalProperties": false,
          "properties": {
            "som": { "type": "string", "title": "SoM MPN", "description": "MPN of the SoM in soms/<vendor>/<mpn>.json." },
            "carrier": { "type": "string", "title": "Carrier MPN", "description": "MPN of the carrier in carriers/<vendor>/<mpn>.json." }
          }
        },
        "extras": {
          "type": "array",
          "title": "Extras",
          "description": "Additional items bundled with the kit beyond the SoM and carrier (cables, PSUs, antennas, etc.).",
          "items": { "type": "string" },
          "minItems": 1
        }
      }
    },

    "fmc_card": {
      "type": "object",
      "title": "FMC Card",
      "description": "An FPGA Mezzanine Card (FMC LPC / HPC / FMC+) that plugs into an FMC site on a board or carrier. Compatibility is recorded as fmc-mates relationships, not in the card file. vadj range will live here once we model it — schema reserves the spot.",
      "required": ["mpn", "name", "status", "url", "vendor", "price", "connector_type"],
      "additionalProperties": false,
      "properties": {
        "mpn": { "$ref": "#/$defs/mpn" },
        "name": { "$ref": "#/$defs/name" },
        "status": { "$ref": "#/$defs/status" },
        "url": { "$ref": "#/$defs/url" },
        "vendor": { "$ref": "#/$defs/vendor" },
        "price": { "$ref": "#/$defs/price_or_null" },
        "connector_type": {
          "type": "string",
          "title": "FMC Connector Type",
          "enum": ["lpc", "hpc", "fmcp"],
          "description": "LPC (160-pin VITA 57.1), HPC (400-pin VITA 57.1), or FMC+ (560-pin VITA 57.4)."
        },
        "vadj_min": {
          "type": "number",
          "title": "VADJ Min (V)",
          "description": "Minimum VADJ supply voltage the card accepts. For fixed-voltage cards (e.g. 1.8V-only variants), set vadj_min == vadj_max."
        },
        "vadj_max": {
          "type": "number",
          "title": "VADJ Max (V)",
          "description": "Maximum VADJ supply voltage the card accepts."
        },
        "eeprom": { "type": "array", "title": "EEPROM", "description": "On-card EEPROMs (VITA 57.1 IPMI EEPROM holds board ID and is common on FMC cards).", "items": { "$ref": "#/$defs/eeprom_chip" }, "minItems": 1 },
        "analog": { "type": "array", "title": "Analog", "description": "Discrete ADCs, DACs, RF transceivers, and AFEs on the FMC card.", "items": { "$ref": "#/$defs/analog_chip" }, "minItems": 1 },
        "sensors": { "type": "array", "title": "Sensors", "description": "On-card sensor chips.", "items": { "$ref": "#/$defs/sensor" }, "minItems": 1 },
        "audio": { "$ref": "#/$defs/audio" },
        "clocking": { "$ref": "#/$defs/clocking" },
        "pcie": { "type": "array", "title": "PCIe", "items": { "$ref": "#/$defs/pcie_interface" }, "minItems": 1 },
        "m2": { "type": "array", "title": "M.2", "items": { "$ref": "#/$defs/m2_interface" }, "minItems": 1 },
        "high_speed_io": { "$ref": "#/$defs/high_speed_io" },
        "video": { "$ref": "#/$defs/video" },
        "networking": { "$ref": "#/$defs/networking" },
        "serial": { "$ref": "#/$defs/serial" },
        "usb": { "type": "array", "title": "USB", "items": { "$ref": "#/$defs/usb_interface" }, "minItems": 1 },
        "storage": { "$ref": "#/$defs/storage" },
        "user_io": { "$ref": "#/$defs/user_io" },
        "features": { "$ref": "#/$defs/features" }
      }
    },

    "som_mates": {
      "type": "object",
      "title": "SoM Compatibility Bundle",
      "description": "One file per carrier listing all SoMs that physically and electrically mate with it. Filename: relationships/som-mates/<carrier-vendor>/<carrier-mpn>.json — the parent folder matches the carrier's vendor (mirrors carriers/<vendor>/<mpn>.json), and the filename stem must match the top-level `carrier` field.",
      "required": ["carrier", "compatible_soms"],
      "additionalProperties": false,
      "properties": {
        "carrier": { "type": "string", "title": "Carrier MPN", "description": "MPN of the carrier in carriers/<vendor>/<mpn>.json. Must match the filename." },
        "compatible_soms": {
          "type": "array",
          "title": "Compatible SoMs",
          "minItems": 1,
          "items": {
            "type": "object",
            "required": ["som"],
            "additionalProperties": false,
            "properties": {
              "som": { "type": "string", "title": "SoM MPN", "description": "MPN of the SoM in soms/<vendor>/<mpn>.json." },
              "verified_by": { "$ref": "#/$defs/verified_by" },
              "notes": { "$ref": "#/$defs/relationship_notes" }
            }
          }
        }
      }
    },

    "fmc_mates": {
      "type": "object",
      "title": "FMC Card Compatibility Bundle",
      "description": "One file per FMC card listing all hosts (standalone boards, kits, or carriers) it mates with. Filename: relationships/fmc-mates/<fmc-card-vendor>/<fmc-card-mpn>.json — the parent folder matches the card's vendor (mirrors fmc-cards/<vendor>/<mpn>.json), and the filename stem must match the top-level `fmc_card` field.",
      "required": ["fmc_card", "compatible_hosts"],
      "additionalProperties": false,
      "properties": {
        "fmc_card": { "type": "string", "title": "FMC Card MPN", "description": "MPN of the FMC card in fmc-cards/<vendor>/<mpn>.json. Must match the filename." },
        "compatible_hosts": {
          "type": "array",
          "title": "Compatible Hosts",
          "minItems": 1,
          "items": {
            "type": "object",
            "required": ["host", "host_type"],
            "additionalProperties": false,
            "properties": {
              "host": { "type": "string", "title": "Host MPN", "description": "MPN of the host (standalone board, kit, or carrier)." },
              "host_type": {
                "type": "string",
                "title": "Host Type",
                "enum": ["standalone", "kit", "carrier"],
                "description": "Which entity folder the host lives in. Drives where the build script looks for the host file."
              },
              "target_slot": {
                "type": "string",
                "title": "Target Slot",
                "description": "Name of the specific FMC slot on the host (matches a slot name in the host's fmc[] array). Omit if the relationship applies to any slot on the host. One entry per slot, so a host with multiple compatible sites gets multiple entries."
              },
              "verified_by": { "$ref": "#/$defs/verified_by" },
              "notes": { "$ref": "#/$defs/relationship_notes" }
            }
          }
        }
      }
    },

    "fmc_pinout": {
      "type": "object",
      "title": "FMC Pinout",
      "description": "Pin-level FMC connector routing for one entity, used for automated card<->host compatibility checking. One file per entity under fmc-pinouts/<entity-type>/<vendor>/<mpn>.json — the filename stem matches the entity mpn and the parent folder matches its vendor. Signals use canonical VITA 57.1 names (LA00_CC_P, HA17_N, DP0_C2M_P, CLK0_M2C_N, GBTCLK0_M2C_P) so a card's used-signal set joins directly against a host slot's routed-signal set. Slot names match the host's expansion FMC slot names (and the fmc-mates target_slot).",
      "required": ["mpn", "side", "slots"],
      "additionalProperties": false,
      "properties": {
        "mpn": { "type": "string", "title": "Entity MPN", "description": "MPN of the entity this pinout belongs to. Must match the filename stem and an existing entity." },
        "side": {
          "type": "string",
          "title": "Side",
          "enum": ["host", "card"],
          "description": "host = board/carrier/kit that provides the FMC site (signals routed to its FPGA). card = FMC mezzanine that consumes the site (signals it uses)."
        },
        "device": { "type": "string", "title": "FPGA Device", "description": "Host FPGA part the FMC signals route to (e.g. xczu9eg-ffvb1156-2-e); determines the package used for bank lookup. Omit on the card side." },
        "source": { "type": "string", "title": "Source", "description": "Provenance of the pin data, e.g. 'xilinx-board-store' or 'docs.opsero.com'." },
        "source_ref": { "type": "string", "title": "Source Reference", "description": "Specific source locator, e.g. 'boards/Xilinx/zcu102/3.4' or 'op031/datasheet/pin-configuration'." },
        "slots": {
          "type": "array",
          "title": "Slots",
          "minItems": 1,
          "description": "One entry per FMC site on the entity. A card has a single edge connector (one slot).",
          "items": { "$ref": "#/$defs/fmc_pinout_slot" }
        }
      }
    },

    "fmc_pinout_slot": {
      "type": "object",
      "title": "FMC Pinout Slot",
      "required": ["slot", "type", "signals"],
      "additionalProperties": false,
      "properties": {
        "slot": { "type": "string", "title": "Slot Name", "description": "On a host, matches a slot name in the entity's expansion FMC list (e.g. HPC0, LPC1). On a card, the card's own connector label (typically 'FMC')." },
        "type": { "type": "string", "title": "Connector Type", "enum": ["lpc", "hpc", "fmcp"], "description": "LPC / HPC / FMC+ class of this physical site." },
        "signals": {
          "type": "array",
          "title": "Signals",
          "description": "One entry per routed (host) or used (card) FMC signal. P and N of a differential pair are separate entries. A signal's absence means it is not routed/used at this site (closed-world: the compatibility engine treats a missing signal as 'not connected', never 'unknown').",
          "items": { "$ref": "#/$defs/fmc_pinout_signal" }
        },
        "groups": {
          "type": "array",
          "title": "Functional Groups",
          "description": "Card side only. Independent functional units the card can degrade across — e.g. the four ports of a quad Ethernet FMC. A group is 'satisfied' when every signal it lists is routed on the candidate host slot AND, if same_bank is set, all those signals land in a single host I/O bank (an RGMII port needs its clock and data in one bank). The pair verdict is then compatible (all groups satisfied), partial (some), or incompatible (none). Signals not named in any group are informational and do not affect the verdict. Omit groups entirely for a single-function card — the engine then treats every used signal as one implicit all-or-nothing group (no partial, no same_bank).",
          "items": { "$ref": "#/$defs/fmc_pinout_group" }
        }
      }
    },

    "fmc_pinout_group": {
      "type": "object",
      "title": "FMC Functional Group",
      "description": "A card-side functional unit checked independently by the compatibility engine.",
      "required": ["name", "signals"],
      "additionalProperties": false,
      "properties": {
        "name": { "type": "string", "title": "Group Name", "description": "Human label shown in compatibility results, e.g. 'Port 0' or 'RGMII Port 2'." },
        "signals": {
          "type": "array",
          "title": "Signals",
          "minItems": 1,
          "items": { "type": "string" },
          "description": "Canonical VITA 57.1 signal names that must all be routed for this group to function. Every name should also appear in the slot's signals[] list."
        },
        "same_bank": { "type": "boolean", "title": "Same Bank Required", "description": "When true, all of this group's signals must route to a single host I/O bank (default false). Used for source-synchronous interfaces such as RGMII where the receive clock must share a bank with its data." },
        "min_lanes": { "type": "integer", "minimum": 1, "title": "Minimum Lanes", "description": "Set for a multi-lane transceiver slot (PCIe / QSFP). The group's DP signals are treated as serial lanes (4 signals per DP index) that degrade in width; the group is satisfied when at least this many lanes are routed, and the result reports how many of the full lane set the host provides (e.g. 'x1 of x4'). Non-DP signals in the group remain all-required. Mutually exclusive with same_bank." }
      }
    },

    "fmc_pinout_signal": {
      "type": "object",
      "title": "FMC Pinout Signal",
      "description": "A single FMC signal at a site. Host entries carry the FPGA-side facts (pin/bank); card entries carry the connector position and the card net it drives.",
      "required": ["signal"],
      "additionalProperties": false,
      "properties": {
        "signal": { "type": "string", "title": "Signal", "description": "Canonical VITA 57.1 signal name (e.g. LA00_CC_P, HA17_N, HB06_CC_P, CLK1_M2C_N, DP0_C2M_P, GBTCLK0_M2C_P)." },
        "pin": { "type": "string", "title": "FPGA Pin", "description": "Host side: FPGA package pin (e.g. Y4)." },
        "bank": { "type": "integer", "title": "I/O Bank", "description": "Host side: FPGA I/O or GT bank the pin belongs to. Drives co-bank compatibility checks." },
        "io_type": { "type": "string", "title": "I/O Type", "description": "Host side: bank I/O type from the package file (HP / HD / HR / GTH / GTY / ...)." },
        "iostandard": { "type": "string", "title": "IOSTANDARD", "description": "Host side: default IOSTANDARD asserted by the board file (reflects the site's default VADJ for regular I/O; a placeholder on GT pins)." },
        "fmc_pin": { "type": "string", "title": "FMC Connector Pin", "description": "Card side: physical VITA 57.1 connector position (e.g. G6, H4)." },
        "net": { "type": "string", "title": "Card Net", "description": "Card side: the card net this FMC signal connects to (e.g. E0_RX_CLK)." },
        "function": { "type": "string", "title": "Function", "description": "Card side: human description of the net's purpose." }
      }
    },

    "mpn": {
      "type": "string",
      "title": "Part Number",
      "description": "Manufacturer part number (MPN / SKU). Must be unique within its entity type. URL-safe characters only (letters, digits, hyphens, dots, underscores). For non-buyable carriers (e.g. the KV260's carrier, which is only sold inside the kit), invent a stable MPN like <kit-mpn>-Carrier."
    },
    "name": {
      "type": "string",
      "title": "Name",
      "description": "Display name."
    },
    "status": {
      "type": "string",
      "title": "Status",
      "enum": ["active", "nrnd", "eol", "discontinued"],
      "description": "Lifecycle status of the entity."
    },
    "url": {
      "type": "string",
      "title": "Product Page",
      "format": "uri",
      "description": "Product page URL. For carriers that are not sold standalone, point at the parent kit's URL."
    },
    "vendor": {
      "type": "string",
      "title": "Vendor",
      "description": "Vendor key (must match a key in vendors.json board_vendors)."
    },
    "price_or_null": {
      "title": "Price",
      "description": "Price. Use null when the price is unknown, not published, or the entity is not sold standalone.",
      "anyOf": [
        { "$ref": "#/$defs/price" },
        { "type": "null" }
      ]
    },
    "verified_by": {
      "type": "string",
      "title": "Verified By",
      "enum": ["vendor", "user"],
      "description": "Who asserts this compatibility. 'vendor' = officially supported by the vendor. 'user' = community / user-reported. A future 'fpgadeveloper' value will indicate automated verification from vadj range + pin assignment data."
    },
    "relationship_notes": {
      "type": "string",
      "title": "Notes",
      "description": "Free-text caveats specific to this combination (e.g. 'Only port 0 is usable on HPC1', 'Requires Vivado 2022.1+', 'Needs cooling modification')."
    },

    "price": {
      "type": "object",
      "required": ["value", "currency"],
      "additionalProperties": false,
      "properties": {
        "value": {
          "type": "number",
          "minimum": 0,
          "description": "Price as a numeric value."
        },
        "currency": {
          "type": "string",
          "description": "ISO 4217 currency code (e.g. USD, EUR, GBP)."
        }
      }
    },
    "device": {
      "type": "object",
      "title": "Device",
      "required": ["part", "vendor"],
      "additionalProperties": false,
      "properties": {
        "part": {
          "type": "string",
          "description": "Orderable manufacturer part number."
        },
        "vendor": {
          "type": "string",
          "description": "Silicon vendor key (must match a key in vendors.json silicon_vendors)."
        }
      }
    },
    "pcie_interface": {
      "type": "object",
      "required": ["type"],
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "enum": ["Edge", "Slot", "PCIe/104", "iPass", "MCIO", "MiniPCIe-Half", "MiniPCIe-Full"],
          "description": "Physical form factor of the PCIe interface. M.2 sockets and M.2 form-factor boards live in a separate `m2` array, not here — M.2 is a multi-protocol form factor (PCIe / SATA / USB), so modelling it under `pcie` was misleading. MCIO is the SFF-TA-1016 cabled PCIe connector (Mini Cool Edge IO). MiniPCIe slots are PCIe x1 + USB + (optionally) mSATA — use `msata_capable: true` to mark slots that also accept mSATA SSDs."
        },
        "lanes": {
          "type": "integer",
          "minimum": 1,
          "description": "Number of PCIe lanes."
        },
        "gen": {
          "type": "integer",
          "minimum": 1,
          "maximum": 6,
          "description": "PCIe generation (1-6)."
        },
        "msata_capable": {
          "type": "boolean",
          "description": "Set on MiniPCIe slots that also accept mSATA SSDs (the slot is electrically multiplexed). Not a separate storage entry — the physical socket is one connector."
        }
      }
    },
    "m2_interface": {
      "type": "object",
      "required": ["key", "direction"],
      "additionalProperties": false,
      "description": "An M.2 connector — either a socket the board provides, or the M.2 edge-connector of a board that IS an M.2 module. M.2 is a physical form factor, not an interface, so the protocols routed to the connector are listed separately in `interfaces`.",
      "properties": {
        "key": {
          "type": "string",
          "enum": ["M-key", "E-key", "B-key", "B+M-key"],
          "description": "M.2 keying. M-key = PCIe x4 NVMe (the common storage slot), E-key = WiFi/BT modules (PCIe x1 + USB + SDIO), B-key = WWAN / older SATA SSDs, B+M-key = double-keyed card / socket compatible with both."
        },
        "direction": {
          "type": "string",
          "enum": ["socket", "form-factor"],
          "description": "socket = the board PROVIDES an M.2 receptacle (you plug a module into it — e.g. Alinx AXU5EVB-P). form-factor = the BOARD itself is in M.2 form factor and its edge connector plugs into a host (e.g. RHS Research LiteFury / NiteFury)."
        },
        "interfaces": {
          "type": "array",
          "items": { "type": "string", "enum": ["PCIe", "SATA", "USB"] },
          "minItems": 1,
          "uniqueItems": true,
          "description": "Protocols actually routed to this M.2 connector. Most M-key sockets carry PCIe (NVMe); the Xilinx ZCU series carry SATA only; E-key sockets typically carry PCIe + USB. When omitted, defaults are inferred by key (M-key/B+M-key → PCIe, E-key → PCIe+USB, B-key → USB)."
        },
        "lanes": {
          "type": "integer",
          "minimum": 1,
          "description": "Number of PCIe lanes routed (only meaningful when `interfaces` includes PCIe)."
        },
        "gen": {
          "type": "integer",
          "minimum": 1,
          "maximum": 6,
          "description": "PCIe generation (1-6). Only meaningful when `interfaces` includes PCIe."
        },
        "length": {
          "type": "string",
          "enum": ["2230", "2242", "2260", "2280", "22110"],
          "description": "Module length in M.2 nomenclature (WWLL, where WW=width=22mm, LL=length in mm). 2280 is the dominant size for NVMe SSDs; 2230 is common for WiFi modules."
        }
      }
    },
    "ethernet_interface": {
      "type": "object",
      "required": ["speed", "ports"],
      "additionalProperties": false,
      "properties": {
        "speed": {
          "type": "integer",
          "description": "Link speed in Mbps (e.g. 100, 1000, 10000)."
        },
        "ports": {
          "type": "integer",
          "minimum": 1,
          "description": "Number of ports at this speed."
        },
        "endpoint": {
          "type": "string",
          "enum": ["phy", "rj45", "phy+rj45"],
          "default": "phy+rj45",
          "description": "Which part of the Ethernet signal chain this entity carries. 'phy' = PHY/MAC exposed with no connector (typical SoM — the RJ45 jack is on the carrier). 'rj45' = RJ45 jack + magnetics with the PHY supplied by a mated SoM (some carriers). 'phy+rj45' = the complete port (standalone boards). Defaults to 'phy+rj45' when omitted."
        }
      }
    },
    "usb_connector": {
      "type": "string",
      "enum": ["Type-A", "Type-B", "Type-C", "Mini-B", "Micro-B", "Header"],
      "description": "Physical USB connector on the board."
    },
    "usb_interface": {
      "type": "object",
      "required": ["speed", "role"],
      "additionalProperties": false,
      "properties": {
        "connector": { "$ref": "#/$defs/usb_connector" },
        "endpoint": {
          "type": "string",
          "enum": ["connector", "phy"],
          "default": "connector",
          "description": "Which part of the USB signal chain this entity carries. 'connector' = a complete USB port terminating in a physical receptacle (standalone boards — the default). 'phy' = a USB PHY / ULPI transceiver routed to the entity's mating connectors with no receptacle of its own; the receptacle is on a mated carrier (typical SoM). Defaults to 'connector' when omitted. When 'phy', `connector` is omitted; otherwise `connector` is required."
        },
        "speed": {
          "type": "string",
          "enum": ["1.1", "2.0", "3.0", "3.1", "3.2", "4"],
          "description": "USB version (e.g. 2.0 = High-Speed 480 Mbps, 3.0 = SuperSpeed 5 Gbps)."
        },
        "role": {
          "type": "string",
          "enum": ["host", "device", "otg"],
          "description": "host = peripherals plug in (Type-A), device = board acts as peripheral, otg = either."
        },
        "ports": {
          "type": "integer",
          "minimum": 1,
          "description": "Number of physical ports of this kind. Defaults to 1 if omitted."
        }
      },
      "allOf": [
        {
          "if": { "required": ["endpoint"], "properties": { "endpoint": { "const": "phy" } } },
          "then": { "not": { "required": ["connector"] } },
          "else": { "required": ["connector"] }
        }
      ]
    },
    "usb_bridge_interface": {
      "type": "object",
      "required": ["connector", "functions"],
      "additionalProperties": false,
      "properties": {
        "connector": { "$ref": "#/$defs/usb_connector" },
        "functions": {
          "type": "array",
          "items": { "type": "string", "enum": ["jtag", "uart", "i2c"] },
          "minItems": 1,
          "uniqueItems": true,
          "description": "What the bridge does — typically [\"jtag\", \"uart\"] for combined chips like FT2232. 'i2c' covers bridge chips that also expose an I2C utility channel (common on Lattice FTDI-based eval boards)."
        },
        "ports": {
          "type": "integer",
          "minimum": 1,
          "description": "Number of physical ports of this kind. Defaults to 1 if omitted."
        }
      }
    },
    "flash_interface": {
      "type": "object",
      "required": ["type", "size_mb"],
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "enum": ["QSPI", "OSPI", "SPI", "BPI", "eMMC", "NAND"],
          "description": "Flash family. QSPI/OSPI/SPI/BPI are NOR (typically used for FPGA configuration + some user data). eMMC is managed NAND for Linux rootfs storage. NAND is raw NAND."
        },
        "size_mb": {
          "type": "integer",
          "minimum": 1,
          "description": "Total capacity of this chip in megabytes. 1 GB → 1024."
        },
        "width_bits": {
          "type": "integer",
          "enum": [1, 2, 4, 8, 16],
          "description": "Data bus width. Usually implicit from type (SPI=1, QSPI=4, OSPI=8). Useful to set for BPI (typically 16) and NAND (8 or 16)."
        }
      }
    },
    "memory_interface": {
      "type": "object",
      "required": ["type", "size_mb"],
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "enum": ["SDRAM", "DDR2", "DDR3", "DDR3L", "DDR4", "DDR5", "LPDDR", "LPDDR2", "LPDDR3", "LPDDR4", "LPDDR4X", "LPDDR5", "LPDDR5X", "GDDR5", "GDDR5X", "GDDR6", "GDDR6X", "HBM2", "HBM2E", "HBM3", "QDR-II+", "QDR-IV", "RLD3", "HyperRAM", "PSRAM"],
          "description": "DRAM family. 'SDRAM' is single-data-rate SDRAM; 'LPDDR' is the original low-power/mobile DDR (LPDDR1). HBM variants are on-package, not strictly DDR — listed here because they fill the same role in spec sheets. RLD3 (reduced-latency DRAM), HyperRAM (serial/octal DRAM), and PSRAM (pseudo-static RAM) are specialty DRAM types found on smaller or high-bandwidth boards."
        },
        "size_mb": {
          "type": "integer",
          "minimum": 1,
          "description": "Total capacity of this bank in megabytes. 4 GB → 4096."
        },
        "form_factor": {
          "type": "string",
          "enum": ["component", "sodimm", "dimm", "udimm", "rdimm", "lrdimm"],
          "description": "Physical form. 'component' = soldered DRAM (default). Slot-based = sodimm / dimm / etc."
        },
        "width_bits": {
          "type": "integer",
          "enum": [8, 16, 18, 32, 36, 40, 48, 64, 72, 80, 128, 256],
          "description": "Data bus width. 72 typically implies ECC (64-bit data + 8-bit ECC); 40 and 80 are likewise data+ECC widths on some SoMs. 8 is a narrow-bus single DDR3 chip. 48 is a non-ECC bank built from three 16-bit chips. 18/36 are common for QDR-II+ / QDR-IV high-bandwidth SRAM."
        },
        "ecc": {
          "type": "boolean",
          "description": "True when this bank has ECC (error-correcting) support."
        }
      }
    },
    "sram_interface": {
      "type": "object",
      "title": "SRAM Chip",
      "description": "A discrete static or non-volatile RAM chip — distinct from the DRAM banks in `memory`. Covers asynchronous SRAM, synchronous SRAM (SSRAM), and the non-volatile MRAM / FRAM / nvSRAM families. HyperRAM and PSRAM are pseudo-static DRAM and live in `memory`, not here. One entry per chip.",
      "required": ["type", "size_kb"],
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "enum": ["SRAM", "SSRAM", "MRAM", "FRAM", "nvSRAM"],
          "description": "Static-RAM family. 'SRAM' = asynchronous static RAM; 'SSRAM' = synchronous SRAM (pipelined / ZBT / QDR-class). MRAM (magnetoresistive), FRAM (ferroelectric), and nvSRAM are non-volatile — they retain data without power."
        },
        "size_kb": {
          "type": "integer",
          "minimum": 1,
          "description": "Total capacity of this chip in kilobytes (1 MB -> 1024). SRAM chips are far smaller than DRAM, so capacity is recorded in KB rather than MB."
        },
        "width_bits": {
          "type": "integer",
          "enum": [1, 2, 4, 8, 16, 32, 36],
          "description": "Data bus width. 1/2/4 are serial SRAMs (SPI / QSPI); 8/16/32 are parallel; 36 is a parity / byte-enable width seen on some SSRAMs."
        },
        "interface": {
          "type": "string",
          "enum": ["parallel", "SPI", "QSPI"],
          "description": "Bus the chip hangs off. 'parallel' is the default for classic asynchronous / synchronous SRAM; serial SRAMs use SPI or QSPI."
        }
      }
    },

    "video": {
      "type": "object",
      "title": "Video I/O",
      "description": "Video input/output interfaces. Only include keys with non-zero values.",
      "additionalProperties": false,
      "properties": {
        "hdmi_in": { "type": "integer", "title": "HDMI In", "minimum": 1 },
        "hdmi_out": { "type": "integer", "title": "HDMI Out", "minimum": 1 },
        "displayport": { "type": "integer", "title": "DisplayPort", "minimum": 1 },
        "sdi_in": { "type": "integer", "title": "SDI In", "minimum": 1 },
        "sdi_out": { "type": "integer", "title": "SDI Out", "minimum": 1 },
        "aes_in": { "type": "integer", "title": "AES3 In", "minimum": 1 },
        "aes_out": { "type": "integer", "title": "AES3 Out", "minimum": 1 },
        "mipi_dsi": { "type": "integer", "title": "MIPI DSI", "minimum": 1 },
        "mipi_csi": { "type": "integer", "title": "MIPI CSI", "minimum": 1 },
        "vga_out": { "type": "integer", "title": "VGA Out", "minimum": 1 }
      },
      "minProperties": 1
    },
    "networking": {
      "type": "object",
      "title": "Networking",
      "description": "All networking interfaces — Ethernet ports plus high-speed optical/copper cages.",
      "additionalProperties": false,
      "properties": {
        "ethernet": {
          "type": "array",
          "title": "RJ45",
          "description": "Wired Ethernet ports (RJ45 connectors), grouped by link speed. Separate from optical/copper SFP/QSFP cages which sit alongside this in the same `networking` object.",
          "items": {
            "$ref": "#/$defs/ethernet_interface"
          },
          "minItems": 1
        },
        "sfp":         { "type": "integer", "title": "SFP",         "minimum": 1, "description": "SFP cage, 1x1G — original 1 GbE generation." },
        "sfp_plus":    { "type": "integer", "title": "SFP+",        "minimum": 1, "description": "SFP+ cage, 1x10G — 10 GbE." },
        "sfp28":       { "type": "integer", "title": "SFP28",       "minimum": 1, "description": "SFP28 cage, 1x25G — 25 GbE / 32G FC." },
        "sfp56":       { "type": "integer", "title": "SFP56",       "minimum": 1, "description": "SFP56 cage, 1x50G PAM4 — 50 GbE." },
        "sfp_dd":      { "type": "integer", "title": "SFP-DD",      "minimum": 1, "description": "SFP-DD double-density cage, 2x50G PAM4 — 100 GbE." },
        "qsfp":        { "type": "integer", "title": "QSFP",        "minimum": 1, "description": "QSFP cage, 4x1G — 4 GbE (rare on FPGA boards)." },
        "qsfp_plus":   { "type": "integer", "title": "QSFP+",       "minimum": 1, "description": "QSFP+ cage, 4x10G — 40 GbE." },
        "qsfp28":      { "type": "integer", "title": "QSFP28",      "minimum": 1, "description": "QSFP28 cage, 4x25G — 100 GbE / 32G FC." },
        "qsfp56":      { "type": "integer", "title": "QSFP56",      "minimum": 1, "description": "QSFP56 cage, 4x50G PAM4 — 200 GbE." },
        "qsfp_dd":     { "type": "integer", "title": "QSFP-DD",     "minimum": 1, "description": "QSFP-DD double-density cage, 8x50G PAM4 — 400 GbE." },
        "qsfp_dd800":  { "type": "integer", "title": "QSFP-DD800",  "minimum": 1, "description": "QSFP-DD800 cage, 8x100G PAM4 — 800 GbE." },
        "osfp":        { "type": "integer", "title": "OSFP",        "minimum": 1, "description": "OSFP cage, 8 lanes — 400/800 GbE." },
        "osfp_xd":     { "type": "integer", "title": "OSFP-XD",     "minimum": 1, "description": "OSFP-XD extended-density cage, 16 lanes — 1.6 TbE." },
        "cfp":         { "type": "integer", "title": "CFP",         "minimum": 1, "description": "CFP (CFP1) cage — legacy 100 GbE." },
        "cfp2":        { "type": "integer", "title": "CFP2",        "minimum": 1, "description": "CFP2 cage — 100/200 GbE." },
        "cfp4":        { "type": "integer", "title": "CFP4",        "minimum": 1, "description": "CFP4 cage — 100 GbE (smaller body)." },
        "cfp8":        { "type": "integer", "title": "CFP8",        "minimum": 1, "description": "CFP8 cage — 400 GbE." }
      },
      "minProperties": 1
    },
    "fmc_slot": {
      "type": "object",
      "title": "FMC Slot",
      "description": "A single FMC site on a host. vadj_min/vadj_max are reserved for future automated compatibility checking.",
      "required": ["slot", "type"],
      "additionalProperties": false,
      "properties": {
        "slot": {
          "type": "string",
          "title": "Slot Name",
          "description": "Name the vendor prints on the silkscreen (e.g. HPC0, HPC1, LPC, J3). Used by fmc-mates relationships to target a specific slot."
        },
        "type": {
          "type": "string",
          "title": "Connector Type",
          "enum": ["lpc", "hpc", "fmcp"],
          "description": "LPC (160-pin), HPC (400-pin), or FMC+ (560-pin)."
        },
        "vadj_min": {
          "type": "number",
          "title": "VADJ Min (V)",
          "description": "Minimum VADJ voltage supported by this slot. Reserved for future fpgadeveloper-validated compatibility."
        },
        "vadj_max": {
          "type": "number",
          "title": "VADJ Max (V)",
          "description": "Maximum VADJ voltage supported by this slot."
        }
      }
    },
    "expansion": {
      "type": "object",
      "title": "Expansion",
      "description": "Expansion connectors and headers. RFMC is kept as a count for now — it will be promoted to a richer first-class form once we model RFMC mezzanine cards.",
      "additionalProperties": false,
      "properties": {
        "fmc": {
          "type": "array",
          "title": "FMC",
          "description": "FMC sites on the board/carrier/kit. One entry per slot. Each slot is a {slot, type} object — slot names appear on the silkscreen and are referenced by fmc-mates relationships.",
          "items": { "$ref": "#/$defs/fmc_slot" },
          "minItems": 1
        },
        "pmod": { "type": "integer", "title": "Pmod", "minimum": 1 },
        "arduino": { "type": "integer", "title": "Arduino", "minimum": 1 },
        "raspberry_pi": { "type": "integer", "title": "Raspberry Pi", "minimum": 1 },
        "click": { "type": "integer", "title": "Click", "minimum": 1, "description": "Mikroe Click / mikroBUS sockets." },
        "syzygy": { "type": "integer", "title": "SYZYGY", "minimum": 1 },
        "syzygy_trx": { "type": "integer", "title": "SYZYGY TRX", "minimum": 1 },
        "rfmc": { "type": "integer", "title": "RFMC", "minimum": 1 },
        "gpio_header": { "type": "integer", "title": "GPIO Header", "minimum": 1, "description": "Count of generic GPIO breakout headers (no standardised pinout — pin count varies)." },
        "xadc_header": { "type": "integer", "title": "XADC Header", "minimum": 1, "description": "Count of XADC / SYSMON / SmartVIO analog-channel breakout headers (common on Xilinx 7-series and Ultrascale eval boards)." },
        "hsmc": { "type": "integer", "title": "HSMC", "minimum": 1, "description": "Count of HSMC (High-Speed Mezzanine Card) connectors — the Altera / Terasic 172-pin mezzanine standard." },
        "cruvi_hs": { "type": "integer", "title": "CRUVI HS", "minimum": 1, "description": "Count of CRUVI HS (high-speed) mezzanine connectors." },
        "cruvi_ls": { "type": "integer", "title": "CRUVI LS", "minimum": 1, "description": "Count of CRUVI LS (low-speed) mezzanine connectors." },
        "grove": { "type": "integer", "title": "Grove", "minimum": 1, "description": "Count of Seeed Grove 4-pin connectors." },
        "boards96_ls": { "type": "integer", "title": "96Boards Low-Speed", "minimum": 1, "description": "Count of 96Boards low-speed (40-pin) expansion connectors." },
        "boards96_hs": { "type": "integer", "title": "96Boards High-Speed", "minimum": 1, "description": "Count of 96Boards high-speed (60-pin) expansion connectors." }
      },
      "minProperties": 1
    },
    "storage": {
      "type": "object",
      "title": "Storage",
      "description": "Storage sockets and ports.",
      "additionalProperties": false,
      "properties": {
        "sata": { "type": "integer", "title": "SATA", "minimum": 1 },
        "sd": { "type": "integer", "title": "SD", "minimum": 1, "description": "Full-size SD card slot count." },
        "microsd": { "type": "integer", "title": "microSD", "minimum": 1, "description": "Micro-SD card slot count." }
      },
      "minProperties": 1
    },
    "wireless": {
      "type": "object",
      "title": "Wireless",
      "description": "Wireless connectivity.",
      "additionalProperties": false,
      "properties": {
        "wifi": { "type": "boolean", "title": "WiFi" },
        "bluetooth": { "type": "boolean", "title": "Bluetooth" }
      },
      "minProperties": 1
    },
    "analog_chip": {
      "type": "object",
      "title": "Analog Chip",
      "description": "A discrete ADC, DAC, RF transceiver, or analog front-end soldered to the board — distinct from the FPGA's built-in XADC/SYSMON, and from chips on FMC/expansion daughter cards.",
      "required": ["role"],
      "additionalProperties": false,
      "properties": {
        "role": {
          "type": "string",
          "title": "Role",
          "enum": ["adc", "dac", "rf_transceiver", "afe"],
          "description": "Chip's function. adc/dac = standalone sample-rate-and-bits chip; rf_transceiver = wideband RF AFE (AD9361 etc.); afe = generic analog conditioning frontend."
        },
        "part": {
          "type": "string",
          "title": "Part Number",
          "description": "Manufacturer part number (e.g. LTC2387, AD9361). Optional but recommended — lets users search by chip."
        },
        "channels": { "type": "integer", "minimum": 1, "title": "Channels", "description": "Number of channels on this chip." },
        "resolution_bits": { "type": "integer", "minimum": 1, "title": "Resolution (bits)", "description": "Sample resolution. ADC/DAC only." },
        "sample_rate_msps": { "type": "number", "minimum": 0, "title": "Sample Rate (MSPS)", "description": "Maximum sample rate. ADC/DAC only." },
        "frequency_min_mhz": { "type": "number", "minimum": 0, "title": "Min Frequency (MHz)", "description": "Minimum RF operating frequency. RF transceiver / AFE only." },
        "frequency_max_mhz": { "type": "number", "minimum": 0, "title": "Max Frequency (MHz)", "description": "Maximum RF operating frequency. RF transceiver / AFE only." }
      }
    },
    "sensor": {
      "type": "object",
      "title": "Sensor",
      "description": "An on-board sensor chip. One entry per sensor.",
      "required": ["type"],
      "additionalProperties": false,
      "properties": {
        "type": {
          "type": "string",
          "title": "Sensor Type",
          "enum": ["imu", "accelerometer", "gyroscope", "magnetometer", "temperature", "pressure", "humidity", "light", "proximity", "microphone", "camera"],
          "description": "Sensor category. 'imu' implies combined accelerometer + gyroscope (and usually magnetometer). 'camera' is an image sensor soldered to the board — distinct from video.mipi_csi, which counts camera connectors."
        },
        "part": {
          "type": "string",
          "title": "Part Number",
          "description": "Manufacturer part number (e.g. MPU-9250, TMP102). Optional but recommended."
        }
      }
    },
    "audio": {
      "type": "object",
      "title": "Audio",
      "description": "On-board audio I/O. Distinct from analog[] — captures user-facing audio jacks and the codec routing them.",
      "additionalProperties": false,
      "properties": {
        "codec": { "type": "string", "title": "Codec Part", "description": "Audio codec chip part number (e.g. SSM2603, WM8731, TLV320AIC23)." },
        "line_in": { "type": "integer", "title": "Line In", "minimum": 1 },
        "line_out": { "type": "integer", "title": "Line Out", "minimum": 1 },
        "headphone_jack": { "type": "integer", "title": "Headphone", "minimum": 1 },
        "microphone": { "type": "integer", "title": "Microphone", "minimum": 1 },
        "speaker": { "type": "boolean", "title": "Speaker", "description": "On-board speaker." }
      },
      "minProperties": 1
    },
    "clocking": {
      "type": "object",
      "title": "Clocking",
      "description": "Reference-clock features beyond the FPGA's own internal oscillators.",
      "additionalProperties": false,
      "properties": {
        "programmable": { "type": "boolean", "title": "Programmable Clock", "description": "Board includes a programmable clock generator (Si5xxx, IDT FemtoClock, etc.)." },
        "sma_clock_in": { "type": "integer", "title": "Clock Input SMA", "minimum": 1, "description": "Count of SMA connectors for external reference-clock inputs." },
        "sma_clock_out": { "type": "integer", "title": "Clock Output SMA", "minimum": 1 },
        "gps_disciplined": { "type": "boolean", "title": "GPS-Disciplined Oscillator", "description": "Board has an on-board GPSDO for time-locked operation." }
      },
      "minProperties": 1
    },
    "transceiver_connector": {
      "type": "object",
      "title": "High-Speed Connector",
      "description": "A connector whose purpose is to break out the FPGA's high-speed serial transceiver lanes (GTX / GTH / GTY / PS-GTR / ...) outside the PCIe protocol wrapper. One entry per connector form.",
      "required": ["form"],
      "additionalProperties": false,
      "properties": {
        "form": {
          "type": "string",
          "enum": ["SMA", "FireFly", "SlimSAS", "BullsEye", "MXP", "Mini-DP", "Z-Ray"],
          "description": "Physical connector type. SMA = coaxial SMA pairs (one TX+RX pair = one lane). FireFly = Samtec FireFly micro optical/copper module slot. SlimSAS = SFF-8654 cabled connector. BullsEye = Samtec high-density coaxial test connector. MXP = Samtec MXP cabled connector. Mini-DP = mini-DisplayPort connector wired to FPGA transceivers (not video). Z-Ray = Samtec Z-Ray high-density array connector."
        },
        "count": {
          "type": "integer",
          "minimum": 1,
          "description": "Number of connectors of this form. Defaults to 1 if omitted."
        },
        "lanes": {
          "type": "integer",
          "minimum": 1,
          "description": "Total number of transceiver lanes (TX/RX pairs) routed across all `count` connectors of this form."
        },
        "transceiver": {
          "type": "string",
          "description": "Transceiver type the lanes belong to — e.g. GTP, GTX, GTH, GTY, GTYP, GTM, GTF, PS-GTR (AMD), or an Intel / Lattice / etc. equivalent. Free-form because transceiver naming is silicon-vendor-specific."
        },
        "max_rate_gbps": {
          "type": "number",
          "minimum": 0,
          "description": "Maximum per-lane line rate in Gbps."
        }
      }
    },
    "high_speed_io": {
      "type": "array",
      "title": "High-Speed I/O",
      "description": "Connectors that break out the FPGA's high-speed serial transceiver lanes (GTX / GTH / GTY / PS-GTR / ...) outside the PCIe protocol wrapper. One entry per connector form.",
      "items": { "$ref": "#/$defs/transceiver_connector" },
      "minItems": 1
    },
    "serial": {
      "type": "object",
      "title": "Serial",
      "description": "Industrial / automotive serial interfaces with on-board transceivers. The USB-bridged UART/JTAG ports live in usb_bridge, not here.",
      "additionalProperties": false,
      "properties": {
        "can": { "type": "integer", "title": "CAN", "minimum": 1, "description": "Count of CAN 2.0 / CAN-FD transceiver ports." },
        "rs485": { "type": "integer", "title": "RS-485", "minimum": 1 },
        "rs232": { "type": "integer", "title": "RS-232", "minimum": 1 },
        "lin": { "type": "integer", "title": "LIN", "minimum": 1 }
      },
      "minProperties": 1
    },
    "user_io": {
      "type": "object",
      "title": "User I/O",
      "description": "On-board user I/O elements. Counts only — for boot-mode DIP switches and other config-only inputs, use board notes.",
      "additionalProperties": false,
      "properties": {
        "leds": { "type": "integer", "title": "LEDs", "minimum": 1, "description": "Count of user-controllable single-colour LEDs (excludes power/status LEDs and excludes RGB LEDs — those go in rgb_leds)." },
        "rgb_leds": { "type": "integer", "title": "RGB LEDs", "minimum": 1, "description": "Count of user-controllable RGB / multi-colour LEDs. Counted separately from plain leds because they take 3 FPGA pins each." },
        "pushbuttons": { "type": "integer", "title": "Pushbuttons", "minimum": 1, "description": "Count of user pushbuttons (excludes reset)." },
        "dip_switches": { "type": "integer", "title": "DIP Switches", "minimum": 1, "description": "Count of user DIP switch positions (excludes boot-mode switches)." },
        "seven_segment": { "type": "integer", "title": "7-Segment Displays", "minimum": 1 },
        "rotary_encoders": { "type": "integer", "title": "Rotary Encoders", "minimum": 1 }
      },
      "minProperties": 1
    },
    "features": {
      "type": "object",
      "title": "Features",
      "description": "Boolean capability flags that don't warrant their own structured section.",
      "additionalProperties": false,
      "properties": {
        "power_monitoring": { "type": "boolean", "title": "Power Monitoring", "description": "On-board PMBus / INA-class power-rail monitoring." },
        "programmable_vadj": { "type": "boolean", "title": "Programmable VADJ", "description": "User can adjust VADJ / core voltage rails (typically via I2C / PMBus)." },
        "rtc": { "type": "boolean", "title": "RTC", "description": "On-board real-time clock chip. Set `battery_backed_rtc` instead (or as well) when the RTC is backed by a coin cell or supercap." },
        "battery_backed_rtc": { "type": "boolean", "title": "Battery-Backed RTC", "description": "RTC backed by coin cell or supercap." },
        "secure_element": { "type": "boolean", "title": "Secure Element", "description": "Discrete TPM / ATECC / equivalent cryptographic chip." },
        "tamper_detection": { "type": "boolean", "title": "Tamper Detection", "description": "On-board tamper-detection circuitry." }
      },
      "minProperties": 1
    },
    "display": {
      "type": "object",
      "title": "Display",
      "description": "Small on-board display panels (character LCD, OLED). Distinct from `video` — video covers source/sink ports to external screens (HDMI, DisplayPort, VGA), `display` covers indicator-class panels soldered to the board.",
      "additionalProperties": false,
      "properties": {
        "character_lcd": {
          "type": "string",
          "title": "Character LCD",
          "description": "HD44780-class character LCD, expressed as 'ROWSxCOLS' (e.g. '2x16', '4x20')."
        },
        "oled": {
          "type": "string",
          "title": "OLED",
          "description": "Monochrome / RGB OLED panel resolution as 'WIDTHxHEIGHT' pixels (e.g. '128x32', '128x64', '64x48')."
        }
      },
      "minProperties": 1
    },
    "eeprom_chip": {
      "type": "object",
      "title": "EEPROM",
      "description": "A small on-board EEPROM (typically I2C/SPI, used for MAC address, board UID, board identification, or user configuration). One entry per chip.",
      "required": ["purpose"],
      "additionalProperties": false,
      "properties": {
        "purpose": {
          "type": "array",
          "title": "Purpose",
          "items": { "type": "string", "enum": ["mac_address", "unique_id", "board_id", "user_data"] },
          "minItems": 1,
          "uniqueItems": true,
          "description": "What this EEPROM stores. SoMs typically combine `mac_address` + `unique_id`. FMC cards typically use `board_id` (VITA 57.1 IPMI EEPROM). `user_data` is for general-purpose EEPROMs available to the FPGA design."
        },
        "interface": {
          "type": "string",
          "title": "Interface",
          "enum": ["I2C", "SPI", "1-Wire"],
          "description": "Bus the EEPROM hangs off."
        },
        "size_kbits": {
          "type": "integer",
          "minimum": 1,
          "title": "Size (Kbits)",
          "description": "Capacity in kilobits (1 Kb = 128 bytes). Common values: 2, 4, 8, 16, 32, 64."
        },
        "part": {
          "type": "string",
          "title": "Part Number",
          "description": "Manufacturer part number (e.g. 24LC04, AT24C512, M24C32). Optional."
        }
      }
    }
  }
}
