armhub.dev
Peripherals · 19 pages
Peripherals

Quad-SPI & Octo-SPI

Memory-mapped access to external NOR flash over 4 or 8 parallel data lines. Single/dual/quad/octal SDR and DDR modes, the command-address-data phase model, indirect vs memory-mapped operation, and XIP from external flash on STM32 and NXP parts.

7 min read · 1,462 wordsupdated · May 13, 2026

The classic SPI interface tops out around 50 MHz on a single data lane — enough for a configuration EEPROM but nowhere near enough for execute-in-place (XIP) from external flash. Quad-SPI and Octo-SPI fix this by running 4 or 8 data lines in parallel, optionally double-data-rate, optionally with a dedicated DQS (data strobe), and — critically — by exposing the external flash as a region in the CPU's address map. With memory-mapped mode enabled, the CPU can fetch instructions and read data straight from external flash as if it were on-chip; the QSPI controller silently issues read commands behind the scenes.

This page covers the protocol model, the controller programming model on STM32, NXP, and Nordic parts, and the practical configuration of a Winbond W25Q128 — the most common QSPI flash in modern designs.

QSPI / OSPI at a glance
Wire counts
Single (1 data), Dual (2), Quad (4), Octo (8). Plus CS, CLK, optional DQS.
Maximum clock
QSPI typically 100–133 MHz; OSPI with DQS up to 200 MHz DDR (so 3.2 Gbit/s on 8 data lines)
Phases per transfer
INSTRUCTION (1 byte) + ADDRESS (0..4 B) + ALTERNATE (0..4 B) + DUMMY (N cycles) + DATA
Indirect modes
Indirect read, indirect write, status polling — software queues commands, controller executes
Memory-mapped mode
External flash mapped to a fixed address region (e.g. 0x9000_0000 on STM32H7). CPU read = automatic QSPI read.
Prefetch
Speculatively reads ahead of the CPU PC to hide flash latency. Typically 1–4 cache lines.
DMA support
Indirect mode integrates with DMA (or built-in DMA on FlexSPI) for bulk transfers

The QSPI/OSPI phase model

Every QSPI transfer consists of up to five phases. Each phase can independently use 0, 1, 2, 4, or 8 data lines (the "width" of that phase). The full phase set is:

  1. INSTRUCTION — one byte, identifies the command (read, write, erase, status, etc.). Almost always sent on a single line, even when subsequent phases go wide.
  2. ADDRESS — 0 to 4 bytes (24-bit or 32-bit address depending on the chip and operating mode).
  3. ALTERNATE BYTES — 0 to 4 bytes of mode/control flags. Many flash chips use a single alternate byte to indicate "continuous read mode" or "performance enhance mode."
  4. DUMMY CYCLES — N cycles of nothing, while the flash internally drives its data lines into output mode. Required at high clock rates; the count is chip-specific (typically 4–10 cycles).
  5. DATA — variable length, on 1/2/4/8 lines, SDR or DDR.

The configuration register names this "1-1-4" (instr 1-line, addr 1-line, data 4-line) or "1-4-4" or "4-4-4" depending on what each phase uses. The Winbond fast-read-quad-IO command (0xEB) is 1-4-4 SDR; the Macronix octal read (0x8B) is 8-8-8 DDR.

Quad-SPI 1-4-4 fast read — instruction, address, dummy, data
CSCLKIO0IO1IO2IO3instr (1-line)addr (4-line)dummydata (4-line)
The first byte is shipped on IO0 alone while IO1-IO3 stay quiet; once the address phase starts, all four IO lines transition in parallel, dummy cycles let the flash turn the bus around, and four-line data finally streams back to the controller.
CS    \______________________________________________/
CLK    _||||||||||||||||||||||||||||||||||||||||||___
IO[0]  -- INSTR ----- ADDR ----- DUMMY -- DATA -----
IO[1]                 ADDR --             DATA      
IO[2]                 ADDR --             DATA      
IO[3]                 ADDR --             DATA      

