SystemVerilog can make RTL contracts explicit without pulling verification-only constructs into synthesis. The useful design subset packages shared types, groups related signals, checks parameters, and removes copy-pasted width logic.
These independent patterns cover a shared-type package, packed request fields, a type-parameterized pipeline, width-scoped helper functions, and a stream-interface declaration. They are building blocks, not a connected packet-pipeline reference implementation. The interface-stage body is intentionally omitted; its handshake implementation must supply buffering, reset and backpressure behavior.
7 lessons, about 42 min
It makes hardware intent and connectivity clearer while elaborating into predictable synthesizable structures supported by the target tool flow.
The whole topic on one page, taken from each lesson's Remember box. Read it before an interview, or print it.
Packages and Compile Order
One definition in a package; every block imports it, so a layout change reaches all of them.
Compile packages before the modules that import them, and avoid circular imports.
Prefer explicit pkg::type in widely shared headers, because wildcard imports can collide.
Rule of thumb: move common definitions into a lower-level package instead of importing in a circle.
Packed Structs and Unions
The first declared field takes the most significant bits: 1 + 4 + 8 + 32 = 45 bits for request_t.
Packed positions describe the value; they do not set which byte goes on a bus first.
Union members are views of the same bits, so carry an explicit tag if the interpretation changes.
Rule of thumb: publish the bit layout, and convert explicitly where an external standard defines byte order.
Parameter Types and Guards
Guard zero and one values before $clog2, ranges or part-selects, and use a localparam for derived values.
A module-scope $error inside a generate-if stops the build at elaboration; an initial $fatal runs only at simulation time zero.
Test the minimum, a normal value and one more configuration.
Rule of thumb: a parameterised module is not reusable until its legal range is documented and checked.
Functions and Tasks
In RTL, a function computes one value from its inputs; keep waiting tasks in testbenches.
Gray encode is one XOR level, g = (b >> 1) ^ b; decode ripples from the top bit down.
W/8 truncates: W=4 gives [-1:0] and W=12 leaves four bits unmerged, so keep the legal-width guard.
Rule of thumb: outputs depend only on inputs, so simulation, synthesis and formal agree.
Interfaces, Modports, and Portability
Modports state source and sink ownership; assertions still have to check stability, transfer and reset.
Flatten interfaces at external IP boundaries when a tool or language needs plain ports.
Compile a small representative design in every supported simulator and synthesis tool before standardising.
Rule of thumb: type safety does not enforce valid/ready stability; the assertions do.
Compile-Order and Package Errors, Decoded
Before declaration with a second location means file order; Can't find means a missing import; Import package not found means a typo.
Two wildcard imports that export the same name are ambiguous; one tool may silently pick by import order. Qualify the name.
Keep imports inside module headers and the order in one file list, packages first, checked by a script before the compiler runs.
Rule of thumb: when a package error is unclear in one tool, run the second one: the message that names a location or a package is the cause.
Mini Project: a Reusable FIFO IP
Guard parameters at elaboration, then check the guard stops the build in every flow: Verilator reports $error as a warning, which -Wno-fatal lets through.
Put checks in the RTL behind macros each tool defines: FORMAL for Yosys, __ICARUS__ to skip SVA Icarus cannot parse, plain SVA for Verilator.
A bounded pass counts only if a cover shows the bound reaches the corner: full needs one reset step plus DEPTH pushes.
Rule of thumb: an IP is done when a stranger can run one script and see every check pass, and a seeded bug fail.
Code lab: Package functions
Practice question: Packed versus unpacked structs in RTL datapaths
Code lab: Packed struct beat
Practice question: Synchronous FIFO with full and empty flags
Practice question: Synchronous FIFO blocking on full and empty
Interview: run a mock interview on this topic