Files
thejayman77 ec82764bef 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>
2026-06-29 20:10:50 -04:00

535 lines
21 KiB
Systemverilog
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// retroDE_ps2 — tb_iop_self_driven
//
// First RAM-backed self-driven IOP transaction. After a single go_i
// pulse, the TB stops orchestrating. The exec stub fetches its control
// program from IOP RAM — same 32-bit map path a real IOP CPU would use
// — and runs it to completion. The TB's only responsibility after
// reset is to lay the script and DMA payload into RAM, then observe.
//
// Chain (all architectural addresses routed by the real IOP map):
// iop_exec_stub
// ├── map reads 0x0000_0400..0x0000_04FF → IOP RAM (ifetch)
// ├── map write 0x1F80_1074 → IOP INTC I_MASK
// ├── map writes 0x1F80_1520/4/8 → IOP DMAC ch9 MADR/BCR/CHCR
// ├── map reads 0x1F80_1070 → IOP INTC I_STAT
// └── map writes 0x1F80_1070 → IOP INTC I_STAT (W1C)
//
// iop_dmac_reg_stub (ch9) pulls source words through map.dma_rd_*,
// drives an endpoint whose ep_ready the TB uses for the stall gate.
// On S_DONE, irq_completion_o pulses; INTC latches and raises cpu_irq.
//
// RAM layout preloaded by the TB via bridge_wr_*:
// 0x0000_0200..0x0000_020F DMA payload (4 × 32-bit words)
// 0x0000_0400..0x0000_049F 10-op exec script (16 bytes / op)
//
// Script (one transaction):
// pc=0 WRITE INTC_MASK = 1
// pc=1 WRITE DMAC_MADR = 0x0000_0200
// pc=2 WRITE DMAC_BCR = 4
// pc=3 WRITE DMAC_CHCR = 1 (start)
// pc=4 WAIT_IRQ
// pc=5 READ INTC_STAT (verify pending)
// pc=6 WRITE INTC_STAT = 1 (W1C ack)
// pc=7 READ INTC_STAT (verify cleared)
// pc=8 READ DMAC_CHCR (verify start cleared)
// pc=9 HALT
//
// Pre-completion stall window:
// The TB holds ep_ready=0 for STALL_WINDOW cycles after reset. During
// that period the DMAC starts, tries to ship beat 0, and stalls. The
// exec stub must stay in WAIT_IRQ with pc at the wait slot. No
// cpu_irq must assert. Halt must NOT happen. Once ep_ready rises,
// the DMAC completes, cpu_irq comes up, the exec stub walks the
// remaining ops and halts.
`timescale 1ns/1ps
module tb_iop_self_driven;
localparam int IOP_RAM_BYTES = 4 * 1024;
localparam int IOP_RAM_ADDR_W = $clog2(IOP_RAM_BYTES);
// Script location (must match exec stub SCRIPT_BASE parameter)
localparam logic [31:0] SCRIPT_BASE = 32'h0000_0400;
localparam logic [31:0] PAYLOAD_BASE = 32'h0000_0200;
// Architectural addresses reached by the script
localparam logic [31:0] DMAC_MADR_ADDR = 32'h1F80_1520;
localparam logic [31:0] DMAC_BCR_ADDR = 32'h1F80_1524;
localparam logic [31:0] DMAC_CHCR_ADDR = 32'h1F80_1528;
localparam logic [31:0] INTC_STAT_ADDR = 32'h1F80_1070;
localparam logic [31:0] INTC_MASK_ADDR = 32'h1F80_1074;
// Opcodes (keep in sync with iop_exec_stub)
localparam logic [31:0] OP_HALT = 32'h0000_0000;
localparam logic [31:0] OP_WRITE = 32'h0000_0001;
localparam logic [31:0] OP_READ = 32'h0000_0002;
localparam logic [31:0] OP_WAIT_IRQ = 32'h0000_0003;
localparam logic [7:0] EXEC_PC_WAIT_IRQ = 8'd4;
localparam int STALL_WINDOW = 30;
logic clk;
logic rst_n;
initial clk = 1'b0;
always #5 clk = ~clk;
// ------------------------------------------------------------------
// IOP exec stub (drives the IOP map CPU-side port)
// ------------------------------------------------------------------
logic exec_go;
logic exec_halt;
logic [7:0] exec_pc;
logic map_cpu_rd_en;
logic [31:0] map_cpu_rd_addr;
logic [31:0] map_cpu_rd_data;
logic map_cpu_rd_valid;
logic map_cpu_wr_en;
logic [31:0] map_cpu_wr_addr;
logic [31:0] map_cpu_wr_data;
logic [3:0] map_cpu_wr_be;
logic iop_cpu_irq;
logic exec_ev_valid;
trace_pkg::subsys_e exec_ev_subsys;
trace_pkg::event_e exec_ev_event;
logic [63:0] exec_ev_arg0, exec_ev_arg1, exec_ev_arg2, exec_ev_arg3;
logic [31:0] exec_ev_flags;
iop_exec_stub #(.SCRIPT_BASE(SCRIPT_BASE)) u_exec (
.clk(clk), .rst_n(rst_n),
.go_i(exec_go),
.map_rd_en(map_cpu_rd_en), .map_rd_addr(map_cpu_rd_addr),
.map_rd_data(map_cpu_rd_data), .map_rd_valid(map_cpu_rd_valid),
.map_wr_en(map_cpu_wr_en), .map_wr_addr(map_cpu_wr_addr),
.map_wr_data(map_cpu_wr_data), .map_wr_be(map_cpu_wr_be),
.cpu_irq(iop_cpu_irq),
.halt_o(exec_halt), .pc_o(exec_pc),
.ev_valid(exec_ev_valid), .ev_subsys(exec_ev_subsys),
.ev_event(exec_ev_event),
.ev_arg0(exec_ev_arg0), .ev_arg1(exec_ev_arg1),
.ev_arg2(exec_ev_arg2), .ev_arg3(exec_ev_arg3),
.ev_flags(exec_ev_flags)
);
// ------------------------------------------------------------------
// IOP map + IOP RAM. TB drives bridge_wr_* to preload payload + script.
// ------------------------------------------------------------------
logic iop_ram_rd_en;
logic [20:0] iop_ram_rd_addr;
logic [31:0] iop_ram_rd_data;
logic iop_ram_rd_valid;
logic iop_ram_wr_en;
logic [20:0] iop_ram_wr_addr;
logic [31:0] iop_ram_wr_data;
logic [3:0] iop_ram_wr_be;
logic [7:0] iop_ram_master_id;
logic map_dmac_rd_en;
logic [3:0] map_dmac_rd_addr;
logic [31:0] map_dmac_rd_data;
logic map_dmac_rd_valid;
logic map_dmac_wr_en;
logic [3:0] map_dmac_wr_addr;
logic [31:0] map_dmac_wr_data;
logic map_intc_rd_en;
logic [7:0] map_intc_rd_addr;
logic [31:0] map_intc_rd_data;
logic map_intc_rd_valid;
logic map_intc_wr_en;
logic [7:0] map_intc_wr_addr;
logic [31:0] map_intc_wr_data;
logic dma_rd_en;
logic [31:0] dma_rd_addr;
logic [7:0] dma_master_id;
logic [31:0] dma_rd_data;
logic dma_rd_valid;
// TB-driven bridge_wr_* for preload
logic tb_br_wr_en;
logic [31:0] tb_br_wr_addr;
logic [31:0] tb_br_wr_data;
logic [3:0] tb_br_wr_be;
logic iop_map_ev_valid;
trace_pkg::subsys_e iop_map_ev_subsys;
trace_pkg::event_e iop_map_ev_event;
logic [63:0] iop_map_ev_arg0, iop_map_ev_arg1, iop_map_ev_arg2, iop_map_ev_arg3;
logic [31:0] iop_map_ev_flags;
iop_memory_map_stub u_iop_map (
.clk(clk), .rst_n(rst_n),
.iop_rd_en(map_cpu_rd_en), .iop_rd_addr(map_cpu_rd_addr),
.iop_rd_data(map_cpu_rd_data), .iop_rd_valid(map_cpu_rd_valid),
.iop_wr_en(map_cpu_wr_en), .iop_wr_addr(map_cpu_wr_addr),
.iop_wr_data(map_cpu_wr_data), .iop_wr_be(map_cpu_wr_be),
.master_id(8'd2),
.bridge_wr_en(tb_br_wr_en), .bridge_wr_addr(tb_br_wr_addr),
.bridge_wr_data(tb_br_wr_data), .bridge_wr_be(tb_br_wr_be),
.bridge_master_id(8'd0), // TB direct
.dma_rd_en(dma_rd_en), .dma_rd_addr(dma_rd_addr),
.dma_master_id(dma_master_id),
.dma_rd_data(dma_rd_data), .dma_rd_valid(dma_rd_valid),
.sif_rd_en(), .sif_rd_addr(),
.sif_rd_data(32'd0), .sif_rd_valid(1'b0),
.sif_wr_en(), .sif_wr_addr(), .sif_wr_data(),
.iop_dmac_rd_en(map_dmac_rd_en), .iop_dmac_rd_addr(map_dmac_rd_addr),
.iop_dmac_rd_data(map_dmac_rd_data), .iop_dmac_rd_valid(map_dmac_rd_valid),
.iop_dmac_wr_en(map_dmac_wr_en), .iop_dmac_wr_addr(map_dmac_wr_addr),
.iop_dmac_wr_data(map_dmac_wr_data),
.iop_intc_rd_en(map_intc_rd_en), .iop_intc_rd_addr(map_intc_rd_addr),
.iop_intc_rd_data(map_intc_rd_data), .iop_intc_rd_valid(map_intc_rd_valid),
.iop_intc_wr_en(map_intc_wr_en), .iop_intc_wr_addr(map_intc_wr_addr),
.iop_intc_wr_data(map_intc_wr_data),
.input_p1(32'd0), .input_p2(32'd0),
// BIOS ROM port — unused by this TB
.bios_rd_en(), .bios_rd_addr(),
.bios_rd_data(32'd0), .bios_rd_valid(1'b0),
.ram_rd_en(iop_ram_rd_en), .ram_rd_addr(iop_ram_rd_addr),
.ram_rd_data(iop_ram_rd_data), .ram_rd_valid(iop_ram_rd_valid),
.ram_wr_en(iop_ram_wr_en), .ram_wr_addr(iop_ram_wr_addr),
.ram_wr_data(iop_ram_wr_data), .ram_wr_be(iop_ram_wr_be),
.ram_master_id(iop_ram_master_id),
.ev_valid(iop_map_ev_valid), .ev_subsys(iop_map_ev_subsys),
.ev_event(iop_map_ev_event),
.ev_arg0(iop_map_ev_arg0), .ev_arg1(iop_map_ev_arg1),
.ev_arg2(iop_map_ev_arg2), .ev_arg3(iop_map_ev_arg3),
.ev_flags(iop_map_ev_flags)
);
iop_ram_stub #(.SIZE_BYTES(IOP_RAM_BYTES)) u_iop_ram (
.clk(clk), .rst_n(rst_n),
.rd_en(iop_ram_rd_en), .rd_addr(iop_ram_rd_addr[IOP_RAM_ADDR_W-1:0]),
.rd_data(iop_ram_rd_data), .rd_valid(iop_ram_rd_valid),
.wr_en(iop_ram_wr_en), .wr_addr(iop_ram_wr_addr[IOP_RAM_ADDR_W-1:0]),
.wr_data(iop_ram_wr_data), .wr_be(iop_ram_wr_be),
.master_id(iop_ram_master_id),
.ev_valid(), .ev_subsys(), .ev_event(),
.ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
);
// ------------------------------------------------------------------
// IOP DMAC ch9 — endpoint drained into the TB (data on floor)
// ------------------------------------------------------------------
logic ep_valid;
logic [31:0] ep_data;
logic ep_last;
logic ep_ready;
logic tb_stall;
assign ep_ready = !tb_stall;
logic dmac_irq_completion;
logic dmac_busy;
logic [31:0] dmac_done_count;
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;
iop_dmac_reg_stub u_dmac (
.clk(clk), .rst_n(rst_n),
.reg_wr_en(map_dmac_wr_en), .reg_rd_en(map_dmac_rd_en),
.reg_offset(map_dmac_wr_en ? map_dmac_wr_addr : map_dmac_rd_addr),
.reg_wr_data(map_dmac_wr_data),
.reg_rd_data(map_dmac_rd_data), .reg_rd_valid(map_dmac_rd_valid),
.mem_rd_en(dma_rd_en), .mem_rd_addr(dma_rd_addr),
.mem_master_id(dma_master_id),
.mem_rd_data(dma_rd_data), .mem_rd_valid(dma_rd_valid),
.ep_valid(ep_valid), .ep_data(ep_data), .ep_last(ep_last),
.ep_ready(ep_ready),
.irq_completion_o(dmac_irq_completion),
.busy_o(dmac_busy), .done_count_o(dmac_done_count),
.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)
);
// ------------------------------------------------------------------
// IOP INTC
// ------------------------------------------------------------------
logic [15:0] iop_irq_src;
assign iop_irq_src = {15'd0, dmac_irq_completion};
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 #(
.INTC_STAT_OFFSET(8'h70),
.INTC_MASK_OFFSET(8'h74)
) u_intc (
.clk(clk), .rst_n(rst_n),
.reg_wr_en(map_intc_wr_en), .reg_rd_en(map_intc_rd_en),
.reg_addr(map_intc_wr_en ? map_intc_wr_addr : map_intc_rd_addr),
.reg_wr_data(map_intc_wr_data),
.reg_rd_data(map_intc_rd_data), .reg_rd_valid(map_intc_rd_valid),
.irq_src(iop_irq_src), .cpu_irq(iop_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("iop_self_driven_exec.trace"), .SINK_LABEL("iop_exec"))
u_trace_exec (.clk(clk), .rst_n(rst_n),
.ev_valid(exec_ev_valid), .ev_subsys(exec_ev_subsys),
.ev_event(exec_ev_event), .ev_arg0(exec_ev_arg0),
.ev_arg1(exec_ev_arg1), .ev_arg2(exec_ev_arg2),
.ev_arg3(exec_ev_arg3), .ev_flags(exec_ev_flags));
trace_sink_stub #(.FILENAME("iop_self_driven_map.trace"), .SINK_LABEL("iop_map"))
u_trace_map (.clk(clk), .rst_n(rst_n),
.ev_valid(iop_map_ev_valid), .ev_subsys(iop_map_ev_subsys),
.ev_event(iop_map_ev_event), .ev_arg0(iop_map_ev_arg0),
.ev_arg1(iop_map_ev_arg1), .ev_arg2(iop_map_ev_arg2),
.ev_arg3(iop_map_ev_arg3), .ev_flags(iop_map_ev_flags));
trace_sink_stub #(.FILENAME("iop_self_driven_dmac.trace"), .SINK_LABEL("iop_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("iop_self_driven_intc.trace"), .SINK_LABEL("iop_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));
// ------------------------------------------------------------------
// Observers (no orchestration after go)
// ------------------------------------------------------------------
int dmac_done_events;
int intc_assert_events;
int intc_ack_events;
int exec_op_events;
int wait_irq_exit_events;
int halt_events;
logic [7:0] stall_pc_sample;
logic stall_irq_sample;
logic stall_halt_sample;
int errors;
initial begin
dmac_done_events = 0;
intc_assert_events = 0;
intc_ack_events = 0;
exec_op_events = 0;
wait_irq_exit_events = 0;
halt_events = 0;
stall_pc_sample = 8'd0;
stall_irq_sample = 1'b0;
stall_halt_sample = 1'b0;
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
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
if (rst_n && exec_ev_valid) begin
exec_op_events <= exec_op_events + 1;
if ((exec_ev_flags & 32'h2) != 32'd0)
wait_irq_exit_events <= wait_irq_exit_events + 1;
if ((exec_ev_flags & 32'h4) != 32'd0)
halt_events <= halt_events + 1;
end
end
// ------------------------------------------------------------------
// Preload helpers (bridge_wr_* → iop_ram_stub, 32-bit words)
// ------------------------------------------------------------------
task automatic bridge_write(input logic [31:0] addr, input logic [31:0] data);
@(negedge clk);
tb_br_wr_en = 1'b1;
tb_br_wr_addr = addr;
tb_br_wr_data = data;
tb_br_wr_be = 4'b1111;
@(negedge clk);
tb_br_wr_en = 1'b0;
tb_br_wr_addr = 32'd0;
tb_br_wr_data = 32'd0;
tb_br_wr_be = 4'd0;
endtask
task automatic emit_op(input int slot,
input logic [31:0] opcode,
input logic [31:0] addr_or_tgt,
input logic [31:0] data_or_exp);
logic [31:0] base;
base = SCRIPT_BASE + (slot << 4);
bridge_write(base + 32'd0, opcode);
bridge_write(base + 32'd4, addr_or_tgt);
bridge_write(base + 32'd8, data_or_exp);
bridge_write(base + 32'd12, 32'd0);
endtask
// ------------------------------------------------------------------
// Stimulus
// ------------------------------------------------------------------
initial begin
rst_n = 1'b0;
exec_go = 1'b0;
tb_stall = 1'b1;
tb_br_wr_en = 1'b0;
tb_br_wr_addr = 32'd0;
tb_br_wr_data = 32'd0;
tb_br_wr_be = 4'd0;
repeat (4) @(posedge clk);
rst_n = 1'b1;
repeat (2) @(posedge clk);
// Preload DMA payload (4 distinctive words into IOP RAM)
bridge_write(PAYLOAD_BASE + 32'h00, 32'hBEEF_0000);
bridge_write(PAYLOAD_BASE + 32'h04, 32'hBEEF_0001);
bridge_write(PAYLOAD_BASE + 32'h08, 32'hBEEF_0002);
bridge_write(PAYLOAD_BASE + 32'h0C, 32'hBEEF_0003);
// Preload control program at SCRIPT_BASE
emit_op( 0, OP_WRITE, INTC_MASK_ADDR, 32'h0000_0001);
emit_op( 1, OP_WRITE, DMAC_MADR_ADDR, PAYLOAD_BASE);
emit_op( 2, OP_WRITE, DMAC_BCR_ADDR, 32'h0000_0004);
emit_op( 3, OP_WRITE, DMAC_CHCR_ADDR, 32'h0000_0001);
emit_op( 4, OP_WAIT_IRQ, 32'd0, 32'd0);
emit_op( 5, OP_READ, INTC_STAT_ADDR, 32'd0);
emit_op( 6, OP_WRITE, INTC_STAT_ADDR, 32'h0000_0001);
emit_op( 7, OP_READ, INTC_STAT_ADDR, 32'd0);
emit_op( 8, OP_READ, DMAC_CHCR_ADDR, 32'd0);
emit_op( 9, OP_HALT, 32'd0, 32'd0);
// Single-shot start
@(negedge clk);
exec_go = 1'b1;
@(negedge clk);
exec_go = 1'b0;
// Let ops 0..3 execute and reach WAIT_IRQ. Each op = 3 ifetch
// reads (6 cycles) + op execution (~2 cycles). Slack is ample.
repeat (80) @(posedge clk);
// Stall-window invariants
stall_pc_sample = exec_pc;
stall_irq_sample = iop_cpu_irq;
stall_halt_sample = exec_halt;
if (stall_pc_sample !== EXEC_PC_WAIT_IRQ) begin
$error("[tb_iop_self_driven] exec pc in stall window: got %0d, expected %0d (WAIT_IRQ)",
stall_pc_sample, EXEC_PC_WAIT_IRQ);
errors = errors + 1;
end
if (stall_irq_sample !== 1'b0) begin
$error("[tb_iop_self_driven] cpu_irq asserted during stall window");
errors = errors + 1;
end
if (stall_halt_sample !== 1'b0) begin
$error("[tb_iop_self_driven] exec halted during stall window");
errors = errors + 1;
end
repeat (STALL_WINDOW) @(posedge clk);
if (exec_pc !== EXEC_PC_WAIT_IRQ) begin
$error("[tb_iop_self_driven] exec advanced past WAIT_IRQ without IRQ: pc=%0d",
exec_pc);
errors = errors + 1;
end
if (iop_cpu_irq !== 1'b0) begin
$error("[tb_iop_self_driven] cpu_irq rose during extended stall");
errors = errors + 1;
end
// Release the stall — chain completes, exec stub walks remaining ops
@(negedge clk);
tb_stall = 1'b0;
while (!exec_halt) @(posedge clk);
repeat (4) @(posedge clk);
if (dmac_done_events != 1) begin
$error("[tb_iop_self_driven] expected 1 DMA_DONE, got %0d", dmac_done_events);
errors = errors + 1;
end
if (intc_assert_events != 1) begin
$error("[tb_iop_self_driven] expected 1 INTC assert, got %0d", intc_assert_events);
errors = errors + 1;
end
if (intc_ack_events != 1) begin
$error("[tb_iop_self_driven] expected 1 INTC ack, got %0d", intc_ack_events);
errors = errors + 1;
end
if (wait_irq_exit_events != 1) begin
$error("[tb_iop_self_driven] expected 1 WAIT_IRQ exit, got %0d",
wait_irq_exit_events);
errors = errors + 1;
end
if (halt_events != 1) begin
$error("[tb_iop_self_driven] expected 1 halt event, got %0d", halt_events);
errors = errors + 1;
end
$display("[tb_iop_self_driven] stall_pc=%0d ops=%0d wait_irq_exit=%0d halt=%0d done=%0d asserts=%0d acks=%0d errors=%0d",
stall_pc_sample, exec_op_events, wait_irq_exit_events,
halt_events, dmac_done_events,
intc_assert_events, intc_ack_events, errors);
if (errors == 0 &&
dmac_done_events == 1 &&
intc_assert_events == 1 &&
intc_ack_events == 1 &&
wait_irq_exit_events == 1 &&
halt_events == 1)
$display("[tb_iop_self_driven] PASS");
else
$display("[tb_iop_self_driven] FAIL");
$finish;
end
initial begin
#2000000;
$error("[tb_iop_self_driven] timeout");
$finish;
end
endmodule : tb_iop_self_driven