More WIP on PPU
This commit is contained in:
parent
3c3dbd2175
commit
82584dab34
@ -1,5 +1,5 @@
|
|||||||
TB = tb_top
|
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
|
INCLUDES = cpu_pkg.svh sva_common.svh
|
||||||
|
|
||||||
DPI_SOURCES = vgasim.c
|
DPI_SOURCES = vgasim.c
|
||||||
|
158
rtl/ppu/ppu.sv
158
rtl/ppu/ppu.sv
@ -11,93 +11,114 @@ module ppu (
|
|||||||
input logic cpu_we_i,
|
input logic cpu_we_i,
|
||||||
input logic [ 7:0] cpu_wdata_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 [ 1:0] pixel_o,
|
||||||
output logic vsync_o,
|
output logic vsync_o,
|
||||||
output logic hsync_o
|
output logic hsync_o
|
||||||
);
|
);
|
||||||
|
|
||||||
`define IOREG_DECL(name) \
|
`define REG_DECL(t, name) \
|
||||||
logic [ 7:0] name``_r; \
|
t name``_r; \
|
||||||
logic [ 7:0] name``_next; \
|
t name``_next; \
|
||||||
logic name``_we; \
|
logic name``_we;
|
||||||
logic name``_sel;
|
|
||||||
|
|
||||||
`define IOREG_DEF(name, addr, rstval) \
|
`define REG_DEF(name, rstval) \
|
||||||
always_ff @(posedge clk or negedge nreset) begin \
|
always_ff @(posedge clk or negedge nreset) begin \
|
||||||
if (!nreset) \
|
if (!nreset) \
|
||||||
name``_r <= rstval; \
|
name``_r <= rstval; \
|
||||||
else if (name``_we) \
|
else if (name``_we) \
|
||||||
name``_r <= name``_next; \
|
name``_r <= name``_next; \
|
||||||
end \
|
end
|
||||||
assign name``_sel = (cpu_addr_i == addr); \
|
|
||||||
assign name``_we = name``_sel & cpu_we_i;
|
|
||||||
|
|
||||||
typedef struct packed {
|
typedef enum {
|
||||||
logic lcd_en;
|
ST_RESET,
|
||||||
logic win_tilemap_sel;
|
ST_OAM_SCAN,
|
||||||
logic win_en;
|
ST_FETCH_TILE_INDEX,
|
||||||
logic tiledata_sel;
|
ST_FETCH_TILE_DATA0,
|
||||||
logic bg_tilemap_sel;
|
ST_FETCH_TILE_DATA1,
|
||||||
logic obj_size;
|
ST_PUSH_PIXELS,
|
||||||
logic obj_display;
|
ST_HBLANK
|
||||||
logic bgwin_display;
|
} state_t;
|
||||||
} lcdc_t;
|
|
||||||
|
|
||||||
lcdc_t lcdc;
|
`REG_DECL(state_t, state);
|
||||||
logic [3:0] [1:0] bgp;
|
`REG_DECL(logic [7:0], oam);
|
||||||
|
|
||||||
// LX is not read/writeable
|
`REG_DECL(logic [7:0], tile);
|
||||||
logic [ 8:0] lx_r;
|
`REG_DECL(logic [7:0], tile_no);
|
||||||
logic [ 8:0] lx_next;
|
|
||||||
logic lx_end_of_line;
|
`REG_DECL(logic [7:0], ly);
|
||||||
|
|
||||||
|
logic ly_sel;
|
||||||
logic ly_end_of_frame;
|
logic ly_end_of_frame;
|
||||||
|
|
||||||
`IOREG_DECL(lcdc);
|
logic [ 8:0] lx_r;
|
||||||
`IOREG_DECL(ly);
|
logic [ 8:0] lx_next;
|
||||||
`IOREG_DECL(sy);
|
logic lx_we;
|
||||||
`IOREG_DECL(bgp);
|
logic lx_end_of_line;
|
||||||
|
|
||||||
`IOREG_DEF (lcdc, 16'hFF40, '0);
|
logic tilemap_base_sel;
|
||||||
`IOREG_DEF ( sy, 16'hFF42, '0);
|
logic [ 7:0] ioregs_rdata;
|
||||||
`IOREG_DEF ( bgp, 16'hFF47, '0);
|
|
||||||
|
|
||||||
assign lcdc_next = cpu_wdata_i;
|
`REG_DEF(state, ST_RESET);
|
||||||
assign sy_next = cpu_wdata_i;
|
assign state_we = 1'b1;
|
||||||
assign bgp_next = cpu_wdata_i;
|
|
||||||
|
|
||||||
assign lcdc = lcdc_r;
|
always_comb begin
|
||||||
assign bgp = bgp_r;
|
state_next = state_r;
|
||||||
|
case (state_r)
|
||||||
|
ST_RESET: state_next = ST_OAM_SCAN;
|
||||||
always_ff @(posedge clk or negedge nreset) begin
|
ST_OAM_SCAN: state_next = oam_we ? ST_OAM_SCAN : ST_FETCH_TILE_INDEX; // 80 cycles per row
|
||||||
if (!nreset) begin
|
ST_FETCH_TILE_INDEX: state_next = ST_FETCH_TILE_DATA0; // 1 cycle x 20 per row
|
||||||
ly_r <= '0;
|
ST_FETCH_TILE_DATA0: state_next = ST_FETCH_TILE_DATA1; // 1 cycle x 20 per row
|
||||||
lx_r <= '0;
|
ST_FETCH_TILE_DATA1: state_next = ST_PUSH_PIXELS; // 1 cycle x 20 per row
|
||||||
end else begin
|
ST_PUSH_PIXELS: state_next = (tile_no_r == 5'd20) ? ST_HBLANK : ST_PUSH_PIXELS; // 8 cycles x 20 per row
|
||||||
ly_r <= ly_next;
|
ST_HBLANK: state_next = lx_end_of_line ? ST_FETCH_TILE_INDEX : ST_HBLANK;
|
||||||
lx_r <= lx_next;
|
endcase
|
||||||
end
|
|
||||||
end
|
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_sel = (cpu_addr_i == 16'hFF44);
|
||||||
assign ly_next = (ly_sel & cpu_we_i) ? 8'h00 : // Clear on write
|
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) ? 8'h00 :
|
||||||
(lx_end_of_line & ~ly_end_of_frame) ? (ly_r + 8'h01) :
|
(lx_end_of_line & ~ly_end_of_frame) ? (ly_r + 8'h01) :
|
||||||
ly_r;
|
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_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 lx_end_of_line = lx_r > 9'd456;
|
||||||
|
|
||||||
assign cpu_rdata_o =
|
|
||||||
{8{lcdc_sel}} & lcdc_r |
|
ppu_ioregs ppu_ioregs_inst (
|
||||||
`ifdef VSYNC_HACK
|
.clk (clk),
|
||||||
{8{ ly_sel}} & 8'h90 |
|
.nreset(nreset),
|
||||||
`else
|
|
||||||
{8{ ly_sel}} & ly_r |
|
.cpu_addr_i (cpu_addr_i),
|
||||||
`endif
|
.cpu_rdata_o(ioregs_rdata),
|
||||||
{8{ sy_sel}} & sy_r;
|
.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
|
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;
|
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 vsync_o = ly_r >= 8'd144;
|
||||||
assign hsync_o = lx_r >= 8'd160;
|
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
|
`ifdef SVA_ENABLE
|
||||||
|
logic sva_sel;
|
||||||
logic sva_ppu_sel;
|
assign sva_sel = ly_sel;
|
||||||
|
|
||||||
assign sva_ppu_sel = lcdc_sel | ly_sel | sy_sel;
|
|
||||||
|
|
||||||
`endif /* SVA_ENABLE */
|
`endif /* SVA_ENABLE */
|
||||||
|
|
||||||
endmodule : ppu
|
endmodule : ppu
|
||||||
|
76
rtl/ppu/ppu_ioregs.sv
Normal file
76
rtl/ppu/ppu_ioregs.sv
Normal file
@ -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
|
@ -138,7 +138,7 @@ assign we = gb_inst.cpu_we;
|
|||||||
|
|
||||||
assign vram_sel = gb_inst.vram_sel;
|
assign vram_sel = gb_inst.vram_sel;
|
||||||
assign hiram_sel = gb_inst.hiram_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
|
always_ff @(posedge gb_clk or negedge nreset) begin
|
||||||
if (!nreset)
|
if (!nreset)
|
||||||
|
@ -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_reset();
|
||||||
import "DPI-C" function int vgasim_tick(input int r, input int g, input int b, input int hsync, input int vsync);
|
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
|
initial begin
|
||||||
automatic int ret = vgasim_init(SCREEN_W, SCREEN_H, DEPTH);
|
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));
|
$display($sformatf("[%0t] vgasim init failed: %d", $time, ret));
|
||||||
$fatal();
|
end else begin
|
||||||
|
vgasim_initialised = '1;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
always @(negedge nreset) begin
|
always @(negedge nreset iff vgasim_initialised) begin
|
||||||
automatic int ret = vgasim_reset();
|
automatic int ret = vgasim_reset();
|
||||||
assert (ret == 0) else begin
|
assert (ret == 0) else begin
|
||||||
$display($sformatf("[%0t] vgasim reset failed: %d", $time, ret));
|
$display($sformatf("[%0t] vgasim reset failed: %d", $time, ret));
|
||||||
@ -33,7 +36,7 @@ always @(negedge nreset) begin
|
|||||||
end
|
end
|
||||||
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);
|
automatic int ret = vgasim_tick(r_i, g_i, b_i, hsync_i, vsync_i);
|
||||||
assert (ret == 0) else begin
|
assert (ret == 0) else begin
|
||||||
$display($sformatf("[%0t] vgasim tick failed: %d", $time, ret));
|
$display($sformatf("[%0t] vgasim tick failed: %d", $time, ret));
|
||||||
|
Loading…
Reference in New Issue
Block a user