`timescale 1ns/1ps module tb_dp_stream_calculator; logic clk = 1'b0; always #5 clk = !clk; logic rst = 1'b1; logic in_valid = 1'b0, in_ready; logic [7:0] in_a = 8'd0, in_b = 8'd0, in_tag = 8'd0; logic out_valid, out_ready = 1'b0; logic [7:0] out_y, out_tag; dp_stream_calculator dut (.*); integer expected_y [0:8191]; logic [7:0] expected_tag [0:8191]; integer head = 0, tail = 0; integer accepted = 0, delivered = 0, canceled = 0; integer cycles = 0, stall_edges = 0, full_replacements = 0; integer wraps = 0, reset_with_work = 0, zero_results = 0; logic was_stalled = 1'b0; logic [7:0] held_y, held_tag; logic old_read_ptr = 1'b0; // Observe signals before sequential nonblocking updates at each edge. always @(posedge clk) begin cycles = cycles + 1; if (rst) begin if (tail > head) reset_with_work = reset_with_work + 1; canceled = canceled + tail - head; head = 0; tail = 0; was_stalled = 1'b0; old_read_ptr = 1'b0; if (in_ready !== 1'b0 || out_valid !== 1'b0) $fatal(1, "reset must suppress transfers"); end else begin if (dut.fifo_count > 2) $fatal(1, "FIFO occupancy exceeds depth"); if ((tail - head) != (int'(dut.fifo_count) + int'(dut.v1) + int'(dut.v2))) $fatal(1, "outstanding/occupancy mismatch"); if (was_stalled && (out_valid !== 1'b1 || out_y !== held_y || out_tag !== held_tag)) $fatal(1, "stalled output changed"); if (out_valid && !out_ready) stall_edges = stall_edges + 1; if (dut.fifo_count == 2 && dut.fifo_push && dut.fifo_pop) full_replacements = full_replacements + 1; if (old_read_ptr && !dut.read_ptr) wraps = wraps + 1; old_read_ptr = dut.read_ptr; if (out_valid && out_ready) begin if (head >= tail) $fatal(1, "unexpected output"); if (out_y !== expected_y[head][7:0] || out_tag !== expected_tag[head]) $fatal(1, "output mismatch at cycle %0d: got %0d/%0d expected %0d/%0d", cycles, out_tag, out_y, expected_tag[head], expected_y[head]); if (out_y == 0) zero_results = zero_results + 1; head = head + 1; delivered = delivered + 1; end if (in_valid && in_ready) begin if (tail >= 8192) $fatal(1, "testbench queue capacity"); // Expanded integer expression differs from the staged implementation. expected_y[tail] = (3 * int'(in_a) + 3 * int'(in_b)) % 256; expected_tag[tail] = in_tag; tail = tail + 1; accepted = accepted + 1; end was_stalled = out_valid && !out_ready; held_y = out_y; held_tag = out_tag; end end // Deterministic bounded source. Its payload changes only after acceptance. task automatic send(input integer a, input integer b, input integer tag); begin @(negedge clk); in_valid = 1'b1; in_a = a[7:0]; in_b = b[7:0]; in_tag = tag[7:0]; @(posedge clk); while (!in_ready) @(posedge clk); @(negedge clk); in_valid = 1'b0; end endtask task automatic drain; integer waited; begin @(negedge clk); in_valid = 1'b0; out_ready = 1'b1; waited = 0; // Read scoreboard after its positive-edge monitor has completed. while (head != tail) begin @(negedge clk); waited = waited + 1; if (waited > 100) $fatal(1, "drain timeout"); end repeat (2) @(negedge clk); if (dut.fifo_count != 0 || dut.v1 || dut.v2 || out_valid) $fatal(1, "storage not empty after drain"); end endtask integer i, phase; logic driver_done = 1'b0; initial begin repeat (3) @(negedge clk); rst = 1'b0; out_ready = 1'b1; // Numeric boundaries and valid zero results, with no sink stalls. send(0,0,0); send(1,2,1); send(250,10,2); send(255,255,3); send(128,128,4); send(17,240,5); send(90,90,6); send(4,8,7); drain(); // Concurrent source and sink: long stalls, replacement, and pointer wrap. fork begin for (i = 0; i < 512; i = i + 1) send((i*53)%256, (i*97+17)%256, i); driver_done = 1'b1; end begin phase = 0; while (!driver_done) begin @(negedge clk); out_ready = (phase % 19 >= 9); phase = phase + 1; end end join drain(); // Cancel a full, stalled four-item pipeline, then start a fresh interval. @(negedge clk); out_ready = 1'b0; send(1,9,240); send(2,8,241); send(3,7,242); send(4,6,243); repeat (3) @(negedge clk); if (tail - head != 4) $fatal(1, "reset test did not fill storage"); rst = 1'b1; repeat (2) @(negedge clk); rst = 1'b0; out_ready = 1'b1; send(250,10,250); send(255,255,251); send(0,0,252); drain(); if (accepted != delivered + canceled) $fatal(1, "final transaction accounting"); if (stall_edges == 0 || full_replacements == 0 || wraps == 0 || reset_with_work == 0 || zero_results == 0) $fatal(1, "required boundary coverage missing"); $display("RTL PASS: accepted=%0d delivered=%0d canceled=%0d stalls=%0d full_replace=%0d wraps=%0d reset_with_work=%0d zero=%0d", accepted, delivered, canceled, stall_edges, full_replacements, wraps, reset_with_work, zero_results); $finish; end initial begin #200000; $fatal(1, "global test timeout"); end endmodule