/* * Copyright (c) 2006, Peter M. Chen and Steven Lieberman. All rights * reserved. This software is supplied as is without expressed or implied * warranties of any kind. */ /* * VGA controller for Analog Devices ADV7123 Video DAC (fmax 140 MHz). * Uses ISSI IS61LV25616 (10 ns) SRAM. vga.{fm,pdf} has a timing diagram * that shows how I pipeline the accesses through the SRAM. */ module vga( input wire osc_50, input wire clock_valid, input wire reset, input wire vga_valid, output reg vga_ack, input wire vga_write, input wire [9:0] vga_x1, input wire [8:0] vga_y1, input wire [9:0] vga_x2, input wire [8:0] vga_y2, input wire [7:0] vga_color_write, // write color data to screen output reg [7:0] vga_color_read, // read color data from screen output reg [9:0] VGA_R, output reg [9:0] VGA_G, output reg [9:0] VGA_B, output reg VGA_CLK, output reg VGA_BLANK, output reg VGA_HS, output reg VGA_VS, output reg VGA_SYNC, inout reg [15:0] SRAM_DQ, output reg SRAM_CE_N, output reg SRAM_OE_N, output reg SRAM_LB_N, output reg SRAM_UB_N, output reg SRAM_WE_N, output reg [17:0] SRAM_ADDR); reg [9:0] vga_horiz; reg [9:0] vga_vert; reg vga_horiz_incr; reg [9:0] vga_horiz_delay; reg [9:0] vga_vert_delay; reg vga_hs_delay; reg vga_vs_delay; reg next_vga_clk; reg next_sram_we_n; reg vga_rgb_write; reg [18:0] sram_address; reg [9:0] vga_address_x; reg [9:0] vga_address_y; reg [9:0] cpu_address_x; reg [8:0] cpu_address_y; reg address_select; reg cpu_address_load; // load the CPU address registers reg cpu_address_incr; // increment the CPU address registers reg next_vga_ack; reg vga_color_read_write; reg [7:0] sram_out; reg [3:0] state; reg [3:0] next_state; /* * VGA parameters: from Altera's DE2_Default */ parameter H_SYNC_CYC = 10'd96; parameter H_SYNC_BACK = 10'd45 + 10'd3; parameter H_SYNC_ACT = 10'd640; // 646 parameter H_SYNC_TOTAL = 10'd800; parameter V_SYNC_CYC = 10'd2; parameter V_SYNC_BACK = 10'd30 + 10'd2; parameter V_SYNC_ACT = 10'd480; // 484 parameter V_SYNC_TOTAL = 10'd525; parameter X_START = H_SYNC_CYC+H_SYNC_BACK; parameter Y_START = V_SYNC_CYC+V_SYNC_BACK; /* * VGA path. */ always @(posedge osc_50) begin if (reset == 1'b1) begin vga_horiz <= 10'd0; vga_vert <= 10'd0; end else begin /* * VGA horizontal and vertical counters. */ if (vga_horiz_incr == 1'b1) begin if (vga_horiz >= H_SYNC_TOTAL) begin vga_horiz <= 10'd0; if (vga_vert >= V_SYNC_TOTAL) begin vga_vert <= 10'd0; end else begin vga_vert <= vga_vert + 10'd1; end end else begin vga_horiz <= vga_horiz + 10'd1; end end /* * VGA control signals and color. vga_{hs,vs,horiz,vert}_delay * are there just to get the control signals and color to the VGA * chip at the same time (the color values are delayed through the * SRAM address). */ if (vga_horiz < H_SYNC_CYC) begin vga_hs_delay <= 1'b0; end else begin vga_hs_delay <= 1'b1; end if (vga_vert < V_SYNC_CYC) begin vga_vs_delay <= 1'b0; end else begin vga_vs_delay <= 1'b1; end vga_horiz_delay <= vga_horiz; vga_vert_delay <= vga_vert; VGA_HS <= vga_hs_delay; VGA_VS <= vga_vs_delay; if (vga_hs_delay == 1'b1 && vga_vs_delay == 1'b1) begin VGA_BLANK <= 1'b1; end else begin VGA_BLANK <= 1'b0; end /* * Calculate color based on delayed horiz/vert counters, since * these are the values that generated the colors being considered. */ if (vga_rgb_write == 1'b1) begin if (vga_horiz_delay >= X_START && vga_horiz_delay < X_START+H_SYNC_ACT && vga_vert_delay >= Y_START && vga_vert_delay < Y_START+V_SYNC_ACT) begin // 0=>0; 1=>341; 2=>682; 3=>1023 VGA_R <= {5{sram_out[5:4]}}; VGA_G <= {5{sram_out[3:2]}}; VGA_B <= {5{sram_out[1:0]}}; end else begin VGA_R <= 10'b0; VGA_G <= 10'b0; VGA_B <= 10'b0; end end VGA_CLK <= next_vga_clk; end end always @* begin VGA_SYNC = 1'b0; vga_address_x = vga_horiz - X_START; vga_address_y = vga_vert - Y_START; end /* * SRAM */ always @* begin SRAM_CE_N = 1'b0; // chip is always enabled SRAM_OE_N = 1'b0; // chip is always driving output (maybe set // SRAM_OE_N=1'b1 when writing) if (address_select == 1'b0) begin sram_address = {vga_address_y[8:0], vga_address_x[9:0]}; end else begin sram_address = {cpu_address_y[8:0], cpu_address_x[9:0]}; end /* * Get the data being read from the sram. If we're not reading * the sram, sram_out is undefined and should be ignored. */ if (SRAM_LB_N == 1'b0) begin sram_out = SRAM_DQ[7:0]; // lower byte end else begin sram_out = SRAM_DQ[15:8]; // upper byte end end /* * SRAM_DQ needs to be in a separate always @* block from the one used * to compute sram_out. Otherwise the assignment of zzzz will affect the * value assigned to sram_out. */ always @* begin if (SRAM_WE_N == 1'b0) begin SRAM_DQ = {vga_color_write, vga_color_write}; end else begin SRAM_DQ = {16{1'bz}}; end end always @(posedge osc_50) begin /* * Enable lower or upper byte based on sram_address[0]. * Signals are active low. * sram_address[0] == 0 ==> enable lower byte * sram_address[0] == 1 ==> enable upper byte */ SRAM_LB_N <= sram_address[0]; SRAM_UB_N <= ~sram_address[0]; SRAM_ADDR[17:0] <= sram_address[18:1]; SRAM_WE_N <= next_sram_we_n; end /* * CPU path */ always @(posedge osc_50) begin if (clock_valid == 1'b0) begin end else if (cpu_address_load == 1'b1) begin cpu_address_x <= vga_x1; cpu_address_y <= vga_y1; end else if (cpu_address_incr == 1'b1) begin if (cpu_address_x == vga_x2) begin cpu_address_x <= vga_x1; cpu_address_y <= cpu_address_y + 9'd1; end else begin cpu_address_x <= cpu_address_x + 10'd1; end end if (vga_color_read_write == 1'b1) begin vga_color_read <= sram_out; end vga_ack <= next_vga_ack; // register vga_ack to prevent glitches end /* * Main state machine. */ parameter state_reset = 4'h0; parameter state_idle1 = 4'h1; parameter state_idle2 = 4'h2; parameter state_write1 = 4'h3; parameter state_write2 = 4'h4; parameter state_read1 = 4'h5; parameter state_read2 = 4'h6; parameter state_ack1 = 4'h7; parameter state_ack2 = 4'h8; always @* begin next_state = state_idle1; next_vga_clk = 1'b0; vga_horiz_incr = 1'b0; cpu_address_load = 1'b0; cpu_address_incr = 1'b0; address_select = 1'b0; vga_rgb_write = 1'b0; next_sram_we_n = 1'b1; next_vga_ack = 1'b0; vga_color_read_write = 1'b0; if (state == state_reset) begin next_state = state_idle1; end else if (state == state_idle1) begin // VGA_CLK=0 next_vga_clk = 1'b1; cpu_address_load = 1'b1; // get ready in case it's needed if (vga_valid == 1'b0) begin next_state = state_idle2; end else if (vga_write == 1'b0) begin next_state = state_read1; end else if (vga_x1 > vga_x2 || vga_y1 > vga_y2) begin /* * Don't change any pixels for negatively sized rectangle, * but do finish the I/O protocol. */ next_state = state_ack1; end else begin next_state = state_write1; end end else if (state == state_idle2) begin // VGA_CLK=1 vga_horiz_incr = 1'b1; cpu_address_load = 1'b1; // get ready in case it's needed address_select = 1'b1; vga_rgb_write = 1'b1; next_state = state_idle1; end else if (state == state_write1) begin // VGA_CLK=1 vga_horiz_incr = 1'b1; address_select = 1'b1; vga_rgb_write = 1'b1; next_sram_we_n = 1'b0; next_state = state_write2; end else if (state == state_write2) begin // VGA_CLK=0 next_vga_clk = 1'b1; cpu_address_incr = 1'b1; if (cpu_address_x == vga_x2 && cpu_address_y == vga_y2) begin next_state = state_ack1; end else begin next_state = state_write1; end end else if (state == state_read1) begin // VGA_CLK=1 vga_horiz_incr = 1'b1; address_select = 1'b1; vga_rgb_write = 1'b1; next_state = state_read2; end else if (state == state_read2) begin // VGA_CLK=0 next_vga_clk = 1'b1; vga_color_read_write = 1'b1; next_state = state_ack1; end else if (state == state_ack1) begin // VGA_CLK=1 vga_horiz_incr = 1'b1; address_select = 1'b1; vga_rgb_write = 1'b1; next_vga_ack = 1'b1; if (vga_valid == 1'b0) begin next_state = state_idle1; end else begin next_state = state_ack2; end end else if (state == state_ack2) begin // VGA_CLK=0 next_vga_clk = 1'b1; next_vga_ack = 1'b1; next_state = state_ack1; end end always @(posedge osc_50) begin if (clock_valid == 1'b0) begin end else if (reset == 1'b1) begin state <= state_reset; end else begin state <= next_state; end end endmodule