An SPI master generates the clock, shifts a byte out on MOSI while shifting a byte in from MISO, and controls chip select. Supporting all four SPI modes takes only a few lines more than supporting one, if the design is built around leading and trailing edges instead of rising and falling ones. This article builds such a master and looks at what limits its clock speed.

8 min read | Intermediate

What You'll Learn

  • Generate SCLK from a system clock with a divider
  • Shift data out and in on the correct edges for any mode
  • Control chip select across multi-byte transactions
  • Understand the timing margins between master and slave

The Plan

  • A divider counts CLK_DIV system clocks for every half period of SCLK. Each time it expires, SCLK toggles: that is one edge.
  • A byte takes 16 edges. Even-numbered edges (0, 2, ...) are leading edges and odd-numbered edges are trailing edges, whatever CPOL is.
  • For CPHA = 0: put the first bit on MOSI at the start, sample MISO on leading edges, change MOSI on trailing edges. For CPHA = 1: change MOSI on leading edges, sample on trailing edges.
  • Chip select is controlled separately with cs_req, so that several bytes can be sent in one transaction.

The RTL


// SPI master: one byte per transfer, MSB first, any of the four modes.
//   CLK_DIV  system clocks per half SCLK period
//   CPOL     SCLK level when idle
//   CPHA     0: sample on the leading edge, change on the trailing edge
//            1: change on the leading edge, sample on the trailing edge
// cs_req holds the chip select active across several bytes.
module spi_master #(
  parameter CLK_DIV = 4,
  parameter CPOL    = 0,
  parameter CPHA    = 0
) (
  input            clk,
  input            rst,
  input            start,              // begin a byte transfer (when not busy)
  input      [7:0] tx_byte,
  input            cs_req,             // 1: keep chip select active
  output reg       busy,
  output reg       done,               // one-cycle pulse: rx_byte is valid
  output reg [7:0] rx_byte,
  output reg       sclk,
  output reg       mosi,
  input            miso,
  output           cs_n
);
  reg [15:0] div;
  reg [4:0]  edges;                    // SCLK edges so far in this byte (16 per byte)
  reg [7:0]  tx_sh;
  reg [7:0]  rx_sh;

  assign cs_n = !cs_req;

  always @(posedge clk) begin
    if (rst) begin
      busy <= 1'b0;
      done <= 1'b0;
      sclk <= CPOL;
      mosi <= 1'b0;
    end
    else begin
      done <= 1'b0;
      if (!busy) begin
        sclk <= CPOL;
        if (start) begin
          busy  <= 1'b1;
          div   <= 16'd0;
          edges <= 5'd0;
          tx_sh <= tx_byte;
          if (CPHA == 0)
            mosi <= tx_byte[7];        // the first bit must be ready before the first edge
        end
      end
      else if (div == CLK_DIV - 1) begin
        div <= 16'd0;
        if (edges == 5'd16) begin      // all 16 edges done
          busy    <= 1'b0;
          done    <= 1'b1;
          rx_byte <= rx_sh;
        end
        else begin
          sclk  <= ~sclk;
          edges <= edges + 5'd1;
          if (edges[0] == 1'b0) begin  // leading edge
            if (CPHA == 0) begin
              rx_sh <= {rx_sh[6:0], miso};             // sample
            end
            else begin
              mosi  <= tx_sh[7];                       // change
              tx_sh <= {tx_sh[6:0], 1'b0};
            end
          end
          else begin                   // trailing edge
            if (CPHA == 0) begin
              mosi  <= tx_sh[6];                       // change: the next bit
              tx_sh <= {tx_sh[6:0], 1'b0};
            end
            else begin
              rx_sh <= {rx_sh[6:0], miso};             // sample
            end
          end
        end
      end
      else begin
        div <= div + 16'd1;
      end
    end
  end
endmodule

SCLK idles at CPOL while the master is not busy. Shift registers shift left because SPI sends the most significant bit first. done pulses half a clock after the sixteenth edge, when rx_byte holds the byte that came in.

Quick Check

Q: The system clock is 100 MHz and CLK_DIV is 4. What is the SCLK frequency, and how long does one byte take?

Show Answer

Each half period is 4 cycles of 10 ns, so SCLK has an 80 ns period: 12.5 MHz. A byte is 8 periods, 640 ns, plus the half period before done.

Driving It

The testbench wraps the byte interface in a task and holds chip select over a two-byte transaction:


task xfer(input [7:0] b, output [7:0] got);
  begin
    @(posedge clk);
    tx_byte <= b;
    start   <= 1;
    @(posedge clk);
    start   <= 0;
    @(posedge done);
    got = rx_byte;
  end
endtask

task access(input rw, input [1:0] addr, input [7:0] wdata);
  reg [7:0] id;
  reg [7:0] rdata;
  begin
    cs_req <= 1;
    repeat (4) @(posedge clk);
    xfer({rw, 5'b0, addr}, id);
    xfer(rw ? wdata : 8'h00, rdata);
    repeat (2) @(posedge clk);
    cs_req <= 0;
    // ...
  end
endtask
Output
SPI mode 0 (CPOL=0, CPHA=0)
1535  write reg1 = 0x3c   (slave ID 0xa5)
3055  write reg2 = 0xc3   (slave ID 0xa5)
4575  read  reg1 = 0x3c   (slave ID 0xa5)
6095  read  reg2 = 0xc3   (slave ID 0xa5)

The few clock cycles between chip select and the first edge, and after the last edge, give the slave setup and hold time on CS_n. Real devices specify these in their datasheets, often a few nanoseconds to tens of nanoseconds.

What Limits the Clock

The master samples MISO half an SCLK period after the slave changed it. In that half period the slave's output must change, travel across the board and arrive at the master in time. On a PCB with a fast slave that allows tens of MHz. Inside a chip, a slave that oversamples SCLK with its own clock adds a synchronizer delay of two or three of its clock cycles, which must fit in the half period too.

Quick Check

Q: Why does the master sample MISO in the same system clock cycle in which it toggles SCLK, rather than a cycle later?

Show Answer

The sample is taken from MISO as it is just before the edge, which is the value the slave set up half a period earlier. Sampling later would risk seeing the slave's next bit if it reacts quickly to the edge.

Running It in ChipVerify Lab

The example contains this master and the register slave. Change CLK_DIV and the mode, and watch the timing between SCLK edges and MISO changes.

Common Mistakes

Mistake #1: Coding rising and falling instead of leading and trailing

A master written for mode 0 with "sample on rising edge" breaks in mode 2, where the leading edge is falling. Decide in terms of leading and trailing edges and let CPOL pick the direction.

Mistake #2: A glitch on SCLK when changing mode

Changing CPOL while chip select is active creates an extra edge. Change the mode only while idle with chip select high.

Mistake #3: Dropping chip select too early

Raising CS_n in the same cycle as the last edge violates the slave's hold time. Leave a little time after the last edge.

Practice Exercise

Challenge: Twice as Fast

Halve CLK_DIV from 4 to 2 so that SCLK runs at 25 MHz. The slave samples SCLK with the same 100 MHz clock through a two-flip-flop synchronizer. Does it still work?

Hint

Count the system clock cycles from an SCLK edge until the slave reacts, and compare with the 2 cycles until the master's next edge.

Solution
Output
SPI mode 0 (CPOL=0, CPHA=0)
855  write reg1 = 0x3c   (slave ID 0xd2)
1695  write reg2 = 0xc3   (slave ID 0xd2)
2535  read  reg1 = 0x1e   (slave ID 0xd2)
3375  read  reg2 = 0xe1   (slave ID 0xd2)

It fails: the ID arrives as 0xD2 and the reads return wrong values. The slave needs about three system clocks to see an edge and update MISO, longer than the two-cycle half period. An oversampling SPI slave needs a system clock several times faster than SCLK; slaves that must run faster clock their shift register directly from SCLK.

Open the ready-made example in the lab and run the simulation.

Summary

  • A divider toggles SCLK every CLK_DIV system clocks; 16 edges make a byte.
  • Even edges are leading, odd edges trailing; CPHA picks which one samples and which one changes data.
  • For CPHA = 0, the first bit is on MOSI before the first edge.
  • Chip select is held across all bytes of a transaction, with setup and hold time around the clocks.
  • The half period must cover the slave's reaction time; oversampling slaves need a much faster clock.
Did you find what you were looking for?

Quiz

No quiz questions available for this article.