SDR vs DDR

Single Data Rate (SDR) latches data on one clock edge per cycle. Double Data Rate (DDR) latches on both edges, doubling throughput at the same clock rate. DDR requires more careful PCB layout — signal integrity must be good at both edges — and at high rates (>100 MHz) requires a dedicated data strobe (DQS) generated by the flash to track signal-to-clock skew dynamically.

ModeLinesEdges per cycleTheoretical at 100 MHz
Single SDR1112.5 MB/s
Quad SDR4150 MB/s
Quad DDR42100 MB/s
Octal SDR81100 MB/s
Octal DDR82200 MB/s

At the very top end (OctaFlash 200 MHz DDR), you're moving 400 MB/s through one peripheral — competitive with parallel async memory bus interfaces and far cheaper in pins.

Indirect mode vs memory-mapped mode

A QSPI controller operates in one of three modes:

Indirect mode

Software writes a command descriptor — instruction byte, address, length, data direction, phase widths, dummy cycle count — into the controller. The controller executes the transaction and either fills a software-readable data register (or DMA target) or transmits from one. Used for:

  • Erase commands (no data phase, just instruction + address)
  • Status-register polling
  • Write-enable, configuration-register writes
  • Programming (data phase out from RAM to flash)
// STM32H7 QUADSPI example — erase a 64 KB block at address `addr`
QUADSPI->CR |= QUADSPI_CR_EN;
QUADSPI->CCR = (3 << QUADSPI_CCR_FMODE_Pos)      // no data
             | (1 << QUADSPI_CCR_IMODE_Pos)      // 1-line instruction
             | (1 << QUADSPI_CCR_ADMODE_Pos)     // 1-line address
             | (2 << QUADSPI_CCR_ADSIZE_Pos)     // 24-bit address
             | 0xD8;                              // BE block erase opcode
QUADSPI->AR = addr;
while (QUADSPI->SR & QUADSPI_SR_BUSY) { }

Memory-mapped mode

The controller is permanently armed with a read-command descriptor. When the CPU (or DMA) issues a read to the QSPI memory region, the controller automatically generates the corresponding QSPI read command. From software's perspective, external flash is just memory: *(volatile uint32_t*)0x9000_0000 returns the first word, transparent to the application.

This is what enables XIP — code linked at 0x9000_0000 runs directly from external flash without ever being copied to RAM. Every instruction fetch causes a QSPI read; with prefetch and 4-line operation, sustained instruction throughput on a 100 MHz QSPI is roughly 50–80 MIPS — about 1/4 the speed of internal flash, but acceptable for cold code paths.

Status-polling mode (auto-polling)

A specialized variant of indirect mode. The controller repeatedly issues a read-status-register command and inspects the returned byte against a mask and a match value. When the condition is met (or a timeout), it raises an interrupt. Used for waiting on flash erase/program completion without burning CPU.

Prefetch and cache interaction

The QSPI controller has a small prefetch buffer (typically 32 bytes — one Cortex-M cache line) that speculatively reads ahead of the CPU's PC. When the CPU fetches sequential instructions, the next cache line is already on its way. This hides most of the round-trip latency for sequential code, but branches kill prefetch — a taken branch invalidates the buffer and forces a fresh QSPI read.

On Cortex-M7 parts (STM32H7), the I-cache and D-cache cache QSPI reads just like internal flash. The cache is a far bigger win than prefetch — measurements on STM32H743 show that with the 16 KB I-cache enabled, QSPI XIP runs within 10–15% of internal-flash speed for typical control-loop code.

D-cache on QSPI memory needs explicit SCB_CleanDCache_by_Addr / SCB_InvalidateDCache_by_Addr calls around writes (when the QSPI is being reprogrammed and the new data must not be served from stale cache). This is the same cache-coherency story as for any non-coherent peripheral region.

Controllers — STM32, NXP, Nordic

