SPI · Module 3
Mode 0 (CPOL=0, CPHA=0)
The most widely used SPI mode, and the one carrying a real implementation problem: why CPHA=0 forces the first bit onto the line before any clock edge, and the first-bit launch path in Verilog, SystemVerilog and VHDL.
Chapter 3.3 gave you a derivation that produces any mode's behaviour in three steps, and a decoder that implements all four in two multiplexers. It also flagged that the modes are not equally easy to build, and that the difficulty belongs to CPHA = 0.
This chapter takes the most common of the CPHA = 0 modes apart.
Why does Mode 0 require a transmitter to place a bit on the line before the transfer's first clock edge — and what does that cost?
Mode 0 is worth this attention for two reasons. It is the mode you will meet most often, so getting it exactly right pays repeatedly. And it contains the single piece of hardware that the mode bits genuinely add: every other consequence of CPOL and CPHA was two muxes in the decoder, but the first-bit launch is a new datapath with its own timing obligation.
1. Mode 0, Derived
Run Chapter 3.3's three steps rather than reciting a table.
Step 1 — where does the clock rest? Mode 0 is CPOL = 0, so SCLK idles low.
Step 2 — which direction is the leading edge? It takes the clock away from rest, so leading is rising and trailing is falling.
Step 3 — which logical edge samples? Mode 0 is CPHA = 0, so the leading edge samples and the trailing edge launches.
Composing: Mode 0 samples on rising edges and launches on falling edges, with the clock idling low. That is the familiar summary, and you have now produced it rather than remembered it.
2. The Transfer
Mode 0 — CS launches the first bit, rising edges sample
10 cyclesThe figure contains the whole chapter in one detail: at column 1, before any clock edge has occurred, MOSI already carries b2.
Everything else is routine. Column 2 is the first rising edge and samples b2. Column 3 is a falling edge and launches b1. Column 4 samples it. The pattern repeats, and the clock returns low before CS releases, satisfying the even-edge requirement from Chapter 3.1 §4.
But that first bit had no edge to launch it, and something had to put it there.
3. The First-Bit Problem, Precisely
State the difficulty exactly, because imprecision here is what produces the bug.
With CPHA = 0, the leading edge samples. The first leading edge of the transfer is therefore the first sampling edge — there is no earlier edge, and in particular there is no earlier launching edge. So by the time the first clock edge arrives, a valid bit must already be on the line and settled.
The event available to launch it is the assertion of chip select. Chapter 1.2 established CS as the signal that prepares a device to participate; under CPHA = 0 it acquires a second job as the launch event for bit one.
Three obligations follow, and a design that meets only the first is subtly broken.
The transmitter needs a second launch path. Its datapath cannot be "present the next bit on every launch strobe" — it must also present the first bit on CS assertion. That path does not exist under CPHA = 1, which is why supporting both phases costs more than supporting one.
The CS lead interval acquires a data requirement. Chapter 2.5 sized that interval for the device's internal preparation. Under CPHA = 0 it must additionally cover the transmitter's CS-to-output-valid delay — the parameter Chapter 2.6 §5 named — because the first bit must be stable, not merely driven, before the first edge samples it. A lead interval that is adequate for preparation but not for settling produces a corrupt first bit and nothing else.
Both ends have the problem, independently. The master launches the first MOSI bit from CS assertion, and the selected slave launches the first MISO bit from the same event. They are separate implementations with separate timing, and a link can have one right and the other wrong — which is why the failure sometimes appears in only one direction.
4. Building the Transmit Path — Three HDLs
This is the hardware the mode bits actually require. It is small, and its smallness is deceptive.
Circuit
A shift register with two ways to present its first bit. A registered copy of CS provides edge detection; on the cycle CS falls, the transmit word is loaded. Under CPHA = 0 the most significant bit becomes live immediately; under CPHA = 1 it becomes live on the first launch strobe instead. Every subsequent launch strobe shifts.
State
Three pieces. shreg, the shift register holding the word being transmitted. sdo_live, a single flag recording whether the output currently carries a valid bit — this is the flag that encodes the phase difference. And cs_n_q, a registered copy of chip select used purely to detect its falling edge.
sdo_live is the interesting one. It exists because "has the first bit been launched yet?" is genuinely a piece of state, and under CPHA = 1 it is set by a different event than under CPHA = 0. Without it, the two phases would need separate datapaths.
Combinational logic
One multiplexer selecting the register's top bit or an idle level, and the CS falling-edge term. That is all.
Clock
The system clock, with the launch strobe from Chapter 3.3's decoder acting as an enable. Note carefully that the module is not clocked by SCLK — the separation Chapter 2.1 argued for is preserved throughout.
Enables
launch_stb gates shifting. cs_n gates everything: while deselected the register stays preloaded and the output is not live, so a spurious strobe cannot advance the transfer.
Reset
Asynchronous, active-low, clearing the register and marking the output not live. Not-live is the safe state — a transmitter asserting stale data before a transfer begins is exactly what Chapter 1.2 warned about on a shared bus.
Timing
Under CPHA = 0, the first bit is valid from the clock edge on which CS was observed low — so its settling time against the first SCLK edge is the CS lead interval minus this module's own output delay. Under CPHA = 1, the first bit is valid from the first launch strobe and has a normal half period, like every other bit.
Synthesis
WIDTH flip-flops for the shift register, two more for the flag and the CS copy, a multiplexer, and a little control. Negligible.
Limitation
This is the transmit path only. There is no receive capture (Chapter 1.3's core does that), no bit counter, no transfer-complete signal, no CS sequencing (Chapter 2.5 built that separately), and no output enable for a slave's MISO (Chapter 1.2 built that). It also assumes cs_n is already synchronous to clk — true for a master, which generates it, and emphatically not true for a slave, which receives it asynchronously. That is Module 15's problem and a genuine reason slave implementations are harder.
module spi_tx_path #(
parameter int WIDTH = 8 // >= 2
) (
input logic clk,
input logic rst_n, // asynchronous, active-low
input logic cs_n, // ASSUMED synchronous to clk here
input logic cpha, // 0: CS assertion launches bit one
input logic launch_stb, // from the mode decoder (Ch. 3.3)
input logic [WIDTH-1:0] tx_data, // word to send, MSB first
output logic sdo, // MOSI (master) or MISO (slave)
output logic sdo_live // is sdo carrying a real bit?
);
logic [WIDTH-1:0] shreg;
logic live;
logic cs_n_q;
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
shreg <= '0;
live <= 1'b0;
cs_n_q <= 1'b1;
end else begin
cs_n_q <= cs_n;
if (cs_n) begin
// Deselected: stay preloaded and DO NOT present a bit.
shreg <= tx_data;
live <= 1'b0;
end else if (cs_n_q) begin
// This cycle CS fell — the selecting edge.
shreg <= tx_data;
// CPHA=0: bit one is live RIGHT NOW, with no clock edge.
// CPHA=1: it waits for the first launch strobe below.
live <= ~cpha;
end else if (launch_stb) begin
if (!live) begin
live <= 1'b1; // CPHA=1: first launch makes it live
end else begin
shreg <= {shreg[WIDTH-2:0], 1'b0}; // advance to next bit
end
end
end
end
assign sdo = live ? shreg[WIDTH-1] : 1'b0; // idle level while not live
assign sdo_live = live;
endmoduleThe assertion worth binding is the one that catches the omission this chapter exists to prevent:
// If a transmitter forgets the CS launch path, `live` is still low when the
// first sampling edge arrives and this fires. Without it the symptom is a
// single wrong bit that looks like half a dozen other faults.
property p_first_bit_ready;
@(posedge clk) disable iff (!rst_n)
(!cpha && !cs_n && sample_stb) |-> sdo_live;
endproperty
a_first_bit_ready : assert property (p_first_bit_ready)
else $error("CPHA=0: sampling edge with no bit launched");What it proves. That whenever a sampling edge occurs during a CPHA = 0 transfer, the transmitter believes it is presenting a valid bit. It catches a missing CS launch path directly, at the first sampling edge of the very first transfer.
What it does not prove. That the bit had settled at the receiving device's pin — that is the CS-lead-versus-output-valid question of §3, which lives in analogue time and is checked by static timing analysis and measurement, not by a cycle-based property. Nor does it prove the bit is the right bit; that is the scoreboard's job.
The testbench exercises the phase difference directly: it runs the same word through the same hardware under both cpha values and checks not only the bit sequence but when the first bit became live — which is the only thing that differs.
module spi_tx_path_tb;
localparam int WIDTH = 8;
localparam logic [WIDTH-1:0] WORD = 8'hA5; // 1010_0101
logic clk = 1'b0, rst_n, cs_n, cpha, launch_stb;
logic sdo, sdo_live;
int errors = 0;
spi_tx_path #(.WIDTH(WIDTH)) dut (
.clk(clk), .rst_n(rst_n), .cs_n(cs_n), .cpha(cpha),
.launch_stb(launch_stb), .tx_data(WORD),
.sdo(sdo), .sdo_live(sdo_live));
always #5 clk = ~clk;
initial begin // watchdog
#20_000; $display("FAIL: timeout"); $finish;
end
// Drive one whole word and check every bit, MSB first.
task automatic run_word(input bit phase);
cpha = phase;
rst_n = 1'b0; cs_n = 1'b1; launch_stb = 1'b0;
@(posedge clk); #1;
if (sdo_live !== 1'b0) begin
$error("cpha=%b: live asserted while deselected", phase); errors++;
end
rst_n = 1'b1;
@(posedge clk); #1;
// Assert CS. This is the launch event for CPHA=0.
cs_n = 1'b0;
@(posedge clk); #1;
if (phase == 1'b0) begin
// CPHA=0: bit one must be live NOW, with no clock edge yet.
if (sdo_live !== 1'b1) begin
$error("cpha=0: CS did not launch the first bit"); errors++;
end
if (sdo !== WORD[WIDTH-1]) begin
$error("cpha=0: first bit is %b, expected %b", sdo, WORD[WIDTH-1]);
errors++;
end
end else begin
// CPHA=1: nothing live until the first launch strobe.
if (sdo_live !== 1'b0) begin
$error("cpha=1: bit launched by CS, should wait for an edge");
errors++;
end
launch_stb = 1'b1; @(posedge clk); #1; launch_stb = 1'b0;
if (sdo_live !== 1'b1) begin
$error("cpha=1: first launch strobe did not make sdo live"); errors++;
end
if (sdo !== WORD[WIDTH-1]) begin
$error("cpha=1: first bit is %b, expected %b", sdo, WORD[WIDTH-1]);
errors++;
end
end
// Remaining bits: one launch strobe each.
for (int i = 1; i < WIDTH; i++) begin
launch_stb = 1'b1; @(posedge clk); #1; launch_stb = 1'b0;
if (sdo !== WORD[WIDTH-1-i]) begin
$error("cpha=%b bit %0d: sdo=%b expected %b",
phase, i, sdo, WORD[WIDTH-1-i]);
errors++;
end
end
// Deselect must drop live so a stale bit is never presented.
cs_n = 1'b1;
@(posedge clk); #1;
if (sdo_live !== 1'b0) begin
$error("cpha=%b: live still asserted after deselect", phase); errors++;
end
endtask
initial begin
run_word(1'b0);
run_word(1'b1);
if (errors == 0)
$display("PASS: %h sent MSB-first in both phases; CS launches only when cpha=0",
WORD);
else
$display("FAIL: %0d errors", errors);
$finish;
end
endmodule module spi_tx_path #(
parameter WIDTH = 8
) (
input clk,
input rst_n,
input cs_n,
input cpha,
input launch_stb,
input [WIDTH-1:0] tx_data,
output sdo,
output sdo_live
);
reg [WIDTH-1:0] shreg;
reg live;
reg cs_n_q;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
shreg <= {WIDTH{1'b0}};
live <= 1'b0;
cs_n_q <= 1'b1;
end else begin
cs_n_q <= cs_n;
if (cs_n) begin
shreg <= tx_data;
live <= 1'b0;
end else if (cs_n_q) begin
shreg <= tx_data;
live <= ~cpha; // CPHA=0 launches here
end else if (launch_stb) begin
if (!live) begin
live <= 1'b1; // CPHA=1 launches here
end else begin
shreg <= {shreg[WIDTH-2:0], 1'b0};
end
end
end
end
assign sdo = live ? shreg[WIDTH-1] : 1'b0;
assign sdo_live = live;
endmodule module spi_tx_path_tb;
parameter WIDTH = 8;
localparam [WIDTH-1:0] WORD = 8'hA5;
reg clk = 1'b0;
reg rst_n, cs_n, cpha, launch_stb;
wire sdo, sdo_live;
integer errors = 0, i;
spi_tx_path #(.WIDTH(WIDTH)) dut (
.clk(clk), .rst_n(rst_n), .cs_n(cs_n), .cpha(cpha),
.launch_stb(launch_stb), .tx_data(WORD),
.sdo(sdo), .sdo_live(sdo_live));
always #5 clk = ~clk;
initial begin
#20000; $display("FAIL: timeout"); $finish;
end
task run_word;
input phase;
begin
cpha = phase;
rst_n = 1'b0; cs_n = 1'b1; launch_stb = 1'b0;
@(posedge clk); #1;
if (sdo_live !== 1'b0) begin
$display("ERROR cpha=%b live asserted while deselected", phase);
errors = errors + 1;
end
rst_n = 1'b1;
@(posedge clk); #1;
cs_n = 1'b0;
@(posedge clk); #1;
if (phase == 1'b0) begin
if (sdo_live !== 1'b1) begin
$display("ERROR cpha=0 CS did not launch the first bit");
errors = errors + 1;
end
if (sdo !== WORD[WIDTH-1]) begin
$display("ERROR cpha=0 first bit %b expected %b",
sdo, WORD[WIDTH-1]);
errors = errors + 1;
end
end else begin
if (sdo_live !== 1'b0) begin
$display("ERROR cpha=1 bit launched by CS");
errors = errors + 1;
end
launch_stb = 1'b1; @(posedge clk); #1; launch_stb = 1'b0;
if (sdo_live !== 1'b1) begin
$display("ERROR cpha=1 first launch did not make sdo live");
errors = errors + 1;
end
if (sdo !== WORD[WIDTH-1]) begin
$display("ERROR cpha=1 first bit %b expected %b",
sdo, WORD[WIDTH-1]);
errors = errors + 1;
end
end
for (i = 1; i < WIDTH; i = i + 1) begin
launch_stb = 1'b1; @(posedge clk); #1; launch_stb = 1'b0;
if (sdo !== WORD[WIDTH-1-i]) begin
$display("ERROR cpha=%b bit %0d sdo=%b expected %b",
phase, i, sdo, WORD[WIDTH-1-i]);
errors = errors + 1;
end
end
cs_n = 1'b1;
@(posedge clk); #1;
if (sdo_live !== 1'b0) begin
$display("ERROR cpha=%b live still asserted after deselect", phase);
errors = errors + 1;
end
end
endtask
initial begin
run_word(1'b0);
run_word(1'b1);
if (errors == 0)
$display("PASS: %h sent MSB-first in both phases; CS launches only when cpha=0",
WORD);
else
$display("FAIL: %0d errors", errors);
$finish;
end
endmodule library ieee;
use ieee.std_logic_1164.all;
entity spi_tx_path is
generic (
WIDTH : positive := 8 -- >= 2
);
port (
clk : in std_logic;
rst_n : in std_logic; -- asynchronous, active-low
cs_n : in std_logic; -- ASSUMED synchronous to clk here
cpha : in std_logic; -- '0': CS assertion launches bit one
launch_stb : in std_logic; -- from the mode decoder
tx_data : in std_logic_vector(WIDTH-1 downto 0);
sdo : out std_logic;
sdo_live : out std_logic
);
end entity spi_tx_path;
architecture rtl of spi_tx_path is
signal shreg : std_logic_vector(WIDTH-1 downto 0) := (others => '0');
signal live : std_logic := '0';
signal cs_n_q : std_logic := '1';
begin
tx_proc : process (clk, rst_n)
begin
if rst_n = '0' then
shreg <= (others => '0');
live <= '0';
cs_n_q <= '1';
elsif rising_edge(clk) then
cs_n_q <= cs_n;
if cs_n = '1' then
-- Deselected: stay preloaded, present nothing.
shreg <= tx_data;
live <= '0';
elsif cs_n_q = '1' then
-- This cycle CS fell — the selecting edge.
shreg <= tx_data;
live <= not cpha; -- CPHA='0': live immediately
elsif launch_stb = '1' then
if live = '0' then
live <= '1'; -- CPHA='1': first launch makes it live
else
shreg <= shreg(WIDTH-2 downto 0) & '0';
end if;
end if;
end if;
end process tx_proc;
sdo <= shreg(WIDTH-1) when live = '1' else '0';
sdo_live <= live;
end architecture rtl; library ieee;
use ieee.std_logic_1164.all;
entity spi_tx_path_tb is
end entity spi_tx_path_tb;
architecture sim of spi_tx_path_tb is
constant WIDTH : positive := 8;
constant WORD : std_logic_vector(WIDTH-1 downto 0) := "10100101"; -- 0xA5
constant TP : time := 10 ns;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
signal cs_n : std_logic := '1';
signal cpha : std_logic := '0';
signal launch_stb : std_logic := '0';
signal sdo : std_logic;
signal sdo_live : std_logic;
signal done : boolean := false;
begin
clk <= not clk after TP/2 when not done else '0';
dut : entity work.spi_tx_path
generic map (WIDTH => WIDTH)
port map (clk => clk, rst_n => rst_n, cs_n => cs_n, cpha => cpha,
launch_stb => launch_stb, tx_data => WORD,
sdo => sdo, sdo_live => sdo_live);
stim : process
variable errors : natural := 0;
procedure run_word (phase : in std_logic) is
begin
cpha <= phase;
rst_n <= '0';
cs_n <= '1';
launch_stb <= '0';
wait until rising_edge(clk); wait for 1 ns;
if sdo_live /= '0' then
report "live asserted while deselected" severity error;
errors := errors + 1;
end if;
rst_n <= '1';
wait until rising_edge(clk); wait for 1 ns;
cs_n <= '0';
wait until rising_edge(clk); wait for 1 ns;
if phase = '0' then
if sdo_live /= '1' then
report "cpha=0: CS did not launch the first bit" severity error;
errors := errors + 1;
end if;
if sdo /= WORD(WIDTH-1) then
report "cpha=0: wrong first bit" severity error;
errors := errors + 1;
end if;
else
if sdo_live /= '0' then
report "cpha=1: bit launched by CS" severity error;
errors := errors + 1;
end if;
launch_stb <= '1';
wait until rising_edge(clk); wait for 1 ns;
launch_stb <= '0';
if sdo_live /= '1' then
report "cpha=1: first launch did not make sdo live" severity error;
errors := errors + 1;
end if;
if sdo /= WORD(WIDTH-1) then
report "cpha=1: wrong first bit" severity error;
errors := errors + 1;
end if;
end if;
for i in 1 to WIDTH-1 loop
launch_stb <= '1';
wait until rising_edge(clk); wait for 1 ns;
launch_stb <= '0';
if sdo /= WORD(WIDTH-1-i) then
report "wrong bit at index " & integer'image(i) severity error;
errors := errors + 1;
end if;
end loop;
cs_n <= '1';
wait until rising_edge(clk); wait for 1 ns;
if sdo_live /= '0' then
report "live still asserted after deselect" severity error;
errors := errors + 1;
end if;
end procedure run_word;
begin
run_word('0');
run_word('1');
if errors = 0 then
report "PASS: word sent MSB-first in both phases; CS launches only when cpha=0"
severity note;
else
report "FAIL" severity error;
end if;
done <= true;
wait;
end process stim;
end architecture sim;What the three agree on, and where they differ
All three infer the same circuit: a WIDTH-bit shift register, a live flag, a registered CS for edge detection, an asynchronous active-low reset, and an output multiplexer. The phase behaviour is identical — CPHA = 0 sets the flag on the CS falling edge, CPHA = 1 sets it on the first launch strobe — and both present the same MSB-first sequence thereafter.
The notable difference is in how the phase decision is written. SystemVerilog and Verilog use ~cpha and not cpha on a single-bit value interchangeably; VHDL's not cpha on a std_logic is the same operation but the surrounding comparisons must be explicit (cs_n = '1' rather than a bare cs_n), which is the strictness that prevents a whole class of accidental truncation elsewhere. The VHDL testbench again uses a procedure inside the process, giving it direct access to the shared errors variable without passing it in and out.
5. Why a Verification Engineer Cares
Mode 0 changes a monitor in one specific way, and it introduces a coverage hole that is easy to leave open.
The first bit comes from a different event. A monitor that collects MOSI and MISO on sampling edges works for every bit including the first under CPHA = 0, because the first sampling edge does capture bit one. That much is fine. What changes is the checker: a monitor should verify that the line was already stable before that first edge rather than transitioning through it, and the assertion in §4 is the cheap version of that check.
The bug is once per transaction, so it hides in aggregate coverage. A missing first-bit path corrupts one bit in eight — or one in sixty-four on a long burst. A scoreboard comparing whole words will catch it, but a coverage model reporting "all four modes exercised, thousands of transfers" gives no hint that the failing path is proportionally rarer in the tests that ran most. This is exactly why Chapter 3.3 §7 crossed mode with transfer length: short transfers weight the first bit most heavily, and a suite of long bursts can pass while barely testing it.
Test the transmit side specifically. The first-bit launch is a transmit obligation, so a test suite that only reads from devices exercises the slave's first-bit path and never the master's. Crossing mode with direction closes that gap.
6. Why an FPGA or ASIC Engineer Cares
As a master, the path is easy but must exist. The master generates CS, so the falling edge is a synchronous event in its own clock domain and the first-bit launch is an ordinary enable term — exactly what §4's RTL shows. The only real obligation is ensuring the CS lead interval is long enough to cover the module's own output delay so the bit settles before the first edge.
As a slave, this is one of the hardest paths in the design. The slave receives CS as an asynchronous input, so "launch the first bit on CS assertion" becomes a path from an asynchronous pin to an output — with no clock edge available to register it against, because under CPHA = 0 the first clock edge is the one that samples it. Synchronising CS to a system clock costs latency that eats directly into the master's CS lead interval; using CS combinationally is fast but is an unclocked path that static timing analysis will treat as unconstrained unless you tell it otherwise. There is no free option, and choosing between them is an architecture decision. Module 15 covers it properly; this chapter's job is to make clear why the decision exists.
It is a genuine reason to prefer CPHA = 1 when you can. Chapter 3.2 §10 framed that as a scoping decision; this is the concrete engineering content behind it.
7. Failure Signature — The First Bit Is Wrong, Every Other Bit Is Right
Symptom. Received words differ from expected in exactly the most significant bit. Every other bit is correct. Fully reproducible, at every clock rate, on every transfer.
Plausible mechanisms. A missing first-bit launch path is the leading candidate — the transmitter waits for a launch strobe that, for bit one, arrives after the sample. Competing explanations are a CS lead interval too short for the transmitter's CS-to-output-valid delay, so the bit is launched but not settled; and a receiver that starts its bit counter one position early.
The discriminating observations. First, exactly one bit or the whole word? A mode mismatch displaces every bit (Chapter 3.8); a first-bit fault leaves bits two onward correct. This is visible in the data alone.
Second, is the configuration CPHA = 0? Under CPHA = 1 the special case does not exist and this mechanism is eliminated outright.
Third, scope the line between CS assertion and the first clock edge. Undefined or still transitioning means the bit was never launched, or launched too late. Settled and correct means the transmitter is fine and the receiver is at fault.
Constant versus intermittent separates the two remaining causes. A missing launch path is structural and fails identically every time. A lead interval marginal against the output-valid delay produces an intermittent first bit that varies with temperature and part. The same symptom, two mechanisms, one cheap discriminator — and the fixes are entirely different: add a datapath, or lengthen an interval.
8. Common Misconceptions
9. Reason It Through
Work this before reading the answers.
An FPGA acts as an SPI slave in Mode 0. A host reads a 4-byte register block. Bytes 2, 3 and 4 are always correct. Byte 1 is correct in its lower seven bits and wrong in its most significant bit — and only when the host issues reads back to back. Single reads, issued from a debugger, are always perfect.
What does the "only back to back" qualifier rule in? It points at something that differs between an isolated transaction and a repeated one — and Chapter 2.5 established there is exactly one such thing: the interval between transactions, spanning CS deassertion, the deselect time, and the next CS assertion plus its lead interval. A debugger's transactions are separated by milliseconds; a driver's by whatever the controller does between them.
Why is it the most significant bit of byte 1 specifically? Because that is the bit launched by CS assertion under CPHA = 0. Every other bit in the whole 4-byte burst is launched by a clock edge and has a full half period to settle. Bit one of byte one is the only bit whose settling window is the CS lead interval — so it is the only bit that a short lead interval can corrupt.
So which two mechanisms remain, and how do they differ? Either the slave's first-bit path does not exist at all, or it exists and is too slow. The first is eliminated immediately: if the path were missing, single reads from the debugger would fail too, and they do not. So the path exists and the CS lead interval is marginal against the slave's CS-to-output-valid delay — which back-to-back operation shortens, either because the controller's inter-transaction gap is smaller or because the slave has not fully returned to idle from the previous transaction.
What would you measure? CS and MISO at the slave's pins, across two consecutive transactions. Measure the interval from CS falling to the first SCLK edge, and see whether MISO has settled within it. Compare the back-to-back case against the debugger-issued case — the difference in that interval is the whole story.
What are the fixes, in order of preference? Lengthen the master's CS lead interval, which is a configuration change on the host and costs a little latency per transaction. Reduce the slave's CS-to-output-valid delay by shortening its path from the CS pin to the MISO output — an I/O register and a more direct launch path. Or, if the slave's design allows and the host can be configured for it, move to CPHA = 1, which removes the special case entirely and gives bit one the same half period every other bit gets.
Why is "add a delay in the host driver between transactions" the wrong instinct? It works, it is invisible in review, it costs throughput on every transfer, and it will be removed by someone optimising the driver later — at which point the fault returns with no hardware having changed. The same trap Chapter 2.5 §10 identified for the deselect interval, and the same answer: enforce the requirement in hardware, parameterised from the slave's specification.
10. Understanding Check
11. Summary
Mode 0 is CPOL = 0, CPHA = 0: clock idles low, leading edge is rising, leading edge samples. So it samples on rising and launches on falling — derived in three steps rather than recalled.
Its significance is not the numbering but the implementation consequence. Because the leading edge samples, the transfer's first clock edge is its first sampling edge, and a valid bit must already be on the line when it arrives. The only event available to launch it is the assertion of chip select, which gives CPHA = 0 a genuine special case in the transmit datapath — the one piece of hardware the mode bits actually add, everything else having been two muxes in Chapter 3.3's decoder.
Three obligations follow: a second launch path in the transmitter; a CS lead interval long enough to cover the transmitter's CS-to-output-valid delay so the bit is settled rather than merely driven; and the fact that both ends have the problem independently, so a link can have one right and the other wrong.
In RTL the answer is a shift register plus a live flag recording whether the output yet carries a real bit — set by the CS falling edge under CPHA = 0, by the first launch strobe under CPHA = 1. One datapath, both phases, and the flag is exactly where the phase difference lives.
The failure signature is distinctive: one wrong bit, always the most significant, every other bit correct. Reproducible means a missing launch path; intermittent means a marginal lead interval. And the bug hides in regression because it corrupts one bit per transaction, so short transfers and transmit-side traffic are what actually exercise it.
12. What Comes Next
Chapter 3.5 — Mode 1 keeps the same clock polarity and flips the phase. That single change removes everything this chapter spent its length on: no CS launch, no extra datapath, no data requirement on the lead interval. Seeing the same transfer without the special case is the clearest way to understand what the special case cost — and it is why CPHA = 1 is the easier half of the mode space to implement.
Browse the path on the SPI curriculum index, or revisit Deriving Mode Behaviour for the method that produced Mode 0's behaviour, or CS-to-SCLK and SCLK-to-CS Timing for the interval this chapter added a requirement to.
Continue learning
Related tutorials
- Related topic
MOSI Data Flow and CS Framing
What the master drives on MOSI through every region of a transfer, what the two chip-select edges bracket, and why an asynchronous CS must cross into the slave's clock domain before any edge is derived from it.
- Related topic
Bus Topologies and Daisy-Chain
What the four-wire model costs as devices are added: shared clock and data with one select per device, or a daisy chain that turns several peripherals into one long shift ring. Pin arithmetic, ownership consequences, and why chaining is a device property.
- Related topic
CS-to-SCLK and SCLK-to-CS Timing
Chip select has timing requirements of its own: the lead before the first clock edge, the lag after the last, and the minimum deselect between transactions. Why violating them breaks a transfer whose every SCLK edge was correct.
- Related topic
Bit Ordering — MSB-First and LSB-First
Which end of the shift register goes out first, the two multiplexers that make the order configurable in three HDLs, and why a bit-order bug is perfectly deterministic and yet invisible on certain data.
