Files
InputTest_MiSTer/rtl/sprite_engine.v
2022-02-06 20:01:02 +00:00

845 lines
27 KiB
Verilog

`timescale 1ps / 1ps
/*============================================================================
Aznable (custom 8-bit computer system) - Comet (sprite engine)
Author: Jim Gregory - https://github.com/JimmyStones/
Version: 0.1
Date: 2021-10-31
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
===========================================================================*/
module sprite_engine #(
parameter SPRITE_RAM_WIDTH = 7,
parameter SPRITE_ROM_WIDTH = 14,
parameter SPRITE_POSITION_WIDTH = 9,
parameter SPRITE_COLRAM_WIDTH = 5,
parameter SPRITE_SIZE_WIDTH = 5 // Width of sprite size related operations
)(
input clk,
input reset,
input pause,
input hsync,
input vsync,
input vblank,
input [SPRITE_POSITION_WIDTH-1:0] hcnt,
input [SPRITE_POSITION_WIDTH-1:0] vcnt,
input [7:0] spriterom_data_out,
input [7:0] spriteram_data_out,
input [15:0] palrom_data_out,
input [15:0] spritelbram_data_out,
input spritecollisionram_data_out,
output reg [SPRITE_RAM_WIDTH-1:0] spriteram_addr,
output reg [SPRITE_COLRAM_WIDTH-1:0] spritecollisionram_addr,
output reg spritecollisionram_data_in,
output reg [SPRITE_ROM_WIDTH-1:0] sprom_addr,
output reg [7:0] palrom_addr,
output [SPRITE_POSITION_WIDTH:0] spritelbram_rd_addr,
output reg [SPRITE_POSITION_WIDTH:0] spritelbram_wr_addr,
output reg spritelbram_wr,
output reg [15:0] spritelbram_data_in,
output reg spritecollisionram_wr,
`ifdef DEBUG_SPRITE_COLLISION
output reg [16:0] spritedebugram_addr_b,
output reg [7:0] spritedebugram_data_in_b,
input [7:0] spritedebugram_data_out_b,
output reg spritedebugram_wr_b,
`endif
output [7:0] spr_r,
output [7:0] spr_g,
output [7:0] spr_b,
output spr_a
);
// State machine constants
localparam SE_INIT = 0;
localparam SE_IDLE = 1;
localparam SE_WAIT = 2;
localparam SE_RESET = 3;
localparam SE_CLEAR_BUFFER = 4;
localparam SE_SETUP_READ_Y = 5;
localparam SE_READ_Y_UPPER = 6;
localparam SE_READ_Y_LOWER = 7;
localparam SE_CHECK_Y = 8;
localparam SE_READ_X_UPPER = 9;
localparam SE_READ_X_LOWER = 10;
localparam SE_SETUP_WRITE = 11;
localparam SE_GET_PIXEL = 12;
localparam SE_STAGE_PIXEL = 13;
localparam SE_WRITE_PIXEL = 14;
localparam SE_LINE_COMPLETE = 15;
localparam SE_SETUP_LOAD_8x8_UPPER = 20;
localparam SE_SETUP_LOAD_8x8_LOWER = 21;
localparam SE_SETUP_LOAD_16x16_UPPER = 22;
localparam SE_SETUP_LOAD_16x16_LOWER = 23;
localparam SE_SETUP_LOAD_32x32_UPPER = 24;
localparam SE_SETUP_LOAD_32x32_LOWER = 25;
// Sprite line buffer has two slots - read and write. They alternate when hsync goes high.
reg spritelb_slot_rd = 1'b0;
reg spritelb_slot_wr = 1'b1;
// Track last hsync value
reg hsync_last;
// Sprite state machine control
reg [4:0] spr_state;
reg [4:0] spr_state_next;
// Sprite index counter and maximum sprite limit
reg [5:0] spr_index;
localparam spr_index_max = 6'd31;
// Sprite maximum sizes
localparam [6:0] spr_ram_item_width = 4;
localparam [SPRITE_POSITION_WIDTH-1:0] spr_line_max = 352;
reg [SPRITE_POSITION_WIDTH-1:0] spr_size; // Size of sprite in pixels (always square)
reg [SPRITE_POSITION_WIDTH-1:0] spr_x; // Sprite X position
reg [SPRITE_POSITION_WIDTH-1:0] spr_y; // Sprite Y position
reg spr_enable; // Sprite visibility enabled
reg spr_collide; // Sprite collision enabled
reg [1:0] spr_palette_index; // Sprite palette index
reg [5:0] spr_image_index; // Sprite image index
localparam [SPRITE_POSITION_WIDTH-1:0] spr_border_size = 32; // Sprite screen border width
reg [SPRITE_POSITION_WIDTH-1:0] spr_active_y; // Current active sprite engine Y
reg [SPRITE_SIZE_WIDTH:0] spr_pixel_index;// Current sprite X pixel
reg [SPRITE_ROM_WIDTH-1:0] spr_rom_offset; // Offset for current sprite size in image ROM
reg [9:0] spr_rom_y_offset; // Offset for current sprite Y line in image ROM
localparam [SPRITE_POSITION_WIDTH-1:0] spr_size_8x8 = {{SPRITE_POSITION_WIDTH-5{1'b0}}, 5'd7};
localparam [SPRITE_POSITION_WIDTH-1:0] spr_size_16x16 = {{SPRITE_POSITION_WIDTH-5{1'b0}}, 5'd15};
localparam [SPRITE_POSITION_WIDTH-1:0] spr_size_32x32 = {{SPRITE_POSITION_WIDTH-5{1'b0}}, 5'd31};
reg [SPRITE_ROM_WIDTH-1:0] spr_rom_offset_8x8;
reg [SPRITE_ROM_WIDTH-1:0] spr_rom_offset_16x16;
reg [SPRITE_ROM_WIDTH-1:0] spr_rom_offset_32x32;
//`define CASVAL_DEBUG
//`define CASVAL_DEBUG_TIMES
//`define CASVAL_DEBUG_OUTLINE
`ifdef CASVAL_DEBUG_TIMES
parameter SPR_TIMER_WIDTH = 11;
reg [SPR_TIMER_WIDTH-1:0] spr_timer_idle;
reg [SPR_TIMER_WIDTH-1:0] spr_linetime_max;
reg [SPR_TIMER_WIDTH-1:0] spr_timer_line;
`endif
// Sprite engine outputs
assign spritelbram_rd_addr = {spritelb_slot_rd, (hcnt + spr_border_size)};
assign spr_r = {spritelbram_data_out[4:0],spritelbram_data_out[4:2]};
assign spr_g = {spritelbram_data_out[9:5],spritelbram_data_out[9:7]};
assign spr_b = {spritelbram_data_out[14:10],spritelbram_data_out[14:12]};
assign spr_a = spritelbram_data_out[15];
// Collision system
localparam CP_IDLE = 0;
localparam CP_WAIT = 1;
localparam CP_STAGE_PIXEL = 2;
localparam CP_CHECK_PIXEL = 3;
localparam CP_WRITE_PIXEL = 4;
localparam CS_IDLE = 0;
localparam CS_WAIT = 1;
localparam CS_DETECT_BEGIN = 2;
localparam CS_DETECT = 3;
localparam CS_DETECT_COMPLETE = 4;
reg [3:0] col_primary_state = CP_IDLE;
reg [3:0] col_primary_state_next;
reg [3:0] col_secondary_state = CS_IDLE;
reg [3:0] col_secondary_state_next;
reg [SPRITE_POSITION_WIDTH-1:0] col_x;
reg [4:0] col_spriteindex;
reg [SPRITE_POSITION_WIDTH-1:0] col_buffer_primary_addr;
reg col_buffer_primary_wr;
reg [31:0] col_buffer_primary_data_in;
wire [31:0] col_buffer_primary_data_out;
reg [SPRITE_POSITION_WIDTH-1:0] col_buffer_secondary_addr;
reg col_buffer_secondary_wr;
reg [31:0] col_buffer_secondary_data_in;
wire [31:0] col_buffer_secondary_data_out;
reg [31:0] col_buffer_secondary_collisions;
wire [4:0] col_buffer_secondary_collisions_count1;
wire [4:0] col_buffer_secondary_collisions_count2;
count count1 (
.clk(clk),
.a(col_buffer_secondary_collisions),
.sum(col_buffer_secondary_collisions_count1)
);
count count2 (
.clk(clk),
.a(col_buffer_secondary_data_out),
.sum(col_buffer_secondary_collisions_count2)
);
reg col_buffer_primary_is_a;
//`define CASVAL_COLLISION_PRIMARY_DEBUG
//`define CASVAL_COLLISION_SECONDARY_DEBUG
reg [15:0] col_secondary_timer;
always @(posedge clk)
begin
if(reset)
begin
spr_state <= SE_IDLE;
end
hsync_last <= hsync;
if(!pause)
begin
// Primary collision state machine - Takes stage pixel instruction from sprite engine state machine
col_secondary_timer <= col_secondary_timer + 16'd1;
case (col_primary_state)
CP_IDLE:
begin
end
CP_WAIT:
begin
col_primary_state <= col_primary_state_next;
end
CP_STAGE_PIXEL:
begin
`ifdef CASVAL_COLLISION_PRIMARY_DEBUG
$display("CP_STAGE_PIXEL: x=%d i=%d", col_x, col_spriteindex);
`endif
// Set primary buffer to read existing pixel
col_buffer_primary_addr <= col_x;
col_buffer_primary_wr <= 1'b0;
//col_primary_state <= CP_CHECK_PIXEL;
col_primary_state_next <= CP_CHECK_PIXEL;
col_primary_state <= CP_WAIT;
end
CP_CHECK_PIXEL:
begin
`ifdef CASVAL_COLLISION_PRIMARY_DEBUG
$display("CP_CHECK_PIXEL: x,y=%d,%d i=%d ra=%x do=%b m=%b di=%", col_x, spr_active_y, col_spriteindex, col_buffer_primary_addr, col_buffer_primary_data_out, (32'b1 << col_spriteindex),col_buffer_primary_data_out | (32'b1 << col_spriteindex));
`endif
col_buffer_primary_data_in <= col_buffer_primary_data_out | (32'b1 << col_spriteindex);
col_buffer_primary_wr <= 1'b1;
col_primary_state <= CP_WRITE_PIXEL;
end
CP_WRITE_PIXEL:
begin
`ifdef CASVAL_COLLISION_PRIMARY_DEBUG
$display("CP_WRITE_PIXEL: x,y=%d,%d i=%d - wa=%x di=%x do=%x", col_x, spr_active_y, col_spriteindex, col_buffer_primary_addr, col_buffer_primary_data_in, col_buffer_primary_data_out);
`endif
col_buffer_primary_wr <= 1'b0;
col_buffer_primary_data_in <= 32'b0;
col_primary_state <= CP_IDLE;
end
endcase
end
// Sprite engine state machine
`ifdef CASVAL_DEBUG_TIMES
spr_timer_line <= spr_timer_line + 1'b1;
`endif
case (spr_state)
SE_INIT:
begin
`ifdef CASVAL_DEBUG_TIMES
// Reset line timer maximum
spr_linetime_max <= {{SPR_TIMER_WIDTH-1{1'b0}},1'b1};
`endif
// Start loading sprite ROM offsets
sprom_addr <= {SPRITE_ROM_WIDTH{1'b0}};
spr_state <= SE_WAIT;
spr_state_next <= SE_SETUP_LOAD_32x32_UPPER;
end
SE_SETUP_LOAD_32x32_UPPER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_SETUP_LOAD_32x32_UPPER: addr=%x dout=%x", sprom_addr, spriterom_data_out);
`endif
// Load upper byte of 32x32 image offset
spr_rom_offset_32x32[SPRITE_ROM_WIDTH-1:8] <= spriterom_data_out[SPRITE_ROM_WIDTH-9:0];
// Increment sprite ROM address
sprom_addr <= sprom_addr + {{SPRITE_ROM_WIDTH-1{1'b0}},1'b1};
// Move to next state
spr_state <= SE_WAIT;
spr_state_next <= SE_SETUP_LOAD_32x32_LOWER;
end
SE_SETUP_LOAD_32x32_LOWER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_SETUP_LOAD_32x32_LOWER: addr=%x dout=%x", sprom_addr, spriterom_data_out);
`endif
// Load lower byte of 32x32 image offset
spr_rom_offset_32x32[7:0] <= spriterom_data_out;
// Increment sprite ROM address
sprom_addr <= sprom_addr + {{SPRITE_ROM_WIDTH-1{1'b0}},1'b1};
// Move to next state
spr_state <= SE_WAIT;
spr_state_next <= SE_SETUP_LOAD_16x16_UPPER;
end
SE_SETUP_LOAD_16x16_UPPER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_SETUP_LOAD_16x16_UPPER: addr=%x dout=%x", sprom_addr, spriterom_data_out);
`endif
// Load upper byte of 16x16 image offset
spr_rom_offset_16x16[SPRITE_ROM_WIDTH-1:8] <= spriterom_data_out[SPRITE_ROM_WIDTH-9:0];
// Increment sprite ROM address
sprom_addr <= sprom_addr + {{SPRITE_ROM_WIDTH-1{1'b0}},1'b1};
// Move to next state
spr_state <= SE_WAIT;
spr_state_next <= SE_SETUP_LOAD_16x16_LOWER;
end
SE_SETUP_LOAD_16x16_LOWER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_SETUP_LOAD_16x16_LOWER: addr=%x dout=%x", sprom_addr, spriterom_data_out);
`endif
// Load lower byte of 16x16 image offset
spr_rom_offset_16x16[7:0] <= spriterom_data_out;
// Increment sprite ROM address
sprom_addr <= sprom_addr + {{SPRITE_ROM_WIDTH-1{1'b0}},1'b1};
// Move to next state
spr_state <= SE_WAIT;
spr_state_next <= SE_SETUP_LOAD_8x8_UPPER;
end
SE_SETUP_LOAD_8x8_UPPER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_SETUP_LOAD_8x8_UPPER: addr=%x dout=%x", sprom_addr, spriterom_data_out);
`endif
// Load upper byte of 8x8 image offset
spr_rom_offset_8x8[SPRITE_ROM_WIDTH-1:8] <= spriterom_data_out[SPRITE_ROM_WIDTH-9:0];
// Increment sprite ROM address
sprom_addr <= sprom_addr + {{SPRITE_ROM_WIDTH-1{1'b0}},1'b1};
// Move to next state
spr_state <= SE_WAIT;
spr_state_next <= SE_SETUP_LOAD_8x8_LOWER;
end
SE_SETUP_LOAD_8x8_LOWER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_SETUP_LOAD_8x8_LOWER: addr=%x dout=%x", sprom_addr, spriterom_data_out);
`endif
// Load lower byte of 8x8 image offset
spr_rom_offset_8x8[7:0] <= spriterom_data_out;
// Increment sprite ROM address
sprom_addr <= sprom_addr + {{SPRITE_ROM_WIDTH-1{1'b0}},1'b1};
// Move to next state
spr_state <= SE_IDLE;
end
SE_IDLE:
begin
// Wait for hsync to go high outside of reset
if(reset == 1'b0 && hsync && !hsync_last)
begin
// Rotate line buffer slots
spritelb_slot_rd <= spritelb_slot_rd + 1'b1;
spritelb_slot_wr <= spritelb_slot_wr + 1'b1;
// Calculate active Y line
spr_active_y <= (vcnt == 9'd255) ? spr_border_size : (vcnt + spr_border_size) + 9'd1;
spr_state <= SE_RESET;
end
`ifdef CASVAL_DEBUG_TIMES
else
begin
spr_timer_idle <= spr_timer_idle + 1'b1;
end
`endif
end
SE_WAIT:
begin
spr_state <= spr_state_next;
end
SE_RESET:
begin
// Reset sprite index
spr_index <= 6'd0;
`ifdef CASVAL_DEBUG_TIMES
$display("CASVAL->LEAVING SE_IDLE: vcnt = %d spr_active_y = %d", vcnt, spr_active_y);
$display("CASVAL->LEAVING SE_IDLE: spr_timer_idle = %d, spr_linetime_max=%d", spr_timer_idle, spr_linetime_max);
spr_timer_line <= {SPR_TIMER_WIDTH{1'b0}};
`endif
// Setup line buffer RAM for clear operation
spritelbram_wr_addr <= {spritelb_slot_wr, {SPRITE_POSITION_WIDTH{1'b0}}};
spritelbram_wr <= 1'b1;
spritelbram_data_in <= 16'b0;
spr_state <= SE_CLEAR_BUFFER;
end
SE_CLEAR_BUFFER:
begin
if(spritelbram_wr_addr[8:0] < spr_line_max[8:0])
begin
spritelbram_wr_addr <= spritelbram_wr_addr + 1'b1;
end
else
begin
// Disable line buffer write
spritelbram_wr <= 1'b0;
spr_state <= SE_SETUP_READ_Y;
end
end
SE_SETUP_READ_Y:
begin
// Setup address to read Y from sprite RAM
spriteram_addr <= spr_index * spr_ram_item_width;
spr_state <= SE_WAIT;
spr_state_next <= SE_READ_Y_UPPER;
end
SE_READ_Y_UPPER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_READ_Y_UPPER: spr: %d addr=%x dout=%x", spr_index, spriteram_addr, spriteram_data_out);
`endif
// Read enable bit from sprite RAM
spr_enable <= spriteram_data_out[7];
// Read collide bit from sprite RAM
spr_collide <= spriteram_data_out[6];
// Read palette index bits from sprite RAM
spr_palette_index <= spriteram_data_out[5:4];
// Read size bits from sprite RAM
spr_size <= (spriteram_data_out[3:2] == 2'd2 ? spr_size_8x8 :
spriteram_data_out[3:2] == 2'd1 ? spr_size_16x16 :
spriteram_data_out[3:2] == 2'd0 ? spr_size_32x32 : {SPRITE_POSITION_WIDTH{1'b0}});
// Read Y upper 1 bit from sprite RAM
spr_y[8] <= spriteram_data_out[0];
// Increment sprite RAM address
spriteram_addr <= spriteram_addr + 1'b1;
spr_state <= SE_WAIT;
spr_state_next <= SE_READ_Y_LOWER;
end
SE_READ_Y_LOWER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_READ_Y_LOWER: spr: %d addr=%x dout=%x", spr_index, spriteram_addr, spriteram_data_out);
`endif
// Read Y lower 8 bits from sprite RAM
spr_y[7:0] <= spriteram_data_out;
// Increment sprite RAM address
spriteram_addr <= spriteram_addr + 1'b1;
spr_state <= SE_CHECK_Y;
end
SE_CHECK_Y:
begin
`ifdef CASVAL_DEBUG
//$display("CASVAL->SE_CHECK_Y: spr_index=%d y: %d", spr_index, spr_y);
`endif
//// If this sprite is enabled and current line is within sprite Y area
if(spr_enable==1'b1 && spr_active_y >= spr_y && spr_active_y <= spr_y + spr_size)
begin
`ifdef CASVAL_DEBUG
$display("SE_CHECK_Y PASSED: spr_index=%d", spr_index);
`endif
spr_state <= SE_READ_X_UPPER;
end
else
begin
// If no then move to next sprite or finish
if(spr_index == spr_index_max)
begin
spr_state <= SE_LINE_COMPLETE;
end
else
begin
// Increment sprite index
spr_index <= spr_index + 1'd1;
spr_state <= SE_SETUP_READ_Y;
end
end
end
SE_READ_X_UPPER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_READ_X_UPPER: addr=%x dout=%x", spriteram_addr, spriteram_data_out);
`endif
// Read image index 6 bits from sprite RAM
spr_image_index <= spriteram_data_out[7:2];
// Read X upper 1 bit from sprite RAM
spr_x[8] <= spriteram_data_out[0];
// Increment sprite RAM address
spriteram_addr <= spriteram_addr + 1'b1;
spr_state <= SE_WAIT;
spr_state_next <= SE_READ_X_LOWER;
end
SE_READ_X_LOWER:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_READ_X_LOWER: addr=%x dout=%x", spriteram_addr, spriteram_data_out);
`endif
// Read X lower 8 bits from sprite RAM
spr_x[7:0] <= spriteram_data_out;
// Set up offset for sprite ROM read
spr_rom_y_offset <= spr_active_y - spr_y;
// Set up ROM offset for sprite size
case(spr_size)
spr_size_32x32: spr_rom_offset <= spr_rom_offset_32x32;
spr_size_16x16: spr_rom_offset <= spr_rom_offset_16x16;
default: spr_rom_offset <= spr_rom_offset_8x8;
endcase
spr_state <= SE_SETUP_WRITE;
end
SE_SETUP_WRITE:
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_SETUP_WRITE: AY: %d Y: %d X: %d I: %d O: %d", spr_active_y, spr_y, spr_x, spr_image_index, spr_rom_y_offset);
`endif
// Enable line buffer write
spritelbram_wr <= 1'b0;
// Setup line buffer write address
spritelbram_wr_addr <= {spritelb_slot_wr, spr_x};
// Set sprite rom read address
case(spr_size)
spr_size_32x32:
begin
sprom_addr <= spr_rom_offset + { spr_image_index[3:0], 10'b0} + { spr_rom_y_offset[7:0], 5'b0};
end
spr_size_16x16:
begin
sprom_addr <= spr_rom_offset + { spr_image_index[5:0], 8'b0} + { spr_rom_y_offset[8:0], 4'b0};
end
default:
begin
// Default to 8x8
sprom_addr <= spr_rom_offset + { 2'b0, spr_image_index[5:0], 6'b0} + { spr_rom_y_offset[9:0], 3'b0};
end
endcase
// Reset sprite pixel index and count
spr_pixel_index <= {SPRITE_SIZE_WIDTH+1{1'b0}};
spr_state <= SE_WAIT;
spr_state_next <= SE_GET_PIXEL;
end
SE_GET_PIXEL:
begin
if(spr_pixel_index > spr_size[SPRITE_SIZE_WIDTH:0])
begin
// Move to next sprite or finish
if(spr_index == spr_index_max)
begin
spr_state <= SE_LINE_COMPLETE;
end
else
begin
spr_index <= spr_index + 5'd1;
spr_state <= SE_SETUP_READ_Y;
end
end
else
begin
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_GET_PIXEL: y: %d, x: %d i: %d, sprom_addr < %x, palrom_addr < %x", spr_y, spr_pixel_index, spr_image_index, sprom_addr, {spriterom_data_out[4:0],1'b0});
`endif
// Setup palette address to read pixel colour
palrom_addr <= {spr_palette_index, spriterom_data_out[4:0],1'b0};
// Increment sprite ROM address
sprom_addr <= sprom_addr + 1'b1;
// Disable line buffer write
spritelbram_wr <= 1'b0;
spr_state <= SE_WAIT;
spr_state_next <= SE_STAGE_PIXEL;
end
end
SE_STAGE_PIXEL:
begin
`ifdef CASVAL_DEBUG_OUTLINE
/* verilator lint_off WIDTH */
if(spr_pixel_index == 0 || spr_pixel_index == spr_size || spr_active_y == spr_y || spr_active_y == (spr_y + spr_size)) begin
spritelbram_data_in <= 16'hFFFF;
spritelbram_wr <= 1'b1;
spr_state <= SE_WRITE_PIXEL;
end
/* verilator lint_on WIDTH */
else
begin
`endif
// Get pixel colour from palette rom
if(palrom_data_out[15])
begin
// If palette colour alpha is high, stage input to line buffer
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_STAGE_PIXEL: ay: %d y: %d, x: %d i: %d, spritelbram_data_in < %x", spr_active_y, spr_y, spr_pixel_index, spr_image_index, palrom_data_out);
`endif
// Enable line buffer write
spritelbram_wr <= 1'b1;
// Fill line buffer data in with palette ROM data out
spritelbram_data_in <= palrom_data_out;
// Trigger collision check (not in vblank)
if(!vblank && spr_collide)
begin
col_spriteindex <= spr_index[4:0];
col_x <= spr_x + {{(SPRITE_POSITION_WIDTH-SPRITE_SIZE_WIDTH)-1{1'b0}}, spr_pixel_index};
col_primary_state <= CP_STAGE_PIXEL;
end
// Move to write pixel state
spr_state <= SE_WRITE_PIXEL;
end
else
begin
// Pixel is transparent so move to next
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_STAGE_PIXEL: y: %d, x: %d i: %d, spritelbram_data_in < %x - FAIL ALPHA CHECK", spr_y,spr_pixel_index, spr_image_index, palrom_data_out);
`endif
// Increment line buffer write address
spritelbram_wr_addr <= spritelbram_wr_addr + 1'b1;
// Increment sprite pixel index
spr_pixel_index <= spr_pixel_index + 1'b1;
spr_state <= SE_GET_PIXEL;
end
end
`ifdef CASVAL_DEBUG_OUTLINE
end
`endif
SE_WRITE_PIXEL:
begin
// Get pixel colour from palette rom and stage input to line buffer
`ifdef CASVAL_DEBUG
$display("CASVAL->SE_WRITE_PIXEL: y: %d, x: %d i: %d, spritelbram_wr_addr < %d, spritelbram_data_in=%b", spr_y, spr_pixel_index, spr_image_index, spritelbram_wr_addr[SPRITE_POSITION_WIDTH-1:0], spritelbram_data_in);
`endif
// Disable line buffer write
spritelbram_wr <= 1'b0;
// Increment line buffer write address
spritelbram_wr_addr <= spritelbram_wr_addr + 1'b1;
// Increment sprite pixel index
spr_pixel_index <= spr_pixel_index + 1'b1;
spr_state <= SE_GET_PIXEL;
end
SE_LINE_COMPLETE:
begin
// Once all sprites have been processed for each pixel return to idle
`ifdef CASVAL_DEBUG_TIMES
$display("CASVAL->SE_LINE_COMPLETE: counter=%d", spr_timer_line);
// Update slowest line counter
if(spr_timer_line > spr_linetime_max)
begin
spr_linetime_max <= spr_timer_line;
end
// Reset idle timer
spr_timer_idle <= {SPR_TIMER_WIDTH{1'b0}};
`endif
spr_state <= SE_IDLE;
end
endcase
if(!pause)
begin
// Collision FSM
// When hsync goes high, rotate collision buffers and start detection pass for previous line
if(hsync && !hsync_last)
begin
col_secondary_state <= CS_DETECT_BEGIN;
col_buffer_primary_is_a <= ~col_buffer_primary_is_a;
end
case (col_secondary_state)
CS_WAIT:
begin
col_secondary_state <= col_secondary_state_next;
end
CS_DETECT_BEGIN:
begin
// Reset address and write states for secondary buffer
col_buffer_secondary_addr <= 9'b0;
col_buffer_secondary_data_in <= 32'b0;
col_buffer_secondary_wr <= 1'b1;
col_buffer_secondary_collisions <= 32'b0;
col_secondary_timer <= 16'b0;
col_secondary_state <= CS_DETECT;
`ifdef CASVAL_COLLISION_SECONDARY_DEBUG
$display("CS_DETECT_BEGIN t=%d hc=d%", col_secondary_timer, hcnt);
`endif
end
CS_DETECT:
begin
`ifdef CASVAL_COLLISION_SECONDARY_DEBUG
$display("CS_DETECT t=%d : x=%d c=%d cc1=%d", col_secondary_timer, col_buffer_secondary_addr, col_buffer_secondary_collisions, col_buffer_secondary_collisions_count1,);
`endif
// Check each pixel for a collision
if(col_buffer_secondary_collisions_count2 > 5'd1)
begin
//$display("CS_DETECT t=%d : x=%d c=%d cc1=%d", col_secondary_timer, col_buffer_secondary_addr, col_buffer_secondary_collisions, col_buffer_secondary_collisions_count1);
col_buffer_secondary_collisions <= col_buffer_secondary_collisions | col_buffer_secondary_data_out;
`ifdef DEBUG_SPRITE_COLLISION
//$display("CS_DETECT_DEBUG_OUT t=%d : a=%x", col_secondary_timer, ((spr_active_y) * 9'd320) + {8'b0, col_buffer_secondary_addr});
spritedebugram_data_in_b <= 8'hFF;
spritedebugram_wr_b <= 1'b1;
spritedebugram_addr_b <= ((spr_active_y) * 9'd320) + {8'b0, col_buffer_secondary_addr};
`endif
end
if(col_buffer_secondary_addr == spr_line_max - 1'b1)
begin
spritecollisionram_wr <= 1'b0;
spritecollisionram_addr <= {SPRITE_COLRAM_WIDTH{1'b0}};
`ifdef CASVAL_COLLISION_SECONDARY_DEBUG
$display(">CS_DETECT_COMPLETE t=%d : y=%d hc=%d vc=%d", col_secondary_timer, spr_active_y, hcnt, vcnt);
`endif
col_secondary_state <= CS_DETECT_COMPLETE;
end
else
begin
col_buffer_secondary_addr <= col_buffer_secondary_addr + 9'b1;
end
end
CS_DETECT_COMPLETE:
begin
// Copy individual bits from col_buffer_secondary_collisions into sprite collision ram
if(spritecollisionram_wr == 1'b0)
begin
`ifdef CASVAL_COLLISION_SECONDARY_DEBUG
$display("CS_DETECT_COMPLETE t=%d : RD b=%b c=%d x=%d col=%d y=%d do=%d di=%d", col_secondary_timer, col_buffer_secondary_collisions, col_buffer_secondary_collisions_count1, spritecollisionram_addr, col_buffer_secondary_collisions[spritecollisionram_addr], spr_active_y, spritecollisionram_data_out, col_buffer_secondary_collisions[spritecollisionram_addr] | spritecollisionram_data_out);
`endif
if(col_buffer_secondary_collisions_count1 > 5'b0)
begin
spritecollisionram_data_in <= col_buffer_secondary_collisions[spritecollisionram_addr] | spritecollisionram_data_out;
end
else
begin
spritecollisionram_data_in <= spritecollisionram_data_out;
end
spritecollisionram_wr <= 1'b1;
col_secondary_state <= CS_WAIT;
col_secondary_state_next <= CS_DETECT_COMPLETE;
end
else
begin
`ifdef CASVAL_COLLISION_SECONDARY_DEBUG
$display("CS_DETECT_COMPLETE WR t=%d : x=%d b=%b c=%d col=%d y=%d do=%d di=%d", col_secondary_timer, spritecollisionram_addr, col_buffer_secondary_collisions, col_buffer_secondary_collisions_count1, col_buffer_secondary_collisions[spritecollisionram_addr], spr_active_y, spritecollisionram_data_out, col_buffer_secondary_collisions[spritecollisionram_addr] | spritecollisionram_data_out);
`endif
if(spritecollisionram_addr == {SPRITE_COLRAM_WIDTH{1'b1}})
begin
`ifdef CASVAL_COLLISION_SECONDARY_DEBUG
$display("CS_DETECT_COMPLETE FINISHED t=%d hc=d%", col_secondary_timer, hcnt);
$display("CS_DETECT_COMPLETE FINISHED t=%d : b=%b bc=%d col=%d y=%d do=%d di=%d", col_secondary_timer, col_buffer_secondary_collisions, col_buffer_secondary_collisions_count1, col_buffer_secondary_collisions[spritecollisionram_addr], spr_active_y, spritecollisionram_data_out, col_buffer_secondary_collisions[spritecollisionram_addr] | spritecollisionram_data_out);
`endif
spritecollisionram_addr <= {SPRITE_COLRAM_WIDTH{1'b0}};
col_secondary_state <= CS_IDLE;
end
else
begin
spritecollisionram_addr <= spritecollisionram_addr + {{SPRITE_COLRAM_WIDTH-1{1'b0}},1'b1};
col_secondary_state <= CS_WAIT;
col_secondary_state_next <= CS_DETECT_COMPLETE;
end
spritecollisionram_wr <= 1'b0;
end
end
endcase
end
end
wire [31:0] col_buffer_data_out_a;
wire [31:0] col_buffer_data_out_b;
assign col_buffer_primary_data_out = col_buffer_primary_is_a ? col_buffer_data_out_a : col_buffer_data_out_b;
assign col_buffer_secondary_data_out = !col_buffer_primary_is_a ? col_buffer_data_out_a : col_buffer_data_out_b;
// Sprite Collision Buffer RAM A
spram #(9,32) spritecollisionbufferram_a
(
.clock(clk),
.address(col_buffer_primary_is_a ? col_buffer_primary_addr : col_buffer_secondary_addr),
.wren(col_buffer_primary_is_a ? col_buffer_primary_wr : col_buffer_secondary_wr),
.data(col_buffer_primary_is_a ? col_buffer_primary_data_in : col_buffer_secondary_data_in),
.q(col_buffer_data_out_a)
);
// Sprite Collision Buffer RAM B
spram #(9,32) spritecollisionbufferram_b
(
.clock(clk),
.address(!col_buffer_primary_is_a ? col_buffer_primary_addr : col_buffer_secondary_addr),
.wren(!col_buffer_primary_is_a ? col_buffer_primary_wr : col_buffer_secondary_wr),
.data(!col_buffer_primary_is_a ? col_buffer_primary_data_in : col_buffer_secondary_data_in),
.q(col_buffer_data_out_b)
);
endmodule
module count
(
input clk,
input [31:0] a,
output reg [4:0] sum
);
reg [5:0] n;
always @(negedge clk) begin
sum = 0;
for (n=0; n<=6'd31; n=n+6'd1) begin
sum = sum + {4'b0,a[n[4:0]]};
end
end
endmodule