interface stream_if #(parameter type T = logic [31:0]) (input logic clk);
logic valid, ready;
T data;
modport source (input clk, ready, output valid, data);
modport sink (input clk, valid, data, output ready);
endinterface
module stage(stream_if.sink in_if, stream_if.source out_if);
// valid/ready stage implementation
endmoduleRead the arrows as who drives each wire. Each modport line in the interface lists the same wires; only the direction differs.
source has output valid, data and input ready: the producer drives the forward wires.
sink has input valid, data and output ready: the stage drives only the backward wire.
If the stage assigns valid, a tool that checks modports stops the build: Verilator reports Attempt to drive input-only modport: 'valid'.
This interface declaration can carry a caller-selected packed type. The empty stage body is an interface-port sketch, not an implemented valid/ready stage. Modports establish source and sink ownership, while protocol assertions remain necessary because type safety alone cannot enforce valid/ready stability. For a complete elastic stage and scoreboard, use the valid/ready lesson.
Use modports to document driver direction.
Keep the clock/reset relationship explicit in the interface contract.
Avoid hiding complex combinational logic in interface methods.
Flatten interfaces at external IP boundaries when tool or language interoperability requires plain ports.
Compile a small representative design in every supported simulator and synthesis tool before standardizing a pattern.
At boundaries crossing tool/language/IP compatibility limits, or where the published integration contract must be language-neutral and explicit.
Verilator 5.048 (2026-10-17) rejected a sink-side assignment to valid with Attempt to drive input-only modport. Icarus Verilog 13.0 does not accept interface ports, which is itself a reason to compile a pattern in every supported tool.