I would like to understand different approaches to implement a cross clock-domain counter. In all of the following possibilities I have:
clk_a : in std_logic;
clk_b : in std_logic;
reset : in std_logic;
-- cross-domain counter
signal frm_cnt : standard_logic_vector(7 downto 0);
-- control signal: increment counter (synced to clk_a)
signal frm_cnt_inc : std_logic;
-- control signal: decrement counter (synced to clk_b)
signal frm_cnt_dec : std_logic;
-- control signal, perform action on the counter
signal frm_cnt_modif : std_logic;
The approaches I consider are:
Non-clocked counter similar to the answer in How is this simple counter implemented on an FPGA without a clock?
frm_cnt_modif <= frm_cnt_inc or frm_cnt_dec; -- can also be xored
frame_counter : process(frm_cnt_modif, reset)
begin
if reset = RST_ACTIVE then
frm_cnt <= (others => '0');
elsif rising_edge(frm_cnt_modif) then
if frm_cnt_inc = '1' then
frm_cnt <= incr_vec(frm_cnt);
elsif frm_cnt_dec = '1' then
frm_cnt <= decr_vec(frm_cnt);
end if;
end if;
end process;
This simulates fine, and Vivado is able to synthesize it, however it complains about using frm_cnt_modif
as clock, which causes timing problems. Additionally the control signals have a chance of overlapping.
Another approach I have tried is to have separate counters in their respective clock domains and perform logic based on their difference. Here, I am concerned with counter overflow, so resetting them when no action on both sides is performed and they are equal seemed like a good solution, however I think that the signal controlling the reset will be subject to similar synchronization problems.
What I am experimenting right now is the 'synchronization to the faster clock'. I have a:
control_synchronizer : process(clk_b, reset)
begin
if reset = RST_ACTIVE then
frm_cnt_inc_sync <= '0';
elsif rising_edge(clk_b) then
if frm_cnt_inc_sync = '1' then
frm_cnt_inc_sync <= '0';
elsif frm_cnt_inc = '1' then
frm_cnt_inc_sync <= '1';
else
frm_cnt_inc_sync <= '0';
end if;
end if;
end process;
And the actual counter increment and decrement are performed in a process synchronized with clk_b
. This seems to work fine as long as clk_b > clk_a and clk_b < 2 * clk_a
.
Any other possible solutions and/or best practices? (I am aware that I should probably use grey-codes for the counter itself).
EDIT1:
After reply from Dave and reading http://fpgacenter.com/examples/basic/edge_detector.php I ended up with the following:
control_a : process(clk_a, reset)
begin
if reset = RST_ACTIVE then
frm_cnt_inc_d <= '0';
elsif rising_edge(clk_a) then
if frm_cnt_inc = '1' then
frm_cnt_inc_d <= '1';
else
frm_cnt_inc_d <= '0';
end if;
end if;
end process;
control_b : process(clk_b, reset)
begin
if reset = RST_ACTIVE then
frm_cnt_inc_sync <= '0';
frm_cnt_inc_sync_d <= '0';
elsif rising_edge(clk_b) then
frm_cnt_inc_sync_d <= frm_cnt_inc_sync;
-- this check was ensuring 1 to 1 pulse translation before rising edge detection
--if frm_cnt_inc_sync = '1' then
--frm_cnt_inc_sync <= '0';
if frm_cnt_inc = '1' then
frm_cnt_inc_sync <= '1';
else
frm_cnt_inc_sync <= '0';
end if;
end if;
end process;
frm_cnt_inc_result <= not(frm_cnt_inc_sync_d) and frm_cnt_inc_sync;
This seems safe for any clock values. What I do not understand however, is the purpose of the frm_cnt_inc_d
FF (code simulates perfectly fine without it). Anyone able to explain if it is indeed necessary?
Best Answer
Are you by any chance counting video frames in a frame buffer? I do this sort of thing all the time.
One way to pass a pulse from one clock domain to another is to turn it into an edge and then do edge detection on it. In domain A, use the pulse to toggle a FF. In domain B, synchronize the output through two FFs. Run the output of the synchronizer through an edge detector (FF and XOR gate).
You'll get a pulse in domain B for every pulse in domain A, as long as the pulses don't occur faster than either clock.
This is needed often enough that I've created a generic module to implement it.
xd_
is short for "cross-domain" — I have other modules in this library that perform similar functions.