module tb_top; logic gb_clk; logic vga_clk; logic gb_nreset; logic vga_nreset; logic nreset; wire gb_cart_clk; wire gb_cart_nreset; wire gb_cart_nrd; wire gb_cart_nwr; wire gb_cart_ncs; wire [15:0] gb_cart_addr; wire [ 7:0] gb_cart_data; wire display_enable; wire [ 1:0] ppu_pixel; wire ppu_hsync; wire ppu_vsync; logic [7:0] vga_r; logic [7:0] vga_g; logic [7:0] vga_b; logic vga_hsync; logic vga_vsync; // 4.19MHz clkgen #( .PERIOD_NS (238), .RESET_DELAY_NS (400) ) gb_clkgen_inst ( .clk (gb_clk), .nreset(gb_nreset) ); // 25MHz clkgen #( .PERIOD_NS (40), .RESET_DELAY_NS (70) ) vga_clkgen_inst ( .clk (vga_clk), .nreset(vga_nreset) ); assign nreset = gb_nreset & vga_nreset; gb gb_inst ( .clk (gb_clk), .nreset(nreset), .cart_clk_o (gb_cart_clk), .cart_nreset_o(gb_cart_nreset), .cart_nrd_o (gb_cart_nrd), .cart_nwr_o (gb_cart_nwr), .cart_ncs_o (gb_cart_ncs), .cart_addr_o (gb_cart_addr), .cart_data_io (gb_cart_data), .ppu_display_enable_o(display_enable), .ppu_pixel_o (ppu_pixel), .ppu_vsync_o (ppu_vsync), .ppu_hsync_o (ppu_hsync) ); cart cart_inst ( .clk (gb_cart_clk), .nreset (gb_cart_nreset), .nrd_i (gb_cart_nrd), .nwr_i (gb_cart_nwr), .ncs_i (gb_cart_ncs), .addr_i (gb_cart_addr), .data_io (gb_cart_data) ); display display_inst ( .nreset (nreset), .VGA_PALETTE( // RR GG BB '{24'h00_00_00, // colour 00 24'h60_60_60, // colour 01 24'hA0_A0_A0, // colour 10 24'hFF_FF_FF} // colour 11 ), .display_enable_i(display_enable), .ppu_clk (gb_clk), .ppu_pixel_i(ppu_pixel), .ppu_vsync_i(ppu_vsync), .ppu_hsync_i(ppu_hsync), .vga_clk (vga_clk), .vga_r_o (vga_r), .vga_g_o (vga_g), .vga_b_o (vga_b), .vga_hsync_o(vga_hsync), .vga_vsync_o(vga_vsync) ); vgasim vgasim_inst ( .clk (vga_clk), .nreset(nreset), .r_i (vga_r), .g_i (vga_g), .b_i (vga_b), .hsync_i (vga_hsync), .vsync_i (vga_vsync) ); `ifdef SVA_ENABLE `include "sva_common.svh" `SVA_DEF_CLK(gb_clk); `SVA_DEF_NRESET(nreset); logic instr_valid; logic instr_undef; logic halted; logic [15:0] current_pc; logic [ 7:0] current_opcode[2:0]; logic we; logic [15:0] last_write_address; logic [ 7:0] last_write_value; logic vram_sel; logic hiram_sel; logic ppu_sel; assign halted = gb_inst.cpu_inst.ctrl_inst.halted_r; assign instr_valid = gb_inst.cpu_inst.instr_valid; assign instr_undef = gb_inst.cpu_inst.ctrl_inst.instr_undef; assign current_opcode = gb_inst.cpu_inst.instr; assign we = gb_inst.cpu_we; assign vram_sel = gb_inst.vram_sel; assign hiram_sel = gb_inst.hiram_sel; assign ppu_sel = gb_inst.ppu_inst.ppu_ioregs_inst.sva_sel; always_ff @(posedge gb_clk or negedge nreset) begin if (!nreset) current_pc <= '0; else if (instr_valid) current_pc <= gb_inst.cpu_inst.pc_r; end always_ff @(posedge gb_clk or negedge nreset) begin if (!nreset) begin last_write_address <= '0; last_write_value <= '0; end else if (we) begin last_write_address <= gb_inst.cpu_addr; last_write_value <= gb_inst.cpu_wdata; end end `SVA_ASSERT_PROP_FATAL(undefined_opcode_pushed, halted |-> !instr_undef, $sformatf("PC: 0x%X | Undefined opcode pushed: 0x%X (0x%X, 0x%X)", current_pc, current_opcode[0], current_opcode[1], current_opcode[2]) ); logic selected_memory_implemented; assign selected_memory_implemented = hiram_sel | vram_sel | ppu_sel; `SVA_ASSERT_PROP(write_to_unimplemented_memory, we |-> selected_memory_implemented, $sformatf("PC: 0x%X | Write to unimplemented memory: 0x%X <= 0x%X", current_pc, last_write_address, last_write_value) ); // Watchdog timer localparam HIST_SIZE = 8; localparam MAX_COUNT = 10000; logic [15:0] pc_history [$]; int unsigned pc_history_count [$]; always @(posedge gb_clk iff instr_valid) begin automatic int unsigned current_pc_count = 0; foreach (pc_history[i]) begin if (current_pc == pc_history[i]) begin // History contains current PC, remove from history but keep counter current_pc_count = pc_history_count[i]; pc_history.delete(i); pc_history_count.delete(i); break; end end // Put in front of history queue pc_history.push_front(current_pc); pc_history_count.push_front(current_pc_count + 1); // Prune back of history if (pc_history.size() > HIST_SIZE) begin void'(pc_history.pop_back()); void'(pc_history_count.pop_back()); end assert (current_pc_count < MAX_COUNT) else begin $display($sformatf("[%0t] Watchdog timeout", $time)); $display("History:"); foreach (pc_history[i]) begin $display($sformatf("0x%X: %d", pc_history[i], pc_history_count[i])); end $fatal(); end end `endif /* SVA_ENABLE */ endmodule : tb_top