SoC and Drivers

SoC: System-on-Chip

On a silicon die, traditionally, this was just a processor, but now it can include a lot more. The following two graphics show what is can be inside a SoC

ProcessorProcessorProcessorMemory ControllerGraphicsCrypto EngineUSB ControllerEthernetUART/SPIUART/SPIBluetooth/WiFiADCSerialControllerProcessorMemory ControllerGraphicsCrypto EngineUSB ControllerEthernetUART/SPIUART/SPIBluetooth/WiFiSerialControllerMemory

Some SoC's you might be familiar with:

  • TI MSP432: More like a microcontroller (more like the righ-hand SoC)
    • Processor Core
    • A couple serial controllers
    • A couple of ADCs
    • A DAC
    • Power control module
    • Built in clock select
    • Timers
  • Raspberry Pi (more like the left-hand SoC)

Remember back to CPE 329, to control peripheral, you would include a header file, and that header file would have structs and variables:

#include msp432.h
EUSCI_BO->CTLW0 = EUSCI_B_CTLW0_SWRST|...;

Under the hood, the way most systems work (including ARM systems), all I/O commands to peripheral are done using memory mapped IO (MMIO). In the SoC diagram, to send data from the processor to the graphics engine, loads and stores are done to send data between the two.

The code snippet below shows writing a 1 to address 0x20000000

int *p = (int*)0x20000000;
*p = 0x01;

To know what this actually does, you would need two things:

  1. TI would need to give you the address range of all memory peripheral
  2. TI would need to provide the documentation for the accelerators themselves so you would know what bit in the address space corresponds to what.

However, if things were left at this level, programmers would need to remember, or look up all of this every time they want to use a peripheral. This is abstracted with things like the msp432.h header file.

There are a couple of issues with leaving things at such a low level. Programmers don't want to have to remember things at such a low level, and the code is no longer portable.

Generally, to separate low level block commands from app code, we build drivers.

Driver Definition

Code that takes a high-level function, and translates it into peripheral control signals. These may be customized per OS.

For example, for the UART module, you may have a set_baud() function. In Linux, for instance, you would need to register the driver with the kernel (key part of OS) and/or since Linux is a monolithic kernel, you may have to compile the kernel with the driver. Linux mandates some driver kernel interactions and for common device types, Linux will mandate certain generic high-level function calls that the driver must support.

For URAT, the following may be an example:

// UART required functions - EXAMPLE
set_baud();
tx_byte(uchar);
rx_byte();
tx_buffer(*uchar);
rx_buffer(*uchar);

Using these standard signatures allow programs to work agnostic of the underlying driver. If you have a device that Linux is aware of, and there is a driver for that device, Linux will put a pointer to that driver in the /dev/ directory.

For instance, if you connect a USB serial device, it will show up as /dev/usbtty0. Memory will show up as /dev/mem. So, for DMA, you just need to point it to /dev/mem.

ARM: devicetree.dtb contains a listing of peripherals and address ranges. For instance, it may look something like:

devicetree.dtb
ethernet 0x10010FF 0x200000

x86: BIOS/UEFI

Last updated on