EE1D2 Lecture 2 — SystemVerilog Study Guide
📚 Key Concepts
1. Combinational Logic in SystemVerilog (always_comb)
The always_comb block is used to describe combinational (stateless) logic. It re-evaluates whenever any input signal changes.
Important rules:
- Every output signal must be assigned a value on every possible path through the block. If a path exists where an output is not assigned, the synthesiser will infer a latch to hold the previous value — which is almost always unintended.
- Use default assignments at the top of the block to avoid accidental latches:
always_comb begin
z = '0; // safe default
next_state = S0; // safe default
unique case (state)
...
endcase
end
Common operators:
| Operator | Meaning | Example |
|---|---|---|
& |
Bitwise AND | u = a & b |
| |
Bitwise OR | v = b | c |
^ |
Bitwise XOR | s = a ^ b |
+ |
Addition | w = a + b |
{a,b} |
Concatenation | z = {u,v} |
&var |
Reduction AND | s = &tmp |
|var |
Reduction OR | s = |tmp |
2. Latches vs. No Latches
A latch is inferred when a signal in an always_comb block is not assigned on every branch.
// ⚠️ LATCH INFERRED — z is unassigned when add=0 and sub=0
always_comb
if (add) z = x + y;
else if (sub) z = x - y;
// missing else!
The unique case keyword tells the compiler the case is complete and mutually exclusive, preventing a latch even if not all values are listed:
always_comb begin
unique case (ctrl)
2'b00: z = x;
2'b01: z = x + y;
2'b10: z = x - y;
// 2'b11 not listed — unique guarantees no latch
endcase
end
3. Generate Blocks
Generate blocks allow you to instantiate hardware structures in a loop — useful for parameterised designs.
generate
genvar i;
for (i = 0; i < N; i = i + 1)
and g1 (tmp[i], a[i], b[i]);
endgenerate
assign s = |tmp; // reduction OR across all bits
Key points:
genvaris the loop variable type (notintorlogic).- The wire
tmpmust be the same width asaandb, i.e.,[N-1:0] tmp. - After AND-ing each pair of bits, use a reduction OR (
|tmp) to produce a single output.
4. Module Instantiation & Port Maps
Modules are connected using port maps. When using positional mapping, the order of signals in the instantiation must exactly match the order in the module's port declaration.
// Module declaration
module half_adder (input logic a, b, output logic sum, c_out);
// Correct instantiation (positional)
half_adder adder_0 (a[0], b[0], sum[0], s1);
// ^a ^b ^sum ^c_out
For a ripple-carry adder, the carry-out of each stage connects to the carry-in of the next:
[half_adder: a[0],b[0]] --c_out=s1--> [full_adder: a[1],b[1],c_in=s1] --c_out=s2--> [full_adder: a[2],b[2],c_in=s2]
5. Flip-Flops (always_ff)
Flip-flops are sequential elements sensitive to clock edges. The sensitivity list determines the behaviour.
// Negative-edge triggered FF with synchronous reset
always_ff @(negedge clk)
if (reset) q <= 0;
else q <= d;
// Positive-edge triggered FF with ASYNCHRONOUS reset
always_ff @(posedge clk, posedge reset)
if (reset) q <= 0;
else q <= d;
Key distinctions:
| Synchronous Reset | Asynchronous Reset | |
|---|---|---|
| Reset triggers on | Clock edge only | Reset signal edge (any time) |
| Sensitivity list | @(posedge clk) |
@(posedge clk, posedge reset) |
⚠️ If the sensitivity list is only
@(posedge reset), the block describes combinational logic driven by reset — not a flip-flop at all.
6. Finite State Machines (FSMs)
Moore FSM
Output depends only on the current state.
typedef enum logic [1:0] {S0, S1, S2, S3} state_t;
state_t state, next_state;
// State register
always_ff @(posedge clk)
if (reset) state <= S0;
else state <= next_state;
// Next-state and output logic
always_comb begin
next_state = S0; // default
y = 1'b0; // default output
unique case (state)
S0: if (x) next_state = S1;
S1: if (~x) next_state = S2;
S2: if (x) next_state = S3;
S3: y = 1'b1;
endcase
end
Mealy FSM
Output depends on both the current state and the current input. Transitions and outputs are typically written together in the always_comb block.
Common mistakes in FSM code
- Missing
enumkeyword in typedef. - Reset state set to a non-initial state.
- Missing default assignments for
next_stateor output signals — causes latches. - Forgetting to reset output
yto 0 before the case statement.
✏️ Exercises
Exercise 1 — Bitwise Operations & Concatenation
Given the SystemVerilog code below, with inputs a = 101, b = 001, c = 100, what is the output z?
module random_function (
input logic [2:0] a, b, c,
output logic [5:0] z
);
logic [2:0] u, v, w, x;
always_comb begin
u = a & b;
v = b | c;
w = a + b;
x = b + c;
if (w < x)
z = {u, v};
else
z = {v, u};
end
endmodule
Options:
- a.
z = 001101 - b.
z = 101001 - c.
z = 110010 - d.
z = 011001
How to approach it
- Evaluate each intermediate signal by substituting the input values.
- Determine the condition of the
ifstatement. - Apply the correct concatenation.
Step-by-step:
u = a & b = 101 & 001 = 001v = b | c = 001 | 100 = 101w = a + b = 101 + 001 = 110x = b + c = 001 + 100 = 101- Is
w < x? Is6 < 5? No → take theelsebranch:z = {v, u} = {101, 001} = 101001
Answer: b
Exercise 2 — Generate Block & Parameterised Design
The code below implements the N-bit AND-then-OR circuit shown. Fill in the three missing locations.
module and_or_n #(parameter N = 4) (
input logic [N-1:0] a, b,
output logic s
);
logic <??1>;
generate
genvar i;
for <??2>
and g_1 (tmp[i], a[i], b[i]);
endgenerate
assign <??3>;
endmodule
Options:
- a.
<??1>: [N:0] tmp - b.
<??2>: (i = 0; i < N; i = i + 1) - c.
<??3>: s = &tmp - d. All answers above are incorrect
How to approach it
tmpmust hold one AND result per bit pair, so it needsNbits →[N-1:0] tmp, not[N:0]. Eliminates a.- The
forloop must iterate from0toN-1. Option b is correct syntax. - The final assign needs a reduction OR (any AND pair being 1 makes s high), so
s = |tmp, not&tmp. Eliminates c.
Since only b is fully correct on its own, the answer is b.
Answer: b
Exercise 3 — Latches & the unique case Keyword
Given two codes and two statements, determine which are true:
- Code 1 can create a latch during synthesis.
- Code 2 can create a latch during synthesis.
// Code 1
always_comb
if (add) z = x + y;
else if (sub) z = x - y;
// No else — z is unassigned when add=0, sub=0
// Code 2
always_comb begin
unique case (ctrl)
2'b00: z = x;
2'b01: z = x + y;
2'b10: z = x - y;
// 2'b11 not covered
endcase
end
Options:
- a. Statement 1 true, statement 2 false
- b. Statement 1 false, statement 2 true
- c. Both false
- d. Both true
How to approach it
- Code 1: When
add = 0andsub = 0,zis never assigned → latch is inferred. Statement 1 is true. - Code 2: The
uniquekeyword informs the compiler thatctrl = 2'b11will never occur, so no latch is needed. Statement 2 is false.
Answer: a
Exercise 4 — Module Instantiation (Port Maps)
The code implements a 3-bit ripple-carry adder. How should the port maps be initialised?
module adder_3bit (
input logic [2:0] a, b,
output logic [2:0] sum,
output logic c_out
);
logic s1, s2;
<???>
endmodule
Options:
- a.
full_adder adder_0 (a[0], b[0], sum[0], s1);... - b.
half_adder adder_0 (a, b, sum[0], s1);... - c.
half_adder adder_0 (a[0], b[0], sum[0], s1);... - d.
half_adder adder_0 (sum[0], s1, a[0], b[0]);...
How to approach it
- The first stage has no carry in → use a half adder.
- Stages 1 and 2 take a carry in from the previous stage → use full adders.
- Individual bits must be selected:
a[0],b[0], etc. (not the whole bus). - Port order must match the module declaration exactly (positional mapping).
- The carry chain:
s1connects adder_0's carry-out to adder_1's carry-in;s2connects adder_1 to adder_2.
Answer: c
Exercise 5 — D Flip-Flop Types
Given two codes and two statements:
- Code 1 describes a negative-edge triggered D-FF with synchronous reset.
- Code 2 describes a positive-edge triggered D-FF with asynchronous reset.
// Code 1
always_ff @(negedge clk)
if (reset) q = 0;
else q = d;
// Code 2
always_ff @(posedge reset)
if (reset) q = 0;
else q = d;
How to approach it
- Code 1: Sensitivity list is
negedge clkonly → negative-edge triggered, reset is synchronous (evaluated only on clock edge). Statement 1 is true. - Code 2: Sensitivity list is
posedge resetonly — there is no clock at all. This describes a circuit that evaluates!reset && d, not a flip-flop. Statement 2 is false.
Answer: a
Exercise 6 — Moore FSM Implementation
The code below implements a Moore FSM that detects the sequence 101 in input x. What should be written at the three marked locations?
module detect_101 (
input logic clk, reset, x,
output logic y
);
<??1>
state_t state, next_state;
always_ff @(posedge clk)
if (reset) <??2>
else state <= next_state;
always_comb begin
<??3>
unique case (state)
S0: if (x) next_state = S1;
S1: if (~x) next_state = S2;
S2: if (x) next_state = S3;
S3: y = 1'b1;
endcase
end
endmodule
Options:
- a.
<??1>: typedef {S0,S1,S3,S4} states - b.
<??2>: state <= S2 - c.
<??3>: next_state = S0; - d. None of the above are both correct and sufficient
How to approach it
<??1>: The correct syntax requires theenumkeyword and must use the type namestate_t:typedef enum logic [1:0] {S0,S1,S2,S3} state_t;— option a is wrong.<??2>: Reset should send the FSM to the initial state, which isS0, notS2— option b is wrong.<??3>: Option c sets a default fornext_state, which is correct, but it is not sufficient on its own — the outputyalso needs a default value (y = 1'b0;) to prevent a latch ony.
Answer: d
Exercise 7 — Mealy FSM Diagram
The always_comb block below describes a Mealy machine. Which FSM diagram (a–d) matches it?
always_comb begin
next_state = S2; // default
y = 0; // default
unique case (state)
S0: if (x) next_state = S1;
else y = 1;
S1: if (x) y = 1;
else next_state = S0;
S2: if (x) next_state = S0;
else y = 1;
endcase
end
How to approach it
Build a transition table from the code. For each state, determine (next_state, y) for both x=0 and x=1, using the defaults where not overridden:
| State | x=0 | x=1 |
|---|---|---|
| S0 | stay S0, y=1 | go to S1, y=0 |
| S1 | go to S0, y=0 | stay S2 (default), y=1 |
| S2 | stay S2 (default), y=1 | go to S0, y=0 |
Match this table to the FSM diagram where each arc is labelled input/output. Diagram a reflects these transitions correctly.
Answer: a
🧠 Quick-Reference Cheat Sheet
| Concept | Watch out for |
|---|---|
always_comb |
Assign every output on every path, or use defaults at the top |
| Latches | Any unassigned output path → latch inferred |
unique case |
Tells compiler case is complete — suppresses latch, enables warnings |
| Generate loop | genvar for loop variable; tmp must be [N-1:0] not [N:0] |
Reduction OR |var |
OR all bits together into one bit |
| Port map (positional) | Order must exactly match module declaration |
always_ff sensitivity list |
Must include posedge clk (and optionally posedge reset for async) |
| Synchronous reset | Only in sensitivity list as posedge clk |
| Asynchronous reset | Add posedge reset to sensitivity list |
| FSM defaults | Always set next_state and all outputs before the case |
| Moore FSM | Output depends on state only |
| Mealy FSM | Output depends on state and input |