Wire Protocol¶
The bootloader uses a compact binary protocol over UART (115200 baud, 8N1, no flow control).
Command Bytes¶
| Name | Value | Direction | Payload |
|---|---|---|---|
CMD_GET_VERSION |
0x01 |
Host → BL | none |
CMD_START |
0x02 |
Host → BL | sizeof(header_t) bytes |
CMD_NEXT_PAGE |
0x03 |
Host → BL | FLASH_PAGE_SIZE bytes |
CMD_RESET |
0x04 |
Host → BL | none |
CMD_OK |
0x40 |
BL → Host | ORed with the command byte |
CMD_ERR |
0x80 |
BL → Host | ORed with the command byte |
Response bytes are always a bitwise OR of the status flag with the original command:
0x41 = CMD_OK | CMD_GET_VERSION (success)
0xC3 = CMD_ERR | CMD_NEXT_PAGE (CRC mismatch or out-of-range write)
Command Descriptions¶
CMD_GET_VERSION (0x01)¶
Requests the bootloader identity. Valid only when the FSM is in COM_START state (before any upload session).
Request: single byte 0x01
Response: 17 bytes
Byte 0 : 0x41 (CMD_OK | CMD_GET_VERSION)
Bytes 1–4 : protocol_version (uint32_t, little-endian) = 0x00000001
Bytes 5–12 : device_id (uint64_t, little-endian)
Bytes 13–16 : flash_page_size (uint32_t, little-endian) = 2048
CMD_START (0x02)¶
Opens an upload session. The host sends this command followed immediately by the firmware header. The bootloader validates the header, erases the application flash region, and initialises AES and CRC state.
Request: 0x02 + sizeof(header_t) bytes
typedef struct {
uint32_t protocol_version; // Must match PROTOCOL_VERSION
uint32_t product_ID_MSB; // Upper 32 bits of DEVICE_ID
uint32_t product_ID_LSB; // Lower 32 bits of DEVICE_ID
uint32_t app_version; // Informational; not validated
uint32_t page_count; // 1 … (APP_LAST_PAGE - APP_START_PAGE + 1)
uint32_t flash_page_size; // Must match FLASH_PAGE_SIZE
uint8_t iv[16]; // AES-CBC initialisation vector
uint32_t crc; // Expected CRC-32 of the plaintext image
} header_t;
Validation rules (any failure → CMD_ERR | CMD_START):
protocol_version == PROTOCOL_VERSION(product_ID_MSB << 32) | product_ID_LSB == DEVICE_IDflash_page_size == FLASH_PAGE_SIZEpage_count >= 1page_count <= (APP_LAST_PAGE - APP_START_PAGE + 1)
Response on success: 0x42 (CMD_OK | CMD_START)
On success the bootloader:
- Unlocks flash
- Erases every page from
APP_START_PAGEthroughAPP_LAST_PAGE - Locks flash
- Calls
AES_CBC_init(KEY, header.iv) - Calls
CRC_init()and stores the running CRC - Resets
page_pos = APP_START,page_rem = page_count - Transitions to
COM_READYand refreshes the communication timeout
CMD_NEXT_PAGE (0x03)¶
Delivers one page of encrypted firmware. The host sends this command followed immediately by exactly FLASH_PAGE_SIZE (2048) bytes of AES-CBC ciphertext.
Request: 0x03 + FLASH_PAGE_SIZE encrypted bytes
Processing:
- Decrypt the page in-place:
AES_CBC_decrypt_buffer(page_buf, FLASH_PAGE_SIZE) - Accumulate CRC:
crc = CRC_add_byte_tab(crc, page_buf, FLASH_PAGE_SIZE) - First page only — buffer the first 8 bytes (reset vector + initial SP) into
app_begin_word[]; advance the write pointer past them - Write the remaining data to flash at
page_pos - Advance
page_pos += FLASH_PAGE_SIZE, decrementpage_rem - Last page — call
CRC_result(crc)and compare withheader.crc:- Match → write the deferred 8 bytes to
APP_START - Mismatch → respond
CMD_ERR | CMD_NEXT_PAGE
- Match → write the deferred 8 bytes to
Response on success: 0x43 (CMD_OK | CMD_NEXT_PAGE)
Pre-condition errors (→ CMD_ERR | CMD_NEXT_PAGE):
page_pos < APP_STARTorpage_pos >= APP_ENDpage_rem == 0
CMD_RESET (0x04)¶
Forces an immediate system reset. The bootloader sends CMD_OK | CMD_RESET (0x44), waits for the byte to be fully transmitted, then calls system_reset().
Protocol State Machine¶
stateDiagram-v2
[*] --> COM_START : power-on / reset
COM_START --> COM_START : any byte except CMD_GET_VERSION
COM_START --> COM_DONE : CMD_GET_VERSION (0x01)
COM_DONE --> COM_READY : command dispatched\ntimeout refreshed
COM_READY --> COM_NEXT_BYTE : CMD_START (0x02)\nbuf_ptr → &header\npage_tail = sizeof(header)
COM_READY --> COM_NEXT_BYTE : CMD_NEXT_PAGE (0x03)\nbuf_ptr → page_buf\npage_tail = FLASH_PAGE_SIZE
COM_READY --> COM_DONE : CMD_RESET (0x04)
COM_READY --> COM_DONE : unknown command
COM_NEXT_BYTE --> COM_NEXT_BYTE : byte stored at *buf_ptr++\npage_tail--
COM_NEXT_BYTE --> COM_DONE : last byte received\n(page_tail reaches 0)
COM_DONE --> COM_DONE : any byte ignored\n(busy processing)
State descriptions¶
| State | Meaning |
|---|---|
COM_START |
Initial state after reset. Only CMD_GET_VERSION is accepted. All other bytes are silently discarded (timeout still counts down). |
COM_READY |
Session open; any valid command accepted. Entered after the first successful command dispatch. |
COM_NEXT_BYTE |
Receiving multi-byte payload (header or page data). Each UART byte is stored via buf_ptr and page_tail is decremented. |
COM_DONE |
Full frame received; pending dispatch by update_com_fsm(). Bytes received in this state are ignored. |
Timeout Behaviour¶
Three independent timeouts control how long the bootloader stays alive:
| Constant | Default | Condition |
|---|---|---|
START_TIMEOUT |
5 ticks (500 ms) | Initial window before any command is received |
COMM_TIMEOUT |
10 ticks (1 s) | Added to _100ms_Tick after each successful command; resets the deadline |
PUSH_BUTTON_TIMEOUT |
3000 ticks (300 s) | Set when a confirmed button press-release is detected |
PUSH_BUTTON_DET_TIMEOUT |
600 ticks (60 s) | Set at startup when the button is already held |
When _100ms_Tick >= timeout:
- If
com_state != COM_NEXT_BYTEandCORE_is_valid_app()→deinit_hardware()thenjump_to_app() - Otherwise →
do_reset()(sendsCMD_OK | CMD_RESETthensystem_reset())
The COM_NEXT_BYTE guard prevents a timeout from interrupting a page transfer mid-stream.
Typical Transfer Session¶
Host Bootloader
│ │
│──── 0x01 (GET_VERSION) ───────►│
│◄─── 0x41, ver, id, page_sz ───│
│ │
│──── 0x02 (START) ─────────────►│
│──── header[0..N] ─────────────►│ N = sizeof(header_t)
│◄─── 0x42 (OK|START) ──────────│
│ │
│──── 0x03 (NEXT_PAGE) ─────────►│
│──── page_data[0..2047] ───────►│ 2048 bytes AES-CBC ciphertext
│◄─── 0x43 (OK|NEXT_PAGE) ──────│
│ ⋮ (repeat for each page)
│──── 0x03 (NEXT_PAGE) ─────────►│ last page
│──── page_data[0..2047] ───────►│
│◄─── 0x43 (OK|NEXT_PAGE) ──────│ only if CRC matches
│ │
│ (bootloader times out and jumps to app)