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:
- The tools must be requested to consider this clock as an unrelated clock with regards to all other clocks. This is true even for clocks that are generated with the same PLL. The reason is that the tools consider the output pin as the time reference. Hence there is no compensation for differences between clocks regarding their clock path delays (i.e. clock skews are not taken into account).
- It's impossible (or very difficult) to define I/O timing constraints that are related to a clock that is visible outside the FPGA. This is because such constraints rely on a clock object as the time reference. But once again, the time reference is the global clock buffer's output pin. The clock skew from the external clock to the global clock buffer is unknown (e.g. it changes with temperature).
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.