Topic 8 · Hierarchy & Reuse

Design for Reuse

Video 4 of 4 · ~8 minutes · Topic 8 Capstone

Dr. Mike Borowczak · Electrical & Computer Engineering · CECS · UCF

HierarchyParametersGenerateReuse

🌍 Where This Lives

Where it shows up

The libraries you use every day in software — libc, OpenSSL, zlib, libpng, SQLite — were written in the 1990s and still run inside everything. Their hardware equivalents — UART controllers, SPI masters, AXI bridges, DDR controllers — predate that and still ship in this year's silicon. When a building block is done well, you forget it exists. When it's done badly, every team rewrites their own version, and the same bug appears in twenty places at once.

When it goes wrong

Heartbleed was one flaw in one widely-shared library that suddenly meant every web server on Earth was exposed simultaneously. The opposite failure — when teams don't share — is worse over the long run: ten teams writing ten almost-identical blocks, each with their own bugs, none documented well enough for the next engineer to safely pick up. Both are reuse failures. Both show up in bug trackers for years.

Your Growing Module Library

ModuleTopicParametersWhere it lives
debounce5CLKS_STABLEshared/lib/ — reference impl
edge_detect5shared/lib/ — reference impl
counter_mod_n5Nshared/lib/ — reference impl
sync_2ff5WIDTHlecture example (d08_s1_ex1)
traffic_light7lab (week2_day07/ex1)
pattern_detector7lab (week2_day07/ex2)
button_array8N, CLKS_STABLElecture example (d08_s3_ex3)
piso_shift / sipo_shift5*WIDTHnot yet built — you’ll need these
fifo8*DEPTH, WIDTHsketched today — not yet built
Honest accounting: Some modules are in shared/lib/ ready to instantiate; others exist as starter/solution code in your labs; a few you haven’t built yet. The next theme’s composition work (UART = shift + baud + FSM; SPI = shift + debounce) will surface the gaps — that’s when we promote the lab versions into the shared library.

⚠️ “Works for Me” vs. “Reusable”

❌ “Works for me”

Hard-coded widths. Assumes particular clock rate. Testbench works only with “my” input pattern. Undocumented. Signal names reflect current project, not purpose. Sub-modules tangled with the top. Only original author can use it — and only in this project.

✓ Reusable

Parameterized over everything that varies. Clock-rate independence where possible. Testbench covers the module's contract (not a specific application). Documented inputs, outputs, behavior, timing, assumptions. Named for function. Self-contained — no external dependencies. A stranger can drop it in and it works.

The receipt: A reusable module passes a simple test: someone who didn't write it can instantiate it correctly by reading only the module header and a short comment block. If they need to read the implementation, the module has failed at reuse.

👁️ I Do — Anatomy of a Reusable Module

//----------------------------------------------------
// debounce.v — counter-based switch debouncer
//
// Takes an asynchronous, bouncing input, produces
// a clean version that only changes after
// CLKS_STABLE consecutive cycles of persistence.
//
// Parameters:
//   CLKS_STABLE : cycles before accepting new value
//                 (default 500_000 = 20ms @ 25MHz)
//
// Ports:
//   i_clk    : synchronous clock
//   i_reset  : synchronous reset (active high)
//   i_noisy  : input from external switch
//   o_clean  : stable debounced output
//
// Latency: up to CLKS_STABLE cycles.
// Usage:   debounce #(.CLKS_STABLE(N)) u_deb (...);
//----------------------------------------------------
module debounce #(parameter CLKS_STABLE=500_000) (
    input  wire i_clk, i_reset, i_noisy,
    output reg  o_clean
);
    // ... implementation ...
endmodule
RTL view of a reusable debounce module. Left: an amber datasheet card listing purpose, parameter CLKS_STABLE (gold), and ports i_clk (blue), i_reset (red), i_noisy (orange), o_clean (green), plus documented latency. Right: a dark-bordered black-box module labeled debounce #(.CLKS_STABLE) with the four colored ports entering/exiting and three internal blocks shown abstractly (==? comparator, $clog2-sized counter, r_clean register).
My thinking: The header comment is the “IP datasheet.” A reuser sees parameters, ports, behavior, latency, and usage — everything needed to instantiate correctly without reading the implementation. Every colored signal in the header maps to the same-colored arrow on the black-box module to the right.

