SPI Transactions Explained: Read, Write and Multi-Byte Data Transfers

SPI-04: SPI Transactions β€” Programmed I/O, Read, Write & Streaming β€” VLSI Trainers
SPI Series · Article 4 of 6

SPI-04: SPI Transactions

Building programmed I/O on top of SPI β€” how multiple exchanges combine into read, write, and multi-byte streaming transactions. Header format, unused wires, burst accesses, and performance implications.

πŸ”€Exchange vs Transaction

SPI-03 covered the fundamental unit of SPI communication β€” the exchange: exactly 8 bits transferred simultaneously in both directions. But 8 bits alone are rarely enough for real-world communication. A CPU talking to a peripheral needs to say what address to read or write, whether it is reading or writing, and what data to transfer. This requires building a higher-level structure on top of exchanges.

That structure is called an SPI transaction. The key distinction:

TermDefinitionDuration
ExchangeTransfer of exactly 8 bits between master and slave β€” simultaneously in both directions8 SCLK cycles, SS stays LOW throughout
TransactionOne or more consecutive exchanges that together implement a complete read or write operation at a specific address16+ SCLK cycles (2+ exchanges), SS stays LOW across all exchanges
The SPI protocol only standardises the exchange β€” not the transaction. Every SPI peripheral manufacturer defines their own transaction protocol. Always check the device datasheet. The examples in this article describe one common pattern; your specific device may differ.

In the programmed I/O model that SPI transactions implement, the CPU (master) communicates with peripherals (slaves) through register addresses. A write transaction sets a register value; a read transaction fetches one. This is the same model used for memory-mapped I/O in full CPU architectures β€” SPI simply serialises those operations over 4 wires.

πŸ“‹The Transaction Header Format

Every transaction begins with a header exchange β€” the first 8-bit SPI exchange in which the master tells the slave:

  • Which register address to access
  • Whether the operation is a read or write
  • Whether this is a single-byte or multi-byte (streaming) transaction

First exchange β€” Header byte structure (MOSI)

Bits 7–2ADDRESS
Bit 1R/WΜ„
Bit 0STREAM

6-bit address (bits 7–2) Β· Read/Write flag (bit 1): 1 = write, 0 = read Β· Stream flag (bit 0): 1 = multi-byte, 0 = single byte

Figure 1 β€” Header byte bit field layout
7 6 5 4 3 2 1 0 MSB LSB ADDRESS [5:0] R/WΜ„ STREAM Address (6 bits β€” register to read/write) R/WΜ„: 0=read, 1=write STREAM: 0=single, 1=multi-byte MISO during first exchange: slave sends all-zeros (has not yet retrieved data)

Header byte transmitted by master on MOSI during the first exchange. The 6-bit address field selects the register; R/WΜ„ (bit 1) specifies the direction; STREAM (bit 0) flags a multi-byte operation. MISO is all zeros during this exchange β€” the slave has nothing to send yet.

πŸ” Worked Example β€” Decoding a Header Byte

Header byte seen on MOSI: 0x59 = 0101 1001β‚‚

Field extraction:

Bits 7–2 = 010110 = address 0x16 (22 decimal)
Bit 1 = 0 β†’ Read operation
Bit 0 = 1 β†’ Streaming (multi-byte)

Interpretation: Master wants to read from register address 0x16, and this is a streaming transaction β€” more exchanges will follow specifying the length and delivering the data.

This is exactly Transaction 1 from the textbook’s streaming exercise (page 17 of the PDF).

πŸ“₯Read Transaction

A single-byte read transaction uses exactly two consecutive exchanges with SS held LOW throughout:

  • Exchange 1 (Header): Master sends header byte on MOSI (address + R/WΜ„=0 + STREAM=0). Slave sends zeros on MISO β€” it has not yet fetched the data.
  • Exchange 2 (Data): Master sends zeros on MOSI (nothing to say). Slave sends the requested register data on MISO.
Figure 2 β€” Single-byte SPI read transaction (CPOL=1, CPHA=0, MSB first)
CPOL=1, CPHA=0, MSB first β€” READ TRANSACTION (2 exchanges) ← one transaction β†’ ← exchange 1 (header) β†’ ← exchange 2 (data) β†’ SS SCLK MOSI MISO 0 1 0 1 1 0 0 1 0101 1001 = 0x59 | addr=0x16, R/W=0, stream=1 MOSI = 0x00 (master has nothing to send β€” unused) MISO = 0x00 (slave fetching data β€” sends zeros) 1 0 1 0 0 1 0 1 MISO = 1010 0101 = 0xA5 ← data from slave at addr 0x16

Single-byte read transaction. Exchange 1: master sends header 0x59 (addr=0x16, read, single). Exchange 2: master sends 0x00; slave returns 0xA5 from the requested register. SS stays LOW across both exchanges.

πŸ“€Write Transaction

