/* Module vidram1 - Written by Antti Karttunen, 30. 5. 2005 - Last edited 31. May 2005. This module implements a simple-minded VGA-controller for Spartan-3 Starter Kit Board, utilizing its "poor man's 3-bit RGB-VGA" and ISSI Fast Static Asynchronous RAM as its video memory. Whenever vidram_client_can_write signal is on (i.e. Hgate or Vgate is off) the calling module can write its display data to the video memory. This is done by setting vidram_addr_in to the 18-bit address, vidram_data_in to the 16-bit value, and then raising the input signal vidram_we. The format for the video memory: Each sixteen-bit word contains 5 pixels, each pixel having a 3-bit "poor man RGB" value. The three least significant bits of the word correspond to the leftmost pixel, and the bits 12-14 to the rightmost pixel of each 5-pixel horizontal strip. Each horizontal row of 640 pixels fits into 128 (= 640/5) 16-bit words. The total memory consumption is 128*480 = 61440 words. */ module vidram1(input CLK, input RST, input vidram_we, input [17:0] vidram_addr_in, input [15:0] vidram_data_in, output vidram_client_can_write, input [9:0] cursor_x, input [9:0] cursor_y, input test_quilt_pat, input hsync_polarity, input vsync_polarity, output VGA_R, output VGA_G, output VGA_B, output VGA_HSYNC, output VGA_VSYNC, output [17:0] ISSI_ADDR, inout [15:0] ISSI_DATA_IO, output ISSI_CE1N, output ISSI_OEN, output ISSI_WEN, output ISSI_LB1N, output ISSI_UB1N ); reg [17:0] vidram_addr_out = 18'b000000000000000000; wire [15:0] vidram_data_out; // Take later also pixels_left to the consideration: wire vidram_client_can_write = (~Hgate | ~Vgate); wire vidram_we_really = (vidram_we & vidram_client_can_write); issia VIDRAM(.CLK(CLK), .ISSI_ADDR(ISSI_ADDR), .ISSI_DATA_IO(ISSI_DATA_IO), .ISSI_CE1N(ISSI_CE1N), .ISSI_OEN(ISSI_OEN), .ISSI_WEN(ISSI_WEN), .ISSI_LB1N(ISSI_LB1N), .ISSI_UB1N(ISSI_UB1N), .STARTWRITE(vidram_we_really), .ADDR(vidram_we_really ? vidram_addr_in : vidram_addr_out), .DATAWRITTEN(vidram_data_in), .DATAREAD(vidram_data_out), .WRITEREADY(writeready) ); /* Parameters, wires and registers involved with VGA controller. */ parameter RES_HOR = 640; parameter RES_VER = 480; // horizontal timing parameters parameter V_THSYNC = 96-1; // horizontal sync pulse width (in pixels) parameter V_THGDEL = 48-1; // horizontal gate delay (in pixels) parameter V_THGATE = RES_HOR-1; // hor. gate (number of visible pixels per line) parameter V_THLEN = 800-1; // hor. length (tot. number of pixels per line) // vertical timing parameters parameter V_TVSYNC = 2-1; // vertical sync pulse width (in lines) parameter V_TVGDEL = 29-1; // vertical gate delay (in lines) parameter V_TVGATE = RES_VER-1; // vertical gate (# of visible lines per frame) parameter V_TVLEN = 521-1; // vertical length (number of lines per frame) reg [ 7:0] Thsync = V_THSYNC; reg [ 7:0] Thgdel = V_THGDEL; reg [15:0] Thgate = V_THGATE; reg [15:0] Thlen = V_THLEN; reg [ 7:0] Tvsync = V_TVSYNC; reg [ 7:0] Tvgdel = V_TVGDEL; reg [15:0] Tvgate = V_TVGATE; reg [15:0] Tvlen = V_TVLEN; reg pix_clk = 0; reg [9:0] vga_x = 0; // Allow range 0-1023. (in practice 0-639) reg [9:0] vga_y = 0; // Allow range 0-1023. (in practice 0-479) reg [9:0] vga_x_plus1 = 1; reg [9:0] vga_y_plus1 = 1; wire hsync; wire vsync; assign VGA_HSYNC = hsync^hsync_polarity; assign VGA_VSYNC = vsync^vsync_polarity; wire eof; wire Hgate, Vgate; wire Gate = (Hgate & Vgate); wire Hdone; wire cursor_x_now = ((vga_x == cursor_x) || (vga_x_plus1 == cursor_x)); wire cursor_y_now = ((vga_y == cursor_y) || (vga_y_plus1 == cursor_y)); reg [2:0] pixels_left = 0; reg [14:0] pentaplet; wire current_R = ((cursor_x_now && cursor_y_now)^pentaplet[2]); wire current_G = ((cursor_x_now && cursor_y_now)^pentaplet[1]); wire current_B = ((cursor_x_now && cursor_y_now)^pentaplet[0]); // These three functions generate a nice 20x15 (640/32 x 480/32) quilt pattern. // The top-left corner should be white (111) function foo_R; input [9:0] x; input [9:0] y; begin foo_R = ~x[5]; end endfunction function foo_G; input [9:0] x; input [9:0] y; begin foo_G = ~y[5]; end endfunction function foo_B; input [9:0] x; input [9:0] y; begin // Use (x[5] == y[5]); for less colours (W R G B) but also less ugly colours! foo_B = (x[6] == y[6]); end endfunction assign VGA_R = Gate & (test_quilt_pat ? foo_R(vga_x,vga_y) : current_R); assign VGA_G = Gate & (test_quilt_pat ? foo_G(vga_x,vga_y) : current_G); assign VGA_B = Gate & (test_quilt_pat ? foo_B(vga_x,vga_y) : current_B); // hookup horizontal timing generator vgatimer hor_gen( .clk(CLK), .ena(pix_clk), .rst(RST), .Tsync(Thsync), .Tgdel(Thgdel), .Tgate(Thgate), .Tlen(Thlen), .Sync(hsync), .Gate(Hgate), .Done(Hdone) ); // hookup vertical timing generator wire vclk_ena = Hdone & pix_clk; vgatimer ver_gen( .clk(CLK), .ena(vclk_ena), .rst(RST), .Tsync(Tvsync), .Tgdel(Tvgdel), .Tgate(Tvgate), .Tlen(Tvlen), .Sync(vsync), .Gate(Vgate), .Done(eof) ); always @(posedge CLK) begin if(RST) then begin pix_clk <= 0; pixels_left <= 0; vidram_addr_out <= 0; end else begin pix_clk <= ~pix_clk; // Toggle the pixel clock. // Note how we increment the vidram address on the "even" (non-pix_clk) cycles, // so that ISSI SRAM has time to return the vidram_data_out for // the next cycle, when pix_clk=1 and its contents are copied to // pentaplet: if(~pix_clk) begin vidram_addr_out <= (~Vgate ? 0 : vidram_addr_out + (~pixels_left)); end else // On the pix_clk begin if(0 == pixels_left) begin pentaplet <= vidram_data_out[14:0]; pixels_left <= 5; end else // Shift pentaplet right by 3 bits (1 pixel): begin pentaplet <= {3b'000,pentaplet[14:3]}; pixels_left <= pixels_left - 1; end vga_x <= (~Hgate ? 0 : vga_x + 1); vga_x_plus1 <= (~Hgate ? 1 : vga_x_plus1 + 1); vga_y <= (~Vgate ? 0 : vga_y + Hdone); vga_y_plus1 <= (~Vgate ? 1 : vga_y_plus1 + Hdone); end end end endmodule