Skip to content

Commit 71efa34

Browse files
committed
test: Drive reg-frontend TB with the apb_test driver
Replace the hand-rolled strict-APB4 tasks with the standard apb_test::apb_driver from the apb dep. Bridge one APB_DV interface per config port to the packed dma_ctrl_req_i/rsp_o via the apb assign macros and give each port its own driver so the multi-port arbitration test can drive two ports concurrently. Recover the bounded read latency by timing the blocking driver.read and subtracting its fixed framing; the per-read watchdog still fatals a read that never returns.
1 parent 01dc219 commit 71efa34

1 file changed

Lines changed: 80 additions & 81 deletions

File tree

test/frontend/tb_idma_reg_frontend.sv

Lines changed: 80 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
66
// - Daniel Keller <dankeller@iis.ee.ethz.ch>
77

88
// Self-checking testbench for the iDMA register frontend (idma_reg32_3d, apb4-flat).
9-
// Drives the APB config slave against a controllable backend stub and checks the
10-
// non-blocking next_id launch contract: the config read completes promptly (even
11-
// under backend backpressure) and the launch fires exactly once when the arbiter
12-
// grants. A per-read watchdog guards against any read that hangs.
9+
// Drives the APB config slave with the standard apb_test::apb_driver against a
10+
// controllable backend stub and checks the non-blocking next_id launch contract:
11+
// the config read completes promptly (even under backend backpressure) and the
12+
// launch fires exactly once when the arbiter grants. A per-read watchdog guards
13+
// against any read that hangs.
1314

1415
`include "apb/typedef.svh"
16+
`include "apb/assign.svh"
1517
`include "idma/typedef.svh"
1618

17-
module tb_idma_reg_frontend import idma_pkg::*; #(
19+
module tb_idma_reg_frontend import idma_pkg::*; import apb_test::apb_driver; #(
1820
// number of streams the elaborated DUT exposes (checked at instantiation)
1921
parameter int unsigned NumStreams = 32'd1,
2022
// number of config-bus ports (arbitrated by the reg frontend's rr_arb_tree)
@@ -25,6 +27,8 @@ module tb_idma_reg_frontend import idma_pkg::*; #(
2527
// Parameters
2628
// --------------------------------------------------------------------------
2729
localparam time TCK = 10ns;
30+
localparam time TA = TCK * 1 / 4; // driver application time
31+
localparam time TT = TCK * 3 / 4; // driver test (sample) time
2832
localparam int unsigned CfgAddrWidth = 32'd32;
2933
localparam int unsigned CfgDataWidth = 32'd32;
3034
localparam int unsigned CfgStrbWidth = CfgDataWidth / 32'd8;
@@ -34,11 +38,16 @@ module tb_idma_reg_frontend import idma_pkg::*; #(
3438
localparam int unsigned DataWidth = 32'd32;
3539
localparam int unsigned NumDim = 32'd3;
3640
localparam int unsigned RepWidth = 32'd32;
37-
// bounded-latency bound for a non-blocking next_id read: it must complete within
38-
// this many config-clock cycles of the ACCESS phase even while req_ready_i is low.
39-
localparam int unsigned MaxReadLatency = 32'd2;
41+
// apb_driver framing: the blocking driver.read() spans SETUP + first-ACCESS-check +
42+
// trailing edge before returning, so a same-cycle (non-blocking) read takes this many
43+
// config clocks end-to-end; each extra ACCESS wait state adds one more clock.
44+
localparam int unsigned DrvFraming = 32'd2;
45+
// bounded-latency bound for a non-blocking next_id read: measured ACCESS-phase latency
46+
// (raw driver.read span minus DrvFraming) must be 0 config-clock cycles — the read must
47+
// complete in its first ACCESS check even while req_ready_i is low.
48+
localparam int unsigned MaxReadLatency = 32'd0;
4049
// watchdog bound: any next_id APB read that does not complete within this many
41-
// config-clock cycles is a hang (the non-blocking read completes in <= 2 cycles).
50+
// config-clock cycles is a hang (the non-blocking read completes immediately).
4251
localparam int unsigned DeadlockCycles = 32'd2000;
4352

4453
// register map (idma_reg32_3d_addrmap_pkg): base + per-stream stride 0x4
@@ -84,6 +93,13 @@ module tb_idma_reg_frontend import idma_pkg::*; #(
8493

8594
typedef logic [IdCounterWidth-1:0] cnt_width_t;
8695

96+
typedef apb_driver #(
97+
.ADDR_WIDTH ( CfgAddrWidth ),
98+
.DATA_WIDTH ( CfgDataWidth ),
99+
.TA ( TA ),
100+
.TT ( TT )
101+
) apb_driver_t;
102+
87103
// --------------------------------------------------------------------------
88104
// Clock / reset
89105
// --------------------------------------------------------------------------
@@ -110,6 +126,30 @@ module tb_idma_reg_frontend import idma_pkg::*; #(
110126
idma_busy_t [NumStreams-1:0] busy;
111127
logic [NumStreams-1:0] midend_busy;
112128

129+
// --------------------------------------------------------------------------
130+
// APB DV interfaces + drivers: one per config port. Each interface is bridged
131+
// to the DUT's packed dma_ctrl_req_i[i]/dma_ctrl_rsp_o[i] via the apb assign
132+
// macros; one apb_driver per port lets Test 5 drive two ports concurrently.
133+
// --------------------------------------------------------------------------
134+
// virtual-interface handles (interface arrays can only be indexed by a constant,
135+
// so each element is captured into this array from the generate loop below)
136+
typedef virtual APB_DV #(.ADDR_WIDTH(CfgAddrWidth), .DATA_WIDTH(CfgDataWidth)) apb_dv_t;
137+
apb_dv_t apb_vif[NumRegs];
138+
apb_driver_t drv[NumRegs];
139+
140+
for (genvar i = 0; i < NumRegs; i++) begin : gen_apb_bridge
141+
APB_DV #(
142+
.ADDR_WIDTH ( CfgAddrWidth ),
143+
.DATA_WIDTH ( CfgDataWidth )
144+
) apb_dv (clk);
145+
// master interface -> DUT req struct, DUT rsp struct -> master interface
146+
`APB_ASSIGN_TO_REQ(apb_req[i], apb_dv)
147+
assign apb_dv.pready = apb_rsp[i].pready;
148+
assign apb_dv.prdata = apb_rsp[i].prdata;
149+
assign apb_dv.pslverr = apb_rsp[i].pslverr;
150+
initial apb_vif[i] = apb_dv; // publish the vif handle for the driver
151+
end
152+
113153
// --------------------------------------------------------------------------
114154
// Transfer-id generator (owns the next/completed counters). Reset next=2.
115155
// issue on an accepted launch, retire on a modeled backend completion.
@@ -228,8 +268,8 @@ module tb_idma_reg_frontend import idma_pkg::*; #(
228268

