Volume 08 Beginner 6 sub-modules ~25 min read

Real-World Controllers

Everything so far has been practice for this volume. Here are six controllers from real products, each built the same way: say what it must do, draw the states, write the Verilog, and watch it run. By the end you will have written the controllers inside traffic lights, vending machines, lifts, and the serial ports found on almost every chip.

You will learn
  • A traffic light that waits for cars, using a shared timer
  • A vending machine that gives change, with registered Mealy outputs
  • A three-floor lift: a state machine working with registers
  • A complete UART transmitter and receiver
  • An SPI master in mode 0
  • A two-phase bus controller with a timeout
You need
  • Volume 04: the Verilog template and testbenches
  • Volume 07: timers, counters and synchronisers

8.1 Traffic light with timers

A real traffic light is a small state machine plus one shared timer. The machine decides the order of the colours; the timer decides how long each one lasts.

What it must do

A main road crosses a quiet side road. The main road should stay green, unless a car is waiting on the side road. A sensor in the side road gives car = 1 while a car is waiting. The rules:

  1. The main road is green for at least a minimum time. After that, it stays green until a car waits on the side road.
  2. The main road then shows yellow for a short time.
  3. The side road is green for a fixed time, then yellow.
  4. Then the main road is green again.

Whenever one road is green or yellow, the other road is red. That is the safety rule of every crossing, and the state machine makes sure it always holds.

The states

Four states are enough: main green (MG), main yellow (MY), side green (SG) and side yellow (SY). Each state's second line shows the lamps: M for the main road, S for the side road.

State diagram of a road crossing with states MG, MY, SG and SY MG M=G S=R MY M=Y S=R SG M=R S=G SY M=R S=Y d·car d d d else else else else reset
Figure 8.1 - The crossing. The main road leaves green only when the timer is done and a car is waiting. Every other state simply waits for its timer. d is done from the timer; car is the sensor.

The code

This is the timer pattern from Volume 07: load the timer on entry to each state, count down, and move on when done. One detail is new: in MG, the timer stops at 0 and stays there. From then on, done is 1, and the first waiting car ends the green at once.