VendorPeripheralFeatures
ST (STM32H7, U5, L4+)QUADSPI / OCTOSPI / HSPIDTR, memory-mapped, auto-poll, dual-flash (2× quad = 8-bit), built-in encryption (OTFDEC)
NXP (i.MX RT)FlexSPIUp to dual ports, LUT-based command sequencing, DDR with DQS, AHB master prefetch
Nordic (nRF52, nRF53)QSPIQuad SDR only; no memory-mapped mode — uses internal DMA to copy XIP image into RAM at boot
Microchip (SAM E70)QSPIMemory-mapped, single/dual/quad
Renesas (RA6)OSPIOcto SDR/DDR, DOTF (decryption-on-the-fly) for encrypted external flash

The standout architectural difference is FlexSPI's Lookup Table (LUT): instead of fixed control bits per phase, you write a sequence of "LUT instructions" (CMD, RADDR, MODE, DUMMY, READ, JMP) into a 64-entry table, and trigger a sequence by referencing its starting LUT index. This is more flexible than STM32's fixed-field CCR/TCR registers and supports any chip without firmware changes — useful for designs that target multiple flash vendors.

Configuring a W25Q128 — a worked example

The Winbond W25Q128 is a 128 Mbit (16 MB) QSPI flash; it ships in nearly every STM32 dev board with external memory. Bring-up sequence:

  1. Configure pins — six lines (CS, CLK, IO0..IO3) on AF for QUADSPI. Speed = very-high. Pull-up on CS to avoid floating during reset.

  2. Clock the QSPI peripheral — set the prescaler so the resulting CLK is within the chip's spec. For W25Q128 at 3.3 V, max clock is 133 MHz at the fastest commands and 50 MHz for 0x03 (legacy slow read). For QUAD-IO (0xEB) the chip needs 4 dummy cycles at 80 MHz and 6 at 100 MHz — check the datasheet's Table 9.6.

  3. Read JEDEC ID — confirm communication. The 1-1-1 read-ID command (0x9F) returns 3 bytes: 0xEF 0x40 0x18 for the W25Q128.

  4. Enable Quad Enable bit — in the flash's status register 2, bit QE must be set before quad-IO commands work. This is a one-time non-volatile write:

    qspi_send_cmd(0x06);                 // WREN
    qspi_send_cmd_with_data(0x31, &qe);  // write SR2 with QE=1
  5. Configure memory-mapped read — set the QSPI control register for 0xEB fast-read-quad-IO with 4 dummy cycles, 24-bit addressing, 1-4-4 widths.

  6. Map and execute — code or data linked to 0x9000_0000 now reads directly from flash.

XIP boot patterns

The challenging part of XIP is the chicken-and-egg at reset: the CPU reset vector lives in internal flash, jumps to Reset_Handler in internal flash, but main() lives in external flash — so the QSPI controller and external flash must be fully configured before the first instruction fetch from 0x9000_0000.

Typical boot flow:

  1. Reset vector in internal flash (vector table at 0x0800_0000).
  2. Reset_Handler (internal flash) sets up clocks, configures the QSPI peripheral, enables memory-mapped mode, optionally enables I/D-cache.
  3. Calls main() — which is linked at 0x9000_0000. The first fetch from 0x9000_0000 triggers a QSPI read; the controller delivers the instruction; execution proceeds.

For security-critical designs, the QSPI controller's hardware decryption (OTFDEC on STM32, BEE on NXP) decrypts blocks on the fly. The decryption key is stored in OTP fuses or HUK-derived storage; the flash image is encrypted at provisioning. With this enabled, a physical attacker who reads the external flash chip sees only ciphertext.

Cross-references

  • SPI — the single-line predecessor and electrical foundation
  • GPIO alternate functions — pin configuration for QSPI signals (very-high speed required)
  • Advanced DMA — DMA integration with indirect-mode transfers
  • Memory model — Normal vs Device attributes for the memory-mapped region
  • Linker scripts — placing code/data sections at the external-flash address
  • Winbond W25Q128JV datasheet — reference QSPI flash
  • Macronix MX25LM51245G datasheet — reference octal flash