🤝 The Reusability Checklist

  • ☐ Every dimension that could vary is a parameter
  • ☐ All parameters have sensible defaults
  • ☐ Module is self-contained — no globals, no external definitions
  • Header comment describes purpose, parameters, ports, timing, usage
  • ☐ Signal names describe function, not the current project's context
  • ☐ Self-checking testbench covers the module's contract, not just happy-path inputs
  • ☐ Testbench runs under a Makefile target with one command
  • ☐ Synthesizable; make stat produces expected cell count
  • ☐ If the module is part of a pipeline, its latency is documented

🧪 You Do — Audit One of Your Modules

Pick a module from any earlier lab. Score it against the reusability checklist. For each unchecked box, name the work you’d do to check it.

Most common gaps (I've seen them all):
  • No header comment → add datasheet block
  • Hard-coded 25_000_000 or 250_000 → parameterize CLKS_PER_SEC
  • Signal names like tmp, x, cnt → rename for purpose
  • Testbench that only tests one specific case → add boundary + random
  • No Makefile target → add make sim

🔧 Before / After Comparison

BEFORE: “works for me”

module btn (
    input clk, rst, x,
    output y
);
    reg [18:0] cnt;
    // ... 20 ms @ 25 MHz ...
    // (hard-coded)
endmodule

AFTER: reusable

// Header comment: datasheet
module debounce #(
    parameter CLKS_STABLE = 500_000
) (
    input  wire i_clk, i_reset, i_noisy,
    output reg  o_clean
);
    localparam W = $clog2(CLKS_STABLE);
    reg [W-1:0] r_count;
    // ... logic with docs ...
endmodule
Side-by-side RTL comparison. Left (red): module btn with grey unlabeled pins clk, rst, x, y — the diagram cannot show what x and y mean because the source code refused to say. Right (green): module debounce #(CLKS_STABLE) with brightly colored ports i_clk (blue), i_reset (red), i_noisy (orange), o_clean (green) and gold parameter annotation — every signal's role is visible at a glance.
Same functionality. Different reach. “After” works on any clock rate, any window, any project — and the diagram is self-describing. The change took 5 minutes; the payoff lasts years.

🤖 Check the Machine

Ask AI: “Here's my debouncer. Refactor it to be reusable: add a parameter for the debounce window, auto-size the counter width, add a header comment block in a datasheet style, and rename signals with the i_/o_/r_ convention.”

TASK

AI refactors to reusable style.

BEFORE

Predict: parameter added, $clog2 used, renamed signals, datasheet comment.

AFTER

Strong AI documents latency + timing assumptions. Weak AI only does syntactic rename.

TAKEAWAY

“Reusable” means semantic changes + docs, not just renaming.

Key Takeaways

 A module is reusable when a stranger can use it from the header alone.

 Everything that could vary is a parameter; everything else is documented.

 The header comment is the IP datasheet. Write it as such.

 Your module library becomes the next theme’s UART + SPI backbone.

If reading the implementation is necessary to use it, it isn't reusable.

Pre-Class Self-Check

Q1: Name three things that should be parameterized in a debouncer module.

CLKS_STABLE (window in cycles), possibly RESET_VAL (initial output), possibly the input-width if you want a multi-bit debouncer. Clock rate itself stays external — the module is in units of “clocks,” not milliseconds.

Q2: What goes in a header comment block that doesn't go in the Verilog code itself?

Purpose, assumptions, timing (latency), usage example, required context. Code answers “how”; comments answer “what for” and “how to use.”

Pre-Class Self-Check (cont.)

Q3: Why is signal naming part of reusability?

Signals with project-specific names (sw1_press, fan_on) can't be used in other projects. Signals with functional names (i_noisy, o_clean) work everywhere.

Q4: What single question do you ask to tell if a testbench tests a module's contract vs. a specific application?

“If I doubled the parameter values, would this testbench still be relevant?” If no, the testbench is application-specific. If yes, it tests the module itself.

🔗 End of Topic 8 · Hierarchy & Reuse

Next Theme: Real Communication Protocols

UART · SPI · Integration Projects

▸ WHY THIS MATTERS NEXT

You’ve now learned the alphabet of hardware design: combinational logic, sequential logic, synchronization, verification, state machines, and reuse. The next theme is where you compose words and sentences. UART TX + RX — to actually talk to your computer over a serial cable. SPI master — to drive peripherals. Your module library from the foundational topics isn’t the byproduct; it’s the material of the upcoming designs. You’re not going to learn many new primitives next — you’re going to build real things out of the ones you already have. See you in the next topic.