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