01signal.com

The clock period constraint, and clock objects

This page belongs to a series of pages about timing. The previous pages explained the theory behind timing calculations, discussed the clock period constraint, and showed the principles of timing closure. It's now time to start with the technical details of timing constraints.

The meaning of create_clock as a Tcl command

The previous pages have all revolved around this timing constraint:

create_clock -period 4 -name clk [get_ports clk]

I've deliberately not discussed the syntax of this row until now. So it's time to explain what this really means.

This timing constraint is written in SDC (Synopsys Design Constraints) format, which is the most common format for timing constraints. Vivado and Quartus use this format, as well as several other FPGA tools.

An SDC file is in essence a script that is written in Tcl. Hence the content of an SDC file is a short computer program, and not just a collection of information. However, the capabilities of an SDC file as a script are limited to a small subset of commands, that are intended for writing constraints: Not everything that is allowed in a Tcl script can be done in an SDC file.

The create_clock command is used to define a timing constraint. But in fact, this command tells the FPGA software to create a new clock object. And the word "object" means what it usually means in terms of software engineering. The new clock object is hence something that is stored in the Tcl interpreter's memory as an object that has its own properties.

For example, the part in the create_clock command that says "-name clk" gives the value "clk" to the property that is called "name". Recall from one of the previous pages that this name was used in the timing reports: The name "clk" appeared along with timing paths that were calculated because of this constraint (or more precisely, because of this clock object).

Later on, we saw that there were other names of clocks, for example clk_out1_clk_wiz_1 and clk_out2_clk_wiz_1. These were in fact names of other clock objects, which were created automatically by the tools.

There's a Tcl command for listing all clocks: get_clocks. So with the example with two clocks from the previous page, this is a session on Vivado's Tcl console:

> get_clocks
clk clkfbout_clk_wiz_1 clk_out1_clk_wiz_1 clk_out2_clk_wiz_1

get_clocks and similar commands are explained in more detail on the next page.

It's also possible to view the properties of these objects. There's no need to understand all of these properties: I'm showing this just to make the point that a clock is an object. Personally, I never had any need to manipulate any object's property directly.

> report_property [get_clocks clk]
Property           Type     Read-only  Value
CLASS              string   true       clock
INPUT_JITTER       double   true       0.040
IS_GENERATED       bool     true       0
IS_PROPAGATED      bool     true       1
IS_USER_GENERATED  bool     true       0
IS_VIRTUAL         bool     true       0
NAME               string   true       clk
PERIOD             double   true       4.000
SOURCE_PINS        string*  true       clk
SYSTEM_JITTER      double   true       0.050
WAVEFORM           double*  true       0.000 2.000

> report_property [get_clocks clk_out1_clk_wiz_1]
Property           Type     Read-only  Value
CLASS              string   true       clock
EDGES              int*     true       1 2 3
EDGE_SHIFT         double*  true       0.000 2.000 4.000
INPUT_JITTER       double   true       0.000
IS_GENERATED       bool     true       1
IS_INVERTED        bool     true       0
IS_PROPAGATED      bool     true       1
IS_RENAMED         bool     true       0
IS_USER_GENERATED  bool     true       0
IS_VIRTUAL         bool     true       0
MASTER_CLOCK       clock    true       clk
NAME               string   true       clk_out1_clk_wiz_1
PERIOD             double   true       8.000
SOURCE             pin      true       pll_i/inst/mmcme3_adv_inst/CLKIN1
SOURCE_PINS        string*  true       pll_i/inst/mmcme3_adv_inst/CLKOUT0
SYSTEM_JITTER      double   true       0.050
WAVEFORM           double*  true       0.000 4.000

The important thing to realize is that create_clock just creates an object. The parameters of this command only determine how to set this object's properties. For example, the part that says "-period 4" (in the timing constraint that I've shown repeatedly) just means that a certain property, which is called "PERIOD", should have the value 4.

In case you want to try these Tcl commands yourself, note that there are differences between the FPGA tools.

In Vivado, these commands can be made only after opening the Implemented Design.

In Quartus, first open the TimeQuest Timing Analyzer, and then click Create Timing Netlist, Read SDC File and Update Timing Netlist. Then try some commands on the Tcl console, for example:

> join [ query_collection -all [ get_clocks ] ] "\n"
> get_clock_info -waveform [get_clocks clk]

The meaning of the word "clock"

When FPGA tools use the word "clock", that usually means a clock object, and not the physical signal inside the FPGA. This is true in particular in timing reports.

Recall from the previous page that I used the term "theoretical clocks" a few times. These are in fact clock objects. They are called "clocks" in the timing report, but their use in the timing analysis indicates that they are just containers of information.

So what is the connection between these clock objects and real signals? We've already the names of clock objects in the timing analysis. How does all this work together?

When the tools carry out the static timing analysis of the design, all paths are examined. If a path starts with a flip-flop, the tools examine the signal (i.e. the net) that is connected to the flip-flop's clock input: Is there any clock object that is related to this signal? For example, when the signal is @clk, the related clock object is the one that was given the name "clk" with the create_clock command. After finding the relevant clock object, the tools can fetch the necessary information from this object's properties.

The same thing happens with the flip-flop at the end of the path. So now the tools have the two clock objects that correspond to the path. With the information from these objects, the tools carry out the timing analysis.

And of course, the same procedure applies to any sequential element, not just flip-flops.

Why is it important to understand this? Among others, because there is sometimes an error message in the timing report that says that there are registers with no clock. Usually, that doesn't mean that there is a flip-flop that has nothing connected to its clock input. Rather, this means that the tools didn't find a clock object that is related to this clock input. In other words, the tools didn't find any information about this flip-flop's clock input. So the problem is usually not in the logic design, but a timing constraint is missing (or written incorrectly).

