From 82584dab34b8fc6927e9b84cbabf2f439147320d Mon Sep 17 00:00:00 2001 From: Koray Yanik Date: Tue, 31 Oct 2023 22:48:14 +0000 Subject: [PATCH] More WIP on PPU --- build/tb_top.Makefile | 2 +- rtl/ppu/ppu.sv | 158 +++++++++++++++++++++++++----------------- rtl/ppu/ppu_ioregs.sv | 76 ++++++++++++++++++++ sim/tb_top.sv | 2 +- sim/vgasim.sv | 11 +-- 5 files changed, 179 insertions(+), 70 deletions(-) create mode 100644 rtl/ppu/ppu_ioregs.sv diff --git a/build/tb_top.Makefile b/build/tb_top.Makefile index a470ac5..99d5430 100644 --- a/build/tb_top.Makefile +++ b/build/tb_top.Makefile @@ -1,5 +1,5 @@ TB = tb_top -SOURCES = gb.sv cpu.sv ppu.sv idec.sv ctrl.sv alu.sv alu16.sv regbank.sv rom.sv ram.sv cart.sv display.sv tb_top.sv vgasim.sv clkgen.sv +SOURCES = gb.sv cpu.sv ppu.sv ppu_ioregs.sv idec.sv ctrl.sv alu.sv alu16.sv regbank.sv rom.sv ram.sv cart.sv display.sv tb_top.sv vgasim.sv clkgen.sv INCLUDES = cpu_pkg.svh sva_common.svh DPI_SOURCES = vgasim.c diff --git a/rtl/ppu/ppu.sv b/rtl/ppu/ppu.sv index 57dcd10..43d1017 100644 --- a/rtl/ppu/ppu.sv +++ b/rtl/ppu/ppu.sv @@ -11,93 +11,114 @@ module ppu ( 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 IOREG_DECL(name) \ - logic [ 7:0] name``_r; \ - logic [ 7:0] name``_next; \ - logic name``_we; \ - logic name``_sel; +`define REG_DECL(t, name) \ + t name``_r; \ + t name``_next; \ + logic name``_we; -`define IOREG_DEF(name, addr, rstval) \ +`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 \ - assign name``_sel = (cpu_addr_i == addr); \ - assign name``_we = name``_sel & cpu_we_i; + end -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; +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; -lcdc_t lcdc; -logic [3:0] [1:0] bgp; +`REG_DECL(state_t, state); +`REG_DECL(logic [7:0], oam); -// LX is not read/writeable -logic [ 8:0] lx_r; -logic [ 8:0] lx_next; -logic lx_end_of_line; +`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; -`IOREG_DECL(lcdc); -`IOREG_DECL(ly); -`IOREG_DECL(sy); -`IOREG_DECL(bgp); +logic [ 8:0] lx_r; +logic [ 8:0] lx_next; +logic lx_we; +logic lx_end_of_line; -`IOREG_DEF (lcdc, 16'hFF40, '0); -`IOREG_DEF ( sy, 16'hFF42, '0); -`IOREG_DEF ( bgp, 16'hFF47, '0); +logic tilemap_base_sel; +logic [ 7:0] ioregs_rdata; -assign lcdc_next = cpu_wdata_i; -assign sy_next = cpu_wdata_i; -assign bgp_next = cpu_wdata_i; +`REG_DEF(state, ST_RESET); +assign state_we = 1'b1; -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 +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 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; + +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; @@ -105,17 +126,26 @@ assign pixel_o = ((lx_r < 8'd160) & (ly_r < 8'd144)) ? ly_r[1:0] : // should cr assign vsync_o = ly_r >= 8'd144; assign hsync_o = lx_r >= 8'd160; -assign display_enable_o = lcdc_r[7]; +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[]) -`undef IOREG_DEF -`undef IOREG_DECL `ifdef SVA_ENABLE - -logic sva_ppu_sel; - -assign sva_ppu_sel = lcdc_sel | ly_sel | sy_sel; - + logic sva_sel; + assign sva_sel = ly_sel; `endif /* SVA_ENABLE */ endmodule : ppu diff --git a/rtl/ppu/ppu_ioregs.sv b/rtl/ppu/ppu_ioregs.sv new file mode 100644 index 0000000..8bc8323 --- /dev/null +++ b/rtl/ppu/ppu_ioregs.sv @@ -0,0 +1,76 @@ +module ppu_ioregs ( + 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, + + output logic display_enable_o, + output logic tilemap_base_sel_o, + output logic [ 8:0] ly_o, + output logic [ 7:0] lx_o +); + +`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``_sel & 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; + +`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; + + +assign cpu_rdata_o = + {8{lcdc_sel}} & lcdc_r | + {8{ sy_sel}} & sy_r; + +assign display_enable_o = lcdc.lcd_en; + +assign tilemap_base_sel_o = lcdc.bg_tilemap_sel; + +`ifdef SVA_ENABLE + logic sva_sel; + assign sva_sel = lcdc_sel | sy_sel; +`endif /* SVA_ENABLE */ + +endmodule : ppu_ioregs diff --git a/sim/tb_top.sv b/sim/tb_top.sv index 3afbd8c..a624aa5 100644 --- a/sim/tb_top.sv +++ b/sim/tb_top.sv @@ -138,7 +138,7 @@ 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.sva_ppu_sel; +assign ppu_sel = gb_inst.ppu_inst.ppu_ioregs_inst.sva_sel; always_ff @(posedge gb_clk or negedge nreset) begin if (!nreset) diff --git a/sim/vgasim.sv b/sim/vgasim.sv index be29bcd..613354b 100644 --- a/sim/vgasim.sv +++ b/sim/vgasim.sv @@ -17,15 +17,18 @@ import "DPI-C" function int vgasim_init(input int screen_h, input int screen_w, import "DPI-C" function int vgasim_reset(); import "DPI-C" function int vgasim_tick(input int r, input int g, input int b, input int hsync, input int vsync); +logic vgasim_initialised = 0; + initial begin automatic int ret = vgasim_init(SCREEN_W, SCREEN_H, DEPTH); - assert (ret == 0) else begin + if (ret != 0) begin $display($sformatf("[%0t] vgasim init failed: %d", $time, ret)); - $fatal(); + end else begin + vgasim_initialised = '1; end end -always @(negedge nreset) begin +always @(negedge nreset iff vgasim_initialised) begin automatic int ret = vgasim_reset(); assert (ret == 0) else begin $display($sformatf("[%0t] vgasim reset failed: %d", $time, ret)); @@ -33,7 +36,7 @@ always @(negedge nreset) begin end end -always @(posedge clk iff nreset) begin +always @(posedge clk iff nreset && vgasim_initialised) begin automatic int ret = vgasim_tick(r_i, g_i, b_i, hsync_i, vsync_i); assert (ret == 0) else begin $display($sformatf("[%0t] vgasim tick failed: %d", $time, ret));