3.5.1 · D4HDL & Digital Design Flow

Exercises — Verilog - VHDL syntax basics

3,462 words16 min readBack to topic

Level 1 — Recognition

Exercise L1.1 — Name the connection

State whether each Verilog line describes combinational logic (output is a function, always) or sequential logic (output remembers, updates on a clock edge). (Reminder: ^ is XOR, & is AND, <= is the clocked non-blocking assignment — all defined just above.)

  1. assign y = a ^ b;
  2. always @(posedge clk) q <= d;
  3. always @(*) y = a & b;
Recall Solution L1.1

The clue is what triggers the update.

  1. assign = continuousy re-evaluates the instant a or b changes. Combinational. (^ is XOR: 1 when the two bits differ.)
  2. always @(posedge clk) fires only on a rising clock edge, and stores d into q until the next edge; <= marks it as a register update. Sequential.
  3. always @(*) re-runs whenever any right-hand-side input changes, with no clock, so y tracks a & b (AND) continuously. Combinational.

Rule map: assign and always @(*) → combinational; always @(posedge clk) → sequential. See Combinational vs Sequential Logic.

Exercise L1.2 — Pick the assignment operator

For each block, which operator belongs in the blank, = or <=? (Reminder: | is bitwise OR — 1 if either input is 1.)

  1. always @(posedge clk) begin q ___ d; end
  2. always @(*) begin y ___ a | b; end
Recall Solution L1.2

Apply the Golden Rule: clocked → <=, combinational → =.

  1. This is posedge clk (clocked) → <= (non-blocking).
  2. This is always @(*) (combinational) → = (blocking).

Mnemonic from the parent: the arrow <= "waits for the edge"; plain = "equals now."

Exercise L1.3 — Read a literal

What decimal value does 4'hC represent, and how many wires does it drive?

Recall Solution L1.3

The prefix 4'h means 4 bits wide, base hexadecimal. The hex digit C = binary 1100.

  • Binary 1100 = .
  • Value = 12, and it drives 4 wires (one physical line per bit). See Number Systems and Bit-Widths.

Level 2 — Application

Exercise L2.1 — Concatenation value

Give the 8-bit value (binary and hex) produced by {4'b1100, 4'b0011}.

Recall Solution L2.1

