A configurable, synthesizable SPI master in Verilog supporting all four SPI modes, full-duplex 8-bit transfers, and a self-checking testbench against a behavioral slave model.
Author: Avinash Kollu · GitHub: @avinashkollu-git
spi_master is a parameterized SPI master controller implementing full-duplex, MSB-first, 8-bit serial transfers with a single active-low chip select. It generates the serial clock (SCLK) from the system clock via a configurable divider, drives MOSI, and samples MISO on the correct edge for the selected SPI mode. A three-state FSM (IDLE → TRANSFER → FINISH) sequences each byte and exposes a simple busy/done handshake alongside the received byte on rx_byte.
All four SPI modes are supported through the CPOL and CPHA parameters, so the same RTL retargets to any standard SPI slave without modification.
- All 4 SPI modes selectable via
CPOLandCPHAparameters - Configurable clock divider:
CLK_DIVsets the SCLK period to2 * CLK_DIVsystem clocks - Full-duplex: simultaneous transmit on
MOSIand receive onMISO - MSB-first, 8-bit transfers
done/busyhandshake for clean integration with a host FSM or bus- Fully parameterized: no hard-coded mode or timing
| Mode | CPOL | CPHA | Sample edge |
|---|---|---|---|
| 0 | 0 | 0 | Sample on rising (leading) edge; SCLK idles low |
| 1 | 0 | 1 | Sample on falling (trailing) edge; SCLK idles low |
| 2 | 1 | 0 | Sample on falling (leading) edge; SCLK idles high |
| 3 | 1 | 1 | Sample on rising (trailing) edge; SCLK idles high |
spi_master
┌──────────────────────────────────────────────┐
│ │
tx_byte[7:0] ─►┌──────────────────┐ │
│ │ TX shift register├──► MOSI │──► MOSI
│ └──────────────────┘ │
│ │
MISO ─┼──────► ┌──────────────────┐ │
│ │ RX shift register├──► rx_byte[7:0] │──► rx_byte[7:0]
│ └──────────────────┘ │
│ │
CLK_DIV ─────► ┌──────────────────┐ │
│ │ SCLK generator ├──► SCLK │──► SCLK
│ └──────────────────┘ │
│ │
│ ┌──────────────────┐ │
│ │ FSM: IDLE/TRANSFER├──► cs_n control │──► cs_n (active low)
│ │ /FINISH ├──► busy, done │──► busy, done
│ └──────────────────┘ │
│ │
└──────────────────────────────────────────────┘
spi-master/
├── rtl/
│ └── spi_master.v # configurable SPI master RTL
├── tb/
│ ├── spi_slave_model.v # behavioral SPI slave for loopback
│ └── tb_spi_master.v # self-checking testbench (all four modes)
├── tools/
│ └── vcd2svg.py # VCD → SVG waveform renderer
├── docs/
│ └── spi_wave.svg # committed reference waveform
├── Makefile
├── LICENSE # MIT
└── README.md
Built and run on the open-source flow, Icarus Verilog for simulation, GTKWave for waveform inspection.
make test # run all four SPI modes, full-duplex, self-checking
make wave # regenerate docs/spi_wave.svg from the VCDThe testbench instantiates a spi_master + spi_slave_model pair for each of the four modes (CPOL/CPHA) and runs a full-duplex transfer, checking both directions:
| Mode | CPOL | CPHA | Master TX | Master RX (exp/got) | Slave RX (exp/got) | Result |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0xA5 | 0x3C / 0x3C | 0xA5 / 0xA5 | PASS |
| 1 | 0 | 1 | 0x5A | 0xC3 / 0xC3 | 0x5A / 0x5A | PASS |
| 2 | 1 | 0 | 0x0F | 0xF0 / 0xF0 | 0x0F / 0x0F | PASS |
| 3 | 1 | 1 | 0x99 | 0x66 / 0x66 | 0x99 / 0x99 | PASS |
RESULT: ALL TESTS PASSED
In this mode-0 capture, cs_n asserts to frame the transfer, SCLK toggles for eight cycles, and 0xA5 shifts out MSB-first on MOSI while 0x3C is captured on MISO.
- CPOL / CPHA edge semantics.
CPOLsets the idle level ofSCLK;CPHAselects which of the two clock edges captures data. Sampling and shifting always occur on opposite SCLK edges so that data is stable when latched. - CPHA first-edge MSB handling. For
CPHA = 0, the first data bit is presented before the leading edge and sampled on that leading edge. ForCPHA = 1, the MSB is presented on the first leading edge; a dedicated first-edge guard prevents the MSB from being dropped, which is a common source of off-by-one bugs in CPHA=1 implementations. - Clock divider.
CLK_DIVsets the SCLK period to2 * CLK_DIVsystem clocks, giving a symmetric, integer-divided serial clock and decoupling the design from any specific system-clock frequency.
- Protocol / interface design (SPI, all four modes)
- Finite state machine design (
IDLE/TRANSFER/FINISH) - Clock generation and integer clock division
- Full-duplex serial data path (parallel-in/serial-out and serial-in/parallel-out shift registers)
- Parameterized, reusable IP
- Self-checking verification against a reference slave model
Released under the MIT License.