Fix PPU VRAM access timing when OAM is enabled
diff --git a/verilog/rtl/ppu.v b/verilog/rtl/ppu.v
index 01ab72e..662fbb1 100644
--- a/verilog/rtl/ppu.v
+++ b/verilog/rtl/ppu.v
@@ -287,7 +287,8 @@
localparam S_OFRD0B = 5'd16; // Object Fetch Read Data 0 Stage B
localparam S_OFRD1A = 5'd17; // Object Fetch Read Data 1 Stage A
localparam S_OFRD1B = 5'd18; // Object Fetch Read Data 1 Stage B
- localparam S_OWB = 5'd19; // Object Write Back
+ localparam S_OWAIT = 5'd19; // Object Wait
+ localparam S_OWB = 5'd20; // Object Write Back
localparam PPU_OAM_SEARCH_LENGTH = 6'd40;
@@ -434,8 +435,8 @@
assign vram_addr_int_sel =
((r_state == S_OAMRDB) || (r_state == S_OFRD0A) || (r_state == S_OFRD0B)
|| (r_state == S_OFRD1A) || (r_state == S_OFRD1B)) ? 1'b1 : 1'b0;
- assign vram_rd = (r_state == S_FTIDB) || (r_state == S_FRD0B) ||
- (r_state == S_FRD1B) || (r_state == S_OFRD0B) || (r_state == S_OFRD1B);
+ assign vram_rd = (r_state == S_FTIDA) || (r_state == S_FRD0A) ||
+ (r_state == S_FRD1A) || (r_state == S_OFRD0A) || (r_state == S_OFRD1A);
// Current mode logic, based on current state
always @ (posedge clk)
@@ -464,6 +465,7 @@
S_OFRD0B: reg_stat[1:0] <= PPU_MODE_PIX_TRANS;
S_OFRD1A: reg_stat[1:0] <= PPU_MODE_PIX_TRANS;
S_OFRD1B: reg_stat[1:0] <= PPU_MODE_PIX_TRANS;
+ S_OWAIT: reg_stat[1:0] <= PPU_MODE_PIX_TRANS;
S_OWB: reg_stat[1:0] <= PPU_MODE_PIX_TRANS;
default: reg_stat[1:0] <= PPU_MODE_V_BLANK;
endcase
@@ -549,6 +551,7 @@
else
current_obj_tile_data_1 <= vram_data_out;
// nothing to do for S_OWB
+ S_OWAIT: begin end
S_OWB: begin end
default: begin
$display("Invalid state!");
@@ -655,6 +658,13 @@
end
end
+ wire ram_ready = ((ct == 2'b00) || (ct == 2'b10));
+ wire ppu_ram_stall = !ram_ready && vram_rd;
+ wire need_prewait = (r_state == S_FTIDA) || (r_state == S_FRD0A) ||
+ (r_state == S_FRD1A) || (r_state == S_OFRD0A) || (r_state == S_OFRD1A) ||
+ (r_state == S_FWAITA);
+ reg postwait;
+
// Enter Next State
// and handle object interrupt
// (sorry but I need to backup next state so I could not handle these in the next state logic)
@@ -671,25 +681,37 @@
if (obj_trigger && (reg_mode == PPU_MODE_PIX_TRANS)) begin
// If already in object rendering stages
if ((r_state == S_OFRD0A)||(r_state == S_OFRD0B)||
- (r_state == S_OFRD1A)||(r_state == S_OFRD1B)||
+ (r_state == S_OFRD1A)||(r_state == S_OFRD1B)||(r_state == S_OWAIT)||
(r_state == S_OAMRDA)||(r_state == S_OAMRDB)) begin
r_state <= r_next_state;
end
// Finished one object, but there is more
else if (r_state == S_OWB) begin
- r_state <= S_OAMRDA;
+ if (postwait)
+ r_state <= S_OAMRDA;
+ else
+ r_state <= S_OWAIT;
obj_trigger_id <= obj_trigger_id_next;
end
// Not rendering object before, start now
else begin
+ if (need_prewait) begin
+ r_state <= S_OWAIT;
+ postwait <= 1'b0;
+ end
+ else begin
+ r_state <= S_OAMRDA;
+ postwait <= 1'b1;
+ end
r_next_backup <= r_next_state;
- r_state <= S_OAMRDA;
+
obj_trigger_id <= obj_trigger_id_next;
end
end
else begin
//h_pix_obj <= h_pix_output + 8'd2;
- r_state <= r_next_state;
+ //if (!ppu_ram_stall)
+ r_state <= r_next_state;
// Finished one object, and there is no more currently
if (r_state == S_OWB) begin
obj_trigger_id <= OBJ_TRIGGER_NOT_FOUND;
@@ -698,8 +720,6 @@
end
end
- wire ram_ready = ((ct == 2'b00) || (ct == 2'b10));
-
// Next State Logic
// Since new state get updated during posedge
always @(*)
@@ -720,26 +740,27 @@
) : (S_IDLE);
S_OAMX: r_next_state = (reg_lcd_en) ? (S_OAMY) : (S_IDLE);
S_OAMY: r_next_state = (reg_lcd_en) ? ((oam_search_count == (PPU_OAM_SEARCH_LENGTH - 1'b1)) ? (S_FTIDA) : (S_OAMX)) : (S_IDLE);
- S_FTIDA: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (ram_ready ? S_FTIDB : S_FTIDA))) : (S_IDLE);
+ S_FTIDA: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (S_FTIDB))) : (S_IDLE);
S_FTIDB: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (S_FRD0A))) : (S_IDLE);
- S_FRD0A: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (ram_ready ? S_FRD0B : S_FRD0A))) : (S_IDLE);
+ S_FRD0A: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (S_FRD0B))) : (S_IDLE);
S_FRD0B: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (S_FRD1A))) : (S_IDLE);
- S_FRD1A: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (ram_ready ? S_FRD1B : S_FRD1A))) : (S_IDLE);
+ S_FRD1A: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (S_FRD1B))) : (S_IDLE);
S_FRD1B: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : ((pf_empty != PF_FULL) ? (S_FTIDA) : (S_FWAITA)))) : (S_IDLE); // If fifo not full, no wait state is needed
S_FWAITA: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (S_FWAITB))) : (S_IDLE);
S_FWAITB: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : ((window_trigger) ? (S_SWW) : (S_FTIDA))) : (S_IDLE);
S_SWW: r_next_state = (reg_lcd_en) ? ((h_pix_output == (PPU_H_OUTPUT - 1'b1)) ? (S_BLANK) : (S_FTIDA)) : (S_IDLE);
S_OAMRDA: r_next_state = (reg_lcd_en) ? (S_OAMRDB) : (S_IDLE);
S_OAMRDB: r_next_state = (reg_lcd_en) ? (S_OFRD0A) : (S_IDLE);
- S_OFRD0A: r_next_state = (reg_lcd_en) ? (ram_ready ? S_OFRD0B : S_OFRD0A) : (S_IDLE);
+ S_OFRD0A: r_next_state = (reg_lcd_en) ? (S_OFRD0B) : (S_IDLE);
S_OFRD0B: r_next_state = (reg_lcd_en) ? (S_OFRD1A) : (S_IDLE);
- S_OFRD1A: r_next_state = (reg_lcd_en) ? (ram_ready ? S_OFRD1B : S_OFRD1A) : (S_IDLE);
- S_OFRD1B: r_next_state = (reg_lcd_en) ? (S_OWB) : (S_IDLE);
+ S_OFRD1A: r_next_state = (reg_lcd_en) ? (S_OFRD1B) : (S_IDLE);
+ S_OFRD1B: r_next_state = (reg_lcd_en) ? (postwait ? S_OWAIT : S_OWB) : (S_IDLE);
+ S_OWAIT: r_next_state = (reg_lcd_en) ? (postwait ? S_OWB : S_OAMRDA) : (S_IDLE);
S_OWB: r_next_state = (reg_lcd_en) ? (r_next_backup) : (S_IDLE);
default: r_next_state = S_IDLE;
endcase
end
-
+
// Interrupt
always @(posedge clk)
if (rst)