Programmable Devices
CPLDs, FPGAs, SoC FPGAs, Configuration, and Transceivers
20704 Discussions

Digitize RGB video using DE0-Nano

Altera_Forum
Honored Contributor II
1,250 Views

Hi all, 

 

I'm building a 15Khz RGB to VGA converter using the DE0-Nano, to be used with old computers from the 80's. I know there are some boards in the market like GBS8200 but I'm not happy with them, then I decided to make my very own and learn FPGA. 

 

So far I was able to digitize the signal into an internal RAM array of 640x204 pixels using 1-bit color, thus 8 colors for now. Once I solve all the problems I will go using an external static RAM (the SDRAM seemed to slow in my tests) for more color depth. Look the video, it's already working: 

 

https://www.youtube.com/watch?v=pcyzlljwaea 

 

Basically I digitize every incoming pixel using a 12.58Mhz PLL and output it using a 25.175Mhz PLL (vga pixel clock). It's working but I get jitter on the image due to my 12.58Mhz clocks is not synched with the external HSYNC input signal. HSYNC is not a clock signal, but a pulse that happens each 15Khz. 

 

My question is how could I sync my PLL to the same HSYNC phase ? What could I do ? I don't want to use any other video IC, I want to build my own digitizer and implement very special features for each type of computer. 

 

Here the top schematic for little more understanding: 

 

http://i.imgur.com/hrbqyup.png?1 

 

Thanks,
0 Kudos
6 Replies
Altera_Forum
Honored Contributor II
518 Views

You won't be able to use HSYNC to directly sync your PLL. 

 

I assume (with your current implementation) you are adjusting the outgoing data, by a full clock cycle, every time you detect a 'change' to the incoming HSYNC pulse timing. 

 

You need to 'hide' these timing changes in the VSYNC inter frame gap - the dead time between frames. Providing you deliver a full frame of video without adjusting your outgoing data and HSYNC timing your jitter should disappear. 

 

Cheers, 

Alex
0 Kudos
Altera_Forum
Honored Contributor II
518 Views

 

--- Quote Start ---  

 

 

I assume (with your current implementation) you are adjusting the outgoing data, by a full clock cycle, every time you detect a 'change' to the incoming HSYNC pulse timing. 

 

 

--- Quote End ---  

 

 

I will copy the full code here later when I get home. But basically I have a framebuffer and I control the incoming hcount and vcount by waiting the hsync and vsync (they are positive pulses in this case) like 

 

process(CLOCK_25) 

begin 

if (rising_edge(CLOCK_25)) then 

hblank <= '1'; 

if (HSYNC_IN = '1' and hcount >= 655) then 

hblank <= '0'; 

end if; 

end if; 

end process; 

 

process(CLOCK_25) 

begin 

if (rising_edge(CLOCK_25)) then 

if (hblank = '0') then 

hcount <= 0; 

else 

hcount <= hcount + 1; 

end if; 

end if; 

end process; 

 

Being CLOCK_25 the 12Mhz clock. Then I use hcount together the vcount to calculate the offset in the pixel array to store it. 

 

The VGA output has it's own 25Mhz clock and since it's buffered, it doesn't rely on the incoming hsync, vsync. It just pumps out whatever is in the buffer.
0 Kudos
Altera_Forum
Honored Contributor II
518 Views

here my code. Look that I force the HSYNC to match the end of the line: 655 (for the sync) and the vsync to match the last scanline 262.5. CLOCK_50 = 25.17Mhz and CLOCK_25=12.58Mhz. I got less jitter using 25Mhz for the horizontal counter: 

 

 

library IEEE; 

use IEEE.STD_LOGIC_1164.ALL; 

use IEEE.NUMERIC_STD.ALL; 

 

entity vgaoutput is 

generic( 

front_porch : integer := 100; 

top_border : integer := 35 

); 

 

port(CLOCK_50 : in std_logic; 

CLOCK_25 : in std_logic; 

VSYNC_IN : in std_logic; 

HSYNC_IN : in std_logic; 

RGB_IN : in unsigned(2 downto 0); -- r, g, b 

VGA_OUT : out unsigned(4 downto 0) -- r, g, b, hsync, vsync  

); 

end vgaoutput; 

 

architecture behavioral of vgaoutput is 

 

signal hcount : unsigned(9 downto 0) := (others => '0'); 

signal vcount : unsigned(9 downto 0) := (others => '0'); 

signal pixelcolor : unsigned(2 downto 0) := "000"; 

signal videoon, videov, videoh, hsync, vsync : std_ulogic := '0'; 

signal reset : std_ulogic := '1'; 

 

signal vblank : std_ulogic := '1'; 

signal hblank : std_ulogic := '1'; 

 

signal hcount_in : unsigned(10 downto 0) := (others => '0'); 

signal vcount_in : unsigned(9 downto 0) := (others => '0'); 

 

type line_memory_t is array (0 to 640*204-1) of unsigned(2 downto 0); 

shared variable line_memory : line_memory_t; 

 

begin 

 

----------------------------------------- 

-- RGB INPUT 

----------------------------------------- 

 

vsyncin: process(CLOCK_25) 

begin 

if (rising_edge(CLOCK_25)) then 

vblank <= '1';  

if (VSYNC_IN = '1' and vcount_in >= 262 and hcount_in(10 downto 1) >= 399) then 

vblank <= '0';  

end if; 

end if; 

end process; 

 

hsyncin: process(CLOCK_50)  

begin 

if (rising_edge(CLOCK_50)) then 

hblank <= '1';  

if (HSYNC_IN = '1' and hcount_in(10 downto 1) >= 655) then 

hblank <= '0'; 

