Reading a Complete Small RTL Design
Review a packet counter from its interface through corner cases, then connect the code to expected hardware.
The requirement
The following module receives a stream with valid, ready, data, and last. It adds the bytes in one packet. On transfer of the last byte, it publishes the sum and raises o_sum_valid for one cycle.
A transfer occurs only when i_valid && o_ready is 1 on a rising edge. The module can accept one byte every cycle.
module packet_sum #(
parameter int unsigned DATA_WIDTH = 8,
parameter int unsigned SUM_WIDTH = 16
) (
input logic i_clk,
input logic i_rst,
input logic i_valid,
output logic o_ready,
input logic [DATA_WIDTH-1:0] i_data,
input logic i_last,
output logic [SUM_WIDTH-1:0] o_sum,
output logic o_sum_valid
);
logic [SUM_WIDTH-1:0] accumulator_q;
logic [SUM_WIDTH-1:0] extended_data;
always_comb begin
extended_data = '0;
extended_data[DATA_WIDTH-1:0] = i_data;
o_ready = 1'b1;
end
always_ff @(posedge i_clk) begin
if (i_rst) begin
accumulator_q <= '0;
o_sum <= '0;
o_sum_valid <= 1'b0;
end else begin
o_sum_valid <= 1'b0;
if (i_valid && o_ready) begin
if (i_last) begin
o_sum <= accumulator_q + extended_data;
o_sum_valid <= 1'b1;
accumulator_q <= '0;
end else begin
accumulator_q <= accumulator_q + extended_data;
end
end
end
end
endmoduleReview the interface first
Sum width must be at least data width. The module does not know the maximum packet length, so it cannot guarantee that overflow is impossible. The specification must accept wraparound, request an overflow output, or limit packet length.
o_ready is always 1, so there is no backpressure. If the output had to wait for a consumer, the design would need to store a pending result and lower o_ready at the right time.
Walk through one case
For a packet containing 3, 5, and 2, with last on 2:
- after
3, the accumulator is 3; - after
5, it is 8; - on the final transfer,
o_sumreceives8 + 2; o_sum_validis 1 for the cycle after that edge;- the accumulator returns to zero for the next packet.
Nonblocking assignment semantics explain why o_sum uses the old accumulator_q value and adds the current input.
Look for corner cases
A useful review asks at least:
- What happens for a one-byte packet?
- Are inputs ignored while
i_validis 0? - Does reset cleanly discard an incomplete packet?
- Can two packets arrive with no empty cycle?
- What happens when the sum exceeds
SUM_WIDTH? - Is
SUM_WIDTH < DATA_WIDTHforbidden?
A testbench should cover these cases rather than only a nominal packet.
Connect code to hardware
The circuit contains an accumulator register, an adder, an output register, and a valid bit. The main combinational path passes through the addition before the accumulator or output. If the target frequency is too high, width, pipelining, or protocol behavior must be reviewed.
Key points
- Review begins with the port and protocol contract.
- A nominal example is not enough; boundaries and consecutive sequences matter.
- Parameter widths need a valid relationship.
- Nonblocking assignments use the previous values of registers.
- Reading RTL also means predicting registers, operators, and timing paths.
📝 Test your knowledge - Chapter quiz