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