svgb/rtl/ppu/ppu.sv

152 lines
3.9 KiB
Systemverilog

`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