5
\$\begingroup\$

This clock mux is meant to allow glitch-free muxing between asynchronous clocks clk_a and clk_b via a (also asynchronous to both clocks) sel signal.

I'm looking for any feedback, but in particular I'm looking for confirmation that there can be no output glitches or metastability issues assuming synchronous/related paths meet timing and that one period of either clock is long enough for a 2-flop synchronizer to allow potential metastable states to settle.

Additionally, I'm interested in ways to improve the switchover time or reduce the FF/LUT utilization.

library ieee;
use ieee.std_logic_1164.all;

entity clk_mux is
   port(
      clk_a   : in  std_logic;
      clk_b   : in  std_logic;
      clk_o   : out std_logic;
      sel     : in  std_logic
   );
end clk_mux;

architecture rtl of clk_mux is
--combinational
   signal clk_i : std_logic;

   --async set for disabled clock's lock to ensure internal select can safely be turned low again on first rising edge of the new active clock
   signal lock_a  : std_logic; --despite being combinational, output always last's exactly 1/2 the period of the other clock and cannot have glitches (inputs never change near each other in time)
   signal lock_b  : std_logic;   

--FF   
   --ensures the disabled clock can't be re-enabled until the currently enabled clock is disabled
   signal force_a_lock : std_logic := '0'; 
   signal force_b_lock : std_logic := '1';

   --forces the respective clock being disabled high (and keeps it that way) its rising edge to prevent output glitches
   signal force_a_re : std_logic := '0'; 
   signal force_b_re : std_logic := '1';

   --releases the forcing on the falling edge (also to prevent output glitches)
   signal force_a_fe : std_logic := '0';
   signal force_b_fe : std_logic := '1';

   --sel is syncronized and pulse extended to 2 cycles of active clock to prevent internal select from changing right as final rising of edge of the old active clock happens
   signal sel_sync : std_logic_vector(1 to 3) := (others => '0');
   signal sel_ce : std_logic := '0'; --sync. CE used to pulse extend when both high pulses and low pulses need to be extended
begin
   process(clk_a, lock_a)
   begin
      if (lock_a='1') then
         force_a_lock <= '1';
      elsif (clk_a'event and clk_a='1') then
         force_a_lock <= not force_b_re; --release lock once b is being forced. asnyc input with possibly metastable output, so metastability must settle between here and force_a_re
      end if;
      if (clk_a'event and clk_a='1') then
         force_a_re <= force_a_lock or sel_sync(3); --changes to select can only be async when a is already locked, preventing input to force_a_re from changing. No metastability on the output
      end if;
      if (clk_a'event and clk_a='0') then
         force_a_fe <= force_a_re; --re to fe is twice as tight of timing as the other sync ff's. Shouldn't be difficlut to meet with no intermediary logic.
      end if;
   end process;

   process(clk_b, lock_b)
   begin
      if (lock_b='1') then
         force_b_lock <= '1';
      elsif (clk_b'event and clk_b='1') then
         force_b_lock <= not force_a_re; --release lock once a is being forced. asnyc input, so metastability must settle between here and force_b_re
      end if;
      if (clk_b'event and clk_b='1') then
         force_b_re <= force_b_lock or not sel_sync(3); --changes to select can only be async when b is already locked, preventing input to force_b_re from changing. No metastability on the output
      end if;
      if (clk_b'event and clk_b='0') then
         force_b_fe <= force_b_re; --re to fe is twice as tight of timing as the other sync ff's. Shouldn't be difficlut to meet with no intermediary logic.
      end if;
   end process;

   process(clk_i) --dynamic to ensure that the internal select is syncronous to force_*_re when it makes an immediate difference to the output. Also prevents the internal select from changing while switching over even when one clock is much slower or faster than the other
   begin
      if (clk_i'event and clk_i='1') then
         if (sel_ce='1') then
            sel_sync(3) <= sel_sync(2);
         end if;
         sel_sync(1 to 2) <= sel & sel_sync(1); --sync to avoid metastability from async (to either/both clocks) sel input
         sel_ce <= sel_sync(3) xnor sel_sync(2);
      end if;
   end process;


lock_a <= not force_b_re and force_b_fe; 
lock_b <= not force_a_re and force_a_fe; --anync reset for b's lock to ensure internal select can be safely turned high again on first rising edge of active clk_a

clk_i <= (force_a_re or force_a_fe or clk_a) and (force_b_re or force_b_fe or clk_b);
clk_o <= clk_i;

end rtl;

Test:

library ieee;
use ieee.std_logic_1164.all;
entity tb_clk_mux is
end tb_clk_mux;

architecture testbench of tb_clk_mux is
    component clk_mux is
        port(
            clk_a   : in  std_logic;
            clk_b   : in  std_logic;
            clk_o   : out std_logic;
            sel     : in  std_logic
        );
    end component;

    signal clk_a : std_logic := '0';
    signal clk_b : std_logic := '0';
    signal clk_o : std_logic := '0';
    signal sel   : std_logic := '0';
begin
    clk_a <= not clk_a after 10 ns; --periods arbitrary
    clk_b <= not clk_b after 23 ns;
    sel   <= not sel   after 200 ns;
    uut : clk_mux
    port map (
        clk_a => clk_a,
        clk_b => clk_b,
        clk_o => clk_o,
        sel   => sel
    );
end testbench;
\$\endgroup\$
5
\$\begingroup\$

I found a good paper some weeks ago for this topic, but currently I can't find the online source. While searching I found another paper1 on the net.

The presented solution should be glitch free, but the circuit is not hardened for meta stability problems. You can improve this by using double flip-flops.

Here is a schematic of the circuit in the paper that I could not find anymore.

Schematic


1 B. Jovanović and M. Damnjanović, "Glitch Free Clock Switching Techniques in Modern Microcontrollers," in Proceedings of the 5th Small Systems Simulation Symposium, 2014, pp. 119–122.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.