A single-byte write transaction also uses two consecutive exchanges, but the roles of MOSI and MISO in exchange 2 are reversed:

  • Exchange 1 (Header): Master sends header byte with R/WΜ„=1 (write). Slave sends zeros.
  • Exchange 2 (Data): Master sends the data byte to write on MOSI. MISO is idle (zeros) throughout β€” there is nothing for the slave to send back.
READ transaction β€” MISO active in exchange 2
  • Header: R/WΜ„ = 0
  • Exchange 2 MOSI: zeros (unused)
  • Exchange 2 MISO: register data from slave
  • MOSI unused for one exchange
  • MISO unused for one exchange
WRITE transaction β€” MOSI active in exchange 2
  • Header: R/WΜ„ = 1
  • Exchange 2 MOSI: data to write to slave
  • Exchange 2 MISO: zeros (idle throughout)
  • MOSI active both exchanges
  • MISO unused entirely
Figure 3 β€” Single-byte SPI write transaction (CPOL=1, CPHA=1, MSB first)
CPOL=1, CPHA=1, MSB first β€” WRITE TRANSACTION (2 exchanges) ← one transaction β†’ ← exchange 1 (header) β†’ ← exchange 2 (data) β†’SS SCLK MOSI MISO 1 1 1 1 0 1 0 1 0xF5 | addr=0x3D, R/W=1 (write), stream=1 1 0 1 1 0 1 0 0 MOSI = 1011 0100 = 0xB4 ← data written to addr 0x3D MISO = 0x00 throughout β€” idle, slave has nothing to send in a write transaction

Single-byte write transaction. Exchange 1: master sends header 0xF5 (addr=0x3D, write, stream). Exchange 2: master sends data 0xB4 to be written; MISO stays idle (all zeros) for the entire transaction β€” there is no acknowledgement in SPI.

πŸ”‡Unused Wires & Don’t-Care Bits

A key feature β€” and potential confusion point β€” of SPI transactions is that not both data wires are used in every exchange:

Transaction typeExchange 1 (header)Exchange 2 (data)
ReadMOSI active (header)
MISO = zeros (slave has no data yet)
MISO active (read data)
MOSI = zeros (master has nothing to say)
WriteMOSI active (header)
MISO = zeros
MOSI active (write data)
MISO = zeros (idle throughout)
Why is MISO zeros during exchange 1 of a read? The slave receives the full 8-bit address only after the 8th clock cycle of exchange 1. It has no time to look up the data and place it on MISO before exchange 2 starts. This works because the peripheral’s internal clock is typically much faster than SCLK β€” it can access internal registers between the last bit of exchange 1 and the first bit of exchange 2.
No acknowledgement in SPI write transactions. Unlike IΒ²C, SPI has no ACK/NACK mechanism. After a write transaction, the master has no way to confirm whether the slave received and applied the data correctly. If your application needs acknowledgement, you must implement it at the software layer β€” for example, by issuing a read to a status register after every write.

⚑Performance Implications

Every 2-exchange transaction spends the entire first exchange transmitting address/control information β€” no data is moved during that time. For single-byte operations this is acceptable, but when reading or writing many consecutive bytes, the overhead is significant.

Efficiency of 2-exchange transactions:

  • Total exchanges: 2 per byte transferred
  • Useful data exchanges: 1 (50% efficiency)
  • For 16 bytes: 32 exchanges, 16 headers = 50% channel time wasted on headers

This is why peripherals that transfer blocks of data (accelerometer FIFO, display buffers, flash memory) all support multi-byte (burst/streaming) transactions.

🌊Multi-Byte Streaming Transactions

A streaming transaction allows the master to transfer multiple bytes in a single transaction by keeping SS LOW across multiple data exchanges. Two challenges must be solved:

Challenge 1 β€” Signalling a multi-byte operation

A reserved bit in the header flags the transaction as streaming. In the format we are using, bit 0 (STREAM) = 1 indicates a multi-byte transaction.

Challenge 2 β€” Specifying the byte count

Two common approaches:

MethodHow it worksTrade-off
SS durationNumber of data bytes = (total SS-low cycles βˆ’ 8) / 8. The master simply keeps SS LOW for as many extra cycles as bytes it wants.Simple, no extra exchange needed. Requires master to know the count before starting.
Length exchangeA second header exchange sends the byte count N. Then N data exchanges follow.More flexible β€” slave learns count before data starts. Costs one extra exchange per transaction.
Streaming transaction structure (length-exchange method):
Exchange 1: Header (addr + R/W + STREAM=1)
Exchange 2: Length N
Exchanges 3 through N+2: Data bytes (read on MISO or write on MOSI)

πŸ“₯πŸ“₯Multi-Byte Read Transaction

Figure 4 β€” Multi-byte read transaction (3 data bytes, length-exchange method)
CPOL=0, CPHA=1, MSB first β€” STREAMING READ (5 exchanges total) ← one streaming read transaction (SS held LOW across all 5 exchanges) β†’ E1: header E2: length N=3 E3: data byte 1 E4: data byte 2 E5: data byte 3 SS SCLK MOSI MISO 0x59 (addr=0x16, R=0, S=1) 0x03 (N = 3 bytes) MOSI = 0x00 (unused β€” master has nothing to send during data exchanges) header length zeros zeros (fetching) zeros 0xA5 (byte 1) 0xBF (byte 2) 0x4D (byte 3) zeros zeros 0xA5 0xBF 0x4D Output: RD STREAM 16 a5 bf 4d (address in hex, values in hex, no leading 0x)

