`include "hvsync_generator.v" `include "ram.v" /* sprite_scanline_renderer - Module that renders multiple sprites whose attributes are fetched from shared RAM, and whose bitmaps are stored in ROM. Made to be paired with the FEMTO-16 CPU. */ module example_bitmap_rom(addr, data); input [15:0] addr; output [15:0] data; reg [15:0] bitarray[0:255]; assign data = bitarray[addr & 15]; initial begin/*{w:16,h:16,bpw:16,count:1}*/ bitarray['h00] = 16'b11110000000; bitarray['h01] = 16'b100001000000; bitarray['h02] = 16'b1111111100000; bitarray['h03] = 16'b1111111100000; bitarray['h04] = 16'b11110000000; bitarray['h05] = 16'b11111111110000; bitarray['h06] = 16'b111100001111000; bitarray['h07] = 16'b1111101101111100; bitarray['h08] = 16'b1101100001101111; bitarray['h09] = 16'b1101111111100110; bitarray['h0a] = 16'b1001111111100000; bitarray['h0b] = 16'b1111111100000; bitarray['h0c] = 16'b1110011100000; bitarray['h0d] = 16'b1100001100000; bitarray['h0e] = 16'b1100001100000; bitarray['h0f] = 16'b11100001110000; end endmodule module sprite_scanline_renderer(clk, reset, hpos, vpos, rgb, ram_addr, ram_data, ram_busy, rom_addr, rom_data); parameter NB = 5; // 2^NB == number of sprites parameter MB = 3; // 2^MB == slots per scanline localparam N = 1<= 256 if (reset || vpos[8]) begin // load sprites from RAM on line 260 // 8 cycles per sprite // do first sprite twice b/c CPU might still be busy if (vpos == 260 && hpos < N*2+8) begin ram_busy <= 1; case (hpos[0]) 0: begin ram_addr <= {load_index, 1'b0}; // load X and Y position (2 cycles ago) sprite_xpos[load_index] <= ram_data[7:0]; sprite_ypos[load_index] <= ram_data[15:8]; end 1: begin ram_addr <= {load_index, 1'b1}; // load attribute (2 cycles ago) sprite_attr[load_index] <= ram_data[7:0]; end endcase end end else if (hpos < N*2) begin // setup vars for next phase k <= 0; romload <= 0; // select the sprites that will appear in this scanline case (hpos[0]) // compute Y offset of sprite relative to scanline 0: z <= 8'(vpos - sprite_ypos[i]); 1: begin // sprite is active if Y offset is 0..15 if (z < 16) begin line_xpos[j] <= sprite_xpos[i]; // save X pos line_yofs[j] <= z; // save Y offset line_attr[j] <= sprite_attr[i]; // save attr line_active[j] <= 1; // mark sprite active j <= j + 1; // inc counter end i <= i + 1; // inc main array counter end endcase end else if (hpos < N*2+M*18) begin // setup vars for next phase j <= 0; // if sprite shift register is empty, load new sprite if (out_bitmap == 0) begin case (romload) 0: begin // set ROM address and fetch bitmap rom_addr <= {4'b0, line_attr[k][7:4], line_yofs[k]}; end 1: begin // load scanline buffer offset to write write_ofs <= {~vpos[0], line_xpos[k]}; // fetch 0 if sprite is inactive out_bitmap <= line_active[k] ? rom_data : 0; // load attribute for sprite out_attr <= line_attr[k]; // disable sprite for next scanline line_active[k] <= 0; // go to next sprite in 2ndary buffer k <= k + 1; end endcase romload <= !romload; end else begin // write color to scanline buffer if low bit == 1 if (out_bitmap[0]) scanline[write_ofs] <= out_attr[3:0]; // shift bits right out_bitmap <= out_bitmap >> 1; // increment to next scanline pixel write_ofs <= write_ofs + 1; end end else begin // clear counters i <= 0; end // read and clear buffer rgb <= scanline[read_bufidx]; scanline[read_bufidx] <= 0; end endmodule module test_scanline_render_top(clk, reset, hsync, vsync, rgb); input clk, reset; output hsync, vsync; output [3:0] rgb; wire display_on; wire [8:0] hpos; wire [8:0] vpos; hvsync_generator hvsync_gen( .clk(clk), .reset(reset), .hsync(hsync), .vsync(vsync), .display_on(display_on), .hpos(hpos), .vpos(vpos) ); wire [15:0] rom_addr; wire [15:0] rom_data; example_bitmap_rom bitmap_rom( .addr(rom_addr), .data(rom_data) ); reg [5:0] ram_addr; wire [15:0] ram_read; reg [15:0] ram_write; reg ram_we; reg ram_busy; // 64-word RAM RAM_sync #(6,16) ram( .clk(clk), .addr(ram_addr), .dout(ram_read), .din(ram_write), .we(ram_we) ); sprite_scanline_renderer ssr( .clk(clk), .reset(reset), .hpos(hpos), .vpos(vpos), .rgb(rgb), .ram_addr(ram_addr), .ram_data(ram_read), .ram_busy(ram_busy), .rom_addr(rom_addr), .rom_data(rom_data) ); always @(posedge clk) begin // wiggle sprites randomly once per frame if (vpos == 256) begin ram_addr <= hpos[7:2]; // 4 clocks per read/write cycle if (!hpos[1]) begin ram_we <= 0; end else begin ram_we <= 1; ram_write <= ram_read + 16'(($random&3)-1); end end else ram_we <= 0; end endmodule