📄 vga_driver.vhd
字号:
-- ================================================================================
-- (c) 2005 Altera Corporation. All rights reserved.
-- Altera products are protected under numerous U.S. and foreign patents, maskwork
-- rights, copyrights and other intellectual property laws.
--
-- This reference design file, and your use thereof, is subject to and governed
-- by the terms and conditions of the applicable Altera Reference Design License
-- Agreement (either as signed by you, agreed by you upon download or as a
-- "click-through" agreement upon installation andor found at www.altera.com).
-- By using this reference design file, you indicate your acceptance of such terms
-- and conditions between you and Altera Corporation. In the event that you do
-- not agree with such terms and conditions, you may not use the reference design
-- file and please promptly destroy any copies you have made.
--
-- This reference design file is being provided on an "as-is" basis and as an
-- accommodation and therefore all warranties, representations or guarantees of
-- any kind (whether express, implied or statutory) including, without limitation,
-- warranties of merchantability, non-infringement, or fitness for a particular
-- purpose, are specifically disclaimed. By making this reference design file
-- available, Altera expressly does not recommend, suggest or require that this
-- reference design file be used in combination with any other product not
-- provided by Altera.
--================================================================================
--
-- This design implements an 8-bit 640x480 VGA driver. The driver sources
-- it's data from a 256 word buffer. The frame buffer holds 32-bit
-- words therefore each word read from the frame buffer will contain 4 8-bit pixels.
-- The buffer can therefore hold 1024 pixels at a time.
--
-- For more details on VGA timing view the image_package.vhd file.
--
--
LIBRARY ieee,work;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_unsigned.ALL;
USE ieee.std_logic_arith.ALL;
USE work.image_package.ALL;
ENTITY vga_driver IS
PORT (
reset_n : IN std_logic;
clk : IN std_logic; -- Avalon clock
clock25 : IN std_logic; -- 25 MHz clock to run VGA internals
vga_clk : IN std_logic; -- 25 MHz clock to drive to Video DAC, could be tied to clock25
-- control signals
enable_irq : IN std_logic;
enable : IN std_logic;
mode : IN std_logic;
-- video memory signals
video_data : IN std_logic_vector(15 downto 0);
video_address : OUT std_logic_vector(31 downto 0);
-- parameter signals from avalon slave interface
num_lines : IN std_logic_vector(9 downto 0);
num_pixels_per_line : IN std_logic_vector(9 downto 0);
set_picture_end_irq : OUT std_logic;
-- blanking outputs to any external logic that might need it
hblank : OUT std_logic;
vblank : OUT std_logic;
-- outputs to VGA daughter card
clockext : OUT std_logic;
sync_n : OUT std_logic := '0';
sync_t : OUT std_logic := '0';
blank_n : OUT std_logic := '0';
M1 : OUT std_logic := '0';
M2 : OUT std_logic := '0';
vsync : OUT std_logic;
hsync : OUT std_logic;
R : OUT std_logic_vector(7 downto 0);
G : OUT std_logic_vector(7 downto 0);
B : OUT std_logic_vector(7 downto 0)
);
END vga_driver;
ARCHITECTURE rtl OF vga_driver IS
SIGNAL pixel_counter : std_logic_vector(9 downto 0);
SIGNAL line_counter : std_logic_vector(9 downto 0);
SIGNAL video_address_sig : std_logic_vector(31 downto 0);
SIGNAL video_address_sig_delay : std_logic;
SIGNAL stored_address : std_logic_vector(31 downto 0);
SIGNAL vblank_sig : std_logic;
SIGNAL vblank_delay : std_logic;
SIGNAL hblank_sig : std_logic;
SIGNAL clockext_sig : std_logic;
SIGNAL pixel_value : std_logic_vector(23 downto 0);
-- These signals determine the location of an images smaller than
-- 640x480 on the screen
SIGNAL x_offset : std_logic_vector(9 downto 0);
SIGNAL y_offset : std_logic_vector(9 downto 0);
SIGNAL active : std_logic;
SIGNAL active_delay : std_logic;
SIGNAL actual_num_lines : std_logic_vector(9 downto 0);
SIGNAL actual_num_pixels_per_line : std_logic_vector(9 downto 0);
TYPE TI8134B_state IS (initial, config1, config2, config3, config4);
SIGNAL TI8134B_config_state : TI8134B_state;
SIGNAL gnd : std_logic;
SIGNAL gnd_vector : std_logic_vector(31 downto 0);
BEGIN
gnd <= '0';
gnd_vector <= (others => '0');
PROCESS(video_address_sig,mode,num_lines,num_pixels_per_line)
BEGIN
IF mode = '0' THEN -- normal mode
video_address <= video_address_sig;
actual_num_lines <= num_lines;
actual_num_pixels_per_line <= num_pixels_per_line;
ELSE -- double pixel mode
video_address <= '0' & video_address_sig(31 downto 1);
actual_num_lines <= num_lines(8 downto 0) & '0';
actual_num_pixels_per_line <= num_pixels_per_line(8 downto 0) & '0';
END IF;
END PROCESS;
hblank <= hblank_sig;
vblank <= vblank_sig;
clockext <= vga_clk;
-- Drive the blanking signal to the TI8134B Video DAC
PROCESS(clock25, reset_n)
BEGIN
IF reset_n = '0' THEN
blank_n <= '0';
ELSIF rising_edge(clock25) THEN
blank_n <= hblank_sig AND vblank_sig;
END IF;
END PROCESS;
-- Configure the TI8134B Video DAC on power-up
PROCESS(clock25,reset_n)
BEGIN
IF reset_n = '0' THEN
TI8134B_config_state <= initial;
sync_n <= '0';
M1 <= '0';
M2 <= '0';
sync_t <= '0';
ELSIF rising_edge(clock25) THEN
CASE TI8134B_config_state IS
WHEN initial =>
TI8134B_config_state <= config1;
sync_n <= '0';
M1 <= '0';
M2 <= '0';
sync_t <= '0';
WHEN config1 =>
TI8134B_config_state <= config2;
sync_n <= '1';
M1 <= '0';
M2 <= '0';
sync_t <= '0';
WHEN config2 =>
TI8134B_config_state <= config3;
sync_n <= '0';
M1 <= '0';
M2 <= '1';
sync_t <= '0';
WHEN config3 =>
TI8134B_config_state <= config4;
sync_n <= '0';
M1 <= '0';
M2 <= '1';
sync_t <= '0';
WHEN config4 =>
TI8134B_config_state <= config4;
sync_n <= '1';
M1 <= '0';
M2 <= '0';
sync_t <= '0';
WHEN others =>
TI8134B_config_state <= config4;
sync_n <= '1';
M1 <= '0';
M2 <= '0';
sync_t <= '0';
END CASE;
END IF;
END PROCESS;
-- This is a roll-over counter which will count a period
-- from 0 to HWIDTH. It is used to update the pixels as
-- horizontally across the screen.
pixel_count: PROCESS(clock25,reset_n)
BEGIN
IF reset_n = '0' THEN
pixel_counter <= (others => '0');
ELSIF rising_edge(clock25) THEN
IF pixel_counter < HWIDTH THEN
pixel_counter <= pixel_counter + 1;
ELSE
pixel_counter <= (others => '0');
END IF;
END IF;
END PROCESS;
-- This counter keeps track of the current line being displayed
-- on the screen. It resets when it reaches a value of VDEPTH.
-- The line counter is enabled whenever the pixel counter has
-- reached a value of END_HSYNC
line_count: PROCESS(clock25,reset_n)
BEGIN
IF reset_n = '0' THEN
line_counter <= (others => '0');
ELSIF rising_edge(clock25) THEN
IF pixel_counter = END_HSYNC THEN
IF line_counter < VDEPTH THEN
line_counter <= line_counter + 1;
ELSE
line_counter <= (others => '0');
END IF;
END IF;
END IF;
END PROCESS;
-- The driver supports images that are smaller than 640x480. It looks a little
-- nicer to display smaller images directly in the center of the screen so we
-- need to calculate the offset in both the x and y domains.
PROCESS(clock25,reset_n)
VARIABLE x_offset_reg : std_logic_vector(9 downto 0);
VARIABLE y_offset_reg : std_logic_vector(9 downto 0);
BEGIN
IF reset_n = '0' THEN
x_offset_reg := (others => '0');
y_offset_reg := (others => '0');
ELSIF rising_edge(clock25) THEN
IF vblank_sig = '0' THEN
x_offset_reg := LINE_WIDTH - actual_num_pixels_per_line;
y_offset_reg := NUMBER_LINES - actual_num_lines;
END IF;
END IF;
-- divide offsets by 2 to balance borders on each side of image
x_offset <= '0' & x_offset_reg(9 downto 1);
y_offset <= '0' & y_offset_reg(9 downto 1);
END PROCESS;
-- Generation of the hsync and hblank pulses and address signal to line buffer
-- *** Important Note ***
-- This vga driver pulls images from a line buffer therefore the video adddress
-- signal to the line buffer will get reset after each line has been transmitted.
-- If at some point a frame buffer is needed instead of a line buffer then this
-- process will need to be modified such that the address gets reset on every
-- frame instead of on every line.
hsync_gen: PROCESS(clock25,reset_n)
BEGIN
IF reset_n = '0' THEN
hsync <= '0';
hblank_sig <= '0';
video_address_sig <= (others => '0');
active <= '0';
vblank_sig <= '0';
video_address_sig_delay <= '0';
ELSIF rising_edge(clock25) THEN
video_address_sig_delay <= video_address_sig(0);
-- generate the blanking signal and the address to the line buffer
IF (pixel_counter >= BEGIN_HBLANK AND pixel_counter < END_HBLANK) THEN
hblank_sig <= '1';
ELSE
hblank_sig <= '0';
END IF;
-- Generate the active signal. This signal indicates when we are driving a pixel
-- out. The active singal would normally be asserted for the duration of hblank
-- for a 640x480 image. However, for smaller images we want to wrap a black border
-- around the image. Therefore the active signal will be of shorter duration than
-- the hblank signal when transmitting smaller images.
IF (line_counter > (CONV_STD_LOGIC_VECTOR(BEGIN_VBLANK,10) + y_offset) AND line_counter <= (CONV_STD_LOGIC_VECTOR(END_VBLANK,10) - y_offset)) THEN
IF (pixel_counter > (CONV_STD_LOGIC_VECTOR(BEGIN_HBLANK,10) + x_offset-2) AND pixel_counter <= (CONV_STD_LOGIC_VECTOR(END_HBLANK,10) - x_offset-2)) THEN
-- This is an active window to increment the address counter
active <= '1';
video_address_sig <= video_address_sig + 1;
ELSE
active <= '0';
-- at the end of a line reset the address to the line buffer
video_address_sig <= (others => '0');
END IF;
ELSE
active <= '0';
video_address_sig <= (others => '0');
END IF;
IF (line_counter > (CONV_STD_LOGIC_VECTOR(BEGIN_VBLANK,10) + y_offset) AND line_counter <= (CONV_STD_LOGIC_VECTOR(END_VBLANK,10) - y_offset-1)) THEN
vblank_sig <= '1';
-- make sure we drop the vblank signal before the next hsync starts
ELSIF line_counter = (CONV_STD_LOGIC_VECTOR(END_VBLANK,10) - y_offset) AND pixel_counter /= end_hsync THEN
vblank_sig <= '1';
ELSE
vblank_sig <= '0';
END IF;
-- Generate the hsync signal to send the the VGA daughter card
IF (pixel_counter < END_HSYNC) THEN
hsync <= '0';
ELSE
hsync <= '1';
END IF;
END IF;
END PROCESS;
-- set the irq on the falling edge of vblank. This indicate the last pixel of the image
-- has been read from the frame buffer.
PROCESS(clock25,reset_n)
BEGIN
IF reset_n = '0' THEN
set_picture_end_irq <= '0';
vblank_delay <= '0';
ELSIF rising_edge(clock25) THEN
vblank_delay <= vblank_sig;
IF enable_irq = '1' THEN
IF vblank_sig = '0' AND vblank_delay = '1' THEN
set_picture_end_irq <= '1';
ELSE
set_picture_end_irq <= '0';
END IF;
ELSE
set_picture_end_irq <= '0';
END IF;
END IF;
END PROCESS;
-- Generate the vertical synchronization and blanking signals
vsync_gen: PROCESS(clock25,reset_n)
BEGIN
IF reset_n = '0' THEN
vsync <= '0';
ELSIF rising_edge(clock25) THEN
-- If the hsync signal is transitioning from a 1 to a 0 then...
IF pixel_counter = end_hsync THEN
IF (line_counter < END_VSYNC) THEN
vsync <= '0';
ELSE
vsync <= '1';
END IF;
END IF;
END IF;
END PROCESS;
-- This process generates the RGB signals. Whenever the active signal
-- is asserted the process will drive the video_data out onto the RGB
-- signals. If active is low the RGB signals will be driven to black.
-- The 16-bit video_data signal is also converted to a 24-bit RGB
-- value by stuffing 1's onto the LSBs of the RGB signals.
color_generator: PROCESS(reset_n,clock25)
BEGIN
IF reset_n = '0' THEN
R <= (others => '0');
G <= (others => '0');
B <= (others => '0');
active_delay <= '0';
ELSIF rising_edge(clock25) THEN
active_delay <= active;
IF active_delay = '1' AND enable = '1' THEN
R <= video_data(15 downto 11) & "111";
G <= video_data(10 downto 5) & "11";
B <= video_data(4 downto 0) & "111";
ELSE
R <= X"00";
G <= X"00";
B <= X"00";
END IF;
END IF;
END PROCESS;
END rtl;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -