Skip to content
VLSI Mentor

SPI · Module 13

Clock Divider and SCLK Generation

A divider that produces SCLK and the two edge strobes a datapath needs: why the strobes must not be called launch and capture, why an odd divisor puts the longer half first, and why SCLK must be a registered output rather than a gated clock.

Chapter 13.3 built a machine that knows when the clock should run. It does not produce one, and producing one is less obvious than it looks.

A divider with a divisor of 5 has to make a period out of five system clocks. Which half gets three of them?

There is a right answer, it depends on what the slave has to do in each half, and a design that picks the other one gives the returning data less time than an even divisor would.

1. SCLK Is Not The Only Output

A divider that produces only SCLK is not enough. The datapath needs to know when each edge happens, one system clock at a time, so it can put a bit on MOSI on one edge and sample MISO on the other. So the divider produces three things:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   sclk        the pin
   edge_a_stb  a one-cycle pulse on the LEADING edge   (SCLK departs from idle)
   edge_b_stb  a one-cycle pulse on the TRAILING edge  (SCLK returns to idle)
   bit_done    a one-cycle pulse when a full period has elapsed

Now notice what is not in that list: launch and capture. Which of the two edges launches a bit and which captures one depends on CPHA, and that mapping is Chapter 13.5's job.

This is not fussiness about naming. If the divider emitted launch_stb and capture_stb, it would need cpha as an input — and a divider that depends on the mode has to be re-verified every time the mode logic changes. Keeping the two blocks independent means the divider's 46-configuration sweep in §6 is valid for all four modes without running it four times.

2. Leading and Trailing, Not Rising and Falling

The strobes are defined relative to the idle level, not to the voltage:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   CPOL = 0   idle low    leading = rising    trailing = falling
   CPOL = 1   idle high   leading = falling   trailing = rising

That definition is what lets CPOL disappear from every block downstream. The divider is the only place in the master that knows which voltage idle is, and it absorbs the whole of CPOL by choosing which way the pin moves first. Everything after it sees "the first edge" and "the second edge", and inverting CPOL inverts the pin and moves nothing else.

The alternative — naming them sclk_rise and sclk_fall and letting the datapath work out which is which — pushes CPOL into the mode logic, which then has four cases instead of two. Chapter 13.5 is entirely about why that matters.

3. The Odd-Divisor Decision

div is the SCLK period measured in system clocks. For an even value the two halves are equal. For an odd value they cannot be, and the remainder has to go somewhere:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   div = 5, first half longer:    ___/‾‾‾\__      3 cycles away, 2 back
   div = 5, second half longer:   ___/‾‾\___      2 cycles away, 3 back

The decision follows from what happens in each half. In every mode, one edge launches a bit and the other captures one, and the interval between them is what the round trip has to fit into:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   launch edge
      |
      |<--- slave's t_V, plus the round-trip delay --->|
      |                                               |
      +-----------------------------------------------+
                                            capture edge

The launch edge is either the leading edge (CPHA=1) or the trailing edge (CPHA=0). In both cases the capture edge is the other one, and the interval from launch to capture is one of the two halves. So the question becomes: which half is the launch-to-capture interval?

For CPHA=1, launch is on the leading edge and capture on the trailing, so launch-to-capture is the first half. For CPHA=0 it is the second half of one period into the first half of the next — which is a full half-period either way.

Since the first half is the launch-to-capture interval in the CPHA=1 case and neither case is disadvantaged by a longer first half, the first half gets the remainder:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   half_a = ceil(div / 2)       the launch-to-capture half in CPHA=1
   half_b = floor(div / 2)

Choosing the other way would give the returning data less time at div=5 than it gets at div=4, which is the wrong direction for a parameter whose whole purpose is to slow the interface down.

4. The Block

A clock divider. A divisor input feeds two combinational computations: ceiling of div over two for the first half, and floor of div over two for the second. A phase bit selects which half the counter is currently timing. The counter compares against the selected half and produces a terminal count that flips the phase bit. Phase transitions drive a registered SCLK output plus a leading edge strobe, a trailing edge strobe, and a bit-done strobe. A divisor below two sets an error output and holds SCLK at its idle level.divperiod in system clocksHalf computationceil and floor — a shift,not a divideshift_enfrom 13.7 — clock runs ornotdiv_errbelow two: report, do notimprovisePhase bitwhich half is being timedCountercompare against theselected halfSCLK registera flop, never a gateEdge strobesleading, trailing, bit donehalf_a, half_bpublished, so they can becheckedSCLK pinrests at CPOL when disabledlimits< 2enablewhich limitterminal countedge and periodhold idle12
Figure 1 — the divider. One counter and one phase bit produce the pin and three strobes; the phase bit is what selects SCLK's level, and the counter times how long it holds it. The halves are computed combinationally from the divisor, and both are published so a consumer — or a testbench — can compare them against what the waveform actually did. A divisor below two is reported rather than improvised.

5. What The Waveform Looks Like

Twenty cycles across five rows at a divisor of five. SCLK is low for two cycles, then high for three, then low for two, repeating. A leading-edge strobe pulses once per period on the cycle after SCLK rises. A trailing-edge strobe pulses once per period on the cycle after SCLK falls. A bit-done strobe pulses once per period at the end of the second half. A half row labels which half is being timed.leading edge — 3 cycles of half Aleading edge — 3 cycles ofhalf Aperiod complete, not at the edgeperiod complete, not at theedgetrailing edge — half B is 2trailing edge — half B is 2sclkhalfAAABBAAABBAAABBAAABBedge_aedge_bbit_donet0t1t2t3t4t5t6t7t8t9t10t11t12t13t14t15t16t17t18t19
Figure 2 — a divisor of five, CPOL=0. The first half is three cycles and the second is two, so the leading-to-trailing interval — the one the slave's output-valid time has to fit into when CPHA=1 — is the longer of the two. The strobes are one cycle wide and land the cycle after the pin moves, because SCLK is a registered output.

Two things in that figure are worth reading carefully.

bit_done is not at the trailing edge. It fires at the end of the second half, which is half a period after the trailing edge. A consumer that counted trailing edges as periods would start the next bit half a period early, and at a divisor of 2 that is one system clock — which usually still works and occasionally does not.

The strobes land one cycle after the pin moves. SCLK is a registered output, so the pin changes at the clock edge that sets the register, and the strobe is registered on the same edge and therefore high during the following cycle. Every consumer in this module accounts for that, and the monitors in the testbenches compare strobes against the pin one cycle apart for the same reason.

6. Building the Divider — Three HDLs

The circuit

One counter, one phase bit, four registered outputs. Three decisions are embedded in it and each was a bug at some point:

SCLK is a register, not a gated clock. sclk is the output of a flop whose value is set by the phase logic. It is never clk & something, never a clock-tree signal, and nothing in this design is clocked on it. That is what makes the block synthesisable without any clock-gating cells or constraints, and it is why the master has exactly one clock domain.

When disabled, the counter is held ready rather than left where it stopped. On en deasserting, the counter and phase bit are cleared so that the first edge after re-enabling is a full half-period away. Leaving the counter where it stopped means the first bit of the next frame has a random-length first half, which is a real timing violation that depends on how long the previous frame was.

A divisor below two holds the line idle and reports. There is no correct clock for div of 0 or 1, and improvising one — toggling every cycle, say — produces a clock the slave will sample and a design that has silently reinterpreted its configuration. Holding SCLK at its idle level means the device sees no clock at all, which is a state every slave handles, and div_err makes the cause visible.

Azvya Education Pvt. Ltd.VLSI Mentor
spi_clkdiv_strobe.sv — one counter, one phase bit, and the odd-divisor decision
// spi_clkdiv_strobe.sv
//
// Chapter 13.4 -- the divider, and the two strobes it really has to produce.
//
// A divider that only produces SCLK is not enough. The datapath needs to know
// WHEN each edge happens, one system clock at a time, so it can launch a bit
// on one and capture on the other. So this block emits three things:
//
//   sclk        the pin
//   edge_a_stb  a one-cycle pulse on the LEADING edge  (away from idle)
//   edge_b_stb  a one-cycle pulse on the TRAILING edge (back to idle)
//
// Note what it does NOT emit: launch and capture. Which of a and b launches
// depends on CPHA, and that mapping is Chapter 13.5's job. Putting it here
// would mean this block needed the mode, and a divider that needs the mode is
// a divider that has to be re-verified when the mode logic changes.
//
// THE ODD-DIVISOR DECISION. `div` is the SCLK period in system clocks, and
// for an odd value the two halves cannot be equal. The choice made here is
// that the FIRST half is the longer one:
//
//   div = 5  ->  3 cycles from edge A, then 2 cycles to edge B
//
// That is not arbitrary. The interval between the launching edge and the
// capturing edge is what the slave's t_V and the round trip have to fit into
// (Chapter 9.4), so the longer half belongs there. Choosing the other way
// would give the returning data LESS time than an even divisor would, which
// is the opposite of what an odd divisor should cost.

