VGA controller with VHDL

13,299

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.

Share:
13,299
osuarez
Author by

osuarez

Updated on June 17, 2022

Comments

  • osuarez
    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;