end if; 

 

end if; 

end process; 

 

hcounter_in: process(CLOCK_50)  

begin 

if (rising_edge(CLOCK_50)) then 

if hblank = '0' then 

hcount_in <= (others => '0'); 

else 

hcount_in <= hcount_in + 1; 

end if; 

end if; 

end process; 

 

vcounter_in: process (CLOCK_25) 

begin 

if(rising_edge(CLOCK_25)) then 

if vblank = '0' then 

vcount_in <= (others => '0'); 

elsif hblank = '0' then 

vcount_in <= vcount_in + 1; 

end if; 

end if; 

end process; 

 

pixel_in: process(CLOCK_25) 

variable addr: integer range 0 to 153600; 

variable row, col: integer range 0 to 153600; 

begin 

if (rising_edge(CLOCK_25)) then 

 

if (hcount_in(10 downto 1) >= front_porch and hcount_in(10 downto 1) < front_porch+640 and vcount_in >= top_border and vcount_in < top_border+204) then 

 

col := to_integer(hcount_in(10 downto 1)) - front_porch;  

row := to_integer(vcount_in) - top_border; 

addr := (row * 640) + col; 

line_memory(addr) := RGB_IN; 

end if; 

 

end if; 

end process; 

 

----------------------------------------- 

-- VGA OUTPUT 

----------------------------------------- 

 

vcounter: process (CLOCK_50, hcount) --, vblank) 

 

begin 

if(rising_edge(CLOCK_50)) then 

 

if hcount = 799 then 

vcount <= vcount + 1; 

end if; 

 

if vcount = 525 then  

vcount <= (others => '0'); 

end if;  

 

end if; 

end process; 

 

v_sync: process(CLOCK_50, vcount) 

begin 

if(rising_edge(CLOCK_50)) then 

vsync <= '1'; 

if (vcount <= 492 and vcount >= 490) then 

vsync <= '0'; 

end if; 

end if; 

end process; 

 

hcounter: process (CLOCK_50) 

begin 

if (rising_edge(CLOCK_50)) then  

 

hcount <= hcount + 1; 

if hcount=799 then  

hcount <= (others => '0'); 

end if; 

 

end if; 

end process; 

 

h_sync: process (CLOCK_50, hcount) 

begin 

if (rising_edge(CLOCK_50)) then  

hsync <= '1';  

if (hcount <= 752 and hcount >= 655) then 

hsync <= '0'; 

end if; 

end if;  

end process; 

 

pixel_out: process (CLOCK_50) 

variable addr: integer range 0 to 153600; 

variable row, col: integer range 0 to 153600; 

 

begin 

if (rising_edge(CLOCK_50)) then 

 

if (hcount < 640 and vcount < 408) then 

 

col := to_integer(hcount);  

row := to_integer(vcount(9 downto 1)); 

addr := (row * 640) + col; 

pixelcolor(2 downto 0) <= line_memory(addr)(2 downto 0); 

end if; 

 

end if; 

end process; 

 

process (vcount) 

begin 

videov <= '1';  

if vcount > 479 then 

videov <= '0'; 

end if; 

end process; 

 

process (hcount) 

begin 

videoh <= '1'; 

if hcount > 639 then 

videoh <= '0'; 

end if; 

end process; 

 

videoon <= videoh and videov; 

VGA_OUT(4 downto 2) <= pixelcolor and videoon&videoon&videoon; 

VGA_OUT(1 downto 0) <= hsync & vsync; 

 

end behavioral; 

0 Kudos
Altera_Forum
Honored Contributor II
518 Views

I dont really like the way you've done your frame buffer - I dont see this actually inferring a ram because of your unregistered read and write addresses (not to mention potential poor timing performance, but at 25MHz you can probably get away with it). 

 

As for the jitter, rather than syncing the input to the output you might be better off just free running the output and inputs and using the frame buffer in the middle to get the data accross. You might get the odd tearing frame but to mitigate you should be able to make the output repeat a frame if the output catches the input.
0 Kudos
Altera_Forum
Honored Contributor II
518 Views

Your code is re-syncing the outgoing HSYNC with every incoming HSYNC pulse. So, you are seeing inter-row jitter - i.e. the entire row is moving with respect to an adjacent row. This you can see in the video you posted. 

 

You must re-sync your counters once and only once per VSYNC cycle only. Having done this you must play the data out of your buffer using your internal timers only. This is the 'free running' that Tricky refers to. Don't reset them until the next VSYNC pulse. Any difference in the clock rate will be hidden in the VSYNC dead time, where it doesn't matter. 

 

Cheers, 

Alex
0 Kudos
Altera_Forum
Honored Contributor II
518 Views

I managed to get a pixel perfect genlock. The main problem was that my pixel clock wasn't a NTSC clock multiple. I'm running now at 114.54Mhz. I also changed the RAM access using altram 2 port and implemented a SDRAM module to capture the full frame. It's working well as you can see in the video:  

 

https://www.youtube.com/watch?v=p8typuh1hqq 

 

But I'm getting noise, specially with some colors red or cyan. I'm grounding the computer, the altera and the VGA output together. Is there anything else I can do ? I tried to space the input pins away across the GPIO_0 banks to avoid cross talk but still the problem remains (maybe less noisy now). 

 

I also tried to use LVDS inputs to implement fast 2-bit ADC but what I get is even more noise. I was using a resistor ladder for different voltage levels: 1.65, 1.0, 0.4 but LVDS inputs seemed to oscillate. When I outputted the LVDS inputs to the LEDs and I could see the LEDs with a dim light where it was supposed to be off due to the applied voltage.
0 Kudos
Reply