freq counter using CPLD - Page 1
Forum section: electronics / projects

author: electros6
author_rank: Regular Contributor
timestamp: June 16, 2014, 02:29:56 am
content: hi guys,I am trying to do frequency counter using CPLD. I am newbie to CPLD and FPGA. I am using CPLD because of reduce cost ,complicity and high speed. I used xc9535xl to generate gate time using a 10MHz clock and its work fine. Then I tried to implement counter and data serializing to micro - controller using XC9572XL. I got an error. I tried a lot but in vein . somebody please spot the error please.my code is----------------------------------------------------------------------------------library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity counter isPort ( gate : in STD_LOGIC ;clk : in STD_LOGIC;reset : in STD_LOGIC;ser_en : out std_logic;ser_clk : in std_logic;ser_data : out std_logic);end counter;architecture Behavioral of counter issignal cnt : STD_LOGIC_VECTOR(23 downto 0) :="000000000000000000000000";signal ser_data_tmp : STD_LOGIC :='0';signal ser_en_tmp : std_logic := '0';beginser_data <= ser_data_tmp;ser_en <= ser_en_tmp;process(clk , reset ,gate, ser_clk)beginif gate = '0' and rising_edge(clk) thencnt <= cnt + '1';end if;if gate = '1' and reset = '0' thencnt <= "000000000000000000000000";end if;if gate = '1' thenser_en_tmp <='1';elseser_en_tmp <= '0';end if;if rising_edge(ser_clk )thenser_data_tmp <= cnt(23);cnt(23) <= cnt (22);cnt(22) <= cnt (21);cnt(21) <= cnt (20);cnt(20) <= cnt (19);cnt(19) <= cnt (18);cnt(18) <= cnt (17);cnt(17) <= cnt (16);cnt(16) <= cnt (15);cnt(15) <= cnt (14);cnt(14) <= cnt (13);cnt(13) <= cnt (12);cnt(12) <= cnt (11);cnt(11) <= cnt (10);cnt(10) <= cnt (9);cnt(9) <= cnt (;cnt(<= cnt (7);cnt(7) <= cnt (6);cnt(6) <= cnt (5);cnt(5) <= cnt (4);cnt(4) <= cnt (3);cnt(3) <= cnt (2);cnt(2) <= cnt (1);cnt(1) <= cnt (0);cnt(0) <= '0';end if;end process;end Behavioral;my error isERROR:Xst:827 - "E:/projects/frequency counter/freq counter/counter/counter.vhd" line 56: Signal cnt<10> cannot be synthesized, bad synchronous description. The description style you are using to describe a synchronous element (register, memory, etc.) is not supported in the current software release.-->gate is generated by the previous cpld , clk is the input clock of the counter, ser_en to indicate the complete of the count , ser_clk and ser_data for serializing and reset signal for Uc to reset the counter . I used Uc for displaying and user interface.One more dought , wheather an input clk is needed to CPLD to work or not.

author: Jay_Diddy_B
author_rank: Super Contributor
timestamp: June 16, 2014, 03:03:56 am
content: Hi,I am a bit rusty in VHDL, but if I look at these two lines:ser_data_tmp <= cnt(23);cnt(23) <= cnt (22);You aresimultaneouslytrying to copy cnt(23) to ser_data_tmpand at thesame timeyou are trying to replace the contents of cnt(23) with cnt(22)I suggest transferring the data from cnt(23) to ser_data_tmp on the rising edge of the ser_clk.and on the negative edge of ser_clk shifting the contents leftI am too rusty in VHDL to give you the correct syntax.Regards,Jay_Diddy_B

author: FrankBuss
author_rank: Unknown
timestamp: June 16, 2014, 06:32:09 am
content: First I would write it like this:Code:library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity counter isPort (gate : in STD_LOGIC ;clk : in STD_LOGIC;reset : in STD_LOGIC;ser_clk : in std_logic;ser_data : out std_logic);end counter;architecture Behavioral of counter issignal cnt : STD_LOGIC_VECTOR(23 downto 0) := (others => '0');beginprocess(ser_clk)beginif reset = '0' thencnt <= (others => '0');elseif gate = '0' thenif rising_edge(clk) thencnt <= cnt + '1';end if;elseif rising_edge(ser_clk )thenser_data <= cnt(23);cnt <= cnt(22 downto 0) & '0';end if;end if;end if;end process;end Behavioral;Much easier to read. But now I get the interesting error "ERROR:Xst:1534 - Sequential logic for node <cnt> appears to be controlled by multiple clocks.". Looks like this is a limitation of the CPLD and the ISE synthesis tool is too dumb to solve it. You can do it by hand:Code:library IEEE;use IEEE.STD_LOGIC_1164.ALL;use IEEE.STD_LOGIC_UNSIGNED.ALL;entity counter isPort (gate : in STD_LOGIC ;clk : in STD_LOGIC;reset : in STD_LOGIC;ser_clk : in std_logic;ser_data : out std_logic);end counter;architecture Behavioral of counter issignal cnt : STD_LOGIC_VECTOR(23 downto 0) := (others => '0');signal clk2 : std_logic;beginclk2 <= clk when gate = '0' else ser_clk;process(clk2, reset)beginif reset = '0' thencnt <= (others => '0');elsif rising_edge(clk2) thenif gate = '0' thencnt <= cnt + '1';elseser_data <= cnt(23);cnt <= cnt(22 downto 0) & '0';end if;end if;end process;end Behavioral;Note: usually gated clocks are bad, but should work for your application.

author: FrankBuss
author_rank: Unknown
timestamp: June 16, 2014, 06:35:56 am
content: PS: I removed "ser_en_tmp", because it has the same level as "gate", so you could use a wire outside the CPLD instead

author: AndyC_772
author_rank: Super Contributor
timestamp: June 16, 2014, 07:38:14 am
content: Quote from: Jay_Diddy_B on June 16, 2014, 03:03:56 amThis is completely valid. What it means is that ser_data_tmp gets the value which was held by cnt(23) at the instant the active clock edge occurred, and cnt(23) gets the value which was held by cnt(22).It's a major difference between coding for an FPGA and coding for a microprocessor. Similarly, the old exam question about exchanging the contents of two variables:a <= b;b <= a;...does actually work in VHDL. The two variables do indeed exchange values.To the OP: the problem you have here is an absolutely classic example of the issue faced by designers when a signal updated in one clock domain has to be read by some logic which operates in another - and it's NOT trivial to solve.First of all, make your life manageable by learning how to use vectors rather than lots of individual assignments. Seriously.The real issue here, though, is that you can't have a register which is updated on the edges of two different clocks. The underlying architecture is based on D-type latches, and they only have one clock input. The output takes the value of the input on an active clock edge, and the device fabric probably also supports an asynchronous set or reset. But it won't support two clocks. None do.What you need to do, therefore, is have two registers. The logic you need is something along the lines of this (grossly oversimplified to highlight the important point):Code:IF rising_edge (clk) THENcnt <= cnt + 1;END IF;IF rising_edge (snapshot_signal) THENcnt_snapshot <= cnt;END IF;IF rising_edge (ser_clk) THENser_data_tmp <= cnt_snapshot (23);cnt_snapshot (23 DOWNTO 1) <= cnt_snapshot (22 DOWNTO 0);END IF;Code written along these lines should compile and run, though there's a lot more to it than this. You need to consider, for example, what happens if the snapshot is taken at the same time as a clock pulse comes in, and the counter is updating. How will you tell? What if, at the time of the snapshot, some bits have updated and others haven't?What will you do about metastability? If you find your design is working 99.9% of the time but produces occasional errors, then it's likely that metastability is your problem.On occasion I've worked around problems like this by having a single clock in the device running really fast, and then I treat the external clocks as asynchronous data signals instead. So then, my code becomes:QuoteThis works well provided you know the external clocks are slow enough compared to the fastest clock you can have inside the device. It's a technique I've used for all manner of slow-ish I/O and can save a lot of headaches, as you know for certain that when you sample the value of a counter, you'll get a valid sample rather than one in which some of the bits might have updated while others haven't.

author: hamster_nz
author_rank: Super Contributor
timestamp: June 16, 2014, 09:34:09 am
content: You have got quite a few options on how to capture your frequency too.If you have the resources to implement a 1024x5 bits of ROM (eg maybe a BRAM block), possibly the very best way is to have a short grey counter (e.g. 5 bits) driven by the input signal and then sample it very often (e.g. 10MHz). You can then look up the difference between the two samples in the 10-bit look up table, to give the number of ticks between the two samples. These can then be accumulated for the desired gate time. If implemented properly this will give very few synchronisation problems and should be able to count a very fast clock as well as a very slow one.Here are some other options...If the frequency being counted is significantly less than half your clock speed then you can just look for rising edges.Code:process(clk)beginif rising_edge(clk) thenif gate_count = 9999999 then -- output the count every 10,000,000 cyclesif samples(samples'high downto samples'high-1) = "01" thenedges <= count + 1;elseout <= count;end if;count <= (others => '0');gate_count <= (others => '0');elseif samples(samples'high downto samples'high-1) = "01" thencount <= count + 1;end if;gate_count <= gate_count + 1;end if;samples <= samples(samples'high-1 downto 0) & signal_in;end if;end process;If the frequency is around half the clock speed or higher, then you can't just look at the input, as you will miss edges. You can however prescale the input signal in the clock domain of the input:Code:process(signal_in)beginif rising_edge(signal_in) thenprescaler <= prescaler+1;end if;end process;And then the same code as above, except replace:Code:samples <= samples(samples'high-1 downto 0) & signal_in;withCode:samples <= samples(samples'high-1 downto 0) & prescaler(prescaler'high);You still have to synchronize the top bit of the precaler counter as you bring it into the other clock domain. Using a prescaler has the downside that the resolution also drops too.There is a whole lot of other options. You could also handshake between clock domains, have a pair of signals where one is "give me your counter" and the other is "your counter is ready". That only works if there is guaranteed to be a few clock ticks on the input allow the logic to do something. For example:Code:process(signal_in)beginif rising_edge(signal_in) thenif give_me_your_counter_sync = '1' and your_counter_is_ready <= '0' thensamples <= count+1;count <= (others => '0');your_counter_is_ready <= '1';elsecount <= count + 1;if give_me_your_counter_sync = '0' thenyour_counter_is_ready <= '1';end if;end if;give_me_your_counter_sync <= give_me_your_counter_almost_sync;give_me_your_counter_almost_sync <= give_me_your_counter;end if;end process;As long as you get a few clock pulses (and you synchronise 'your_counter_is_ready' in the other clock domain, assert 'give_me_your_counter_sync', wait for 'your_counter_is_ready' to be asserted and then grab the value in 'samples' before dropping 'give_me_your_counter_sync' then wait for 'your_counter_is_ready' to be dropped.The problem with this implementation is that your gate time is now dependent on the time between rising clock edges, rather than being a defined time window.On more advanced devices you use a DDR input or serialiser to get multiple input bits each cycle...

author: hamster_nz
author_rank: Super Contributor
timestamp: June 16, 2014, 10:15:45 am
content: Quote from: AndyC_772 on June 16, 2014, 07:38:14 amIf you find your design works 99.9% of the time it is most likely that you have used an async signal (or one from a different clock domain) without correctly synchronizing it to the local clock. If it works 99.9999999% of the time then it is most likely metastability, which is when a flip-flop doesn't flip or flop correctly due to setup/hold violations.I once did a design to find out how unlikely it is, IIRC I set up two FPGA boards and capturing a 32 MHz signal from one board at 200 MHz in the other. The signal was first captured in register, and then the output from that register was captured in eight other registers deeper in the design, and checked to see if they all agreed. I managed to capture meta-stability events on a timescale of one every hour or so - literally at odds of around one in one hundred billion!You will almost-never-never-never-never see true metastability error from a push button or switch, or just sampling a counter every second - you are more likely to find $10 on the footpath and use it to buy the winning lottery ticket. Those other errors are just "you didn't synchronize your signals at all" errors.

author: hamster_nz
author_rank: Super Contributor
timestamp: June 22, 2014, 10:53:26 am
content: Sorry to resurrect a quiet thread, but I'm just in the final steps of testing my FPGA based frequency counter that was inspired by this thread.Should be good for counting well-behaved clocks of up to just shy of 400 MHz... if you can get them into the FPGAhttp://hamsterworks.co.nz/mediawiki/index.php/High_Speed_Frequency_Counter

author: miguelvp
author_rank: Super Contributor
timestamp: June 22, 2014, 07:21:44 pm
content: Nice

author: Jay_Diddy_B
author_rank: Super Contributor
timestamp: June 22, 2014, 09:31:47 pm
content: Hi Hamster_nz and the group,I had a quick look at the projects on hamster-nz's website. There are nice projects in VHDL.Thank you for sharing.They would have been very useful when I was learning VHDL a few years ago.Regards,Jay_Diddy_B

author: hamster_nz
author_rank: Super Contributor
timestamp: June 23, 2014, 09:31:20 am
content: Finally got round to testing it (well, with whatever I had to hand) - seems to work OK at about 320 MHz over LVDS. My 100 MHz scope just doesn't cut itI've put a few photos and the design used to generate the 320 MHz on the second board.

author: curiousbob
author_rank: Newbie
timestamp: June 24, 2014, 04:23:47 am
content: Hamster,I started tinkering with FPGAs not long ago ( ~1.5 yrs ago). I am at a point where I can understand & appreciatewhat you have done... clever.I'm sure you know this but for others here is a binary to gray conversion formula:gray = (bin >>1) ^ bin

author: FrankBuss
author_rank: Unknown
timestamp: June 24, 2014, 05:32:56 am
content: Quote from: curiousbob on June 24, 2014, 04:23:47 amYes, Wikipedia knows this, too:http://en.wikipedia.org/wiki/Gray_code#Converting_to_and_from_Gray_codeInteresting is the Wikipedia reference to faster methods to convert from Gray code back to binary:http://aggregate.org/MAGIC/#Gray%20Code%20ConversionBoth tricks might save some logic compared to hamster's current lookup-table implementation, and the VHDL source code would be shorter. The MAGIC webpage has some interesting other binary formulas as well.

author: hamster_nz
author_rank: Super Contributor
timestamp: June 24, 2014, 09:41:13 am
content: Quote from: curiousbob on June 24, 2014, 04:23:47 amI did not know that! Thanks!When I wrote it I was more concerned about being explicit about the coding system. If I didn't know gray code and I saw the formula would I would have to write out all the combinations before I could see how the heck it solved the clock domain crossing problem.... and if you use a formula doesn't really make much difference to the design's speed or logic resource usage, only code source size. The LUT6s in the Spartan 6 logic blocks get configured as two 5-input LUTs. This allows 5-bit binary to gray conversion (or gray to binary conversion) to be implemented using only two LUT6s (as the MSB is the same in in both coding schemes).Mike