Streaming read transaction: 5 exchanges total. Exchange 1 sends header (0x59 = addr 0x16, read, stream). Exchange 2 sends length (N=3). Exchanges 3–5: master sends zeros, slave returns three data bytes 0xA5, 0xBF, 0x4D on MISO. Output log: RD STREAM 16 a5 bf 4d.

πŸ“€πŸ“€Multi-Byte Write Transaction

Figure 5 β€” Multi-byte write transaction (2 data bytes, SS-duration method)
CPOL=1, CPHA=1, MSB first β€” STREAMING WRITE (4 exchanges, SS-duration method) ← one streaming write transaction β†’ E1: header E2: write data 1 E3: write data 2 E4: write data 3 SS SCLK MOSI MISO 0xF5 (addr=0x3D, W=1, S=1) 0x12 (write data 1) 0x34 (write data 2) 0x56 (write data 3) header 0x12 0x34 0x56 MISO = 0x00 throughout (idle β€” no acknowledgement in SPI) Master determines byte count by how long it holds SS LOW β€” (32 cycles βˆ’ 8) / 8 = 3 data bytes

Streaming write transaction (SS-duration method). Exchange 1: header 0xF5 (addr=0x3D, write, stream). Exchanges 2–4: three data bytes 0x12, 0x34, 0x56 sent on MOSI. MISO stays idle throughout. Output log: WR STREAM 3d 12 34 56.

πŸ” Worked Example β€” Decode a Complete Streaming Read Sequence

Given: CPOL=0, CPHA=1, MSB-first. You observe the following sequence of exchange values (from a logic analyser):

ExchangeMOSI (master→slave)MISO (slave→master)
10x590x00
20x030x00
30x000xA5
40x000xBF
50x000x4D

Step 1 β€” Decode header (exchange 1, MOSI=0x59=0101 1001):
Bits 7–2 = 010110 = 0x16 (address 22)
Bit 1 = 0 β†’ Read
Bit 0 = 1 β†’ Streaming

Step 2 β€” Read length (exchange 2, MOSI=0x03): N = 3 data bytes follow.

Step 3 β€” Collect data (exchanges 3–5, MISO): 0xA5, 0xBF, 0x4D

Output: RD STREAM 16 a5 bf 4d

This is exactly Transaction 1 from the textbook’s part-2 streaming exercise on page 17.

πŸ”¬VLSI Connections

πŸ”¬ SPI transactions in SoC register maps

In a real SoC, every peripheral (GPIO controller, UART, SPI itself, timers) is memory-mapped at a base address. When a CPU accesses address 0x4000_2000 to configure a GPIO pin, the underlying bus transaction β€” whether AXI, APB, or something else β€” carries exactly the same information as an SPI transaction: address, R/W direction, and data. SPI is simply a serialised, 4-wire version of the same programmed I/O model that runs on a parallel bus inside the chip. Understanding SPI transactions gives you a concrete mental model for how CPU-peripheral communication works at every level of abstraction.

πŸ”¬ Burst accesses and FIFO depth

Multi-byte streaming transactions map directly to FIFO-based peripheral design. An accelerometer (e.g. LIS3DH) stores X, Y, Z samples in an internal FIFO. A burst read fetches all available samples in a single transaction β€” one header exchange, then N data exchanges with SS held low. The N is bounded by FIFO depth. In RTL design, you must size the FIFO so it does not overflow between SPI transactions β€” this is a classic SPI-to-system-clock domain design problem involving asynchronous FIFOs (which you studied in the DE sequential circuits articles).

πŸ”¬ Protocol decoder as a verification component

The transaction decoder you are about to implement in SPI-06 is structurally identical to a SystemVerilog BFM (Bus Functional Model) used in RTL verification. A BFM watches SCLK, SS, MOSI, and MISO and reconstructs transactions for the scoreboard to compare against expected values. Writing the software decoder in SPI-06 gives you exactly the logic you would later implement as an SV class with a run() task that loops reading the clocking block inputs. The only difference is the language and the scheduling model.

Summary β€” SPI-04 key points: An SPI transaction is one or more consecutive exchanges forming a complete read or write at a specific address. The first exchange always carries a header byte (address + R/W + stream flags). Single-byte transactions use 2 exchanges; streaming transactions use 2+N exchanges. MISO is idle during write transactions; MOSI sends zeros in exchange 2 of a read. There is no acknowledgement β€” MISO remains zero throughout a write. Streaming transactions improve efficiency by amortising the header cost across multiple data bytes. The byte count is communicated either through a length exchange or by how long SS is held low.
Scroll to Top