Skip to content

Pinout and Peripherals

Pinout

Compared to a normal microcontroller, the pins on the QL-EOS-S3 SoC are a little goofy. Similar to a normal microcontroller, it has some hardware GPIO pins that are attached to the ARM core, along with standard peripherals such as UART, and SPI busses, along with an analog port. Unlike a normal microcontroller, however, most of the pins are not connected to the ARM core at all! Instead, they are all connected to the FPGA core, and can be used by writing your own peripherals in Verilog.

Here is a map of all of the fixed pins on the board:

Pinout

All of the pins on the right and left sides of the boards can be used freely. Note that the two JTAG pins (IO_44 and IO_45 on RevB) have on-board pullup resistors to the 3.3V line. The pins in the center section are also possible to use use, however they affect how the SoC boots.

Use with caution: SPI pins

The SPI pins (CLK, MOSI, MISO, and SS1) are attached to the on-board SPI flash. The CLK, MOSI, and MISO pins can be safely shared with other SPI devices, as long as they do not interfere with the boot process.

Use with caution: Bootstrap pins

IO_8, IO_9, IO_19, and IO_20 are avaiable as general-purpose IO, however they have 10k pull-down resistors connected to them, and their values have to be logic low at boot time for the microcontroller to turn on correctly.

Hardware GPIO

The arm core is able to control 8 of the pins (marked GPIO0..7 in the pinout diagram). Here are the steps to use them:

Step 1: Check that the GPIO is assigned to the correct pin

Each GPIO can be connected to one of two hardware pins. The pin selection is stored in IOMUX_IO_REG_SEL, at 0x40004D60. Here is a table of the available GPIO pin mappings, and the proper one for the breakout board:

GPIO Default pin Alternate pin Breakout board pin Notes
0 IO_6 IO_24 IO_6
1 IO_9 IO_26 IO_9 Bootstrap pin
2 IO_11 IO_28 IO_28 Breakout board uses alternate pin
3 IO_14 IO_30 IO_14
4 IO_18 IO_31 IO_18 Connected to USER BUTTON
5 IO_21 IO_36 IO_21
6 IO_22 IO_38 IO_22 Connected to STATUS LED
7 IO_23 IO_45 IO_23

Only GPIO2 needs to be assigned to the alternate pin, so the following can be used to fully configure the GPIO pin assignments:

machine.mem32[0x40004D60] = (1<<2)

Step 2: Configure the pin for GPIO mode

Now that we know the pin that the GPIO is assigned to, the next step is to configure it for GPIO mode, as either an input or output. To do this, write to the IOMUX_PADxx_CTRL register associated with the pad. The address is: 0x40004C00 + 4*(pad number)

Example: Configure pin IO_18 as a GPIO input with pull-up enabled:

machine.mem32[0x40004C00 + (18*4)] = (0x3<<0) | (0x0<<3) | (1<<8) | (1<<5) | (1<<11) | (0x1<<6)

Example: Configure pin IO_22 as a GPIO output with 4mA drive strength:

machine.mem32[0x40004C00 + (22*4)] = (0x3<<0) | (0x0<<3) | (1<<8)

Step 3: Read (or write) to the GPIO

To read from a gpio, use the register MISC_IO_INPUT at 0x40005100. For instance, to read the value of GPIO4 (user button):

print((machine.mem32[0x40005100] >> 4) & 0x1)

To write a GPIO value, us the register MISC_IO_OUTPUT at 0x40005104. For instance, to set GPIO6 (status led) high:

machine.mem32[0x40005104] |= (1 << 6)

Interrupts

Each GPIO pin can be used as a level or edge sensitive interrupt. All GPIO pins share a single interrupt vector.

TODO: Example for using interrupts

Example GPIO library

Here is a barebones GPIO library, written in Micropython:

class GPIO:
  IOMUX_BASE = 0x40004C00
  IOMUX_IO_REG_SEL = 0x40004D60
  MISC_IO_INPUT = 0x40005100
  MISC_IO_OUTPUT = 0x40005104
  def __init__(self):
    # Configure GPIO pin selects for breakout board
    # Note: Only GPIO2 uses alternate pin setting
    machine.mem32[self.IOMUX_IO_REG_SEL] = (1<<2)
  def write(self, gpio, val):
    if val == 0: # Low
      machine.mem32[self.MISC_IO_OUTPUT] &= ~(1 << gpio)
    else: # High
      machine.mem32[self.MISC_IO_OUTPUT] |= (1 << gpio)
  def read(self, gpio):
    return ((machine.mem32[self.MISC_IO_INPUT] >> gpio) & 0x01)
  def pinmode(self, gpio, mode):
    pads = (6,9,11,14,18,21,22,23)
    if mode == 'input':
      machine.mem32[self.IOMUX_BASE + pads[gpio]*4] = (0x3<<0) | (0x0<<3) | (1<<8) | (1<<5) | (1<<11)
    elif mode == 'input_pullup':
      machine.mem32[self.IOMUX_BASE + pads[gpio]*4] = (0x3<<0) | (0x0<<3) | (1<<8) | (1<<5) | (1<<11) | (0x1<<6)
    elif mode == 'output':
      machine.mem32[self.IOMUX_BASE + pads[gpio]*4] = (0x3<<0) | (0x0<<3) | (1<<8)

