SPI · Module 3
CPOL — Clock Polarity
The first of the two bits that define an SPI mode. What clock polarity specifies, why it is a property of the idle state rather than of the transfer, and the RTL change that makes a divider polarity-aware.
Module 2 built the complete timing vocabulary and deliberately left two things unnamed. Chapter 2.2 observed that SCLK rests at some level between transfers and that which level it is changes what "leading edge" means physically. Chapter 2.3 observed that one edge of each bit time launches and the other samples, and that which is which is configurable.
Those are the two bits. This chapter names the first one.
What does clock polarity actually specify, and what does a master owe a device because of it?
The answer is narrower than most treatments suggest, and getting it narrow is the point. CPOL says exactly one thing, and the four SPI modes are what you get when that one thing is combined with the one thing Chapter 3.2 names.
1. CPOL Is a Statement About Idle, Not About Transfer
CPOL — clock polarity — specifies the logic level SCLK is held at when no transfer is in progress.
CPOL = 0 means the clock idles low. CPOL = 1 means it idles high. That is the entire definition.
Notice what it does not say. It says nothing about which edge launches data, nothing about which edge samples it, and nothing about the number of edges in a transfer. Those are separate matters, and conflating them is the single most common source of confusion about SPI modes — the four modes look like four unrelated behaviours precisely because people try to absorb them as four combined facts instead of two independent bits.
Two consequences follow immediately from the definition, and both are more practical than they sound.
The clock's direction at the start of a transfer is determined. Chapter 2.2 defined the leading edge as the one that takes the clock away from its resting level. With CPOL = 0 the clock rests low, so the leading edge is rising. With CPOL = 1 it rests high, so the leading edge is falling. CPOL does not choose the leading edge's role — it fixes its direction.
The requirement applies before and after the burst, not only during it. A device expects the line at its configured idle level whenever it is not being clocked. That includes the interval before CS asserts, the CS lead interval after CS asserts but before the first edge, the CS lag interval after the last edge, and the whole time the bus is idle. A master that parks the clock at the wrong level has violated the device's expectation even if it never transfers a single bit.
2. Idle, Active, Idle
CPOL fixes the resting level — before, and after
10 cyclesThree things to read off the figure.
The two traces are exact complements. Invert one and you get the other. That is all CPOL does — it inverts the waveform, nothing more.
The first edge points in opposite directions. At the start of the burst the cpol=0 trace rises while the cpol=1 trace falls. Both are leading edges, both occur at the same instant, and an implementation written against "rising" rather than "leading" will act at the wrong moment for one of them — the half-bit-time displacement Chapter 2.2 warned about.
Both bursts contain the same number of edges. Six edges, three bit times, in both traces. CPOL does not add or remove edges; it does not change the transfer's length or structure. This matters because a common misreading is that changing polarity somehow shifts the data by half a bit — it does not, on its own. Displacement comes from mismatching polarity between master and device, which is Chapter 3.8's subject.
3. Why Both Polarities Exist
It is reasonable to ask why the protocol did not simply pick one. Three answers, in increasing order of how often they actually matter.
Historical divergence. SPI was never a single ratified standard (Chapter 1.1), so independent implementations made independent choices early, and by the time the modes were named as a set both conventions had substantial installed bases. Compatibility, not design preference, is why both survive.
Idle power and pin state. The level a line rests at is not always free. Depending on the board — a pull resistor, a level shifter, an opto-isolator, a device's internal pull — one idle level may draw current and the other may not, or one may be safer during reset and power sequencing. On a low-power design this can be a real selection criterion rather than an arbitrary one.
Bus sharing with devices that disagree. This is the one that bites in practice, and it is worth stating plainly: all devices on a shared SCLK net see every edge, whether or not they are selected. If two peripherals on the same bus require different polarities, the master cannot satisfy both simultaneously — it must reconfigure its clock polarity between transactions, and it must do so while the bus is idle, because changing the parked level is itself an edge on a line every device is watching.
That last point has a direct consequence for driver design, and §7 returns to it as a failure mode.
4. What a Master Must Guarantee
CPOL turns into three obligations, and a controller that satisfies only the first is subtly wrong.
Park at the configured level when idle. Between transfers, the clock output holds CPOL. Chapter 2.1's divider parked at 0 unconditionally, which happened to be correct for CPOL = 0 and wrong for CPOL = 1. §5 fixes that.
Begin the burst with a leading edge. The first edge after the CS lead interval must take the clock away from the idle level. A divider that parks high but whose first toggle drives it high again produces no edge at all on the first bit time — a subtle bug that costs exactly one bit and is easy to write.
Return to the idle level before releasing CS. After the final bit time, the clock must be back at CPOL before CS deasserts, so the CS lag interval is spent with the clock resting correctly. A burst containing an even number of edges does this naturally; one that stops mid-period does not, and a device may interpret the stranded level as the beginning of another bit time.
That third obligation is why an SPI transfer always contains an even number of edges — two per bit time. It is not a convention; it falls out of the requirement to start and finish at the same level.
5. Making the Divider Polarity-Aware — Three HDLs
Chapter 2.1 built a clock divider that parked at zero. The change CPOL requires is small, and the small change hides a real bug worth seeing.
Circuit
The same counter-and-toggle divider, with one added input: cpol. On reset and whenever the clock is disabled, the output register loads cpol rather than 0. The counter and toggle logic are unchanged.
State
Two registers, as before: the half-period counter cnt, and the internal toggle flop sclk_q. Note what is not done here: sclk_q still resets to a constant 0, and polarity is applied by an XOR on the output.
That choice is worth dwelling on, because the obvious alternative is wrong in a way that simulates perfectly. Writing sclk_q <= cpol in an asynchronous-reset branch asks for a flip-flop whose reset value is an input signal. Real flops do not have that — an asynchronous reset pin forces a fixed state. A tool will either reject it, or synthesise a mux and a different structure than you drew, or (on some FPGAs) absorb it in a way that is not portable. Resetting to a constant and inverting at the output produces the identical waveform with a structure every technology implements directly.
Combinational logic
A terminal-count comparison, the output XOR, and two edge strobes. The strobes now have to account for the inversion: sclk rises when the internal flop toggles from a value equal to cpol, and falls when it toggles from a value unequal to it. That is one comparison, and getting it wrong is a real bug — the strobes would name the wrong physical direction for cpol = 1, and every downstream role decision would invert.
The meaning of the strobes still changes with polarity, and that is the point of §6: sclk_rise is the leading edge when cpol = 0 and the trailing edge when cpol = 1. The divider does not need to know which; it reports physical transitions and Chapter 2.3's role mapper interprets them.
Clock, enables, reset
All unchanged from Chapter 2.1: the system clock, an en that parks the output, an asynchronous active-low reset. The internal flop resets to 0, and because the output is sclk_q ^ cpol the pin parks at cpol — the correct safe state, since a device seeing the wrong idle level at power-up is seeing a violation before any transfer.
Timing
f_SCLK = f_clk / DIV as before. The first edge after en rises is guaranteed to be a leading edge, because the output was parked at cpol and the first toggle necessarily moves it away.
Synthesis
Identical to Chapter 2.1's — a small counter, a comparator, a toggle flip-flop — plus one XOR gate on the output and one comparison in the strobe logic. No area change worth measuring, and critically no unusual reset structure.
The bug this makes visible
Consider what happens if you park at cpol but forget that the counter also needs resetting. A burst that ends mid-period leaves cnt partway through, so the next burst's first half-period is short — a runt leading edge on the first bit time. Chapter 2.1's design already cleared cnt when en dropped, and this version keeps that. It is worth noticing because it is invisible until you look for it, and it produces a first-bit failure that looks exactly like a CS lead violation.
Limitation
Still not an SPI clock engine. No bit counting, no CS sequencing, no runtime polarity change while a transfer is in flight — that last one matters because, as §3 noted, changing cpol moves a line every device on the bus is watching, so a real controller must gate it to the idle state. Module 13 builds the production version.
module spi_clkdiv_cpol #(
parameter int DIV = 4 // SCLK period in clk cycles; even, >= 2
) (
input logic clk,
input logic rst_n, // asynchronous, active-low
input logic en, // low: counter holds, SCLK parks at cpol
input logic cpol, // 0: idle low 1: idle high
output logic sclk,
output logic sclk_rise, // PHYSICAL transitions — Chapter 2.3
output logic sclk_fall // maps these onto leading/trailing
);
localparam int HALF = DIV / 2;
localparam int CW = (HALF <= 1) ? 1 : $clog2(HALF);
logic [CW-1:0] cnt;
logic sclk_q;
logic tick;
assign tick = en && (cnt == CW'(HALF - 1));
// The toggle flop resets to a CONSTANT. Polarity is applied by the output
// XOR below — a flop cannot have a signal as its asynchronous reset value.
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= '0;
sclk_q <= 1'b0; // constant reset value
end else if (!en) begin
cnt <= '0; // clear so the next burst starts aligned
sclk_q <= 1'b0; // idle state of the internal toggle
end else if (tick) begin
cnt <= '0;
sclk_q <= ~sclk_q; // first toggle always LEAVES the idle state
end else begin
cnt <= cnt + 1'b1;
end
end
assign sclk = sclk_q ^ cpol; // parks at cpol; whole waveform inverts
// sclk rises when the internal flop toggles AWAY FROM a value equal to cpol.
assign sclk_rise = tick && (sclk_q == cpol);
assign sclk_fall = tick && (sclk_q != cpol);
endmoduleThe testbench checks the two things polarity actually changes: where the clock parks, and which way the first edge goes. It runs the whole check for both values of cpol, because a design that only works for one is the exact bug this chapter exists to prevent.
module spi_clkdiv_cpol_tb;
localparam int DIV = 4;
logic clk = 1'b0, rst_n, en, cpol;
logic sclk, sclk_rise, sclk_fall;
int errors = 0;
spi_clkdiv_cpol #(.DIV(DIV)) dut (
.clk(clk), .rst_n(rst_n), .en(en), .cpol(cpol),
.sclk(sclk), .sclk_rise(sclk_rise), .sclk_fall(sclk_fall));
always #5 clk = ~clk;
initial begin // watchdog: a hang must FAIL
#20_000;
$display("FAIL: timeout");
$finish;
end
// Run the full check for BOTH polarities.
task automatic check_polarity(input bit p);
int edges;
cpol = p;
rst_n = 1'b0; en = 1'b0;
@(posedge clk); #1;
if (sclk !== p) begin
$error("cpol=%b: reset parked at %b, expected %b", p, sclk, p); errors++;
end
rst_n = 1'b1;
@(posedge clk); #1;
if (sclk !== p) begin
$error("cpol=%b: idle level is %b, expected %b", p, sclk, p); errors++;
end
// Enable, and confirm the FIRST edge moves AWAY from the idle level.
en = 1'b1;
edges = 0;
while (sclk === p) begin
@(posedge clk); #1;
edges++;
if (edges > DIV) begin
$error("cpol=%b: no first edge within a period", p); errors++;
break;
end
end
if (sclk === p) begin
$error("cpol=%b: clock never left its idle level", p); errors++;
end
// The leading edge is rising for cpol=0 and falling for cpol=1.
if (sclk !== ~p) begin
$error("cpol=%b: first edge went the wrong way (sclk=%b)", p, sclk); errors++;
end
// Run some periods, then park and confirm it returns to cpol.
repeat (3 * DIV) @(posedge clk);
en = 1'b0;
@(posedge clk); #1;
if (sclk !== p) begin
$error("cpol=%b: parked at %b after en low, expected %b", p, sclk, p); errors++;
end
endtask
initial begin
check_polarity(1'b0);
check_polarity(1'b1);
if (errors == 0)
$display("PASS: parks at cpol and leads away from it, for both polarities");
else
$display("FAIL: %0d errors", errors);
$finish;
end
endmoduleThe Verilog form is the same hardware. As in Chapter 2.1, Verilog-2001 has no $clog2, so the counter width comes from a constant function, and the testbench uses a static task with $display rather than $error.
module spi_clkdiv_cpol #(
parameter DIV = 4
) (
input clk,
input rst_n,
input en,
input cpol,
output sclk,
output sclk_rise,
output sclk_fall
);
function integer clog2;
input integer value;
integer i;
begin
clog2 = 0;
for (i = value - 1; i > 0; i = i >> 1) clog2 = clog2 + 1;
end
endfunction
localparam HALF = DIV / 2;
localparam CW = (HALF <= 1) ? 1 : clog2(HALF);
reg [CW-1:0] cnt;
reg sclk_q;
wire tick;
assign tick = en && (cnt == (HALF - 1));
// Constant reset value; polarity applied by the output XOR below.
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= {CW{1'b0}};
sclk_q <= 1'b0;
end else if (!en) begin
cnt <= {CW{1'b0}};
sclk_q <= 1'b0;
end else if (tick) begin
cnt <= {CW{1'b0}};
sclk_q <= ~sclk_q;
end else begin
cnt <= cnt + 1'b1;
end
end
assign sclk = sclk_q ^ cpol;
assign sclk_rise = tick && (sclk_q == cpol);
assign sclk_fall = tick && (sclk_q != cpol);
endmodule module spi_clkdiv_cpol_tb;
parameter DIV = 4;
reg clk = 1'b0;
reg rst_n, en, cpol;
wire sclk, sclk_rise, sclk_fall;
integer errors = 0, edges, i, p;
spi_clkdiv_cpol #(.DIV(DIV)) dut (
.clk(clk), .rst_n(rst_n), .en(en), .cpol(cpol),
.sclk(sclk), .sclk_rise(sclk_rise), .sclk_fall(sclk_fall));
always #5 clk = ~clk;
initial begin
#20000;
$display("FAIL: timeout");
$finish;
end
// Verilog-2001 tasks are static; this one keeps no state between calls.
task check_polarity;
input p_in;
begin
cpol = p_in;
rst_n = 1'b0; en = 1'b0;
@(posedge clk); #1;
if (sclk !== p_in) begin
$display("ERROR cpol=%b reset parked at %b", p_in, sclk);
errors = errors + 1;
end
rst_n = 1'b1;
@(posedge clk); #1;
if (sclk !== p_in) begin
$display("ERROR cpol=%b idle level is %b", p_in, sclk);
errors = errors + 1;
end
en = 1'b1;
edges = 0;
while (sclk === p_in && edges <= DIV) begin
@(posedge clk); #1;
edges = edges + 1;
end
if (sclk === p_in) begin
$display("ERROR cpol=%b clock never left idle", p_in);
errors = errors + 1;
end else if (sclk !== ~p_in) begin
$display("ERROR cpol=%b first edge went the wrong way", p_in);
errors = errors + 1;
end
for (i = 0; i < 3 * DIV; i = i + 1) @(posedge clk);
en = 1'b0;
@(posedge clk); #1;
if (sclk !== p_in) begin
$display("ERROR cpol=%b parked at %b after en low", p_in, sclk);
errors = errors + 1;
end
end
endtask
initial begin
check_polarity(1'b0);
check_polarity(1'b1);
if (errors == 0)
$display("PASS: parks at cpol and leads away from it, for both polarities");
else
$display("FAIL: %0d errors", errors);
$finish;
end
endmoduleThe VHDL form keeps Chapter 2.1's range-constrained counter and parks the output at cpol. Note that cpol is a std_logic and can be assigned to sclk_q directly — no conversion, because both are the same type.
library ieee;
use ieee.std_logic_1164.all;
entity spi_clkdiv_cpol is
generic (
DIV : positive := 4 -- SCLK period in clk cycles; even, >= 2
);
port (
clk : in std_logic;
rst_n : in std_logic; -- asynchronous, active-low
en : in std_logic;
cpol : in std_logic; -- '0': idle low '1': idle high
sclk : out std_logic;
sclk_rise : out std_logic; -- PHYSICAL transitions
sclk_fall : out std_logic
);
end entity spi_clkdiv_cpol;
architecture rtl of spi_clkdiv_cpol is
constant HALF : positive := DIV / 2;
signal cnt : integer range 0 to HALF - 1 := 0;
signal sclk_q : std_logic := '0';
signal tick : std_logic;
begin
tick <= '1' when (en = '1' and cnt = HALF - 1) else '0';
-- The toggle flop resets to a CONSTANT; polarity is applied by the output
-- XOR below. A flip-flop cannot take a signal as its async reset value.
div_proc : process (clk, rst_n)
begin
if rst_n = '0' then
cnt <= 0;
sclk_q <= '0'; -- constant reset value
elsif rising_edge(clk) then
if en = '0' then
cnt <= 0;
sclk_q <= '0'; -- idle state of the internal toggle
elsif tick = '1' then
cnt <= 0;
sclk_q <= not sclk_q; -- first toggle always LEAVES idle
else
cnt <= cnt + 1;
end if;
end if;
end process div_proc;
sclk <= sclk_q xor cpol; -- parks at cpol; waveform inverts
-- sclk rises when the internal flop toggles away from a value equal to cpol.
sclk_rise <= tick when (sclk_q = cpol) else '0';
sclk_fall <= tick when (sclk_q /= cpol) else '0';
end architecture rtl; library ieee;
use ieee.std_logic_1164.all;
entity spi_clkdiv_cpol_tb is
end entity spi_clkdiv_cpol_tb;
architecture sim of spi_clkdiv_cpol_tb is
constant DIV : positive := 4;
constant TP : time := 10 ns;
signal clk : std_logic := '0';
signal rst_n : std_logic := '0';
signal en : std_logic := '0';
signal cpol : std_logic := '0';
signal sclk : std_logic;
signal sclk_rise : std_logic;
signal sclk_fall : std_logic;
signal done : boolean := false;
begin
clk <= not clk after TP/2 when not done else '0';
dut : entity work.spi_clkdiv_cpol
generic map (DIV => DIV)
port map (clk => clk, rst_n => rst_n, en => en, cpol => cpol,
sclk => sclk, sclk_rise => sclk_rise, sclk_fall => sclk_fall);
stim : process
variable errors : natural := 0;
procedure check_polarity (p : in std_logic) is
variable edges : natural := 0;
begin
cpol <= p;
rst_n <= '0';
en <= '0';
wait until rising_edge(clk); wait for 1 ns;
if sclk /= p then
report "reset did not park at cpol" severity error;
errors := errors + 1;
end if;
rst_n <= '1';
wait until rising_edge(clk); wait for 1 ns;
if sclk /= p then
report "idle level is not cpol" severity error;
errors := errors + 1;
end if;
-- The first edge must move AWAY from the idle level.
en <= '1';
edges := 0;
while sclk = p and edges <= DIV loop
wait until rising_edge(clk); wait for 1 ns;
edges := edges + 1;
end loop;
if sclk = p then
report "clock never left its idle level" severity error;
errors := errors + 1;
elsif sclk /= not p then
report "first edge went the wrong way" severity error;
errors := errors + 1;
end if;
for i in 0 to 3 * DIV - 1 loop
wait until rising_edge(clk);
end loop;
en <= '0';
wait until rising_edge(clk); wait for 1 ns;
if sclk /= p then
report "did not park at cpol after en low" severity error;
errors := errors + 1;
end if;
end procedure check_polarity;
begin
check_polarity('0');
check_polarity('1');
if errors = 0 then
report "PASS: parks at cpol and leads away from it, for both polarities"
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 as Chapter 2.1's divider with two changes: an XOR applying cpol to the output, and a comparison against cpol in the strobe logic so the strobes still name physical directions correctly. The toggle flop's reset value stays constant in all three — the structural point from the architecture notes. Divisor semantics, reset polarity and enable behaviour are identical across the three and identical to Chapter 2.1.
The instructive difference is in the testbenches, and it is about parameterised repetition. SystemVerilog's automatic task lets the polarity check declare its own edges variable per call. Verilog-2001 tasks are static, so the same variable is shared between the two invocations — harmless here because each call finishes before the next begins, but a real trap in concurrent code, and worth knowing about before you meet it. VHDL uses a procedure declared inside the process, which sees the process's errors variable directly and declares its own edges per call, giving the cleanest scoping of the three.
6. The Strobes Are Physical; the Roles Are Not
One structural point before Chapter 3.2, because it determines how the rest of the module fits together.
The divider above emits sclk_rise and sclk_fall — physical transitions. It does not emit "leading" and "trailing". That is deliberate, and it is the same separation Chapter 2.3 built: the divider knows the clock's shape, and a separate, tiny piece of logic interprets which physical transition is playing which logical role.
With cpol now available, that interpretation is exactly the mapping Chapter 2.3's spi_edge_roles already implements:
cpol = 0 → leading = sclk_rise, trailing = sclk_fall
cpol = 1 → leading = sclk_fall, trailing = sclk_riseNothing new is required in hardware. The module Chapter 2.3 built takes idle_high as an input and does precisely this — which is why this chapter adds a polarity input to the divider and not to the role mapper. The architecture was already prepared for CPOL before CPOL had a name.
Chapter 3.3 closes the loop by adding the second bit and showing that the pair is all four modes.
7. Failure Signature — Everything Works Except the First Transfer After Power-Up
Symptom. A link works reliably in steady operation, but the very first transaction after power-up or after a reset returns corrupt data. Every subsequent transaction is fine, and the fault returns only after another reset.
Plausible mechanisms. A clock parked at the wrong level at reset is the leading candidate: if the master's output register resets to 0 while the device expects CPOL = 1, then the first CS assertion finds the clock at the wrong level, and the transition to the correct level — whenever it happens — is an extra edge the device may count. Competing explanations are a CS lead violation on the first transaction only, and a device that requires an initialisation sequence the driver omits.
The discriminating observation. Scope SCLK and CS across power-up and into the first transaction. Read the SCLK level before CS first asserts and compare it against the device's required idle level. If they differ, this is the mechanism, and you will usually also see a spurious edge as the master's configuration is applied.
Root cause and fix. The master's clock output resets to a fixed level rather than to the configured polarity — exactly the bug §5's RTL prevents by resetting sclk_q to cpol. In a controller where the polarity register is written by software after reset, the correct fix is ordering: configure polarity before enabling the output or asserting any CS, so the line is never presented at the wrong level while a device is listening.
Why "it works after the first transfer" is the clue. By the second transaction the clock has been parked correctly by the first burst's completion, so the wrong level exists only in the window between reset and the first burst. A fault that occurs exactly once per reset and never again is almost always an initialisation-ordering problem rather than a protocol or timing problem.
8. Why a Verification Engineer Cares
CPOL gives the cheapest genuinely useful assertion in the whole timing area, and it is one most environments do not write.
The invariant is about idle, so check idle. Whenever CS is deasserted, SCLK must be at its configured polarity. That single property catches a master parking at the wrong level, a master leaving the clock stranded mid-period after an aborted transfer, and a configuration mismatch between the testbench's expectation and the design's behaviour:
// Sampled on the system clock, so it observes the master's OUTPUT REGISTER,
// not the level at a device's pin. It proves the design's intent, not the
// board's behaviour — see the limitation note below.
property p_cpol_idle;
@(posedge clk) disable iff (!rst_n)
cs_n |-> (sclk == cpol);
endproperty
a_cpol_idle : assert property (p_cpol_idle)
else $error("SCLK = %b while CS deasserted; cpol = %b", sclk, cpol);What it proves and what it does not. It proves the master's clock output holds the configured level throughout every idle period, including after an aborted or truncated transfer. It does not prove anything about the level at the device's pin — output delay and board propagation sit between them, the layering Chapter 2.4 tabulated — and it cannot detect that cpol itself was configured to the wrong value for the attached part. That second gap matters: the assertion checks internal consistency, and only a comparison against the datasheet catches a correct implementation of a wrong setting.
Configuration belongs in a configuration object. As with the resting level in Chapter 2.2 and the CS intervals in Chapter 2.5, cpol is a property of the attached device. An agent that hard-codes it is single-use. Chapter 3.3 collects both bits into one mode field, which is the form a real configuration object carries.
9. Why an FPGA or ASIC Engineer Cares
Reset value is a real design decision, not a default. The clock output register must reset to the configured polarity, not to zero. On an FPGA that means the reset value depends on an input rather than being a constant — trivially synthesisable, but easy to get wrong if the register is inferred with an initial value instead of an explicit reset. On an ASIC the same applies to the flop's reset pin and to what the pad presents during power sequencing, which may need attention independently of the logic.
Changing polarity is an event on the wire. As §3 noted, every device on a shared SCLK net sees the transition when cpol changes. A controller serving devices with different polarity requirements must sequence that change while the bus is idle and no CS is asserted. This is a driver and register-interface concern, and it is a genuine multi-device hazard rather than a theoretical one.
The output path is unchanged. Polarity does not affect the I/O register or output-delay constraint arguments from Chapter 2.1 and Chapter 2.7. It inverts the waveform; it does not move the edges in time.
10. Common Misconceptions
11. Reason It Through
Work this before reading the answers.
A board has two SPI peripherals on shared SCLK, MOSI and MISO with separate selects. The datasheets specify different clock polarities: device A requires the clock to idle low, device B requires it to idle high. A driver configures polarity once at startup — for device A — and then talks to both. Device A works perfectly. Device B works "most of the time," with occasional corrupt transactions.
Why does device B work at all? This is the part worth pausing on. Device B's transaction still contains the right number of edges with the right spacing, and if its sampling behaviour is defined relative to its expectation of the leading edge, a polarity mismatch displaces its sampling by half a bit time — which, as Chapter 2.2 §4 showed, frequently still lands inside a valid data window. Working is the worst outcome here, because it converts a definite misconfiguration into an intermittent fault.
What makes it occasional rather than constant? The displaced sampling point sits closer to a data transition than it should, so it has less margin than a correct configuration. Whether a given bit survives depends on the data pattern, the round-trip delay, temperature and supply. That is the classic margin-dependent intermittency of Chapter 2.4 — but here it has a configuration cause rather than a physical one, which is why lowering the clock will also appear to fix it and will mislead you.
What is the discriminating observation? Capture SCLK and CS during a device-B transaction and read the SCLK level while CS is deasserted — the habit Chapter 2.2 §5 established. If it rests low while device B's datasheet requires it to rest high, the mismatch is confirmed in one measurement, with no reference to the data at all.
What is the correct fix, and what does it cost? The master must reconfigure its clock polarity between transactions — set cpol for whichever device is about to be addressed. The costs are real and worth naming: the reconfiguration must happen while no CS is asserted, because changing the parked level is an edge that every device on the shared net sees; it adds latency between transactions to different devices; and it means the driver must track which polarity is currently programmed. If the resulting transitions between polarities are themselves problematic for either device, the remaining options are to put the two devices on separate SPI buses, or to select parts that agree.
Why is "it's been working, leave it" the wrong conclusion? Because the link is operating outside the device's specification and its correctness depends on margin it was never granted. It will fail on a board with longer traces, at a higher temperature, at a higher clock, or on a different part lot — and the failure will appear long after the change that caused it, on a system nobody associates with clock polarity.
12. Understanding Check
13. Summary
CPOL specifies one thing: the level SCLK rests at when no transfer is in progress. CPOL = 0 idles low, CPOL = 1 idles high. It says nothing about which edge launches or samples — that is the second bit, and keeping the two separate is what makes the four modes tractable rather than arbitrary.
Two consequences follow. The leading edge's direction is fixed: rising when the clock idles low, falling when it idles high — so an implementation written against "rising" rather than "leading" acts at the wrong instant for one polarity. And the requirement applies to idle, which means before CS asserts, through the CS lead and lag intervals, and from reset onward. A master parked at the wrong level is already violating the device's expectation before it transfers a bit.
Because a transfer must start and finish at the idle level, it always contains an even number of edges — two per bit time.
In RTL the change from Chapter 2.1's divider is one line with real consequences: the output register's reset and park value becomes the cpol input rather than a constant, which guarantees both the correct idle level from power-up and that the first edge of every burst leaves that level. The divider still emits physical rise/fall strobes; the mapping to leading and trailing stays in Chapter 2.3's role mapper, which already took a polarity input before polarity had a name.
The cheapest useful check is an idle-level assertion — SCLK equals cpol whenever CS is deasserted — which catches wrong parking and stranded clocks, and which cannot catch a faithful implementation of a wrongly configured value.
14. What Comes Next
You have one bit. Chapter 3.2 — CPHA, Clock Phase names the other: which of the two logical edges carries the sample role, and therefore which carries the launch role. It is a genuinely independent choice from polarity, and the chapter's main work is showing why it is independent — because that independence is what turns two bits into four modes rather than into four special cases.
Browse the path on the SPI curriculum index, or revisit Leading and Trailing Edges for the vocabulary this chapter has now given a configuration bit.
Continue learning
Related tutorials
- Related topic
CPOL/CPHA-Aware Edge Control
All four SPI modes from one datapath: why the launch and capture assignment depends on CPHA alone, why the two phases are not symmetric, and why CPHA=0 needs both a preload and a suppressed final launch.
- Related topic
The Shift-Register Mental Model
The hardware underneath every SPI transfer: two shift registers wired into a ring, one bit leaving each end and one arriving on every enabled edge. Traced cycle by cycle, built in Verilog, SystemVerilog and VHDL, and separated from the meaning a device layers on top.
- Related topic
SCLK Generation, Period, and Frequency
Where SCLK comes from and what one period buys. Dividing a system clock to a bus clock, why the divisor is an integer and what that costs, and how a period in nanoseconds becomes the budget every later timing parameter is spent from.
- Related topic
Transfer Width
SPI has no native word size. What a 12-bit ADC or 16-bit codec requires of a master, why chip select rather than a bit count delimits a frame, and a width-parameterized transfer engine in three HDLs.