module crossing (
  input  wire clk, rst,
  input  wire car,                                  // a car waits on the side road
  output wire main_g, main_y, main_r,
  output wire side_g, side_y, side_r
);
  localparam [1:0] MG = 2'd0, MY = 2'd1, SG = 2'd2, SY = 2'd3;
  localparam [3:0] T_MG = 4'd3, T_Y = 4'd1, T_SG = 4'd2;   // cycles minus 1
  reg  [1:0] state, next_state;
  reg  [3:0] timer;
  wire done = (timer == 4'd0);

  always @(*) begin
    next_state = state;
    case (state)
      MG: if (done && car) next_state = MY;         // green until a car waits
      MY: if (done)        next_state = SG;
      SG: if (done)        next_state = SY;
      SY: if (done)        next_state = MG;
    endcase
  end

  always @(posedge clk)
    if (rst) begin
      state <= MG;
      timer <= T_MG;
    end else begin
      state <= next_state;
      if (next_state != state)                      // entering a state: load its time
        case (next_state)
          MG:      timer <= T_MG;
          SG:      timer <= T_SG;
          default: timer <= T_Y;                    // MY and SY
        endcase
      else if (!done)
        timer <= timer - 1;
    end

  assign main_g = (state == MG);
  assign main_y = (state == MY);
  assign main_r = (state == SG) || (state == SY);
  assign side_g = (state == SG);
  assign side_y = (state == SY);
  assign side_r = (state == MG) || (state == MY);
endmodule

The outputs come from the state alone - a Moore machine - so no input can ever make both roads green. Figure 8.2 shows one car arriving, with tiny times so that everything fits.

The crossing controller: a waiting car ends the main green, then the lights cycle back 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 clk car state MG MY SG SY MG timer 3 2 1 0 1 0 2 1 0 1 0 3 2 1 0 0 done
Figure 8.2 - A car arrives in cycle 2, but the main road has not had its minimum green yet. In cycle 3 the timer is done and the car is still waiting, so the lights change. After the side road's turn, the main road is green again, and with no car it stays green.

For a real crossing, the times are seconds, so the timer counts ticks of a prescaler rather than clock cycles, as in Volume 07.

Common mistake

Do not make the outputs Mealy here. If "main green" depended on the car input as well as the state, a flicker on the sensor could reach the lamps directly. For anything that controls safety, make the outputs come only from the state - or from flip-flops.

Quick check

The main road has been green for a long time and no car has come. What is the timer doing?

Show the answer

Answer: B. The timer only counts down while it is not done. In MG it reaches 0 after the minimum green time and stays there. So done stays 1 for as long as the main road is green, and the machine leaves MG on the very first cycle that car = 1.

8.2 Vending machine with change

A vending machine remembers how much money it holds. Each state is an amount, and a coin that reaches the price sells the item and gives any change.

What it must do

A machine sells a drink for 15. It accepts coins of 5 and 10, one coin per clock cycle at most. The inputs are c5 and c10, each 1 in the cycle its coin arrives. When the money reaches 15, it must release the drink (vend). If the customer paid 20, it must also return a 5 coin (change).

The states: one per amount still short of the price

The machine only needs to remember the money it holds: 0, 5 or 10. Anything that reaches 15 is sold at once. So there are three states: S0, S5 and S10.

We build it as a Mealy machine, because the right moment to sell is the moment the coin arrives - on the arrow, not in a state. Each arrow reads coin / outputs:

State c5 arrives c10 arrives
S0 (holds 0) go to S5 go to S10
S5 (holds 5) go to S10 15 reached: vend, go to S0
S10 (holds 10) 15 reached: vend, go to S0 20 paid: vend and change, go to S0
Mealy state diagram of a vending machine that sells for 15 and accepts 5 and 10 coins S0 S5 S10 c5 c5 c10 c10 / vend c5 / vend c10 / vend, chg none none none reset
Figure 8.3 - The vending machine as a Mealy machine. The states are the money held. The three arrows back to S0 each sell a drink; the lowest one also gives change.

Registered outputs

vend and change drive motors. A glitch on either could drop a free drink or a coin, so we give them flip-flops - the three-process idea from Volume 04. The logic works out vend_n and change_n ("vend next"), and the flip-flops pass them on at the next edge. The outputs therefore come one cycle after the coin, clean and exactly one cycle long.


module vend15 (
  input  wire clk, rst,
  input  wire c5, c10,             // at most one coin per cycle
  output reg  vend, change         // registered: one clean pulse each
);
  localparam [1:0] S0 = 2'd0, S5 = 2'd1, S10 = 2'd2;
  reg [1:0] state, next_state;
  reg       vend_n, change_n;      // the Mealy outputs, before their flip-flops

  always @(posedge clk)
    if (rst) begin state <= S0; vend <= 1'b0; change <= 1'b0; end
    else     begin state <= next_state; vend <= vend_n; change <= change_n; end

  always @(*) begin
    next_state = state; vend_n = 1'b0; change_n = 1'b0;
    case (state)
      S0:  if (c5) next_state = S5;  else if (c10) next_state = S10;
      S5:  if (c5) next_state = S10; else if (c10) begin next_state = S0; vend_n = 1'b1; end
      S10: if (c5) begin next_state = S0; vend_n = 1'b1; end
           else if (c10) begin next_state = S0; vend_n = 1'b1; change_n = 1'b1; end
      default: next_state = S0;
    endcase
  end
endmodule
The vending machine: 5 then 10 buys one drink, then 10 and 10 buy another with change 0 1 2 3 4 5 6 7 8 9 clk c5 c10 state S0 S5 S0 S10 S0 vend change
Figure 8.4 - A 5 and then a 10 make exactly 15: the drink comes out in cycle 4, one cycle after the second coin. Then two 10 coins make 20: in cycle 9 the machine vends and gives a 5 coin back.
Remember

Count the money. For every run, 15 × (drinks sold) + 5 × (coins returned) + (money still held) must equal the money put in. A check like this, written into a testbench, catches any arrow that loses or creates money.

Common mistake

Forgetting the case where two coins arrive in the same cycle. This design assumes "at most one coin per cycle" - the coin mechanism guarantees it. If it did not, c5 = c10 = 1 would be a real input, and the table would need a row for it. Always write down the assumptions your machine relies on.

Quick check

The machine is in S10, and a 10 coin arrives. What happens?

Show the answer

Answer: C. 10 held plus 10 paid is 20, which is 5 more than the price. So the machine sells the drink, returns a 5 coin, and starts again at 0. There is no S20 state - any amount of 15 or more is sold at once.

8.3 Elevator controller

A lift is a state machine working with registers: the state machine decides what to do next, and registers remember the floor and the waiting calls.

What it must do

A lift serves three floors: 0, 1 and 2. On each floor there is a call button. The inputs call[0], call[1] and call[2] give a one-cycle pulse when a button is pressed. The lift must:

Two kinds of memory

A lift must remember more than "what am I doing". It must also remember where it is and who is waiting. Those are data, not states, so they live in registers beside the state machine:

The registers and the logic around them are called the datapath. The state machine controls the datapath - and the datapath's values steer the state machine. Volume 09 studies this pairing properly.

The states

State diagram of a three-floor lift controller with states IDLE, UP, DOWN and DOOR IDLE UP DOWN DOOR above below here done call here call here no calls next floor next floor wait reset
Figure 8.5 - The lift. IDLE decides where to go. UP and DOWN move one floor each time the timer runs out, and stop at a floor with a call. DOOR waits for its timer, then returns to IDLE.

module lift3 #(parameter [3:0] T_MOVE = 4'd1, T_DOOR = 4'd2) (  // cycles minus 1
  input  wire       clk, rst,
  input  wire [2:0] call,                    // one-cycle presses, floors 0 to 2
  output reg  [1:0] floor,                   // where the lift is now
  output wire       door_open, going_up, going_down
);
  localparam [1:0] IDLE = 2'd0, UP = 2'd1, DOWN = 2'd2, DOOR = 2'd3;
  reg  [1:0] state;
  reg  [2:0] req;                            // remembered calls, one bit per floor
  reg  [3:0] timer;
  wire done  = (timer == 4'd0);
  wire here  = req[floor];
  wire above = (floor == 2'd0) ? (req[1] | req[2]) : (floor == 2'd1) ? req[2] : 1'b0;
  wire below = (floor == 2'd2) ? (req[1] | req[0]) : (floor == 2'd1) ? req[0] : 1'b0;
  wire [1:0] next_floor = (state == UP) ? floor + 2'd1 : floor - 2'd1;

  always @(posedge clk)
    if (rst) begin
      state <= IDLE; floor <= 2'd0; req <= 3'b000; timer <= 4'd0;
    end else begin
      req <= req | call;                     // remember every press
      if (!done) timer <= timer - 1;         // the timer always runs down to 0
      case (state)
        IDLE: if (here)       begin state <= DOOR; timer <= T_DOOR; end
              else if (above) begin state <= UP;   timer <= T_MOVE; end
              else if (below) begin state <= DOWN; timer <= T_MOVE; end
        UP, DOWN:
              if (done) begin                // reached the next floor
                floor <= next_floor;
                if (req[next_floor]) begin state <= DOOR; timer <= T_DOOR; end
                else                 timer <= T_MOVE;          // keep going
              end
        DOOR: if (done) begin
                req[floor] <= 1'b0;          // this call has been served
                state <= IDLE;
              end
      endcase
    end

  assign door_open  = (state == DOOR);
  assign going_up   = (state == UP);
  assign going_down = (state == DOWN);
endmodule

Two lines work together on req. The first, req <= req | call, sets a bit for every press. The second, in DOOR, clears one bit. In Verilog, when two non-blocking assignments in the same block touch the same bit, the later one wins. So a served call is cleared, even though the first line ran too.

The lift answers a call from floor 2 and stops at floor 1 on the way for a later call 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 clk call2 call1 req 000 100 110 100 000 state IDLE UP DOOR IDLE UP DOOR IDLE floor 0 1 2
Figure 8.6 - In cycle 1 someone calls from floor 2, so the lift sets off upwards. In cycle 3 someone calls from floor 1. The lift reaches floor 1 in cycle 5, finds that call, and opens the door there first. Then it carries on to floor 2. The times are tiny so that it fits.

The door output is 1 only in DOOR, and the motor outputs only in UP and DOWN. Because they come from different states, the lift can never move with the door open.

Common mistake

Do not try to make the floor part of the state - states like "UP_AT_0", "UP_AT_1" and so on. With three floors that is already messy; with twenty it is hopeless. Keep states for what the machine is doing, and registers for data, such as the floor number.

Quick check

The lift is at floor 0 and IDLE. Calls are waiting at floors 1 and 2. What does it do?

Show the answer

Answer: D. With calls above, IDLE chooses UP. Each time the lift reaches a floor, it checks that floor's call bit. Floor 1 has a call, so it stops there, opens the door, and only then carries on to floor 2.

8.4 UART transmitter and receiver

A UART sends a byte one bit at a time, framed by a start bit and a stop bit. Both ends agree on the speed, so no clock wire is needed.

The frame

A UART link has just one wire in each direction. When nothing is being sent, the line rests at 1. A byte travels as a frame of 10 bits, each lasting the same time:

Both ends agree on the baud rate - the bits per second - in advance. At 115200 baud, each bit lasts about 8.7 microseconds. With a 50 MHz clock, that is 434 clock cycles per bit. Figure 8.7 shows the letter "A" (hex 41, binary 0100 0001) on the wire.

A UART frame carrying the letter A: start bit, eight data bits lowest first, stop bit frame idle start d0 d1 d2 d3 d4 d5 d6 d7 stop idle tx
Figure 8.7 - The letter A (0100 0001) on a UART line, one slot per bit. The data goes lowest bit first, so the bits appear in the order 1, 0, 0, 0, 0, 0, 1, 0.

The transmitter

The transmitter is a four-state machine with two counters beside it: clk_cnt counts the clock cycles within one bit, and bit_idx counts the data bits.

State diagram of a UART transmitter with states IDLE, START, BITS and STOP IDLE START BITS STOP send bit done bit 7 done bit done else counting next bit counting reset
Figure 8.8 - The UART transmitter. BITS stays in place, one data bit after another, until bit 7 has been sent. bit done means clk_cnt has counted one whole bit time.

module uart_tx #(parameter CPB = 434) (        // clocks per bit: 50 MHz / 115200 baud
  input  wire       clk, rst,
  input  wire       send,                      // one-cycle pulse: send data
  input  wire [7:0] data,
  output reg        tx,                        // the serial line; 1 when idle
  output wire       busy
);
  localparam [1:0] IDLE = 2'd0, START = 2'd1, BITS = 2'd2, STOP = 2'd3;
  reg [1:0] state;
  reg [$clog2(CPB)-1:0] clk_cnt;               // clock cycles within one bit
  reg [2:0] bit_idx;                           // which data bit, 0 to 7
  reg [7:0] shreg;                             // a copy of data, taken at the start
  wire bit_done = (clk_cnt == CPB - 1);

  always @(posedge clk)
    if (rst) begin
      state <= IDLE; tx <= 1'b1; clk_cnt <= 0; bit_idx <= 0; shreg <= 8'd0;
    end else case (state)
      IDLE:  begin
               tx <= 1'b1; clk_cnt <= 0;
               if (send) begin shreg <= data; state <= START; end
             end
      START: begin
               tx <= 1'b0;                                     // the start bit
               if (bit_done) begin clk_cnt <= 0; bit_idx <= 0; state <= BITS; end
               else clk_cnt <= clk_cnt + 1;
             end
      BITS:  begin
               tx <= shreg[bit_idx];                           // lowest bit first
               if (bit_done) begin
                 clk_cnt <= 0;
                 if (bit_idx == 3'd7) state <= STOP;
                 else                 bit_idx <= bit_idx + 1;
               end else clk_cnt <= clk_cnt + 1;
             end
      STOP:  begin
               tx <= 1'b1;                                     // the stop bit
               if (bit_done) begin clk_cnt <= 0; state <= IDLE; end
               else clk_cnt <= clk_cnt + 1;
             end
    endcase

  assign busy = (state != IDLE);
endmodule

Because tx is a registered output, every bit on the wire starts one clock cycle after its state does. All bits move by the same cycle, so each still lasts exactly CPB cycles - and the output can never glitch.

The receiver

The receiver has no clock from the sender. It rebuilds the timing from the start bit alone:

  1. Synchronise the line with two flip-flops, as in Volume 07 - it comes from another chip.
  2. IDLE: wait for the line to fall to 0. That may be a start bit.
  3. START: wait half a bit time, to reach the middle of the start bit. If the line is still 0, it was a real start bit. If it is 1 again, it was a spike - go back to IDLE.
  4. BITS: every whole bit time from there, the receiver is in the middle of the next bit. Sample it. Do this 8 times, filling the byte lowest bit first.
  5. STOP: one bit time later, sample the stop bit. If it is 1, report the byte with a one-cycle valid pulse.

Sampling in the middle of each bit leaves the most room for error. The two ends' clocks may differ slightly, and the edges of each bit may be blurred, but the middle is safe.


module uart_rx #(parameter CPB = 434) (
  input  wire       clk, rst,
  input  wire       rx,                        // the serial line
  output reg  [7:0] data,
  output reg        valid                      // one-cycle pulse: a new byte in data
);
  localparam [1:0] IDLE = 2'd0, START = 2'd1, BITS = 2'd2, STOP = 2'd3;
  reg [1:0] state;
  reg [$clog2(CPB)-1:0] clk_cnt;
  reg [2:0] bit_idx;
  reg [1:0] sync;                              // two-flip-flop synchroniser
  always @(posedge clk) sync <= {sync[0], rx};
  wire line = sync[1];

  always @(posedge clk) begin
    valid <= 1'b0;
    if (rst) begin
      state <= IDLE; clk_cnt <= 0; bit_idx <= 0;
    end else case (state)
      IDLE:  if (!line) begin clk_cnt <= 0; state <= START; end    // the line fell
      START: if (clk_cnt == CPB/2 - 1) begin                       // middle of the start bit
               clk_cnt <= 0; bit_idx <= 0;
               state <= line ? IDLE : BITS;                        // still 0? then it is real
             end else clk_cnt <= clk_cnt + 1;
      BITS:  if (clk_cnt == CPB - 1) begin                         // middle of the next bit
               clk_cnt <= 0;
               data[bit_idx] <= line;                              // lowest bit first
               if (bit_idx == 3'd7) state <= STOP;
               else                 bit_idx <= bit_idx + 1;
             end else clk_cnt <= clk_cnt + 1;
      STOP:  if (clk_cnt == CPB - 1) begin                         // middle of the stop bit
               clk_cnt <= 0;
               valid   <= line;                                    // a good stop bit is 1
               state   <= IDLE;
             end else clk_cnt <= clk_cnt + 1;
    endcase
  end
endmodule

If the stop bit reads 0, the frame was damaged. This is called a framing error, and the receiver above simply drops the byte. A fuller design would also report the error.

Try it yourselfConnect uart_tx's tx straight to uart_rx's rx, with CPB = 8 so that the simulation is short. Send a few bytes and check that each one comes out of the receiver unchanged.
Open EDA Playground
Common mistake

Sampling at the start of each bit instead of the middle. It works in a perfect simulation, then fails on real hardware, where the two clocks differ a little and each bit's edges are blurred. The half-bit wait in START is what moves every sample to the safe middle.

Quick check

Why does the receiver wait only half a bit time in START, but a whole bit time for each data bit?

Show the answer

Answer: A. All bits have the same length. The receiver sees the start of the start bit, so half a bit time later it is in the middle. From any middle, one whole bit time later is the middle of the next bit. The start bit is not shorter at all.

8.5 SPI master

SPI sends a clock along with the data. On each clock cycle, the master and the slave swap one bit: the master's bits go out on one wire while the slave's bits come back on the other.

The wires

SPI uses four wires between a master and a slave:

Wire Driven by Job
SCLK master the clock for the transfer
MOSI master data from master to slave
MISO slave data from slave to master
CS_N master chip select, active low: 0 means "you, listen"

MOSI and MISO are named master out, slave in and master in, slave out. Because SPI carries its own clock, the two ends do not need to agree on a speed in advance, as a UART must.

Mode 0

SPI has four modes. The most common, mode 0, has two rules:

So each bit is set up half a clock period before it is sampled. The master also puts the first bit out as soon as it lowers CS_N, before the first rising edge.

An SPI mode 0 transfer: chip select, clock, and data on MOSI and MISO cs_n sclk mosi t7 t6 t5 t4 t3 t2 t1 t0 miso r7 r6 r5 r4 r3 r2 r1 r0 first rising edge: both sides sample
Figure 8.9 - One SPI byte, in mode 0. CS_N falls and both sides put out their top bit. On each rising edge of SCLK both sides sample; on each falling edge both move to the next bit. After eight clock cycles each side holds the other's byte.

The master

The master makes SCLK itself, by toggling it every HALF clock cycles. A counter, edges, counts the 16 SCLK edges of one byte. A single shift register does double duty. On each rising edge, the bit from MISO shifts in at the bottom. On each falling edge, the new top bit goes out on MOSI.


module spi_master #(parameter HALF = 4) (      // SCLK period = 2 x HALF clocks (HALF >= 2)
  input  wire       clk, rst,
  input  wire       start,                     // one-cycle pulse: begin a transfer
  input  wire [7:0] tx_data,
  output reg  [7:0] rx_data,
  output reg        done,                      // one-cycle pulse at the end
  output reg        sclk, mosi, cs_n,
  input  wire       miso
);
  localparam [1:0] IDLE = 2'd0, XFER = 2'd1, FINISH = 2'd2;
  reg [1:0] state;
  reg [$clog2(HALF)-1:0] cnt;                  // clocks within half an SCLK period
  reg [3:0] edges;                             // SCLK edges still to make, minus 1
  reg [7:0] shreg;

  always @(posedge clk) begin
    done <= 1'b0;
    if (rst) begin
      state <= IDLE; sclk <= 1'b0; cs_n <= 1'b1; mosi <= 1'b0; cnt <= 0; edges <= 0;
    end else case (state)
      IDLE:   if (start) begin
                shreg <= tx_data;
                mosi  <= tx_data[7];           // top bit out before the first rising edge
                cs_n  <= 1'b0;
                cnt   <= 0;
                edges <= 4'd15;                // 16 edges: 8 rising, 8 falling
                state <= XFER;
              end
      XFER:   if (cnt == HALF - 1) begin
                cnt  <= 0;
                sclk <= ~sclk;                 // one SCLK edge
                if (!sclk) shreg <= {shreg[6:0], miso};   // rising edge: sample MISO
                else       mosi  <= shreg[7];             // falling edge: next bit out
                if (edges == 0) state <= FINISH;
                else            edges <= edges - 1;
              end else cnt <= cnt + 1;
      FINISH: begin
                cs_n <= 1'b1; rx_data <= shreg; done <= 1'b1; state <= IDLE;
              end
    endcase
  end
