Ports stay grouped by protocol or function instead of random ordering. Clocks and resets lead, then each interface forms one group.
Suffixes mark direction: _i for in, _o for out. Clocks and resets take _i too, and an active-low signal adds n: clk_i, rst_ni. Reviews then audit direction at a glance.
// snippet
module stream_stage #(
parameter int W = 16
) (
// clock and reset
input logic clk_i,
input logic rst_ni,
// upstream port
input logic valid_i,
input logic [W-1:0] data_i,
output logic ready_o,
// downstream port
...
);Rule | Form |
|---|---|
Group by interface | Clock and reset first, then one group per port |
Mark direction |
|
Connect by name |
|
Keep debug out | No debug-only wires as permanent top-level ports |
Structs and interfaces belong only where the toolflow supports them through synthesis and lint. Plain grouped ports work everywhere.
No. Named connections keep the existing bindings intact, but the new enable may be left dangling. Positional instances also risk shifted bindings or a port-count error. Require missing-port lint failures and explicitly connect the new input at every instance.
A permanent debug port freezes into the interface contract and every future review pays for it. Probe internally and keep the top-level list to production signals.
The disciplined stage plus a testbench that proves named and wildcard connections agree. The full code sits below.
module stream_stage #(
parameter int W = 16
) (
input logic clk_i,
input logic rst_ni,
input logic valid_i,
input logic [W-1:0] data_i,
output logic ready_o,
output logic valid_o,
output logic [W-1:0] data_o,
input logic ready_i
);
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
valid_o <= 1'b0;
data_o <= '0;
end else if (ready_i || !valid_o) begin
valid_o <= valid_i;
data_o <= data_i;
end
end
assign ready_o = !valid_o || ready_i;
endmodule
Testbench run with Icarus Verilog 13.0 (2026-10-16): prints PASS. It compares the named and wildcard connections cycle by cycle. Connection lint is a separate check.