[Solved] VHDL help needed - Page 1
Forum section: electronics / general

author: ebastler
author_rank: Super Contributor
timestamp: April 10, 2016, 03:08:56 pm
content: I could use some help with my first-ever VHDL project...Trying to create a "retro video upscaler", to convert a composite signal from my 1973 PONG board to XGA output (with added simulated scan lines and a bit of blur). I have built an analog splitter to separate the sync and intensity signals, and can successfully read the resulting two 1-bit signals into the FPGA, a Xilinx Spartan 3A, and echo them back via two outputs.But I have not managed to create a simple digital integrator, to identify the (longer) vertical sync pulses. My integration counter does not behave as expected. Here is a stripped-down version, which does not compare the integrator to a threshold yet, but simply checks whether is has decayed back to zero or is non-zero, and outputs that status via an FPGA output:Code:entity SyncSplit isPort (clk: in STD_LOGIC;csync: in STD_LOGIC;vsync: out STD_LOGIC);end SyncSplit;architecture Behavioral of SyncSplit isbeginprocess isvariable isync: natural := 0; -- integrated sync signalbegin-- The process is carried out for every positive edge of the clockwait until clk = '1';-- integrate vertical sync signalif csync='0' thenif isync>0 then isync := isync-1; end if; -- decayelseisync := isync+1; -- increaseend if;-- test: indicate on output whether intregrator is zeroif (isync = 0) then vsync <= '0';else vsync <= '1';end if;end process;end Behavioral;"csync" is the composite sync input, active high. I.e. it is mostly zero, with short high phases for the line syncs, and a longer high phase for the vertical sync (interrupted by short inverted line sync).The integrator value "isync" should rise during the active high period of the csync input, the decay again after csync goes back to low. I.e. it should remain non-zero for twice the "csync = high" phase, the stay at zero for the rest of the scan line. The "vsync" output should echo this behaviour.Indeed, when I look at the "vsync" output with a scope, it shows short bursts (a few scan lines) where is behaves as expected. But then it goes back to high (suggesting isync > 0) and stays there, often for several full composite frames. The duration of both, the "works as expected" and the "stays high" phases, seem random. I have attached a scope screenshot, although I don't think it tells much more -- channel 1 is the "csync" input, channel 2 the "vsync" output.I must be overlooking something fundamental here. Isn't variable declared in a process guaranteed to be persistent? Am I doing something stupid in the VHDL code?Many thanks for any pointers you might have!EDIT: For quick future reference, the solution to my conundrum is in post #15 by hamster_nz below. Thanks to hamster_nz and all other posters for helpful hints!

