Peripherals · 19 pages
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.
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.
- 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:
- INSTRUCTION — one byte, identifies the command (read, write, erase, status, etc.). Almost always sent on a single line, even when subsequent phases go wide.
- ADDRESS — 0 to 4 bytes (24-bit or 32-bit address depending on the chip and operating mode).
- 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."
- 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).
- 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.
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.
| Mode | Lines | Edges per cycle | Theoretical at 100 MHz |
|---|---|---|---|
| Single SDR | 1 | 1 | 12.5 MB/s |
| Quad SDR | 4 | 1 | 50 MB/s |
| Quad DDR | 4 | 2 | 100 MB/s |
| Octal SDR | 8 | 1 | 100 MB/s |
| Octal DDR | 8 | 2 | 200 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
| Vendor | Peripheral | Features |
|---|---|---|
| ST (STM32H7, U5, L4+) | QUADSPI / OCTOSPI / HSPI | DTR, memory-mapped, auto-poll, dual-flash (2× quad = 8-bit), built-in encryption (OTFDEC) |
| NXP (i.MX RT) | FlexSPI | Up to dual ports, LUT-based command sequencing, DDR with DQS, AHB master prefetch |
| Nordic (nRF52, nRF53) | QSPI | Quad SDR only; no memory-mapped mode — uses internal DMA to copy XIP image into RAM at boot |
| Microchip (SAM E70) | QSPI | Memory-mapped, single/dual/quad |
| Renesas (RA6) | OSPI | Octo 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:
-
Configure pins — six lines (CS, CLK, IO0..IO3) on AF for QUADSPI. Speed = very-high. Pull-up on CS to avoid floating during reset.
-
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. -
Read JEDEC ID — confirm communication. The 1-1-1 read-ID command (
0x9F) returns 3 bytes:0xEF 0x40 0x18for the W25Q128. -
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 -
Configure memory-mapped read — set the QSPI control register for
0xEBfast-read-quad-IO with 4 dummy cycles, 24-bit addressing, 1-4-4 widths. -
Map and execute — code or data linked to
0x9000_0000now 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:
- Reset vector in internal flash (vector table at 0x0800_0000).
Reset_Handler(internal flash) sets up clocks, configures the QSPI peripheral, enables memory-mapped mode, optionally enables I/D-cache.- Calls
main()— which is linked at0x9000_0000. The first fetch from0x9000_0000triggers 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