SPI · Module 3
Deriving Mode Behaviour from CPOL and CPHA
The four SPI modes are a two-bit truth table you can rebuild in seconds. The standard numbering, the derivation, the complete mode decoder in three HDLs, and the assertions that keep a configurable design honest.
Chapter 3.1 named the bit that fixes the idle level. Chapter 3.2 named the bit that assigns the sampling role, and argued that the two are genuinely independent.
Two independent bits give four combinations. Those four combinations are the SPI modes, and this chapter is where they stop being a table to memorise.
Given CPOL and CPHA, how do you work out what the waveform does — without looking anything up?
The derivation takes about ten seconds once you have the method, and the method is worth far more than the table. Engineers who memorise "Mode 0 samples on rising" reconstruct it wrongly under pressure, misapply it to a device whose datasheet uses different words, and have nothing to fall back on when they meet a part that describes its timing in a way the table does not cover. Engineers who derive it are never stuck.
1. The Standard Numbering
The four combinations are conventionally numbered 0 to 3, with CPOL as the high bit and CPHA as the low bit:
mode = (CPOL << 1) | CPHA| Mode | CPOL | CPHA | Clock idles | Leading edge | Sample on | Launch on |
|---|---|---|---|---|---|---|
| 0 | 0 | 0 | low | rising | leading (rising) | trailing (falling) |
| 1 | 0 | 1 | low | rising | trailing (falling) | leading (rising) |
| 2 | 1 | 0 | high | falling | leading (falling) | trailing (rising) |
| 3 | 1 | 1 | high | falling | trailing (rising) | leading (falling) |
Do not memorise this table. Derive it — §2 is the method, and the table is here so you can check yourself.
Two remarks on the numbering itself, because both cause real confusion.
The numbering is a convention, not part of the signalling. Nothing on the wire encodes a mode number. It is a shorthand for a pair of configuration bits, and a device is free to document its requirement as "Mode 3", as "CPOL = 1, CPHA = 1", or as a timing diagram with no mention of either. All three say the same thing, and Chapter 3.8 shows why the third form is the most reliable to work from.
Not every vendor uses the same names. The CPOL/CPHA nomenclature is near-universal, but some parts label the phase bit CKP/CKE, or SPH, or express it as "data captured on the first/second clock edge" — and at least one widely used family defines its phase bit with the opposite polarity to CPHA. The mode number is more portable than the bit names, and a timing diagram is more portable than either. When a datasheet gives you both, check they agree.
2. The Derivation
Three questions, in order. Each has a two-way answer, and the first two are just reading off the configuration bits.
Question 1 — where does the clock rest?
CPOL = 0 → low. CPOL = 1 → high.
Question 2 — which physical direction is the leading edge? The leading edge takes the clock away from rest (Chapter 2.2). Rests low → leading is rising. Rests high → leading is falling. The trailing edge is the other one.
Question 3 — which logical edge samples?
CPHA = 0 → leading samples, trailing launches.
CPHA = 1 → trailing samples, leading launches.
Compose them and you have the physical answer.
Worked: Mode 2. mode = 2 → CPOL = 1, CPHA = 0. Clock rests high. Leading edge is therefore falling. CPHA = 0 means leading samples, so the device samples on falling and launches on rising. Check against the table: correct.
Worked: Mode 1. CPOL = 0, CPHA = 1. Rests low, so leading is rising. CPHA = 1 means trailing samples, so it samples on rising's partner — falling — and launches on rising. Correct.
3. Seeing All Four
All four modes over the same three bit times
10 cyclesThe figure makes two structural facts visible that the table states but does not show.
Modes 0 and 1 have identical clocks. So do modes 2 and 3. CPOL alone determines the waveform's shape, so the traces pair up by polarity. What separates the members of each pair is invisible on the clock line — it is when the data is read, which is why the markers rather than the traces carry the distinction.
Modes 0 and 2 sample at the same moment; so do 1 and 3. The CPHA = 0 modes both sample at the first edge of each bit time, and the CPHA = 1 modes both sample at the second — regardless of which physical direction that edge points. That is CPHA's independence, drawn.
4. Which Modes You Actually Meet
The four are not equally common, and knowing the distribution saves time.
Modes 0 and 3 dominate. A large majority of SPI peripherals — serial flash especially — specify one or the other, and many support both. The reason is a useful piece of insight rather than an accident: in Mode 0 the clock idles low and data is launched on falling edges; in Mode 3 it idles high and data is launched on falling edges as well. Look at the table again: mode 0 launches on trailing, which with CPOL = 0 is falling; mode 3 launches on leading, which with CPOL = 1 is also falling.
So modes 0 and 3 have the same launch and sample directions — launch on falling, sample on rising — and differ only in the level the clock rests at between transfers. A device that samples on rising edges and does not care what the line does while idle works in both. That is precisely why so many parts list both, and it is the most useful single fact in this chapter for practical work. Chapter 3.7 develops the interoperability consequences.
Modes 1 and 2 are less common but not rare. Certain converters, displays and radio transceivers specify them, and a controller that supports only 0 and 3 will eventually meet a part it cannot drive.
5. The Mode Decoder — Three HDLs
Now build the hardware. This is the block that turns a mode number into the strobes the datapath acts on, and it composes everything Module 2 and this module have built.
Circuit
A thin decode plus the role mapping. The two-bit mode input is split into cpol and cpha; cpol drives the divider's polarity (Chapter 3.1); cpol and cpha together select which physical strobe plays which logical role, exactly as Chapter 2.3 established. The output is the launch and sample strobes plus the polarity the divider needs.
State
None. As with the role mapper, this is pure routing: the clock's phase lives in the divider and the data in the shift register. Keeping it combinational means the strobes reach the datapath in the same system-clock cycle as the SCLK transition they describe — a register here would delay the response by a cycle relative to the edge it is supposed to correspond to.
Combinational logic
A bit split and four two-input selections. That is the whole design, and its smallness is the lesson: the four modes are not four behaviours in hardware, they are two muxes.
Clock, enables, reset
No internal clock and no state, so no reset. The input strobes are themselves the qualification — if the divider is parked, neither pulses and neither output fires.
Timing
launch_stb and sample_stb are one system-clock cycle wide and coincide with the SCLK transition they name. They are mutually exclusive by construction, because they are complementary selections of the same two mutually exclusive inputs — the invariant Chapter 2.3 established and §6 asserts.
Synthesis
A handful of gates.
Limitation
This decodes a mode; it does not know what a transfer is. No bit counter, no CS sequencing, and — importantly — no first-bit handling. Under CPHA = 0 the first bit must be launched by CS assertion rather than by an edge (Chapter 3.2 §4), and nothing here does that. Chapter 3.4 builds it. A production controller also needs the mode to be changeable only while idle, since altering cpol moves a line every device on the bus is watching.
module spi_mode_decode (
// Configuration: the conventional encoding is {CPOL, CPHA}.
input logic [1:0] mode,
// Physical edge strobes from the divider (Chapter 3.1), mutually
// exclusive and one system-clock cycle wide.
input logic sclk_rise,
input logic sclk_fall,
// To the divider: the level SCLK parks at (Chapter 3.1).
output logic cpol,
// To the datapath: which edge does what (Chapter 2.3).
output logic launch_stb,
output logic sample_stb
);
logic cpha;
logic leading_stb, trailing_stb;
assign cpol = mode[1]; // high bit is polarity
assign cpha = mode[0]; // low bit is phase
// Step 2 of the derivation: the leading edge LEAVES the idle level.
assign leading_stb = cpol ? sclk_fall : sclk_rise;
assign trailing_stb = cpol ? sclk_rise : sclk_fall;
// Step 3: CPHA assigns the roles. Complementary selection, so the two
// strobes can never coincide.
assign sample_stb = cpha ? trailing_stb : leading_stb;
assign launch_stb = cpha ? leading_stb : trailing_stb;
endmoduleThe testbench is the derivation, executed. It sweeps all four modes against both physical edges — eight cases — and checks the decoded roles against the table in §1, which is the only place in this curriculum where that table is used as a reference rather than derived.
module spi_mode_decode_tb;
logic [1:0] mode;
logic sclk_rise, sclk_fall;
logic cpol, launch_stb, sample_stb;
int errors = 0;
spi_mode_decode dut (
.mode(mode), .sclk_rise(sclk_rise), .sclk_fall(sclk_fall),
.cpol(cpol), .launch_stb(launch_stb), .sample_stb(sample_stb));
// Apply ONE physical edge and check which role it played.
task automatic check(input bit [1:0] m, input bit rise,
input bit exp_launch, input bit exp_sample,
input string what);
mode = m;
sclk_rise = rise; sclk_fall = !rise; #1;
if (launch_stb !== exp_launch || sample_stb !== exp_sample) begin
$error("mode %0d %s: launch=%b sample=%b expected %b/%b",
m, what, launch_stb, sample_stb, exp_launch, exp_sample);
errors++;
end
if (launch_stb && sample_stb) begin
$error("mode %0d %s: both strobes asserted", m, what); errors++;
end
sclk_rise = 1'b0; sclk_fall = 1'b0; #1;
endtask
initial begin
sclk_rise = 1'b0; sclk_fall = 1'b0;
// Mode 0: cpol=0 (leading=rising), cpha=0 (leading samples).
check(2'd0, 1'b1, 1'b0, 1'b1, "rising"); // leading -> sample
check(2'd0, 1'b0, 1'b1, 1'b0, "falling"); // trailing -> launch
// Mode 1: cpol=0 (leading=rising), cpha=1 (trailing samples).
check(2'd1, 1'b1, 1'b1, 1'b0, "rising"); // leading -> launch
check(2'd1, 1'b0, 1'b0, 1'b1, "falling"); // trailing -> sample
// Mode 2: cpol=1 (leading=falling), cpha=0 (leading samples).
check(2'd2, 1'b0, 1'b0, 1'b1, "falling"); // leading -> sample
check(2'd2, 1'b1, 1'b1, 1'b0, "rising"); // trailing -> launch
// Mode 3: cpol=1 (leading=falling), cpha=1 (trailing samples).
check(2'd3, 1'b0, 1'b1, 1'b0, "falling"); // leading -> launch
check(2'd3, 1'b1, 1'b0, 1'b1, "rising"); // trailing -> sample
// cpol must follow the high bit for every mode.
for (int m = 0; m < 4; m++) begin
mode = m[1:0]; #1;
if (cpol !== m[1]) begin
$error("mode %0d: cpol=%b expected %b", m, cpol, m[1]); errors++;
end
end
// No physical edge must produce no strobe, in every mode.
sclk_rise = 1'b0; sclk_fall = 1'b0;
for (int m = 0; m < 4; m++) begin
mode = m[1:0]; #1;
if (launch_stb || sample_stb) begin
$error("mode %0d: strobe asserted with no SCLK edge", m); errors++;
end
end
// Modes 0 and 3 must agree on PHYSICAL directions — the §4 insight.
begin
bit m0_launch_on_fall, m3_launch_on_fall;
mode = 2'd0; sclk_rise = 1'b0; sclk_fall = 1'b1; #1;
m0_launch_on_fall = launch_stb;
mode = 2'd3; #1;
m3_launch_on_fall = launch_stb;
if (!(m0_launch_on_fall && m3_launch_on_fall)) begin
$error("modes 0 and 3 should both launch on falling"); errors++;
end
sclk_rise = 1'b0; sclk_fall = 1'b0; #1;
end
if (errors == 0)
$display("PASS: all four modes decode per the derivation; 0 and 3 agree physically");
else
$display("FAIL: %0d errors", errors);
$finish;
end
endmodule module spi_mode_decode (
input [1:0] mode,
input sclk_rise,
input sclk_fall,
output cpol,
output launch_stb,
output sample_stb
);
wire cpha;
wire leading_stb;
wire trailing_stb;
assign cpol = mode[1];
assign cpha = mode[0];
assign leading_stb = cpol ? sclk_fall : sclk_rise;
assign trailing_stb = cpol ? sclk_rise : sclk_fall;
assign sample_stb = cpha ? trailing_stb : leading_stb;
assign launch_stb = cpha ? leading_stb : trailing_stb;
endmodule module spi_mode_decode_tb;
reg [1:0] mode;
reg sclk_rise, sclk_fall;
wire cpol, launch_stb, sample_stb;
integer errors = 0, m;
reg m0_launch_on_fall, m3_launch_on_fall;
spi_mode_decode dut (
.mode(mode), .sclk_rise(sclk_rise), .sclk_fall(sclk_fall),
.cpol(cpol), .launch_stb(launch_stb), .sample_stb(sample_stb));
task check;
input [1:0] m_in;
input rise;
input exp_launch;
input exp_sample;
input [255:0] what;
begin
mode = m_in;
sclk_rise = rise; sclk_fall = !rise; #1;
if (launch_stb !== exp_launch || sample_stb !== exp_sample) begin
$display("ERROR mode %0d %0s: launch=%b sample=%b expected %b/%b",
m_in, what, launch_stb, sample_stb, exp_launch, exp_sample);
errors = errors + 1;
end
if (launch_stb && sample_stb) begin
$display("ERROR mode %0d %0s: both strobes asserted", m_in, what);
errors = errors + 1;
end
sclk_rise = 1'b0; sclk_fall = 1'b0; #1;
end
endtask
initial begin
sclk_rise = 1'b0; sclk_fall = 1'b0;
check(2'd0, 1'b1, 1'b0, 1'b1, "rising");
check(2'd0, 1'b0, 1'b1, 1'b0, "falling");
check(2'd1, 1'b1, 1'b1, 1'b0, "rising");
check(2'd1, 1'b0, 1'b0, 1'b1, "falling");
check(2'd2, 1'b0, 1'b0, 1'b1, "falling");
check(2'd2, 1'b1, 1'b1, 1'b0, "rising");
check(2'd3, 1'b0, 1'b1, 1'b0, "falling");
check(2'd3, 1'b1, 1'b0, 1'b1, "rising");
for (m = 0; m < 4; m = m + 1) begin
mode = m[1:0]; #1;
if (cpol !== m[1]) begin
$display("ERROR mode %0d: cpol=%b expected %b", m, cpol, m[1]);
errors = errors + 1;
end
end
sclk_rise = 1'b0; sclk_fall = 1'b0;
for (m = 0; m < 4; m = m + 1) begin
mode = m[1:0]; #1;
if (launch_stb || sample_stb) begin
$display("ERROR mode %0d: strobe asserted with no SCLK edge", m);
errors = errors + 1;
end
end
mode = 2'd0; sclk_rise = 1'b0; sclk_fall = 1'b1; #1;
m0_launch_on_fall = launch_stb;
mode = 2'd3; #1;
m3_launch_on_fall = launch_stb;
if (!(m0_launch_on_fall && m3_launch_on_fall)) begin
$display("ERROR modes 0 and 3 should both launch on falling");
errors = errors + 1;
end
sclk_rise = 1'b0; sclk_fall = 1'b0; #1;
if (errors == 0)
$display("PASS: all four modes decode per the derivation; 0 and 3 agree physically");
else
$display("FAIL: %0d errors", errors);
$finish;
end
endmodule library ieee;
use ieee.std_logic_1164.all;
entity spi_mode_decode is
port (
-- Conventional encoding: mode(1) is CPOL, mode(0) is CPHA.
mode : in std_logic_vector(1 downto 0);
-- Physical edge strobes from the divider (Chapter 3.1).
sclk_rise : in std_logic;
sclk_fall : in std_logic;
-- To the divider.
cpol : out std_logic;
-- To the datapath (Chapter 2.3).
launch_stb : out std_logic;
sample_stb : out std_logic
);
end entity spi_mode_decode;
architecture rtl of spi_mode_decode is
signal cpol_i : std_logic;
signal cpha_i : std_logic;
signal leading_stb : std_logic;
signal trailing_stb : std_logic;
begin
cpol_i <= mode(1);
cpha_i <= mode(0);
-- Step 2: the leading edge leaves the idle level.
leading_stb <= sclk_fall when cpol_i = '1' else sclk_rise;
trailing_stb <= sclk_rise when cpol_i = '1' else sclk_fall;
-- Step 3: CPHA assigns the roles. Complementary, so never coincident.
sample_stb <= trailing_stb when cpha_i = '1' else leading_stb;
launch_stb <= leading_stb when cpha_i = '1' else trailing_stb;
cpol <= cpol_i;
end architecture rtl; library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity spi_mode_decode_tb is
end entity spi_mode_decode_tb;
architecture sim of spi_mode_decode_tb is
signal mode : std_logic_vector(1 downto 0) := "00";
signal sclk_rise : std_logic := '0';
signal sclk_fall : std_logic := '0';
signal cpol : std_logic;
signal launch_stb : std_logic;
signal sample_stb : std_logic;
begin
dut : entity work.spi_mode_decode
port map (mode => mode, sclk_rise => sclk_rise, sclk_fall => sclk_fall,
cpol => cpol, launch_stb => launch_stb, sample_stb => sample_stb);
stim : process
variable errors : natural := 0;
variable m0_fall, m3_fall : std_logic;
procedure check (m : in integer;
rise : in std_logic;
exp_launch : in std_logic;
exp_sample : in std_logic;
what : in string) is
begin
mode <= std_logic_vector(to_unsigned(m, 2));
sclk_rise <= rise;
sclk_fall <= not rise;
wait for 1 ns;
if launch_stb /= exp_launch or sample_stb /= exp_sample then
report "mode " & integer'image(m) & " " & what &
": wrong role mapping" severity error;
errors := errors + 1;
end if;
if launch_stb = '1' and sample_stb = '1' then
report "mode " & integer'image(m) & " " & what &
": both strobes asserted" severity error;
errors := errors + 1;
end if;
sclk_rise <= '0';
sclk_fall <= '0';
wait for 1 ns;
end procedure check;
begin
check(0, '1', '0', '1', "rising");
check(0, '0', '1', '0', "falling");
check(1, '1', '1', '0', "rising");
check(1, '0', '0', '1', "falling");
check(2, '0', '0', '1', "falling");
check(2, '1', '1', '0', "rising");
check(3, '0', '1', '0', "falling");
check(3, '1', '0', '1', "rising");
-- cpol must follow the high bit in every mode.
for m in 0 to 3 loop
mode <= std_logic_vector(to_unsigned(m, 2));
wait for 1 ns;
if cpol /= mode(1) then
report "cpol does not follow mode(1)" severity error;
errors := errors + 1;
end if;
end loop;
-- No edge must produce no strobe, in every mode.
sclk_rise <= '0';
sclk_fall <= '0';
for m in 0 to 3 loop
mode <= std_logic_vector(to_unsigned(m, 2));
wait for 1 ns;
if launch_stb = '1' or sample_stb = '1' then
report "strobe asserted with no SCLK edge" severity error;
errors := errors + 1;
end if;
end loop;
-- Modes 0 and 3 must both launch on a falling edge.
mode <= "00"; sclk_rise <= '0'; sclk_fall <= '1'; wait for 1 ns;
m0_fall := launch_stb;
mode <= "11"; wait for 1 ns;
m3_fall := launch_stb;
if not (m0_fall = '1' and m3_fall = '1') then
report "modes 0 and 3 should both launch on falling" severity error;
errors := errors + 1;
end if;
sclk_rise <= '0'; sclk_fall <= '0'; wait for 1 ns;
if errors = 0 then
report "PASS: all four modes decode per the derivation; 0 and 3 agree physically"
severity note;
else
report "FAIL" severity error;
end if;
wait;
end process stim;
end architecture sim;What the three agree on, and where they differ
All three describe the same gates: a bit split, two selections mapping physical edges to logical ones by polarity, and two complementary selections mapping logical edges to roles by phase. No state, no clock, no reset, and exclusivity holding by construction in every one.
The instructive difference is again in the testbenches, and it is about type strictness. SystemVerilog and Verilog index mode as a bit vector and compare against m[1] directly. VHDL cannot treat an integer loop variable as a std_logic_vector, so the sweep converts explicitly with to_unsigned(m, 2) from numeric_std — an extra line that also makes the width explicit at every use. That strictness is a nuisance in a four-iteration loop and the reason VHDL catches width mismatches elsewhere that the Verilog dialects silently truncate.
6. The Assertion Worth Writing
Mode configurability introduces exactly one invariant that a refactor can break silently, and it is cheap to state.
// The decoder derives launch and sample as COMPLEMENTARY selections, so they
// cannot both fire. A "simplification" that computed them independently --
// for example from mode-specific case statements -- could break this without
// any test noticing, because most stimulus never exercises the bad case.
property p_roles_exclusive;
@(posedge clk) disable iff (!rst_n)
!(launch_stb && sample_stb);
endproperty
a_roles_exclusive : assert property (p_roles_exclusive)
else $error("launch and sample coincided in mode %0d", mode);
// And the polarity fed to the divider must track the mode's high bit --
// a mismatch here means the clock idles at the wrong level (Chapter 3.1).
property p_cpol_tracks_mode;
@(posedge clk) disable iff (!rst_n)
cpol == mode[1];
endproperty
a_cpol_tracks_mode : assert property (p_cpol_tracks_mode)
else $error("cpol=%b does not match mode[1] of mode %0d", cpol, mode);What they prove. That the design never issues both instructions in the same simulation cycle, in any mode, and that the divider is always told the polarity the mode requires. Both are genuine and both catch real refactoring mistakes.
What they do not prove. Neither says the configured mode is right for the attached device — that is a comparison against a datasheet, which no signal-level assertion can perform. And as Chapter 2.3 §4 stressed, a cycle-based exclusivity check says nothing about analogue separation at a device's pins. These are internal-consistency checks, and internal consistency is exactly what they are worth.
7. Coverage: Four Modes Is a Small, Checkable Space
Mode is the clearest example in this curriculum of a state space small enough to cover exhaustively and important enough that you should.
// Four modes is trivially coverable, and un-covered modes are the classic
// source of "it worked in the lab" integration failures.
covergroup spi_mode_cg @(posedge transfer_done);
cp_mode : coverpoint cfg.mode {
bins m[] = {[0:3]}; // all four, individually
}
// CPHA=0 has the first-bit path (Chapter 3.2 §4); a short transfer
// exercises it proportionally more, so the cross matters.
cp_len : coverpoint cfg.n_bits {
bins one_byte = {8};
bins multi_byte = {[16:64]};
}
// Direction matters because the first-bit path is on the TRANSMIT side.
cp_dir : coverpoint cfg.has_miso_payload {
bins tx_only = {0};
bins with_rx = {1};
}
x_mode_len : cross cp_mode, cp_len;
x_mode_dir : cross cp_mode, cp_dir;
endgroupThree notes on why these particular crosses and not others.
Mode alone is not enough. All four modes passing a single byte-length test proves the decoder works, not that the datapath works in each mode. The first-bit path exists only for CPHA = 0, so crossing mode with transfer length ensures the modes that have a special case are exercised where that case is proportionally most visible.
The direction cross targets a real asymmetry. The first-bit launch is a transmit-side obligation, so a test suite that only ever reads from devices can cover all four modes while never exercising the path that breaks.
Do not cross everything. Crossing mode with clock divisor, for example, would multiply the bins without testing an interaction that exists — the mode decoder is combinational and indifferent to the divisor. A cross should correspond to a mechanism, which is the discipline Module 17 develops properly.
8. Reading a Mode Out of a Datasheet
The practical inverse of the derivation: a part's timing diagram is in front of you and you need the mode number.
Step 1 — find the idle level. Look at SCLK on the left of the diagram, before the first edge and before CS asserts. Resting low is CPOL = 0; high is CPOL = 1. This is Chapter 2.2 §5's habit, applied to paper instead of a scope.
Step 2 — find where the data is sampled. Most timing diagrams mark it with an arrow, a dashed vertical line, or a note such as "data valid" or "MSB in". Identify which edge of each bit time it points at.
Step 3 — is that edge leading or trailing? The first edge of the bit time is leading, the second is trailing. Leading sampling is CPHA = 0; trailing is CPHA = 1.
Compose: mode = (CPOL << 1) | CPHA.
Two cautions. Some diagrams mark the launch point rather than the sample point — read the label rather than assuming the arrow's meaning, since mistaking one for the other inverts CPHA. And where a datasheet states a mode number and shows a diagram, trust the diagram and raise the discrepancy: vendor documentation errors on mode numbering are not rare, and the diagram is the more primitive statement. Full datasheet technique is Module 10.
9. Failure Signature — One Device Works, Another on the Same Bus Does Not
Symptom. A bus with two peripherals: one communicates perfectly, the other returns corrupt data on every transaction. Both are wired identically, both are selected correctly, and the corruption is fully reproducible at every clock rate.
Plausible mechanisms. A single configured mode that suits one device and not the other is the leading candidate — Chapter 3.1 §3 established that all devices share the clock net, so a master cannot satisfy conflicting requirements simultaneously. Competing explanations are a device-specific command-format error, and a device that requires an initialisation sequence the driver omits.
The discriminating observations. First, reproducibility at every rate rules out margin (Chapter 2.4) and the round trip, which are frequency-dependent — so this is configuration, not timing.
Second, compare both datasheets' mode requirements. If they differ, you have it without touching the board.
Third, if they agree on paper, scope the idle level and the sampling edge for the failing device and derive the mode actually present on the wire using §8. A mismatch between that and the datasheet means the controller is not configured the way the driver believes — which happens when a bootloader or another driver instance has programmed the peripheral first.
The fix and its cost. Reconfigure the mode between transactions to the different devices, which must happen while no CS is asserted because changing cpol moves the shared clock line (Chapter 3.1 §9). If the two devices differ only in CPOL and both tolerate either idle level — the modes 0 and 3 relationship of §4 — a single setting may serve both, which is worth checking before adding reconfiguration logic.
10. Common Misconceptions
11. Reason It Through
Work this before reading the answers.
A logic analyser capture from a working link shows: SCLK sitting high whenever CS is deasserted; MOSI changing state on falling edges of SCLK; and the device's datasheet timing diagram marking data capture on rising edges. A new engineer is asked which mode to configure a replacement controller for, and answers "Mode 1, because data is captured on rising edges and that is what Mode 1 does."
Is the answer right? No, and the reasoning has the right ingredients composed in the wrong order.
Derive it properly. Step 1: the clock rests high when idle, so CPOL = 1. Step 2: the leading edge takes the clock away from rest, so with CPOL = 1 the leading edge is falling and the trailing edge is rising. Step 3: the datasheet captures on rising, which we have just established is the trailing edge — so CPHA = 1. Therefore mode = (1 << 1) | 1 = Mode 3.
Where did the wrong answer come from? From treating "samples on rising" as a property that identifies a mode by itself. It does not — it identifies a mode only once you know the polarity, because the same physical direction plays different roles under different polarities. Mode 1 also samples on rising edges, and so does Mode 3; they are distinguished by the idle level, which the engineer's reasoning never used.
Is the MOSI observation consistent? Yes, and it is a useful cross-check. In Mode 3 the leading edge launches, and with CPOL = 1 leading is falling — so MOSI should change on falling edges, which is exactly what the capture shows. Two independent observations agreeing is how you gain confidence in a derived mode before committing it to a driver.
What would have made this trivially unambiguous? Reading the idle level first. It is the cheapest observation available, it is visible whenever nothing is happening, and it immediately halves the hypothesis space. An engineer who starts every mode question with "where is the clock resting?" will not make this mistake.
A closing subtlety. Note that Modes 1 and 3 both sample on rising and both launch on falling — they are the §4 pairing seen from the other side. So a device that genuinely does not care what the clock does between transfers would work in either, and the distinction only bites if the device has an opinion about the idle level. Checking whether the datasheet expresses such an opinion is the last step before deciding that "either will do."
12. Understanding Check
13. Summary
The four SPI modes are two independent bits, conventionally numbered mode = (CPOL << 1) | CPHA. They are derived, not memorised, in three steps: CPOL gives the resting level; the resting level gives the leading edge's direction; CPHA assigns the sample role to the leading or trailing edge. Compose and you have the physical behaviour of any mode in seconds — and, run backwards, the mode number from any timing diagram.
Two structural facts are worth carrying. CPOL alone determines the clock's shape, so modes 0 and 1 share a waveform, as do 2 and 3; what separates each pair is when the data is read, which is invisible on the clock line. And modes 0 and 3 agree on both physical directions — launch on falling, sample on rising — differing only in the idle level, which is why so many devices support both and why an idle-level-indifferent part works in either.
In hardware the four modes are two multiplexers: polarity selects which physical strobe is leading, phase selects which logical edge samples. There are no mode-specific states and no mode-specific datapath. The genuinely new logic belongs to CPHA = 0 alone — the first-bit launch — and is built next.
The invariants worth asserting are that the two role strobes never coincide in any mode, and that the polarity handed to the divider tracks the mode's high bit. Both catch real refactoring errors; neither can tell you the configured mode is right for the attached device, which is a datasheet comparison rather than a signal-level property. And mode is a four-member state space, small enough to cover exhaustively and worth crossing with transfer length and direction — because the paths that differ between modes are not exercised by mode selection alone.
14. What Comes Next
The derivation is complete and the decoder is built. Chapter 3.4 — Mode 0 takes the most widely used combination apart in full, and confronts the one piece of hardware the mode bits genuinely require: the first-bit launch that CPHA = 0 forces, built in all three HDLs with the assertion that catches its omission.
Browse the path on the SPI curriculum index, or revisit CPOL and CPHA for the two bits this chapter combines.
Continue learning
Related tutorials
- Related topic
Command, Address, and Data Phases
How a device layers a transaction onto a raw byte stream: why the opcode decides the shape of everything after it, how a slave tracks phases with no phase marker, and the sequencer that requires in three HDLs.
- Related topic
Chip Select Semantics and Device Selection
What selection means on an SPI bus: why active-low is a convention, what each CS edge commits the device to, why selection is physical rather than addressed, and the generator that makes multi-select structurally impossible.
- Related topic
Launch and Sample Edges
One edge of each bit time places a bit on the wire, the other captures it, and they must never be the same edge. Why the separation is forced, why it buys half a period, and how RTL maps physical edges onto those roles.
- Related topic
Mode Mismatch and Its Failure Signature
What happens when the two ends disagree about the mode. The distinct signature each mismatch produces, how to tell polarity from phase disagreement from the data alone, and the monitor and coverage work that catches it.
