πŸ“Œ 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 for always_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 inside for 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.
  • 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.
  • 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:

    1. Seq: always_ff @(posedge clk) + <=.
    2. Simple comb: assign.
    3. Complex comb: always_comb + =, full assignments.
    4. Assign signal once only.

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=0
    

    Code:

    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);
    endmodule

    Diagram:

    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

  1. 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.
  2. 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.
  3. 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.