Board Variables
===============
A firmware image is compatible with a board by virtue of two things: the board
type, which selects the image out of a firmware bundle, and the board
variables, which are hardware attributes that vary between boards of the same
type.
Board variables are numbered from 0, independently of board type and ASIC, in
:file:`include/tenstorrent/board_variables.h`. A number is never reused. The
first is the SPI flash JEDEC ID, which matters because one image can drive
several second-source flash parts while an older image speaks only the Micron
MT25QU512ABB command set. Writing an MT25-only image to a board carrying
another part leaves an image that cannot read itself back at the next boot.
Interlock flow
--------------
1. Each board image lists which board variables it knows and what values it
supports for each. This is included in the fwbundle.
2. tt-flash reads fwbundle info and checks the variables. If any check fails,
it reports an error.
3. tt-flash requests flash unlock and includes the list of variables it
checked. FW confirms that all required variables have been checked,
and fails unlock if any were not.
.. mermaid::
sequenceDiagram
autonumber
participant TF as tt-flash
participant FB as fwbundle
participant TM as SMC telemetry
participant FW as SMC firmware
TF->>TM: read board ID
TM-->>TF: board type and revision
TF->>FB: read BOARD-REV/compat-variables.json
FB-->>TF: the variables this image constrains,
where to read each one, and what it permits
loop each constrained variable
TF->>TM: read the variable's telemetry tag
alt tag reported
TM-->>TF: value
else tag absent
TM-->>TF: not reported
Note right of TF: firmware older than the variable, so this is
an "original design" board. Variable left out of the checked list, but not rejected.
end
end
alt a value is outside what the image supports
Note right of TF: Stop, naming the variable and
what the image supports.
else every value read is supported
TF->>FW: FLASH_UNLOCK, with the bitmap of
variables it verified
Note left of FW: required = the variables that matter on
this board.
alt a required variable is missing from the bitmap
FW-->>TF: error, plus the required bitmap
Note right of TF: name the variables it must check.
Nothing is written
else every required variable is verified
FW-->>TF: ok, plus the required bitmap
Note left of FW: flash unlocked, ready to write
TF->>FW: WRITE_EEPROM
end
end
The FW bundle
-------------
Each image in a firmware bundle carries a ``compat-variables.json`` beside its
image, describing the hardware it is compatible with: the variables it
constrains, how to read each one from the board, and the values it permits.
Values are unsigned 32-bit integers, written as a JSON integer or a ``"0x..."``
string.
.. code-block:: json
{
"version": 1,
"variables": [
{
"name": "SPI EEPROM",
"number": 0,
"formatter": "hex",
"source": { "type": "telemetry", "tag": 80 },
"constraints": [ { "in": ["0x20bb20", "0xc2253a", "0xc8631a", "0xef6020"] } ]
}
]
}
``number``
The board variable number. This is the bit index in the unlock message.
``name``
A free-form display string, printed verbatim in messages. Not an identifier.
``formatter``
Optional. Names how to render the value and the constraint values in
messages. A consumer renders as hex if this is absent *or* if it names a
formatter the consumer does not recognize, so a new formatter never breaks
an older tool. Defined so far: ``hex``, ``semver``.
``source``
Optional. How to read the value from the board; the only method defined so
far is ``{"type": "telemetry", "tag": }``. Unlike ``formatter``, an
unrecognized ``source`` fails closed: the consumer must treat the variable
as unverified rather than guess, because it affects correctness rather than
presentation.
``constraints``
A list of comparisons, every one of which must hold. Each entry names
exactly one of ``eq``, ``ne``, ``lt``, ``le``, ``gt``, ``ge``, ``in``,
``not_in``, so an operator may appear more than once. An empty list places
no restriction.
The file is generated at build time from the devicetree by
``tt_boot_fs.py generate_compat_variables``, so the flash mux candidate list
stays the single source of truth, and validated against
:file:`scripts/schemas/compat-variables-schema.json`.
The generator currently only supports SPI EEPROM JEDEC ID. It scans devicetree
files and checks the jedec-id of SPI entries. It has special-case code to
comprehend the flash_mux device and the standard SPI generic fallback.
The tt-flash checks
-------------------
After identifying the board ID & board version, tt-flash will check for
compat-variables.json in the board subdirectory. It performs the checks
as documented above. Currently it understands how to use Blackhole-style
telemetry to get a 32-bit value for a variable. If any check fails, it
refuses to load the firmware with an error message.
There is an important exception: If a board variable's telemetry value is not
present, tt-flash will attempt to unlock and flash without this variable in
the list of checked variables. This addresses the case of an original design
board and old firmware from before the variable was introduced. We assume that
original designs will always be supported.
The firmware-side flash unlock
------------------------------
The running firmware is the final authority on which board variables must be
checked. It refuses to unlock the flash for a tool that does not confirm that
it checked every required variable.
``TT_SMC_MSG_FLASH_UNLOCK`` carries a bitmap of the board variables the host
verified against the image it is about to write. An error is reported if any
required variable is not listed. Otherwise the flash is unlocked.
The unlock persists until ``TT_SMC_MSG_FLASH_LOCK`` or until a flash unlock
message with ``num_variable_words != 0``. There is a special exception to
ignore redundant unlocks with ``num_variable_words == 0`` because luwen's
``spi_write`` unlocks and relocks each time it is called.