`define VSYNC_HACK module ppu ( input logic clk, input logic nreset, output logic display_enable_o, 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, output logic [15:0] ppu_addr_o, input logic [ 7:0] ppu_rdata_i, output logic [ 1:0] pixel_o, output logic vsync_o, output logic hsync_o ); `define REG_DECL(t, name) \ t name``_r; \ t name``_next; \ logic name``_we; `define REG_DEF(name, rstval) \ always_ff @(posedge clk or negedge nreset) begin \ if (!nreset) \ name``_r <= rstval; \ else if (name``_we) \ name``_r <= name``_next; \ end typedef enum { ST_RESET, ST_OAM_SCAN, ST_FETCH_TILE_INDEX, ST_FETCH_TILE_DATA0, ST_FETCH_TILE_DATA1, ST_PUSH_PIXELS, ST_HBLANK } state_t; `REG_DECL(state_t, state); `REG_DECL(logic [7:0], oam); `REG_DECL(logic [7:0], tile); `REG_DECL(logic [7:0], tile_no); `REG_DECL(logic [7:0], ly); logic ly_sel; logic ly_end_of_frame; logic [ 8:0] lx_r; logic [ 8:0] lx_next; logic lx_we; logic lx_end_of_line; logic tilemap_base_sel; logic [ 7:0] ioregs_rdata; `REG_DEF(state, ST_RESET); assign state_we = 1'b1; always_comb begin state_next = state_r; case (state_r) ST_RESET: state_next = ST_OAM_SCAN; ST_OAM_SCAN: state_next = oam_we ? ST_OAM_SCAN : ST_FETCH_TILE_INDEX; // 80 cycles per row ST_FETCH_TILE_INDEX: state_next = ST_FETCH_TILE_DATA0; // 1 cycle x 20 per row ST_FETCH_TILE_DATA0: state_next = ST_FETCH_TILE_DATA1; // 1 cycle x 20 per row ST_FETCH_TILE_DATA1: state_next = ST_PUSH_PIXELS; // 1 cycle x 20 per row ST_PUSH_PIXELS: state_next = (tile_no_r == 5'd20) ? ST_HBLANK : ST_PUSH_PIXELS; // 8 cycles x 20 per row ST_HBLANK: state_next = lx_end_of_line ? ST_FETCH_TILE_INDEX : ST_HBLANK; endcase end `REG_DEF(tile, '0); assign tile_we = (state_r == ST_FETCH_TILE_INDEX); assign tile_next = ppu_rdata_i; `REG_DEF(tile_no, '0); assign tile_no_we = (state_next == ST_FETCH_TILE_INDEX); assign tile_no_next = (state_r == ST_OAM_SCAN) ? '0 : (tile_no_r + 8'h01); `REG_DEF(oam, '0) assign oam_we = (state_r == ST_OAM_SCAN) & (oam_r < 7'd80); assign oam_next = (oam_r + 7'h01); `REG_DEF(ly, '0) 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 ly_end_of_frame = ly_r > 8'd153; `REG_DEF(lx, '0) assign lx_we = 1'b1; assign lx_next = lx_end_of_line ? 9'h0 : (lx_r + 9'h01); assign lx_end_of_line = lx_r > 9'd456; ppu_ioregs ppu_ioregs_inst ( .clk (clk), .nreset(nreset), .cpu_addr_i (cpu_addr_i), .cpu_rdata_o(ioregs_rdata), .cpu_we_i (cpu_we_i), .cpu_wdata_i(cpu_wdata_i), .display_enable_o(display_enable_o), .tilemap_base_sel_o(tilemap_base_sel) ); assign pixel_o = ((lx_r < 8'd160) & (ly_r < 8'd144)) ? ly_r[1:0] : // should create a nice line-pattern for now 2'h00; assign vsync_o = ly_r >= 8'd144; assign hsync_o = lx_r >= 8'd160; assign cpu_rdata_o = ioregs_rdata | `ifdef VSYNC_HACK {8{ ly_sel}} & 8'h90; `else {8{ ly_sel}} & ly_r; `endif logic [7:0] tilemap_base_addr; assign tilemap_base_addr = tilemap_base_sel ? 8'h9C : 8'h98; assign ppu_addr_o = {tilemap_base_addr, tile_no_r};//(state_next == ST_FETCH_TILE_INDEX) ? {tilemap_base_addr, tile_no} : //(state_next == ST_PUSH_PIXELS & lx_r[]) `ifdef SVA_ENABLE logic sva_sel; assign sva_sel = ly_sel; `endif /* SVA_ENABLE */ endmodule : ppu