Timing Problems with counter

S

Steffen Koepf

Hello,

what i need is a counter that counts from a preset value down to 0
with a clock outside of the system clock domain. The counter run
on a Cyclone III, 15 out of 16 of the counters work well, but one
counts much too slow. Here is the relevant part of the code:


-- transfer clock domain, fcntnext is in the system clockdomain
cntrclk: process (clk, rst)
begin
if (rst = '1') then
fcntnext <= '0';
fcnttoggle <= '0';
elsif falling_edge(clk) then
fcntnext <= '0';
if fcnttoggle /= fcounter then
fcnttoggle <= fcnttoggle xor '1';
if fcnttoggle = '1' then
fcntnext <= '1';
end if;
end if;
end if;
end process cntrclk;

delaycounter: process (clk, rst)
begin
if (rst = '1') then
dcntFin <= '0';
dcounter <= (others => '0');
elsif rising_edge(clk) then
if cntr_reload = '1' then
dcntFin <= '0';
-- Load counter with previous stored value
dcounter <= dcounterLoadVal;
elsif dcounter = std_logic_vector(to_unsigned(0, dcounter_bits)) then
dcntFin <= '1';
elsif fcntnext = '1' then
dcounter <= std_logic_vector(unsigned(dcounter) - 1);
end if;
end if;
end process delaycounter;

all signals are std_logic, and dcounter, dcounterLoadVal are std_logic_vector.

The FPGA runs with a system clock (clk) of 100 MHz.
The fcntnext signal is one clock cycle high (from falling_edge to
falling_edge) after every rising clock edge of the fcounter clock.
The fcounter clock is much slower than the system clock (max 1 MHz
with a Duty-Cycle of 50%).
Has anyone advices?

Best regards,

Steffen
 
M

Mike Treseler

Steffen said:
what i need is a counter that counts from a preset value down to 0
with a clock outside of the system clock domain.

A clock outside of the system clock domain
must be used as in input and synchronized
to the system clock.

-- Mike Treseler
 
P

Pieter Hulshoff

Hello Steffen,
what i need is a counter that counts from a preset value down to 0
with a clock outside of the system clock domain. The counter run
on a Cyclone III, 15 out of 16 of the counters work well, but one
counts much too slow. Here is the relevant part of the code:

Please don't use both flanks of a clock, and properly transfer signals from one
clock domain to the next before using them or you will run into timing and/or
meta-stability issues. As an example (I hope I understood your code correctly,
and I don't use asynchronous resets):

delaycounter: PROCESS IS
BEGIN
WAIT UNTIL clk = '1';
fcounter_c2c <= fcounter;
fcounter_meta <= fcounter_c2c;
fcounter_d <= fcounter_meta;
IF cntr_reload = '1' THEN
dcntFin <= '0';
dcounter <= dcounterLoadVal;
ELSIF unsigned( dcounter ) = 0 THEN
dcntFin <= '1';
ELSIF fcounter_meta = '1' AND fcounter_d <= '0' THEN -- rising edge fcounter
dcounter <= std_logic_vector( unsigned( dcounter ) - 1 );
END IF;
IF rst = '1' THEN
dcntFin <= '0';
dcounter <= (OTHERS => '0');
END IF;
END PROCESS delaycounter;

Keep in mind that since cntr_reload is not synchronized with regards to
fcounter, your counter is never completely accurate. Also, are you sure you want
the reset value of dcntFin to be 0 in stead of 1? What is that signal used for
anyway?

Kind regards,

Pieter Hulshoff
 
S

Steffen Koepf

Hello Pieter,

Pieter Hulshoff said:
Please don't use both flanks of a clock,

Why should one not use this? I know falling edges are not supported
on some devices, but when it is supported?
clock domain to the next before using them or you will run into timing and/or
meta-stability issues. As an example (I hope I understood your code correctly,
and I don't use asynchronous resets):

In the mean time i improved my code a bit and added a rising-edge
synchronizer:

-- transfer clock domain, fcntnext is in the system clockdomain
cntrclk: process (clk, rst)
begin
if (rst = '1') then
clksyncstage1 <= '0';
clksyncstage2 <= '0';
edgedetectff <= '0';
elsif falling_edge(clk) then
clksyncstage1 <= fcounter;
clksyncstage2 <= clksyncstage1;
edgedetectff <= clksyncstage2;
end if;

end process cntrclk;


cntrclkedge: process (clksyncstage2, edgedetectff)
begin
fcntnext <= clksyncstage2 and (not edgedetectff);

end process cntrclkedge;

That solved my problem and the counter works fine now:

delaycounter: process (clk, rst)
begin
if (rst = '1') then
dcntFin <= '0';
dcounter <= (others => '0');
elsif rising_edge(clk) then
if cntr_reload = '1' then
dcntFin <= '0';
dcounter <= dcounterLoadVal; -- Load counter with previous stored val
elsif dcounter = std_logic_vector(to_unsigned(0, dcounter_bits)) then
dcntFin <= '1';
elsif fcntnext = '1' then
dcounter <= std_logic_vector(unsigned(dcounter) - 1);
end if;
end if;
end process delaycounter;

Your code should do the same, thank you.
Keep in mind that since cntr_reload is not synchronized with regards to
fcounter, your counter is never completely accurate. Also, are you sure you want
the reset value of dcntFin to be 0 in stead of 1? What is that signal used for
anyway?

The signal dcntFin is used as input for a gate for error-signals (from
an analogous comparator outside of the fpga). After a enable signal
is released, the counter starts to run. As long as enable is low
(then cntr_reload = 1) or the counter is running for a certain time
after enable switched to 1, the error-signals are "blanked out"
so that a overshoot of switching power supplies after switching
on (by enable) does not trigger an error.
The counter is a 16 bit counter and it is not important if one
or two clocks are not counted/wrong counted.


Thank you,

Steffen Koepf
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,072
Latest member
trafficcone

Latest Threads

Top