VHDL VGA интерфейс
Я моделировал VGA-интерфейс на плате DE0. У меня есть следующая модель для дисплея 640x480, который обновляется с частотой 60 Гц:
Основная модель:
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;
Синхронизировать модель:
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----------
Другой прямой пример - для PLL, сгенерированного из Quartus II, он работает нормально... спасибо, мистер Зилмер:). Модель компилируется нормально. Я загружаю его в DE0. Затем подключите его к монитору и ничего не отобразите на дисплее. Он должен отображать крест в центре экрана. Я использую дисплей Samsung с разрешением 1920x1080. Остановит ли это мою модель от отображения чего-либо? Или я сделал очевидную ошибку в моей модели. Я изменил некоторые стандартные значения синхронизации, чтобы соответствовать частоте обновления 60 Гц с 25 МГц clk. Спасибо д
1 ответ
Ваш код VHDL для синхронизации сущностей не анализируется. Вам не хватает end if
и ваши начальные значения для h_sync
а также v_sync
нарушать ограничение подтипа:
signal h_pos: integer range 1 to 800 := 0;
signal v_pos: integer range 1 to 520 := 0;
куда 0
находится за пределами 1 to 800
или же 1 to 520
,
Это поднимает вопрос о том, есть ли у вас другая архитектура для синхронизации сущностей, или же синхронизация просто не связана. Любой из них может дать вам неправильное указание (и ошибка не продемонстрирована в вашем вопросе).
Мы можем использовать тестовый стенд, чтобы продемонстрировать, что делает синхронизация в симуляции с тактовой частотой 25 МГц:
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;
И теперь мы можем устранить неполадки:
Первое, что мы заметили, это то, что h_sync не так. Также обратите внимание, что значение v_sync составляет около 16.667 мс (1/60 секунды).
Мы можем добавить счетчики h_pos и v_pos, чтобы мы могли посмотреть на h_sync, мы знаем, что счетчик h_pos работает, чтобы получить v_sync, который мы можем видеть в правой окрестности 60 Гц.
Поэтому я выбрал не то место, чтобы добавить end if
, Исправление, которое также отделяет работу счетчиков от работы на их выходах (h_sync, v_sync и 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;
У нас сейчас h_sync:
Обратите внимание, что теперь мы можем видеть вертикальный интервал гашения.
При увеличении мы видим горизонтальную белую линию в v_pos 280:
Наряду с вертикальной линией на h_pos 480 (+1):
И похоже, что это может сработать.
Единственное изменение дизайна, которое я мог бы испытать, это начать видимую часть строки с h_pos = 0, а видимую часть кадра - с v_pos = 0. Это позволило бы что-то еще адресовать пикселям для записи в кадре. буфер без добавления дополнительных счетчиков пикселей или необходимости делать смещение арифметики. (Пиксельная адресация обычно начинается с 0 для осей x и y).