Search code examples
vhdlclockconventionsxilinx-isespartan

Alternative method for creating low clock frequencies in VHDL


In the past I asked a question about resets, and how to divide a high clock frequency down to a series of lower clock square wave frequencies, where each output is a harmonic of one another e.g. the first output is 10 Hz, second is 20 Hz etc.

I received several really helpful answers recommending what appears to be the convention of using a clock enable pin to create lower frequencies.

An alternative since occurred to me; using a n bit number that is constantly incremented, and taking the last x bits of the number as the clock ouputs, where x is the number of outputs.

It works in synthesis for me - but I'm curious to know - as I've never seen it mentioned anywhere online or on SO, am I missing something that means its actually a terrible idea and I'm simply creating problems for later?

I'm aware that the limitations on this are that I can only produce frequencies that are the input frequency divided by a power of 2, and so most of the time it will only approximate the desired output frequency (but will still be of the right order). Is this limitation the only reason it isn't recommended?

Thanks very much!

David

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
library UNISIM;
use UNISIM.VComponents.all;
use IEEE.math_real.all;

ENTITY CLK_DIVIDER IS
    GENERIC(INPUT_FREQ : INTEGER;       --Can only divide the input frequency by a power a of 2 
            OUT1_FREQ  : INTEGER
    );
    PORT(SYSCLK  : IN  STD_LOGIC;
         RESET_N : IN  STD_LOGIC;
         OUT1    : OUT STD_LOGIC;       --Actual divider is  2^(ceiling[log2(input/freq)])
         OUT2    : OUT STD_LOGIC);      --Actual output is input over value above
END CLK_DIVIDER;

architecture Behavioral of Clk_Divider is
    constant divider      : integer                             := INPUT_FREQ / OUT1_FREQ;
    constant counter_bits : integer                             := integer(ceil(log2(real(divider))));
    signal counter        : unsigned(counter_bits - 1 downto 0) := (others => '0');
begin
    proc : process(SYSCLK)
    begin
        if rising_edge(SYSCLK) then
            counter <= counter + 1;
            if RESET_N = '0' then
                counter <= (others => '0');
            end if;
        end if;
    end process;
    OUT1 <= counter(counter'length - 1);
    OUT2 <= not counter(counter'length - 2);
end Behavioral;

Solution

  • Functionally the two outputs OUT1 and OUT2 can be used as clocks, but that method of making clocks does not scale and is likely to cause problems in the implementation, so it is a bad habit. However, it is of course important to understand why this is so.

    The reason it does not scale, is that every signal used as clock in a FPGA is to be distributed through a special clock net, where the latency and skew is well-defined, so all flip-flops and memories on each clock are updated synchronously. The number of such clock nets is very limited, usually in the range of 10 to 40 in a FPGA device, and some restrictions on use and location makes it typically even more critical to plan the use of clock nets. So it is typically required to reserve clock nets for only real asynchronous clocks, where there is no alternative than to use a clock net.

    The reason it is likely to cause problems, is that clocks created based on bits in a counter have no guaranteed timing relation. So if it is required to moved data between these clock domains, it requires additional constrains for synchronization, in order to be sure that the Clock Domain Crossing (CDC) is handled correctly. This is done through constrains for synthesis and/or Static Timing Analysis (STA), and is usually a little tricky to get right, so using a design methodology that simplifies STA is habit that saves design time.

    So in designs where it is possible to use a common clock, and then generate synchronous clock enable signals, this should be the preferred approach. For the specific design above, a clock enable can be generated simply by detecting the '0' to '1' transition of the relevant counter bit, and then assert the clock enable in the single cycle where the transition is detected. Then a single clock net can be used, together with 2 clock enables like CE1 and CE2, and no special STA constrains are required.