Initial commit: retroDE_ps2 — first-of-its-kind PS2 GS FPGA core (DE25-Nano / Agilex 5)
RTL (GS rasterizer, EE core stub, platform bridge, LPDDR4B path), sim regression (272 TBs), docs, and tooling. Copyrighted PS2 content (BIOS, game code, GS dumps, and all dump-derived textures/traces) is excluded via .gitignore and stays local. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,471 @@
|
||||
// retroDE_ps2 — tb_ee_dmac_intc
|
||||
//
|
||||
// First completion-visible EE-side interrupt flow. Wires an EE DMAC
|
||||
// completion pulse into intc_stub as a real interrupt source, and
|
||||
// proves the full mask / pending / ack / re-assertion semantics by
|
||||
// driving two back-to-back transfers.
|
||||
//
|
||||
// Chain:
|
||||
// ee_memory_map_stub (DMAC read master, RAM region)
|
||||
// └─► ee_ram_stub (payload source, preloaded by TB)
|
||||
//
|
||||
// dmac_reg_stub (ch2) — minimal QWC=1 transfer
|
||||
// ep_* drained by a TB-level always-ready sink (no GIF in scope)
|
||||
// irq_completion_o (1-cycle pulse on S_DONE)
|
||||
// │
|
||||
// ▼
|
||||
// intc_stub.irq_src[0] (sticky latch into INTC_STAT[0])
|
||||
//
|
||||
// TB drives INTC register port directly — the EE map does not route
|
||||
// INTC addresses in current scope, per the deferred-follow-on list in
|
||||
// rtl/sif/README.md.
|
||||
//
|
||||
// Scenarios:
|
||||
// 1. Pre-DMA quiescence: INTC_STAT = 0, cpu_irq = 0.
|
||||
// 2. First transfer: DMA runs, DMA_DONE pulses irq_src[0],
|
||||
// INTC_STAT[0] latches to 1. cpu_irq still
|
||||
// 0 because INTC_MASK[0] is 0.
|
||||
// 3. Mask on: Write INTC_MASK = 0x1. cpu_irq becomes 1.
|
||||
// 4. Ack: Write INTC_STAT = 0x1 (W1C). INTC_STAT
|
||||
// reads 0, cpu_irq falls to 0.
|
||||
// 5. Quiescent hold: Wait N cycles. INTC_STAT stays 0 — no
|
||||
// false re-assertion without a new transfer.
|
||||
// 6. Second transfer: DMA runs again. INTC_STAT[0] re-latches.
|
||||
// Ack clears; quiescent again.
|
||||
//
|
||||
// Success = every assertion above holds and the trace files are
|
||||
// consistent (one IRQ event on each assertion, one on each ack).
|
||||
|
||||
`timescale 1ns/1ps
|
||||
|
||||
module tb_ee_dmac_intc;
|
||||
|
||||
localparam int EE_RAM_BYTES = 4 * 1024;
|
||||
localparam int EE_RAM_ADDR_W = $clog2(EE_RAM_BYTES);
|
||||
|
||||
localparam logic [31:0] SRC_BASE = 32'h0000_0100; // qword-aligned
|
||||
|
||||
localparam logic [7:0] DMAC_CHCR_OFF = 8'h00;
|
||||
localparam logic [7:0] DMAC_MADR_OFF = 8'h10;
|
||||
localparam logic [7:0] DMAC_QWC_OFF = 8'h20;
|
||||
|
||||
localparam logic [7:0] INTC_STAT_OFF = 8'h00;
|
||||
localparam logic [7:0] INTC_MASK_OFF = 8'h10;
|
||||
|
||||
logic clk;
|
||||
logic rst_n;
|
||||
|
||||
initial clk = 1'b0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// EE map + EE RAM (TB also preloads RAM through the map's fetch
|
||||
// write path — but the existing ee_memory_map_stub has no bridge
|
||||
// for the EE side separate from the new 128-bit bridge port. For
|
||||
// this TB we preload the EE RAM through the new bridge_wr_* port.)
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
logic dmac_mem_rd_en;
|
||||
logic [31:0] dmac_mem_rd_addr;
|
||||
logic [127:0] dmac_mem_rd_data;
|
||||
logic dmac_mem_rd_valid;
|
||||
|
||||
logic ee_map_ram_rd_en;
|
||||
logic [24:0] ee_map_ram_rd_addr;
|
||||
logic [127:0] ee_ram_rd_data;
|
||||
logic ee_ram_rd_valid;
|
||||
|
||||
logic ee_map_ram_wr_en;
|
||||
logic [24:0] ee_map_ram_wr_addr;
|
||||
logic [127:0] ee_map_ram_wr_data;
|
||||
logic [15:0] ee_map_ram_wr_be;
|
||||
logic [7:0] ee_map_ram_master_id;
|
||||
|
||||
// TB drives the bridge_wr_* port directly to preload RAM
|
||||
logic tb_preload_wr_en;
|
||||
logic [31:0] tb_preload_wr_addr;
|
||||
logic [127:0] tb_preload_wr_data;
|
||||
logic [15:0] tb_preload_wr_be;
|
||||
|
||||
ee_memory_map_stub u_ee_map (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.ee_rd_en(1'b0), .ee_rd_addr(32'd0),
|
||||
.ee_rd_data(), .ee_rd_valid(),
|
||||
.ee_wr_en(1'b0), .ee_wr_addr(32'd0),
|
||||
.ee_wr_data(32'd0), .ee_wr_be(4'd0),
|
||||
.dmac_rd_en(dmac_mem_rd_en), .dmac_rd_addr(dmac_mem_rd_addr),
|
||||
.dmac_rd_data(dmac_mem_rd_data), .dmac_rd_valid(dmac_mem_rd_valid),
|
||||
.bios_rd_en(), .bios_rd_addr(),
|
||||
.bios_rd_data(32'd0), .bios_rd_valid(1'b0),
|
||||
.ram_rd_en(ee_map_ram_rd_en), .ram_rd_addr(ee_map_ram_rd_addr),
|
||||
.ram_rd_data(ee_ram_rd_data), .ram_rd_valid(ee_ram_rd_valid),
|
||||
.bridge_wr_en(tb_preload_wr_en), .bridge_wr_addr(tb_preload_wr_addr),
|
||||
.bridge_wr_data(tb_preload_wr_data), .bridge_wr_be(tb_preload_wr_be),
|
||||
.bridge_master_id(8'd0), // TB direct
|
||||
.ram_wr_en(ee_map_ram_wr_en), .ram_wr_addr(ee_map_ram_wr_addr),
|
||||
.ram_wr_data(ee_map_ram_wr_data), .ram_wr_be(ee_map_ram_wr_be),
|
||||
.ram_master_id(ee_map_ram_master_id),
|
||||
.ee_dmac_ch2_wr_en(), .ee_dmac_ch2_wr_addr(), .ee_dmac_ch2_wr_data(),
|
||||
.ee_dmac_ch2_rd_en(), .ee_dmac_ch2_rd_addr(),
|
||||
.ee_dmac_ch2_rd_data(32'd0), .ee_dmac_ch2_rd_valid(1'b0),
|
||||
.ee_intc_wr_en(), .ee_intc_wr_addr(), .ee_intc_wr_data(),
|
||||
.ee_intc_rd_en(), .ee_intc_rd_addr(),
|
||||
.ee_intc_rd_data(32'd0), .ee_intc_rd_valid(1'b0),
|
||||
.ee_misc_mmio_wr_en(), .ee_misc_mmio_wr_addr(), .ee_misc_mmio_wr_data(), .ee_misc_mmio_wr_be(),
|
||||
.ee_misc_mmio_rd_en(), .ee_misc_mmio_rd_addr(),
|
||||
.ee_misc_mmio_rd_data(32'd0), .ee_misc_mmio_rd_valid(1'b0),
|
||||
.ee_biu_wr_en(), .ee_biu_wr_addr(), .ee_biu_wr_data(), .ee_biu_wr_be(),
|
||||
.ee_biu_rd_en(), .ee_biu_rd_addr(),
|
||||
.ee_biu_rd_data(32'd0), .ee_biu_rd_valid(1'b0),
|
||||
.ev_valid(), .ev_subsys(), .ev_event(),
|
||||
.ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
|
||||
);
|
||||
|
||||
ee_ram_stub #(.SIZE_BYTES(EE_RAM_BYTES)) u_ee_ram (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.rd_en(ee_map_ram_rd_en), .rd_addr(ee_map_ram_rd_addr[EE_RAM_ADDR_W-1:0]),
|
||||
.rd_data(ee_ram_rd_data), .rd_valid(ee_ram_rd_valid),
|
||||
.wr_en(ee_map_ram_wr_en), .wr_addr(ee_map_ram_wr_addr[EE_RAM_ADDR_W-1:0]),
|
||||
.wr_data(ee_map_ram_wr_data), .wr_be(ee_map_ram_wr_be),
|
||||
.master_id(ee_map_ram_master_id),
|
||||
.ev_valid(), .ev_subsys(), .ev_event(),
|
||||
.ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
|
||||
);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// DMAC (ch2). Endpoint drained by TB-level always-ready sink.
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
logic dmac_reg_wr_en;
|
||||
logic [7:0] dmac_reg_offset;
|
||||
logic [31:0] dmac_reg_wr_data;
|
||||
|
||||
logic dmac_ep_valid;
|
||||
logic [127:0] dmac_ep_data;
|
||||
logic dmac_ep_last;
|
||||
logic dmac_ep_ready;
|
||||
assign dmac_ep_ready = 1'b1; // immediately drain
|
||||
|
||||
logic dmac_irq_completion;
|
||||
|
||||
logic dmac_ev_valid;
|
||||
trace_pkg::subsys_e dmac_ev_subsys;
|
||||
trace_pkg::event_e dmac_ev_event;
|
||||
logic [63:0] dmac_ev_arg0, dmac_ev_arg1, dmac_ev_arg2, dmac_ev_arg3;
|
||||
logic [31:0] dmac_ev_flags;
|
||||
|
||||
dmac_reg_stub u_dmac (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.reg_wr_en(dmac_reg_wr_en), .reg_offset(dmac_reg_offset),
|
||||
.reg_wr_data(dmac_reg_wr_data),
|
||||
.reg_rd_en(1'b0), .reg_rd_data(), .reg_rd_valid(),
|
||||
.mem_rd_en(dmac_mem_rd_en), .mem_rd_addr(dmac_mem_rd_addr),
|
||||
.mem_rd_data(dmac_mem_rd_data), .mem_rd_valid(dmac_mem_rd_valid),
|
||||
.ep_valid(dmac_ep_valid), .ep_data(dmac_ep_data),
|
||||
.ep_last(dmac_ep_last), .ep_ready(dmac_ep_ready),
|
||||
.irq_completion_o(dmac_irq_completion),
|
||||
.ev_valid(dmac_ev_valid), .ev_subsys(dmac_ev_subsys),
|
||||
.ev_event(dmac_ev_event),
|
||||
.ev_arg0(dmac_ev_arg0), .ev_arg1(dmac_ev_arg1),
|
||||
.ev_arg2(dmac_ev_arg2), .ev_arg3(dmac_ev_arg3),
|
||||
.ev_flags(dmac_ev_flags)
|
||||
);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// INTC — bit 0 wired to DMAC completion, rest tied low
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
logic intc_reg_wr_en;
|
||||
logic intc_reg_rd_en;
|
||||
logic [7:0] intc_reg_addr;
|
||||
logic [31:0] intc_reg_wr_data;
|
||||
logic [31:0] intc_reg_rd_data;
|
||||
logic intc_reg_rd_valid;
|
||||
|
||||
logic [15:0] intc_irq_src;
|
||||
assign intc_irq_src = {15'd0, dmac_irq_completion};
|
||||
logic cpu_irq;
|
||||
|
||||
logic intc_ev_valid;
|
||||
trace_pkg::subsys_e intc_ev_subsys;
|
||||
trace_pkg::event_e intc_ev_event;
|
||||
logic [63:0] intc_ev_arg0, intc_ev_arg1, intc_ev_arg2, intc_ev_arg3;
|
||||
logic [31:0] intc_ev_flags;
|
||||
|
||||
intc_stub u_intc (
|
||||
.clk(clk), .rst_n(rst_n),
|
||||
.reg_wr_en(intc_reg_wr_en), .reg_rd_en(intc_reg_rd_en),
|
||||
.reg_addr(intc_reg_addr),
|
||||
.reg_wr_data(intc_reg_wr_data),
|
||||
.reg_rd_data(intc_reg_rd_data),
|
||||
.reg_rd_valid(intc_reg_rd_valid),
|
||||
.irq_src(intc_irq_src), .cpu_irq(cpu_irq),
|
||||
.ev_valid(intc_ev_valid), .ev_subsys(intc_ev_subsys),
|
||||
.ev_event(intc_ev_event),
|
||||
.ev_arg0(intc_ev_arg0), .ev_arg1(intc_ev_arg1),
|
||||
.ev_arg2(intc_ev_arg2), .ev_arg3(intc_ev_arg3),
|
||||
.ev_flags(intc_ev_flags)
|
||||
);
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Trace sinks
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
trace_sink_stub #(.FILENAME("ee_dmac_intc_dmac.trace"), .SINK_LABEL("dmac"))
|
||||
u_trace_dmac (.clk(clk), .rst_n(rst_n),
|
||||
.ev_valid(dmac_ev_valid), .ev_subsys(dmac_ev_subsys),
|
||||
.ev_event(dmac_ev_event), .ev_arg0(dmac_ev_arg0),
|
||||
.ev_arg1(dmac_ev_arg1), .ev_arg2(dmac_ev_arg2),
|
||||
.ev_arg3(dmac_ev_arg3), .ev_flags(dmac_ev_flags));
|
||||
|
||||
trace_sink_stub #(.FILENAME("ee_dmac_intc_intc.trace"), .SINK_LABEL("intc"))
|
||||
u_trace_intc (.clk(clk), .rst_n(rst_n),
|
||||
.ev_valid(intc_ev_valid), .ev_subsys(intc_ev_subsys),
|
||||
.ev_event(intc_ev_event), .ev_arg0(intc_ev_arg0),
|
||||
.ev_arg1(intc_ev_arg1), .ev_arg2(intc_ev_arg2),
|
||||
.ev_arg3(intc_ev_arg3), .ev_flags(intc_ev_flags));
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Counters
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
int dmac_done_events;
|
||||
int intc_assert_events;
|
||||
int intc_ack_events;
|
||||
int errors;
|
||||
|
||||
initial begin
|
||||
dmac_done_events = 0;
|
||||
intc_assert_events = 0;
|
||||
intc_ack_events = 0;
|
||||
errors = 0;
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
if (rst_n && dmac_ev_valid &&
|
||||
dmac_ev_subsys == trace_pkg::SUBSYS_DMAC &&
|
||||
dmac_ev_event == trace_pkg::EV_DMA_DONE)
|
||||
dmac_done_events <= dmac_done_events + 1;
|
||||
|
||||
if (rst_n && intc_ev_valid &&
|
||||
intc_ev_subsys == trace_pkg::SUBSYS_INTC &&
|
||||
intc_ev_event == trace_pkg::EV_IRQ) begin
|
||||
// intc_stub labels: arg3=1 is ack (W1C); arg3=0 can be either
|
||||
// a new source assertion OR a mask-register write. flags bit 0
|
||||
// distinguishes writes (mask wr) from source-driven assertions.
|
||||
if (intc_ev_arg3 == 64'd0 && (intc_ev_flags & 32'h1) == 32'd0)
|
||||
intc_assert_events <= intc_assert_events + 1;
|
||||
else if (intc_ev_arg3 == 64'd1)
|
||||
intc_ack_events <= intc_ack_events + 1;
|
||||
end
|
||||
end
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
task automatic preload_qword(input logic [31:0] addr, input logic [127:0] data);
|
||||
@(negedge clk);
|
||||
tb_preload_wr_en = 1'b1;
|
||||
tb_preload_wr_addr = addr;
|
||||
tb_preload_wr_data = data;
|
||||
tb_preload_wr_be = 16'hFFFF;
|
||||
@(negedge clk);
|
||||
tb_preload_wr_en = 1'b0;
|
||||
tb_preload_wr_addr = 32'd0;
|
||||
tb_preload_wr_data = 128'd0;
|
||||
tb_preload_wr_be = 16'd0;
|
||||
endtask
|
||||
|
||||
task automatic dmac_write(input logic [7:0] off, input logic [31:0] data);
|
||||
@(negedge clk);
|
||||
dmac_reg_wr_en = 1'b1;
|
||||
dmac_reg_offset = off;
|
||||
dmac_reg_wr_data = data;
|
||||
@(negedge clk);
|
||||
dmac_reg_wr_en = 1'b0;
|
||||
dmac_reg_offset = 8'd0;
|
||||
dmac_reg_wr_data = 32'd0;
|
||||
endtask
|
||||
|
||||
task automatic intc_write(input logic [7:0] addr, input logic [31:0] data);
|
||||
@(negedge clk);
|
||||
intc_reg_wr_en = 1'b1;
|
||||
intc_reg_addr = addr;
|
||||
intc_reg_wr_data = data;
|
||||
@(negedge clk);
|
||||
intc_reg_wr_en = 1'b0;
|
||||
intc_reg_addr = 8'd0;
|
||||
intc_reg_wr_data = 32'd0;
|
||||
endtask
|
||||
|
||||
task automatic intc_read(input logic [7:0] addr, output logic [31:0] data);
|
||||
@(negedge clk);
|
||||
intc_reg_rd_en = 1'b1;
|
||||
intc_reg_addr = addr;
|
||||
@(negedge clk);
|
||||
intc_reg_rd_en = 1'b0;
|
||||
intc_reg_addr = 8'd0;
|
||||
@(posedge clk);
|
||||
data = intc_reg_rd_data;
|
||||
endtask
|
||||
|
||||
task automatic run_dma_and_wait;
|
||||
dmac_write(DMAC_MADR_OFF, SRC_BASE);
|
||||
dmac_write(DMAC_QWC_OFF, 32'h0000_0001);
|
||||
dmac_write(DMAC_CHCR_OFF, 32'h0000_0001);
|
||||
repeat (20) @(posedge clk); // ample time for QWC=1
|
||||
endtask
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Stimulus
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
logic [31:0] stat_val;
|
||||
|
||||
initial begin
|
||||
rst_n = 1'b0;
|
||||
tb_preload_wr_en = 1'b0;
|
||||
tb_preload_wr_addr = 32'd0;
|
||||
tb_preload_wr_data = 128'd0;
|
||||
tb_preload_wr_be = 16'd0;
|
||||
dmac_reg_wr_en = 1'b0;
|
||||
dmac_reg_offset = 8'd0;
|
||||
dmac_reg_wr_data = 32'd0;
|
||||
intc_reg_wr_en = 1'b0;
|
||||
intc_reg_rd_en = 1'b0;
|
||||
intc_reg_addr = 8'd0;
|
||||
intc_reg_wr_data = 32'd0;
|
||||
|
||||
repeat (4) @(posedge clk);
|
||||
rst_n = 1'b1;
|
||||
repeat (2) @(posedge clk);
|
||||
|
||||
// Preload a distinctive qword into EE RAM for the DMAC to fetch.
|
||||
preload_qword(SRC_BASE, 128'hCAFE_BABE_DEAD_BEEF_1234_5678_AABB_CCDD);
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Scenario 1: pre-DMA quiescence
|
||||
// --------------------------------------------------------------
|
||||
intc_read(INTC_STAT_OFF, stat_val);
|
||||
if (stat_val !== 32'd0) begin
|
||||
$error("[tb_ee_dmac_intc] pre-DMA INTC_STAT nonzero: 0x%08h", stat_val);
|
||||
errors = errors + 1;
|
||||
end
|
||||
if (cpu_irq !== 1'b0) begin
|
||||
$error("[tb_ee_dmac_intc] pre-DMA cpu_irq asserted");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Scenario 2: first DMA — completion latches INTC_STAT[0]
|
||||
// --------------------------------------------------------------
|
||||
run_dma_and_wait();
|
||||
|
||||
if (dmac_done_events != 1) begin
|
||||
$error("[tb_ee_dmac_intc] expected 1 DMA_DONE after first run, got %0d",
|
||||
dmac_done_events);
|
||||
errors = errors + 1;
|
||||
end
|
||||
intc_read(INTC_STAT_OFF, stat_val);
|
||||
if ((stat_val & 32'h1) == 32'd0) begin
|
||||
$error("[tb_ee_dmac_intc] INTC_STAT bit 0 not set after DMA: 0x%08h", stat_val);
|
||||
errors = errors + 1;
|
||||
end
|
||||
// Mask is still 0 → cpu_irq should be 0
|
||||
if (cpu_irq !== 1'b0) begin
|
||||
$error("[tb_ee_dmac_intc] cpu_irq asserted with mask=0");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Scenario 3: mask on — cpu_irq rises
|
||||
// --------------------------------------------------------------
|
||||
intc_write(INTC_MASK_OFF, 32'h0000_0001);
|
||||
@(posedge clk);
|
||||
if (cpu_irq !== 1'b1) begin
|
||||
$error("[tb_ee_dmac_intc] cpu_irq not asserted after unmasking");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Scenario 4: ack — W1C clears STAT, cpu_irq falls
|
||||
// --------------------------------------------------------------
|
||||
intc_write(INTC_STAT_OFF, 32'h0000_0001);
|
||||
@(posedge clk);
|
||||
intc_read(INTC_STAT_OFF, stat_val);
|
||||
if (stat_val !== 32'd0) begin
|
||||
$error("[tb_ee_dmac_intc] INTC_STAT not cleared after ack: 0x%08h", stat_val);
|
||||
errors = errors + 1;
|
||||
end
|
||||
if (cpu_irq !== 1'b0) begin
|
||||
$error("[tb_ee_dmac_intc] cpu_irq still high after ack");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Scenario 5: quiescent hold — no false re-assertion
|
||||
// --------------------------------------------------------------
|
||||
repeat (30) @(posedge clk);
|
||||
intc_read(INTC_STAT_OFF, stat_val);
|
||||
if (stat_val !== 32'd0) begin
|
||||
$error("[tb_ee_dmac_intc] INTC_STAT reasserted without new DMA: 0x%08h", stat_val);
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Scenario 6: second DMA re-latches; ack clears again
|
||||
// --------------------------------------------------------------
|
||||
run_dma_and_wait();
|
||||
|
||||
if (dmac_done_events != 2) begin
|
||||
$error("[tb_ee_dmac_intc] expected 2 DMA_DONE after second run, got %0d",
|
||||
dmac_done_events);
|
||||
errors = errors + 1;
|
||||
end
|
||||
intc_read(INTC_STAT_OFF, stat_val);
|
||||
if ((stat_val & 32'h1) == 32'd0) begin
|
||||
$error("[tb_ee_dmac_intc] second DMA did not re-latch INTC_STAT[0]: 0x%08h",
|
||||
stat_val);
|
||||
errors = errors + 1;
|
||||
end
|
||||
if (cpu_irq !== 1'b1) begin
|
||||
$error("[tb_ee_dmac_intc] cpu_irq not high with mask=1 and stat=1");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
intc_write(INTC_STAT_OFF, 32'h0000_0001);
|
||||
@(posedge clk);
|
||||
intc_read(INTC_STAT_OFF, stat_val);
|
||||
if (stat_val !== 32'd0) begin
|
||||
$error("[tb_ee_dmac_intc] second ack didn't clear INTC_STAT");
|
||||
errors = errors + 1;
|
||||
end
|
||||
|
||||
repeat (4) @(posedge clk);
|
||||
|
||||
$display("[tb_ee_dmac_intc] done=%0d asserts=%0d acks=%0d errors=%0d",
|
||||
dmac_done_events, intc_assert_events, intc_ack_events, errors);
|
||||
|
||||
if (dmac_done_events != 2) $error("expected 2 DMA_DONE, got %0d", dmac_done_events);
|
||||
if (intc_assert_events != 2) $error("expected 2 INTC assertions, got %0d", intc_assert_events);
|
||||
if (intc_ack_events != 2) $error("expected 2 INTC acks, got %0d", intc_ack_events);
|
||||
|
||||
if (errors == 0 &&
|
||||
dmac_done_events == 2 &&
|
||||
intc_assert_events == 2 &&
|
||||
intc_ack_events == 2)
|
||||
$display("[tb_ee_dmac_intc] PASS");
|
||||
else
|
||||
$display("[tb_ee_dmac_intc] FAIL");
|
||||
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
#500000;
|
||||
$error("[tb_ee_dmac_intc] timeout");
|
||||
$finish;
|
||||
end
|
||||
|
||||
endmodule : tb_ee_dmac_intc
|
||||
Reference in New Issue
Block a user