Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/frontend/inst64/idma_inst64_events.sv
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,14 @@ module idma_inst64_events #(
events_o.r_done = axi_req_i.r_ready && axi_rsp_i.r_valid;
events_o.r_bw = axi_req_i.r_ready && axi_rsp_i.r_valid;
events_o.r_stall = axi_req_i.r_ready && !axi_rsp_i.r_valid;
events_o.buf_r_stall = !axi_req_i.r_ready && axi_rsp_i.r_valid;

// w
events_o.w_valid = axi_req_i.w_valid;
events_o.w_ready = axi_rsp_i.w_ready;
events_o.w_done = axi_rsp_i.w_ready && axi_req_i.w_valid;
events_o.w_stall = !axi_rsp_i.w_ready && axi_req_i.w_valid;
events_o.buf_w_stall = axi_rsp_i.w_ready && !axi_req_i.w_valid;
if (axi_rsp_i.w_ready && axi_req_i.w_valid) begin
events_o.num_bytes_written = num_bytes_written;
end
Expand Down
40 changes: 38 additions & 2 deletions test/frontend/idma_inst64_base.sv
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
/// one AXI plus one OBI simulation memory per channel.
module idma_inst64_base #(
parameter int unsigned DMATracing = idma_inst64_tb_pkg::DMATracing,
/// Stall the AXI R source and W sink in antiphase, see `gen_stall` below
parameter bit StallPattern = 1'b0,
/// TCDM (OBI) window; every address outside it decodes to ToSoC, i.e. AXI
parameter logic [63:0] TcdmStart = 64'h0000_0000_1000_0000,
parameter logic [63:0] TcdmEnd = 64'h0000_0000_1001_0000
Expand Down Expand Up @@ -92,6 +94,40 @@ module idma_inst64_base #(
//--------------------------------------
// Memory subsystem
//--------------------------------------
axi_req_t [NumChannels-1:0] mem_req;
axi_resp_t [NumChannels-1:0] mem_res;

// Antiphase R/W stall: the DMA buffer alternately backs up and runs dry.
// A go bit only falls after its channel handshake, so no valid is ever withdrawn.
if (StallPattern) begin : gen_stall
logic [4:0] phase;
logic w_go, r_go;

always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
phase <= '0;
w_go <= 1'b1;
r_go <= 1'b1;
end else begin
phase <= phase + 5'd1;
w_go <= !phase[4] || (w_go && axi_req[0].w_valid && !mem_res[0].w_ready);
r_go <= phase[4] || (r_go && mem_res[0].r_valid && !axi_req[0].r_ready);
end
end

always_comb begin
mem_req = axi_req;
axi_res = mem_res;
mem_req[0].w_valid = axi_req[0].w_valid && w_go;
mem_req[0].r_ready = axi_req[0].r_ready && r_go;
axi_res[0].w_ready = mem_res[0].w_ready && w_go;
axi_res[0].r_valid = mem_res[0].r_valid && r_go;
end
end else begin : gen_no_stall
assign mem_req = axi_req;
assign axi_res = mem_res;
end

