Timing Problems with counter

S

Steffen Koepf

Guest
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
 
Steffen Koepf wrote:

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
 
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
 
Hello Pieter,

Pieter Hulshoff <phulshof@xs4all.nl> wrote:
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
 

Welcome to EDABoard.com

Sponsor

Back
Top