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 execution modes - firmware and application. Basically, in application mode
the access is more restrictive. 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 ### Cores

View File

@ -1,70 +1,66 @@
# Tillitis TKey software # 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 ## 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 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 The firmware and application uses a memory mapped input/output (MMIO)
communication. This MMIO resides at `0xc000_0000`. *Nota bene*: Almost for communication with the hardware. The memory map is constrained
all access to MMIO should be word (32 bit) aligned. See table below. 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, See table in the [System
which is outlined below. E.g. UDS isn't readable, and the `APP_{ADDR, Description](system_description.md#memory-mapped-hardware-functions)
SIZE}` are not writable for the application. for details about the memory system and MMIO.
The firmware (and optionally all software) on the TKey communicates to The firmware (and optionally all software) on the TKey can communicate
the host via the `UART_{RX,TX}_{STATUS,DATA}` registers, using the to the host via the `UART_{RX,TX}_{STATUS,DATA}` registers, using the
framing protocol described in [Framing framing protocol described in [Framing
Protocol](../framing_protocol/framing_protocol.md). Protocol](../framing_protocol/framing_protocol.md).
The firmware defines a protocol (command/response interface) on top of The firmware defines a protocol on top of this framing layer which is
this framing layer which is used to bootstrap the application onto the used to bootstrap the application. All commands are initiated by the
device. All commands are initiated by the host. All commands receive a host. All commands receive a reply. See [Firmware
reply. protocol](#firmware-protocol) for specific details.
Applications define their own per-application protocol used for Applications define their own protocol used for communication with
communication with their host part. They may or may not be based on their host part. They may or may not be based on the Framing Protocol.
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 ## Firmware
The purpose of the firmware is to bootstrap and measure an The purpose of the firmware is to bootstrap and measure an
application. application.
The TKey has 128 KB RAM. Firmware loads the app at the start of RAM. The TKey has 128 kilobyte RAM. Firmware loads the application at the
The current C runtime (crt0.S) of apps in our [apps 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 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 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. comes at the compromise of it having a smaller stack.
@ -78,14 +74,14 @@ coming in on `UART_RX`.
Typical use scenario: Typical use scenario:
1. The host sends the `FW_CMD_LOAD_APP` command with the size of the 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 device app and the optional user-supplied secret as arguments and
a `FW_RSP_LOAD_APP` back. After using this it's not possible to and gets a `FW_RSP_LOAD_APP` back. After using this it's not
restart the loading of a device app. possible to restart the loading of an application.
2. If the the host receive a sucessful response, it will send 2. If the the host receive a sucessful response, it will send
multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the
full application. full application.
3. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places 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 with a `FW_RSP_LOAD_APP_DATA` response to the host for each
received block except the last data block. received block except the last data block.
4. When the final block of the application image is received with a 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 firmware send back the `FW_RSP_LOAD_APP_DATA_READY` response
containing the measurement. containing the measurement.
5. The Compound Device Identifier (CDI) is then computed by using 5. The Compound Device Identifier (CDI) is then computed by using
the `UDS`, the measurement of the application, and the `USS`, and the `UDS`, application digest, and the `USS`, and placed in
placed in the `CDI` register. Then the start address of the `CDI`. (see [Compound Device Identifier
device app, `0x4000_7000`, is written to `APP_ADDR` and the size computation](#compound-device-identifier-computation)) Then the
to `APP_SIZE` to let the device application know where it is start address of the device app, `0x4000_0000`, is written to
loaded and how large it is, if it wants to relocate in RAM. `APP_ADDR` and the size to `APP_SIZE` to let the device
6. The firmware now starts the application by switching to application know where it is loaded and how large it is, if it
application mode by writing to the `SWITCH_APP` register. In this wants to relocate in RAM.
mode the MMIO region is restricted; e.g. some registers are 6. The firmware now clears the special `FW_RAM` where it keeps it
removed (`UDS`), and some are switched from read/write to stack. After this it does no more function calls and uses no more
read-only. This is outlined in the memory map below. 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 Then the firmware jumps to what's in `APP_ADDR` which starts
stack and data of the firmware, then jumps to what's in the application.
`APP_ADDR` which starts device app execution.
There is now no other means of getting back from application mode There is now no other means of getting back from application mode
to firmware mode than resetting/power cycling the device. 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 build just the firmware. You don't need all the FPGA development tools
mentioned in [Toolchain setup](../toolchain_setup.md). mentioned in [Toolchain setup](../toolchain_setup.md).
You need Clang with 32 RISC-V support `-march=rv32iczmmul` which comes 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 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 `-march=rv32imc` but things will break if you ever cause it to emit
`div` instructions. `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 If your available `objcopy` and `size` commands is anything other than
the default `llvm-objcopy` and `llvm-size` define `OBJCOPY` and `SIZE` the default `llvm-objcopy` and `llvm-size` define `OBJCOPY` and `SIZE`
to whatever they're called on your system before calling `make 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 `htif_putinthex()` `htif_hexdump()` and friends for printf-like
debugging. 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 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`. registers`, or run qemu with `-d in_asm` or `-d trace:riscv_trap`.
### Reset ### Reset
The PicoRV32 starts executing at `0x0000_0000`. Our firmware starts at The PicoRV32 starts executing at `0x0000_0000`. We allow no `.data` or
`_start` from `start.S` which initializes the `.data`, and `.bss` at `.bss` sections. Our firmware starts at the `_start` symbol in
`0x4000_0000` and upwards. A stack is also initialized, starting at `start.S` which first clears all RAM for any remaining data from
0x4000_6ff0 and downwards. When the initialization is finished, the previous applications, then initializes a stack starting at the top of
firmware waits for incoming commands from the host, by busy-polling `FW_RAM` at `0xd000_0800` and downwards and then calls `main`.
the `UART_RX_{STATUS,DATA}` registers. When a complete command is
read, the firmware executes the command. 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 ### Firmware state machine
States: States:
- `initial` - At start. - `initial` - At start.
- `loading` - Expect app data. - `loading` - Expect application data.
- `run` - Computes CDI and starts the device app. - `run` - Computes CDI and starts the application.
- `fail` - Stops waiting for commands, flashes LED forever. - `fail` - Stops waiting for commands, flashes LED forever.
Commands in state `initial`: Commands in state `initial`:
@ -194,20 +206,23 @@ Commands in state `loading`:
| *command* | *next state* | | *command* | *next state* |
|------------------------|----------------------------------| |------------------------|----------------------------------|
| `FW_CMD_NAME_VERSION` | unchanged |
| `FW_CMD_GET_UDI` | unchanged |
| `FW_CMD_LOAD_APP_DATA` | unchanged or `run` on last chunk | | `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) ### User-supplied Secret (USS)
USS is a 32 bytes long secret provided by the user. Typically a host 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 program gets a secret from the user and then does a key derivation
sort, for instance a BLAKE2s, to get 32 bytes which it sends to the function of some sort, for instance a BLAKE2s, to get 32 bytes which
firmware to be part of the CDI computation. 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: 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 The firmware instead does the CDI computation using the special
firmware-only `FW_RAM` which is invisible after switching to app mode. firmware-only `FW_RAM` which is invisible after switching to app mode.
The `blake2s()` function in the firmware is fed with a buffer in We keep the entire firmware stack in `FW_RAM` and clear it just before
`FW_RAM` containing the UDS, the app digest, and the USS. It is also switching to app mode just in case.
fed with a context used for computations that is also part of the
`FW_RAM`.
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 The firmware commands and responses are built on top of the [Framing
Protocol](../framing_protocol/framing_protocol.md). Protocol](../framing_protocol/framing_protocol.md).
@ -414,8 +436,9 @@ host <-
### Firmware services ### Firmware services
The firmware exposes a BLAKE2s function through a function pointer The firmware exposes a BLAKE2s function through a function pointer
located in MMIO `BLAKE2S` (see memory map below) with the with located in MMIO `BLAKE2S` (see [memory
function signature: map](system_description.md#memory-mapped-hardware-functions)) with the
with function signature:
```c ```c
int blake2s(void *out, unsigned long outlen, const void *key, int blake2s(void *out, unsigned long outlen, const void *key,
@ -438,98 +461,10 @@ typedef struct {
The `libcommon` library in The `libcommon` library in
[tillitis-key1-apps](https://github.com/tillitis/tillitis-key1-apps/) [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: See [our apps repo](https://github.com/tillitis/tillitis-key1-apps)
for examples of client and TKey apps as well as libraries for writing
| *name* | *prefix* | *address length* | both.
|----------|----------|--------------------------------------|
| 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.

View File

@ -16,7 +16,7 @@ The described functionality and requirements applies to version 1 of
the TKey (TK1) the TKey (TK1)
The intended users of this document are: 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 - Developers of secure applications for the TKey
- Technically skilled third parties that wants to understand the - Technically skilled third parties that wants to understand the
TKey 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 (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 has - the specific device, and something the user knows (the USS). And
the derived can be trusted because of the measurement being used 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 ### 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 Note that once the monitor has been enabled it can't be disabled and
the addresses defining the area can't be changed. the addresses defining the area can't be changed.
### Illegal instruction monitor ### Illegal instruction monitor
Execution of illegal instructions will cause the CPU to enter its trap 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 start flashing the RED led, signalling that the TKey is stuck in an
error state. error state.
### RAM memory protection ### RAM memory protection
The TKey hardware includes a simple form of RAM memory protection. The 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, altered during the life time of a given device. May be copied,
extracted, read from the device. extracted, read from the device.
- CDI - Compound Device Identity. Dervied by the FW when an application - CDI - Compound Device Identity. Computed by the FW when an
is loaded using the UDS and the application binary. Used by the application is loaded using the UDS and the application binary. Used
application to derive secrets, keys as needed. The CDI should never by the application to derive secrets, keys as needed. The CDI should
be exposed outside of the application\_fpga never be exposed.
Additionally the following asset could be provided from the host: 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 Supplied from the host to the device. Should not be revealed to a
third party. 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 ## Subsystems and Components