It's worth saying this again: When it says "clock" in a timing report, that doesn't mean that there is a signal with that name in the logic design, but that a clock object has been created with that name. How do we know which signal? That's the next topic.

Whose clock is this?

One of the things that makes the timing report difficult to read is the names of the clocks. Most of the clock signals in a logic design are created by a PLL, and we've already seen that the name that appears in the timing report can be unhelpful. Most FPGA tools allow renaming the clock objects by adding commands to the SDC file, but in most projects this is not done. And Golden Rule #4 is to refrain from things that are special to your project.

The problem with names becomes even more difficult when the source of the clock is an IP block (e.g. a Gigabit transceiver, a PCIe block or an on-chip processor core). In this case the name of the clock often says very little about where it came from and what it relates to.

So how is this problem solved? Let's start with the simplest situation, when the clock's name comes from the create_clock command in our own SDC file. This is the same timing constraint once again:

create_clock -period 4 -name clk [get_ports clk]

The last part in this command is "[get_ports clk]". In the Tcl language, square brackets means to execute the content of the brackets as a Tcl command, and then use the result of this command in place of these brackets.

The get_ports command finds an I/O port with the name "clk". The result of this command is the object that represents this port. So in the create_clock command above, this object is an argument to this command. This is how create_clock makes the connection between the clock object and a real signal.

Note that both the name of the port and the name of the object are "clk". It's not required that these are the same, but it's recommended to do so: The object's name appears in the timing reports. The name of the port is therefore usually the best choice.

It is also possible to use net objects and pin objects as the identifier of a signal. This is common in timing constraints that are generated automatically on behalf of IP blocks. However, if you feel the need to do this in your own constraints, there is good chance that you're doing something wrong.

So if a create_clock command was used in an SDC file, it's easy to tell which signal the clock object relates to. But what about clock objects that were created automatically by the tools?

In this case, the best way to recognize the clock is to look in the timing report. For example, which clock object is related to @pll_clk_8 in the example from the previous page? An easy way to find out is to perform a text search in the timing report. So searching for "pll_clk_8", the following part is found:

Location          Delay type                Incr(ns)  Path(ns)    Netlist Resource(s)
--------------------------------------------------------------  -------------------
                  (clock clk_out1_clk_wiz_1 rise edge)
                                              16.000    16.000  
AG12                                           0.000    16.000  clk (IN)
                  net (fo=0)                   0.000    16.000  pll_i/inst/clkin1_ibuf/I
AG12              INBUF (Prop_INBUF_HRIO_PAD_O)
                                               0.738    16.738  pll_i/inst/clkin1_ibuf/INBUF_INST/O
                  net (fo=1, routed)           0.105    16.843  pll_i/inst/clkin1_ibuf/OUT
AG12              IBUFCTRL (Prop_IBUFCTRL_HRIO_I_O)
                                               0.049    16.892  pll_i/inst/clkin1_ibuf/IBUFCTRL_INST/O
                  net (fo=1, routed)           0.975    17.867  pll_i/inst/clk_in1_clk_wiz_1
MMCME3_ADV_X1Y0   MMCME3_ADV (Prop_MMCME3_ADV_CLKIN1_CLKOUT0)
                                              -4.438    13.429  pll_i/inst/mmcme3_adv_inst/CLKOUT0
                  net (fo=1, routed)           0.501    13.930  pll_i/inst/clk_out1_clk_wiz_1
BUFGCE_X1Y1       BUFGCE (Prop_BUFCE_BUFGCE_I_O)
                                               0.101    14.031  pll_i/inst/clkout1_buf/O
X2Y0 (CLOCK_ROOT) net (fo=1, routed)           1.369    15.400  pll_clk_8
SLICE_X49Y58      FDRE                                          foo_reg_reg/C

This is the Source Clock Path for clk_out1_clk_wiz_1, and there's the answer to the question.

Another method is to obtain the information with a Tcl command. How to do this exactly differs from one FPGA tool to another. In Vivado, a command like the following can be used after opening the Implemented Design:

> get_clocks -of_objects [ get_nets pll_clk_8 ]
clk_out1_clk_wiz_1

This method requires knowing the name of the net. Sometimes it's as simple as in this example, and sometimes it requires to find the name of this net. The FPGA tools usually offer a way to do this with GUI. It's also possible to use Tcl commands for this purpose.

In fact, I hope that the few examples with Tcl has convinced you about the importance to know how to work with Tcl properly. This is what the next page is about.

The significance of using get_port

In the example above, the create_clock command relies on get_port to make the connection between the clock object and a physical input pin. As mentioned above, this connection is necessary for knowing which logic elements are connected to this clock (or clocks that are generated from it).

But using get_port isn't the only possibility. For example, it's also possible to refer to the output pin of a global clock buffer. Something like this:

create_clock -name clk -period 4 [get_pins my_BUFG_inst/O]

The difference is that the tools consider the global clock buffer's output pin as the origin of the clock. In other words, the clock paths' calculation begins from this position. The first clock edge at this origin occurs at 0 ns, so this output pin becomes the time reference.

This is a legal timing constraint, but it has two important shortcomings:

Hence, get_port should always be used when possible. Otherwise, the clock's timing should be considered unknown in relation to anything but itself.


In this page, a lot of Tcl commands were presented, but without explaining them adequately. The next page fills that gap.

Copyright © 2021-2024. All rights reserved. (6f913017)