And to use it:

gpio=GPIO()
gpio.pinmode(6,'output')
gpio.write(6,1) # turn on LED
gpio.pinmode(4,'input_pullup')
gpio.read(4) # read button input

UART

TODO

SPI Master

SPI Master 1 is connected to the ARM, and available for use. Unfortunately, the technical reference manual is very light on the details, and the SDK drivers are buggy and poorly documented. It appears to be an instantion of a Synopsys DesignWire IP 'DW_apb_ssi'. Because this a commonly used IP block, there are some references to similar hardware:

TODO

Analog to digital controller

TODO

FPGA

The FPGA is the swiss army knife of peripherals

Clocks

There are 3 input clocks from the ARM to the FPGA:

Clock Name Max frequency Notes
C16 Sys_Clk0 ? General purpose
C21 Sys_Clk1 ? General purpose
C02 Sys_Pclk ? SPI master APB bus clock (?)

Sys_Clk0 and Sys_Clk1 are 'general purpose' clocks. Both are fed from dedicated clocks, so configuring them should not intefere with any other ARM peripherals. They can be driven by either the high or low speed system clocks, with a divisor from 0-512. The technical reference manual specifies a default maximum speed of 10MHz, however speeds up to the system clock (72MHz) appear to function well.

Sys_Pclk is fed from C02, which is shared with some other ARM peripherals. It is also available for use, but reconfiguring it might cause problems.

There are 2 output clocks from the FPGA to the ARM:

Clock Name Max frequency Usage
C40 WB_CLK 10 Mhz Fabric to Wishbone clock
C41 Sys_Pkfb_Clk 10 MHz Fabric to PKFB (packet fifo) clock

Both of these clocks are special purpose, and should be configured if you are planning to use their associated peripherals.

IO pads

The FPGA can control all of the IO pads, including a direction function. However, the pads must first be configured by the ARM processor, for both driving source and I/O control functions.

Some of the pads are special clock inputs (TODO: which ones?)

SPI Master

The FPGA is supposed to be able to interface with the SPI master, for some reason. It's not clear how to do this, or what the intended use case is.

Communicating with the FPGA

To get the best use out of your FPGA designs, you'll want to talk to them from the ARM core. Here are some of the ways to do so.

AHB <-> wishbone bridge

The easiest way to talk to the FPGA is through the AHB<->wishbone bus, which directly maps into the ARM memory space.

Block M4 base address Size
Fabric (FPGA) 0x4002_0000 128KB

Transactions must be intiated by the ARM. For bulk data transfer, consider the Packet FIFO interface.

SDMA

TODO

Packet FIFO

There are 4 fifo blocks:

Block M4 base address Size
RAMFIFO0 (FPGA) 0x4001_8000 4KB
RAMFIFO1 (FPGA) 0x4001_9000 4KB
RAMFIFO2 (FPGA) 0x4001_A000 4KB
RAMFIFO3 (FPGA) 0x4001_B000 4KB

TODO

Interrupts

There are 4 interrupt sources from the FPGA to the ARM.

Intr Num NVIC Intr Address Exception Priority Comments
20 4 0x50 Fabric Message 0-7 (programmable) SoC WIC interrupts

FB_0_INTR, FB_1_INTR, FB_2_INTR, FB_3_INTR

Other peripherals (I2C, I2S, PDM, FFE, Sensor Manager)

The datasheet and technical reference talk about some other useful peripherals, such as I2C, I2S, and PDM. Unfortunately, these peripherals seem to be attached to the Sensor Manager, and thus not directly accessable to the ARM part. It's unclear if the Sensor Manager is present in the version of the SoC that we have. The Flex Fusion Engine (FFE) is also mentioned, but is likely not enabled in the part variant that we have without some trickery.

Fortunately, this is the kind of task that the FPGA is perfect for. For instance, if you want an I2C bus, or ten, you can design them using Verilog, then use them directly from the ARM core. You're free to make the best possbile peripherals!