229269
// --------------------------------------------------------------------------
230270
// Watchdog: fatal if a next_id APB read stays outstanding too long.
231-
// `nxt_read_active` is raised by apb_read on a next_id address and cleared
232-
// when pready completes it.
271+
// `nxt_read_active` is raised by launch() around the blocking driver.read and
272+
// cleared when it returns — a driver.read that never returns is caught here.
233273
// --------------------------------------------------------------------------
234274
logic nxt_read_active;
235275
int unsigned nxt_read_watchdog;
@@ -272,74 +312,30 @@ module tb_idma_reg_frontend import idma_pkg::*; #(
272312
initial sb_mismatch = 0;
273313

274314
// --------------------------------------------------------------------------
275-
// APB driver
315+
// APB stimulus via apb_test::apb_driver (per-port drivers built in init).
276316
// --------------------------------------------------------------------------
277-
task automatic apb_idle(input int unsigned port = 0);
278-
apb_req[port].psel = 1'b0;
279-
apb_req[port].penable = 1'b0;
280-
apb_req[port].pwrite = 1'b0;
281-
apb_req[port].paddr = '0;
282-
apb_req[port].pwdata = '0;
283-
apb_req[port].pstrb = '0;
284-
apb_req[port].pprot = '0;
285-
endtask
286-
287-
// Strict APB4 master. `access_cycles` (out) counts the ACCESS-phase config cycles
288-
// a read waited for pready, so a test can bound the read latency. One APB
289-
// transaction == one CPUIF request (a guaranteed idle cycle follows).
290-
task automatic apb_xact(input bit write,
291-
input logic [31:0] addr,
292-
input logic [31:0] wdata,
293-
output logic [31:0] rdata,
294-
output int unsigned access_cycles,
295-
input bit is_next_id = 1'b0,
296-
input int unsigned port = 0);
297-
access_cycles = 0;
298-
// SETUP phase: psel high, penable low, for one cycle
299-
@(negedge clk);
300-
apb_req[port].psel = 1'b1;
301-
apb_req[port].penable = 1'b0;
302-
apb_req[port].pwrite = write;
303-
apb_req[port].paddr = addr;
304-
apb_req[port].pwdata = wdata;
305-
apb_req[port].pstrb = write ? '1 : '0;
306-
apb_req[port].pprot = '0;
307-
if (is_next_id) nxt_read_active = 1'b1;
308-
309-
// ACCESS phase: raise penable, then wait for pready at the posedge. Count the
310-
// ACCESS-phase cycles until pready — for a next_id read this is the read latency
311-
// the non-blocking contract bounds (must not depend on req_ready_i).
312-
@(negedge clk);
313-
apb_req[port].penable = 1'b1;
314-
forever begin
315-
@(posedge clk);
316-
access_cycles = access_cycles + 1;
317-
if (apb_rsp[port].pready) begin
318-
rdata = apb_rsp[port].prdata; // sample in the completing cycle
319-
break;
320-
end
321-
end
322-
// retire the transaction: return to IDLE immediately (no extra held posedge)
323-
apb_idle(port);
324-
if (is_next_id) nxt_read_active = 1'b0;
325-
// mandatory idle cycle so the FSM's is_active fully drops with psel low
326-
@(negedge clk);
327-
endtask
328-
329317
task automatic apb_write(input logic [31:0] addr, input logic [31:0] data,
330318
input int unsigned port = 0);
331-
logic [31:0] dummy;
332-
int unsigned cyc;
333-
apb_xact(1'b1, addr, data, dummy, cyc, 1'b0, port);
319+
logic err;
320+
drv[port].write(addr, data, '1, err);
334321
endtask
335322

336-
// APB4 read that respects pready; `cyc` returns the ACCESS-phase cycle count.
337-
task automatic apb_read(input logic [31:0] addr,
338-
output logic [31:0] data,
339-
output int unsigned cyc,
340-
input bit is_next_id = 1'b0,
341-
input int unsigned port = 0);
342-
apb_xact(1'b0, addr, 32'h0, data, cyc, is_next_id, port);
323+
// next_id read via the driver, TIMED to recover the ACCESS-phase latency the
324+
// non-blocking contract bounds. driver.read blocks until pready; measure the
325+
// elapsed config clocks and subtract the fixed driver framing. The watchdog
326+
// flag is held across the (blocking) call so a read that never returns fatals.
327+
task automatic apb_read_next(input logic [31:0] addr,
328+
output logic [31:0] data,
329+
output int unsigned cyc,
330+
input int unsigned port = 0);
331+
logic err;
332+
time t0;
333+
nxt_read_active = 1'b1;
334+
t0 = $time;
335+
drv[port].read(addr, data, err);
336+
// raw span in config clocks, minus the driver's fixed SETUP+trailing framing
337+
cyc = (($time - t0) / TCK) - DrvFraming;
338+
nxt_read_active = 1'b0;
343339
endtask
344340

345341
// --------------------------------------------------------------------------
@@ -357,12 +353,12 @@ module tb_idma_reg_frontend import idma_pkg::*; #(
357353
endtask
358354

359355
// launch: read next_id (the transfer trigger, non-blocking); returns id and the
360-
// ACCESS-phase latency in `cyc`. Snapshots the accept count before the read so
361-
// an accept coinciding with the (non-blocking) read is still observed.
356+
// measured ACCESS-phase latency in `cyc`. Snapshots the accept count before the
357+
// read so an accept coinciding with the (non-blocking) read is still observed.
362358
task automatic launch(output logic [31:0] id, output int unsigned cyc,
363359
input int unsigned s = 0, input int unsigned port = 0);
364360
launch_acc_base = launch_accept_count;
365-
apb_read(reg_next_id(s), id, cyc, .is_next_id(1'b1), .port(port));
361+
apb_read_next(reg_next_id(s), id, cyc, port);
366362
endtask
367363

368364
// wait until the launch read since the last launch() has been accepted (the
@@ -379,8 +375,8 @@ module tb_idma_reg_frontend import idma_pkg::*; #(
379375
endtask
380376

381377
task automatic read_done(output logic [31:0] id, input int unsigned s = 0);
382-
int unsigned cyc;
383-
apb_read(reg_done_id(s), id, cyc);
378+
logic err;
379+
drv[0].read(reg_done_id(s), id, err);
384380
endtask
385381

386382
// poll done_id until it reaches `id` (or a bounded number of tries)
@@ -424,11 +420,14 @@ module tb_idma_reg_frontend import idma_pkg::*; #(
424420
initial begin : test
425421
errors = 0;
426422
checks = 0;
427-
for (int unsigned p = 0; p < NumRegs; p++) apb_idle(p);
428423
req_ready = 1'b1;
424+
rst_n = 1'b0;
425+
// let the generate-block initials publish their vif handles, then bind drivers
426+
@(negedge clk);
427+
for (int unsigned p = 0; p < NumRegs; p++) drv[p] = new (apb_vif[p]);
428+
for (int unsigned p = 0; p < NumRegs; p++) drv[p].reset_master();
429429

430430
// reset
431-
rst_n = 1'b0;
432431
repeat (5) @(negedge clk);
433432
rst_n = 1'b1;
434433
repeat (2) @(negedge clk);

0 commit comments

Comments
 (0)