endmodule

sclk <= ~sclk uses the old value of sclk, so if (!sclk) means "SCLK was 0, so this edge is a rising one". After 16 edges SCLK is back at 0, where mode 0 says it must rest.

In plain words

An SPI transfer is a swap. The master's byte leaves from the top of its shift register while the slave's byte arrives at the bottom. After eight clock cycles the swap is complete.

Common mistake

Changing MOSI on the rising edge in mode 0. The slave samples on that same edge, so it may catch the old bit or the new one. In mode 0 the data must change on the falling edge, half a period away from the sampling edge - just as a testbench changes inputs away from the clock.

Quick check

In SPI mode 0, on which edge of SCLK does the slave read MOSI?

Show the answer

Answer: B. In mode 0 both sides sample on the rising edge and change their data on the falling edge. That gives each bit half a clock period to settle before it is read.

8.6 Bus handshake controller

On a bus, each transfer is agreed by a handshake. The master says "here is a request" and waits until the device says "done" - or until a timeout says it has waited too long.

A two-phase bus

Inside a chip, a processor talks to devices - timers, UARTs, GPIO pins - over a shared set of wires. The rules for using those wires are the bus protocol. The circuit that starts each transfer is the bus master.

A simple and popular style of protocol splits every transfer into two phases. ARM's APB bus works this way. Our version writes one byte to one device:

  1. SETUP (one cycle): the master drives the address and the data, and raises sel to select the device.
  2. ACCESS: the master also raises en. It then waits, cycle after cycle, until the device raises ready to say "done".
  3. The master lowers sel and en, and reports ok.

