Reputation: 854
I've been modelling a VGA interface on the DE0 board. I have the following model for a 640x480 display which refreshes at 60Hz:
Main model:
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
ENTITY VGA is
PORT (clk : IN std_logic; -- demo had 2 bit vector
vga_hs, vga_vs : OUT std_logic;
vga_r, vga_g, vga_b : OUT std_logic_vector(3 DOWNTO 0));
END ENTITY VGA;
ARCHITECTURE A1 OF VGA IS
SIGNAL rst, clk25 : std_logic; -- rst only resets pixel clock
BEGIN
SYNC1 : ENTITY work.sync(A1)
PORT MAP (clk25, vga_hs, vga_vs, vga_r, vga_g, vga_b);
CLK_25 : ENTITY work.PLL(rtl)
PORT MAP (clk, rst, clk25);
END ARCHITECTURE A1;
Sync model:
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
ENTITY SYNC IS
PORT(
clk : IN std_logic;
h_sync, v_sync : OUT std_logic;
r, g, b : OUT std_logic_vector(3 DOWNTO 0)
);
END ENTITY SYNC;
ARCHITECTURE A1 OF SYNC IS
SIGNAL h_pos: integer RANGE 0 TO 800:=0;
SIGNAL v_pos : integer RANGE 0 TO 520:=0;
BEGIN
TIMING :PROCESS(clk) IS
BEGIN
IF rising_edge(clk) THEN
IF (h_pos = 480 or v_pos = 280) THEN -- middle of the screen is pic res/2 + (FP + sync + BP)
r <= (OTHERS => '1');
g <= (OTHERS => '1');
b <= (OTHERS => '1');
ELSE
r <= (OTHERS => '0');
g <= (OTHERS => '0');
b <= (OTHERS => '0');
END IF;
IF (h_pos < 800) THEN
h_pos <= h_pos + 1;
ELSE
h_pos <= 1;
IF (v_pos < 520) THEN
v_pos <= v_pos + 1;
ELSE
v_pos <= 1;
END IF;
END IF;
IF (h_pos > 16 and h_pos < 112 ) THEN -- H_POS between end of FP and the end of H_SYNC
h_sync <= '0'; -- H_SYNC needs to stay low during display
ELSE
h_sync <= '1';
END IF;
IF (v_pos > 8 and v_pos < 10 ) THEN --V_POS between end of FP and the end of V_SYNC
v_sync <= '0'; -- V_SYNC needs to stay low during display
ELSE
v_sync <= '1';
END IF;
IF ((h_pos > 0 and h_pos < 160) or (v_pos > 0 and v_pos < 40 )) THEN--During all of SYNC i.e FP + SYNC + BP colour signals stay low
r <= (OTHERS => '0');
g <= (OTHERS => '0');
b <= (OTHERS => '0');
END IF;
END IF;
END PROCESS TIMING;
END ARCHITECTURE A1;
----------Amendments made to model 09/02 13:42----------
The other direct instantiation is for PLL generated from Quartus II it seems to work fine.. thank you Mr Zilmer :). The model compiles fine. I load it into the DE0. Then connect this to a monitor and get nothing on the display. It should display a cross in the centre of the screen. The display I am using is a Samsung with 1920x1080. Would this stop my model from displaying anything? Or have I made a obvious mistake in my model. I have changed some of the standard timing values to fit a 60Hz refresh with 25Mz clk. Thanks D
Upvotes: 0
Views: 803
Reputation:
Your VHDL code for entity sync doesn't analyze. You are missing an end if
and your initial values for h_sync
and v_sync
violate the subtype constraint:
signal h_pos: integer range 1 to 800 := 0;
signal v_pos: integer range 1 to 520 := 0;
Where 0
is outside the bounds of 1 to 800
or 1 to 520
.
This raises the question of whether or not you have another architecture for entity sync, or whether sync is simply unbound. Either of which might give you an incorrect indication (and the error isn't demonstrated in your question).
We can use a testbench to demonstrate what sync does in simulation with a 25 MHz clock:
library ieee;
use ieee.std_logic_1164.all;
--use ieee.numeric_std.all;
entity sync is
port (
clk: in std_logic;
h_sync, v_sync: out std_logic;
r, g, b: out std_logic_vector(3 downto 0)
);
end entity sync;
architecture a1 of sync is
signal h_pos: integer range 1 to 800 := 1; -- was := 0;
signal v_pos: integer range 1 to 520 := 1; -- was := 0;
begin
timing:
process (clk) is
begin
if rising_edge(clk) then
if h_pos = 480 or v_pos = 280 then -- middle of the screen
r <= (others => '1');
g <= (others => '1');
b <= (others => '1');
else
r <= (others => '0');
g <= (others => '0');
b <= (others => '0');
end if;
if h_pos < 800 then
h_pos <= h_pos + 1;
else
h_pos <= 1;
if v_pos < 520 then
v_pos <= v_pos + 1;
else
v_pos <= 1;
end if;
if h_pos > 16 and h_pos < 112 then
h_sync <= '0'; -- h_sync low during display
else
h_sync <= '1';
end if;
if v_pos > 8 and v_pos < 10 then
v_sync <= '0'; -- v_sync low during display
else
v_sync <= '1';
end if;
if (h_pos > 1 and h_pos < 160) or
(v_pos > 1 and v_pos < 40 ) then -- black during blanking
r <= (others => '0');
g <= (others => '0');
b <= (others => '0');
end if;
end if;
end if; -- added misssing end if
end process timing;
end architecture a1;
library ieee;
use ieee.std_logic_1164.all;
entity sync_tb is
end entity;
architecture foo of sync_tb is
signal clk: std_logic := '0';
signal h_sync: std_logic;
signal v_sync: std_logic;
signal r, g, b: std_logic_vector (3 downto 0);
begin
DUT:
entity work.sync
port map (
clk => clk,
h_sync => h_sync,
v_sync => v_sync,
r => r,
g => g,
b => b
);
CLOCK:
process
begin
wait for 20 ns; -- clock period 25 MHz = 40 ns;
clk <= not clk;
if now > 20 ms then -- one frame time plus a bit
wait;
end if;
end process;
end architecture;
And now we get to troubleshoot:
The first thing we notice is h_sync is wrong. Also note that v_sync appears to be around 16.667 ms (1/60th of a second).
We can add the h_pos and v_pos counters so we can look at h_sync, we do know the h_pos counter is running to get a v_sync we can see in the right neighborhood of 60 Hz.
So I picked the wrong place to add the end if
. Correcting that also separates operating the counters from operating on their outputs (h_sync, v_sync and r,g,b).
timing:
process (clk) is
begin
if rising_edge(clk) then
if h_pos = 480 or v_pos = 280 then -- middle of the screen
r <= (others => '1');
g <= (others => '1');
b <= (others => '1');
else
r <= (others => '0');
g <= (others => '0');
b <= (others => '0');
end if;
if h_pos < 800 then
h_pos <= h_pos + 1;
else
h_pos <= 1;
if v_pos < 520 then
v_pos <= v_pos + 1;
else
v_pos <= 1;
end if;
end if; -- separate the counters for what they produce
-- HSYNC
if h_pos > 16 and h_pos < 112 then
h_sync <= '0'; -- h_sync low during display
else
h_sync <= '1';
end if;
-- VSYNC
if v_pos > 8 and v_pos < 10 then
v_sync <= '0'; -- v_sync low during display
else
v_sync <= '1';
end if;
-- BLANKING
if (h_pos > 1 and h_pos < 160) or
(v_pos > 1 and v_pos < 40 ) then
r <= (others => '0');
g <= (others => '0');
b <= (others => '0');
end if;
end if;
end process timing;
We have h_sync now:
Notice we can see the vertical blanking interval now too.
Zooming in we can see there a horizontal white line at v_pos 280:
Along with a vertical line at h_pos 480 (+1):
And this looks like it just might work.
The only design change I might be tempted to make would be to start the visible portion of the line at h_pos = 0 and the the visible portion of the frame at v_pos = 0. This would allow something else to address pixels for writing in a frame buffer without adding additional pixel counters or having to do offset arithmetic. (Pixel addressing usually starts at 0 for x and y axis).
Upvotes: 4