Concatenation {high, low} glues bit-groups left-to-right, high bits first.

  • High nibble 1100, low nibble 001111000011.
  • Group into hex: 1100 = C, 0011 = 38'hC3.
  • Check decimal: , and `0xC3 = 12\cdot16 + 3 = 195$. ✓

Exercise L2.2 — Write a 4-input AND (Verilog and VHDL)

Write a module that outputs y = a AND b AND c AND d using continuous (combinational) assignment, once in Verilog and once in VHDL.

Recall Solution L2.2

Verilog (& = AND):

module and4 (input a, input b, input c, input d, output y);
    assign y = a & b & c & d;   // continuous → combinational
endmodule

VHDL (the interface is the entity, the behaviour is the architecture; and is the keyword, and <= here is a concurrent signal assignment, not a clocked one):

entity and4 is
    port ( a, b, c, d : in  std_logic;
           y          : out std_logic );
end and4;
architecture rtl of and4 is
begin
    y <= a and b and c and d;   -- concurrent → combinational
end rtl;

Why assign / concurrent <=? There is no memory and no clock — y must always equal the AND of the four inputs, instantly. Both forms create a permanent wire relationship.

Exercise L2.3 — Complete the D flip-flop with reset (fill Verilog, then read its VHDL twin)

Fill the blanks so this is a correct positive-edge D flip-flop with synchronous reset to 0.

module dff (input clk, input rst, input d, output reg q);
    always @(_____) begin
        if (rst) q ___ 1'b0;
        else     q ___ d;
    end
endmodule
Recall Solution L2.3

Verilog:

module dff (input clk, input rst, input d, output reg q);
    always @(posedge clk) begin   // synchronous → sample only on clock edge
        if (rst) q <= 1'b0;       // clocked → non-blocking
        else     q <= d;
    end
endmodule

The same flip-flop in VHDL (a process plays the role of always; rising_edge(clk) is posedge clk):

entity dff is
    port ( clk, rst, d : in  std_logic;
           q           : out std_logic );
end dff;
architecture rtl of dff is
begin
    process (clk) begin
        if rising_edge(clk) then      -- synchronous
            if rst = '1' then q <= '0';
            else              q <= d;
            end if;
        end if;
    end process;
end rtl;

Why posedge clk / rising_edge(clk)? Synchronous reset means the reset only acts at the clock edge, so rst lives inside the clocked block. Why <=? Clocked storage → non-blocking. See Flip-Flops and Latches.


Level 3 — Analysis

Exercise L3.1 — Blocking swap

Two registers a and b start at a=1, b=0. What are their values after one clock edge for each block?

Block A (blocking):

always @(posedge clk) begin
    a = b;
    b = a;
end

Block B (non-blocking):

always @(posedge clk) begin
    a <= b;
    b <= a;
end
Recall Solution L3.1

Block A (=, blocking): lines execute in order, updating immediately.

  • a = b;a becomes 0.
  • b = a; → reads the new a (which is now 0) → b becomes 0.
  • Result: a=0, b=0 — the swap is destroyed, a was clobbered.

Block B (<=, non-blocking): all right-hand sides are sampled first (using old values), then all left-hand sides update together.

  • RHS sampled: b=0, a=1 (old values).
  • Then assign: a<=0, b<=1.
  • Result: a=0, b=1 — a genuine swap. ✓

This is why real flip-flops need <=: on one edge every register grabs its input simultaneously. See Synthesis vs Simulation.

Exercise L3.2 — Shift register vs parallel load

Given a=1, b=0, c=0 initially, what are a,b,c after one clock edge?

always @(posedge clk) begin
    b <= a;
    c <= b;
end
Recall Solution L3.2

Non-blocking → all RHS sampled from old values first:

  • old a=1, old b=0.
  • Then: b <= 1, c <= 0.
  • a is not assigned here, so it keeps 1.
  • Result: a=1, b=1, c=0.

Notice c got the old b (0), not the new one. This is the correct shift-register behaviour: on each edge the "wave" moves one stage. Had this been blocking =, b would update to 1 first and c would also grab 1 — collapsing two stages into one edge (a classic bug).

Exercise L3.3 — Spot the inferred latch

Does this combinational block create an unintended latch? If so, on which input pattern is the old value remembered?

always @(*) begin
    if (sel) y = a;
end
Recall Solution L3.3

Yes — it infers a latch. In a combinational block, every output must be assigned on every path.

  • When sel = 1: y = a. Fine.
  • When sel = 0: y is never assigned → the tool must remember the previous y → it builds a latch (memory you didn't ask for).
  • So the old value is held whenever sel = 0.

The figure below traces exactly this decision: follow the two branches out of if (sel) and watch which one fails to drive y.

Fix: give a default, e.g. y = 1'b0; before the if, or add an else y = ...;. Then y is defined on all paths and stays purely combinational.

Figure — the latch decision tree. The block wakes up on always @(*), then splits at if (sel). Follow the mint (sel = 1) branch: y is assigned, so the path is pure combinational (mint box, bottom-left). Follow the coral (sel = 0) branch: y is not assigned, so the tool must remember the old y — a latch is born (coral box, bottom-right). The italic caption shows the fix. Use this picture to see why "assign on every path" removes the latch: it collapses both bottom boxes into the mint one.

Figure — Verilog - VHDL syntax basics

Level 4 — Synthesis

Exercise L4.1 — 2-to-1 multiplexer (no latch)

Write a combinational Verilog module mux2 with inputs a, b, sel and output y, such that y = a when sel = 0 and y = b when sel = 1. Make sure it infers no latch.

Recall Solution L4.1

Every path must assign y, so we use a complete if/else (or a default).

module mux2 (input a, input b, input sel, output reg y);
    always @(*) begin
        if (sel) y = b;   // sel = 1
        else     y = a;   // sel = 0  → every path assigns y
    end
endmodule

Why =? always @(*) is combinational → blocking. Why complete if/else? Both branches drive y, so no old value is ever remembered → no latch. (A one-liner assign y = sel ? b : a; works too and is even cleaner.)

Exercise L4.2 — 4-bit up-counter

Write a Verilog module counter4 with clk, rst, and 4-bit output count. On each rising edge, count increments by 1; a synchronous reset sets it to 0. What value follows count = 4'hF?

Recall Solution L4.2
module counter4 (input clk, input rst, output reg [3:0] count);
    always @(posedge clk) begin
        if (rst) count <= 4'd0;      // clocked → non-blocking
        else     count <= count + 1;
    end
endmodule

[3:0] declares a 4-bit vector (bit 3 = MSB). Wrap-around: 4 bits hold values 0–15. After 4'hF (= 15 = 1111), adding 1 overflows to 10000, but only the low 4 bits are kept → 0000. So 4'hF is followed by 4'h0 — the counter naturally wraps every 16 edges. See Number Systems and Bit-Widths.

Exercise L4.3 — Registered adder

Build add_reg: on each clock edge it stores the sum of two 4-bit inputs x and y into a 5-bit output s (5 bits so the carry-out fits). Give the module.

Recall Solution L4.3
module add_reg (input clk, input [3:0] x, input [3:0] y, output reg [4:0] s);
    always @(posedge clk) begin
        s <= x + y;   // clocked → non-blocking; 5-bit result holds carry
    end
endmodule

Why 5 bits? Two 4-bit numbers max out at , which needs bits (11110). A 4-bit output would drop the carry and give a wrong sum on overflow. Why <=? The result is stored on the edge → sequential → non-blocking.


Level 5 — Mastery

Exercise L5.1 — Simulation vs synthesis mismatch

The following combinational logic uses non-blocking assignments where blocking was needed. Compute y in simulation vs what a synthesizer would build, given a just changed to 1 (previously all signals 0). (& is AND; 1'b1 is a 1-bit constant 1, so a & 1'b1 just equals a.)

always @(*) begin
    t <= a & 1'b1;   // wrong operator for combinational
    y <= t;
end
Recall Solution L5.1

Simulation with <= in a combinational block: RHS are sampled from old values, so:

  • t <= a → schedules t = 1, but reads old t = 0 for the next line.
  • y <= t → uses old t = 0y gets 0 this pass.
  • The block re-triggers when t changes to 1, and only then does y become 1 — one delta-cycle late. So in simulation y needs two evaluation passes to reach 1 (it lags behind a).

What synthesis builds: the tool ignores the delta-timing and wires y = a directly (pure combinational path). So hardware gives y = 1 immediately, but simulation showed a delay.

That gap — sim behaves one way, silicon another — is the dreaded simulation/synthesis mismatch. Fix: use blocking = in combinational blocks so simulation matches the wire the tool actually builds. See Synthesis vs Simulation.

Exercise L5.2 — Blocking shift register bug

A student wants a 3-stage shift register but writes blocking assignments. Given q1=1, q2=0, q3=0, find q1,q2,q3 after one edge, and explain why only one flip-flop's worth of shift happens.

always @(posedge clk) begin
    q3 = q2;
    q2 = q1;
    q1 = d;      // d = 0
end
Recall Solution L5.2

Blocking = updates immediately, in order:

  • q3 = q2;q3 = 0 (old q2).
  • q2 = q1;q2 = 1 (old q1).
  • q1 = d;q1 = 0.
  • Result: q1=0, q2=1, q3=0.

Here it happens to look correct — because the order (q3 first, then q2, then q1) reads the still-old value before it's overwritten. But reverse the order (q1=d; q2=q1; q3=q2;) and every stage grabs the just-written value → d races through all three flip-flops in one edge → the shift register collapses to a single stage.

Lesson: blocking makes correctness depend on line order — fragile and non-obvious. Non-blocking <= samples all old values first, so the shift is always exactly one stage regardless of order. Always use <= in clocked blocks.

Exercise L5.3 — Defend the design: async vs sync reset

Two flip-flops below differ only in the sensitivity list. For each, state: (a) is the reset synchronous or asynchronous, and (b) if rst pulses high between clock edges, does q go to 0 immediately or wait for the next clock edge?

// DFF-A
always @(posedge clk)              begin if (rst) q <= 0; else q <= d; end
// DFF-B
always @(posedge clk or posedge rst) begin if (rst) q <= 0; else q <= d; end
Recall Solution L5.3

The sensitivity list decides when the block wakes up.

  • DFF-A wakes only on posedge clk. So rst is examined only at the clock edgesynchronous reset. If rst pulses between edges, q does not change until the next rising clock. (b) → waits for the next edge.
  • DFF-B also wakes on posedge rst. So a rising rst fires the block immediately, independent of the clock → asynchronous reset. (b) → q goes to 0 immediately.

Why it matters: async reset clears instantly (good for power-up safety) but its release must be handled carefully to avoid metastability; sync reset is simpler and cleaner in timing but can't clear a stuck clock. Both are valid — the sensitivity list is your explicit declaration of which you built. See Flip-Flops and Latches.


Recall One-line self-check before you leave

Golden Rule ::: Clocked always @(posedge clk)<=; combinational always @(*)=, and assign every output on every path. Sum of two -bit numbers needs how many bits? ::: (to hold the carry-out). 4'hF + 1 in a 4-bit counter → ::: 4'h0 (wraps around, mod 16).


Connections