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.
- 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
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:
- The main road is green for at least a minimum time. After that, it stays green until a car waits on the side road.
- The main road then shows yellow for a short time.
- The side road is green for a fixed time, then yellow.
- 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.
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.
For a real crossing, the times are seconds, so the timer counts ticks of a prescaler rather than clock cycles, as in Volume 07.
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.
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 |
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
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.
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.
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:
- remember every call until it is served
- move towards waiting calls, one floor at a time
- stop at any floor on its way that has a call, and open the door for a while
- never move with the door open
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:
floor- a 2-bit register holding 0, 1 or 2.req- a 3-bit register with one bit per floor. A press sets that floor's bit; serving the floor clears it.timer- the travel time between floors, and the time the door stays open.
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
- IDLE: if there is a call at this floor ("here"), open the door. Otherwise, if any call is above, go UP; if any is below, go DOWN.
- UP and DOWN: when the travel timer runs out, the lift has reached the next floor. If that floor has a call, stop and open the door; otherwise keep going.
- DOOR: when the door timer runs out, clear this floor's call, and go back 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 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.
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.
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:
- a start bit, always 0 - the line falls, and the receiver knows a byte is coming
- 8 data bits, lowest bit first
- a stop bit, always 1 - the line returns to rest
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.
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.
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:
- Synchronise the line with two flip-flops, as in Volume 07 - it comes from another chip.
- IDLE: wait for the line to fall to 0. That may be a start bit.
- 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.
- 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.
- STOP: one bit time later, sample the stop bit. If it is 1, report the byte with a one-cycle
validpulse.
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.
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.
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:
- SCLK rests at 0 between transfers.
- Both sides sample on the rising edge of SCLK, and change their data on the falling edge.
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.
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.
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.
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.
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:
- SETUP (one cycle): the master drives the address and the data, and raises sel to select the device.
- ACCESS: the master also raises en. It then waits, cycle after cycle, until the device raises ready to say "done".
- 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
Every output here comes from a flip-flop, so the device sees clean bus signals, and ok and err are single clean pulses.
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.
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
- A traffic light is a small Moore machine and one shared timer; a timer that stops at 0 lets an input end a state at once.
- A vending machine's states are the money held; Mealy arrows sell at the moment the coin arrives, and flip-flops make the outputs clean.
- A lift keeps "what am I doing" in its states and "where am I, who is waiting" in registers - a state machine with a datapath.
- A UART frames each byte with a start bit and a stop bit; the receiver finds the middle of each bit from the start bit alone.
- SPI carries a clock; in mode 0 both sides sample on the rising edge and change on the falling edge, swapping one bit per cycle.
- A bus master handshakes with each device, and a timeout makes sure it can never hang.
Key words from this volume
Every word below has a plain-English entry in the glossary.
- Prescaler
- Datapath
- UART
- Start bit
- Stop bit
- Baud rate
- Framing error
- SPI
- Chip select
- MOSI and MISO
- Bus protocol
- Bus master
- Handshake
- Timeout
Practice
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.
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.
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
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."
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.