`define VSYNC_HACK module ppu ( input logic clk, input logic nreset, input logic [15:0] cpu_addr_i, output logic [ 7:0] cpu_rdata_o, input logic cpu_we_i, input logic [ 7:0] cpu_wdata_i ); `define IOREG_DECL(name) \ logic [ 7:0] name``_r; \ logic [ 7:0] name``_next; \ logic name``_we; \ logic name``_sel; `define IOREG_DEF(name, addr, rstval) \ always_ff @(posedge clk or negedge nreset) begin \ if (!nreset) \ name``_r <= rstval; \ else if (name``_we) \ name``_r <= name``_next; \ end \ assign name``_sel = (cpu_addr_i == addr); \ assign name``_we = name``_we & cpu_we_i; typedef struct packed { logic lcd_en; logic win_tilemap_sel; logic win_en; logic tiledata_sel; logic bg_tilemap_sel; logic obj_size; logic obj_display; logic bgwin_display; } lcdc_t; lcdc_t lcdc; logic [3:0] [1:0] bgp; // LX is not read/writeable logic [ 8:0] lx_r; logic [ 8:0] lx_next; logic lx_end_of_line; logic ly_end_of_frame; `IOREG_DECL(lcdc); `IOREG_DECL(ly); `IOREG_DECL(sy); `IOREG_DECL(bgp); `IOREG_DEF (lcdc, 16'hFF40, '0); `IOREG_DEF ( sy, 16'hFF42, '0); `IOREG_DEF ( bgp, 16'hFF47, '0); assign lcdc_next = cpu_wdata_i; assign sy_next = cpu_wdata_i; assign bgp_next = cpu_wdata_i; assign lcdc = lcdc_r; assign bgp = bgp_r; always_ff @(posedge clk or negedge nreset) begin if (!nreset) begin ly_r <= '0; lx_r <= '0; end else begin ly_r <= ly_next; lx_r <= lx_next; end end assign ly_sel = (cpu_addr_i == 16'hFF44); assign ly_next = (ly_sel & cpu_we_i) ? 8'h00 : // Clear on write (lx_end_of_line & ly_end_of_frame) ? 8'h00 : (lx_end_of_line & ~ly_end_of_frame) ? (ly_r + 8'h01) : ly_r; assign lx_next = lx_end_of_line ? 9'h0 : (lx_r + 9'h01); assign ly_end_of_frame = ly_r > 8'd153; assign lx_end_of_line = lx_r > 9'd456; assign cpu_rdata_o = {8{lcdc_sel}} & lcdc_r | `ifdef VSYNC_HACK {8{ ly_sel}} & 8'h90 | `else {8{ ly_sel}} & ly_r | `endif {8{ sy_sel}} & sy_r; `undef IOREG_DEF `undef IOREG_DECL `ifdef SVA_ENABLE logic sva_ppu_sel; assign sva_ppu_sel = lcdc_sel | ly_sel | sy_sel; `endif /* SVA_ENABLE */ endmodule : ppu