A top level connects proven blocks with clear contracts. Hierarchy, port discipline, and parameter flow decide whether that top stays reviewable as block count grows.
The example benches are finite digital checks. Timing and implementation signoff remain separate obligations.
Read the boxes from the outside in. Every boundary is a port list and a parameter list, which the lessons below cover one at a time.
Top-down starts at the outer box and fixes its contracts before the leaves exist.
Bottom-up starts from a verified inner box and checks its assumptions against the outer ones.
Parameters cross every boundary downwards, by name, from one owner at the top.
3 lessons, about 12 min
The boundary mismatches: a 16-bit unsigned input connection is zero-extended into the 32-bit leaf; its 32-bit output is truncated on connection to a 16-bit receiver. Signed extension depends on the source expression's signedness. These are deterministic conversions, not automatically floating bits, and can mask a missing override. Pass parameters by name and make width lint diagnostics fatal.
The whole topic on one page, taken from each lesson's Remember box. Read it before an interview, or print it.
Top-down vs bottom-up integration
Top-down fixes the boundary contracts first; bottom-up composes proven leaves and must check their assumptions.
A leaf's passing testbench shows what was driven, not what is required: write pulse/level, clock and reset rules beside each port.
Fix a broken assumption at the boundary the top owns, not inside the verified leaf.
Rule of thumb: unowned timing at a boundary becomes an integration bug.
Port list discipline
Group ports by interface; clocks and resets lead. Use _i and _o, and n for active-low: clk_i, rst_ni.
Connect by name, and make missing-port lint diagnostics fatal.
Use .* only where every name already matches in width and intent.
Rule of thumb: keep the port list to production signals; probe internally and do not freeze debug ports into the contract.
Parameter propagation across hierarchy
Keep one source of truth for each dimension at the top, and pass it by name down every level.
A narrower-to-wider connection is zero-extended and a wider-to-narrower one is truncated; treat width lint as an error.
Test with non-default values and compare each descendant's actual parameters.
Rule of thumb: a copy of a dimension in a leaf agrees on the day it is written and diverges on the next edit.