Barcelona Abroad · Week 2 · Day 5+6  🤖 AI verification deep dive

Counters, Testbenches & AI Verification

CRAFT cycle · 2.5 hours · Mon 6/1 · PM visit: Semidynamics

HDL for Digital System Design · UCF ECE · Barcelona Summer 2026

CRAFT

Today at a Glance

PhaseTimeActivity
🌍 Contextualize10 minCounting in the city · Semidynamics framing
⚠️ Reframe15 mindelay() ≠ a counter · sim ≠ proof · TBs find bugs
🛠 Assemble70 minDebouncer · LED chase · self-checking TB · AI-generated TB
🛡 Fortify45 minNoisy-input stim · AI TB review · hardware verify
🔗 Transfer10 minBrief for Semidynamics · D7 preview
Merged-day pacing: D5 (counters/shift/debounce) and D6 (testbenches + AI) collapse into a single arc — the modules you build at the top of class become the DUTs for the testbenches you write at the bottom.

▸ Phase 1 of 5  ·  ~10 min

🌍 Contextualize

Every digital system counts, shifts, and debounces

The City Counts

  • Metro L3 train doors — debounced sensors decide when to close
  • Camp Nou turnstile counters — each click is a saturating counter event
  • BCN airport gate timers — countdowns + state-aware displays
  • Semidynamics RISC-V pipelines — built from the same counters and shift registers you'll write today

This afternoon · 16:00

Semidynamics — RISC-V vector processor startup in Barcelona. Ask: how much of their development effort is verification? The answer will reframe today's Fortify phase.

Counters · shift registers · debouncers · testbenches. Four primitives that show up in every commercial design.

▸ Phase 2 of 5  ·  ~15 min

⚠️ Reframe

Two software habits to unlearn today

⚠️ delay() Is Not Hardware

❌ Wrong Model

"To wait 10 ms for a button to stop bouncing, call delay(10). It works in Arduino — same idea here."

✓ Right Model

Hardware doesn't wait — it counts clock cycles. A debouncer is a saturating counter: increment when the input is stable, reset when it changes, output flips only when the counter saturates.

Every "wait" in hardware is a counter. 10 ms @ 25 MHz = 250 000 cycles — parameterize the threshold.

Why a Button Needs Debouncing

A single physical button press produces a burst of fast contact-bounce transitions before settling; the debouncer's saturating counter ignores the bounces and emits one clean edge
One press, many electrical transitions. The debouncer counts cycles of stable input and only commits when the count saturates — exactly the Ex 1 build below.

⚠️ Simulation Does Not Prove Correctness

❌ Wrong Model

"My testbench printed PASS, so my module works."

✓ Right Model

Simulation only proves correctness for the cases you tested. A TB finds bugs; it cannot prove their absence. The discipline: directed tests for known corners, then sweeps for everything else.

Today's pattern: write the TB first, then the DUT. The TB tells you what "done" means before you start.

A Testbench Wraps the DUT

The testbench is a top-level module with no ports; it instantiates the device-under-test, drives its inputs with stimulus, and checks its outputs against expected values
The TB has no ports — it's the top of the sim world. It drives the DUT's inputs and checks its outputs. That's the harness you write for every module below.

The Self-Checking Pattern

// Apply inputs → wait → compare → report
a = 4'd3; b = 4'd5; op = OP_ADD; #10;
if (y !== 4'd8) begin
    $display("FAIL: ADD 3+5 got %0d", y);
    fails = fails + 1;
end

// At the end:
if (fails == 0) $display("PASS: %0d tests", tests);
else            $display("FAIL: %0d/%0d", fails, tests);
$finish;
  • !==, not != — catches X/Z
  • Count both fails and tests — silent zero-test runs are a footgun
  • $finish, not infinite loops — otherwise the simulator never returns

▸ Phase 3 of 5  ·  ~70 min  ·  You build · you test

🛠 Assemble

Build the DUTs · then build the TBs that prove them

Build Plan — Two Halves

  1. Ex 1 · 20 min  Reusable debouncer — parameterized threshold, 2-FF synchronizer baked in. Sim with a noisy stimulus.
  2. Ex 2 · 15 min  8-bit shift register (PISO) — the building block for UART (Week 3).
  3. Ex 3 · 10 min  LED chase pattern — counter (speed) + shift register (pattern). Visible Knight Rider sweep on the Go Board.
  4. Ex 4 · 15 min  Hand-written ALU TB — self-checking, every opcode, edge cases.
  5. Ex 5 · 🤖 10 min  AI-generated debouncer TB — prompt → critique → fix → run.
Stretch: file-driven testing ($readmemh stimulus vectors) — preview of Day 9.

Debouncer — The Universal Snippet

module debouncer #(
    parameter THRESHOLD = 250_000      // 10 ms @ 25 MHz
)(
    input  wire clk, rst,
    input  wire in_async,
    output reg  out_clean
);
    // 2-FF synchronizer
    reg s1, s2;
    always @(posedge clk) {s2, s1} <= {s1, in_async};

    reg [$clog2(THRESHOLD)-1:0] cnt;
    always @(posedge clk) begin
        if (rst)                cnt <= 0;
        else if (s2 != out_clean) begin
            if (cnt == THRESHOLD-1) begin
                out_clean <= s2;        // commit
                cnt       <= 0;
            end else cnt <= cnt + 1;
        end else cnt <= 0;              // reset on noise
    end
