3
\$\begingroup\$

I am at the moment trying to make an vga driver for my FPGA, but something isn't going right, and I can't seem to find out what is going wrong...

The code is based on this code example: Example VGA controller

Here is my code: (I've only changed the resolution and some variable names, other than that it should be exactly the same.)

    ----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date: 03/07/2016 08:53:44 AM
-- Design Name: 
-- Module Name: vga - Behavioral
-- Project Name: 
-- Target Devices: 
-- Tool Versions: 
-- Description: 
-- 
-- Dependencies: 
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
----------------------------------------------------------------------------------


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity vga is
    GENERIC(
            constant HR:integer:=640;--Horizontal Resolution
            constant HFP:integer:=16;--Horizontal Front Porch
            constant HBP:integer:=48;--Horizontal Back Porch
            constant HRet:integer:=96;--Horizontal retrace
            h_pol    :  STD_LOGIC := '0';   --horizontal sync pulse polarity (1 = positive, 0 = negative)

            constant VR:integer:=480;--Vertical Resolution
            constant VFP:integer:=10;--Vertical Front Porch
            constant VBP:integer:=33;--Vertical Back Porch
            constant VRet:integer:=2;--Vertical Retrace); 
            v_pol    :  STD_LOGIC := '0'  --vertical sync pulse polarity (1 = positive, 0 = negative)

            );
    Port ( clk : in STD_LOGIC;
           LED0: out std_logic;
           vga_r: out std_logic_vector(4 downto 0);
           vga_g: out std_logic_vector(5 downto 0);
           vga_b: out std_logic_vector(4 downto 0);
           vga_vs: out std_logic;
           vga_hs: out std_logic);
end vga;

architecture Behavioral of vga is
signal COUNT : integer range 0 to 100000000;
constant x_counter:  integer := HR + HFP + HBP + HRet;   
constant y_counter:  integer := VR + VFP + VBP + VRet;
signal newCounter: integer range 0 to 4 := 0;
signal newClk: std_logic; 
begin

alive: process(CLK)
    begin
        if rising_edge(CLK) then 
           COUNT  <= COUNT + 1;

           if COUNT = 100000000 then 
               COUNT <= 0; 
           end if;

           if COUNT < 100000000/2 then 
           --     LED0 <=  '1';
           else 
           --     LED0 <= '0';
           end if;           
        end if;
end process;

prescaler: process(clk)
begin
    if rising_edge(clk) then 
        newCounter <= newCounter +1; 
            if newCounter = 4  then 
                newClk <= ‘1’;
                newCounter <= 0;
            else    
                newClk <= ‘0’;  
            end if;
    end if; 
end process;




screen:process(newClk)
VARIABLE h_count  :  INTEGER RANGE 0 TO x_counter - 1 := 0;  --horizontal counter (counts the columns)
VARIABLE v_count  :  INTEGER RANGE 0 TO y_counter - 1 := 0;  --vertical counter (counts the rows)
begin
 -- Horizontal 640 
 -- Vertical 480
 if rising_edge(newClk) then
 -- Increase Horizontal counter
    if (h_count < x_counter - 1 ) then 
        h_count := h_count + 1;
    else 
        h_count := 0 ;    
        -- Increase Vertical counter   
        if (v_count < y_counter - 1 ) then 
            v_count := v_count + 1;
        else 
            v_count := 0 ;
        end if; 
    end if; 

     --horizontal sync signal
     IF(h_count < HR + HFP OR h_count > HR + HFP + HRet) THEN
        vga_hs <= NOT h_pol;    --deassert horiztonal sync pulse
        LED0 <= NOT h_pol; 
     ELSE
        vga_hs <= h_pol;        --assert horiztonal sync pulse
        LED0 <= h_pol;
     END IF;

     --vertical sync signal
     IF(v_count < VR + VFP OR v_count > VR + VFP + VRet) THEN
        vga_vs <= NOT v_pol;    --deassert vertical sync pulse
     ELSE
        vga_vs <= v_pol;        --assert vertical sync pulse
     END IF;       
 end if;
 end process;

 vga_r <= "11111";
 vga_g <= "000000";
 vga_b <= "00000";



