An SPI master generates the clock, shifts a byte out on MOSI while shifting a byte in from MISO, and controls chip select. Supporting all four SPI modes takes only a few lines more than supporting one, if the design is built around leading and trailing edges instead of rising and falling ones. This article builds such a master and looks at what limits its clock speed.
8 min read | Intermediate
What You'll Learn
- Generate SCLK from a system clock with a divider
- Shift data out and in on the correct edges for any mode
- Control chip select across multi-byte transactions
- Understand the timing margins between master and slave
The Plan
- A divider counts
CLK_DIVsystem clocks for every half period of SCLK. Each time it expires, SCLK toggles: that is one edge. - A byte takes 16 edges. Even-numbered edges (0, 2, ...) are leading edges and odd-numbered edges are trailing edges, whatever CPOL is.
- For CPHA = 0: put the first bit on MOSI at the start, sample MISO on leading edges, change MOSI on trailing edges. For CPHA = 1: change MOSI on leading edges, sample on trailing edges.
- Chip select is controlled separately with
cs_req, so that several bytes can be sent in one transaction.
The RTL
// SPI master: one byte per transfer, MSB first, any of the four modes.
// CLK_DIV system clocks per half SCLK period
// CPOL SCLK level when idle
// CPHA 0: sample on the leading edge, change on the trailing edge
// 1: change on the leading edge, sample on the trailing edge
// cs_req holds the chip select active across several bytes.
module spi_master #(
parameter CLK_DIV = 4,
parameter CPOL = 0,
parameter CPHA = 0
) (
input clk,
input rst,
input start, // begin a byte transfer (when not busy)
input [7:0] tx_byte,
input cs_req, // 1: keep chip select active
output reg busy,
output reg done, // one-cycle pulse: rx_byte is valid
output reg [7:0] rx_byte,
output reg sclk,
output reg mosi,
input miso,
output cs_n
);
reg [15:0] div;
reg [4:0] edges; // SCLK edges so far in this byte (16 per byte)
reg [7:0] tx_sh;
reg [7:0] rx_sh;
assign cs_n = !cs_req;
always @(posedge clk) begin
if (rst) begin
busy <= 1'b0;
done <= 1'b0;
sclk <= CPOL;
mosi <= 1'b0;
end
else begin
done <= 1'b0;
if (!busy) begin
sclk <= CPOL;
if (start) begin
busy <= 1'b1;
div <= 16'd0;
edges <= 5'd0;
tx_sh <= tx_byte;
if (CPHA == 0)
mosi <= tx_byte[7]; // the first bit must be ready before the first edge
end
end
else if (div == CLK_DIV - 1) begin
div <= 16'd0;
if (edges == 5'd16) begin // all 16 edges done
busy <= 1'b0;
done <= 1'b1;
rx_byte <= rx_sh;
end
else begin
sclk <= ~sclk;
edges <= edges + 5'd1;
if (edges[0] == 1'b0) begin // leading edge
if (CPHA == 0) begin
rx_sh <= {rx_sh[6:0], miso}; // sample
end
else begin
mosi <= tx_sh[7]; // change
tx_sh <= {tx_sh[6:0], 1'b0};
end
end
else begin // trailing edge
if (CPHA == 0) begin
mosi <= tx_sh[6]; // change: the next bit
tx_sh <= {tx_sh[6:0], 1'b0};
end
else begin
rx_sh <= {rx_sh[6:0], miso}; // sample
end
end
end
end
else begin
div <= div + 16'd1;
end
end
end
endmodule
SCLK idles at CPOL while the master is not busy. Shift registers shift left because SPI sends the most significant bit first. done pulses half a clock after the sixteenth edge, when rx_byte holds the byte that came in.
Quick Check
Q: The system clock is 100 MHz and CLK_DIV is 4. What is the SCLK frequency, and how long does one byte take?
Show Answer
Each half period is 4 cycles of 10 ns, so SCLK has an 80 ns period: 12.5 MHz. A byte is 8 periods, 640 ns, plus the half period before done.
Driving It
The testbench wraps the byte interface in a task and holds chip select over a two-byte transaction:
task xfer(input [7:0] b, output [7:0] got);
begin
@(posedge clk);
tx_byte <= b;
start <= 1;
@(posedge clk);
start <= 0;
@(posedge done);
got = rx_byte;
end
endtask
task access(input rw, input [1:0] addr, input [7:0] wdata);
reg [7:0] id;
reg [7:0] rdata;
begin
cs_req <= 1;
repeat (4) @(posedge clk);
xfer({rw, 5'b0, addr}, id);
xfer(rw ? wdata : 8'h00, rdata);
repeat (2) @(posedge clk);
cs_req <= 0;
// ...
end
endtask
SPI mode 0 (CPOL=0, CPHA=0) 1535 write reg1 = 0x3c (slave ID 0xa5) 3055 write reg2 = 0xc3 (slave ID 0xa5) 4575 read reg1 = 0x3c (slave ID 0xa5) 6095 read reg2 = 0xc3 (slave ID 0xa5)
The few clock cycles between chip select and the first edge, and after the last edge, give the slave setup and hold time on CS_n. Real devices specify these in their datasheets, often a few nanoseconds to tens of nanoseconds.
What Limits the Clock
The master samples MISO half an SCLK period after the slave changed it. In that half period the slave's output must change, travel across the board and arrive at the master in time. On a PCB with a fast slave that allows tens of MHz. Inside a chip, a slave that oversamples SCLK with its own clock adds a synchronizer delay of two or three of its clock cycles, which must fit in the half period too.
Quick Check
Q: Why does the master sample MISO in the same system clock cycle in which it toggles SCLK, rather than a cycle later?
Show Answer
The sample is taken from MISO as it is just before the edge, which is the value the slave set up half a period earlier. Sampling later would risk seeing the slave's next bit if it reacts quickly to the edge.
Running It in ChipVerify Lab
The example contains this master and the register slave. Change CLK_DIV and the mode, and watch the timing between SCLK edges and MISO changes.
Common Mistakes
Mistake #1: Coding rising and falling instead of leading and trailing
A master written for mode 0 with "sample on rising edge" breaks in mode 2, where the leading edge is falling. Decide in terms of leading and trailing edges and let CPOL pick the direction.
Mistake #2: A glitch on SCLK when changing mode
Changing CPOL while chip select is active creates an extra edge. Change the mode only while idle with chip select high.
Mistake #3: Dropping chip select too early
Raising CS_n in the same cycle as the last edge violates the slave's hold time. Leave a little time after the last edge.
Practice Exercise
Challenge: Twice as Fast
Halve CLK_DIV from 4 to 2 so that SCLK runs at 25 MHz. The slave samples SCLK with the same 100 MHz clock through a two-flip-flop synchronizer. Does it still work?
Hint
Count the system clock cycles from an SCLK edge until the slave reacts, and compare with the 2 cycles until the master's next edge.
Solution
SPI mode 0 (CPOL=0, CPHA=0) 855 write reg1 = 0x3c (slave ID 0xd2) 1695 write reg2 = 0xc3 (slave ID 0xd2) 2535 read reg1 = 0x1e (slave ID 0xd2) 3375 read reg2 = 0xe1 (slave ID 0xd2)
It fails: the ID arrives as 0xD2 and the reads return wrong values. The slave needs about three system clocks to see an edge and update MISO, longer than the two-cycle half period. An oversampling SPI slave needs a system clock several times faster than SCLK; slaves that must run faster clock their shift register directly from SCLK.
Open the ready-made example in the lab and run the simulation.
Summary
- A divider toggles SCLK every
CLK_DIVsystem clocks; 16 edges make a byte. - Even edges are leading, odd edges trailing; CPHA picks which one samples and which one changes data.
- For CPHA = 0, the first bit is on MOSI before the first edge.
- Chip select is held across all bytes of a transaction, with setup and hold time around the clocks.
- The half period must cover the slave's reaction time; oversampling slaves need a much faster clock.