Reference Manual for Sonata Core

In a classical microcontroller, you would have a core along with the peripherals around the core. On the Sonata system this is all part of an open-source FPGA design, which allows you to modify the core to add new features (and for us to add updates to your core without needing you to desolder your main IC!).

This also means you can customize your design. You may want to have a different number of UARTs or SPI blocks for example. This document describes the base configuration.

The FPGA image is parameterizable to enable custom setups. It should be easy, for example, to change the number of UART, SPI and I2C instances. We will provide pre-built images for common configurations.

Interoperate

For the interoperable requirement, we need to make sure our hardware design can interact with that of OpenTitan Earl Grey. Since OpenTitan Earl Grey uses a TileLink Uncached Lightweight (TL-UL) bus, we use the same in the Sonata system to ease designing a bridge interface.

Hardware IP blocks

To support all the peripherals that are on the FPGA boards, we need corresponding hardware IP blocks for Ibex to be able to interact with them:

  • I2C for QWIIC
  • SPI for the LCD screen and ethernet
  • GPIO for buttons and LEDs
  • HyperRAM controller

There might be other IP blocks necessary for interacting with headers such as an analogue to digital converter. We also need some modifications to CHERIoT Ibex, which are detailed in its own page.

Wherever possible, we reuse existing, high-quality, open-source hardware IP blocks that are fit for purpose.

Memory layout

For all registers in this section, the functionality is mapped onto the least significant bits of registers and each register is 32 bits wide.

Base addressSizeFunctionality
0x0010_0000256 KiBInternal SRAM
0x3000_00004 KiBRevocation tags
0x4000_00001 MiBTagged RAM
0x4010_00007 MiBUntagged RAM
0x8000_00004 KiBGPIO
0x8000_10004 KiBPWM
0x8000_20004 KiBReserved for DMA
0x8000_30004 KiBHyperRAM
0x8000_40004 KiBReserved
0x8000_50004 KiBPinmux
0x8000_60004 KiBR-Pi Header GPIO
0x8000_70004 KiBArduino GPIO
0x8000_80004 KiBPMOD GPIO
0x8000_90004 KiBRGB LED controller
0x8000_A0004 KiBHardware revoker
0x8000_B0004 KiBADC
0x8004_000064 KiBTimer
0x8010_00001 MiBUART
0x8020_00001 MiBI2C host
0x8030_00001 MiBSPI host
0x8040_00001 MiBUSB device
0x8800_000064 MiBPLIC
0xB000_00004 KiBDebug module

Clocking infrastructure

The whole system is driven by the same clock with the exception of the HyperRAM controller. Optionally the HyperRAM controller can be clocked higher than the rest of the chip. To accommodate this, we introduce a synchronization interface with primitive FIFOs.

Memory architecture

We have a few different types of memory in the Sonata system: FPGA SRAM, HyperRAM and flash. With CHERI we need to think about capability tags and revocation tags. Any memory that needs to contain capabilities must have one capability tag per 32 bits. Any memory that needs to be revocable must have one revocation tag per 32 bits.

Capability tags

All capability tags live in SRAM. All SRAM that is allocated for code and data will have corresponding capability tags. Any data stored to HyperRAM and flash are not expected to be tagged. Since capability tags are out of band information and do not need to be memory mapped, we can store these within the error correction bits that are available on the FPGA's memory.

We envision that code can live in HyperRAM with an instruction cache for improved performance. However, this does require code to be able to live in untagged memory. This should be fine as CHERI capabilities are derived and manipulated at runtime, but does require toolchain changes to LLVM and the corresponding RTOS.

Revocation tags

Revocation tags are essential in providing temporal memory safety in CHERI. This only covers a subset of memory that is likely to be used by the heap. Setting the revocation bit effectively stops any capability with that base address from being loaded from memory. This is a temporary step as the revocation engine scans through memory to invalidate all capabilities to this address. Once the complete memory is scanned, the revocation bit can be unset and the memory can be reused.

In Sonata, the revocation tags only cover a subset of mapped memory. They should apply to memory regions that are most likely to be used as heap, it is likely this will cover all of internal SRAM and some of HyperRAM. Unlike capability tags, revocation tags need to be memory mapped so the memory allocator can manipulate them.

In CHERIoT Ibex the size of memory allocated for this is defined by TSMapSize which indicates how many 32-bit words can be used for revocation bits. The default value for this is 1,024, which corresponds to 8 KiB. In CHERIoT Safe the size of the revocation tag memory is 16 KiB.

List of SRAM blocks

Here's a list of blocks by size that we need to allocate in SRAM. The XC7A35T has 100 blocks of 18 kilobit block RAM, see datasheet. In total that gives use 225 KiB of block RAM, but we may not efficiently map onto 18 kilobit blocks and thus lose some memory. The block RAM usage in the table below was calculated using Vivado 2023.2's block memory generator.

TypeSizeWidthDepthRAM Blocks
Internal memory128 KiB3332,76860
Revocation tags16 KiB324,0968
RAM capability tags32 KiB328,19215
Instruction cache data4 KiB645122
Instruction cache tags1 KiB225121
Total181 KiB86
Available225 KiB100