| The Spartan 3e "S3E Sample Pack" board, a freebie I picked up a few years back. |
The design is very straightforward. I wanted it to be modular and configurable, with the least duplication of code. With minimal modifications it should be able to generate 800x600 instead of 640x480. The only inputs (for now) are a clock signal and a reset signal. I did want to have the current row and column available to me so I can generate a pattern.
![]() |
| System overview, reset signal not shown. |
The column and row sync generators are basically the same. For a given number of input clock cycles (for the row or vertical sync generator, the horizontal sync is the clock), pull the sync signal low, wait for a short time (the back porch), then have the signal displayed, then allow for a short time before going to the next line/frame (the front porch).
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity subsync is
Generic (
sync : integer;
bporch : integer;
active : integer;
total : integer );
Port (
clock : in STD_LOGIC;
reset : in STD_LOGIC;
outsig : out STD_LOGIC;
inhibit : out STD_LOGIC;
outcount : out STD_LOGIC_VECTOR (9 downto 0));
end subsync;
architecture Behavioral of subsync is
signal counter : integer range 0 to total;
signal subcount : std_logic_vector (9 downto 0);
begin
process (reset, clock, counter)
begin
if reset = '1' then
counter <= 0;
inhibit <= '1';
outsig <= '1';
else
if rising_edge(clock) then
if counter < sync then
outsig <= '0';
counter <= counter+1;
elsif counter < sync+bporch-1 then
outsig <= '1';
subcount <= "1111111111"; -- start at -1
counter <= counter+1;
elsif counter < sync+bporch+active-1 then
subcount <= subcount+'1';
counter <= counter+1;
inhibit <= '0';
elsif counter < total-1 then
counter <= counter+1;
inhibit <= '1';
else
counter <= 0;
end if;
end if;
end if;
end process;
outcount <= subcount;
end Behavioral;
Note the sync signal is negative, for standard VGA resolutions. Also note that the "inhibit" signal could be called "NOT active". This sybsync block is instantiated twice in the higher block, which also defines the timing; for the horizontal sync, timing is in clock cycles, for the vertical sync, in lines.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity VGAsync is
Generic (
h_sync : integer := 95;
h_bporch : integer := 10;
h_active : integer := 640;
h_total : integer := 800;
v_sync : integer := 2;
v_bporch : integer := 25;
v_active : integer := 480;
v_total : integer := 525);
Port (
clock : in STD_LOGIC;
Hsync : out STD_LOGIC;
Vsync : out STD_LOGIC;
reset : in STD_LOGIC;
inhibit : out STD_LOGIC;
row : out STD_LOGIC_VECTOR (9 downto 0);
col : out STD_LOGIC_VECTOR (9 downto 0));
end VGAsync;
architecture Behavioral of VGAsync is
signal local_hsync : std_logic;
signal h_inhibit : std_logic;
signal v_inhibit : std_logic;
component subsync is
generic (
sync : integer;
bporch : integer;
active : integer;
total : integer );
port (
clock : in std_logic;
reset : in std_logic;
outsig : out std_logic;
inhibit : out std_logic;
outcount : out std_logic_vector (9 downto 0));
end component;
begin
hs : subsync
generic map (
sync => h_sync, bporch => h_bporch, active => h_active, total => h_total )
port map (
clock => clock, reset => reset, outsig => local_hsync,
inhibit => h_inhibit, outcount => col );
vs : subsync
generic map (
sync => v_sync, bporch => v_bporch, active => v_active, total => v_total )
port map (
clock => local_hsync, reset => reset, outsig => Vsync,
inhibit => v_inhibit, outcount => row );
inhibit <= h_inhibit or v_inhibit;
Hsync <= local_hsync;
end Behavioral;
The only logic in this block is to combine the two inhibit signals. Finally, there is the top block, with a quickly thrown together test pattern generator:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;
entity VGAtop is
Port (
MemAddress : out STD_LOGIC_VECTOR (22 downto 1);
MemData : in STD_LOGIC_VECTOR (15 downto 0);
MemOE : out STD_LOGIC;
VGA_HSync : out STD_LOGIC;
VGA_VSync : out STD_LOGIC;
VGA_out : out STD_LOGIC_VECTOR (1 downto 0);
clock_in : in STD_LOGIC;
reset : in STD_LOGIC);
end VGAtop;
architecture Behavioral of VGAtop is
component VGAsync
generic (
h_sync : integer := 95;
h_bporch : integer := 10;
h_active : integer := 640;
h_total : integer := 800;
v_sync : integer := 2;
v_bporch : integer := 25;
v_active : integer := 480;
v_total : integer := 525);
port (
clock : in STD_LOGIC;
Hsync : out STD_LOGIC;
Vsync : out STD_LOGIC;
reset : in STD_LOGIC;
inhibit : out STD_LOGIC;
row : out STD_LOGIC_VECTOR (9 downto 0);
col : out STD_LOGIC_VECTOR (9 downto 0));
end component;
signal clk_25: std_logic;
signal col: std_logic_vector (9 downto 0);
signal row: std_logic_vector (9 downto 0);
signal sub_row: std_logic_vector (3 downto 0);
signal inhibit: std_logic;
signal VGA: std_logic_vector (1 downto 0);
begin
-- instantiate the sync block that outputs row and column
sync_gen : VGAsync
generic map (
h_bporch => 30
)
port map (
clock => clk_25, Hsync => VGA_HSync,
Vsync => VGA_Vsync, reset => reset,
inhibit => inhibit, row => row,
col => col
);
-- divide external clock 50 MHz to 25 MHz.
process (clock_in, reset)
begin
if reset = '1' then
clk_25 <= '0';
else
if rising_edge(clock_in) then
clk_25 <= not clk_25;
end if;
end if;
end process;
-- modify the VGA output signal based on row and col counters
sub_row <= row(3 downto 0);
process(row, col)
begin
if row(7 downto 6) = "01" then
VGA <= col(6 downto 5);
else
case sub_row is
when "0000" => VGA(1) <= '1';
when "0001" => VGA(1) <= col(0);
when "0010" => VGA(1) <= col(1);
when "0011" => VGA(1) <= col(2);
when "0100" => VGA(1) <= col(3);
when "0101" => VGA(1) <= col(4);
when "0110" => VGA(1) <= col(5);
when "0111" => VGA(1) <= col(6);
when "1000" => VGA(1) <= col(7);
when "1001" => VGA(1) <= col(8);
when "1010" => VGA(1) <= '0';
when others => VGA(1) <= col(0) xor row(0);
end case;
VGA(0) <= row(4);
end if;
end process;
VGA_out(1) <= VGA(1) and not inhibit;
VGA_out(0) <= VGA(0) and not inhibit;
end Behavioral;
The final result looks like this.
| test setup, the FPGA generating a pattern on a LCD screen. |
The major problem I found with my setup is a very jittery image. I don't have sensitive enough equipment to be completely sure of the cause, but it may be due to the power supply (I can reduce the jitter by using a battery instead) and hum from all the equipment around. It's an annoying artefact, but does not invalidate the design.
Next, I want to use this design to display bitmaps from the built-in flash memory. As it turns out the column counter may not be so useful for this. To display a pixel, I need to latch its value BEFORE the beam reaches the position; it is probably easier to use the "inhibit" and clk_25 signals to clock out pixels directly. The row counter can still be useful to load the appropriate address at the beginning of the line.

No comments:
Post a Comment