module spi_clkdiv_strobe #(
    parameter int DIV_W = 8
) (
    input  logic             clk,
    input  logic             rst_n,

    input  logic             en,       // the FSM's shift_en
    input  logic [DIV_W-1:0] div,      // SCLK period in clk cycles, >= 2
    input  logic             cpol,     // the idle level

    output logic             sclk,
    output logic             edge_a_stb,   // leading edge, away from idle
    output logic             edge_b_stb,   // trailing edge, back to idle
    output logic             bit_done,     // one full SCLK period elapsed

    output wire [DIV_W-1:0]  half_a,       // the two half-period lengths,
    output wire [DIV_W-1:0]  half_b,       // published so they can be checked
    output wire              div_err       // div < 2 is not a divider
);

    // The halves. Integer division by two is a shift, and the remainder goes
    // to the FIRST half -- the decision argued for in the header.
    assign half_a  = (div + {{(DIV_W-1){1'b0}}, 1'b1}) >> 1;   // ceil(div/2)
    assign half_b  = div >> 1;                                  // floor(div/2)
    assign div_err = (div < {{(DIV_W-2){1'b0}}, 2'b10});

    logic [DIV_W-1:0] cnt;
    logic             in_b;     // in the second half of the period

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt        <= {DIV_W{1'b0}};
            in_b       <= 1'b0;
            sclk       <= 1'b0;
            edge_a_stb <= 1'b0;
            edge_b_stb <= 1'b0;
            bit_done   <= 1'b0;
        end else begin
            edge_a_stb <= 1'b0;
            edge_b_stb <= 1'b0;
            bit_done   <= 1'b0;

            if (!en) begin
                // Not shifting: SCLK rests at the idle level and the counter
                // is held ready, so the first edge after enable is a full
                // half-period away rather than wherever the counter happened
                // to stop.
                sclk <= cpol;
                cnt  <= {DIV_W{1'b0}};
                in_b <= 1'b0;
            end else if (div_err) begin
                // A divisor below two cannot produce an SCLK period at all.
                // Holding the line idle is the safe response: a device sees no
                // clock rather than a malformed one.
                sclk <= cpol;
            end else if (!in_b) begin
                // First half: SCLK is away from idle.
                if (cnt == {DIV_W{1'b0}}) begin
                    sclk       <= ~cpol;
                    edge_a_stb <= 1'b1;
                end
                if (cnt >= half_a - {{(DIV_W-1){1'b0}}, 1'b1}) begin
                    cnt  <= {DIV_W{1'b0}};
                    in_b <= 1'b1;
                end else begin
                    cnt <= cnt + 1'b1;
                end
            end else begin
                // Second half: SCLK returns to idle.
                if (cnt == {DIV_W{1'b0}}) begin
                    sclk       <= cpol;
                    edge_b_stb <= 1'b1;
                end
                if (cnt >= half_b - {{(DIV_W-1){1'b0}}, 1'b1}) begin
                    cnt      <= {DIV_W{1'b0}};
                    in_b     <= 1'b0;
                    // The period is complete at the END of the second half,
                    // not at the trailing edge -- a consumer that counted
                    // trailing edges would start the next bit half a period
                    // early.
                    bit_done <= 1'b1;
                end else begin
                    cnt <= cnt + 1'b1;
                end
            end
        end
    end

endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
spi_clkdiv_strobe_tb.sv — halves measured from the strobes, not computed
// spi_clkdiv_strobe_tb.sv
//
// Every interval here is MEASURED from the waveform the divider produces,
// not inferred from its parameters -- because the parameters are what is
// under test. The checks then assert relationships that must hold for every
// divisor: the halves sum to the period, each strobe fires exactly once per
// period, and the first half is never shorter than the second.

`timescale 1ns/1ps

module spi_clkdiv_strobe_tb;

    localparam int DIV_W = 8;

    logic clk = 1'b0;
    logic rst_n = 1'b0;
    always #5 clk = ~clk;

    logic             en = 1'b0;
    logic [DIV_W-1:0] div = 8'd4;
    logic             cpol = 1'b0;

    wire              sclk, edge_a_stb, edge_b_stb, bit_done;
    wire [DIV_W-1:0]  half_a, half_b;
    wire              div_err;

    int errors = 0;
    int d, k;
    int m_half_a, m_half_b, m_period;
    int n_a, n_b, n_done, n_periods;
    int since_a, since_b;
    int idle_wrong;
    // SCLK is a REGISTERED output, so it necessarily takes one clock to
    // follow a change of en or cpol. That one-cycle lag is the property that
    // makes it glitch-free, so the idle check must allow for it rather than
    // treat it as a fault.
    logic en_q, cpol_q;

    spi_clkdiv_strobe #(.DIV_W(DIV_W)) dut (
        .clk(clk), .rst_n(rst_n),
        .en(en), .div(div), .cpol(cpol),
        .sclk(sclk), .edge_a_stb(edge_a_stb), .edge_b_stb(edge_b_stb),
        .bit_done(bit_done),
        .half_a(half_a), .half_b(half_b), .div_err(div_err)
    );

    // Measure the halves from the strobes themselves: A to B is the first
    // half, B to the next A is the second.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            n_a <= 0; n_b <= 0; n_done <= 0; n_periods <= 0;
            since_a <= 0; since_b <= 0;
            m_half_a <= 0; m_half_b <= 0; m_period <= 0;
            idle_wrong <= 0;
        end else begin
            if (edge_a_stb) begin
                n_a      <= n_a + 1;
                since_a  <= 1;
                m_half_b <= since_b;        // B .. A is the second half
                m_period <= since_b + m_half_a;
            end else begin
                since_a <= since_a + 1;
            end

            if (edge_b_stb) begin
                n_b      <= n_b + 1;
                since_b  <= 1;
                m_half_a <= since_a;        // A .. B is the first half
            end else begin
                since_b <= since_b + 1;
            end

            if (bit_done)  n_done    <= n_done + 1;
            if (bit_done)  n_periods <= n_periods + 1;

            // While disabled the line must rest at the idle level -- checked
            // only once en and cpol have both been stable for a cycle.
            en_q   <= en;
            cpol_q <= cpol;
            if (!en && !en_q && (cpol == cpol_q) && (sclk !== cpol))
                idle_wrong <= idle_wrong + 1;
        end
    end

    task automatic run_div(input int dv, input bit pol, input int periods);
        begin
            @(negedge clk);
            en = 1'b0; div = DIV_W'(dv); cpol = pol;
            repeat (3) @(negedge clk);
            n_a = 0; n_b = 0; n_done = 0;
            en = 1'b1;
            // Run for the requested number of periods plus slack.
            repeat (dv * periods + dv + 4) @(negedge clk);
            en = 1'b0;
            repeat (3) @(negedge clk);
        end
    endtask

    initial begin
        n_a = 0; n_b = 0; n_done = 0; n_periods = 0;
        since_a = 0; since_b = 0;
        m_half_a = 0; m_half_b = 0; m_period = 0; idle_wrong = 0;
        en_q = 1'b0; cpol_q = 1'b0;

        repeat (3) @(negedge clk);
        rst_n = 1'b1;
        @(negedge clk);

        // 1. THE IDLE LEVEL. Disabled, SCLK rests at CPOL -- for both
        //    polarities, because a divider that only ever rests low passes a
        //    CPOL=0 test and fails silently on a mode-3 device.
        cpol = 1'b0; repeat (4) @(negedge clk);
        if (sclk !== 1'b0) begin
            $display("  FAIL: disabled with CPOL=0, SCLK is %0b", sclk);
            errors++;
        end
        cpol = 1'b1; repeat (4) @(negedge clk);
        if (sclk !== 1'b1) begin
            $display("  FAIL: disabled with CPOL=1, SCLK is %0b", sclk);
            errors++;
        end
        cpol = 1'b0;
        $display("  idle: SCLK rests at CPOL for both polarities");

        // 2. AN EVEN DIVISOR. Equal halves, and the period is the divisor.
        run_div(4, 1'b0, 6);
        if (m_half_a != 2 || m_half_b != 2 || m_period != 4) begin
            $display("  FAIL: div=4 measured halves %0d/%0d, period %0d",
                     m_half_a, m_half_b, m_period);
            errors++;
        end
        $display("  div=4:  halves %0d/%0d, period %0d, %0d A-edges %0d B-edges %0d bit_done",
                 m_half_a, m_half_b, m_period, n_a, n_b, n_done);

        // 3. AN ODD DIVISOR. The halves differ by one and the FIRST is the
        //    longer -- the decision the header argues for, checked rather
        //    than assumed.
        run_div(5, 1'b0, 6);
        if (m_half_a != 3 || m_half_b != 2 || m_period != 5) begin
            $display("  FAIL: div=5 measured halves %0d/%0d, period %0d",
                     m_half_a, m_half_b, m_period);
            errors++;
        end
        if (m_half_a <= m_half_b) begin
            $display("  FAIL: on an odd divisor the first half is not the longer one");
            errors++;
        end
        $display("  div=5:  halves %0d/%0d -- the launch-to-capture half is the longer one",
                 m_half_a, m_half_b);

        // 4. THE SMALLEST LEGAL DIVISOR. div=2 toggles every system clock,
        //    which is the fastest SCLK a synchronous divider can make.
        run_div(2, 1'b0, 8);
        if (m_half_a != 1 || m_half_b != 1 || m_period != 2) begin
            $display("  FAIL: div=2 measured halves %0d/%0d, period %0d",
                     m_half_a, m_half_b, m_period);
            errors++;
        end
        if (div_err) begin
            $display("  FAIL: div=2 was reported as an error"); errors++;
        end
        $display("  div=2:  halves %0d/%0d -- the fastest a synchronous divider can produce",
                 m_half_a, m_half_b);

        // 5. AN ILLEGAL DIVISOR. Below two there is no period to produce, so
        //    it is reported and the line is held idle rather than toggling at
        //    some improvised rate.
        @(negedge clk); div = 8'd1; cpol = 1'b0; en = 1'b1;
        repeat (8) @(negedge clk);
        if (!div_err) begin
            $display("  FAIL: div=1 was not reported as illegal"); errors++;
        end
        if (sclk !== 1'b0) begin
            $display("  FAIL: an illegal divisor produced a clock"); errors++;
        end
        @(negedge clk); div = 8'd0;
        repeat (8) @(negedge clk);
        if (!div_err || sclk !== 1'b0) begin
            $display("  FAIL: div=0 was not held idle and reported"); errors++;
        end
        en = 1'b0; div = 8'd4;
        repeat (3) @(negedge clk);
        $display("  div<2:  reported, and the line is held idle rather than improvised");

        // 6. STROBE COUNTS. Each strobe fires exactly once per period, and
        //    bit_done once per period too -- so a consumer counting any of
        //    the three counts the same number of bits.
        run_div(6, 1'b0, 10);
        // A run does not stop on a period boundary, so the counts are not
        // equal -- they are in STEP. Each period produces A then B then done,
        // so at any instant the three counts differ by at most one and never
        // go out of order. That is the real invariant, and it holds at every
        // cycle rather than only at boundaries.
        if ((n_a - n_b) > 1 || (n_a - n_b) < 0 ||
            (n_b - n_done) > 1 || (n_b - n_done) < 0) begin
            $display("  FAIL: strobe counts out of step -- A=%0d B=%0d done=%0d",
                     n_a, n_b, n_done);
            errors++;
        end
        if (n_done < 9) begin
            $display("  FAIL: only %0d periods in a run of 10", n_done);
            errors++;
        end
        $display("  strobes: A=%0d B=%0d bit_done=%0d -- in step, one of each per period",
                 n_a, n_b, n_done);

        // 7. THE SWEEP. For every divisor from 2 to 24, both polarities, the
        //    measured halves must match the published ones and sum to the
        //    period. This is the relationship that must hold for every
        //    divisor rather than for the three that were checked by hand.
        for (d = 2; d <= 24; d++) begin
            for (k = 0; k < 2; k++) begin
                run_div(d, k[0], 4);
                if (m_period != d) begin
                    $display("  FAIL: div=%0d cpol=%0b measured a period of %0d",
                             d, k[0], m_period);
                    errors++;
                end
                if ((m_half_a + m_half_b) != d) begin
                    $display("  FAIL: div=%0d halves %0d+%0d do not sum to the period",
                             d, m_half_a, m_half_b);
                    errors++;
                end
                if (m_half_a != int'(half_a) || m_half_b != int'(half_b)) begin
                    $display("  FAIL: div=%0d measured %0d/%0d but published %0d/%0d",
                             d, m_half_a, m_half_b, half_a, half_b);
                    errors++;
                end
                if (m_half_a < m_half_b) begin
                    $display("  FAIL: div=%0d first half %0d is shorter than second %0d",
                             d, m_half_a, m_half_b);
                    errors++;
                end
                if ((n_a - n_b) > 1 || (n_a - n_b) < 0 ||
                    (n_b - n_done) > 1 || (n_b - n_done) < 0) begin
                    $display("  FAIL: div=%0d strobe counts out of step (A=%0d B=%0d done=%0d)",
                             d, n_a, n_b, n_done);
                    errors++;
                end
            end
        end
        $display("  46 (divisor, polarity) pairs swept: measured halves match the published ones, sum to the period, and the first is never shorter");

        // 8. THE LINE NEVER MOVED WHILE DISABLED, across everything above.
        if (idle_wrong != 0) begin
            $display("  FAIL: SCLK left the idle level on %0d disabled cycles",
                     idle_wrong);
            errors++;
        end
        $display("  disabled: 0 cycles on which SCLK left the idle level");

        if (errors == 0)
            $display("PASS: SCLK rests at CPOL for both polarities whenever the divider is disabled and never left it once, an even divisor gives equal halves and an odd one gives a longer FIRST half so the launch-to-capture interval is the longer of the two, the smallest legal divisor of two toggles every system clock, a divisor below two is reported and the line held idle rather than improvised, and across 46 divisor and polarity pairs the measured halves match the published ones, sum to the period, and produce exactly one A strobe, one B strobe and one bit_done per period");
        else
            $display("FAIL: %0d error(s)", errors);
        $finish;
    end

endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
spi_clkdiv_strobe.v — the same divider in Verilog-2001
// spi_clkdiv_strobe.v
//
// Chapter 13.4 -- the divider, and the two strobes it really has to produce.
//
// A divider that only produces SCLK is not enough. The datapath needs to know
// WHEN each edge happens, one system clock at a time, so it can launch a bit
// on one and capture on the other. So this block emits three things:
//
//   sclk        the pin
//   edge_a_stb  a one-cycle pulse on the LEADING edge  (away from idle)
//   edge_b_stb  a one-cycle pulse on the TRAILING edge (back to idle)
//
// Note what it does NOT emit: launch and capture. Which of a and b launches
// depends on CPHA, and that mapping is Chapter 13.5's job. Putting it here
// would mean this block needed the mode, and a divider that needs the mode is
// a divider that has to be re-verified when the mode logic changes.
//
// THE ODD-DIVISOR DECISION. `div` is the SCLK period in system clocks, and
// for an odd value the two halves cannot be equal. The choice made here is
// that the FIRST half is the longer one:
//
//   div = 5  ->  3 cycles from edge A, then 2 cycles to edge B
//
// That is not arbitrary. The interval between the launching edge and the
// capturing edge is what the slave's t_V and the round trip have to fit into
// (Chapter 9.4), so the longer half belongs there. Choosing the other way
// would give the returning data LESS time than an even divisor would, which
// is the opposite of what an odd divisor should cost.

module spi_clkdiv_strobe #(
    parameter DIV_W = 8
) (
    input  wire              clk,
    input  wire              rst_n,

    input  wire              en,       // the FSM's shift_en
    input  wire  [DIV_W-1:0] div,      // SCLK period in clk cycles, >= 2
    input  wire              cpol,     // the idle level

    output reg               sclk,
    output reg               edge_a_stb,   // leading edge, away from idle
    output reg               edge_b_stb,   // trailing edge, back to idle
    output reg               bit_done,     // one full SCLK period elapsed

    output wire [DIV_W-1:0]  half_a,       // the two half-period lengths,
    output wire [DIV_W-1:0]  half_b,       // published so they can be checked
    output wire              div_err       // div < 2 is not a divider
);

    // The halves. Integer division by two is a shift, and the remainder goes
    // to the FIRST half -- the decision argued for in the header.
    assign half_a  = (div + {{(DIV_W-1){1'b0}}, 1'b1}) >> 1;   // ceil(div/2)
    assign half_b  = div >> 1;                                  // floor(div/2)
    assign div_err = (div < {{(DIV_W-2){1'b0}}, 2'b10});

    reg [DIV_W-1:0] cnt;
    reg             in_b;     // in the second half of the period

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cnt        <= {DIV_W{1'b0}};
            in_b       <= 1'b0;
            sclk       <= 1'b0;
            edge_a_stb <= 1'b0;
            edge_b_stb <= 1'b0;
            bit_done   <= 1'b0;
        end else begin
            edge_a_stb <= 1'b0;
            edge_b_stb <= 1'b0;
            bit_done   <= 1'b0;

            if (!en) begin
                // Not shifting: SCLK rests at the idle level and the counter
                // is held ready, so the first edge after enable is a full
                // half-period away rather than wherever the counter happened
                // to stop.
                sclk <= cpol;
                cnt  <= {DIV_W{1'b0}};
                in_b <= 1'b0;
            end else if (div_err) begin
                // A divisor below two cannot produce an SCLK period at all.
                // Holding the line idle is the safe response: a device sees no
                // clock rather than a malformed one.
                sclk <= cpol;
            end else if (!in_b) begin
                // First half: SCLK is away from idle.
                if (cnt == {DIV_W{1'b0}}) begin
                    sclk       <= ~cpol;
                    edge_a_stb <= 1'b1;
                end
                if (cnt >= half_a - {{(DIV_W-1){1'b0}}, 1'b1}) begin
                    cnt  <= {DIV_W{1'b0}};
                    in_b <= 1'b1;
                end else begin
                    cnt <= cnt + 1'b1;
                end
            end else begin
                // Second half: SCLK returns to idle.
                if (cnt == {DIV_W{1'b0}}) begin
                    sclk       <= cpol;
                    edge_b_stb <= 1'b1;
                end
                if (cnt >= half_b - {{(DIV_W-1){1'b0}}, 1'b1}) begin
                    cnt      <= {DIV_W{1'b0}};
                    in_b     <= 1'b0;
                    // The period is complete at the END of the second half,
                    // not at the trailing edge -- a consumer that counted
                    // trailing edges would start the next bit half a period
                    // early.
                    bit_done <= 1'b1;
                end else begin
                    cnt <= cnt + 1'b1;
                end
            end
        end
    end

endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
spi_clkdiv_strobe_tb.v — the same 46-pair sweep in Verilog-2001
// spi_clkdiv_strobe_tb.v
//
// Every interval here is MEASURED from the waveform the divider produces,
// not inferred from its parameters -- because the parameters are what is
// under test. The checks then assert relationships that must hold for every
// divisor: the halves sum to the period, each strobe fires exactly once per
// period, and the first half is never shorter than the second.

`timescale 1ns/1ps

module spi_clkdiv_strobe_tb;

    localparam DIV_W = 8;

    reg clk;
    reg rst_n;
    always #5 clk = ~clk;

    reg             en;
    reg [DIV_W-1:0] div;
    reg             cpol;

    wire              sclk, edge_a_stb, edge_b_stb, bit_done;
    wire [DIV_W-1:0]  half_a, half_b;
    wire              div_err;

    integer errors;
    integer d, k;
    integer m_half_a, m_half_b, m_period;
    integer n_a, n_b, n_done, n_periods;
    integer since_a, since_b;
    integer idle_wrong;
    // SCLK is a REGISTERED output, so it necessarily takes one clock to
    // follow a change of en or cpol. That one-cycle lag is the property that
    // makes it glitch-free, so the idle check must allow for it rather than
    // treat it as a fault.
    reg en_q, cpol_q;

    spi_clkdiv_strobe #(.DIV_W(DIV_W)) dut (
        .clk(clk), .rst_n(rst_n),
        .en(en), .div(div), .cpol(cpol),
        .sclk(sclk), .edge_a_stb(edge_a_stb), .edge_b_stb(edge_b_stb),
        .bit_done(bit_done),
        .half_a(half_a), .half_b(half_b), .div_err(div_err)
    );

    // Measure the halves from the strobes themselves: A to B is the first
    // half, B to the next A is the second.
    always @(posedge clk) begin
        if (!rst_n) begin
            n_a <= 0; n_b <= 0; n_done <= 0; n_periods <= 0;
            since_a <= 0; since_b <= 0;
            m_half_a <= 0; m_half_b <= 0; m_period <= 0;
            idle_wrong <= 0;
        end else begin
            if (edge_a_stb) begin
                n_a      <= n_a + 1;
                since_a  <= 1;
                m_half_b <= since_b;        // B .. A is the second half
                m_period <= since_b + m_half_a;
            end else begin
                since_a <= since_a + 1;
            end

            if (edge_b_stb) begin
                n_b      <= n_b + 1;
                since_b  <= 1;
                m_half_a <= since_a;        // A .. B is the first half
            end else begin
                since_b <= since_b + 1;
            end

            if (bit_done)  n_done    <= n_done + 1;
            if (bit_done)  n_periods <= n_periods + 1;

            // While disabled the line must rest at the idle level -- checked
            // only once en and cpol have both been stable for a cycle.
            en_q   <= en;
            cpol_q <= cpol;
            if (!en && !en_q && (cpol == cpol_q) && (sclk !== cpol))
                idle_wrong <= idle_wrong + 1;
        end
    end

        task run_div;
        input integer dv;
        input pol;
        input integer periods;
        begin
            @(negedge clk);
            en = 1'b0; div = (dv); cpol = pol;
            repeat (3) @(negedge clk);
            n_a = 0; n_b = 0; n_done = 0;
            en = 1'b1;
            // Run for the requested number of periods plus slack.
            repeat (dv * periods + dv + 4) @(negedge clk);
            en = 1'b0;
            repeat (3) @(negedge clk);
        end
    endtask

    initial begin
        n_a = 0; n_b = 0; n_done = 0; n_periods = 0;
        since_a = 0; since_b = 0;
        m_half_a = 0; m_half_b = 0; m_period = 0; idle_wrong = 0;
        en_q = 1'b0; cpol_q = 1'b0;

        repeat (3) @(negedge clk);
        rst_n = 1'b1;
        @(negedge clk);

        // 1. THE IDLE LEVEL. Disabled, SCLK rests at CPOL -- for both
        //    polarities, because a divider that only ever rests low passes a
        //    CPOL=0 test and fails silently on a mode-3 device.
        cpol = 1'b0; repeat (4) @(negedge clk);
        if (sclk !== 1'b0) begin
            $display("  FAIL: disabled with CPOL=0, SCLK is %0b", sclk);
            errors = errors + 1;
        end
        cpol = 1'b1; repeat (4) @(negedge clk);
        if (sclk !== 1'b1) begin
            $display("  FAIL: disabled with CPOL=1, SCLK is %0b", sclk);
            errors = errors + 1;
        end
        cpol = 1'b0;
        $display("  idle: SCLK rests at CPOL for both polarities");

        // 2. AN EVEN DIVISOR. Equal halves, and the period is the divisor.
        run_div(4, 1'b0, 6);
        if (m_half_a != 2 || m_half_b != 2 || m_period != 4) begin
            $display("  FAIL: div=4 measured halves %0d/%0d, period %0d",
                     m_half_a, m_half_b, m_period);
            errors = errors + 1;
        end
        $display("  div=4:  halves %0d/%0d, period %0d, %0d A-edges %0d B-edges %0d bit_done",
                 m_half_a, m_half_b, m_period, n_a, n_b, n_done);

        // 3. AN ODD DIVISOR. The halves differ by one and the FIRST is the
        //    longer -- the decision the header argues for, checked rather
        //    than assumed.
        run_div(5, 1'b0, 6);
        if (m_half_a != 3 || m_half_b != 2 || m_period != 5) begin
            $display("  FAIL: div=5 measured halves %0d/%0d, period %0d",
                     m_half_a, m_half_b, m_period);
            errors = errors + 1;
        end
        if (m_half_a <= m_half_b) begin
            $display("  FAIL: on an odd divisor the first half is not the longer one");
            errors = errors + 1;
        end
        $display("  div=5:  halves %0d/%0d -- the launch-to-capture half is the longer one",
                 m_half_a, m_half_b);

        // 4. THE SMALLEST LEGAL DIVISOR. div=2 toggles every system clock,
        //    which is the fastest SCLK a synchronous divider can make.
        run_div(2, 1'b0, 8);
        if (m_half_a != 1 || m_half_b != 1 || m_period != 2) begin
            $display("  FAIL: div=2 measured halves %0d/%0d, period %0d",
                     m_half_a, m_half_b, m_period);
            errors = errors + 1;
        end
        if (div_err) begin
            $display("  FAIL: div=2 was reported as an error"); errors = errors + 1;
        end
        $display("  div=2:  halves %0d/%0d -- the fastest a synchronous divider can produce",
                 m_half_a, m_half_b);

        // 5. AN ILLEGAL DIVISOR. Below two there is no period to produce, so
        //    it is reported and the line is held idle rather than toggling at
        //    some improvised rate.
        @(negedge clk); div = 8'd1; cpol = 1'b0; en = 1'b1;
        repeat (8) @(negedge clk);
        if (!div_err) begin
            $display("  FAIL: div=1 was not reported as illegal"); errors = errors + 1;
        end
        if (sclk !== 1'b0) begin
            $display("  FAIL: an illegal divisor produced a clock"); errors = errors + 1;
        end
        @(negedge clk); div = 8'd0;
        repeat (8) @(negedge clk);
        if (!div_err || sclk !== 1'b0) begin
            $display("  FAIL: div=0 was not held idle and reported"); errors = errors + 1;
        end
        en = 1'b0; div = 8'd4;
        repeat (3) @(negedge clk);
        $display("  div<2:  reported, and the line is held idle rather than improvised");

        // 6. STROBE COUNTS. Each strobe fires exactly once per period, and
        //    bit_done once per period too -- so a consumer counting any of
        //    the three counts the same number of bits.
        run_div(6, 1'b0, 10);
        // A run does not stop on a period boundary, so the counts are not
        // equal -- they are in STEP. Each period produces A then B then done,
        // so at any instant the three counts differ by at most one and never
        // go out of order. That is the real invariant, and it holds at every
        // cycle rather than only at boundaries.
        if ((n_a - n_b) > 1 || (n_a - n_b) < 0 ||
            (n_b - n_done) > 1 || (n_b - n_done) < 0) begin
            $display("  FAIL: strobe counts out of step -- A=%0d B=%0d done=%0d",
                     n_a, n_b, n_done);
            errors = errors + 1;
        end
        if (n_done < 9) begin
            $display("  FAIL: only %0d periods in a run of 10", n_done);
            errors = errors + 1;
        end
        $display("  strobes: A=%0d B=%0d bit_done=%0d -- in step, one of each per period",
                 n_a, n_b, n_done);

        // 7. THE SWEEP. For every divisor from 2 to 24, both polarities, the
        //    measured halves must match the published ones and sum to the
        //    period. This is the relationship that must hold for every
        //    divisor rather than for the three that were checked by hand.
        for (d = 2; d <= 24; d = d + 1) begin
            for (k = 0; k < 2; k = k + 1) begin
                run_div(d, k[0], 4);
                if (m_period != d) begin
                    $display("  FAIL: div=%0d cpol=%0b measured a period of %0d",
                             d, k[0], m_period);
                    errors = errors + 1;
                end
                if ((m_half_a + m_half_b) != d) begin
                    $display("  FAIL: div=%0d halves %0d+%0d do not sum to the period",
                             d, m_half_a, m_half_b);
                    errors = errors + 1;
                end
                if (m_half_a != (half_a) || m_half_b != (half_b)) begin
                    $display("  FAIL: div=%0d measured %0d/%0d but published %0d/%0d",
                             d, m_half_a, m_half_b, half_a, half_b);
                    errors = errors + 1;
                end
                if (m_half_a < m_half_b) begin
                    $display("  FAIL: div=%0d first half %0d is shorter than second %0d",
                             d, m_half_a, m_half_b);
                    errors = errors + 1;
                end
                if ((n_a - n_b) > 1 || (n_a - n_b) < 0 ||
                    (n_b - n_done) > 1 || (n_b - n_done) < 0) begin
                    $display("  FAIL: div=%0d strobe counts out of step (A=%0d B=%0d done=%0d)",
                             d, n_a, n_b, n_done);
                    errors = errors + 1;
                end
            end
        end
        $display("  46 (divisor, polarity) pairs swept: measured halves match the published ones, sum to the period, and the first is never shorter");

        // 8. THE LINE NEVER MOVED WHILE DISABLED, across everything above.
        if (idle_wrong != 0) begin
            $display("  FAIL: SCLK left the idle level on %0d disabled cycles",
                     idle_wrong);
            errors = errors + 1;
        end
        $display("  disabled: 0 cycles on which SCLK left the idle level");

        if (errors == 0)
            $display("PASS: SCLK rests at CPOL for both polarities whenever the divider is disabled and never left it once, an even divisor gives equal halves and an odd one gives a longer FIRST half so the launch-to-capture interval is the longer of the two, the smallest legal divisor of two toggles every system clock, a divisor below two is reported and the line held idle rather than improvised, and across 46 divisor and polarity pairs the measured halves match the published ones, sum to the period, and produce exactly one A strobe, one B strobe and one bit_done per period");
        else
            $display("FAIL: %0d error(s)", errors);
        $finish;
    end


    initial begin
        clk = 1'b0;
        rst_n = 1'b0;
        en = 1'b0;
        div = 8'd4;
        cpol = 1'b0;
        errors = 0;
    end

endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
spi_clkdiv_strobe.vhd — the same divider in VHDL
-- spi_clkdiv_strobe.vhd
--
-- Chapter 13.4 -- the divider, and the two strobes it really has to produce,
-- in VHDL.
--
-- A divider that only produces SCLK is not enough. The datapath needs to know
-- WHEN each edge happens, one system clock at a time, so it can launch a bit
-- on one and capture on the other:
--
--   sclk        the pin
--   edge_a_stb  a one-cycle pulse on the LEADING edge  (away from idle)
--   edge_b_stb  a one-cycle pulse on the TRAILING edge (back to idle)
--
-- Note what it does NOT emit: launch and capture. Which of a and b launches
-- depends on CPHA, and that mapping is Chapter 13.5's job. Putting it here
-- would mean this block needed the mode, and a divider that needs the mode
-- has to be re-verified whenever the mode logic changes.
--
-- THE ODD-DIVISOR DECISION. `div` is the SCLK period in system clocks, and
-- for an odd value the halves cannot be equal. The FIRST half is made the
-- longer one, because the interval between the launching edge and the
-- capturing edge is what the slave's output valid time and the round trip
-- must fit into. Choosing the other way would give the returning data LESS
-- time than an even divisor would.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity spi_clkdiv_strobe is
    generic (
        DIV_W : positive := 8
    );
    port (
        clk        : in  std_logic;
        rst_n      : in  std_logic;

        en         : in  std_logic;                      -- the FSM's shift_en
        div        : in  unsigned(DIV_W - 1 downto 0);   -- SCLK period, >= 2
        cpol       : in  std_logic;                      -- the idle level

        sclk       : out std_logic;
        edge_a_stb : out std_logic;   -- leading edge, away from idle
        edge_b_stb : out std_logic;   -- trailing edge, back to idle
        bit_done   : out std_logic;   -- one full SCLK period elapsed

        half_a     : out unsigned(DIV_W - 1 downto 0);
        half_b     : out unsigned(DIV_W - 1 downto 0);
        div_err    : out std_logic
    );
end entity;

architecture rtl of spi_clkdiv_strobe is

    -- The halves. Integer division by two is a shift, and the remainder goes
    -- to the FIRST half -- the decision argued for in the header.
    signal h_a  : unsigned(DIV_W - 1 downto 0) := (others => '0');
    signal h_b  : unsigned(DIV_W - 1 downto 0) := (others => '0');
    signal derr : std_logic := '1';

    signal cnt  : unsigned(DIV_W - 1 downto 0) := (others => '0');
    signal in_b : std_logic := '0';

    signal sclk_r : std_logic := '0';
    signal a_r    : std_logic := '0';
    signal b_r    : std_logic := '0';
    signal done_r : std_logic := '0';

begin

    h_a  <= shift_right(div + 1, 1);   -- ceil(div/2)
    h_b  <= shift_right(div, 1);       -- floor(div/2)
    derr <= '1' when to_integer(div) < 2 else '0';

    half_a     <= h_a;
    half_b     <= h_b;
    div_err    <= derr;
    sclk       <= sclk_r;
    edge_a_stb <= a_r;
    edge_b_stb <= b_r;
    bit_done   <= done_r;

    divide : process (clk, rst_n)
    begin
        if rst_n = '0' then
            cnt    <= (others => '0');
            in_b   <= '0';
            sclk_r <= '0';
            a_r    <= '0';
            b_r    <= '0';
            done_r <= '0';
        elsif rising_edge(clk) then
            a_r    <= '0';
            b_r    <= '0';
            done_r <= '0';

            if en = '0' then
                -- Not shifting: SCLK rests at the idle level and the counter
                -- is held ready, so the first edge after enable is a full
                -- half-period away rather than wherever the counter stopped.
                sclk_r <= cpol;
                cnt    <= (others => '0');
                in_b   <= '0';
            elsif derr = '1' then
                -- A divisor below two cannot produce an SCLK period at all.
                -- Holding the line idle is the safe response: a device sees no
                -- clock rather than a malformed one.
                sclk_r <= cpol;
            elsif in_b = '0' then
                -- First half: SCLK is away from idle.
                if cnt = 0 then
                    sclk_r <= not cpol;
                    a_r    <= '1';
                end if;
                if to_integer(cnt) >= to_integer(h_a) - 1 then
                    cnt  <= (others => '0');
                    in_b <= '1';
                else
                    cnt <= cnt + 1;
                end if;
            else
                -- Second half: SCLK returns to idle.
                if cnt = 0 then
                    sclk_r <= cpol;
                    b_r    <= '1';
                end if;
                if to_integer(cnt) >= to_integer(h_b) - 1 then
                    cnt  <= (others => '0');
                    in_b <= '0';
                    -- The period is complete at the END of the second half,
                    -- not at the trailing edge -- a consumer counting trailing
                    -- edges would start the next bit half a period early.
                    done_r <= '1';
                else
                    cnt <= cnt + 1;
                end if;
            end if;
        end if;
    end process;

end architecture;
Azvya Education Pvt. Ltd.VLSI Mentor
spi_clkdiv_strobe_tb.vhd — the same 46-pair sweep in VHDL
-- spi_clkdiv_strobe_tb.vhd
--
-- Every interval here is MEASURED from the waveform the divider produces,
-- not inferred from its parameters -- because the parameters are what is
-- under test. The checks then assert relationships that must hold for every
-- divisor: the halves sum to the period, the strobes stay in step, and the
-- first half is never shorter than the second.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity spi_clkdiv_strobe_tb is
end entity;

architecture sim of spi_clkdiv_strobe_tb is

    constant DIV_W : positive := 8;

    signal clk   : std_logic := '0';
    signal rst_n : std_logic := '0';
    signal halt  : boolean   := false;

    signal en   : std_logic := '0';
    signal div  : unsigned(DIV_W - 1 downto 0) := to_unsigned(4, DIV_W);
    signal cpol : std_logic := '0';

    signal sclk       : std_logic;
    signal edge_a_stb : std_logic;
    signal edge_b_stb : std_logic;
    signal bit_done   : std_logic;
    signal half_a     : unsigned(DIV_W - 1 downto 0);
    signal half_b     : unsigned(DIV_W - 1 downto 0);
    signal div_err    : std_logic;

    signal n_a, n_b, n_done : natural := 0;
    signal m_half_a, m_half_b, m_period : natural := 0;
    signal idle_wrong : natural := 0;
    signal clr_counts : std_logic := '0';

    signal errors : natural := 0;

begin

    clk <= not clk after 5 ns when not halt else '0';

    dut : entity work.spi_clkdiv_strobe
        generic map (DIV_W => DIV_W)
        port map (
            clk => clk, rst_n => rst_n,
            en => en, div => div, cpol => cpol,
            sclk => sclk, edge_a_stb => edge_a_stb, edge_b_stb => edge_b_stb,
            bit_done => bit_done,
            half_a => half_a, half_b => half_b, div_err => div_err
        );

    -- Measure the halves from the strobes themselves: A to B is the first
    -- half, B to the next A is the second.
    measure : process (clk)
        variable since_a, since_b : natural := 0;
        variable a_now,   b_now   : natural := 0;
        -- SCLK is a REGISTERED output, so it necessarily takes one clock to
        -- follow a change of en or cpol. That one-cycle lag is the property
        -- that makes it glitch-free, so the idle check allows for it.
        variable en_q, cpol_q : std_logic := '0';
    begin
        if rising_edge(clk) then
            if rst_n = '0' then
                n_a <= 0; n_b <= 0; n_done <= 0;
                since_a := 0; since_b := 0;
                idle_wrong <= 0;
            else
                if clr_counts = '1' then
                    n_a <= 0; n_b <= 0; n_done <= 0;
                else
                    if edge_a_stb = '1' then n_a    <= n_a + 1;    end if;
                    if edge_b_stb = '1' then n_b    <= n_b + 1;    end if;
                    if bit_done   = '1' then n_done <= n_done + 1; end if;
                end if;

                -- Both counters are snapshotted before either is touched.
                -- Without that, the A branch's increment runs first and the B
                -- branch then reads a count that has already advanced, so
                -- every first half measures one cycle long -- which looks
                -- exactly like the divider putting the remainder in the wrong
                -- half.
                a_now := since_a;
                b_now := since_b;

                if edge_a_stb = '1' then
                    since_a  := 1;
                    m_half_b <= b_now;                -- B .. A is the second half
                    m_period <= b_now + m_half_a;
                else
                    since_a := a_now + 1;
                end if;

                if edge_b_stb = '1' then
                    since_b  := 1;
                    m_half_a <= a_now;                -- A .. B is the first half
                else
                    since_b := b_now + 1;
                end if;

                -- Checked only once en and cpol have both been stable for a
                -- cycle, because the output is registered.
                if en = '0' and en_q = '0' and cpol = cpol_q and
                   sclk /= cpol then
                    idle_wrong <= idle_wrong + 1;
                end if;
                en_q   := en;
                cpol_q := cpol;
            end if;
        end if;
    end process;

    stim : process
        variable errs : natural := 0;

        procedure run_div(dv : natural; pol : std_logic; periods : natural) is
        begin
            wait until falling_edge(clk);
            en   <= '0';
            div  <= to_unsigned(dv, DIV_W);
            cpol <= pol;
            for k in 1 to 3 loop
                wait until falling_edge(clk);
            end loop;
            clr_counts <= '1';
            wait until falling_edge(clk);
            clr_counts <= '0';
            en <= '1';
            for k in 1 to dv * periods + dv + 4 loop
                wait until falling_edge(clk);
            end loop;
            en <= '0';
            for k in 1 to 3 loop
                wait until falling_edge(clk);
            end loop;
        end procedure;
    begin
        for k in 0 to 2 loop
            wait until falling_edge(clk);
        end loop;
        rst_n <= '1';
        wait until falling_edge(clk);

        -- 1. THE IDLE LEVEL, for BOTH polarities -- a divider that only ever
        --    rests low passes a CPOL=0 test and fails silently on mode 3.
        cpol <= '0';
        for k in 1 to 4 loop wait until falling_edge(clk); end loop;
        if sclk /= '0' then
            report "  FAIL: disabled with CPOL=0, SCLK is not low";
            errs := errs + 1;
        end if;
        cpol <= '1';
        for k in 1 to 4 loop wait until falling_edge(clk); end loop;
        if sclk /= '1' then
            report "  FAIL: disabled with CPOL=1, SCLK is not high";
            errs := errs + 1;
        end if;
        cpol <= '0';
        report "  idle: SCLK rests at CPOL for both polarities";

        -- 2. AN EVEN DIVISOR. Equal halves, and the period is the divisor.
        run_div(4, '0', 6);
        if m_half_a /= 2 or m_half_b /= 2 or m_period /= 4 then
            report "  FAIL: div=4 did not measure 2/2 with a period of 4";
            errs := errs + 1;
        end if;
        report "  div=4:  halves " & integer'image(m_half_a) & "/" &
               integer'image(m_half_b) & ", period " &
               integer'image(m_period) & ", A=" & integer'image(n_a) &
               " B=" & integer'image(n_b) & " done=" & integer'image(n_done);

        -- 3. AN ODD DIVISOR. The halves differ by one and the FIRST is the
        --    longer -- the decision the header argues for, checked rather
        --    than assumed.
        run_div(5, '0', 6);
        if m_half_a /= 3 or m_half_b /= 2 or m_period /= 5 then
            report "  FAIL: div=5 did not measure 3/2 with a period of 5";
            errs := errs + 1;
        end if;
        if m_half_a <= m_half_b then
            report "  FAIL: on an odd divisor the first half is not the longer";
            errs := errs + 1;
        end if;
        report "  div=5:  halves " & integer'image(m_half_a) & "/" &
               integer'image(m_half_b) &
               " -- the launch-to-capture half is the longer one";

        -- 4. THE SMALLEST LEGAL DIVISOR toggles every system clock.
        run_div(2, '0', 8);
        if m_half_a /= 1 or m_half_b /= 1 or m_period /= 2 then
            report "  FAIL: div=2 did not measure 1/1 with a period of 2";
            errs := errs + 1;
        end if;
        if div_err = '1' then
            report "  FAIL: div=2 was reported as an error"; errs := errs + 1;
        end if;
        report "  div=2:  halves 1/1 -- the fastest a synchronous divider can produce";

        -- 5. AN ILLEGAL DIVISOR is reported and the line held idle rather
        --    than toggling at some improvised rate.
        wait until falling_edge(clk);
        div <= to_unsigned(1, DIV_W); cpol <= '0'; en <= '1';
        for k in 1 to 8 loop wait until falling_edge(clk); end loop;
        if div_err /= '1' then
            report "  FAIL: div=1 was not reported as illegal"; errs := errs + 1;
        end if;
        if sclk /= '0' then
            report "  FAIL: an illegal divisor produced a clock"; errs := errs + 1;
        end if;
        wait until falling_edge(clk);
        div <= to_unsigned(0, DIV_W);
        for k in 1 to 8 loop wait until falling_edge(clk); end loop;
        if div_err /= '1' or sclk /= '0' then
            report "  FAIL: div=0 was not held idle and reported";
            errs := errs + 1;
        end if;
        en <= '0'; div <= to_unsigned(4, DIV_W);
        for k in 1 to 3 loop wait until falling_edge(clk); end loop;
        report "  div<2:  reported, and the line held idle rather than improvised";

        -- 6. STROBE COUNTS. A run does not stop on a period boundary, so the
        --    counts are not equal -- they are in STEP. Each period produces A
        --    then B then done, so the three differ by at most one and never
        --    go out of order. That holds at every cycle rather than only at
        --    boundaries.
        run_div(6, '0', 10);
        if (n_a - n_b) > 1 or n_a < n_b or (n_b - n_done) > 1 or
           n_b < n_done then
            report "  FAIL: the strobe counts are out of step";
            errs := errs + 1;
        end if;
        if n_done < 9 then
            report "  FAIL: too few periods in a run of ten"; errs := errs + 1;
        end if;
        report "  strobes: A=" & integer'image(n_a) & " B=" &
               integer'image(n_b) & " done=" & integer'image(n_done) &
               " -- in step, one of each per period";

        -- 7. THE SWEEP. For every divisor from 2 to 24, both polarities, the
        --    measured halves must match the published ones and sum to the
        --    period.
        for d in 2 to 24 loop
            for k in 0 to 1 loop
                if k = 1 then run_div(d, '1', 4); else run_div(d, '0', 4); end if;
                if m_period /= d then
                    report "  FAIL: a swept divisor measured the wrong period";
                    errs := errs + 1;
                end if;
                if (m_half_a + m_half_b) /= d then
                    report "  FAIL: the halves do not sum to the period";
                    errs := errs + 1;
                end if;
                if m_half_a /= to_integer(half_a) or
                   m_half_b /= to_integer(half_b) then
                    report "  FAIL: the measured halves differ from the published";
                    errs := errs + 1;
                end if;
                if m_half_a < m_half_b then
                    report "  FAIL: the first half is shorter than the second";
                    errs := errs + 1;
                end if;
                if (n_a - n_b) > 1 or n_a < n_b or (n_b - n_done) > 1 or
                   n_b < n_done then
                    report "  FAIL: a swept divisor's strobes went out of step";
                    errs := errs + 1;
                end if;
            end loop;
        end loop;
        report "  46 (divisor, polarity) pairs swept: measured halves match the published ones, sum to the period, and the first is never shorter";

        -- 8. THE LINE NEVER MOVED WHILE DISABLED, across everything above.
        if idle_wrong /= 0 then
            report "  FAIL: SCLK left the idle level while disabled";
            errs := errs + 1;
        end if;
        report "  disabled: 0 cycles on which SCLK left the idle level";

        errors <= errs;
        if errs = 0 then
            report "PASS: SCLK rests at CPOL for both polarities whenever the divider is disabled and never left it once, an even divisor gives equal halves and an odd one gives a longer FIRST half so the launch-to-capture interval is the longer of the two, the smallest legal divisor of two toggles every system clock, a divisor below two is reported and the line held idle rather than improvised, and across 46 divisor and polarity pairs the measured halves match the published ones, sum to the period, and produce exactly one A strobe, one B strobe and one bit_done per period";
        else
            report "FAIL: " & integer'image(errs) & " error(s)" severity error;
        end if;
        halt <= true;
        wait;
    end process;

end architecture;

Parity

All three implementations sweep 46 divisor-and-polarity pairs, and in every one the measured halves match the published ones, sum to the period, and the first is never shorter than the second. All three also confirm that SCLK never leaves its idle level while the divider is disabled — across the entire run, not merely at the points where it is checked.

7. Why a Verification Engineer Cares

Azvya Education Pvt. Ltd.VLSI Mentor
spi_clkdiv_strobe.sva — the period, the halves, and the strobe ordering
// Every property here is about the RELATIONSHIP between the pin and the strobes,
// which is the contract the datapath depends on. None of them recomputes the
// halves from `div`: doing that would reproduce the design's own arithmetic and
// agree with it whatever it does.

module spi_clkdiv_strobe_sva #(parameter int DIV_W = 8) (
    input logic             clk,
    input logic             rst_n,
    input logic             en,
    input logic             cpol,
    input logic [DIV_W-1:0] div,
    input logic             sclk,
    input logic             edge_a_stb,
    input logic             edge_b_stb,
    input logic             bit_done,
    input logic [DIV_W-1:0] half_a,
    input logic [DIV_W-1:0] half_b,
    input logic             div_err
);

    default clocking cb @(posedge clk); endclocking
    default disable iff (!rst_n);

    // The two strobes must alternate, strictly. This is the property the
    // datapath relies on and the one a counter off-by-one breaks.
    logic expect_a;
    always_ff @(posedge clk) begin
        if (!rst_n)              expect_a <= 1'b1;
        else if (edge_a_stb)     expect_a <= 1'b0;
        else if (edge_b_stb)     expect_a <= 1'b1;
    end
    a_alternate_a: assert property (edge_a_stb |-> expect_a);
    a_alternate_b: assert property (edge_b_stb |-> !expect_a);

    // Never both on one cycle, at any divisor -- including two, where the
    // halves are a single cycle each.
    a_not_both: assert property (!(edge_a_stb && edge_b_stb));

    // A leading strobe means SCLK left idle on the previous edge; a trailing
    // strobe means it returned. Stated against the PIN, one cycle back, because
    // SCLK is registered.
    a_a_means_left:     assert property (edge_a_stb |-> sclk != cpol);
    a_b_means_returned: assert property (edge_b_stb |-> sclk == cpol);

    // The published halves must sum to the divisor, and the first must never be
    // the shorter. The second is the odd-divisor decision, and it is the one a
    // reviewer is most likely to invert.
    a_halves_sum:  assert property (div >= 2 |-> (half_a + half_b) == div);
    a_first_longer: assert property (div >= 2 |-> half_a >= half_b);

    // Disabled means idle, with one cycle of tolerance because SCLK is a
    // registered output and therefore cannot follow `en` or `cpol` faster.
    a_idle_when_off: assert property (
        !en && $past(!en) && $stable(cpol) |-> sclk == cpol
    );

    // An illegal divisor produces no clock at all, rather than an improvised one.
    a_err_no_clock: assert property (div_err |=> $stable(sclk) || !en);
    a_err_reported: assert property ((div < 2) == div_err);

    // `bit_done` closes a period, so it must follow a trailing strobe and never
    // coincide with a leading one.
    a_done_after_b: assert property (bit_done |-> !edge_a_stb);

endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
spi_clkdiv_strobe_cg.sv — divisors at their boundaries, and the parity of each
// The divisor's absolute value is uninteresting; its PARITY and its distance
// from the legal minimum are what exercise different logic. A suite that sweeps
// 8 through 64 in steps of 8 has tested one case eight times.

covergroup cg_clkdiv @(posedge clk);

    // Parity is the axis that selects between equal and unequal halves.
    parity: coverpoint (div % 2) iff (en) {
        bins even = {0};
        bins odd  = {1};
    }

    // Distance from the minimum. Two is the fastest legal value and has
    // single-cycle halves; one and zero are illegal and must be reported.
    magnitude: coverpoint div {
        bins illegal_zero = {0};
        bins illegal_one  = {1};
        bins minimum      = {2};
        bins three        = {3};
        bins small        = {[4:8]};
        bins medium       = {[9:32]};
        bins large        = {[33:$]};
    }

    // Both polarities, because the strobe definitions are relative to idle and
    // a design that hard-codes rising and falling passes at CPOL=0 only.
    pol: coverpoint cpol { bins low = {0}; bins high = {1}; }

    // The enable's transitions matter more than its level: the interesting case
    // is re-enabling after a stop, where the counter's reset state decides
    // whether the first half is a full one.
    en_edge: coverpoint {$past(en), en} {
        bins starting = {2'b01};
        bins stopping = {2'b10};
        bins running  = {2'b11};
        bins stopped  = {2'b00};
    }

    // Stopping MID-HALF rather than on a boundary: the case that leaves a
    // counter in an arbitrary state, and the reason it is cleared.
    stop_phase: coverpoint phase_at_stop iff (stopping) {
        bins in_first_half  = {0};
        bins in_second_half = {1};
    }

    x_parity_pol: cross parity, pol;
    x_min_pol:    cross magnitude, pol;

endgroup

8. Why an FPGA or ASIC Engineer Cares

SCLK is a registered output, which is the single most important sentence in this chapter for implementation. The alternatives are worse in specific ways:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
   sclk = clk & en              a gated clock: needs a clock-gating cell,
                                a constraint, and a CDC review; glitches if
                                `en` is not clean
   sclk = counter[N]            a divided clock: becomes a clock tree, needs
                                its own constraint, and anything clocked on it
                                creates a second domain
   sclk <= <registered value>   a data output that happens to look like a
                                clock; one domain, no constraints, no cells

The third is what this design does, and it is only available because nothing in the master is clocked on SCLK. The datapath is clocked on clk and told when the edges happen by the strobes, which is exactly what the strobes are for. A design that clocks its shift register on SCLK has a second clock domain, needs a generated-clock constraint, and cannot use the strobes at all.

The counter is one comparator wide, and the comparison is against a value that changes once per half-period. half_a and half_b are combinational from div, and div comes from the configuration latch of Chapter 13.2, so it is stable for the whole frame. That means the comparator's second input is effectively a constant during operation — no arithmetic on the critical path.

Computing the halves is a shift, not a divide. ceil(div/2) is (div + 1) >> 1 and floor(div/2) is div >> 1. No divider is inferred, and a reviewer who sees / 2 in RTL and reaches for a comment can be pointed at the shift the synthesiser will produce.

Cost. DIV_W flops for the counter, one for the phase bit, four for the registered outputs, one comparator and one incrementer. At DIV_W = 8 that is 13 flops and an 8-bit compare — the smallest block in the master.

9. Failure Signature — A Read That Is Marginal At Every Odd Divisor

Symptom. A sensor read is reliable at divisors 4, 6 and 8, and intermittently returns corrupted data at 5 and 7. Raising the divisor from 5 to 6 fixes it. Lowering from 6 to 5 breaks it again, reproducibly.

What that rules out. A fault that tracks the parity of a parameter is not noise, not a supply problem and not layout — those would track frequency monotonically. Parity means the failing configurations differ from the working ones in something that only odd values do, and in a divider there is exactly one such thing.

What is confusing about it. The SCLK frequency at div=5 is lower than at div=4, and div=4 works. So the failure gets slower-and-worse, which contradicts every intuition about clock rate and sends investigations toward signal integrity. The measured SCLK period is correct at every divisor, so a scope on SCLK alone shows nothing wrong.

The mechanism. The design put the remainder in the second half. At div=4, launch-to-capture is 2 cycles. At div=5 it is still 2 cycles, because the extra cycle went into the other half — and the total period got longer, so the slave's internal logic has more time to prepare but no more time to drive. If 2 cycles was already marginal against the part's output-valid time, div=5 is exactly as marginal as div=4 while appearing to be a safer setting.

Why div=7 also fails and div=6 does not. div=6 gives 3 cycles of launch-to-capture; div=7 gives 3 as well, for the same reason. So the pattern is that odd divisors give the same launch-to-capture interval as the even divisor below them — which means the sequence of margins is 2, 2, 3, 3, 4, 4 rather than 2, 3, 3, 4, 4, 5. Every odd step buys nothing.

How the testbench catches it. By asserting half_a >= half_b at every odd divisor in the sweep, and by measuring both halves from the strobes rather than computing them. A test that only checked the period would pass, because the period is right.

The fix, and the audit it implies. Move the remainder to the first half. Then audit any recorded "this part needs a slower clock than its datasheet claims" notes: the usual cause of such a note is this bug, and the divisor is one step higher than it needs to be everywhere it appears.

10. Common Misconceptions

"The divider should emit launch and capture strobes, since that is what the datapath wants." Then the divider needs cpha, and it has to be re-verified whenever the mode logic changes. The divider publishes facts about the clock; Chapter 13.5 converts them into decisions about the protocol.

"bit_done and the trailing edge are the same event." They are half a period apart. bit_done closes the period at the end of the second half; the trailing edge is at its start. A consumer using the trailing edge as a period boundary starts every bit half a period early.

"An odd divisor is an edge case not worth handling carefully." It is half of all divisors, and getting it wrong costs margin rather than function — which makes it the hardest kind of bug to find, because nothing fails.

"SCLK should be generated by dividing the clock, which is what a divider does." A divided clock becomes a clock tree with its own constraints and creates a second domain for anything that uses it. A registered output that happens to look like a clock stays in one domain and needs no constraints. The difference is whether anything in your design is clocked on it, and in this design nothing is.

"Holding SCLK idle on an illegal divisor loses information; it should toggle as fast as it can." Toggling as fast as it can is a clock the slave will sample, which turns a configuration error into corrupted data. Holding idle produces a state every slave already handles — no clock — and div_err carries the information that would otherwise be lost.

11. Reason It Through

Why is the counter cleared when the divider is disabled rather than frozen?

So that the first edge after re-enabling is a full half-period away from the enable. If the counter keeps whatever value it had when the previous frame ended, the first half of the next frame is shortened by that amount — and the amount depends on the previous frame's length, which makes the resulting timing violation depend on history. Clearing it means every frame's first half is the same length as every other frame's.

What would go wrong if the halves were recomputed from div inside the testbench and compared against the strobes?

Nothing, if the arithmetic were written independently. Everything, if it were copied from the design — which is what happens in practice, because the expression is right there and obviously correct. The reason the testbench measures the halves from the strobes is that a measurement cannot share a bug with the thing it measures.

At a divisor of two, both halves are one cycle. What does that break, and what does it prove?

Nothing, and that is the point: div=2 is the fastest legal setting and it makes every interval degenerate. The leading and trailing strobes land on consecutive cycles, bit_done lands the cycle after the trailing strobe, and any check that assumed a gap between two strobes fails. It is the single most valuable divisor in the sweep because it is where every off-by-one shows.

Why is div_err a combinational function of div rather than a sticky bit?

Because it describes the current configuration, not a past event. A sticky version would stay set after the configuration was corrected, and a driver that fixed its divisor would still see the error. The sticky flags in this module — change_ignored, rx_overrun, rx_trunc — all describe events; div_err, len_err and sel_err all describe states, and the distinction is worth keeping consistent.

A consumer counts trailing edges to know when a bit period has elapsed, and the design works at every divisor above two but fails at two. Explain.

At div=2 the trailing edge is one cycle before the period ends and one cycle after the leading edge, so a consumer counting trailing edges starts the next bit one cycle early — which at div=2 is half the period. At larger divisors the same error is a smaller fraction and usually absorbed by the slave's setup margin, so the bug hides until someone configures the fastest clock. That is the general shape: an off-by-one in a period boundary scales as one over the divisor, so it appears first at the fastest setting, which is the last one anybody tests.

12. Understanding Check

13. Summary

The divider produces SCLK and three strobes — leading, trailing, and period-complete — and deliberately not launch and capture, because that mapping depends on CPHA and a divider that knows the mode must be re-verified when the mode logic changes.

The strobes are defined relative to the idle level, which is how the divider absorbs the whole of CPOL. Everything downstream sees a first and a second edge, so inverting CPOL inverts the pin and nothing else.

At an odd divisor the first half gets the remainder, because in CPHA=1 the launch-to-capture interval is the first half and that is what the slave's output-valid time and the round trip must fit into. The opposite choice gives an odd divisor the same margin as the even divisor below it — a failure that costs margin rather than function, and that presents as "this part needs a slower clock than its datasheet says".

bit_done closes the period at the end of the second half, half a period after the trailing edge. A consumer using the trailing edge as a period boundary starts each bit early, and the error is largest at the fastest divisor.

SCLK is a registered output. Not a gated clock, not a divided clock — a data output that happens to look like one, which keeps the master in a single clock domain and needs no constraints or cells. This is only available because nothing in the design is clocked on SCLK, and that is what the strobes are for.

When disabled, the counter is cleared rather than frozen, so every frame's first half is the same length. A divisor below two holds the line idle and reports, because improvising a clock turns a configuration error into corrupted data.

For verification the assertions are about the relationship between the pin and the strobes, and none recomputes the halves from div. Coverage bins parity and distance from the minimum, because the absolute divisor is uninteresting and a sweep in steps of eight tests one case repeatedly. And the counts of the three strobes are asserted to be in step, not equal, because a run does not end on a period boundary.

14. What Comes Next

The clock runs and its edges are announced. Nothing yet decides what they mean.

Chapter 13.5 — CPOL/CPHA-Aware Edge Control is the whole of the mode logic, and it is smaller than expected: CPOL has already been consumed, so the decision depends on CPHA alone. What is not smaller than expected is the asymmetry between the two phases — one of them needs a bit on MOSI before any clock has moved, and has an edge at the far end that must be ignored.

Continue learning