A Fixed-Cycle, Timer-Driven Controller
Reading a written spec for a phase-cycling controller, and implementing it with a built-in safety property, using the dual-block FSM pattern.
Updated 2026-07-21
Not every FSM branches on input the way a sequence detector does. Some FSMs simply cycle through a fixed sequence of phases, moving to the next one whenever a single "advance" signal pulses — a real pattern behind traffic lights, simple sequencers, and any controller with a strict, non-negotiable order of operations.
Reading a spec like this starts the same way as any other FSM: find the states first. A two-road intersection controller with a phase for each road's green light and each road's yellow light has four states — one condition per phase, nothing more.
reg [1:0] phase, phase_next;
always @(posedge clk) begin
if (reset) phase <= MAIN_GO;
else phase <= phase_next;
end
always @(*) begin
phase_next = phase; // safe default: hold the current phase
case (phase)
MAIN_GO: if (advance) phase_next = MAIN_YIELD;
MAIN_YIELD: if (advance) phase_next = CROSS_GO;
CROSS_GO: if (advance) phase_next = CROSS_YIELD;
CROSS_YIELD: if (advance) phase_next = MAIN_GO;
endcase
endEvery case item follows the exact same shape: if advance fires, move to the next phase in the fixed order; otherwise, the default-at-top assignment holds the current phase. There is no branching logic to design here — the order is given by the spec, not discovered by reasoning about inputs.
The safety property — two directions must never show green at the same time — does not need a special check bolted on afterward. It falls out of the output logic for free, as long as each output is assigned from its own distinct state comparison:
assign main_go = (phase == MAIN_GO);
assign cross_go = (phase == CROSS_GO);Since the FSM can only occupy one state at a time, and each output only reads true for exactly one state, main_go and cross_go can never both be true simultaneously — the mutual exclusivity is a direct consequence of the state encoding, not something tested in afterward as an extra rule.