Writing an OS Kernel from Scratch | 349: SPI Bus — High-Speed Serial Communication

Abstract: SPI is a higher-speed serial bus compared to I2C, commonly used with Flash storage, displays, sensors, and more. This article details SPI’s four modes, protocol timing, the Linux spidev driver, and its application in Flash storage.


1. SPI Protocol: Four-Wire Full-Duplex

1.1 Signal Lines

SPI has 4 core signal lines (sometimes multiple CS lines):

Bash
         Master                    Slave
        ┌──────┐                ┌──────┐
   CLK ─┤      ├────────────────┤      ├── CLK
  MOSI ─┤      ├────────────────┤      ├── MOSI (sometimes called SDO)
  MISO ─┤      │◄───────────────┤      ├── MISO (sometimes called SDI)
   CS ──┤      ├────────────────┤      ├── CS
        └──────┘                └──────┘
  • SCLK / CLK: Clock line, generated by Master
  • MOSI (Master Out Slave In): Data from master to slave
  • MISO (Master In Slave Out): Data from slave to master
  • CS / SS: Chip select line, can have multiple, each selects one slave

1.2 Full-Duplex vs. Half-Duplex

SPI is full-duplex: While the Master sends 1 bit, the Slave returns 1 bit. So SPI’s actual throughput is 2× the clock frequency (each data line transfers 1 bit per clock).

Bash
Clock:   ─┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──┐  ┌──
          └──┘  └──┘  └──┘  └──┘  └──┘  └──┘  └──┘
MOSI:    ──[D7]──[D6]──[D5]──[D4]──[D3]──[D2]──[D1]──[D0]
          ↑    ↑    ↑    ↑    ↑    ↑    ↑    ↑
MISO:    ──[R7]──[R6]──[R5]──[R4]──[R3]──[R2]──[R1]──[R0]
          ↑    ↑    ↑    ↑    ↑    ↑    ↑    ↑
       MSB first, each bit sampled on clock rising or falling edge (configurable)

1.3 Four Modes (Mode 0/1/2/3)

SPI has 4 modes, determined by two parameters:

Mode CPOL (Clock Polarity) CPHA (Clock Phase) Idle Clock Sample Edge
Mode 0 0 0 Low Rising
Mode 1 0 1 Low Falling
Mode 2 1 0 High Falling
Mode 3 1 1 High Rising

Bash
Mode 0 (CPOL=0, CPHA=0):
         SCLK: ____________/
               └──┘└──┘└──┘
          MOSI: ─[D7]──[D6]──
                      ↑  Sample (rising edge)
          MISO: ─[R7]──[R6]──

Mode 1 (CPOL=0, CPHA=1):
         SCLK: ____________/
               └──┘└──┘└──┘
          MOSI: ─[D7]──[D6]──
               ↑  Sample (falling edge)

Different slave devices use different modes. Always check the datasheet. A common mistake is assuming all SPI devices are compatible with Mode 0, but many sensors prefer Mode 3.


2. Flash Storage: SPI’s Home Turf

2.1 NOR Flash vs. NAND Flash

Bash
SPI NOR Flash vs. NAND Flash:

                  SPI NOR Flash          NAND Flash
Interface         SPI                    Parallel (8/16-bit)
Read speed        Fast (random access)   Fast (sequential)
Write speed       Slow                   Fast (page-based)
Erase             Sector (4KB)           Block (128KB+)
Density           Low (< 512Mb)          High (> 1Gb)
Application       Boot, code storage     Mass storage
XIP (Execute in Place) Yes               No

SPI NOR is commonly used:
  - Boot ROM for embedded devices
  - Firmware storage
  - Configuration data
  - FPGA configuration (bitstream)

2.2 Linux spidev Usage

C
#include <linux/spi/spidev.h>
#include <fcntl.h>
#include <sys/ioctl.h>

int main() {
    int fd = open("/dev/spidev0.0", O_RDWR);

    // Configure SPI mode and speed
    uint8_t mode = SPI_MODE_0;
    uint32_t speed = 10000000;  // 10 MHz
    uint8_t bits = 8;

    ioctl(fd, SPI_IOC_WR_MODE, &mode);
    ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);

    // Transfer data
    uint8_t tx[4] = {0x03, 0x00, 0x00, 0x00};  // Read command
    uint8_t rx[4] = {0};
    struct spi_ioc_transfer tr = {
        .tx_buf = (unsigned long)tx,
        .rx_buf = (unsigned long)rx,
        .len = 4,
        .speed_hz = speed,
        .delay_usecs = 0,
        .bits_per_word = bits,
    };
    ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

    close(fd);
    return 0;
}
Last modified: 2024年5月1日

Author

Comments

Write a Reply or Comment

Your email address will not be published.