author: lem_ix
author_rank: Regular Contributor
timestamp: April 10, 2016, 04:34:58 pm
content: Been a while since I've done any vhdl but some suggestions. Use signals instead of variables whenever possible.Also I'd change that clock statement to smt like:if (clk'event and clk='1').Also don't make incomplete if statements, that usually means trouble.Hope that helps.

author: nctnico
author_rank: Super Contributor
timestamp: April 10, 2016, 05:13:26 pm
content: Actually the correct syntax 'du jour' for doing something on the rising edge of a clock is:Code:if rising_edge(clk) thenend if;Also use signals instead of variables.

author: ebastler
author_rank: Super Contributor
timestamp: April 10, 2016, 06:14:53 pm
content: lem_ix and nctnico, many thanks for your suggestions!Unfortunately, no luck so far:I was pretty confident that the clocking works OK: I had tried to toggle a separate output every time the process runs, and got the expected result. I tried the "if rising_edge (clk)" construct nevertheless, but it did not change the behavior. (By the way, I am clocking the SyncSplitter at close to 65 MHz -- the pixel clock for the XGA output I want to up-sample to. So I am oversampling the composite signal significantly.)The "use signals instead of variables" recommendation seemed very promising, since I do not trust my counter variable. However, makingisynca signal instead does not change the behavior -- same random changes between short bursts of working and longer phases of non-working behavior.lem_ix, could you please elaborate on the "incomplete if" problems? I have read elsewhere that "if" constructs which do not explicitly take care of every possible outcome can cause unwanted latches to be created for variables or signals. (Which makes sense, since in situations where no "if" branch applies, the signal must retain its value.) But Iwanta latched behavior for my counter, so I don't think this should cause a problem. Are there other issues with incompleteifsthat I should be aware of?Thanks again!Juergen

author: nctnico
author_rank: Super Contributor
timestamp: April 10, 2016, 06:30:25 pm
content: Rule number one: don't use latches in your design. Always use the clock in the primary if.

author: ebastler
author_rank: Super Contributor
timestamp: April 10, 2016, 06:37:31 pm
content: Quote from: nctnico on April 10, 2016, 06:30:25 pmHmm, I may have misunderstood "latches" then. The counter must (on the lowest level) live in D flip-flops, which are triggered by the clock signal, right? When clocked, they change their values in some situations, but do not change in others (in this case, when csync=0 and the counter is already zero). Hence, there is a feedback from the D flip-flop's output to the input. That's what I meant by "latches".So far, so good. What is the "evil" kind of latches that one should avoid?

author: ebastler
author_rank: Super Contributor
timestamp: April 10, 2016, 06:59:36 pm
content: Answering my own question here... Turns out that using the terms "latch" and "D flip-flop" interchangeably, which I have done for the past 30 years, is wrong -- ouch! A one-bit register is called a "D flip-flop" if it is edge-sensitive, i.e. triggered by a clock, and a "latch" if it is state-sensitive, i.e. controlled by a latch/hold input. Learned something...Nevertheless, my design only creates D flip-flops, as it should. Here is a snippet from the HDL synthesis log. (Names differ a bit from the minimized version I pasted above.)Code:Synthesizing Unit <Composite>.Related source file is "C:/Hardware/FPGA/XGA_Upscaler/Composite.vhd".Found 1-bit register for signal <vsync_out>.Found 31-bit updown counter for signal <isync>.Found 31-bit comparator greater for signal <isync$cmp_gt0000> created at line 51.Summary:inferred 1 Counter(s).inferred 1 D-type flip-flop(s).inferred 1 Comparator(s).Unit <Composite> synthesized.So, I am still at a loss why this code does not work. Any further ideas?

author: rstofer
author_rank: Super Contributor
timestamp: April 10, 2016, 08:39:47 pm
content: It has been established that I know next to nothing about variables. But I do know this: When I synthesize your code for a CoolRunner II CPLD, it takes 77 of 256 macrocells and the RTL schematic is pretty busy. When I change the variable to a std_logic_vector, the synthesis uses just 37 of 256 macrocells.Unfortunately, I have no idea why your code doesn't work but I suspect I'll stick with std_logic_vectors.ETA: I also replaced the 'wait until' statement with if (rising_edge(clk)) then..

author: hamster_nz
author_rank: Super Contributor
timestamp: April 10, 2016, 09:06:43 pm
content: Here's how I would code what you have written:Code:library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.NUMERIC_STD.ALL;entity SyncSplit isPort (clk: in STD_LOGIC;csync: in STD_LOGIC;vsync: out STD_LOGIC);end SyncSplit;architecture Behavioral of SyncSplit issignal isync : unsigned(15 downto 0) := (others => '0');beginprocess(clk)beginif rising_edge(clk) thenif csync='0' thenif isync != 0 thenisync <= isync-1;elsevsync <= '0'; -- decayed to zero, so resetend ifelseisync <= isync+1; -- increase the countervsync <= '1'; -- set whenever csync is 1;end if;end if;end process;end Behavioral;I haven't verified it at all, It is written to ensure that the latency is what looks to be correct (i.e. if csync is asserted for a single cycle, vsync is asserted for two cycles, with one cycle of latency from the edge of csync to the edge of vsyncIt also avoids any tests against anything but equality to zero, so should be pretty compact.Code:csync: ____X_____vsync: _____XX___This may or not be what you are after.

author: ebastler
author_rank: Super Contributor
timestamp: April 11, 2016, 04:42:49 am
content: @rstofer - thank you; I will give that a try and replace the natural counter with a std_logic_vector. You are still type-casting that to an integer in order to increment or decrement the counter, right?@hamster_nz - thank you too! I have not had the opportunity to test your suggested implementation, but will do as soon as I get back to my bench. Looking at the code, I agree that it should do the job as well, probably with a more compact synthesis. And hopefully it avoids whatever glitch causes my version to fail!I will probably not find the time to do further tests today, and will update the post tomorrow. Thanks again for everyone's input!

author: hamster_nz
author_rank: Super Contributor
timestamp: April 11, 2016, 05:12:10 am
content: Quote from: ebastler on April 11, 2016, 04:42:49 amI forgot to say that you should increase the length of the counter to be correct for the maximum pulse length you are expecting.Oh, and if at any time the duty cycle of the pulses exceed 50% you will have issues, as the counter will never count back down to zero but will instead overflow at some point and give a glitch. It may pay to add a test and see if that prior value for csync was '0', and the current value is '1' then explicit set the counter to 1. Just ask If I make no sense, and I'll send you some code...

author: rstofer
author_rank: Super Contributor
timestamp: April 11, 2016, 05:50:47 am
content: Quote from: ebastler on April 11, 2016, 04:42:49 amNo, I am usinglibrary IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_ARITH.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;Code:library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_ARITH.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity SyncSplit isPort (clk: in STD_LOGIC;csync: in STD_LOGIC;vsync: out STD_LOGIC);end SyncSplit;architecture Behavioral of SyncSplit issignal isync: std_logic_vector(31 downto 0) := (others => '0'); -- integrated sync signalbeginprocess isbegin-- The process is carried out for every positive edge of the clockif rising_edge(clk) then-- integrate vertical sync signalif csync='0' thenif isync>0 then isync <= isync-1; end if; -- decayelseisync <= isync+1; -- increaseend if;-- test: indicate on output whether intregrator is zeroif (isync = 0) then vsync <= '0';else vsync <= '1';end if;end if;end process;end Behavioral;

author: ebastler
author_rank: Super Contributor
timestamp: April 11, 2016, 06:48:10 am
content: Quote from: rstofer on April 11, 2016, 05:50:47 amAh -- I didn't realize that there was a library which supports arithmetics with standard logic vectors. I will definitely give that a try tomorrow!

author: lem_ix
author_rank: Regular Contributor
timestamp: April 11, 2016, 06:49:06 am
content: Should use numeric_std instead(standard unlike arith). Also when doing numeric operations you should type cast to a numeric type, then do the operation(Addition in this case), then cast back. There's no guarantee to what exactly std_logic_vector + 1 means.Like this for example:output <= std_logic_vector(unsigned(input) + 1);But the code hamster wrote seems nice and avoids the issue

author: Kalvin
author_rank: Super Contributor
timestamp: April 11, 2016, 08:57:47 am
content: Does it make any difference if you add the clock into process's sensitivity list:process(clk)...end process;

author: hamster_nz
author_rank: Super Contributor
timestamp: April 11, 2016, 09:35:27 am
content: After staring at the original code, I just realized that you are not synchronizing the input signal. As csync is asynchronous to your FPGA clock it will be broken due to the relative path delays in the design.I am sure that if you synchronize it your original code (and my code) will actually work - it also explains why it almost works as designed.Standard synchronize code for me isCode:.... in clocked process...csync_safe <= csync_unsafe;csync_unsafe <= csync;... and then only use csync_safe in the rest of the logic.However, you should also attach attributes to the signals to ensure that the tools know what you are trying to do. For Xilinx you would do it this way:Code:signal csync_unsafe: std_logic := '0';signal csync_safe: std_logic := '0';attribute ASYNC_REG : string;attribute ASYNC_REG of csync_unsafe: signal is "true";attribute ASYNC_REG of csync_safe: signal is "true";

author: rstofer
author_rank: Super Contributor
timestamp: April 11, 2016, 01:37:30 pm
content: Quote from: lem_ix on April 11, 2016, 06:49:06 amUsing ARITH and UNSIGNED is a result of 'getting code to work' without really understanding the implications. But it works (at least with Xilinx)!Here is a small article re: the subject of NUMERIC_STD:http://vhdlguru.blogspot.com/2010/03/why-library-numericstd-is-preferred.htmlOn my next project, which is just a recode of an existing proecvt, I'll try the approved approach.

author: rstofer
author_rank: Super Contributor
timestamp: April 11, 2016, 04:17:48 pm
content: Quote from: rstofer on April 11, 2016, 01:37:30 pmOne of the problems with searching the Internet for tutorials is that MANY of them are based on UNSIGNED and ARITH libraries because, well, that's the way it was done some time ago. What you may find for guidance may not coincide with modern thinking and standards. I certainly didn't invent the solution to SLV + 1, that's just the way it was done in whatever tutorial I found.Here is an article on making the conversionhttp://vhdlguru.blogspot.com/2010/03/migrating-from-stdlogicvector-to.htmlNow the choice of SIGNED, UNSIGNED and STD_LOGIC_VECTOR becomes something to think about. My guess is that many of my registers should be declared as UNSIGNED and my delay timers should probably use VARIABLEs. OTOH, if it is just data with no manipulation (words coming in and out of memory), maybe SLV is still appropriate.

author: lem_ix
author_rank: Regular Contributor
timestamp: April 11, 2016, 05:24:58 pm
content: SLV+1 is just plain wrong. Remember you're dealing with hardware SLV could hold just about anything not just numbers. Variables in general are best avoided for synthesis. Keep your design synchronous and as hamster mentioned use synchronizers on external signals. Don't use gated clocks etc etc...Another good idea is to split the design into memory and combinational logic. Memory state gets updated on clock edge.If you're really interested I'd recommend reading:RTL Hardware Design Using VHDL: Coding for Efficiency, Portability, and ScalabilityPong P. Chu

author: rstofer
author_rank: Super Contributor
timestamp: April 11, 2016, 05:37:09 pm
content: Quote from: Kalvin on April 11, 2016, 08:57:47 amFor a clocked process, every signal will be evaluated when the clock changes state whether they are in the list or not. There is no need to have any other signal in the list with the exception of an asynchronous reset. If reset is synchronous, it can be omitted. HOWEVER, the synthesis tool will probably complain about signals missing from the sensitivity list.For combinatorial logic, at least in terms of simulation, you need every signal in the sensitivity list or the block will not be re-evaluated when a signal changes state.I go with the flow - if XST complains, I try to shut it up by adding signals. Every signal on the right hand side of an expression winds up in the list.

author: ebastler
author_rank: Super Contributor
timestamp: April 11, 2016, 05:59:26 pm
content: Quote from: hamster_nz on April 11, 2016, 09:35:27 am... and that did the trick!!!Your code works, and also my original version, once the incoming csync signal is properly synchronized as you suggested.isynccan be a variable or a signal, and the clock synchronization can be done viaif rising_edgeor viawait until clk='1'-- all fine.hamster_nz, I can't thank you enough for finding that one! I could probably have stared at my code for another couple of weeks without coming to the right conclusion... I am still surprised how an inconsistent read of csync (when read in a metastable state) can have such long-lasting consequences, but the two synchronizing flip-flops have certainly put things right.Thanks also to everyone else for your helpful hints. I did learn a few things in the process! I'll be back to (a) proudly present my final design, or (b) ask more questions when I hit the next roadblock. I wonder which outcome might be more likely?

author: rstofer
author_rank: Super Contributor
timestamp: April 11, 2016, 06:00:27 pm
content: Quote from: lem_ix on April 11, 2016, 05:24:58 pmWell, they certainly don't give that textbook away. $132 at Amazon, even more at Alibris!I'm going to recode my minicomputer project while changing from Spartan 3E to Artix-7 so I might as well take the opportunity to clean things up. The book will be here Wednesday.

author: rstofer
author_rank: Super Contributor
timestamp: April 11, 2016, 06:02:56 pm
content: Quote from: ebastler on April 11, 2016, 05:59:26 pmHave you synthesized it both ways and compare the resource usage?

author: ebastler
author_rank: Super Contributor
timestamp: April 11, 2016, 07:00:41 pm
content: Quote from: rstofer on April 11, 2016, 06:02:56 pmComparing anisynccounter declared asnaturalwith one declared as astd_logic_vectorresults in different structures, but essentially the same gate count:For anaturalcounter, the synthesis derives a 31-bit register and a 31-bit add/sub. (Not a full adder, it seems, but able to increment/decrement?) For astd_logic_vector, it creates a 32-bit updown counter and a 32-bit 4-to-1 multiplexer (not sure why the multiplexer?). The total gate count is just two more for the std_logic_vector approach, and would be the same if I had declared a 31-bit vector instead of 32 bits.Overall, I am reassured by the experience with the Xilinx synthesis I gained through this thread. The various constructs which one would naively assume to be equivalent in the low-level synthesis (natural vs. logic_vector, variable vs. signal, different clock sync constructs) do appear to be equivalent indeed.

author: ebastler
author_rank: Super Contributor
timestamp: April 24, 2016, 08:02:57 pm
content: A quick follow-up on my "video upscaler for PONG" project, which got over its first hurdle thanks to your help in this thread: This seems to work now!The attached picture shows the original arcade PONG board (1974 vintage), a low-cost Spartan 3A board (Numato Elbert), and a 4:3 TFT flatscreen happily working together. The FPGA magnifies every PONG pixel to 3*3 pixels on the XGA display, and adds nice scan-lines and subtle blurring in the scan direction (not visible in the photo unfortunately). As a result, the TFT certainly looks close enough to "vintage" for meNow on to building a flat, "picture frame"-style cabinet to show off the PCB in a playable frame...Thanks again for your help two weeks ago!