endmodule

Keep this snippet in your personal library. You'll use it in every project this course.

LED Chase — Counter × Shift Register

module led_chase (
    input  wire       clk,
    input  wire       dir,        // 1 = left, 0 = right
    output reg  [3:0] led
);
    reg [22:0] tick_cnt;
    wire tick = (tick_cnt == 23'd2_500_000 - 1);  // ~10 Hz

    always @(posedge clk) tick_cnt <= tick ? 0 : tick_cnt + 1;

    initial led = 4'b0001;
    always @(posedge clk) if (tick) begin
        led <= dir ? {led[2:0], led[3]}      // rotate left
                   : {led[0],   led[3:1]};   // rotate right
    end
endmodule

First design that combines two sequential blocks. This is RTL composition.

🤖 AI-Generated Debouncer TB

Prompt scaffolding (use this as a template — paste DUT interface verbatim):

Generate a self-checking Verilog testbench for this debouncer.
Interface: clk, rst, in_async, out_clean. Parameter THRESHOLD (use 8 for sim).
Test cases:
  1. Reset behavior
  2. Stable input -> out_clean follows after THRESHOLD cycles
  3. Bouncing input (toggle 5x, then steady) -> out_clean only flips once
  4. Glitch shorter than THRESHOLD -> out_clean does NOT change
Use $display PASS/FAIL with !== comparisons, $finish at end.
Target Icarus Verilog (IEEE 1364-2005). No SystemVerilog features.
  • Run it. Does it compile under iverilog?
  • Does it test the bounce case correctly? Many AIs forget to reset the counter mid-bounce.
  • Save prompt + reply + your fixes to the AI workflow portfolio (8% of course grade).

▸ Phase 4 of 5  ·  ~45 min  ·  Verify · review · harden

🛡 Fortify

Find the bugs sim missed

Sim First — Noisy Input Stimulus

// In the TB — make the input misbehave like a real button
initial begin
    in_async = 0; #100;
    // Bounce: 5 fast toggles
    repeat (5) begin in_async = ~in_async; #3; end
    in_async = 1; #200;                     // settle
    if (out_clean !== 1'b1) $display("FAIL: clean missed rising edge");

    // Glitch test
    in_async = 0; #3; in_async = 1; #200;   // 3-cycle blip
    if (out_clean !== 1'b1) $display("FAIL: glitch passed through");
end

A debouncer that passes a clean unit-step but fails on bounce or glitch is the most common Day-5 bug on hardware.

🤖 AI TB Critique Checklist

What AI usually gets right

  • Module instantiation boilerplate
  • $dumpfile / $dumpvars
  • Clock generation (always #5 clk = ~clk;)
  • Reset sequencing

What AI usually misses

  • Parameter overrides (#(.THRESHOLD(8)))
  • !== for X/Z safety
  • The glitch / bounce-during-counter case
  • SystemVerilog leaks (logic, assert) that won't compile under iverilog -g2005
The skill we're building: review AI verification code before trusting it. That's what Semidynamics engineers do every day.

Hardware Verification

  • Debouncer: press a button rapidly — LED toggles exactly once per physical press. No multi-flicker.
  • Shift register: load 8'b1010_1100, clock out — SO line traces the pattern (GTKWave + on-board LED).
  • LED chase: visible sweep on LED0..LED3. Toggle direction with Button 0 (now debounced!).
  • ALU TB: make sim should print PASS: N/N across every opcode + edge case.
Discipline reminder: if your TB prints PASS but the LED chase doesn't sweep on hardware, you're missing a stimulus case. Add it. This is verification.

▸ Phase 5 of 5  ·  ~10 min

🔗 Transfer

Take it to Semidynamics at 16:00

At Semidynamics This Afternoon

Three questions to answer before you leave the building:

  1. Where are the FSMs? Pipeline control · bus arbitration · cache controllers. What states do they have? (Preview for tomorrow.)
  2. How much of their effort is verification? Industry rule of thumb is 60–70% — confirm or refute.
  3. How do they use AI? For testbench generation? Bug triage? Documentation? Or not at all?
Bring an answer. One-page CRAFT reflection due before D7.

Tomorrow → Finite State Machines

D7 introduces the 3-always-block FSM pattern. State register + next-state logic + output logic — three blocks that map directly to three physical pieces of hardware.

You'll build a traffic light controller — Plaça Catalunya in Verilog — and write the TB to prove every state transition.

PM: HP Barcelona

Tomorrow afternoon is the HP Customer Center visit. They'll show you AI-assisted product workflows — directly parallel to today's AI testbench thread.

Tonight: Day 7 pre-class video (~50 min) + quiz. Watch segment 3 twice — the 3-block pattern is the most-tested concept of Week 2.

🔗 End of Day 5+6 · Semidynamics 16:00

You build it. You prove it. AI helps — you verify.

You can now design parameterized sequential modules and write self-checking testbenches by hand and with AI.
Tomorrow: states — the abstraction that ties sequence to behavior.

CRAFT