32 / 37 · Lab
Integrated lab: periodic pulses and an enabled accumulator
Specify inter-module transfers using previous values and compare enable stalls, consecutive pulses and simultaneous reset against a reference model.
Lessons are free to read. Enroll to save your learning progress.
Define the connection before composing the modules
Build a sampler that accepts a four-bit unsigned sample at every rising edge with enable=1. Every Nth accepted sample produces a registered pulse. An accumulator consumes that pulse and its corresponding saved sample at the following rising edge, even if enable is then 0. The eight-bit total wraps modulo 256. Synchronous reset clears both stages and discards any pending sample.
The one-edge delay is part of the contract. The accumulator reads the old pulse and old saved sample; it cannot see the other stage’s new nonblocking values at the same edge.
Save the parameter lesson’s modulo_tick module as modulo_tick.sv and this module as periodic_accumulator.sv. N must be positive.
module periodic_accumulator #(parameter integer N = 3) (
input logic clk, rst, enable,
input logic [3:0] sample,
output logic pulse,
output logic [7:0] total
);
localparam integer CW = (N <= 1) ? 1 : $clog2(N);
wire [CW-1:0] count;
logic [3:0] saved_sample;
modulo_tick #(.N(N)) tick_u (
.clk(clk), .rst(rst), .enable(enable),
.count(count), .pulse(pulse)
);
always_ff @(posedge clk) begin
if (rst) saved_sample <= 4'b0;
else if (enable) saved_sample <= sample;
end
always_ff @(posedge clk) begin
if (rst) total <= 8'b0;
else if (pulse) total <= total + {4'b0, saved_sample};
end
endmoduleTrack the sample and event together
For N=3, use this table. Inputs are pre-edge and outputs are post-update.
| Edge | rst | enable | sample | pulse after | total after |
|---|---|---|---|---|---|
| E0 | 1 | 1 | 9 | 0 | 0 |
| E1 | 0 | 1 | 2 | 0 | 0 |
| E2 | 0 | 1 | 4 | 0 | 0 |
| E3 | 0 | 1 | 7 | 1 | 0 |
| E4 | 0 | 0 | 9 | 0 | 7 |
| E5 | 0 | 1 | 1 | 0 | 7 |
| E6 | 0 | 1 | 3 | 0 | 7 |
| E7 | 0 | 1 | 5 | 1 | 7 |
| E8 | 1 | 1 | 15 | 0 | 0 |
At E4, the accumulator adds 7, not the new external sample 9. enable only controls new acceptance; it does not cancel a pending event. At E8, reset discards pending 5 and clears the total. Copying the accumulator condition as enable && pulse would lose the E4 event.
Write a reference at the system boundary
Keep an accepted-sample count, an optional pending value and an integer total. At each non-reset edge, first consume the previous pending value modulo 256, then examine current enable/sample to decide whether a new Nth sample becomes pending. Reset clears all three. This model does not depend on tick_u.count or saved_sample.
Reuse the testbench lesson’s falling-edge stimulus and post-update comparison. Compare pulse and total at every edge. Also check that total holds when no event is pending and that a consumed event is removed.
Cover integration failures
- Run N=1 to generate consecutive pulses. Each accepted sample must be consumed once at the next edge.
- Run N=3 with disabled gaps before and after a terminal sample. Count accepted edges, not all clock edges.
- Change the external sample during a disabled consumption edge; the saved sample must be used.
- Reset with a pending event. The total and pending event are both discarded.
- Accumulate enough samples to cross 255 and verify modulo-256 wrap.
Deliberately replace saved_sample with sample in the accumulator. The E4 case must fail. Then gate consumption with enable and confirm another failure. Restore the correct implementation before recording the passing run.
This exercise verifies a single-clock, finite-width functional contract. It does not demonstrate a multi-clock crossing, physical timing closure or a free-account DDS product run. Those require their own execution and acceptance evidence.