docs: Update firmware docs and move memory map

Update firmware docs to reflect new state machine, the new stack in
FW_RAM, and new loading address for app. Remove superflous technical
details from the software description.

Move memory subsystem and memory map to system_description.md and
refer to it directly by subsection elsewhere.
This commit is contained in:
Michael Cardell Widerkrantz 2023-03-22 20:37:15 +01:00
parent fddfd88db2
commit 6613b7e695
No known key found for this signature in database
GPG Key ID: D3DB3DDF57E704E5
3 changed files with 227 additions and 195 deletions

View File

@ -21,7 +21,9 @@ cores in different ways given the current exection mode. There are two
execution modes - firmware and application. Basically, in application mode
the access is more restrictive.
The API for all cores is described in the [Software documentation](software.md).
The API for all cores is described in the Memory mapped hardware
functions in the [System
Description](system_description.md#memory-mapped-hardware-functions).
### Cores

View File

@ -1,70 +1,66 @@
# Tillitis TKey software
## Definitions
* Firmware - software in ROM responsible for loading applications.
The firmware is included as part of the FPGA bit stream.
* Application - software supplied by the host machine, which is
received, loaded, and measured by the firmware (by hashing a
digest over the binary).
Learn more about the concepts in the
[system_description.md](system_description.md).
## CPU
We use a PicoRV32, a 32-bit RISC-V system (RV32ICZmmul), as the CPU
for running the firmware and the loaded app. The firmware and device
app both run in machine mode. All types are little-endian.
## Constraints
The application FPGA is a Lattice ICE40 UP5K, with the following
specifications:
* 30 EBR[^1] x 4 Kbit => 120 Kbit. PicoRV32 uses ~4 EBRs internally
=> 13 KB for Firmware. As of this writing firmware uses 2998 bytes.
* 4 SPRAM[^2] x 32 KB => 128 KB RAM for application/software
[^1]: Embedded Block RAM (also BRAM) residing in the FPGA, can
be configured as RAM or ROM.
[^2]: Single Port RAM (also SRAM).
## Introduction
The TKey has two modes of operation; firmware/loader mode and
This text is both an introduction to and a requirement specification
of the TKey firmware, its protocol, and an overview of how TKey
applications are supposed to work. For an overview of the TKey
concepts, see [System Description](system_description.md).
First, some definitions:
- Firmware - software in ROM responsible for loading applications. The
firmware is included as part of the FPGA bit stream.
- Application or app - software supplied by the host machine which is
received, loaded, measured, and started by the firmware.
The TKey has two modes of software operation: firmware mode and
application mode. The firmware mode has the responsibility of
receiving, measuring, loading, and starting the application.
receiving, measuring, loading, and starting the application. When the
firmware is about to start the application it switches to a more
constrained environment, the application mode.
The firmware and application uses a memory mapped IO for SoC
communication. This MMIO resides at `0xc000_0000`. *Nota bene*: Almost
all access to MMIO should be word (32 bit) aligned. See table below.
The firmware and application uses a memory mapped input/output (MMIO)
for communication with the hardware. The memory map is constrained
when running in application mode, e.g. UDS isn't readable, and the
`APP_{ADDR, SIZE}` are not writable for the application.
The application has a constrained variant of the firmware memory map,
which is outlined below. E.g. UDS isn't readable, and the `APP_{ADDR,
SIZE}` are not writable for the application.
See table in the [System
Description](system_description.md#memory-mapped-hardware-functions)
for details about the memory system and MMIO.
The firmware (and optionally all software) on the TKey communicates to
the host via the `UART_{RX,TX}_{STATUS,DATA}` registers, using the
The firmware (and optionally all software) on the TKey can communicate
to the host via the `UART_{RX,TX}_{STATUS,DATA}` registers, using the
framing protocol described in [Framing
Protocol](../framing_protocol/framing_protocol.md).
The firmware defines a protocol (command/response interface) on top of
this framing layer which is used to bootstrap the application onto the
device. All commands are initiated by the host. All commands receive a
reply.
The firmware defines a protocol on top of this framing layer which is
used to bootstrap the application. All commands are initiated by the
host. All commands receive a reply. See [Firmware
protocol](#firmware-protocol) for specific details.
Applications define their own per-application protocol used for
communication with their host part. They may or may not be based on
the Framing Protocol.
Applications define their own protocol used for communication with
their host part. They may or may not be based on the Framing Protocol.
## CPU
The CPU is a PicoRV32, a 32-bit RISC-V processor (arch: RV32IC\_Zmmul)
which runs the firmware and the application. The firmware and
application both run in RISC-V machine mode. All types are
little-endian.
## Constraints
- ROM: 6 kByte.
- RAM: 128 kByte.
## Firmware
The purpose of the firmware is to bootstrap and measure an
application.
The TKey has 128 KB RAM. Firmware loads the app at the start of RAM.
The current C runtime (crt0.S) of apps in our [apps
The TKey has 128 kilobyte RAM. Firmware loads the application at the
start of RAM. The current C runtime (`crt0.S`) of apps in our [apps
repo](https://github.com/tillitis/tillitis-key1-apps) sets up the
stack to start just below the end of RAM. This means that a larger app
comes at the compromise of it having a smaller stack.
@ -78,14 +74,14 @@ coming in on `UART_RX`.
Typical use scenario:
1. The host sends the `FW_CMD_LOAD_APP` command with the size of the
device app and the user-supplied secret as arguments and and gets
a `FW_RSP_LOAD_APP` back. After using this it's not possible to
restart the loading of a device app.
device app and the optional user-supplied secret as arguments and
and gets a `FW_RSP_LOAD_APP` back. After using this it's not
possible to restart the loading of an application.
2. If the the host receive a sucessful response, it will send
multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the
full application.
3. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places
the data into `0x4000_7000` and upwards. The firmware replies
the data into `0x4000_0000` and upwards. The firmware replies
with a `FW_RSP_LOAD_APP_DATA` response to the host for each
received block except the last data block.
4. When the final block of the application image is received with a
@ -94,20 +90,25 @@ Typical use scenario:
firmware send back the `FW_RSP_LOAD_APP_DATA_READY` response
containing the measurement.
5. The Compound Device Identifier (CDI) is then computed by using
the `UDS`, the measurement of the application, and the `USS`, and
placed in the `CDI` register. Then the start address of the
device app, `0x4000_7000`, is written to `APP_ADDR` and the size
to `APP_SIZE` to let the device application know where it is
loaded and how large it is, if it wants to relocate in RAM.
6. The firmware now starts the application by switching to
application mode by writing to the `SWITCH_APP` register. In this
mode the MMIO region is restricted; e.g. some registers are
removed (`UDS`), and some are switched from read/write to
read-only. This is outlined in the memory map below.
the `UDS`, application digest, and the `USS`, and placed in
`CDI`. (see [Compound Device Identifier
computation](#compound-device-identifier-computation)) Then the
start address of the device app, `0x4000_0000`, is written to
`APP_ADDR` and the size to `APP_SIZE` to let the device
application know where it is loaded and how large it is, if it
wants to relocate in RAM.
6. The firmware now clears the special `FW_RAM` where it keeps it
stack. After this it does no more function calls and uses no more
automatic variables.
7. Firmware starts the application by first switching to application
mode by writing to the `SWITCH_APP` register. In this mode the
MMIO region is restricted, e.g. some registers are removed
(`UDS`), and some are switched from read/write to read-only (see
[memory
map](system_description.md#memory-mapped-hardware-functions)).
The firmware now executes assembler code that writes zeros to
stack and data of the firmware, then jumps to what's in
`APP_ADDR` which starts device app execution.
Then the firmware jumps to what's in `APP_ADDR` which starts
the application.
There is now no other means of getting back from application mode
to firmware mode than resetting/power cycling the device.
@ -118,11 +119,15 @@ Standing in `hw/application_fpga/` you can run `make firmware.elf` to
build just the firmware. You don't need all the FPGA development tools
mentioned in [Toolchain setup](../toolchain_setup.md).
You need Clang with 32 RISC-V support `-march=rv32iczmmul` which comes
in Clang 15. If you don't have version 15 you might get by with
You need clang with 32 RISC-V support `-march=rv32iczmmul` which comes
in clang 15. If you don't have version 15 you might get by with
`-march=rv32imc` but things will break if you ever cause it to emit
`div` instructions.
You also need `llvm-ar`, `llvm-objcopy`, `llvm-size` and `lld`.
Typically these are all available in packages called "clang", "llvm",
"lld" or similar.
If your available `objcopy` and `size` commands is anything other than
the default `llvm-objcopy` and `llvm-size` define `OBJCOPY` and `SIZE`
to whatever they're called on your system before calling `make
@ -159,26 +164,33 @@ Debugging? Use the HTIF console by removing `-DNOCONSOLE` from the
`htif_putinthex()` `htif_hexdump()` and friends for printf-like
debugging.
You can add `-d guest_errors` to the qemu commandline to make QEMU
send errors from the TK1 machine to stderr, typically things like
memory writes outside of mapped regions.
You can also use the qemu monitor for debugging, e.g. `info
registers`, or run qemu with `-d in_asm` or `-d trace:riscv_trap`.
### Reset
The PicoRV32 starts executing at `0x0000_0000`. Our firmware starts at
`_start` from `start.S` which initializes the `.data`, and `.bss` at
`0x4000_0000` and upwards. A stack is also initialized, starting at
0x4000_6ff0 and downwards. When the initialization is finished, the
firmware waits for incoming commands from the host, by busy-polling
the `UART_RX_{STATUS,DATA}` registers. When a complete command is
read, the firmware executes the command.
The PicoRV32 starts executing at `0x0000_0000`. We allow no `.data` or
`.bss` sections. Our firmware starts at the `_start` symbol in
`start.S` which first clears all RAM for any remaining data from
previous applications, then initializes a stack starting at the top of
`FW_RAM` at `0xd000_0800` and downwards and then calls `main`.
When the initialization is finished, the firmware waits for incoming
commands from the host, by busy-polling the `UART_RX_{STATUS,DATA}`
registers. When a complete command is read, the firmware executes the
command.
### Firmware state machine
States:
- `initial` - At start.
- `loading` - Expect app data.
- `run` - Computes CDI and starts the device app.
- `loading` - Expect application data.
- `run` - Computes CDI and starts the application.
- `fail` - Stops waiting for commands, flashes LED forever.
Commands in state `initial`:
@ -194,20 +206,23 @@ Commands in state `loading`:
| *command* | *next state* |
|------------------------|----------------------------------|
| `FW_CMD_NAME_VERSION` | unchanged |
| `FW_CMD_GET_UDI` | unchanged |
| `FW_CMD_LOAD_APP_DATA` | unchanged or `run` on last chunk |
See below for firmware protocol definition.
Commands in state `run`: None.
Commands in state `fail`: None.
See [Firmware protocol](#firmware-protocol) for the definition of the
specific commands and their responses.
### User-supplied Secret (USS)
USS is a 32 bytes long secret provided by the user. Typically a host
program gets a passphrase from the user and then does KDF of some
sort, for instance a BLAKE2s, to get 32 bytes which it sends to the
firmware to be part of the CDI computation.
program gets a secret from the user and then does a key derivation
function of some sort, for instance a BLAKE2s, to get 32 bytes which
it sends to the firmware to be part of the CDI computation.
### Compound Device Identifier (CDI) computation
### Compound Device Identifier computation
The CDI is computed by:
@ -222,14 +237,21 @@ implementation in the FPGA at this time.
The firmware instead does the CDI computation using the special
firmware-only `FW_RAM` which is invisible after switching to app mode.
The `blake2s()` function in the firmware is fed with a buffer in
`FW_RAM` containing the UDS, the app digest, and the USS. It is also
fed with a context used for computations that is also part of the
`FW_RAM`.
We keep the entire firmware stack in `FW_RAM` and clear it just before
switching to app mode just in case.
After doing the computation `FW_RAM` is also cleared with zeroes.
We sleep for a random number of cycles before reading out the UDS,
call `blake2s_update()` with it and then immediately call
`blake2s_update()` again with the program digest, destroying the UDS
stored in the internal context buffer. UDS should now not be in
`FW_RAM` anymore. We can read UDS only once per power cycle so UDS
should now not be available to firmware at all.
### Firmware protocol definition
Then we continue with the CDI computation by updating with an optional
USS and then finalizing the hash, storing the resulting digest in
`CDI`.
### Firmware protocol
The firmware commands and responses are built on top of the [Framing
Protocol](../framing_protocol/framing_protocol.md).
@ -414,8 +436,9 @@ host <-
### Firmware services
The firmware exposes a BLAKE2s function through a function pointer
located in MMIO `BLAKE2S` (see memory map below) with the with
function signature:
located in MMIO `BLAKE2S` (see [memory
map](system_description.md#memory-mapped-hardware-functions)) with the
with function signature:
```c
int blake2s(void *out, unsigned long outlen, const void *key,
@ -438,98 +461,10 @@ typedef struct {
The `libcommon` library in
[tillitis-key1-apps](https://github.com/tillitis/tillitis-key1-apps/)
has a wrapper for using this function.
has a wrapper for using this function called `blake2s()`.
## Memory map
## Applications
Assigned top level prefixes:
| *name* | *prefix* | *address length* |
|----------|----------|--------------------------------------|
| ROM | 0b00 | 30 bit address |
| RAM | 0b01 | 30 bit address |
| reserved | 0b10 | |
| MMIO | 0b11 | 6 bits for core select, 24 bits rest |
Addressing:
```
31st bit 0th bit
v v
0000 0000 0000 0000 0000 0000 0000 0000
- Bits [31 .. 30] (2 bits): Top level prefix (described above)
- Bits [29 .. 24] (6 bits): Core select. We want to support at least 16 cores
- Bits [23 .. 0] (24 bits): Memory/in-core address.
```
The memory exposes SoC functionality to the software when in firmware
mode. It is a set of memory mapped registers (MMIO), starting at base
address `0xc000_0000`. For specific offsets/bitmasks, see the file
[tk1_mem.h](../../hw/application_fpga/fw/tk1_mem.h) (in this repo).
Assigned core prefixes:
| *name* | *prefix* |
|--------|----------|
| ROM | 0x00 |
| RAM | 0x40 |
| TRNG | 0xc0 |
| TIMER | 0xc1 |
| UDS | 0xc2 |
| UART | 0xc3 |
| TOUCH | 0xc4 |
| FW_RAM | 0xd0 |
| TK1 | 0xff |
*Nota bene*: MMIO accesses should be 32 bit wide, e.g use `lw` and
`sw`. Exceptions are `FW_RAM` and `QEMU_DEBUG`.
| *name* | *fw* | *app* | *size* | *type* | *content* | *description* |
|-------------------|-------|-----------|--------|----------|-----------|-------------------------------------------------------------------------|
| `TRNG_STATUS` | r | r | | | | TRNG_STATUS_READY_BIT is 1 when an entropy word is available. |
| `TRNG_ENTROPY` | r | r | 4B | u32 | | Entropy word. Reading a word will clear status. |
| `TIMER_CTRL` | r/w | r/w | | | | If TIMER_STATUS_RUNNING_BIT in TIMER_STATUS is 0, setting |
| | | | | | | TIMER_CTRL_START_BIT here starts the timer. |
| | | | | | | If TIMER_STATUS_RUNNING_BIT in TIMER_STATUS is 1, setting |
| | | | | | | TIMER_CTRL_STOP_BIT here stops the timer. |
| `TIMER_STATUS` | r | r | | | | TIMER_STATUS_RUNNING_BIT is 1 when the timer is running. |
| `TIMER_PRESCALER` | r/w | r/w | 4B | | | Prescaler init value. Write blocked when running. |
| `TIMER_TIMER` | r/w | r/w | 4B | | | Timer init or current value while running. Write blocked when running. |
| `UDS_FIRST` | r[^3] | invisible | 4B | u8[32] | | First word of Unique Device Secret key. |
| `UDS_LAST` | | invisible | | | | The last word of the UDS |
| `UART_BITRATE` | r/w | | | | | TBD |
| `UART_DATABITS` | r/w | | | | | TBD |
| `UART_STOPBITS` | r/w | | | | | TBD |
| `UART_RX_STATUS` | r | r | 1B | u8 | | Non-zero when there is data to read |
| `UART_RX_DATA` | r | r | 1B | u8 | | Data to read. Only LSB contains data |
| `UART_RX_BYTES` | r | r | 4B | u32 | | Number of bytes received from the host and not yet read by SW, FW. |
| `UART_TX_STATUS` | r | r | 1B | u8 | | Non-zero when it's OK to write data |
| `UART_TX_DATA` | w | w | 1B | u8 | | Data to send. Only LSB contains data |
| `TOUCH_STATUS` | r/w | r/w | | | | TOUCH_STATUS_EVENT_BIT is 1 when touched. After detecting a touch |
| | | | | | | event (reading a 1), write anything here to acknowledge it. |
| `FW_RAM` | r/w | invisible | 2 kiB | u8[2048] | | Firmware-only RAM. |
| `UDI` | r | invisible | 8B | u64 | | Unique Device ID (UDI). |
| `QEMU_DEBUG` | w | w | | u8 | | Debug console (only in QEMU) |
| `NAME0` | r | r | 4B | char[4] | "tk1 " | ID of core/stick |
| `NAME1` | r | r | 4B | char[4] | "mkdf" | ID of core/stick |
| `VERSION` | r | r | 4B | u32 | 1 | Current version. |
| `SWITCH_APP` | r/w | r | 1B | u8 | | Write anything here to trigger the switch to application mode. Reading |
| | | | | | | returns 0 if device is in firmware mode, 0xffffffff if in app mode. |
| `LED` | r/w | r/w | 1B | u8 | | Control of the color LEDs in RBG LED on the board. |
| | | | | | | Bit 0 is Blue, bit 1 is Green, and bit 2 is Red LED. |
| `GPIO` | r/w | r/w | 1B | u8 | | Bits 0 and 1 contain the input level of GPIO 1 and 2. |
| | | | | u8 | | Bits 3 and 4 store the output level of GPIO 3 and 4. |
| `APP_ADDR` | r/w | r | 4B | u32 | | Firmware stores app load address here, so app can read its own location |
| `APP_SIZE` | r/w | r | 4B | u32 | | Firmware stores app app size here, so app can read its own size |
| `BLAKE2S` | r/w | r | 4B | u32 | | Function pointer to a BLAKE2S function in the firmware |
| `CDI_FIRST` | r/w | r | 32B | u8[32] | | Compound Device Identifier (CDI). UDS+measurement... |
| `CDI_LAST` | | r | | | | Last word of CDI |
| `RAM_ASLR` | w | invisible | 4B | u32 | | Address Space Randomization seed value for the RAM |
| `RAM_SCRAMBLE` | w | invisible | 4B | u32 | | Data scrambling seed value for the RAM |
| `CPU_MON_CTRL` | w | w | 4B | u32 | | Bit 0 enables CPU execution monitor. Can't be unset. Lock adresses |
| `CPU_MON_FIRST` | w | w | 4B | u32 | | First address of the area monitored for execution attempts |
| `CPU_MON_LAST` | w | w | 4B | u32 | | Last address of the area monitored for execution attempts |
[^3]: The UDS can only be read *once* per power-cycle.
See [our apps repo](https://github.com/tillitis/tillitis-key1-apps)
for examples of client and TKey apps as well as libraries for writing
both.

View File

@ -16,7 +16,7 @@ The described functionality and requirements applies to version 1 of
the TKey (TK1)
The intended users of this document are:
- Implementors of the TKkey hardware, firmware and SDKs
- Implementors of the TKey hardware, firmware and SDKs
- Developers of secure applications for the TKey
- Technically skilled third parties that wants to understand the
TKey
@ -56,7 +56,7 @@ The derivation can also be combined with a User Supplied Secret
(USS). This means that keys derived are both based on something the user
has - the specific device, and something the user knows (the USS). And
the derived can be trusted because of the measurement being used
by the derivation, thereby verifying the intergrity od the application.
by the derivation, thereby verifying the intergrity of the application.
### Execution monitor
@ -86,7 +86,6 @@ monitor for the area by writing to the ADDR\_CPU\_MON\_CTRL register.
Note that once the monitor has been enabled it can't be disabled and
the addresses defining the area can't be changed.
### Illegal instruction monitor
Execution of illegal instructions will cause the CPU to enter its trap
@ -95,7 +94,6 @@ the CPU state. If the CPU enters the trap state, the hardware will
start flashing the RED led, signalling that the TKey is stuck in an
error state.
### RAM memory protection
The TKey hardware includes a simple form of RAM memory protection. The
@ -171,11 +169,10 @@ The TKey store and use the following assets internally:
altered during the life time of a given device. May be copied,
extracted, read from the device.
- CDI - Compound Device Identity. Dervied by the FW when an application
is loaded using the UDS and the application binary. Used by the
application to derive secrets, keys as needed. The CDI should never
be exposed outside of the application\_fpga
- CDI - Compound Device Identity. Computed by the FW when an
application is loaded using the UDS and the application binary. Used
by the application to derive secrets, keys as needed. The CDI should
never be exposed.
Additionally the following asset could be provided from the host:
@ -183,6 +180,104 @@ Additionally the following asset could be provided from the host:
Supplied from the host to the device. Should not be revealed to a
third party.
## Memory
Addressing:
```
31st bit 0th bit
v v
0000 0000 0000 0000 0000 0000 0000 0000
- Bits [31 .. 30] (2 bits): Top level prefix (described below)
- Bits [29 .. 24] (6 bits): Core select. We want to support at least 16 cores
- Bits [23 .. 0] (24 bits): Memory/in-core address.
```
Assigned top level prefixes:
| *name* | *prefix* | *address length* |
|----------|----------|--------------------------------------|
| ROM | 0b00 | 30 bit address |
| RAM | 0b01 | 30 bit address |
| reserved | 0b10 | |
| MMIO | 0b11 | 6 bits for core select, 24 bits rest |
| *memory* | *first byte* | *last byte* |
|----------|--------------|------------------------------|
| ROM | 0x0000\_0000 | 0x0000\_17ff |
| RAM | 0x4000\_0000 | 0x4001\_ffff |
| MMIO | 0xc000\_0000 | 0xffff\_ffff (last possible) |
### Memory mapped hardware functions
Hardware functions, assets, and input/output are memory mapped (MMIO)
starting at base address `0xc000_0000`. For specific offsets/bitmasks,
see the file [tk1_mem.h](../../hw/application_fpga/fw/tk1_mem.h) (in
this repo).
Assigned core prefixes:
| *name* | *address prefix* |
|--------|------------------|
| TRNG | 0xc0 |
| TIMER | 0xc1 |
| UDS | 0xc2 |
| UART | 0xc3 |
| TOUCH | 0xc4 |
| FW_RAM | 0xd0 |
| TK1 | 0xff |
*Nota bene*: MMIO accesses should be 32 bit wide, e.g use `lw` and
`sw`. Exceptions are `UDS`, `FW_RAM` and `QEMU_DEBUG`.
| *name* | *fw* | *app* | *size* | *type* | *content* | *description* |
|-------------------|-------|-----------|--------|----------|-----------|-------------------------------------------------------------------------|
| `TRNG_STATUS` | r | r | | | | TRNG_STATUS_READY_BIT is 1 when an entropy word is available. |
| `TRNG_ENTROPY` | r | r | 4B | u32 | | Entropy word. Reading a word will clear status. |
| `TIMER_CTRL` | r/w | r/w | | | | If TIMER_STATUS_RUNNING_BIT in TIMER_STATUS is 0, setting |
| | | | | | | TIMER_CTRL_START_BIT here starts the timer. |
| | | | | | | If TIMER_STATUS_RUNNING_BIT in TIMER_STATUS is 1, setting |
| | | | | | | TIMER_CTRL_STOP_BIT here stops the timer. |
| `TIMER_STATUS` | r | r | | | | TIMER_STATUS_RUNNING_BIT is 1 when the timer is running. |
| `TIMER_PRESCALER` | r/w | r/w | 4B | | | Prescaler init value. Write blocked when running. |
| `TIMER_TIMER` | r/w | r/w | 4B | | | Timer init or current value while running. Write blocked when running. |
| `UDS_FIRST` | r[^3] | invisible | 4B | u8[32] | | First word of Unique Device Secret key. |
| `UDS_LAST` | | invisible | | | | The last word of the UDS |
| `UART_BITRATE` | r/w | | | | | TBD |
| `UART_DATABITS` | r/w | | | | | TBD |
| `UART_STOPBITS` | r/w | | | | | TBD |
| `UART_RX_STATUS` | r | r | 1B | u8 | | Non-zero when there is data to read |
| `UART_RX_DATA` | r | r | 1B | u8 | | Data to read. Only LSB contains data |
| `UART_RX_BYTES` | r | r | 4B | u32 | | Number of bytes received from the host and not yet read by SW, FW. |
| `UART_TX_STATUS` | r | r | 1B | u8 | | Non-zero when it's OK to write data |
| `UART_TX_DATA` | w | w | 1B | u8 | | Data to send. Only LSB contains data |
| `TOUCH_STATUS` | r/w | r/w | | | | TOUCH_STATUS_EVENT_BIT is 1 when touched. After detecting a touch |
| | | | | | | event (reading a 1), write anything here to acknowledge it. |
| `FW_RAM` | r/w | invisible | 2 kiB | u8[2048] | | Firmware-only RAM. |
| `UDI` | r | invisible | 8B | u64 | | Unique Device ID (UDI). |
| `QEMU_DEBUG` | w | w | | u8 | | Debug console (only in QEMU) |
| `NAME0` | r | r | 4B | char[4] | "tk1 " | ID of core/stick |
| `NAME1` | r | r | 4B | char[4] | "mkdf" | ID of core/stick |
| `VERSION` | r | r | 4B | u32 | 1 | Current version. |
| `SWITCH_APP` | r/w | r | 1B | u8 | | Write anything here to trigger the switch to application mode. Reading |
| | | | | | | returns 0 if device is in firmware mode, 0xffffffff if in app mode. |
| `LED` | r/w | r/w | 1B | u8 | | Control of the color LEDs in RBG LED on the board. |
| | | | | | | Bit 0 is Blue, bit 1 is Green, and bit 2 is Red LED. |
| `GPIO` | r/w | r/w | 1B | u8 | | Bits 0 and 1 contain the input level of GPIO 1 and 2. |
| | | | | u8 | | Bits 3 and 4 store the output level of GPIO 3 and 4. |
| `APP_ADDR` | r/w | r | 4B | u32 | | Firmware stores app load address here, so app can read its own location |
| `APP_SIZE` | r/w | r | 4B | u32 | | Firmware stores app app size here, so app can read its own size |
| `BLAKE2S` | r/w | r | 4B | u32 | | Function pointer to a BLAKE2S function in the firmware |
| `CDI_FIRST` | r/w | r | 32B | u8[32] | | Compound Device Identifier (CDI). UDS+measurement... |
| `CDI_LAST` | | r | | | | Last word of CDI |
| `RAM_ASLR` | w | invisible | 4B | u32 | | Address Space Randomization seed value for the RAM |
| `RAM_SCRAMBLE` | w | invisible | 4B | u32 | | Data scrambling seed value for the RAM |
| `CPU_MON_CTRL` | w | w | 4B | u32 | | Bit 0 enables CPU execution monitor. Can't be unset. Lock adresses |
| `CPU_MON_FIRST` | w | w | 4B | u32 | | First address of the area monitored for execution attempts |
| `CPU_MON_LAST` | w | w | 4B | u32 | | Last address of the area monitored for execution attempts |
[^3]: The UDS can only be read *once* per power-cycle.
## Subsystems and Components