URL
https://opencores.org/ocsvn/rtf_sprite_controller/rtf_sprite_controller/trunk
Subversion Repositories rtf_sprite_controller
[/] [rtf_sprite_controller/] [trunk/] [rtl/] [verilog/] [rtfSpriteController2.v] - Rev 6
Compare with Previous | Blame | View Log
// ============================================================================ // __ // \\__/ o\ (C) 2018-2019 Robert Finch, Waterloo // \ __ / All rights reserved. // \/_// robfinch<remove>@finitron.ca // || // // rtfSpriteController2.v // // 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/>. // // ============================================================================ // //`define USE_CLOCK_GATE 1'b1 `define TRUE 1'b1 `define FALSE 1'b0 `define HIGH 1'b1 `define LOW 1'b0 `define ABITS 31:0 // The cycle at which it's safe to update the working count and address. // A good value is just before the end of the scan, but that depends on // display resolution. `define SPR_WCA 12'd638 //`define SUPPORT_LOWRES 1'b1 module rtfSpriteController2(clk_i, cs_i, cyc_i, stb_i, ack_o, we_i, sel_i, adr_i, dat_i, dat_o, m_clk_i, m_cyc_o, m_stb_o, m_ack_i, m_sel_o, m_adr_o, m_dat_i, m_spriteno_o, dot_clk_i, hsync_i, vsync_i, border_i, zrgb_i, zrgb_o, test ); // Bus slave port input clk_i; input cs_i; input cyc_i; input stb_i; output ack_o; input we_i; input [7:0] sel_i; input [11:0] adr_i; input [63:0] dat_i; output reg [63:0] dat_o; // Bus master port input m_clk_i; output reg m_cyc_o; output m_stb_o; input m_ack_i; output [7:0] m_sel_o; output reg [`ABITS] m_adr_o; input [63:0] m_dat_i; output reg [4:0] m_spriteno_o; // Video port input dot_clk_i; input vsync_i; input hsync_i; input border_i; input [31:0] zrgb_i; output reg [31:0] zrgb_o; input test; parameter NSPR = 32; parameter IDLE = 2'd0; parameter DATA_FETCH = 2'd1; parameter MEM_ACCESS = 2'd2; integer n; reg controller_enable = 1'b1; wire vclk; reg [1:0] state = IDLE; reg [1:0] lowres = 2'b00; wire [5:0] flashcnt; reg rst_collision = 1'b0; reg [31:0] collision, c_collision; reg [4:0] spriteno = 5'd0; reg sprite; reg [31:0] spriteEnable = 32'hFFFFFFFF; reg [31:0] spriteActive; reg [11:0] sprite_pv [0:31]; reg [11:0] sprite_ph [0:31]; reg [3:0] sprite_pz [0:31]; (* ram_style="distributed" *) reg [37:0] sprite_color [0:255]; reg [31:0] sprite_on; reg [31:0] sprite_on_d1; reg [31:0] sprite_on_d2; reg [31:0] sprite_on_d3; (* ram_style="distributed" *) reg [`ABITS] spriteAddr [0:31]; reg [`ABITS] spriteWaddr [0:31]; reg [15:0] spriteMcnt [0:31]; reg [15:0] spriteWcnt [0:31]; reg [63:0] m_spriteBmp [0:31]; reg [63:0] spriteBmp [0:31]; reg [31:0] spriteLink1 = 32'h0; reg [7:0] spriteColorNdx [0:31]; initial begin for (n = 0; n < 256; n = n + 1) begin sprite_color[n] <= {6'h00,8'h00,n[7:5],5'd0,n[4:3],6'd0,n[2:0],5'd0}; // sprite_color[n][31:0] <= {8'h00,n[7:5],5'd0,n[4:3],6'd0,n[2:0],5'd0}; // <- doesn't work end for (n = 0; n < 32; n = n + 1) begin if (n < 16) begin sprite_ph[n] <= 400 + n * 32; sprite_pv[n] <= 100 + n * 8; end else begin sprite_ph[n] <= 400 + (n - 16) * 32; sprite_pv[n] <= 200 + n * 8; end sprite_pz[n] <= 8'h08; spriteMcnt[n] <= 80 * 32; spriteBmp[n] <= 64'hFFFFFFFFFFFFFFFF; spriteAddr[n] <= 32'h40000 + (n << 12); end end wire pe_hsync, pe_vsync; wire [11:0] hctr, vctr; reg [11:0] m_hctr, m_vctr; reg cs; reg we; reg [7:0] sel; reg [11:0] adr; reg [63:0] dat; wire clk; `ifdef USE_CLOCK_GATE BUFH uclk (.I(clk_i), .O(clk)); `else wire clk = clk_i; `endif always @(posedge clk) cs <= cs_i & cyc_i & stb_i; always @(posedge clk) we <= we_i; always @(posedge clk) sel <= sel_i; always @(posedge clk) adr <= adr_i; always @(posedge clk) dat <= dat_i; ack_gen #( .READ_STAGES(4), .WRITE_STAGES(0), .REGISTER_OUTPUT(1) ) uag1 ( .clk_i(clk), .ce_i(1'b1), .i(cs), .we_i(cs & we), .o(ack_o) ); (* ram_style="block" *) reg [63:0] shadow_ram [0:511]; reg [63:0] shadow_ramo; reg [8:0] sradr; always @(posedge clk) if (cs & we) begin if (|sel[1:0]) shadow_ram[adr[11:3]][15: 0] <= dat[15: 0]; if (|sel[3:2]) shadow_ram[adr[11:3]][31:16] <= dat[31:16]; if (|sel[5:4]) shadow_ram[adr[11:3]][47:32] <= dat[47:32]; if (|sel[7:6]) shadow_ram[adr[11:3]][63:48] <= dat[63:48]; end always @(posedge clk) sradr <= adr[11:3]; always @(posedge clk) shadow_ramo <= shadow_ram[sradr]; always @(posedge clk) case(adr[11:3]) 9'b1010_0001_0: dat_o <= c_collision; default: dat_o <= shadow_ramo; endcase always @(posedge clk) begin rst_collision <= `FALSE; if (cs & we) begin casez(adr[11:3]) 9'b0???_????_?: sprite_color[adr[10:3]] <= dat[37:0]; 9'b100?_????_0: spriteAddr[adr[8:4]] <= dat[`ABITS]; 9'b100?_????_1: begin if (|sel[1:0]) sprite_ph[adr[8:4]] <= dat[11: 0]; if (|sel[3:2]) sprite_pv[adr[8:4]] <= dat[27:16]; if (|sel[5:4]) sprite_pz[adr[8:4]] <= dat[39:32]; if (|sel[7:6]) spriteMcnt[adr[8:4]] <= dat[63:48]; end 9'b1010_0000_0: spriteEnable <= dat[31:0]; 9'b1010_0000_1: spriteLink1 <= dat[31:0]; 9'b1010_0001_0: rst_collision <= `TRUE; 9'b1010_0001_1: begin lowres <= dat[1:0]; controller_enable <= dat[8]; end default: ; endcase end end assign m_stb_o = m_cyc_o; assign m_sel_o = 8'hFF; wire clkm; `ifdef USE_CLOCK_GATE BUFHCE uclkm (.I(m_clk_i), .CE(controller_enable), .O(clkm)); `else wire clkm = m_clk_i; `endif // Register hctr to m_clk_i domain always @(posedge clkm) m_hctr <= hctr; // State machine always @(posedge clkm) case(state) IDLE: // dot_clk_i is likely faster than m_clk_i, so check for a trigger zone. if (m_hctr < 12'd10 && controller_enable) state <= DATA_FETCH; DATA_FETCH: if (spriteActive[spriteno]) begin if (~m_ack_i) state <= MEM_ACCESS; end else if (spriteno==5'd31) state <= IDLE; MEM_ACCESS: if (m_ack_i|test) begin if (spriteno==5'd31) state <= IDLE; else state <= DATA_FETCH; end default: state <= IDLE; endcase always @(posedge clkm) case(state) IDLE: m_cyc_o <= `LOW; DATA_FETCH: if (!m_ack_i && spriteActive[spriteno]) begin m_cyc_o <= ~test;//`HIGH; m_adr_o <= spriteWaddr[spriteno]; m_spriteno_o <= spriteno; end MEM_ACCESS: if (m_ack_i|test) begin m_cyc_o <= `LOW; m_spriteBmp[spriteno] <= m_dat_i; if (test) m_spriteBmp[spriteno] <= 64'h00005555AAAAFFFF; end default: ; endcase always @(posedge clkm) case(state) IDLE: spriteno <= 5'd0; DATA_FETCH: if (!spriteActive[spriteno]) spriteno <= spriteno + 5'd1; MEM_ACCESS: if (m_ack_i|test) spriteno <= spriteno + 5'd1; default: ; endcase // Register collision onto clk_i domain. always @(posedge clk) c_collision <= collision; `ifdef USE_CLOCK_GATE BUFHCE ucb1 ( .I(dot_clk_i), .CE(controller_enable), .O(vclk) ); `else assign vclk = dot_clk_i; `endif edge_det ued1 (.clk(vclk), .ce(1'b1), .i(hsync_i), .pe(pe_hsync), .ne(), .ee()); edge_det ued2 (.clk(vclk), .ce(1'b1), .i(vsync_i), .pe(pe_vsync), .ne(), .ee()); VT163 #(12) uhctr (.clk(vclk), .clr_n(1'b1), .ent(1'b1), .enp(1'b1), .ld_n(!pe_hsync), .d(12'd0), .q(hctr), .rco()); VT163 #(12) uvctr (.clk(vclk), .clr_n(1'b1), .ent(pe_hsync), .enp(1'b1), .ld_n(!pe_vsync), .d(12'd0), .q(vctr), .rco()); VT163 # (6) ufctr (.clk(vclk), .clr_n(1'b1), .ent(pe_vsync), .enp(1'b1), .ld_n(1'b1), .d( 6'd0), .q(flashcnt), .rco()); always @(posedge vclk) begin if (rst_collision) collision <= 32'd0; else case(sprite_on) 32'b00000000000000000000000000000000, 32'b00000000000000000000000000000001, 32'b00000000000000000000000000000010, 32'b00000000000000000000000000000100, 32'b00000000000000000000000000001000, 32'b00000000000000000000000000010000, 32'b00000000000000000000000000100000, 32'b00000000000000000000000001000000, 32'b00000000000000000000000010000000, 32'b00000000000000000000000100000000, 32'b00000000000000000000001000000000, 32'b00000000000000000000010000000000, 32'b00000000000000000000100000000000, 32'b00000000000000000001000000000000, 32'b00000000000000000010000000000000, 32'b00000000000000000100000000000000, 32'b00000000000000001000000000000000, 32'b00000000000000010000000000000000, 32'b00000000000000100000000000000000, 32'b00000000000001000000000000000000, 32'b00000000000010000000000000000000, 32'b00000000000100000000000000000000, 32'b00000000001000000000000000000000, 32'b00000000010000000000000000000000, 32'b00000000100000000000000000000000, 32'b00000001000000000000000000000000, 32'b00000010000000000000000000000000, 32'b00000100000000000000000000000000, 32'b00001000000000000000000000000000, 32'b00010000000000000000000000000000, 32'b00100000000000000000000000000000, 32'b01000000000000000000000000000000, 32'b10000000000000000000000000000000: ; default: collision <= collision | sprite_on; endcase end // - - - - - - - - - - - - - - - - - - - - - - - - - - // clock edge #-1 // - - - - - - - - - - - - - - - - - - - - - - - - - - // Compute when to shift sprite bitmaps. // Set sprite active flag // Increment working count and address reg [31:0] spriteShift; always @(posedge vclk) for (n = 0; n < NSPR; n = n + 1) spriteShift[n] <= ((hctr >> lowres) >= sprite_ph[n]); always @(posedge vclk) for (n = 0; n < NSPR; n = n + 1) spriteActive[n] <= (spriteWcnt[n] <= spriteMcnt[n]) && spriteEnable[n]; always @(posedge vclk) for (n = 0; n < NSPR; n = n + 1) spriteWcnt[n] <= ((vctr >> lowres) - sprite_pv[n]) * 16'd32; always @(posedge vclk) for (n = 0; n < NSPR; n = n + 1) spriteWaddr[n] <= spriteAddr[n] + spriteWcnt[n][15:2]; // - - - - - - - - - - - - - - - - - - - - - - - - - - // clock edge #0 // - - - - - - - - - - - - - - - - - - - - - - - - - - // Get the sprite display status // Load the sprite bitmap from ram // Determine when sprite output should appear // Shift the sprite bitmap // Compute color indexes for all sprites always @(posedge vclk) begin for (n = 0; n < NSPR; n = n + 1) if (spriteActive[n] & spriteShift[n]) begin sprite_on[n] <= spriteLink1[n] ? |{spriteBmp[(n+1)&31][63:62],spriteBmp[n][63:62]} : |spriteBmp[n][63:62]; end else sprite_on[n] <= 1'b0; end // Load / shift sprite bitmap // Register sprite data back to vclk domain always @(posedge vclk) begin if (hctr==12'h5) for (n = 0; n < NSPR; n = n + 1) spriteBmp[n] <= m_spriteBmp[n]; for (n = 0; n < NSPR; n = n + 1) if (spriteShift[n]) `ifdef SUPPORT_LOWRES case(lowres) 2'd0,2'd3: spriteBmp[n] <= {spriteBmp[n][61:0],2'h0}; 2'd1: if (hctr[0]) spriteBmp[n] <= {spriteBmp[n][61:0],2'h0}; 2'd2: if (&hctr[1:0]) spriteBmp[n] <= {spriteBmp[n][61:0],2'h0}; endcase `else spriteBmp[n] <= {spriteBmp[n][61:0],2'h0}; `endif end always @(posedge vclk) for (n = 0; n < NSPR; n = n + 1) if (spriteLink1[n]) spriteColorNdx[n] <= {n[3:0],spriteBmp[(n+1)&31][63:62],spriteBmp[n][63:62]}; else if (spriteLink1[(n-1)&31]) spriteColorNdx[n] <= 8'h00; // transparent else spriteColorNdx[n] <= {1'b0,n[4:0],spriteBmp[n][63:62]}; // Compute index into sprite color palette // If none of the sprites are linked, each sprite has it's own set of colors. // If the sprites are linked once the colors are available in groups. // If the sprites are linked twice they all share the same set of colors. // Pipelining register reg border3, border4; reg any_sprite_on2, any_sprite_on3, any_sprite_on4; reg [31:0] zrgb_i3, zrgb_i4; reg [7:0] zb_i3, zb_i4; reg [7:0] sprite_z1, sprite_z2, sprite_z3, sprite_z4; reg [7:0] sprite_pzx; // The color index from each sprite can be mux'ed into a single value used to // access the color palette because output color is a priority chain. This // saves having mulriple read ports on the color palette. reg [37:0] spriteColorOut2; reg [37:0] spriteColorOut3; reg [7:0] spriteClrNdx; // - - - - - - - - - - - - - - - - - - - - - - - - - - // clock edge #1 // - - - - - - - - - - - - - - - - - - - - - - - - - - // Mux color index // Fetch sprite Z order always @(posedge vclk) sprite_on_d1 <= sprite_on; always @(posedge vclk) begin spriteClrNdx <= 8'd0; for (n = NSPR-1; n >= 0; n = n -1) if (sprite_on[n]) spriteClrNdx <= spriteColorNdx[n]; end always @(posedge vclk) begin sprite_z1 <= 8'hff; for (n = NSPR-1; n >= 0; n = n -1) if (sprite_on[n]) sprite_z1 <= sprite_pz[n]; end // - - - - - - - - - - - - - - - - - - - - - - - - - - // clock edge #2 // - - - - - - - - - - - - - - - - - - - - - - - - - - // Lookup color from palette always @(posedge vclk) sprite_on_d2 <= sprite_on_d1; always @(posedge vclk) any_sprite_on2 <= |sprite_on_d1; always @(posedge vclk) spriteColorOut2 <= sprite_color[spriteClrNdx]; always @(posedge vclk) sprite_z2 <= sprite_z1; // - - - - - - - - - - - - - - - - - - - - - - - - - - // clock edge #3 // - - - - - - - - - - - - - - - - - - - - - - - - - - // Compute alpha blending wire [15:0] alphaRed = (zrgb_i[23:16] * spriteColorOut2[31:24]) + (spriteColorOut2[23:16] * (9'h100 - spriteColorOut2[31:24])); wire [15:0] alphaGreen = (zrgb_i[15:8] * spriteColorOut2[31:24]) + (spriteColorOut2[15:8] * (9'h100 - spriteColorOut2[31:24])); wire [15:0] alphaBlue = (zrgb_i[7:0] * spriteColorOut2[31:24]) + (spriteColorOut2[7:0] * (9'h100 - spriteColorOut2[31:24])); reg [23:0] alphaOut; always @(posedge vclk) alphaOut <= {alphaRed[15:8],alphaGreen[15:8],alphaBlue[15:8]}; always @(posedge vclk) sprite_z3 <= sprite_z2; always @(posedge vclk) any_sprite_on3 <= any_sprite_on2; always @(posedge vclk) zrgb_i3 <= zrgb_i; always @(posedge vclk) zb_i3 <= zrgb_i[31:24]; always @(posedge vclk) border3 <= border_i; always @(posedge vclk) spriteColorOut3 <= spriteColorOut2; reg [23:0] flashOut; wire [23:0] reverseVideoOut = spriteColorOut2[37] ? alphaOut ^ 24'hFFFFFF : alphaOut; // - - - - - - - - - - - - - - - - - - - - - - - - - - // clock edge #4 // - - - - - - - - - - - - - - - - - - - - - - - - - - // Compute flash output always @(posedge vclk) flashOut <= spriteColorOut3[36] ? (((flashcnt[5:2] & spriteColorOut3[35:32])!=4'b0000) ? reverseVideoOut : zrgb_i3) : reverseVideoOut; always @(posedge vclk) zrgb_i4 <= zrgb_i3; always @(posedge vclk) sprite_z4 <= sprite_z3; always @(posedge vclk) any_sprite_on4 <= any_sprite_on3; always @(posedge vclk) zb_i4 <= zb_i3; always @(posedge vclk) border4 <= border3; // - - - - - - - - - - - - - - - - - - - - - - - - - - // clock edge #5 // - - - - - - - - - - - - - - - - - - - - - - - - - - // final output registration always @(posedge dot_clk_i) casez({border4,any_sprite_on4 & controller_enable}) 2'b01: zrgb_o <= (zb_i4 <= sprite_z4) ? zrgb_i4 : {sprite_z4,flashOut}; 2'b00: zrgb_o <= zrgb_i4; 2'b1?: zrgb_o <= zrgb_i4; endcase endmodule