π Overview
This lecture covers SystemVerilog for sequential circuits: latches/flip-flops, always statements for sequential/combinational logic, FSMs (Moore/Mealy), and counters. Emphasizes idioms for reliable simulation/synthesis, avoiding latches, using nonblocking assignments in sequential blocks. Builds on prior combinational logic and sequential basics.
Sequential logic uses clocked registers (FFs) for state; combinational is stateless. Always describe hardware: registers + logic.
π―Learning Objectives
- Describe latches and flip-flops in SystemVerilog.
- Use the always statement in SystemVerilog to describe sequential circuits and combinational circuits.
- Describe Finite-State Machines in SystemVerilog.
- Describe counters in SystemVerilog.
- Simulate a sequential circuit in SystemVerilog
π‘Key Concepts & Definitions
β Formulas
- Nonblocking (
<=): Deferred update, for sequential (FFs, FSMs). - Blocking (
=): Immediate update, for combinational. - Sensitivity:
@(posedge clk)for FF; no list foralways_comb/always_latch. - FSM: State register + next-state logic + output logic.
- Moore: Output from state only; Mealy: Output from state + inputs (faster, but glitch-prone).
βοΈ Notes
1. Sequential Logic Basics
SystemVerilog uses specific idioms for hardware synthesis. Avoid general styles that simulate ok but synthesize wrong (e.g., unintended latches).
-
D Flip-Flop (FF): Edge-triggered register.
module dff(input logic clk, d, output logic q); always_ff @(posedge clk) q <= d; // Rising edge: q gets d nonblocking endmodule- Visual:
\usepackage{circuitikz} \begin{document} \begin{circuitikz} \draw (0,0) node[dff] (D) {D}; \draw (D.Q) -- ++(1,0) node[right] {q}; \draw (0,-1) -- (D.D) node[above] {d}; \draw (0.5,-2) rectangle (1.5,-1.5); \draw (1,-1.75) node {clk}; \draw (0.5,-2) -- ++(-0.5,0) -- ++(0,0.5) -- (D.clk); \end{circuitikz} \end{document}- With async reset (preferred for known power-up state):
always_ff @(posedge clk, posedge reset) if (reset) q <= 0; else q <= d;- Sync reset: Reset only on clock edge (inside if).
- When? Always use resettable FFs. Async for immediate reset; sync if timing-critical.
-
4-bit Register (with enable):
module reg4(input logic clk, reset, en, input logic [3:0] d, output logic [3:0] q); always_ff @(posedge clk, posedge reset) if (reset) q <= 4'b0; else if (en) q <= d; // Holds if !en endmodule- Rare: If no reset/en, q β d always (but add for robustness).
-
Latch: Level-sensitive (transparent when clk=1). Avoid! Causes timing loops.
module latch(input logic clk, d, output logic q); always_latch if (clk) q <= d; // No sensitivity list endmodule- Why avoid? Glitches, hard timing. Use FFs instead.
2. Always Statements
Describe behavior; synthesis infers hardware.
-
always_comb: Combinational (no state). No sensitivity list; evaluates on any input change. Use blocking
=.module adder(input logic a, b, cin, output logic s, cout); always_comb begin logic p = a ^ b; // Temp vars ok logic q = a & b; s = p ^ cin; cout = q | (p & cin); end endmodule- Rule: Assign all outputs in all paths (else infers latch!). E.g., if-else must cover cases.
- Bad:
if (s) y = x1;β Latch on y. - Good:
if (s) y = x1; else y = x0;
-
Case Statement (for decoders):
module sevenseg(input logic [3:0] data, output logic [6:0] segments); always_comb case(data) 0: segments = 7'b1111110; // a-g segments // ... (up to 9) default: segments = 7'b0000000; // Must have default! endcase endmodule- Donβt cares: Use
insidefor simplification (e.g., priority encoder).
module priority(input logic [3:0] a, output logic [3:0] y); always_comb case(a) inside 4'b1??? : y = 4'b1000; // ? = don't care // ... default: y = 4'b0000; endcase endmodule- Rare: Overlapping cases? Use priority if/else.
- Donβt cares: Use
-
If Statement (nested for priority):
module priority(input logic [3:0] a, output logic [3:0] y); always_comb if (a[3]) y = 4'b1000; else if (a[2]) y = 4'b0100; // ... else y = 0; endmodule -
General always: For testbenches only. With/without sensitivity list.
- Prefer:
always_ff(seq),always_latch,always_comb.
- Prefer:
-
Blocking (=) vs Nonblocking (β):
<=: All updates after block (parallel hardware). For seq: FFs/FSMs.=: Immediate (serial). For comb.- Bad seq example (blocking):
logic n1; always_ff @(posedge clk) { n1 = d; q = n1; } // q gets old n1 β 1-cycle delay wrong- Good:
always_ff @(posedge clk) { n1 <= d; q <= n1; } // q gets new d next cycle (synchronizer)- In
always_comb: Use=(order matters for temps). - Rare issue: Race conditions in sim if mixing in seq.
-
Rules Summary:
- Seq:
always_ff @(posedge clk)+<=. - Simple comb:
assign. - Complex comb:
always_comb+=, full assignments. - Assign signal once only.
- Seq:
3. Finite State Machines (FSMs)
3 blocks: State reg (FFs), next-state logic (comb), output logic (comb).
-
States:
typedef enum logic [N-1:0] {S0, S1, ...} statetype; -
One-hot/gray/binary encoding (synthesis chooses).
-
Moore FSM: Output depends on state only (stable).
-
Mealy FSM: Output on state + inputs (fewer states, but timing-sensitive).
-
Example 1: Divide-by-3 Counter (Moore, output y=1 every 3rd clk). State diagram:
stateDiagram-v2 [*] --> S0 S0 --> S1 : clk S1 --> S2 : clk S2 --> S0 : clk note right of S0 : y=1 note right of S1 : y=0 note right of S2 : y=0Code:
module div3(input logic clk, reset, output logic y); typedef enum logic [1:0] {S0, S1, S2} statetype; statetype state, nextstate; always_ff @(posedge clk, posedge reset) if (reset) state <= S0; else state <= nextstate; always_comb case(state) S0: nextstate = S1; S1: nextstate = S2; S2: nextstate = S0; default: nextstate = S0; endcase assign y = (state == S0); // Output logic endmodule- When? Simple cycles. Default prevents latches.
-
Example 2: Sequence Detector (Moore): Detect β1001β in input stream a; y=1 on match. States: S0 (start), S1 (1 seen? Wait no: for β1001β). From slides: Detects β1001β? Diagram shows S0-0βS1-0βS2-1βS0, but adjust for seq. Assume standard: Overlapping β1001β. But follow slides: y=1 in S2 after 100. Wait, slides Example 2: Seems detect β100β (S0-a=0βS1, a=0βS1? Wait, code: S0: a? S0 : S1 S1: a? S2 : S1 S2: a? S0 : S1 y = (state==S2) // After two 0s then 1? Detects β001β? Clarify: Input a serial, detects sequence ending with 1 after two 0s? But label βSequence Detectorβ. For notes: Use as-is.
module seq_moore(input logic clk, reset, a, output logic y); // As in slides // ... assign y = (state == S2); endmoduleDiagram:
stateDiagram-v2 [*] --> S0 S0 --> S0 : a=1 S0 --> S1 : a=0 S1 --> S2 : a=1 S1 --> S1 : a=0 S2 --> S0 : a=1 S2 --> S1 : a=0 note right of S2 : y=1 -
Example 3: Sequence Detector (Mealy): Same, but y=1 on transition (fewer states). Diagram: S0 -1/1β S1, S0 -0/0β S0; S1 -1/0β S0, S1 -0/0β S1? Slides: Detect β10β perhaps. Code combines next/output:
module seq_mealy(input logic clk, reset, a, output logic y); typedef enum logic {S0, S1} statetype; statetype state, nextstate; always_ff @(posedge clk, posedge reset) if (reset) state <= S0; else state <= nextstate; always_comb begin y = 0; case(state) S0: if (a) nextstate = S0; else nextstate = S1; S1: if (a) {nextstate = S0; y = 1;} else nextstate = S1; default: nextstate = S0; endcase end endmodule- Moore vs Mealy: Moore glitch-free; Mealy compact but output timing critical (use FF if needed).
- Rare: Combinational loops in output logic β Add FF.
-
Testbench (Simulate):
`timescale 1ns/1ps module tb(); logic clk=0, reset, a, y; dut dut(...); always #10 clk = ~clk; // 20ns period initial begin reset=1; a=0; #20; reset=0; #20; a=1; // Change on neg edge: #10 after pos // Add $dumpfile("dump.vcd"); $dumpvars; end endmodule- Rule: Change inputs on neg clk to avoid setup/hold violations.
4. Counters
Special FSM: State = count, next = count +1 if en.
module counter(input logic clk, reset, en, output logic [7:0] count);
logic [7:0] next_count;
always_ff @(posedge clk)
if (reset) count <= 0; else count <= next_count;
always_comb
if (en) next_count = count + 1; else next_count = count;
endmodule- Visual:
stateDiagram-v2 [*] --> Count0 Count0 --> Count1 : en & clk Count1 --> Count2 : en & clk %% ... up to 255 Count255 --> Count0 : en & clk (overflow) note right of Count : state = count value
- When? For sequencing. Add load:
if (load) next = load_val;. - Rare: Signed? Use signed types, but watch overflow.
Example Questions & Stepwise Solutions
-
Design a 2-bit up-counter with sync reset and enable.
- Step 1: Define module: inputs clk, reset, en; output [1:0] q.
- Step 2: State reg: always_ff @(posedge clk) if(reset) qβ0; else if(en) qβq+1;
- Step 3: No next logic needed (inline). But for clarity: next = en ? q+1 : q;
- Step 4: Simulate: Testbench with clk, assert reset, en=1, check 00β01β10β11β00.
- Code:
module cnt2(input logic clk, reset, en, output logic [1:0] q); always_ff @(posedge clk) if(reset) q <= 0; else if(en) q <= q + 1; endmodule - Allowed: Yes, simple. Not: Async reset if sync preferred.
-
Implement Moore FSM to detect β101β in serial input x; output z=1 on detection.
- Step 1: States: S0 (init), S1 (1 seen), S2 (10 seen), S3 (101 detected, but Moore: stay or reset).
- Step 2: Transitions:
- S0: x=0βS0, x=1βS1
- S1: x=0βS2, x=1βS1
- S2: x=0βS0, x=1βS3? For overlapping: S2 x=1βS1 (since ends with 1). Standard: S0-1βS1-0βS2-1βS0 (z=1 in S0? Wait, detect on entering S0 after 101? Adjust. Simple non-overlap: z=1 in S3, S3βS0 on next.
- Step 3: Code skeleton as div3, output z=(state==S3).
- Step 4: Test: Input 101 β z pulse.
- Rare: If no default β Latch! Always include.
-
Why does blocking in FF cause wrong synth? Fix a bad synchronizer.
- Step 1: Bad: n1 = d; q = n1; β Synth as comb chain, not 2 FFs.
- Step 2: Fix: n1 β d; q β n1; β Parallel FFs, q delayed by 2 cycles.
- Step 3: Sim: d=1 at t=0, posedge1: n1=1,q=0; posedge2: n1=1,q=1.
- Allowed: β in seq always_ff. Not: = in seq (races).
These cover core; practice sim with Icarus/ModelSim.
π Resources
-
Presentation:
-
Book: DDCA Ch. 4.4-4.6
β Post lecture
- Simulate div3 FSM: Add $monitor in tb, verify y every 3 cycles.
- Why avoid latches? Timing analysis ex.
π Homework
- Implement Mealy β1101β detector.
- Counter with load from input [3:0].
- Testbench for priority encoder.