Using Sonata IO
Overview
The v1.0 Sonata System contains the following peripheral blocks for general purpose use:
- 5x SPI (2 dedicated and 3 muxable)
- 3x UART (1 dedicated and 2 muxable)
- 2x I2C (both muxable)
- GPIO (LEDs and Switches on the board plus GPIO for all headers other than mikroBUS)
- PWM (6 muxable channels, 1 dedicated channel)
- ADC (6 dedicated channels)
- 1x USB Device (dedicated)
Sonata contains a pinmux which allows you to chose which block is connected to a particular physical IO pin. Each pin has its own unique selection of blocks it can be attached to. The 'muxable' in the list refers to the blocks that can connect to multiple pins. Some of the blocks are dedicated, they connect to specific IO, e.g. there is a dedicated SPI block for the Ethernet controller.
Pinmux Usage
The pinmux has two kinds of selectors:
- Output selectors - One per pin, chooses which block I/O outputs to that pin
- Input selectors - One per block IO, chooses which pin inputs to that block IO
A pinmux driver is available to allow you to manipulate these selectors.
Note there are defined constants for the selectors themselves but not the options within the selectors.
E.g. PMOD0 pin 2 has output selector constant OutputPin::pmod0_2
but a raw number is used to choose which block I/O is connected to.
The pinmux documentation provides the possible inputs and outputs for each selector.
For example say you wished to connect the PMOD0 SPI to SPI block 1; there are 4 selectors to set.
- PMOD 0 CS (pin 1), COPI (pin 2) and SCK (pin 4) output selectors
- SPI Block 1 CIPO input selector
The code below shows how to use the pinmux driver to do this:
#include <platform-pinmux.hh>
...
auto pinmux = SonataPinmux();
pinmux.output_pin_select(SonataPinmux::OutputPin::pmod0_1, 2);
pinmux.block_input_select(SonataPinmux::BlockInput::spi_1_cipo, 3);
pinmux.output_pin_select(SonataPinmux::OutputPin::pmod0_2, 2);
pinmux.output_pin_select(SonataPinmux::OutputPin::pmod0_4, 2);
Following this you can then use the spi1
SPI instance to communicate with whatever SPI device is plugged into PMOD0.
Driver Usage
The various drivers work with a capability that points to the relevant device address range in the memory map. CHERIoT RTOS provides the MMIO_CAPABILITY macro to easily get a driver instance for a particular device. The compiler, build system and runtime handle setting up the required capability and providing it to the compartment.
An example usage of the I2C driver can be see in sonata-software/examples/all/i2c_example.cc. The relevant line that creates the driver instance is:
auto i2c0 = MMIO_CAPABILITY(OpenTitanI2c, i2c0);
Similar code can be used to instantiate a driver for all of the peripheral instances and types.
Pinouts
To determine the mapping between the pin names in the pinmux documentation and the physical headers on the Sonata PCB there are a few pinout diagrams you can use.
- Arduino Shield - pin names have the form
ah_tmpioN
whereN
is the Arduino digital pin number. This is the number in light pink on the linked diagram and labeled asDN
on the Sonata PCB silkscreen. This matches the numbering seen on Arduino boards on the digital side of the header. The analog pins (A0 - A5) are fixed and connect directly to the ADC. - Raspberry PI HAT - pin names have the form
rph_gN
whereN
is the GPIO number provided on the pinout diagram. Note this is different to the physical pin number, which relates to the physical pin position and is the number written on the Sonata PCB. The physical pin number is the one immediately next to the header in the pinout diagram. For instancerph_g0
is physical pin 27. - PMOD - pin names have the form
pmodX_Y
whereX
is the PMOD header (0 on the left, 1 on the right) andY
is the pin in the header. TheY
pin number is the physical pin number (corresponding to the 'Pin' column of the various interface type tables in the linked specification). Note there are nopmodX_5
andpmodX_6
pins seen in the pinmux documentation as these are ground and power pins. - PMOD C - These are the 6 pins in the middle of the large PMOD header, they have the form
pmodc_N
whereN
corresponds to the physical pin number. 1, 2 and 3 are the top pins.pmodc_1
is the top right pin. - mikroBUS - pin names have the form
mbN
whereN
is the physical pin number. The mikroBUS specification does not specify specific pin numbers. For Sonatamb1
is mikroBUS pinCS
,mb4
is mikroBUS pinMOSI
,mb5
is mikroBUS pinSDA
andmb10
is mikroBUS pinPWM
. The others follow from those. mikroBUS does not define any GPIO pins so there is no GPIO block for this header.
Peripheral Device Details
The device names used here are those given the CHERIoT RTOS Sonata board description file found in cheriot-rtos/sdk/boards/sonata-prerelease.json. For each peripheral type details of the instances available and what pins they can mux to are given below along with a link to the driver file used for that peripheral.
SPI
Driver: cheriot-rtos/sdk/include/platform/platform-spi.hh
2 of the SPI blocks are fixed.
spi_lcd
- Connects to the LCDspi_ethmac
- Connects to the ethernet controller
3 of the SPI instances are muxable.
spi0
- SPI flash and SD cardspi1
- RPI HAT SPI0, Arduino SPI, PMOD 0 SPIspi2
- RPI HAT SPI1, PMOD 1 SPI, mikroBUS SPI
SPI chip-select (CS)
Each SPI block has 4 chip-select lines which are controlled via the cs
register.
This can be accessed directly by read/writing to the cs
member of the SonataSPI
structure.
To see which cs
bit corresponds to which physical pin consult the pinmux documentation.
For example the raspberry PI HAT GPIO 7 pin (rph_g7
) can be controlled by bit 1 of the SPI block 1 CS register when that is chosen as the output in the rph_g7
output selector in the pinmux.
UART
Driver: cheriot-rtos/sdk/include/platform/platform-uart.hh
1 UART is fixed:
uart
- The system UART available on the TX/RX0 UART header (P12). Connects to FTDI USB UART when jumpers are connected. Used as debug log, stdout and sterr in CHERIoT RTOS.
The other 2 UARTs are muxable:
uart1
- TX/RX1 UART header (P12), RPI Hat UART, Arduino UART, mikroBUS UART, PMOD 0 UARTuart2
- TX/RX1 UART header (P12), PMOD1 UART, RS232, RS485
I2C
Driver: cheriot-rtos/sdk/include/platform/platform-i2c.hh
Both I2C instances are muxable, however they act differently to the other muxable busses. As I2C is a low speed shared bus multiple headers can be muxed onto an I2C instance. So for each i2c blocks it's possible to have all the physical pins driven by it at once.
i2c0
- qwiic0 & Arduino I2C, RPI Hat EEPROM I2C, PMOD0 I2Ci2c1
- qwiic1, RPI Hat I2C, mikroBUS I2C, PMOD1 I2C
Note that the qwiic0 & Arduino I2C pins are physically wired together on the PCB.
GPIO
Driver: cheriot-rtos/sdk/include/platform/platform-gpio.hh
All of the GPIO blocks are fixed, in that each are dedicated to a specific set of pins. For many of those pins there's multiple things they can be muxed to (e.g. on the Raspberry Pi HAT header you can choose between using the SPI to drive the SPI pins or switch them to GPIO).
The available GPIO blocks are:
gpio_board
- LEDs and switches on the Sonata PCBgpio_rpi
- Raspberry PI HATgpio_arduino
- Arduino Shieldgpio_pmod0
- PMOD 0gpio_pmod1
- PMOD 1gpio_pmodc
- PMOD C (the middle 6 pins between PMOD 0 and PMOD 1)
PWM
Driver: cheriot-rtos/sdk/include/platform/platform-pwm.hh
6 PWM channels are muxable, accessible through one RTOS device:
pwm
- Muxable between RPI Hat, Arduino, PMOD and mikroBUS
One channel is fixed, accessible through a separate RTOS device:
pwm_lcd
- Connected to LCD backlight control
ADC
Driver: cheriot-rtos/sdk/include/platform/platform-adc.hh
The ADC uses the FPGA's internal XADC. The XADC is 12-bit with a 1 MSPS sampling rate. It is directly connected to the 6 Arduino analog pins. It can tolerate up to 5v input but the measurable range is 0 - 3v.
USB Device
Driver: cheriot-rtos/sdk/include/platform/platform-usbdev.hh
There is a single fixed USB device:
usbdev
- Connected directly to the User USB on the Sonata PCB.
Currently there is no USB stack or full USB examples available. However there is some test software used for Sonata testing in the sonata-system repository. sonata-system/blob/main/sw/cheri/checks/usbdev_check.cc will connect to a host as a virtual UART sending some text and echoing back received text. sonata-system/blob/main/sw/cheri/common/usbdev-utils.hh (used by usbdev_check) provides some basic USB functionality on top of the driver.