top of page

MicroZed Chronicles: UltraScale and UltraScale+ Clock Division

Last week we looked at how we could use 7 series clocking resources to provide integer clock division without using MMCM. In this week’s blog, we are going to look at something similar in the UltraScale and UltraScale+ device architectures.


Much like the BUFG_CE we used last week, there is a clocking primitive in the UNISIM library that we can use for dividing the incoming clock by between 1 and 8 called the BUFGCE_DIV.


There are some nice features of the BUFGCE_DIV which can be of use in our designs. Following the reset release, the clock output will transition from low to high on the next clock edge. This means that if we have several BUFGCE_DIVs implemented within our design, they will be aligned regardless of the division factor.


When running a simulation of the BUFGCE_DIV, it may appear the duty cycle is 50:50 however, it is not for odd division factors. For odd division, this means the high cycle is high for one less clock than during the low cycle. This is just like last week when we needed to think about the constraints and correctly inform Vivado about the clock and its edges.

create_clock -period 10.000 -name clk_In -waveform {0.000 5.000} [get_ports -filter { NAME =~  "*" && DIRECTION == "IN" }]
create_generated_clock -name clk_div -source [get_pins BUFGCE_DIV_inst/I] -edges {1 7 15} -edge_shift {0.000 0.000 0.000} [get_pins BUFGCE_DIV_inst/O]

I created a simple design which implements a BUFGCE_DIV component to demonstrate how the it can be used in our applications. The test bench for this implements several BUFGCE_DIVs, each with a different division factor from 1 to 8.

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

Library UNISIM;
use UNISIM.vcomponents.all;

entity bufg_div is 
Generic ( 
    div : natural range 1 to 8 := 7
);
Port ( 
    clk_in : in std_logic;
    clk_div : out std_logic
);
end bufg_div;

architecture Behavioral of bufg_div is

signal clk_ce  : std_logic := '1';
signal clk_clr : std_logic := '0';

begin

   BUFGCE_DIV_inst : BUFGCE_DIV
   generic map (
      BUFGCE_DIVIDE => div,         
      IS_CE_INVERTED => '0',      
      IS_CLR_INVERTED => '0',     
      IS_I_INVERTED => '0',       
      SIM_DEVICE => "ULTRASCALE"  
   )
   port map (
      O => clk_div,    
      CE => clk_ce,    
      CLR => clk_clr,  
      I => clk_in      
   );
   
end Behavioral;

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

library UNISIM;
use UNISIM.VComponents.all;

entity tb_sim is
--  Port ( );
end tb_sim;

architecture Behavioral of tb_sim is

component bufg_div is 
Generic ( 
    div : natural range 1 to 8
);
Port ( 
    clk_in : in std_logic;
    clk_div : out std_logic
);
end component;

signal s_clk      :  std_logic :='0';
signal s_clk_div  :  std_logic_vector(8 downto 1);


constant clk_period : time := 10 ns;

begin

s_clk <= not s_clk after (clk_period/2);

g1: for i in 1 to 8 generate
    uut:  bufg_div generic map(
        div => i
    )
    port map(
        clk_in       => s_clk,
        clk_div      => s_clk_div(i)
    );
end generate;

end Behavioral;

This elaborates to the following

In the simulation we can see the generate loop which creates the eight different implementations of the BUFGCE_DIV. Each of these has a different divisor and we can see that the first edge for all of the clocks is initially aligned by running the simulation again. Along with being an integer division, they are all aligned and generated from the same clock input so they can be treated as being in the same clock domain. This is good as it simplifies the CDC analysis which needs to be performed as the design progresses through verification and validation.

When constraining the BUFGCE_DIV, I again used the edges to help define transition points in the signal. This is beneficial since we are working with asymmetrical duty cycles which vary for odd and even division factors. One important part when generating these constraints is to remember to count both rising and falling edges. If we only count the rising edge, the constraint will be incorrect.


For this example, I set the generic to be a division of 7 as that provides an asymmetric waveform output. The input clock was defined as 100 MHz, so we should be getting 14.2857MHz from the BUFGCE_DIV.


By looking at the results of the simulation, we can see we have a 3-clock period high followed by a 4-clock cycle low. This provides us with a 70 ns clock which equates to 14.286MHz waveform in simulation.


This blog has introduced a clocking element which can help divide incoming clocks while freeing up the MMCM for more advanced clocking needs.


This also provides the developers with a nice and effective tool for clock division when working with more unique and challenging environments.


This is a nice simple technique we use on some of our production designs!


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

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.


0 comments
bottom of page