Implementing a VGA controller on Spartan 3 board with 25.0 MHz clock - Page 1
Forum section: electronics / general

author: garvind25
author_rank: Regular Contributor
timestamp: September 15, 2017, 05:11:53 pm
content: Hi all,I was planning to implement a VGA controller using the code as below:https://eewiki.net/pages/viewpage.action?pageId=15925278As per the appendix, if I want to implement a VGA controller for 640X480 resolution (60 Hz), I will need a pixel clock of 25.125 MHz. My kit has a 25.0 MHz clock. My queries are;1. How has this number (25.125M) come in existence.2. Can I generate this clock through an on-board 25.0 MHz source (Spartan 3 XC3S400; ISE 10.1)3. What happens if I implement the same using the 25.0 MHz. clock instead of 25.125 MHz clock. I will prefer asking before trying it out to avoid any possible damage tothe only board I have access to.Thanks,Arvind Gupta.

author: Buriedcode
author_rank: Super Contributor
timestamp: September 15, 2017, 05:51:52 pm
content: On that page, scroll down to:https://eewiki.net/pages/viewpage.action?pageId=15925278#VGAController(VHDL)-SignalTimingThat table shows how the pixel clock value is derived based on resolution, refresh rate, front porch, back porch etc.. Essentially, the horizontal front porch, sync, and back porch are in terms of pixel clocks.. so a line may be 640 pixels wide, but.. there are extra pixel periods off-screen (in CRT monitors, this is to give the beam a chance to "fly back" to the start of the next line) so for 16, 96, and 48 pixel clocks for front, sync, and back, thats an extra 160 off-screen "pixels", making each line 800 pixels.Similarly, for the vertical front porch, sync and back porch, these are added as extra 'lines'. 10,2,33 gives an extra 45 lines, with 480 display lines, that's 525.800 pixels * 525 lines * 60Hz refresh = 25200000 Hz. Or 25.2MHz.I believe you can vary the front and back porch times to suit your needs. However, I'm sure the spartan 3 has on-board PLL's which will allow you to generate almost any clock you need.I don't see how you can damage your board.. unless you provide it with over voltage on its IO's, or start desoldering things..

author: Scrts
author_rank: Frequent Contributor
timestamp: September 15, 2017, 06:31:39 pm
content: If the clock is 25MHz, you will just have a slower frame rate. Otherwise, play with the blanking periods. I've used 800x480 display with 27MHz clock, which is easily achievable by any PLL using 27 multiply and 25 division in your case.

author: hamster_nz
author_rank: Super Contributor
timestamp: September 15, 2017, 07:18:08 pm
content: QuoteNo idea, most likely a lot of historical choices that no longer applyQuoteUsing 25MHz will work just fine.QuoteIt will work just fine. If you are using an old CRT monitor I would check the timing with a scope or logic analyzer, as driving very old CRTs with an out-of-spec video signal can break them. If the monitor has an On Screen Display (e.g. shows "No Signal") it will check the signal specs before it displays it.If frame rate is vitally important to you, reduce the total horizontal count from 800 to 796 (by 0.5%) to balance out the clock being about 0.5% slower.

author: rstofer
author_rank: Super Contributor
timestamp: September 15, 2017, 07:46:30 pm
content: For the original Spartan 3 Starter Board, VGA is discussed beginning on page 21:https://www.xilinx.com/support/documentation/boards_and_kits/ug130.pdfThey use 25 MHz...

author: BrianHG
author_rank: Super Contributor
timestamp: September 16, 2017, 04:43:30 am
content: Quote from: hamster_nz on September 15, 2017, 07:18:08 pm***Make the horizontal count 794...LCD screens will need an 'auto adjust' to clean up the pixels if you are using analog RGB VGA.You shouldn't have a problem with 800 anyways, 59.5hz should still lock, but, when playing a video file, you will occasionally drop a frame.

author: garvind25
author_rank: Regular Contributor
timestamp: September 16, 2017, 06:28:38 am
content: Thanks... One more thing. Is there a way to generate 25.2 MHz clock in Xilinx CPLD (say XC2C128/256).Thanks again,Arvind Gupta

author: hamster_nz
author_rank: Super Contributor
timestamp: September 16, 2017, 09:21:43 am
content: AFAIK Most CPLDs do not have PLLs, DCMs or other things that allow you to generate new clocks, however I am sure somebody will correct me....If you use two DCM blocks on the Spartan 3E, you can get pretty close - 25.1241MHzFirst block:Input 25.0 MHzmultiply by 30, Divide by 31Output 24.193,548MHzSecond block:Input 24.193,548 MHzmultiply by 27, Divide by 26Output 25.124 069 MHzThat is off by only 0.004%