The device's ready answers the master's request. A pair of signals like this, by which two circuits agree that a transfer has happened, is a handshake.

Never wait for ever

What if the device is broken, or the address is wrong, and ready never comes? Without care, the master would sit in ACCESS for ever, and the whole chip would hang. So the master uses the timeout pattern from Volume 07. It loads a timer on entry to ACCESS, and gives up with an err pulse if the timer runs out first.


module bus_master (
  input  wire       clk, rst,
  input  wire       go,                    // one-cycle pulse: do one write
  input  wire [7:0] addr_in, data_in,
  output reg        sel, en,               // the bus control signals
  output reg  [7:0] addr, wdata,           // held steady through the transfer
  input  wire       ready,                 // from the device: "done"
  output reg        ok, err                // one-cycle results
);
  localparam [1:0] IDLE = 2'd0, SETUP = 2'd1, ACCESS = 2'd2;
  localparam [3:0] T_WAIT = 4'd15;         // give up after 16 cycles of ACCESS
  reg [1:0] state;
  reg [3:0] timer;

  always @(posedge clk) begin
    ok <= 1'b0; err <= 1'b0;
    if (rst) begin
      state <= IDLE; sel <= 1'b0; en <= 1'b0;
    end else case (state)
      IDLE:   if (go) begin
                addr <= addr_in; wdata <= data_in;
                sel  <= 1'b1;                          // SETUP phase starts
                state <= SETUP;
              end
      SETUP:  begin en <= 1'b1; timer <= T_WAIT; state <= ACCESS; end
      ACCESS: if (ready) begin                         // the device is done
                sel <= 1'b0; en <= 1'b0; ok <= 1'b1; state <= IDLE;
              end else if (timer == 4'd0) begin        // waited too long
                sel <= 1'b0; en <= 1'b0; err <= 1'b1; state <= IDLE;
              end else
                timer <= timer - 1;
    endcase
  end
endmodule
A two-phase bus write: one setup cycle, then access until the device is ready 0 1 2 3 4 5 6 7 clk go state IDLE SETUP ACCESS IDLE sel en ready ok
Figure 8.10 - A write that takes three access cycles. SETUP lasts exactly one cycle with sel = 1 and en = 0. In ACCESS both are 1 until the device raises ready in cycle 4. At the next edge the master ends the transfer and pulses ok.

Every output here comes from a flip-flop, so the device sees clean bus signals, and ok and err are single clean pulses.

Common mistake

Letting addr and wdata change during ACCESS. The device may read them in any cycle of the access phase, so the master must hold them steady from SETUP until the transfer ends. Here they are loaded once, in IDLE, and not touched again until the next transfer.

Quick check

A device never raises ready. What does this bus master do?

Show the answer

Answer: C. On entry to ACCESS the timer is loaded with 15, and it counts down while ready stays 0. When it reaches 0 - after 16 cycles in ACCESS - the master lowers sel and en, pulses err, and returns to IDLE, so the chip does not hang.

What you learned

Key words from this volume

Every word below has a plain-English entry in the glossary.

Practice

Practice 1

Add an all-red pause

At a real crossing, both roads show red for a moment between one road's yellow and the other road's green, to let the crossing clear. Add this to the crossing controller. Which states do you add, and what are their arrows?

Show the solution

Add two states, one after each yellow: AR1 after MY, and AR2 after SY. In both, every lamp is red. Each loads a short time on entry, waits for done, and moves on: MY → AR1 → SG, and SY → AR2 → MG. The outputs change only in the output logic: main_r and side_r are both 1 in AR1 and AR2. Because the outputs come from the states, no other part of the design changes.

Practice 2

A 20 price

The drink now costs 20, and the machine still takes coins of 5 and 10. How many states does it need, and when must it give change?

Show the solution

It needs one state per amount below the price: 0, 5, 10 and 15 - four states. It gives change only when the money passes 20: from 15, a 10 coin makes 25, so the machine vends and returns a 5. From 10, a 10 coin makes exactly 20, and from 15, a 5 coin makes exactly 20 - both vend with no change.

Practice 3

Check the UART sums

A UART runs at 9600 baud from a 16 MHz clock. What should CPB be? How long does one frame take?

Show the solution

CPB = 16,000,000 / 9,600 = 1666.7, so use 1667 clock cycles per bit - an error of about 0.02%, which is fine. A frame has 10 bits (start, 8 data, stop), so it lasts 10 / 9600 seconds, about 1.04 milliseconds.

Interview corner

Interview question 1

Design a UART receiver

"How does a UART receiver find the bits without a clock from the transmitter?"

Show the solution

"Both sides agree on the baud rate in advance. The line idles at 1. The receiver synchronises the line, then waits for it to fall - the start of the start bit. It waits half a bit time and checks that the line is still 0, which rejects noise spikes. From that middle point it samples once every bit time, so each sample lands in the middle of a data bit, where the value is most stable. After eight data bits it checks that the stop bit is 1; if it is not, that is a framing error. Because it re-aligns on every start bit, small differences between the two clocks do not add up."

Interview question 2

Vending machine

"Design a vending machine FSM for a 15-rupee item that accepts 5- and 10-rupee coins and returns change."

Show the solution

State the assumptions first: one coin per cycle at most, and change only ever needs one 5 coin. Then: "The states are the money held: 0, 5 and 10. From 0, a 5 goes to 5 and a 10 goes to 10. From 5, a 5 goes to 10, and a 10 vends and returns to 0. From 10, a 5 vends; a 10 vends and returns a 5 coin. I would make it Mealy, so it sells when the coin arrives. I would also register the outputs, so the dispenser sees clean one-cycle pulses." Then offer the money check - 15 × vends + 5 × change + credit = coins in - as the testbench assertion.

Next, Volume 09 looks at state machines working together: a controller steering a datapath, two machines that handshake, and machines on two different clocks.