/* =============================================================== (C) 2005,2006 Robert Finch All rights reserved. rob@birdcomputer.ca cursorController.v hardware cursor controller This source code is free for use and modification for non-commercial or evaluation purposes, provided this copyright statement and disclaimer remains present in the file. If you do modify the code, please state the origin and note that you have modified the code. NO WARRANTY. THIS Work, IS PROVIDED "AS IS" WITH NO WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED. The user must assume the entire risk of using the Work. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY INCIDENTAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES WHATSOEVER RELATING TO THE USE OF THIS WORK, OR YOUR RELATIONSHIP WITH THE AUTHOR. IN ADDITION, IN NO EVENT DOES THE AUTHOR AUTHORIZE YOU TO USE THE WORK IN APPLICATIONS OR SYSTEMS WHERE THE WORK'S FAILURE TO PERFORM CAN REASONABLY BE EXPECTED TO RESULT IN A SIGNIFICANT PHYSICAL INJURY, OR IN LOSS OF LIFE. ANY SUCH USE BY YOU IS ENTIRELY AT YOUR OWN RISK, AND YOU AGREE TO HOLD THE AUTHOR AND CONTRIBUTORS HARMLESS FROM ANY CLAIMS OR LOSSES RELATING TO SUCH UNAUTHORIZED USE. Video Cursor Controller FEATURES - parameterized number of cursors, up to eight cursors - each cursor has an image cache buffer to eliminate the need to scan from memory while displaying the image - each image cache is capable of holding multiple cursor images - the image cache may be accessed like a normal memory by the processor - an embedded DMA controller may also be used for automatic cache reload - programmable image offset within cache - programmable image width, height, and pixel size - image width and height may vary from 1 to 64 as long as the product doesn't exceed the size of the cache (1024). - pixels may be programmed to be 1,2,3 or 4 video clocks in size; both height and width are programmable - programmable cursor position - 15 bits for color (r,g,b)=(0,5,5,5) eg 32k color OR - 1 bit alpha blending indicator + 8 bit alpha value (1,x,x,8) alpha blend towards black or white alpha value has 1 bit whole, 7 bits fraction - fixed display and DMA priority cursor 0 highest, cursor 7 lowest - may be easily retrofit into an existing video system 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, this core may also be embedded within another core such as a VGA controller. Cursor 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 cursor images. The image RAM may be updated using a built in DMA controller. The DMA controller uses 16 bit accesses to fill the cursor buffers, as the cursor buffers are only 16 bits wide. 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 less expensive resource wise than using a 32 to 1 multiplexor. All registers are 32 bits wide * registers are also accessible as 16 bits using byte selects These registers repeat in incrementing block of four registers and pertain to each cursor 0: POS - position register [15: 0] horizontal position (hctr value) [31:16] vertical position (vctr value) 1: SZO - size and offset register bits [ 5: 0] width of cursor in pixels - 1 [ 7: 6] size of horizontal pixels - 1 in clock cycles [13: 8] height of cursor in pixels -1 [15:14] size of vertical pixels in scan-lines - 1 * the product of width * height cannot exceed 1024 ! if it does, the display will begin repeating [31:16] image offset 2: ADR [31:11] cursor image address The low order 11 bits are fixed at zero. The DMA controller will assign the low order 11 bits during DMA. 3: TC [15:0] transparent color This register identifies which color of the cursor is transparent 4-31: registers for seven other sprites Global status and control 58: BTC [23:0] background transparent color 59: BC [23:0] background color 60: EN [15:0] cursor enable register [31:16] cursor interrupt enable / status 61: COL [15:0] cursor-cursor collision register [31:16] cursor-background collision register 62: DT [15:0] cursor DMA trigger 3 8x8 multipliers (for alpha blending) 4 cursors Webpack 7.1i xc3s1000-4ft256 1181 LUTs / 681 slices / 86MHz - 4 4 block rams 2018 / 1173 / 75 MHz - 8 =============================================================== */ module cursorController( // Bus Slave interface //------------------------------ // Slave signals input rst_i, // reset input clk_i, // clock input cyc_i, // cycle valid input stb_i, // chip select (strobe) input stb2_i, // cursor ram chip select output ack_o, // transfer acknowledge input we_i, // write input [3:0] sel_i, // byte select input [13:0] adr_i, // address input [31:0] dat_i, // data input output reg [31:0] dat_o, // data output output vol_o, // volatile register //------------------------------ // Bus Master Signals output reg m_soc_o, // start of cycle output m_cyc_o, // cycle is valid output m_stb_o, // strobe output input m_ack_i, // input data is ready output m_we_o, // write (always inactive) output [3:0] m_sel_o, // byte select output [31:0] m_adr_o, // DMA address input [31:0] m_dat_i, // data input output [31:0] m_dat_o, // data output (always zero) //-------------------------- input vclk, // video dot clock input hSync, // horizontal sync pulse input vSync, // vertical sync pulse input blank, // blanking signal input [24:0] rgbIn, // input pixel stream output reg [23:0] rgbOut, // output pixel stream output irq // interrupt request ); //-------------------------------------------------------------------- // Core Parameters //-------------------------------------------------------------------- parameter pnSpr = 8; // number of cursors parameter phBits = 11; // number of bits in horizontal timing counter parameter pvBits = 11; // number of bits in vertical timing counter parameter pColorBits = 16; // number of bits used for color data localparam pnSprm = pnSpr-1; //-------------------------------------------------------------------- // Variable Declarations //-------------------------------------------------------------------- wire [2:0] sprN = adr_i[6: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; // cursor output reg outact; // cursor output is active wire bkCollision; // cursor-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 [13:0] sprEn; // enable cursor reg [13:0] sprCollision; // cursor-cursor collision reg sprSprIe; // cursor-cursor interrupt enable reg sprBkIe; // cursor-background interrupt enable reg sprSprIRQPending; // cursor-cursor collision interrupt pending reg sprBkIRQPending; // cursor-background collision interrupt pending reg sprSprIRQPending1; // cursor-cursor collision interrupt pending reg sprBkIRQPending1; // cursor-background collision interrupt pending reg sprSprIRQ1; // vclk domain regs reg sprBkIRQ1; // cursor control registers reg [13:0] sprSprCollision; reg [13:0] sprSprCollision1; reg [13:0] sprBkCollision; reg [13:0] sprBkCollision1; reg [pColorBits-1:0] sprTc [pnSprm:0]; // cursor transparent color code // How big the pixels are: // 1,2,3,or 4 video clocks reg [1:0] hSprRes [pnSprm:0]; // cursor horizontal resolution reg [1:0] vSprRes [pnSprm:0]; // cursor vertical resolution reg [5:0] sprWidth [pnSprm:0]; // number of pixels in X direction reg [5:0] sprHeight [pnSprm:0]; // number of vertical pixels // display and timing signals reg [pnSprm:0] hSprReset; // horizontal reset reg [pnSprm:0] vSprReset; // vertical reset reg [pnSprm:0] hSprDe; // cursor horizontal display enable reg [pnSprm:0] vSprDe; // cursor vertical display enable reg [pnSprm:0] sprDe; // display enable reg [phBits-1:0] hSprPos [pnSprm:0]; // cursor horizontal position reg [pvBits-1:0] vSprPos [pnSprm:0]; // cursor vertical position reg [5:0] hSprCnt [pnSprm:0]; // cursor horizontal display counter reg [5:0] vSprCnt [pnSprm:0]; // vertical display counter reg [9:0] sprImageOffs [pnSprm:0]; // offset within cursor memory reg [9:0] sprAddr [pnSprm:0]; // index into cursor memory reg [9:0] sprAddrB [pnSprm:0]; // backup address cache for rescan wire [pColorBits-1:0] sprOut [pnSprm:0]; // cursor image data output // DMA access reg [15:11] sprSysAddrL [pnSprm:0]; // system memory address of cursor image (low bits) reg [31:16] sprSysAddrH [pnSprm:0]; // system memory address of cursor image (high bits) reg [3:0] dmaOwner; // which cursor has the DMA channel reg [pnSprm:0] sprDt; // DMA trigger register wire dmaDone; // DMA is finished reg [10:0] dmaCount; // this counter forms the low order 11 bits of the system address for DMA reg [10:0] dmaCountNext; // next value dmaCount will be loaded with reg [10:0] updAdr; // this counter is used to index the cursor image cache reg [10:0] updAdrNext; reg dmaStart; // this flag pulses high for a single cycle at the start of a DMA reg dmaActive; // this flag indicates that a block DMA transfer is active integer n; //-------------------------------------------------------------------- // DMA control / bus interfacing //-------------------------------------------------------------------- reg sprRamRdy; always @(posedge clk_i) sprRamRdy = cyc_i & stb2_i; assign m_stb_o = m_cyc_o; assign ack_o = cyc_i & stb_i ? 1 : cyc_i & stb2_i ? (we_i ? 1 : sprRamRdy) : 0; assign vol_o = cyc_i & stb_i & adr_i[7:2]>6'd59; assign irq = sprSprIRQ|sprBkIRQ; //-------------------------------------------------------------------- // DMA control / bus interfacing //-------------------------------------------------------------------- wire btout; wire sbi_rdy1 = m_ack_i|btout; busTimeoutCtr #(20) br0( .rst(rst_i), .crst(1'b0), .clk(clk_i), .ce(1'b1), .req(m_soc_o), .rdy(m_ack_i), .timeout(btout) ); assign m_we_o = 1'b0; assign m_sel_o = m_adr_o[1] ? 4'b1100 : 4'b0011; assign m_adr_o = {sprSysAddrH[dmaOwner],sprSysAddrL[dmaOwner],dmaCount[9:0],1'b0}; assign m_dat_o = 32'd0; // DMA address generator goes based on the requests that have been acknowledged assign dmaDone = dmaCountNext[10] & sbi_rdy1; always @(dmaCount) dmaCountNext = dmaCount + 1; always @(posedge clk_i) if (rst_i) dmaCount = 0; else begin if (dmaStart) dmaCount = 0; else if (sbi_rdy1 && !dmaDone) dmaCount = dmaCountNext; end // image cache address generator goes based on the responses that are ready wire updDone = updAdrNext[10] & sbi_rdy1; always @(updAdr) updAdrNext = updAdr + 1; always @(posedge clk_i) if (rst_i) updAdr = 0; else begin if (dmaStart) updAdr = 0; else if (sbi_rdy1 && !updDone) updAdr = updAdrNext; end // Arbitrate access to DMA channel - priority ordered always @(posedge clk_i) if (rst_i) begin dmaActive <= 1'b0; dmaOwner <= 3'd0; dmaStart <= 1'b0; m_soc_o <= 1'b0; end else begin dmaStart <= 1'b0; m_soc_o <= 1'b0; if (!dmaActive || updDone) begin dmaStart <= |sprDt; dmaActive <= |sprDt; m_soc_o <= |sprDt; dmaOwner <= 0; for (n = pnSprm; n >= 0; n = n - 1) if (sprDt[n]) dmaOwner <= n; end if (sbi_rdy1 && !updDone) m_soc_o <= 1'b1; end assign m_cyc_o = dmaActive & !dmaDone; // generate a write enable strobe for the cursor image memory always @(dmaOwner, dmaActive, adr_i, stb2_i, we_i) for (n = 0; n < pnSpr; n = n + 1) sprWe[n] = (dmaOwner==n && dmaActive)||(stb2_i & we_i & adr_i[13:11]==n); always @(stb2_i, adr_i) for (n = 0; n < pnSpr; n = n + 1) sprRe[n] = stb2_i & adr_i[13:11]==n; wire [15:0] sr_dout [7:0]; wire [15:0] 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]; // register/ursor memory output mux always @* if (stb2_i) dat_o <= {sr_dout_all,sr_dout_all}; else case (adr_i[7:2]) // synopsys full_case parallel_case 6'd60: dat_o <= {sprBkIRQPending|sprSprIRQPending,5'b0,sprBkIRQPending,sprSprIRQPending,6'b0,sprBkIe,sprSprIe,2'b0,sprEn}; 6'd61: dat_o <= {sprBkCollision,2'b0,sprSprCollision}; 6'd62: dat_o <= sprDt; default: dat_o <= 0; endcase // 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 <= 14'h3FFF; sprDt <= 0; for (n = 0; n < pnSpr; n = n + 1) begin sprSysAddrL[n] <= 5'b0100_0 + n; //xxxx_4000 sprSysAddrH[n] <= 16'h0000; //0000_xxxx end sprSprIe <= 0; sprBkIe <= 0; // Set reasonable starting positions on the screen // so that the cursors might be visible for testing for (n = 0; n < pnSpr; n = n + 1) begin hSprPos[n] <= 440 + n * 40; vSprPos[n] <= 200; sprTc[n] <= 16'h6739; sprWidth[n] <= 31; // 32x32 sprites sprHeight[n] <= 31; 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 (cyc_i & stb_i & we_i) begin casex (adr_i[7:2]) 6'b0xxx00: begin if (sel_i[0]) hSprPos[sprN] <= dat_i[15:0]; if (sel_i[2]) vSprPos[sprN] <= dat_i[31:16]; end 6'b0xxx01: begin if (sel_i[0]) begin sprWidth[sprN] <= dat_i[5:0]; hSprRes[sprN] <= dat_i[7:6]; end if (sel_i[1]) begin sprHeight[sprN] <= dat_i[13:8]; vSprRes[sprN] <= dat_i[15:14]; end if (sel_i[2]) sprImageOffs[sprN] <= dat_i[31:16]; end 6'b0xxx10: begin // DMA address set on clk_i domain if (sel_i[0]) sprSysAddrL[sprN] <= dat_i[15:11]; if (sel_i[2]) sprSysAddrH[sprN] <= dat_i[31:16]; end 6'b0xxx11: if (sel_i[0]) sprTc[sprN] <= dat_i[15:0]; 6'd58: begin if (sel_i[0]) bgTc[7:0] <= dat_i[7:0]; if (sel_i[1]) bgTc[15:8] <= dat_i[15:8]; if (sel_i[2]) bgTc[23:16] <= dat_i[23:16]; end 6'd59: begin if (sel_i[0]) bkColor[7:0] <= dat_i[7:0]; if (sel_i[1]) bkColor[15:8] <= dat_i[15:8]; if (sel_i[2]) bkColor[23:16] <= dat_i[23:16]; end 6'd60: begin if (sel_i[0]) sprEn <= dat_i; if (sel_i[2]) begin sprSprIe <= dat_i[16]; sprBkIe <= dat_i[17]; end end // update DMA trigger // dat_i[7:0] indicates which triggers to set (1=set,0=ignore) // dat_i[23:16] indicates which triggers to clear (1=clear,0=ignore) 6'd62: begin if (sel_i[0]) sprDt <= sprDt | dat_i[7:0]; if (sel_i[2]) sprDt <= sprDt & ~dat_i[23:16]; end default: ; endcase end end //------------------------------------------------------------- // Cursor Image Cache RAM // This RAM is dual ported with an SoC side and a display // controller side. //------------------------------------------------------------- wire [10:1] sr_adr = m_cyc_o ? m_adr_o[10:1] : adr_i[10:1]; wire [15:0] sr_din = m_cyc_o ? (sr_adr[1] ? m_dat_i[31:16] : m_dat_i[15:0]) : (sr_adr[1] ? dat_i[31:16] : dat_i[15:0]); wire sr_ce = m_cyc_o ? sbi_rdy1 : cyc_i & stb2_i; // Note: the cursor 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 : genCursorRam syncRam_1rw1r #(.pDw(pColorBits)) sprRam0( .clka(clk_i), .adra(sr_adr), .dia(sr_din), .doa(sr_dout[g]), .cea(sr_ce), .wea(sprWe[g]), .rsta(!sprRe[g]), .clkb(vclk), .adrb(sprAddr[g]), .dob(sprOut[g]), .ceb(1'b1), .rstb(1'b0) ); end endgenerate //------------------------------------------------------------- // Timing counters and addressing // Cursors are like miniature bitmapped displays, they need // all the same timing controls. //------------------------------------------------------------- // Create a timing reference using horizontal and vertical // soc 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 cursor horizontal reset always @(posedge vclk) for (n = 0; n < pnSpr; n = n + 1) hSprReset[n] <= hctr==hSprPos[n]; // track cursor 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 cursor size scaling // video clock division reg [pnSprm:0] hSprNextPixel; reg [pnSprm:0] vSprNextPixel; reg [1:0] hSprPt [pnSprm:0]; // horizontal pixel toggle reg [1:0] vSprPt [pnSprm: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 cursor image address counters always @(posedge vclk) for (n = 0; n < pnSpr; n = n + 1) begin // hReset and vReset - top left of cursor, // 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 cursor 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 cursor 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 cursor 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 cursor 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 cursor 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 [pnSprm:0] sprDe1; reg [pnSprm:0] sproact; always @(posedge vclk) for (n = 0; n < pnSpr; n = n + 1) begin sprDe1[n] <= sprDe[n]; end // Detect which cursor outputs are active // The cursor output is active if the current display pixel // address is within the cursor's area, the cursor 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 cursor activity flag // The image combiner uses this flag to know what to do with // the cursor output. always @(posedge vclk) outact = |sproact; // Display data comes from the active cursor with the // highest display priority. // Make sure that alpha blending is turned off when // no cursor 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 cursor 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 cursor always @(posedge vclk) if (blank) rgbOut <= 0; else begin if (rgbIn[24] && rgbIn[23:0] != bgTc) // color is in front of cursor rgbOut <= rgbIn[23:0]; else if (outact) begin if (!out[15]) // a cursor is displayed without alpha blending rgbOut <= {out[14:10],3'b0,out[9:5],3'b0,out[4:0],3'b0}; else rgbOut <= blendedColor; end else rgbOut <= rgbIn[23:0]; end //-------------------------------------------------------------------- // Collision logic //-------------------------------------------------------------------- // Detect when a cursor-cursor collision has occurred. The criteria // for this is that a pixel from the cursor is being displayed, while // there is a pixel from another cursor that could be displayed at the // same time. always @(sproact) 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 // Detect when a cursor-background collision has occurred assign bkCollision = (rgbIn[24] && rgbIn[23:0] != bgTc) ? 0 : outact && rgbIn[23:0] != bkColor; // Load the cursor 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)||(cyc_i & stb_i && sel_i[0] && adr_i[7:2]==6'd61)) begin sprSprIRQPending1 <= 1; sprSprIRQ1 <= sprSprIe; sprSprCollision1 <= sproact; end else sprSprCollision1 <= sprSprCollision1|sproact; end else if (cyc_i & stb_i && sel_i[0] && adr_i[7:2]==6'd61) begin sprSprCollision1 <= 0; sprSprIRQPending1 <= 0; sprSprIRQ1 <= 0; end // Load the cursor 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) || (cyc_i & stb_i && sel_i[2] && adr_i[7:2]==6'd61)) begin sprBkIRQ1 <= sprBkIe; sprBkCollision1 <= sproact; sprBkIRQPending1 <= 1; end else sprBkCollision1 <= sprBkCollision1|sproact; end else if (cyc_i & stb_i && sel_i[2] && adr_i[7:2]==6'd61) begin sprBkCollision1 <= 0; sprBkIRQPending1 <= 0; sprBkIRQ1 <= 0; end endmodule