Target support
Kernel layer
Overview
The kernel layer is comprised of
an abstract interface and associated support captured by
${FIAT_PATH_REPO}/src/fiat/target/kernel/kernel.h ${FIAT_PATH_REPO}/src/fiat/target/kernel/kernel.c
a concrete implementation captured by
${FIAT_PATH_REPO}/src/fiat/target/kernel/imp/kernel_imp.h ${FIAT_PATH_REPO}/src/fiat/target/kernel/imp/kernel_imp.c
plus associated configuration
${FIAT_PATH_REPO}/src/fiat/target/kernel/imp/kernel_imp.conf
noting that:
The kernel state is reflected by a set of registers, each identified by an 8-bit index
8 7 6 5 4 3 2 1 +-+-+-+-+-+-+-+-+ | | | | | | | | | +-+-+-+-+-+-+-+-+ ^ \___________/ | | | | | +--------- address +----------------- class : 0 = GPR, 1 = SPR
meaning the set is divided into two classes (using the MSB): General-Purpose Registers (GPRs) are defined by the kernel, whereas Special-Purpose Registers (SPRs) are defined by the driver.
Each register has an associated 8-bit type, i.e., some meta-data
8 7 6 5 4 3 2 1 +-+-+-+-+-+-+-+-+ | | | | | | | | | +-+-+-+-+-+-+-+-+ ^ ^ ^ | | | | | | | | +--- read access : 0 = false, 1 = true | +----- write access : 0 = false, 1 = true +------- content length : 0 = fixed, 1 = variable
noting that the terms Read-Only (RO), Write-Only (WO), and Read-Write (RW), are used as a short-hand to describe read and/or write access cases (viewed from the perspective of an external client using the target implementation).
The standard SPRs defined are as follows:
index
0x80 = 128, i.e., SPR address0x00 = 0is calledret: this is an RO register used for a return code,index
0x81 = 129, i.e., SPR address0x01 = 1is calledtsc: this is an RO register used for a time-stamp counter.
API
-
ret_t kernel(int op, kernel_reg_t *spr, kernel_reg_t *gpr)
The kernel functionality.
- Parameters:
op – an operation identifier (or opcode), allowing selection of sub-functionality.
spr – the special-purpose register set.
gpr – the general-purpose register set.
- Returns:
a return code indicating failure (e.g., EXIT_FAILURE) or success (e.g., EXIT_SUCCESS).
-
ret_t kernel_prologue_major(int op, kernel_reg_t *spr, kernel_reg_t *gpr)
A function that performs “major” initialisation, optionally, and manually invoked before the kernel functionality (e.g., to allocate memory).
- Parameters:
op – an operation identifier (or opcode), allowing selection of sub-functionality.
spr – the special-purpose register set.
gpr – the general-purpose register set.
- Returns:
a return code indicating failure (e.g., EXIT_FAILURE) or success (e.g., EXIT_SUCCESS).
-
ret_t kernel_epilogue_major(int op, kernel_reg_t *spr, kernel_reg_t *gpr)
A function that performs “major” finalisation, optionally, and manually invoked after the kernel functionality (e.g., to deallocate memory).
- Parameters:
op – an operation identifier (or opcode), allowing selection of sub-functionality.
spr – the special-purpose register set.
gpr – the general-purpose register set.
- Returns:
a return code indicating failure (e.g., EXIT_FAILURE) or success (e.g., EXIT_SUCCESS).
-
void kernel_prologue_minor(int op, kernel_reg_t *spr, kernel_reg_t *gpr)
A function that performs “minor” initialisation, automatically invoked before the kernel functionality.
- Parameters:
op – an operation identifier (or opcode), allowing selection of sub-functionality.
spr – the special-purpose register set.
gpr – the general-purpose register set.
-
void kernel_epilogue_minor(int op, kernel_reg_t *spr, kernel_reg_t *gpr)
A function that performs “minor” finalisation, automatically invoked after the kernel functionality.
- Parameters:
op – an operation identifier (or opcode), allowing selection of sub-functionality.
spr – the special-purpose register set.
gpr – the general-purpose register set.
Driver layer
Overview
The driver layer essentially implements the interface between the client (i.e., the user) and the kernel (i.e., the functionality being used, as part of the target implementation), using features in and so support from the the board layer. For example, it is responsible for
management of UART-based communication protocol, that e.g., allows transfer of data into and from the kernel register set and invocation of kernel functionality, and
management of GPIO pins to, e.g., act as a trigger signal.
Irrespective of driver type, the generic usage model is captured by
Client Target
======================================================================
:
wr index, data ------ cmd -----> kernel_gprs[ index ] = data
: <----- ack -----
:
kernel_prologue op ------ cmd -----> kernel_prologue_major( op )
: <----- ack -----
:
kernel op, rep ------ cmd -----> kernel_prologue_minor( op ) -+
: trigger = 1 |
: kernel ( op ) |-- repetition 0
: trigger = 0 |
: kernel_epilogue_minor( op ) -+
:
: kernel_prologue_minor( op ) -+
: trigger = 1 |
: kernel ( op ) |-- repetition 1
: trigger = 0 |
: kernel_epilogue_minor( op ) -+
:
: :
:
: kernel_prologue_minor( op ) -+
: trigger = 1 |
: kernel ( op ) |-- repetition rep-1
: trigger = 0 |
: kernel_epilogue_minor( op ) -+
: <----- ack -----
:
kernel_epilogue op ------ cmd -----> kernel_epilogue_major( op )
: <----- ack -----
:
rd index ------ cmd -----> data = kernel_gprs[ index ]
: <----- ack -----
:
======================================================================
where, read top-to-bottom, the idea is that
the client (optionally) issues the
wrcommand to write any input data,the client (optionally) issues the
kernel_prologuecommand to perform “major” initialisation,the client issues the
kernelcommand to invoke the kernel some number of times; in each repetition, itperforms some “minor” initialisation,
sets the GPIO trigger pin to 1,
executes the the kernel itself,
sets the GPIO trigger pin to 0,
performs some “minor” finalisation,
the client (optionally) issues the
kernel_epiloguecommand to perform “major” finalisation,the client (optionally) issues the
rdcommand to read any output data.
Notation
The following notation is used to specify a given protocol:
communicated messages are structured using the following types:
<T:X>denotes a fieldXof typeT,[T:X^n]denotes a fieldXwhich is ann-element sequence whosei-th elementX[i]is of typeT,if a field has a fixed value
Y, it is specified, e.g., as<T:X>=Y.||denotes concatenation.{X}denotes optionality, e.g., thatXmay or may not form part of a message depending on some configuration.
The binary driver
This driver implementation uses a binary protocol: the focus is on machine-friendly, programmatic interaction, which e.g., emphasises efficiency in support of “bulk” target implementation use-cases.
Communication is
stream-based,
with request and acknowledge messages are represented using a
sequence of raw bytes.
Note that
the binary driver
includes a
CRC-based
checksum with every message (computed over all preceding, non-CRC content):
this contrast with
the text driver,
which does not.
Based on this, the following command set is supported:
Ping.
reqsyntax:<byte:req>='!' || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <byte:crc^2>
Reset.
reqsyntax:<byte:req>='*' || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <byte:crc^2>
Query version.
reqsyntax:<byte:req>='$' || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <byte:patch> || <byte:minor> || <byte:major> || <byte:crc^2>
Query identifier of, or “
nameof” a register.reqsyntax:<byte:req>='"' || <byte:index> || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <vint:size> || [byte:data^size] || <byte:crc^2>
Query allocated size of, or “
sizeof” a register (measured in bytes).reqsyntax:<byte:req>='|' || <byte:index> || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <vint:size> || <byte:crc^2>
Query used size of, or “
usedof” a register (measured in bytes).reqsyntax:<byte:req>='#' || <byte:index> || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <vint:size> || <byte:crc^2>
Query type of, or “
typeof” a register.reqsyntax:<byte:req>='?' || <byte:index> || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <byte:type> || <byte:crc^2>
Transfer content (i.e., write) into a register.
reqsyntax:<byte:req>='>' || <byte:index> || <vint:size> || [byte:data^size] || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <byte:crc^2>
Transfer content (i.e., read) from a register.
reqsyntax:<byte:req>='<' || <byte:index> || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <vint:size> || [byte:data^size] || <byte:crc^2>
Execute the kernel.
reqsyntax:<byte:req>='=' || <byte:op> || <vint:rep> || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <byte:crc^2>
note:
opis an operation identifier passed to the implementation, andrepis a repeat count
Execute the kernel prologue (or “major” initialisation).
reqsyntax:<byte:req>='[' || <byte:op> || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <byte:crc^2>
note:
opis an operation identifier passed to the implementation
Execute the kernel epilogue (or “major” finalisation).
reqsyntax:<byte:req>=']' || <byte:op> || <byte:crc^2>acksyntax:on failure,
<byte:ack>='-' || <byte:err> || <byte:crc^2>on success,
<byte:ack>='+' || <byte:crc^2>
note:
opis an operation identifier passed to the implementation
The text driver
This driver implementation uses a text protocol: the focus is on human-friendly interaction, which e.g., emphasises usability in support of “ad-hoc” target implementation use-cases such as debugging.
In concept at least, it is similar to the ChipWhisperer
SimpleSerial
protocol.
Communication is
line-based,
with request and acknowledge messages are represented using a
sequence of
ASCII
characters terminated with a
Carriage Return (CR)
only; a byte is represented by 2 hexadecimal characters
(per the printf format specifier %02X).
Based on this, the following command set is supported:
Ping.
reqsyntax:<char:req>='!'acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+'
Reset.
reqsyntax:<char:req>='*'acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+'
Query version.
reqsyntax:<char:req>='$'acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+' <byte:patch> <byte:minor> <byte:major>
Query identifier of, or “
nameof” a register.reqsyntax:<char:req>='"' <byte:index>acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+' <vint:size> [byte:data^size]
Query allocated size of, or “
sizeof” a register (measured in bytes).reqsyntax:<char:req>='|' <byte:index>acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+' <vint:size>
Query used size of, or “
usedof” a register (measured in bytes).reqsyntax:<char:req>='#' <byte:index>acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+' <vint:size>
Query type of, or “
typeof” a register.reqsyntax:<char:req>='?' <byte:index>acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+' <byte:type>
Transfer content (i.e., write) into a register.
reqsyntax:<char:req>='>' <byte:index> <vint:size> [byte:data^size]acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+'
Transfer content (i.e., read) from a register.
reqsyntax:<char:req>='<' <byte:index>acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+' <vint:size> [byte:data^size]
Execute the kernel.
reqsyntax:<char:req>='=' <byte:op> <vint:rep>acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+'
Execute the kernel prologue (or “major” initialisation).
reqsyntax:<char:req>='[' <byte:op>acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+'
Execute the kernel epilogue (or “major” finalisation).
reqsyntax:<char:req>=']' <byte:op>acksyntax:on failure,
<char:ack>='-' <byte:err>on success,
<char:ack>='+'
Board layer
Overview
The board layer is comprised of
an abstract interface and associated support captured by
${FIAT_PATH_REPO}/src/fiat/target/board/board.h ${FIAT_PATH_REPO}/src/fiat/target/board/board.c
a concrete implementation captured by
${FIAT_PATH_REPO}/src/fiat/target/board/imp/board_imp.h ${FIAT_PATH_REPO}/src/fiat/target/board/imp/board_imp.c
plus associated configuration
${FIAT_PATH_REPO}/src/fiat/target/board/imp/Dockerfile.in ${FIAT_PATH_REPO}/src/fiat/target/board/imp/Makefile.in
noting that although it essentially acts as a Hardware Abstraction Layer (HAL), it is potentially as simple as a shim layer over a platform-specific HAL.
API
-
int board_init()
Initialise the board.
- Returns:
a flag indicating failure (false) or success (true).
-
void board_uart_wr(byte x)
Write a byte to the UART, blocking until successful.
- Parameters:
x – the value to write.
-
byte board_uart_rd()
Read a byte from the UART, blocking until successful.
- Returns:
the value read.
-
void board_gpio_wr(pin_t p, int x)
Write a value to (or assert a value on) a GPIO pin.
- Parameters:
p – the GPIO pin identifier.
x – the value to write.
-
int board_gpio_rd(void)
Read a value from (or sample a value from) a GPIO pin.
- Parameters:
p – the GPIO pin identifier.
- Returns:
the value read.
-
int board_rng_rd(byte *r, int n)
Read (or sample) n bytes of randomness.
- Parameters:
r – the destination buffer.
- Returns:
the number of bytes read (and then written into r, which may be less than n).