for (genvar c = 0; c < NumChannels; c++) begin : gen_mem_ch
axi_sim_mem #(
.AddrWidth ( AxiAddrWidth ),
Expand All @@ -107,8 +143,8 @@ module idma_inst64_base #(
) i_axi_sim_mem (
.clk_i ( clk ),
.rst_ni ( rst_n ),
.axi_req_i ( axi_req[c] ),
.axi_rsp_o ( axi_res[c] ),
.axi_req_i ( mem_req[c] ),
.axi_rsp_o ( mem_res[c] ),
.mon_w_valid_o ( /* NC */ ),
.mon_w_addr_o ( /* NC */ ),
.mon_w_data_o ( /* NC */ ),
Expand Down
4 changes: 2 additions & 2 deletions test/frontend/idma_inst64_tb_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ package idma_inst64_tb_pkg;
logic ar_valid, ar_ready, ar_done, ar_stall;
axi_pkg::len_t ar_len;
axi_pkg::size_t ar_size;
logic r_valid, r_ready, r_done, r_bw, r_stall;
logic w_valid, w_ready, w_done, w_stall;
logic r_valid, r_ready, r_done, r_bw, r_stall, buf_r_stall;
logic w_valid, w_ready, w_done, w_stall, buf_w_stall;
Comment on lines +106 to +107
logic [31:0] num_bytes_written;
logic b_valid, b_ready, b_done;
logic obi_wr_req, obi_rd_req;
Expand Down
22 changes: 17 additions & 5 deletions test/frontend/tb_idma_inst64_axi_copy.sv
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
module tb_idma_inst64_axi_copy;
import idma_inst64_tb_pkg::*;

idma_inst64_base harness ();
// The antiphase R/W stall is what makes the two buffer-stall events assert at all
idma_inst64_base #(.StallPattern(1'b1)) harness ();

localparam int unsigned TimeoutCycles = 32'd200000;
localparam int unsigned CopySize = 32'd4096;
Expand Down Expand Up @@ -44,8 +45,8 @@ module tb_idma_inst64_axi_copy;
typedef enum int unsigned {
EvAwValid, EvAwReady, EvAwDone, EvAwStall, EvAwLen, EvAwSize,
EvArValid, EvArReady, EvArDone, EvArStall, EvArLen, EvArSize,
EvRValid, EvRReady, EvRDone, EvRBw, EvRStall,
EvWValid, EvWReady, EvWDone, EvWStall, EvBytes,
EvRValid, EvRReady, EvRDone, EvRBw, EvRStall, EvRBufStall,
EvWValid, EvWReady, EvWDone, EvWStall, EvWBufStall, EvBytes,
EvBValid, EvBReady, EvBDone, EvBusy,
EvNumFields
} ev_field_e;
Expand Down Expand Up @@ -88,11 +89,15 @@ module tb_idma_inst64_axi_copy;
// r_bw duplicates r_done in the RTL; compare it to the pins, never to ev.r_done.
ev_field_ok[EvRBw ] = ev.r_bw === (bus_req.r_ready && bus_res.r_valid);
ev_field_ok[EvRStall ] = ev.r_stall === (bus_req.r_ready && !bus_res.r_valid);
// Read buffer full: the DUT refuses data the memory is offering
ev_field_ok[EvRBufStall] = ev.buf_r_stall === (!bus_req.r_ready && bus_res.r_valid);

ev_field_ok[EvWValid ] = ev.w_valid === bus_req.w_valid;
ev_field_ok[EvWReady ] = ev.w_ready === bus_res.w_ready;
ev_field_ok[EvWDone ] = ev.w_done === w_hs;
ev_field_ok[EvWStall ] = ev.w_stall === (bus_req.w_valid && !bus_res.w_ready);
// Write buffer empty: the memory is ready and the DUT has no beat to give
ev_field_ok[EvWBufStall] = ev.buf_w_stall === (bus_res.w_ready && !bus_req.w_valid);
ev_field_ok[EvBytes ] = ev.num_bytes_written ===
(w_hs ? 32'($countones(bus_req.w.strb)) : 32'd0);

Expand Down Expand Up @@ -127,6 +132,8 @@ module tb_idma_inst64_axi_copy;
int unsigned ev_b_beats = 0;
int unsigned ev_busy_cycles = 0;
int unsigned ev_bytes_written = 0;
int unsigned ev_buf_r_stalls = 0;
int unsigned ev_buf_w_stalls = 0;
axi_pkg::len_t ev_ar_len_seen, ev_aw_len_seen;
axi_pkg::size_t ev_ar_size_seen, ev_aw_size_seen;

Expand All @@ -150,6 +157,8 @@ module tb_idma_inst64_axi_copy;
if (ev.w_done) ev_w_beats++;
if (ev.b_done) ev_b_beats++;
if (ev.dma_busy) ev_busy_cycles++;
if (ev.buf_r_stall) ev_buf_r_stalls++;
if (ev.buf_w_stall) ev_buf_w_stalls++;
ev_bytes_written += ev.num_bytes_written;
end
end
Expand Down Expand Up @@ -283,16 +292,19 @@ module tb_idma_inst64_axi_copy;
ExpAxLen, ExpAxSize);
end
if (ev_busy_cycles == 0) $fatal(1, "events never reported the DMA busy");
// The stall pattern forces both, so a zero here means the event is not driven
if (ev_buf_r_stalls == 0) $fatal(1, "events never reported a read buffer stall");
if (ev_buf_w_stalls == 0) $fatal(1, "events never reported a write buffer stall");

if (errors != 0) $fatal(1, "TEST FAILED: %0d errors", errors);
$display("[TB] TEST PASSED: %0d B copied, ar=%0d aw=%0d beats", bytes_checked,
axi_ar_beats, axi_aw_beats);
$display({"[TB] events cross-checked vs bus pins: %0d fields every cycle; totals ",
"ar=%0d aw=%0d b=%0d r=%0d r_bw=%0d w=%0d beats, len=%0d size=%0d, ",
"%0d B written, busy %0d cycles"},
"%0d B written, busy %0d cycles, buf_r_stall %0d buf_w_stall %0d cycles"},
NumEvFields, ev_ar_beats, ev_aw_beats, ev_b_beats, ev_r_beats,
ev_r_bw_beats, ev_w_beats, ev_aw_len_seen, ev_aw_size_seen,
ev_bytes_written, ev_busy_cycles);
ev_bytes_written, ev_busy_cycles, ev_buf_r_stalls, ev_buf_w_stalls);
$finish;
end

Expand Down
Loading