Skip to content
VLSI Mentor

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 cycles
A Mode 0 SPI transfer over three bit times. Chip select asserts while the clock is still idle low, and MOSI immediately presents the first bit. The first rising edge samples that bit. Each falling edge then launches the next bit, and each rising edge samples it. Chip select releases after the clock returns low.CS leadCS leadthree bit timesthree bit timesCS launches b2CS launches b2sample b2sample b2cs_nsclkmosiXXXXt0t1t2t3t4t5t6t7t8t9
Figure 1 — a Mode 0 transfer. The clock idles low; CS asserts and the first bit appears immediately, because the first rising edge will sample it. Subsequent bits are launched on falling edges and sampled on the rising edges that follow.

The 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.

Azvya Education Pvt. Ltd.VLSI Mentor
spi_tx_path.sv — a transmit shift register with the CPHA=0 first-bit launch
   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;
   endmodule

The assertion worth binding is the one that catches the omission this chapter exists to prevent:

Azvya Education Pvt. Ltd.VLSI Mentor
spi_first_bit.sva — under CPHA=0 a bit must be live before the first sampling edge
   // 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.

Azvya Education Pvt. Ltd.VLSI Mentor
spi_tx_path_tb.sv — same word, both phases; checks the sequence and the first-bit timing
   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
Azvya Education Pvt. Ltd.VLSI Mentor
spi_tx_path.v — the same transmit path in Verilog-2001
   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
Azvya Education Pvt. Ltd.VLSI Mentor
spi_tx_path_tb.v — the same both-phases check in Verilog-2001
   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
Azvya Education Pvt. Ltd.VLSI Mentor
spi_tx_path.vhd — the same transmit path in VHDL
   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;
Azvya Education Pvt. Ltd.VLSI Mentor
spi_tx_path_tb.vhd — the same both-phases check in VHDL
   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