VGA controller with VHDL
See full details on VGA theory and VHDL code for VGA controllers in chapter 15 of "Circuit Design and Simulation with VHDL", 2nd edition (only in the 2nd edition), 2010, by V. A. Pedroni.
osuarez
Updated on June 17, 2022Comments
-
osuarez about 2 years
I'm trying to learn VHDL programming with some books and an Altera DE1 development kit from Terasic.
The issue here is that I am trying to program a VGA controller for work with a resolution of 640x480 (although my screen is an TFT LCD with 1280x1024).
I am having some problems with the code.
I am using a FSM to make the vertical and horizontal signals, another block to drive the RGB inputs and also a 27 MHz clock from the DE1.
I think there has to be something wrong with the code because the image I print on the screen is not the correct size (640x480) but bigger (approx. 1174x980).
By now I am trying to put one only color in the screen to simplify it until I can discover the mistake.
My project has 3 files, 1 for the block RGB, 1 for the FSM and another one to instantiate both of them.
I would appreciate some kind of help to solve this problem because I have tried hard to figure it out but I can't see where are the mistakes.
Thanks a lot!
Omar
VGA Controller file
library ieee; use ieee.std_logic_1164.all; entity VGA_controller is port(clk, reset : in std_logic; Hsync,Vsync : out std_logic; R,G,B : out std_logic_vector(3 downto 0)); end entity; architecture arch of VGA_controller is component FSM_sync is port(clk,reset : in std_logic; Hsync,Vsync,VIDON : out std_logic; Vcount,Hcount : out integer range 0 to 799); end component; component VGA_display is port(hcount,vcount : in integer range 0 to 799; r,g,b : out std_logic_vector(3 downto 0); video_on : in std_logic); end component; signal video : std_logic; signal signal1 : integer range 0 to 799; signal signal2 : integer range 0 to 799; begin maquinaestado_sync : FSM_sync port map (clk=>clk, reset=>reset, Hsync=>Hsync, Vsync=>Vsync, Vcount=>signal1, Hcount=>signal2, VIDON=>video); salida_pantalla : VGA_display port map (r=>R, g=>G, b=>B, video_on=>video, vcount=>signal1, hcount=>signal2 ); end arch;
FSM sync file
library ieee; use ieee.std_logic_1164.all; entity FSM_sync is port(clk,reset : in std_logic; Hsync,Vsync,VIDON : out std_logic; Vcount,Hcount : out integer range 0 to 799); end entity; architecture arch of FSM_sync is --constantes para definir los ciclos de reloj de cada señal del HSYNC constant counterMAX : integer := 640; constant counterSP : integer := 96; constant counterBP : integer := 48; constant counterHV : integer := 640; constant counterFP : integer := 16; --constantes para definir los ciclos de reloj de cada señal del VSYNC constant counterMAX_V : integer := 384000; -- calculamos estos valores multiplicando el numero de filas por los pixeles en cada fila horizontal (800) constant counterSP_V : integer := 1600; -- de manera que cada estado de la sincronizacion vertical dure todo el recorrido de los pixeles de cada fila constant counterBP_V : integer := 26400; constant counterVV : integer := 384000; constant counterFP_V : integer := 8000; --constantes para el numero de pixeles maximo que debemos controlar en horizontal y en vertical constant number_pixelsMAX_H : integer := 800; constant number_pixelsMAX_V : integer := 525; type state is (SP_1,BP,HV,FP,reseteo); --4 estados para cada maquina de estado de sincronizacion (vertical y horizontal) signal present_state_H,next_state_H,present_state_V,next_state_V : state; signal timer : integer range 0 to counterMAX ; -- señal para pasar el valor counterXX al proceso secuencial para compararlo con un contador y establecer el -- tiempo de duracion de cada estado signal timer2 : integer range 0 to counterMAX_V ; --lo mismo que la señal anterior pero para el sincronizacion vertical signal video_1,video_2 : std_logic; signal hcount_reg,vcount_reg : integer range 0 to 799; begin --============================================== --FSM para la sincronizacion del barrido HORIZONTAL --=============================================== lower_part_1 : process (clk,reset) variable counter : integer range 0 to counterMAX - 1; --variable para crear un contador de pulsos del clk variable counter2 : integer range 0 to number_pixelsMAX_H - 1; --contador para los pixeles horizontales variable counter3 : integer range 0 to number_pixelsMAX_V - 1; --contador para los pixeles verticales begin --se cargan con 800 por que 800 son los pixeles que hay que leer en horizontal if (reset = '1') then --esto implica contadores de al menos 10 bits para llegar a ese numero. --y para que los dos contadores sean del mismo numero de bits se cargan los dos igual counter := 0; --realmente en vertical solo debemos contar hasta 521 counter2 := 0; counter3 := 0; present_state_H <= reseteo; elsif (clk'event and clk = '1') then counter := counter + 1; if (counter2 < number_pixelsMAX_H-1) then counter2 := counter2 + 1; else counter2 := 0; if (counter3 < number_pixelsMAX_V-1) then counter3 := counter3 + 1; else counter3 := 0; end if; end if; hcount_reg <= counter2; vcount_reg <= counter3; if (counter = timer) then present_state_H <= next_state_H; counter := 0; end if; end if; end process lower_part_1; upper_part_1 : process (next_state_H) begin Hsync <= '1'; next_state_H <= HV; case present_state_H is when SP_1 => Hsync <= '0'; next_state_H <= BP; timer <= counterSP; video_1 <= '0'; when BP => Hsync <= '1'; next_state_H <= HV; timer <= counterBP; video_1 <= '0'; when HV => Hsync <= '1'; next_state_H <= FP; timer <= counterHV; video_1 <= '1'; when FP => Hsync <= '1'; next_state_H <= SP_1; timer <= counterFP; video_1 <= '0'; when reseteo => Hsync <= '1'; next_state_H <=HV; end case; end process upper_part_1; --============================================== --FSM para la sincronizacion del barrido VERTICAL --=============================================== lower_part_2 : process (clk,reset) variable counter2 : integer range 0 to counterMAX_V; --variable para crear un contador de pulsos del clk begin if (reset = '1') then counter2 := 0; present_state_V <= reseteo; elsif (clk'event and clk = '1') then counter2 := counter2 + 1; if (counter2 = timer2) then present_state_V <= next_state_V; counter2 := 0; end if; end if; end process lower_part_2; upper_part_2 : process (next_state_V) begin Vsync <= '1'; next_state_V <= HV; case present_state_V is when SP_1 => Vsync <= '0'; next_state_V <= BP; timer2 <= counterSP_V; video_2 <= '0'; when BP => Vsync <= '1'; next_state_V <= HV; timer2 <= counterBP_V; video_2 <= '0'; when HV => Vsync <= '1'; next_state_V <= FP; timer2 <= counterVV; video_2 <= '1'; when FP => Vsync <= '1'; next_state_V <= SP_1; timer2 <= counterFP_V; video_2 <= '0'; when reseteo => Vsync <= '1'; next_state_V <=HV; end case; end process upper_part_2; VIDON <= video_1 AND video_2; Vcount <= vcount_reg; Hcount <= hcount_reg; end arch;
VGD display file
library ieee; use ieee.std_logic_1164.all; entity VGA_display is port(hcount,vcount : in integer range 0 to 799; r,g,b : out std_logic_vector(3 downto 0); video_on : in std_logic); end entity; architecture arch of VGA_display is begin process (video_on) begin if video_on = '1' then --solo activamos los pixeles cuando vidon esté a uno, es decir, esten en la fase HV y VV las sincronizaciones r <= "1111"; g <= "0000"; b <= "0000"; else r <= (others => '0'); g <= (others => '0'); b <= (others => '0'); end if; end process; end arch;