author: hamster_nz
author_rank: Super Contributor
timestamp: September 16, 2017, 10:00:54 am
content: Oh, and here is some code to convert a 25.0 MHz clock to a approximately 25.125 clockCode:library IEEE;use IEEE.STD_LOGIC_1164.ALL;library UNISIM;use UNISIM.VComponents.all;entity make25_125 isPort ( c_in : in STD_LOGIC;c_out : out STD_LOGIC;locked : out STD_LOGIC);end make25_125;architecture Behavioral of make25_125 issignal c_middle : std_logic;signal locked_first : std_logic;signal clkfb_1 : std_logic;signal clkfb_2 : std_logic;signal reset_2nd : std_logic_vector(4 downto 0) := (others => '1');beginDCM_SP_1 : DCM_SP generic map (CLKDV_DIVIDE => 2.0,CLKFX_DIVIDE => 31,CLKFX_MULTIPLY => 30,CLKIN_DIVIDE_BY_2 => FALSE,CLKIN_PERIOD => 0.0,CLKOUT_PHASE_SHIFT => "NONE",CLK_FEEDBACK => "1X",DESKEW_ADJUST => "SYSTEM_SYNCHRONOUS",DLL_FREQUENCY_MODE => "LOW",DUTY_CYCLE_CORRECTION => TRUE,PHASE_SHIFT => 0,STARTUP_WAIT => FALSE)port map (CLK0 => clkfb_1,CLK180 => open,CLK270 => open,CLK2X => open,CLK2X180 => open,CLK90 => open,CLKDV => open,CLKFX => c_middle,CLKFX180 => open,LOCKED => locked_first,PSDONE => open,STATUS => open,CLKFB => clkfb_1,CLKIN => c_in,PSCLK => '0',PSEN => '0',PSINCDEC => '0',RST => '0');process(c_middle)beginif rising_edge(c_middle) then-- hold the reset on the second DCM a few cycles after the first one locksreset_2nd <= '0' & reset_2nd(reset_2nd'high downto 1);end if;end process;DCM_SP_2 : DCM_SP generic map (CLKDV_DIVIDE => 2.0,CLKFX_DIVIDE => 26,CLKFX_MULTIPLY => 27,CLKIN_DIVIDE_BY_2 => FALSE,CLKIN_PERIOD => 0.0,CLKOUT_PHASE_SHIFT => "NONE",CLK_FEEDBACK => "1X",DESKEW_ADJUST => "SYSTEM_SYNCHRONOUS",DLL_FREQUENCY_MODE => "LOW",DUTY_CYCLE_CORRECTION => TRUE,PHASE_SHIFT => 0,STARTUP_WAIT => FALSE)port map (CLK0 => clkfb_2,CLK180 => open,CLK270 => open,CLK2X => open,CLK2X180 => open,CLK90 => open,CLKDV => open,CLKFX => c_out,CLKFX180 => open,LOCKED => locked,PSDONE => open,STATUS => open,CLKFB => clkfb_2,CLKIN => c_middle,PSCLK => '0',PSEN => '0',PSINCDEC => '0',RST => reset_2nd(0));end Behavioral;(I am relying on the tools to infer any required clock buffers, which ISE does, usually).

author: legacy
author_rank: Super Contributor
timestamp: September 16, 2017, 04:41:09 pm
content: In this project I have divided by 2 the physical clock (50Mhz)and everything is fine

author: rstofer
author_rank: Super Contributor
timestamp: September 16, 2017, 05:21:16 pm
content: Quote from: garvind25 on September 16, 2017, 06:28:38 amNoThere is nothing in the datasheet that leads me to believe there is a PLL or DCM in a CPLD.https://www.xilinx.com/support/documentation/data_sheets/ds094.pdf

author: garvind25
author_rank: Regular Contributor
timestamp: November 06, 2017, 08:47:27 am
content: Will it be possible for you to share your VGA controller code pls? I have got a Spartan 3 board from a friend and wanted to check if its VGA port works OK. I downloaded a few codes from the internet but no luck. I have a suspicion that these codes from the internet actually don't workThanks and Regards,Arvind Gupta

author: rstofer
author_rank: Super Contributor
timestamp: November 06, 2017, 05:32:06 pm
content: Here is code from John Kent down under. I used if for a console on a CPU project.http://www.volkerschatz.com/hardware/vhdocl-example/sources/vdu8.htmlNot shown is the fact that you need to populate 'vdu_char_rom' using whatever scheme you wish.Here is my VHDL version of the character ROM:Code:library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_ARITH.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;library UNISIM;use UNISIM.VComponents.all;entity char_rom isPort (clk : in std_logic;rst : in std_logic;cs : in std_logic;rw : in std_logic;addr : in std_logic_vector (9 downto 0);wdata : in std_logic_vector (7 downto 0);rdata : out std_logic_vector (7 downto 0));end char_rom;architecture rtl of char_rom issignal Raddr: std_logic_vector(10 downto 0);beginRaddr <= "0" & addr;CharRom: RAMB16_S9generic map (INIT => "000000000", -- Value of output RAM registers at startupSRVAL => "000000000", -- Ouput value upon SSR assertionWRITE_MODE => "WRITE_FIRST", -- WRITE_FIRST, READ_FIRST or NO_CHANGEINIT_00 => x"000000FF0000001010101010101010003E1C7F7F3E1C08000000FF0000000000",-- 00..03INIT_01 => x"202020202020200000FF0000000000000000000000FF000000000000FF000000",-- 04..07INIT_02 => x"0000E0100808080000000304080808080810E000000000040404040404040420",-- 08..0BINIT_03 => x"808080808080FF80402010080402010102040810204080FF8080808080808000",-- 0C..0FINIT_04 => x"081C3E7F7F7F3600FF000000000000003C7E7E7E7E3C0001010101010101FF80",-- 10..13INIT_05 => x"003C424242423C00814224181824428108080403000000004040404040404040",-- 14..17INIT_06 => x"0808FF0808080800081C3E7F3E1C0802020202020202020008082A772A1C0800",-- 18..1BINIT_07 => x"03070F1F3F7FFF001414543E010000080808080808080850A050A050A050A008",-- 1C..1FINIT_08 => x"0024247E247E2424000000000024242400080000080808080000000000000000",-- 20..23INIT_09 => x"0000000000100804003A444A30484830004626100864620000083C0A1C281E08",-- 24..27INIT_0A => x"000008083E08080000082A1C3E1C2A0800201008080810200004081010100804",-- 28..2BINIT_0B => x"00402010080402000018180000000000000000007E0000001008080000000000",-- 2C..2FINIT_0C => x"003C42021C02423C007E40300C02423C003E080808281808003C42625A46423C",-- 30..33INIT_0D => x"001010100804427E003C42427C40201C003844020478407E0004047E24140C04",-- 34..37INIT_0E => x"00080800000800000000080000080000003804023E42423C003C42423C42423C",-- 38..3BINIT_0F => x"001000100C02423C0070180C060C18700000007E007E0000000E18306030180E",-- 3C..3FINIT_10 => x"001C22404040221C007C22223C22227C004242427E422418001E204C564A221C",INIT_11 => x"001C22424E40221C004040407840407E007E40407840407E0078242222222478",INIT_12 => x"0042444870484442003844040404040E001C08080808081C004242427E424242",INIT_13 => x"0018244242422418004242464A526242004242425A5A6642007E404040404040",INIT_14 => x"003C42023C40423C004244487C42427C001A244A42422418004040407C42427C",INIT_15 => x"0042665A5A4242420018182424424242003C424242424242000808080808083E",INIT_16 => x"003C20202020203C007E40201804027E000808081C2222220042422418244242",INIT_17 => x"007E0000000000000000000000221408003C04040404043C0001020408102040",INIT_18 => x"003C4240423C0000005C6242625C4040003A443C04380000001E204C564A221C",INIT_19 => x"3C023A46463A0000001010107C10120C003C407E423C0000003A4642463A0202",INIT_1A => x"004468504844404038440404040C0004001C08080818000800424242625C4040",INIT_1B => x"003C4242423C000000424242625C00000049494949760000001C080808080818",INIT_1C => x"007C023C403E000000404040625C000002023A46463A000040405C62625C0000",INIT_1D => x"00364949494100000018244242420000003A464242420000000C1210107C1010",INIT_1E => x"000C18183018180C007E2018047E00003C023A46424200000042241824420000",INIT_1F => x"00522A522A522A520000060948300000003018180c1818300018181800181818")port map (DO => rdata, -- 8-bit Data OutputDOP => open, -- 1-bit parity OutputADDR => Raddr, -- 11-bit Address InputCLK => clk, -- ClockDI => wdata, -- 8-bit Data InputDIP => "0", -- 1-bit parity InputEN => cs, -- RAM Enable InputSSR => rst, -- Synchronous Set/Reset InputWE => not rw -- Write Enable Input);end;All of this works under ISE.ETA: This generates a text display of 25 lines of 80 chars. It is NOT a graphics display. Nevertheless, the timing is fully shown and it is just a matter of laying the right bits down at the right time. The memory scheme is all wrong for graphics. There is no pixel memory, just an 80x25 array of ASCII codes and a character ROM containing the bits for the various characters. There is also an 80x25 array of attribute codes.ETA: 25 MHz (even) works well. Note that in the vdu code above, the vertical refresh rate is slightly over 70 Hz. Most monitors can handle this easily. I know that it works on a standard 4:3 1024x768 LCD monitor.Absolute timing accuracy may matter for broadcast TV but not for digital monitors.

author: legacy
author_rank: Super Contributor
timestamp: November 06, 2017, 09:16:58 pm
content: The real first big problem with a graphics display is the video ram! It's requested to be very big, so big that you can't use fpga's bram because too limited in size. Therefore you need to implement a dram controller to interface external dram, and the design goes more complex.The second problem is: how to access it? Of course it still needs to be dual-port-ram but with dual-buffering? Yup, fine! Good! You will want it, but it needs more video memory and here it is an other step.

author: hamster_nz
author_rank: Super Contributor
timestamp: November 06, 2017, 09:29:51 pm
content: Quote from: legacy on November 06, 2017, 09:16:58 pmThe easiest way I've found to deal with this issue is to time-slice - esp with page-mode DRAM:- a few cycles to read some data for display (open a line, read words into display shift register, close the line)- a few cycles to process frame buffer read or writes or on a fixed schedule (e.g. once every N times) replace with a refresh cycle.It isn't optimal, but that way you can ensure that you never stave the display of data.

author: rstofer
author_rank: Super Contributor
timestamp: November 06, 2017, 10:08:43 pm
content: The Digilent Nexys 4 DDR board uses DDR2 memory. The important thing is that Digilent provides a component that allows the memory to be treated as static ram. Nice simple reads and writes.I haven't used the component so I don't know how fast it is but I would try to work it out such that memory accesses could be interleaved.The component documentation says 210 ns Read Cycle and 260 ns Write Cycle while using a 350 MHz clock. The data width is 16 bits (which helps!).https://reference.digilentinc.com/learn/programmable-logic/tutorials/nexys-4-ddr-sram-to-ddr-component/startHere's a project using the Nexys 4 DDR board for graphics and mouse:https://reference.digilentinc.com/learn/programmable-logic/tutorials/nexys-4-ddr-vga-test-pattern-with-mouse-overlay/start

author: rstofer
author_rank: Super Contributor
timestamp: November 07, 2017, 12:42:07 am
content: Quote from: legacy on November 06, 2017, 09:16:58 pmI'm not sure the resolution was ever stated. Further, color depth just keeps increasing word width.640x480 with 256 colors would require 307,200 bytes of memory. So, 512k bytes would cover it.It is possible to get a 512kx8 10 ns SRAM for about $4https://www.mouser.com/Semiconductors/Memory/SRAM/_/N-4bzptInterleave access!

author: legacy
author_rank: Super Contributor
timestamp: November 07, 2017, 08:09:38 am
content: 1024x768x 24bit_color x2(double buffer) => you need an external DDR controllerand a nice dram chip: already done! I am happy, just the complexity has doubled.

author: simmconn
author_rank: Regular Contributor
timestamp: November 09, 2017, 12:41:20 am
content: Using 25MHz for VGA pixel clock is perfectly fine. I've been using it for years. A CRT monitor is almost guaranteed to lock due to their wide frequency tolerance. I haven't have problems with LCD monitors, either. Changing the frame structure to make up for the H/V timing differences could be tricky, as you are creating a non-standard frame structure. Most likely the LCD monitor will lock to the non-standard frame structure, but the video ADC sampling points will be approximate and won't be dot-for-dot accurate. Not a big deal as today's monitor would have to scale the picture up and the artifacts of imperfect sampling would not be that obvious.Using cascaded DCM to do clock syntheses is generally not recommended due to the resulting clock jitter, especially with large M and N values. I think Xilinx removed this configuration in the ISE core gen. Although you can still cascade them manually, you are pretty much on your own wrt jitter performance.