Many of our FPGA designs are multi-clock, meaning several clocks within the design, which can introduce clock domain crossing challenges. When architecting the design and especially the clocking network, we want to ensure we use the most appropriate device feature.
When providing integer divisions of clock frequency, we can use an MMCM or PLL, although this is wasteful of resources. We could also use a simple counter to provide the simple division in the device logic and then use an appropriate clock buffer.
If working with 7 series devices, there is a more efficient method of working with the counters and global buffers to provide integer clock division. We can do a similar method with UltraScale devices, but these have more advanced capabilities, which we will look at in another blog.
To get started with integer clock division, we need to leverage the capabilities provided by the BUFG_CE. The BUFG_CE provides a global clock buffer with an enables line, and we can drive the enable line if we wish to provide an integer division. The CE input can be enabled by a counter which then provides the division required.
This division will not provide a 50:50 waveform but instead, a duty cycle of 1/2*N, where N is the divisor. Thus, if we a divide by 2, we end up with a quarter-duty cycle. This is important to understand in order to constrain the generated clock correctly.
For this example, I have created a simple element of RTL code that uses a BUFG on the input clock. The output from this BUFG is used to drive the clock division logic. The output from the clock division logic is then used as the enable drive on the BUFG_CE. Using the output of the BUFG to drive the clock division logic ensures that the faster clock and the divided clock are phase aligned.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
library UNISIM;
use UNISIM.VComponents.all;
entity bufgce_demo is
generic(
g_div_factor : integer := 2 --div by 32
);
port (
i_clk : in std_logic;
o_clk_fast : out std_logic;
o_clk_div : out std_logic
);
end bufgce_demo;
architecture Behavioral of bufgce_demo is
signal s_counter : unsigned(7 downto 0):=(others=>'0');
signal s_int_clk : std_logic;
signal s_ce : std_logic;
begin
div_cnt : process(s_int_clk)
begin
if rising_edge(s_int_clk) then
s_ce <= '0';
if s_counter = (g_div_factor-1) then
s_ce <= '1';
s_counter <= (others=>'0');
else
s_counter <= s_counter + 1;
end if;
end if;
end process;
BUFG_inst : BUFG
port map (
O => s_int_clk, -- 1-bit output: Clock output
I => i_clk -- 1-bit input: Clock input
);
clk_div_inst : BUFGCE
port map (
O => o_clk_div, -- 1-bit output: Clock output
CE => s_ce, -- 1-bit input: Clock enable input for I0
I => i_clk -- 1-bit input: Primary clock
);
o_clk_fast <= s_int_clk;
end Behavioral;
Elaborating this design looks similar to what we expected in the design concept.
To test this, we can write a simple test bench to stimulate the faster clock and set the generic for the division. This allows us to try several different division factors in the test bench.
With this running as expected, the next element is to define the constraints. For this example, it is critical that the duty cycle is constrained correctly. Therefore, I used the following constraints:
create_generated_clock -name clk_div -source [get_pins clk_div_inst/I0] -edges {1 2 5} -edge_shift {0.000 0.000 0.000} [get_pins clk_div_inst/O]
I used the edges element of the constraint, which defines the rising (1), falling (2), and next rising(3) in place of the duty cycle. I did this because the duty cycle might become difficult to resolve sometimes, so defining the edges makes it easier.
Correctly providing this information to Vivado helps in its timing closure algorithms.
In next week’s blog, we will look at the UltraScale clocking resources and how we can divide clocks even easier in the UltraScale architecture.
Workshops and Webinars
If you enjoyed the blog why not take a look at the free webinars, workshops and training courses we have created over the years. Highlights include
Introduction to Vivado learn how to use AMD Vivado
Ultra96, MiniZed & ZU1 three day course looking at HW, SW and Petalinux
Arty Z7-20 Class looking at HW, SW and Petalinux
Mastering MicroBlaze learn how to create MicroBlaze solutions
HLS Hero Workshop learn how to create High Level Synthesis based solutions
Perfecting Petalinux learn how to create and work with petalinux OS
Embedded System Book
Do you want to know more about designing embedded systems from scratch? Check out our book on creating embedded systems. This book will walk you through all the stages of requirements, architecture, component selection, schematics, layout, and FPGA / software design. We designed and manufactured the board at the heart of the book! The schematics and layout are available in Altium here Learn more about the board (see previous blogs on Bring up, DDR validation, USB, Sensors) and view the schematics here.
Comments