Okay, let's finally look at some code
SystemVerilog is a Hardware Description Language (HDL). The snippet below describes a single, buffered multiplication stage. It's deliberately simple so we can focus on the plumbing, and you can actually execute its testbench using Icarus Verilog.
module stream_multiply #(
parameter int WIDTH = 16
) (
input logic clk,
input logic reset_n,
input logic input_valid,
output logic input_ready,
input logic [WIDTH-1:0] left,
input logic [WIDTH-1:0] right,
output logic output_valid,
input logic output_ready,
output logic [(2*WIDTH)-1:0] product
);
assign input_ready = !output_valid || output_ready;
always_ff @(posedge clk) begin
if (!reset_n) begin
output_valid <= 1'b0;
product <= '0;
end else if (input_ready) begin
output_valid <= input_valid;
if (input_valid)
product <= left * right;
end
end
endmodule
Start from the outside in
Look at parameter int WIDTH=16. This just makes the module configurable so we can stamp out different sizes. A line like logic [WIDTH-1:0] left is declaring a physical bundle of wires, numbered from top to bottom. Notice how the product gets 2 * WIDTH bits, to hold the full unsigned result.
Keep the two behaviors separated in your head
Look for the assign statement. This is continuous logic—it's calculating input_ready on the fly based on whether the output register is empty or about to clear out. Now look at the always_ff block. This only runs when the clock ticks. It handles the reset flag, and if the stage is ready to advance, it reaches out and grabs the new product.
Translating backpressure into a Boolean equation
This module only has room to store one output item. So, when is it allowed to accept a new input? Either when it's completely empty (!output_valid), OR when the current item is guaranteed to leave on this exact clock tick (because the downstream guy said output_ready). So, !output_valid || output_ready is just a mathematical proof of capacity compressed into a single line.
Worked example
Tracing 7 × 9
Imagine we present input_valid=1, left=7, and right=9 while the module is saying input_ready=1. When the clock ticks, the module swallows the data, calculates product=63, and flips its own output_valid=1. If the next module in line drops output_ready=0, our module just holds the 63 steady. Once the blockage clears, the 63 transfers out.
Wait, why doesn't the 63 get overwritten by garbage while we're stalled?
Because during the stall, output_valid is 1 and output_ready is 0. That forces our input_ready to drop to 0. Since the always_ff block says else if (input_ready), the whole block gets skipped! The registers don't get assigned a new value, so they just hold onto the 63 until the traffic clears.
Check your understanding
Why does the module say input_ready is true when output_valid is false?
Section summary
- Ports define the contract your module makes with the outside world.
- assign handles the instant combinational logic.
- always_ff handles the synchronized, edge-triggered register updates.
- This module proves how to safely buffer one piece of data under valid/ready backpressure.
Repository layer · second pass
How do we read SystemVerilog as a circuit contract?
Ports define the module boundary. Continuous assignments describe combinational wiring. always_comb describes a complete combinational procedure. always_ff describes state sampled on a declared edge. Parameters elaborate structure before runtime; they are not mutable variables.
Read in passes: interface and widths, state elements, combinational datapath, handshake conditions, then reset and corner cases. Finally translate the module into a cycle-level table and compare it with the testbench.
Reasoning chain
- 1
Annotate every port direction and width.
- 2
Mark registers written in always_ff.
- 3
Mark pure signals driven combinationally.
- 4
Find the exact transfer condition.
- 5
Trace reset and stall behavior.
- 6
Derive observable latency.
Worked trace
Interpret output_valid
- An output register stores product and valid.
- When output is empty, input_ready is high.
- On input transfer, the product is captured.
- If downstream stalls, valid and payload remain.
Result. The few RTL lines implement a one-entry elastic stage.
Executable lens · Python
Make the hidden state visible
# Cycle model for the RTL stage
if reset:
output_valid = False
elif input_ready:
output_valid = input_valid
if input_valid:
output_data = a * bRetype this example, predict each intermediate value, and then change one input that touches a boundary.
Misconception clinic
Tempting mistakes
- Assuming textual line order defines separate cycles.
- Ignoring implicit width of unsized literals and expressions.
Retrieval and transfer
Close the book first
- Annotate stream_multiply.sv line by line.
- Predict behavior when valid and ready are both low.
- Add an assertion for stable stalled output.