OpenCores
URL https://opencores.org/ocsvn/rtf_sprite_controller/rtf_sprite_controller/trunk

Subversion Repositories rtf_sprite_controller

[/] [rtf_sprite_controller/] [trunk/] [rtl/] [verilog/] [rtfSpriteController.v] - Rev 9

Go to most recent revision | Compare with Previous | Blame | View Log

`timescale 1ns / 1ps
// ============================================================================
//        __
//   \\__/ o\    (C) 2005-2015  Robert Finch, Stratford
//    \  __ /    All rights reserved.
//     \/_//     robfinch<remove>@finitron.ca
//       ||
//
//	rtfSpriteController.v
//		sprite / hardware cursor controller
//
// This source file is free software: you can redistribute it and/or modify 
// it under the terms of the GNU Lesser General Public License as published 
// by the Free Software Foundation, either version 3 of the License, or     
// (at your option) any later version.                                      
//                                                                          
// This source file 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/>.    
//
//
//	Sprite Controller
//
//	FEATURES
//	- parameterized number of sprites 1,2,4,6,8,14 or 32
//	- sprite image cache buffers
//		- each image cache is capable of holding multiple
//		  sprite images
//		- cache may be accessed like a memory by the processor
//		- an embedded DMA controller may also be used for
//			sprite reload
//	- programmable image offset within cache
//	- programmable sprite width,height, and pixel size
//		- sprite width and height may vary from 1 to 64 as long
//		  as the product doesn't exceed 4096.
//	    - pixels may be programmed to be 1,2,3 or 4 video clocks
//	      both height and width are programmable
//	- programmable sprite position
//	- 8 or 16 bits for color
//		eg 32k color + 1 bit alpha blending indicator (1,5,5,5)
//	- fixed display and DMA priority
//	    sprite 0 highest, sprite 31 lowest
//
//		This core requires an external timing generator to
//	provide horizontal and vertical sync signals, but
//	otherwise can be used as a display controller on it's
//	own. However, normally this core would be embedded
//	within another core such as a VGA controller. Sprite
//	positions are referenced to the rising edge of the
//	vertical and horizontal sync pulses.
//		The core includes an embedded dual port RAM to hold the
//	sprite images. The image RAM is updated using a built in DMA
//	controller. The DMA controller uses 32 bit accesses to fill
//	the sprite buffers. The circuit features an automatic bus
//  transaction timeout; if the system bus hasn't responded
//  within 20 clock cycles, the DMA controller moves onto the
//  next address.
//		The controller uses a ram underlay to cache the values
//	of the registers. This is a lot cheaper resource wise than
//	using a 32 to 1 multiplexor (well at least for an FPGA).
//
//	All registers are 32 bits wide
//
//	These registers repeat in incrementing block of four registers
//	and pertain to each sprite
//	00:	- position register
//		HPOS    [11: 0]	horizontal position (hctr value)
//	    VPOS	[27:16]	vertical position (vctr value)
//
//	04:	SZ	- size register
//			bits
//			[ 7: 0]	width of sprite in pixels - 1
//			[15: 8]	height of sprite in pixels -1
//			[19:16]	size of horizontal pixels - 1 in clock cycles
//			[23:20]	size of vertical pixels in scan-lines - 1
//				* the product of width * height cannot exceed 2048 !
//				if it does, the display will begin repeating
//				
//	08: ADR	[31:12] 20 bits sprite image address bits
//			This registers contain the high order address bits of the
//          location of the sprite image in system memory.
//			The DMA controller will assign the low order 12 bits
//			during DMA.
//		    [11:0] image offset bits [11:0]
//			offset of the sprite image within the sprite image cache
//			typically zero
//	
//	0C: TC	[15:0]	transparent color
//			This register identifies which color of the sprite
//			is transparent
//
//
//
//	0C-1FC:	registers reserved for up to thirty-one other sprites
//
//	Global status and control
//	3C0: EN [31:0] sprite enable register
//  3C4: IE	[31:0] sprite interrupt enable / status
//	3C8: SCOL	[31:0] sprite-sprite collision register
//	3CC: BCOL	[31:0] sprite-background collision register
//	3D0: DT		[31:0] sprite DMA trigger on
//	3D4: DT		[31:0] sprite DMA trigger off
//	3E8: BTC	[23:0] background transparent color
//	3EC: BC	    [23:0] background color
//  3FC: ADDR	[31:0] sprite DMA address bits [63:32]
//
//
//	2200 LUTs/ 188MHz - xc7a100t (8 sprites)
//	3 8x8 multipliers (for alpha blending)
//	16 block rams
//=============================================================== */
 
module rtfSpriteController(
// Bus Slave interface
//------------------------------
// Slave signals
input rst_i,			// reset
input clk_i,			// clock
input         s_cyc_i,	// cycle valid
input         s_stb_i,	// data transfer
output        s_ack_o,	// transfer acknowledge
input         s_we_i,	// write
input  [ 3:0] s_sel_i,	// byte select
input  [31:0] s_adr_i,	// address
input  [31:0] s_dat_i,	// data input
output reg [31:0] s_dat_o,	// data output
output vol_o,			// volatile register
//------------------------------
// Bus Master Signals
input m_clk_i,				// clock
output [1:0]  m_bte_o,
output [2:0]  m_cti_o,
output reg    m_cyc_o,	// cycle is valid
output        m_stb_o,	// strobe output
input         m_ack_i,	// input data is ready
input         m_err_i,	
output        m_we_o,
output reg [31:0] m_adr_o,	// DMA address
input  [31:0] m_dat_i,	// data input
output [31:0] m_dat_o,
//--------------------------
input vclk,					// video dot clock
input hSync,				// horizontal sync pulse
input vSync,				// vertical sync pulse
input blank,				// blanking signal
input [1:0] rgbPriority,
input [23:0] rgbIn,			// input pixel stream
output reg [23:0] rgbOut,	// output pixel stream
output irq					// interrupt request
);
 
reg m_soc_o;
 
//--------------------------------------------------------------------
// Core Parameters
//--------------------------------------------------------------------
parameter pnSpr = 8;		// number of sprites
parameter phBits = 12;		// number of bits in horizontal timing counter
parameter pvBits = 12;		// number of bits in vertical timing counter
parameter pColorBits = 16;	// number of bits used for color data
localparam pnSprm = pnSpr-1;
 
 
//--------------------------------------------------------------------
// Variable Declarations
//--------------------------------------------------------------------
 
wire [4:0] sprN = s_adr_i[8:4];
 
reg [phBits-1:0] hctr;		// horizontal reference counter (counts dots since hSync)
reg [pvBits-1:0] vctr;		// vertical reference counter (counts scanlines since vSync)
reg sprSprIRQ;
reg sprBkIRQ;
 
reg [15:0] out;			// sprite output
reg outact;				// sprite output is active
wire bkCollision;		// sprite-background collision
reg [23:0] bgTc;		// background transparent color
reg [23:0] bkColor;		// background color
 
 
reg [pnSprm:0] sprWe;	// block ram write enable for image cache update
reg [pnSprm:0] sprRe;	// block ram read enable for image cache update
 
// Global control registers
reg [31:0] sprEn;   	// enable sprite
reg [pnSprm:0] sprCollision;	    // sprite-sprite collision
reg sprSprIe;			// sprite-sprite interrupt enable
reg sprBkIe;            // sprite-background interrupt enable
reg sprSprIRQPending;   // sprite-sprite collision interrupt pending
reg sprBkIRQPending;    // sprite-background collision interrupt pending
reg sprSprIRQPending1;  // sprite-sprite collision interrupt pending
reg sprBkIRQPending1;   // sprite-background collision interrupt pending
reg sprSprIRQ1;			// vclk domain regs
reg sprBkIRQ1;
 
// Sprite control registers
reg [31:0] sprSprCollision;
reg [pnSprm:0] sprSprCollision1;
reg [31:0] sprBkCollision;
reg [pnSprm:0] sprBkCollision1;
reg [pColorBits-1:0] sprTc [pnSprm:0];		// sprite transparent color code
// How big the pixels are:
// 1 to 16 video clocks
reg [3:0] hSprRes [pnSprm:0];		// sprite horizontal resolution
reg [3:0] vSprRes [pnSprm:0];		// sprite vertical resolution
reg [7:0] sprWidth [pnSprm:0];		// number of pixels in X direction
reg [7:0] sprHeight [pnSprm:0];		// number of vertical pixels
 
// display and timing signals
reg [31:0] hSprReset;   // horizontal reset
reg [31:0] vSprReset;   // vertical reset
reg [31:0] hSprDe;		// sprite horizontal display enable
reg [31:0] vSprDe;		// sprite vertical display enable
reg [31:0] sprDe;			// display enable
reg [phBits-1:0] hSprPos [pnSprm:0];	// sprite horizontal position
reg [pvBits-1:0] vSprPos [pnSprm:0];	// sprite vertical position
reg [7:0] hSprCnt [pnSprm:0];	// sprite horizontal display counter
reg [7:0] vSprCnt [pnSprm:0];	// vertical display counter
reg [11:0] sprImageOffs [pnSprm:0];	// offset within sprite memory
reg [11:0] sprAddr [pnSprm:0];	// index into sprite memory
reg [11:0] sprAddrB [pnSprm:0];	// backup address cache for rescan
wire [pColorBits-1:0] sprOut [pnSprm:0];	// sprite image data output
 
// DMA access
reg [31:12] sprSysAddr [pnSprm:0];	// system memory address of sprite image (low bits)
reg [4:0] dmaOwner;			// which sprite has the DMA channel
reg [31:0] sprDt;		// DMA trigger register
reg dmaActive;				// this flag indicates that a block DMA transfer is active
 
integer n;
 
//--------------------------------------------------------------------
// DMA control / bus interfacing
//--------------------------------------------------------------------
wire cs_ram = s_cyc_i && s_stb_i && (s_adr_i[31:16]==16'hFFD8 || s_adr_i[31:16]==16'hFFD9);
wire cs_regs = s_cyc_i && s_stb_i && (s_adr_i[31:12]==20'hFFDAD);
 
reg sprRdy;
always @(posedge clk_i)
	sprRdy = (cs_ram|cs_regs);
 
//assign s_ack_o = cs_regs ? 1'b1 : cs_ram ? (s_we_i ? 1 : sprRamRdy) : 0;
assign s_ack_o = (cs_regs|cs_ram) ? (s_we_i ? 1'b1 : sprRdy) : 1'b0;
assign vol_o = cs_regs & s_adr_i[9:2]>=8'b11110000;
assign irq = sprSprIRQ|sprBkIRQ;
 
//--------------------------------------------------------------------
// DMA control / bus interfacing
//--------------------------------------------------------------------
reg dmaStart;
 
wire sbi_rdy1 = m_ack_i|m_err_i;
 
reg [7:0] cob;	// count of burst cycles
 
assign m_bte_o = 2'b00;
assign m_cti_o = 3'b000;
assign m_stb_o = 1'b1;
assign m_we_o = 1'b0;
assign m_dat_o = 32'h00000;
 
always @(posedge m_clk_i)
if (rst_i) begin
	dmaStart <= 1'b0;
	dmaActive <= 1'b0;
	dmaOwner <= 4'd0;
	wb_m_nack();
	cob <= 8'd0;
end
else begin
	dmaStart <= 1'b0;
	m_soc_o <= 1'b0;
	if (!dmaActive) begin
		cob <= 8'd0;
		dmaStart <= |sprDt;
		dmaActive <= |sprDt;
		dmaOwner  <= 0;
		for (n = pnSprm; n >= 0; n = n - 1)
			if (sprDt[n]) dmaOwner <= n;
	end
	else begin
		if (!m_cyc_o) begin
			m_cyc_o <= 1'b1;
			m_adr_o <= {sprSysAddr[dmaOwner],cob[7:0],4'h0};
			m_soc_o <= 1'b1;
			cob <= cob + 8'd1;
		end
		else if (m_ack_i|m_err_i) begin
			m_soc_o <= 1'b1;
			m_adr_o[3:0] <= m_adr_o[3:0] + 8'd4;
			if (m_adr_o[3:0]==4'hC) begin
				wb_m_nack();
				if (cob==8'd255)
					dmaActive <= 1'b0;
			end
		end
	end
end
 
task wb_m_nack;
begin
	m_soc_o <= 1'b0;
	m_cyc_o <= 1'b0;
end
endtask
 
// generate a write enable strobe for the sprite image memory
always @(dmaOwner, dmaActive, s_adr_i, cs_ram, s_we_i, m_ack_i)
for (n = 0; n < pnSpr; n = n + 1)
	sprWe[n] = (dmaOwner==n && dmaActive && m_ack_i)||(cs_ram && s_we_i && s_adr_i[16:12]==n);
 
always @(cs_ram, s_adr_i)
for (n = 0; n < pnSpr; n = n + 1)
	sprRe[n] = cs_ram && s_adr_i[16:12]==n;
 
//--------------------------------------------------------------------
//--------------------------------------------------------------------
 
wire [31:0] sr_dout [pnSprm:0];
reg [31:0] sr_dout_all;
 
generate
begin : gSrDout
always @(pnSpr)
if (pnSpr==1)
	sr_dout_all <= sr_dout[0];
else if (pnSpr==2)
	sr_dout_all <= sr_dout[0]|sr_dout[1];
else if (pnSpr==4)
	sr_dout_all <= sr_dout[0]|sr_dout[1]|sr_dout[2]|sr_dout[3];
else if (pnSpr==6)
	sr_dout_all <= sr_dout[0]|sr_dout[1]|sr_dout[2]|sr_dout[3]|sr_dout[4]|sr_dout[5];
else if (pnSpr==8)
	sr_dout_all <= sr_dout[0]|sr_dout[1]|sr_dout[2]|sr_dout[3]|sr_dout[4]|sr_dout[5]|sr_dout[6]|sr_dout[7];//|
else if (pnSpr==14)
	sr_dout_all <= sr_dout[0]|sr_dout[1]|sr_dout[2]|sr_dout[3]|sr_dout[4]|sr_dout[5]|sr_dout[6]|sr_dout[7]|
							sr_dout[8]|sr_dout[9]|sr_dout[10]|sr_dout[11]|sr_dout[12]|sr_dout[13];
else if (pnSpr==32)
	sr_dout_all <= sr_dout[0]|sr_dout[1]|sr_dout[2]|sr_dout[3]|sr_dout[4]|sr_dout[5]|sr_dout[6]|sr_dout[7]|
				   sr_dout[8]|sr_dout[9]|sr_dout[10]|sr_dout[11]|sr_dout[12]|sr_dout[13]|sr_dout[14]|sr_dout[15]|
				   sr_dout[16]|sr_dout[17]|sr_dout[18]|sr_dout[19]|sr_dout[20]|sr_dout[21]|sr_dout[22]|sr_dout[23]|
				   sr_dout[24]|sr_dout[25]|sr_dout[26]|sr_dout[27]|sr_dout[28]|sr_dout[29]|sr_dout[30]|sr_dout[31]
				   ;
end
endgenerate
 
// register/sprite memory output mux
always @(posedge clk_i)
	if (cs_ram)
		s_dat_o <= sr_dout_all;
	else if (cs_regs)
		case (s_adr_i[9:2])		// synopsys full_case parallel_case
		8'b11110000:	s_dat_o <= sprEn;
		8'b11110001:	s_dat_o <= {sprBkIRQPending|sprSprIRQPending,5'b0,sprBkIRQPending,sprSprIRQPending,6'b0,sprBkIe,sprSprIe};
		8'b11110010:	s_dat_o <= sprSprCollision;
		8'b11110011:	s_dat_o <= sprBkCollision;
		8'b11110100:	s_dat_o <= sprDt;
		default:	s_dat_o <= 32'd0;
		endcase
	else
		s_dat_o <= 32'd0;
 
 
// vclk -> clk_i
always @(posedge clk_i)
begin
	sprSprIRQ <= sprSprIRQ1;
	sprBkIRQ <= sprBkIRQ1;
	sprSprIRQPending <= sprSprIRQPending1;
	sprBkIRQPending <= sprBkIRQPending1;
	sprSprCollision <= sprSprCollision1;
	sprBkCollision <= sprBkCollision1;
end
 
 
// register updates
// on the clk_i domain
always @(posedge clk_i)
if (rst_i) begin
	sprEn <= {pnSpr{1'b1}};
	sprDt <= 0;
    for (n = 0; n < pnSpr; n = n + 1) begin
		sprSysAddr[n] <= 20'b0001_0000_0000_0100 + n;	//1000_4000
	end
	sprSprIe <= 0;
	sprBkIe  <= 0;
 
    // Set reasonable starting positions on the screen
    // so that the sprites might be visible for testing
    for (n = 0; n < pnSpr; n = n + 1) begin
        hSprPos[n] <= 400 + (n & 15) * 60;
        vSprPos[n] <= 200 + (n > 16 ? 100 : 0);
        sprTc[n] <= 16'h6739;
		sprWidth[n] <= 56;  // 56x36 sprites
		sprHeight[n] <= 36;
		hSprRes[n] <= 0;	// our standard display
		vSprRes[n] <= 0;
		sprImageOffs[n] <= 0;
	end
    hSprPos[0] <= 290;
    vSprPos[0] <= 72;
 
    bgTc <= 24'h00_00_00;
    bkColor <= 24'hFF_FF_60;
end
else begin
	// clear DMA trigger bit once DMA is recognized
	if (dmaStart)
		sprDt[dmaOwner] <= 1'b0;
 
	if (cs_regs & s_we_i) begin
 
		casex (s_adr_i[9:2])
		8'b11110000:	// 3C0
			begin
				if (s_sel_i[0]) sprEn[7:0] <= s_dat_i[7:0];
				if (s_sel_i[1]) sprEn[15:8] <= s_dat_i[15:8];
				if (s_sel_i[2]) sprEn[23:16] <= s_dat_i[23:16];
				if (s_sel_i[3]) sprEn[31:24] <= s_dat_i[31:24];
			end
		8'b11110001:	// 3C4
			if (s_sel_i[0]) begin
				sprSprIe <= s_dat_i[0];
				sprBkIe <= s_dat_i[1];
			end
		// update DMA trigger
		// s_dat_i[7:0] indicates which triggers to set  (1=set,0=ignore)
		// s_dat_i[7:0] indicates which triggers to clear (1=clear,0=ignore)
		8'b11110100:	// 3D0
			begin
				if (s_sel_i[0])	sprDt[7:0] <= sprDt[7:0] | s_dat_i[7:0];
				if (s_sel_i[1]) sprDt[15:8] <= sprDt[15:8] | s_dat_i[15:8];
				if (s_sel_i[2]) sprDt[23:16] <= sprDt[23:16] | s_dat_i[23:16];
				if (s_sel_i[3])	sprDt[31:24] <= sprDt[31:24] | s_dat_i[31:24];
			end
		8'b11110101:	// 3D4
			begin
				if (s_sel_i[0])	sprDt[7:0] <= sprDt[7:0] & ~s_dat_i[7:0];
				if (s_sel_i[1]) sprDt[15:8] <= sprDt[15:8] & ~s_dat_i[15:8];
				if (s_sel_i[2]) sprDt[23:16] <= sprDt[23:16] & ~s_dat_i[23:16];
				if (s_sel_i[3])	sprDt[31:24] <= sprDt[31:24] & ~s_dat_i[31:24];
			end
		8'b11111010:	// 3E8
			begin
				if (s_sel_i[0])	bgTc[7:0] <= s_dat_i[7:0];
				if (s_sel_i[1])	bgTc[15:8] <= s_dat_i[15:8];
				if (s_sel_i[2])	bgTc[23:16] <= s_dat_i[23:16];
			end
		8'b11111011:	// 3EC
			begin
				if (s_sel_i[0]) bkColor[7:0] <= s_dat_i[7:0];
				if (s_sel_i[1]) bkColor[15:8] <= s_dat_i[15:8];
				if (s_sel_i[2]) bkColor[23:16] <= s_dat_i[23:16];
			end
		8'b0xxxxx00:
			 begin
	    		if (s_sel_i[0]) hSprPos[sprN][ 7:0] <= s_dat_i[ 7: 0];
	    		if (s_sel_i[1]) hSprPos[sprN][10:8] <= s_dat_i[10: 8];
	    		if (s_sel_i[2]) vSprPos[sprN][ 7:0] <= s_dat_i[23:16];
	    		if (s_sel_i[3]) vSprPos[sprN][10:8] <= s_dat_i[26:24];
    		end
    	8'b0xxxxx01:
			begin
	    		if (s_sel_i[0]) begin
					sprWidth[sprN] <= s_dat_i[7:0];
	            end
	    		if (s_sel_i[1]) begin
					sprHeight[sprN] <= s_dat_i[15:8];
	            end
				if (s_sel_i[2]) begin
	            	hSprRes[sprN] <= s_dat_i[19:16];
	            	vSprRes[sprN] <= s_dat_i[23:20];
				end
			end
		8'b0xxxxx10:
			begin	// DMA address set on clk_i domain
	            if (s_sel_i[0]) sprImageOffs[sprN][ 7:0] <= s_dat_i[7:0];
	            if (s_sel_i[1]) sprImageOffs[sprN][10:8] <= s_dat_i[11:8];
				if (s_sel_i[1]) sprSysAddr[sprN][15:12] <= s_dat_i[15:12];
				if (s_sel_i[2]) sprSysAddr[sprN][23:16] <= s_dat_i[23:16];
				if (s_sel_i[3]) sprSysAddr[sprN][31:24] <= s_dat_i[31:24];
			end
		8'b0xxxxx11:
			begin
				if (s_sel_i[0]) sprTc[sprN][ 7:0] <= s_dat_i[ 7:0];
				if (pColorBits>8)
					if (s_sel_i[1]) sprTc[sprN][15:8] <= s_dat_i[15:8];
			end
 
		default:	;
		endcase
 
	end
end
 
//-------------------------------------------------------------
// Sprite Image Cache RAM
// This RAM is dual ported with an SoC side and a display
// controller side.
//-------------------------------------------------------------
wire [11:2] sr_adr = m_cyc_o ? m_adr_o[11:2] : s_adr_i[11:2];
wire [31:0] sr_din = m_cyc_o ? m_dat_i[31:0] : s_dat_i[31:0];
wire sr_ce = m_cyc_o ? sbi_rdy1 : cs_ram;
 
// Note: the sprite output can't be zeroed out using the rst input!!!
// We need to know what the output is to determine if it's the 
// transparent color.
genvar g;
generate
	for (g = 0; g < pnSpr; g = g + 1)
	begin : genSpriteRam
		if (pColorBits==8)
			rtfSpriteRam8 sprRam0
			(
				.clka(vclk),
				.adra(sprAddr[g]),
				.doa(sprOut[g]),
				.cea(1'b1),
 
				.clkb(~clk_i),
				.adrb(sr_adr),
				.dib(sr_din),
				.dob(sr_dout[g]),
				.ceb(sr_ce),
				.web(sprWe[g]),
				.rstb(!sprRe[g])
			);
		else if (pColorBits==16)
			rtfSpriteRam16 sprRam0
			(
				.clka(vclk),
				.adra(sprAddr[g][10:0]),
				.doa(sprOut[g]),
				.cea(1'b1),
 
				.clkb(~clk_i),
				.adrb(sr_adr),
				.dib(sr_din),
				.dob(sr_dout[g]),
				.ceb(sr_ce),
				.web(sprWe[g]),
				.rstb(!sprRe[g])
			);
	end
endgenerate
 
 
 
//-------------------------------------------------------------
// Timing counters and addressing
// Sprites are like miniature bitmapped displays, they need
// all the same timing controls.
//-------------------------------------------------------------
 
// Create a timing reference using horizontal and vertical
// sync
wire hSyncEdge, vSyncEdge;
edge_det ed0(.rst(rst_i), .clk(vclk), .ce(1'b1), .i(hSync), .pe(hSyncEdge), .ne(), .ee() );
edge_det ed1(.rst(rst_i), .clk(vclk), .ce(1'b1), .i(vSync), .pe(vSyncEdge), .ne(), .ee() );
 
always @(posedge vclk)
if (rst_i)        	hctr <= 0;
else if (hSyncEdge) hctr <= 0;
else            	hctr <= hctr + 1;
 
always @(posedge vclk)
if (rst_i)        	vctr <= 0;
else if (vSyncEdge) vctr <= 0;
else if (hSyncEdge) vctr <= vctr + 1;
 
// track sprite horizontal reset
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1)
	hSprReset[n] <= hctr==hSprPos[n];
 
// track sprite vertical reset
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1)
	vSprReset[n] <= vctr==vSprPos[n];
 
always @(hSprDe, vSprDe)
for (n = 0; n < pnSpr; n = n + 1)
	sprDe[n] <= hSprDe[n] & vSprDe[n];
 
 
// take care of sprite size scaling
// video clock division
reg [31:0] hSprNextPixel;
reg [31:0] vSprNextPixel;
reg [3:0] hSprPt [31:0];   // horizontal pixel toggle
reg [3:0] vSprPt [31:0];   // vertical pixel toggle
always @(n)
for (n = 0; n < pnSpr; n = n + 1)
    hSprNextPixel[n] = hSprPt[n]==hSprRes[n];
always @(n)
for (n = 0; n < pnSpr; n = n + 1)
    vSprNextPixel[n] = vSprPt[n]==vSprRes[n];
 
// horizontal pixel toggle counter
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1)
	if (hSprReset[n])
		hSprPt[n] <= 0;
    else if (hSprNextPixel[n])
        hSprPt[n] <= 0;
    else
        hSprPt[n] <= hSprPt[n] + 1;
 
// vertical pixel toggle counter
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1)
    if (hSprReset[n]) begin
    	if (vSprReset[n])
    		vSprPt[n] <= 0;
        else if (vSprNextPixel[n])
            vSprPt[n] <= 0;
        else
            vSprPt[n] <= vSprPt[n] + 1;
    end
 
 
// clock sprite image address counters
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1) begin
    // hReset and vReset - top left of sprite,
    // reset address to image offset
	if (hSprReset[n] & vSprReset[n]) begin
		sprAddr[n]  <= sprImageOffs[n];
		sprAddrB[n] <= sprImageOffs[n];
	end
	// hReset:
	//  If the next vertical pixel
	//      set backup address to current address
	//  else
	//      set current address to backup address
	//      in order to rescan the line
	else if (hSprReset[n]) begin
		if (vSprNextPixel[n])
			sprAddrB[n] <= sprAddr[n];
		else
			sprAddr[n]  <= sprAddrB[n];
	end
	// Not hReset or vReset - somewhere on the sprite scan line
	// just advance the address when the next pixel should be
	// fetched
	else if (sprDe[n] & hSprNextPixel[n])
		sprAddr[n] <= sprAddr[n] + 1;
end
 
 
// clock sprite column (X) counter
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1)
	if (hSprReset[n])
		hSprCnt[n] <= 0;
	else if (hSprNextPixel[n])
		hSprCnt[n] <= hSprCnt[n] + 1;
 
 
// clock sprite horizontal display enable
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1) begin
	if (hSprReset[n])
		hSprDe[n] <= 1;
	else if (hSprNextPixel[n]) begin
		if (hSprCnt[n] == sprWidth[n])
			hSprDe[n] <= 0;
	end
end
 
 
// clock the sprite row (Y) counter
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1)
	if (hSprReset[n]) begin
		if (vSprReset[n])
			vSprCnt[n] <= 0;
		else if (vSprNextPixel[n])
			vSprCnt[n] <= vSprCnt[n] + 1;
	end
 
 
// clock sprite vertical display enable
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1) begin
	if (hSprReset[n]) begin
		if (vSprReset[n])
			vSprDe[n] <= 1;
		else if (vSprNextPixel[n]) begin
			if (vSprCnt[n] == sprHeight[n])
				vSprDe[n] <= 0;
		end
	end
end
 
 
//-------------------------------------------------------------
// Output stage
//-------------------------------------------------------------
 
// function used for color blending
// given an alpha and a color component, determine the resulting color
// this blends towards black or white
// alpha is eight bits ranging between 0 and 1.999...
// 1 bit whole, 7 bits fraction
function [7:0] fnBlend;
input [7:0] alpha;
input [7:0] colorbits;
 
begin
	fnBlend = (({8'b0,colorbits} * alpha) >> 7);
end
endfunction
 
 
// pipeline delays for display enable
reg [31:0] sprDe1;
reg [31:0] sproact;
always @(posedge vclk)
for (n = 0; n < pnSpr; n = n + 1) begin
	sprDe1[n] <= sprDe[n];
end
 
 
// Detect which sprite outputs are active
// The sprite output is active if the current display pixel
// address is within the sprite's area, the sprite is enabled,
// and it's not a transparent pixel that's being displayed.
always @(n, sprEn, sprDe1)
for (n = 0; n < pnSpr; n = n + 1)
	sproact[n] <= sprEn[n] && sprDe1[n] && sprTc[n]!=sprOut[n];
 
// register sprite activity flag
// The image combiner uses this flag to know what to do with
// the sprite output.
always @(posedge vclk)
outact = |sproact;
 
// Display data comes from the active sprite with the
// highest display priority.
// Make sure that alpha blending is turned off when
// no sprite is active.
always @(posedge vclk)
begin
	out = 16'h0080;	// alpha blend max (and off)
	for (n = pnSprm; n >= 0; n = n - 1)
		if (sproact[n]) out = sprOut[n];
end
 
 
// combine the text / graphics color output with sprite color output
// blend color output
wire [23:0] blendedColor = {
 	fnBlend(out[7:0],rgbIn[23:16]),		// R
 	fnBlend(out[7:0],rgbIn[15: 8]),		// G
 	fnBlend(out[7:0],rgbIn[ 7: 0])};	// B
 
 
// display color priority bit [24] 1=display is over sprite
always @(posedge vclk)
if (blank)
	rgbOut <= 0;
else begin
	if (rgbPriority==2'b10 && rgbIn[23:0] != bgTc)	// color is in front of sprite
		rgbOut <= rgbIn[23:0];
	else if (outact) begin
		if (!out[15]) begin			// a sprite is displayed without alpha blending
			if (pColorBits==8)
				rgbOut <= {out[7:5],5'b0,out[4:2],5'b0,out[1:0],6'b0};
			else
				rgbOut <= {out[14:10],3'b0,out[9:5],3'b0,out[4:0],3'b0};
		end
		else
			rgbOut <= blendedColor;
	end else
		rgbOut <= rgbIn[23:0];
end
 
 
//--------------------------------------------------------------------
// Collision logic
//--------------------------------------------------------------------
 
// Detect when a sprite-sprite collision has occurred. The criteria
// for this is that a pixel from the sprite is being displayed, while
// there is a pixel from another sprite that could be displayed at the
// same time.
 
//--------------------------------------------------------------------
// Note this case has to be modified for the number of sprites
//--------------------------------------------------------------------
generate
begin : gSprsColliding
always @(pnSpr or sproact)
if (pnSpr==1)
	sprCollision = 0;
else if (pnSpr==2)
	case (sproact)
	2'b00,
	2'b01,
	2'b10:	sprCollision = 0;
	2'b11:	sprCollision = 1;
	endcase
else if (pnSpr==4)
	case (sproact)
	4'b0000,
	4'b0001,
	4'b0010,
	4'b0100,
	4'b1000:	sprCollision = 0;
	default:	sprCollision = 1;
	endcase
else if (pnSpr==6)
	case (sproact)
	6'b000000,
	6'b000001,
	6'b000010,
	6'b000100,
	6'b001000,
	6'b010000,
	8'b100000:	sprCollision = 0;
	default:	sprCollision = 1;
	endcase
else if (pnSpr==8)
	case (sproact)
	8'b00000000,
	8'b00000001,
	8'b00000010,
	8'b00000100,
	8'b00001000,
	8'b00010000,
	8'b00100000,
	8'b01000000,
	8'b10000000:	sprCollision = 0;
	default:		sprCollision = 1;
	endcase
else if (pnSpr==14)
	case (sproact)
	14'b00000000000000,
	14'b00000000000001,
	14'b00000000000010,
	14'b00000000000100,
	14'b00000000001000,
	14'b00000000010000,
	14'b00000000100000,
	14'b00000001000000,
	14'b00000010000000,
	14'b00000100000000,
	14'b00001000000000,
	14'b00010000000000,
	14'b00100000000000,
	14'b01000000000000,
	14'b10000000000000:	sprCollision = 0;
	default:			sprCollision = 1;
	endcase
else if (pnSpr==32)
	case (sproact)
	32'h00000000,
	32'h00000001,
	32'h00000002,
	32'h00000004,
	32'h00000008,
	32'h00000010,
	32'h00000020,
	32'h00000040,
	32'h00000080,
	32'h00000100,
	32'h00000200,
	32'h00000400,
	32'h00000800,
	32'h00001000,
	32'h00002000,
	32'h00004000,
	32'h00008000,
	32'h00010000,
	32'h00020000,
	32'h00040000,
	32'h00080000,
	32'h00100000,
	32'h00200000,
	32'h00400000,
	32'h00800000,
	32'h01000000,
	32'h02000000,
	32'h04000000,
	32'h08000000,
	32'h10000000,
	32'h20000000,
	32'h40000000,
	32'h80000000:		sprCollision = 0;
	default:			sprCollision = 1;
	endcase
end
endgenerate
 
// Detect when a sprite-background collision has occurred
assign bkCollision = //(rgbIn[24] && rgbIn[23:0] != bgTc) ? 0 :
		outact && rgbPriority==2'b01;//rgbIn[23:0] != bkColor;
 
// Load the sprite collision register. This register continually
// accumulates collision bits until reset by reading the register.
// Set the collision IRQ on the first collision and don't set it
// again until after the collision register has been read.
always @(posedge vclk)
if (rst_i) begin
	sprSprIRQPending1 <= 0;
	sprSprCollision1 <= 0;
	sprSprIRQ1 <= 0;
end
else if (sprCollision) begin
	// isFirstCollision
	if ((sprSprCollision1==0)||(cs_regs && s_sel_i[0] && s_adr_i[9:2]==8'b11110010)) begin
		sprSprIRQPending1 <= 1;
		sprSprIRQ1 <= sprSprIe;
		sprSprCollision1 <= sproact;
	end
	else
		sprSprCollision1 <= sprSprCollision1|sproact;
end
else if (cs_regs && s_sel_i[0] && s_adr_i[9:2]==8'b11110010) begin
	sprSprCollision1 <= 0;
	sprSprIRQPending1 <= 0;
	sprSprIRQ1 <= 0;
end
 
 
// Load the sprite background collision register. This register
// continually accumulates collision bits until reset by reading
// the register.
// Set the collision IRQ on the first collision and don't set it
// again until after the collision register has been read.
// Note the background collision indicator is externally supplied,
// it will come from the color processing logic.
always @(posedge vclk)
if (rst_i) begin
	sprBkIRQPending1 <= 0;
	sprBkCollision1 <= 0;
	sprBkIRQ1 <= 0;
end
else if (bkCollision) begin
	// Is the register being cleared at the same time
	// a collision occurss ?
	// isFirstCollision
	if ((sprBkCollision1==0) || (cs_regs && s_sel_i[0] && s_adr_i[9:2]==8'b11110011)) begin	
		sprBkIRQ1 <= sprBkIe;
		sprBkCollision1 <= sproact;
		sprBkIRQPending1 <= 1;
	end
	else
		sprBkCollision1 <= sprBkCollision1|sproact;
end
else if (cs_regs && s_sel_i[0] && s_adr_i[9:2]==8'b11110011) begin
	sprBkCollision1 <= 0;
	sprBkIRQPending1 <= 0;
	sprBkIRQ1 <= 0;
end
 
endmodule
 
// Sprite RAM for eight bit color depth
module rtfSpriteRam8 (
	clka, adra, doa, cea,
	clkb, adrb, dib, dob, ceb, web, rstb
);
input clka;
input [11:0] adra;
output [7:0] doa;
reg [7:0] doa;
input cea;
input clkb;
input [9:0] adrb;
input [31:0] dib;
output [31:0] dob;
input ceb;
input web;
input rstb;
 
reg [31:0] mem [0:1023];
reg [11:0] radra;
reg [9:0] radrb;
 
always @(posedge clka)	if (cea) radra <= adra;
always @(posedge clkb) 	if (ceb) radrb <= adrb;
always @(radra)
	case(radra[1:0])
	2'b00:	doa <= mem[radra[11:2]][ 7: 0];
	2'b01:	doa <= mem[radra[11:2]][15: 8];
	2'b10:	doa <= mem[radra[11:2]][23:16];
	2'b11:	doa <= mem[radra[11:2]][31:24];
	endcase
assign dob = rstb ? 32'd0 : mem [radrb];
always @(posedge clkb)
	if (ceb & web) mem[adrb] <= dib;
 
endmodule
 
// Sprite RAM for sixteen bit color depth
module rtfSpriteRam16 (
	clka, adra, doa, cea,
	clkb, adrb, dib, dob, ceb, web, rstb
);
input clka;
input [10:0] adra;
output [15:0] doa;
reg [15:0] doa;
input cea;
input clkb;
input [9:0] adrb;
input [31:0] dib;
output [31:0] dob;
input ceb;
input web;
input rstb;
 
reg [31:0] mem [0:1023];
reg [10:0] radra;
reg [9:0] radrb;
 
always @(posedge clka)	if (cea) radra <= adra;
always @(posedge clkb) 	if (ceb) radrb <= adrb;
always @(radra)
	case(radra[1])
	1'b0:	doa <= mem[radra[10:1]][15: 0];
	1'b1:	doa <= mem[radra[10:1]][31:16];
	endcase
assign dob = rstb ? 32'd0 : mem [radrb];
always @(posedge clkb)
	if (ceb & web) mem[adrb] <= dib;
 
endmodule
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.