end Behavioral;
\$\endgroup\$
  • 3
    \$\begingroup\$ "- What happens?" "- It's not working." \$\endgroup\$ – pipe Mar 8 '16 at 19:35
  • \$\begingroup\$ Well.. the screen goes black.. I would expect to display red since all those pins are high \$\endgroup\$ – Carlton Banks Mar 8 '16 at 19:41
  • \$\begingroup\$ Assuming that you've verified that your H and V sync timing and polarity is correct, it's possible that your monitor is outsmarting you -- many modern monitors refuse to display a VGA signal that doesn't have any detectable horizontal and vertical blanking in the analog video channels. \$\endgroup\$ – Dave Tweed Mar 8 '16 at 20:12
  • \$\begingroup\$ The monitor works with VGA - signal, I have tested it with an bit file which was working... The clk freq = 125 mhz The pre scaler scales the clock down to 25 mhz which is those setting (I intended to use) The resolution i am trying to recreate is 640 x 480 \$\endgroup\$ – Carlton Banks Mar 8 '16 at 20:21
  • 1
    \$\begingroup\$ The signal used to provide blanking in the Example VGA controller is disp_ena which tells you when you're not blanked. When disp_ena is not '1' in hw_image_generator.vhd then r,g and b are '0' (black). In an analog display the color is sampled during the horizontal retrace interval to determine where set black. \$\endgroup\$ – user8352 Mar 9 '16 at 0:03
5
\$\begingroup\$

You assign constant values to your VGA color lines, which will not work.

An analog VGA monitor expects to see a reference "black" level during part of the off-screen duration of the trace. If you drive a substantially non-zero value there, it will adapt to the idea that this voltage means black, and you will not get the expected rectangle of color (on some monitors, you might get a brief flash of color upon connection, before going to all black).

You will need to toggle your color lines between the desired color and black during the periods of time when they should be signalling color in the active area or black reference outside of it.

Two very useful tools will be a timing diagram of what VGA should look like, and if possible an oscilloscope comparing your signal to that of a VGA card setup to output something similar (full color screensaver?)

\$\endgroup\$
  • \$\begingroup\$ Soo.. I am not sure quite understand when you want me to toggle the color from one state to another. \$\endgroup\$ – Carlton Banks Mar 8 '16 at 23:07
  • 1
    \$\begingroup\$ The simplest approach is to drive the lines to black anytime the trace is not within the rectangle of the screen that you are trying to paint red. It's quite a bit easier if you just paint a 640x480 rectangle and not try to color the overscan, though with more careful attention to a VGA timing diagram that may(?) be possible. \$\endgroup\$ – Chris Stratton Mar 9 '16 at 0:03
1
\$\begingroup\$

As already mentioned by Chris Stratton et.al., you have to set the color to black during the blanking intervals. This was achieved by the signal disp_ena in the example design linked in your question.

To add such behaviour to your design, you have to:

1) Add a new signal disp_ena in the architecture declaration part:

signal disp_ena : std_logic;

2) Set this signal clock-synchronous to '1' when the image is displayed, and to '0' otherwise (during blanking).

screen:process(newClk)
-- ... existing code
begin
  if rising_edge(newClk) then
     -- ... existing code

     --set display-enable control signal
     IF(h_count < HR and v_count < VR) THEN
        disp_ena <= '1';
     ELSE
        disp_ena <= '0';
     end IF;
  end if;
end process;

3) Set color to red only when display is enabled (disp_ena = '1') otherwise black. Thus, change the existing lines to:

vga_r <= "11111" when disp_ena = '1' else (others => '0');
vga_g <= "000000" when disp_ena = '1' else (others => '0');
vga_b <= "00000" when disp_ena = '1' else (others => '0');

You are generating a new clock signal using a counter. This is not good design practice. You should use one of the PLLs on the FPGA instead. You can create a proper instantiation via the Xilinx IP Core Generator.

If you want to stick with the counter, then you have two options:

1) Use a BUFG component to minimize the skew on the clock network newclk. To use this component, you need to include:

library unisim;
use unisim.vcomponents.all;

Then instantiate the BUFG via:

newclk_bufg: BUFG (I => newclk, O => newclk2);

Then use newclk2 to drive the registers.

2) Use newclk as a clock-enable signal instead of a clock signal. The screen process will then trigger on CLK, but loading the new register values will happen only if newclk is high:

screen:process(CLK)
-- ... existing code
begin
  if rising_edge(CLK) then
     if newclk = '1' then   -- newclk used as clock-enable here
        -- ... existing code
     end if;
  end if;
end process;

Also check if you have specified the correct timing constraints for the clock CLK in the UCF file (ISE) or XDC file (Vivado).

\$\endgroup\$
  • \$\begingroup\$ still not working.. \$\endgroup\$ – Carlton Banks Mar 14 '16 at 11:59
  • \$\begingroup\$ @dfh the generation of newclk might be a problem as now discussed in my extended answer. \$\endgroup\$ – Martin Zabel Mar 14 '16 at 16:08
  • \$\begingroup\$ Well.. code posted above doesn't show anything properly. The screen says that the input is not supported. \$\endgroup\$ – Carlton Banks Mar 14 '16 at 18:32
  • \$\begingroup\$ Which solution for newclk have you tried? Does the screen says anything about a wrong frequency? Are you sure, that the input frequency for your FPGA is 125 MHz? \$\endgroup\$ – Martin Zabel Mar 14 '16 at 20:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.