cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Highlighted
348 Views
Registered: ‎12-12-2019

Vivado inserting backslashes in clock names, then not escaping them properly when passed to get_clocks

a little background here: https://forums.xilinx.com/t5/Synthesis/Scope-of-TCL-variables/m-p/1113899/highlight/false#M35422

and here: https://forums.xilinx.com/t5/Implementation/Place-30-674-Error-upon-adding-a-clock/m-p/1115629#M28240

In case they are relevant, but here's my current issue...  I have clocks in the design that vivado is naming with backslashes in the name, but when those names are passed to get_clocks in my .xdc, vivado thinks the backslashes are escape characters and is removing them.  Then it can't find the clock in questions.

So, for example, If I manually run the lines the xdc in the tcl console, I get up to this point....

set TGP_AllClks [join [list $TNM_gen_1p8a_sys_clk_bus_clk_44 $TNM_gen_1p8a_sys_clk_bus_clk_80 $TNM_gen_1p8a_sys_clk_bus_clk_88 $TNM_gen_1p8a_sys_clk_bus_clk_100 $TNM_gen_1p8a_sys_clk_bus_clk_160 $TNM_iTechnologyComponent_gen_1p8a_sys_clk_bus_clk_176 $TNM_clk_200mhz_mmcm_out $TNM_clk_200mhz_mmcm_out $TNM_clk_240mhz_mmcm_out $TNM_mmcm_clk_out1 $TNM_clk_288mhz_mmcm_out $TNM_clk_320mhz_mmcm_out $TNM_clk_352mhz_mmcm_out]]

vivado responds:

gen_1p8a.sys_clk_bus\.clk_44 gen_1p8a.sys_clk_bus\.clk_80 gen_1p8a.sys_clk_bus\.clk_88 gen_1p8a.sys_clk_bus\.clk_100 gen_1p8a.sys_clk_bus\.clk_160 iTechnologyComponent/gen_1p8a.sys_clk_bus\.clk_176 clk_200mhz_mmcm_out clk_200mhz_mmcm_out clk_240mhz_mmcm_out mmcm_clk_out1 clk_288mhz_mmcm_out clk_320mhz_mmcm_out clk_352mhz_mmcm_out

(note the backslashes as in “sys_clk_bus\.clk_44”. In System Verilog, it's just "sys_clk_bus.clk_44", with no backslash.  At some point in the chain to this point, vivado has added the backslash)

When I try to run this line:

set TGP_Notgen_1p8a_sys_clk_bus_clk_160                      [join [get_clocks $TGP_AllClks -filter "NAME != $TNM_gen_1p8a_sys_clk_bus_clk_80           && NAME != $TNM_gen_1p8a_sys_clk_bus_clk_160          && NAME != $TNM_clk_320mhz_mmcm_out                  "]]

 

vivado replies:

WARNING: [Vivado 12-627] No clocks matched 'gen_1p8a.sys_clk_bus.clk_44'.

WARNING: [Vivado 12-627] No clocks matched 'gen_1p8a.sys_clk_bus.clk_80'.

WARNING: [Vivado 12-627] No clocks matched 'gen_1p8a.sys_clk_bus.clk_88'.

WARNING: [Vivado 12-627] No clocks matched 'gen_1p8a.sys_clk_bus.clk_100'.

WARNING: [Vivado 12-627] No clocks matched 'gen_1p8a.sys_clk_bus.clk_160'.

WARNING: [Vivado 12-627] No clocks matched 'iTechnologyComponent/gen_1p8a.sys_clk_bus.clk_176'.

clk_200mhz_mmcm_out clk_200mhz_mmcm_out clk_240mhz_mmcm_out mmcm_clk_out1 clk_288mhz_mmcm_out clk_352mhz_mmcm_out

 

Note the lack of backslash, as in “sys_clk_bus.clk_44”.  (These warnings are also what I see in the log when I just include the xdc.  That's what started me looking down this path of replicating them on the tcl console as described above.)

 

So the backslashes are there in $TGP_AllClks, but get_clocks thinks they are escape characters, not part of the signal name, so it can’t find them.

 

When I add this line, to escape the backslashes an extra time:

set TGP_AllClks [regsub -all {\\\.} $TGP_AllClks {\\\.}]

 Then running the above line again, it successfully finds those clocks:

set TGP_Notgen_1p8a_sys_clk_bus_clk_160                      [join [get_clocks $TGP_AllClks -filter "NAME != $TNM_gen_1p8a_sys_clk_bus_clk_80           && NAME != $TNM_gen_1p8a_sys_clk_bus_clk_160          && NAME != $TNM_clk_320mhz_mmcm_out                  "]]

gen_1p8a.sys_clk_bus\.clk_44 gen_1p8a.sys_clk_bus\.clk_88 gen_1p8a.sys_clk_bus\.clk_100 iTechnologyComponent/gen_1p8a.sys_clk_bus\.clk_176 clk_200mhz_mmcm_out clk_200mhz_mmcm_out clk_240mhz_mmcm_out mmcm_clk_out1 clk_288mhz_mmcm_out clk_352mhz_mmcm_out

however, this only works on the tcl console.  When I tried to include that hack in the .xdc, it complained that regsub is not supported in .xdc files.

Is there some quote or bracket I can put around my variable names that will allow the variable to expand, but not remove the backslashes?  I've tried {} and "" and neither worked.

0 Kudos
3 Replies
Highlighted
Guide
Guide
331 Views
Registered: ‎01-23-2009

I am having trouble figuring out what is going on with your syntax...

You seem to be using variables for representing your clock names (or your clocks themselves, I can't tell which), and then you are trying to create a list of them with the list command, but I am not really sure why you are using the join command... (I suspect this may have something to do with your problem).

So start by telling us what the actual clocks are named - post the output of report_clocks, or even just get_clocks, so that we can see what the actual clock names are. Then show us the values of the $TNM_* variables as well as how they were set.

In general, I would recommend against trying to make Vivado look more like UCF - the concept of TNMs in UCF doesn't really have any equivalent in Vivado; the engines work completely differently. This too could be part of your problem...

So the best thing is to back up and tell us what you are trying to accomplish; there is almost certainly a nice clean way to do what you want using the syntax of Vivado (XDC) rather than trying to make your constraints look more like UCF.

Avrum

0 Kudos
Highlighted
320 Views
Registered: ‎12-12-2019

Hi Avrum,

I'm trying to port a design from one group into a design from another group, and merging their flows has been quite a task.  The original group used a non-project flow, in which they used a tcl script after synthesis to create max_delay and false_path constraints for clock crossing.  Since the target project uses a project flow, I had to run the tcl script manually and include the resulting xdc from the start of the project flow.   So this xdc is not hand-written, and not by me.  There's a lot of history in this project, and for all I know, these scripts could have come from the days of ucfs. But the goal is to properly constrain all the clock domain crossings in the design.

From what I can tell, the xdc file is doing the following steps:

1) setting a variable name for each output pin of the bufgs, like this one for example:

set gen_1p8a_sys_clk_bus_clk_44 iTechnologyTop/iTechnologyComponent/kt_cwag_framework/kt_cwag_clocking/bufgce_sys_clk44/O

2) using that variable name to get the clocks associated with that pin and the period thereof, assigning those to TNM_ and PER_ variables like this for example:

set TNM_gen_1p8a_sys_clk_bus_clk_44 [get_clocks -of_objects [get_pins $gen_1p8a_sys_clk_bus_clk_44]]

set PER_gen_1p8a_sys_clk_bus_clk_44 [get_property PERIOD [get_clocks $TNM_gen_1p8a_sys_clk_bus_clk_44]]

3) putting all the clocks into a list like this:

set TGP_AllClks [join [list $TNM_gen_1p8a_sys_clk_bus_clk_44 $TNM_gen_1p8a_sys_clk_bus_clk_80 $TNM_gen_1p8a_sys_clk_bus_clk_88 $TNM_gen_1p8a_sys_clk_bus_clk_100 $TNM_gen_1p8a_sys_clk_bus_clk_160 $TNM_iTechnologyComponent_gen_1p8a_sys_clk_bus_clk_176 $TNM_clk_200mhz_mmcm_out $TNM_clk_200mhz_mmcm_out $TNM_clk_240mhz_mmcm_out $TNM_mmcm_clk_out1 $TNM_clk_288mhz_mmcm_out $TNM_clk_320mhz_mmcm_out $TNM_clk_352mhz_mmcm_out]]

4) for each clock, generating a list of clock that are NOT synchronously related to that clock, and assigning to a TGP_Not* variable like this:

set TGP_Notgen_1p8a_sys_clk_bus_clk_44 [join [get_clocks $TGP_AllClks -filter "NAME != $TNM_clk_352mhz_mmcm_out && NAME != $TNM_iTechnologyComponent_gen_1p8a_sys_clk_bus_clk_176 && NAME != $TNM_gen_1p8a_sys_clk_bus_clk_88 && NAME != $TNM_gen_1p8a_sys_clk_bus_clk_44 "]]

5) finally, for each clock, generating a max_delay and a false_path -hold from that clock to its TGP_Not* group and vice versa.

set_max_delay -from [get_clocks $TNM_gen_1p8a_sys_clk_bus_clk_44] -to [get_clocks $TGP_Notgen_1p8a_sys_clk_bus_clk_44] [get_property PERIOD [get_clocks $TNM_gen_1p8a_sys_clk_bus_clk_44]] -datapath_only
set_max_delay -from [get_clocks $TGP_Notgen_1p8a_sys_clk_bus_clk_44] -to [get_clocks $TNM_gen_1p8a_sys_clk_bus_clk_44] [get_property PERIOD [get_clocks $TNM_gen_1p8a_sys_clk_bus_clk_44]] -datapath_only
set_false_path -from [get_clocks $TNM_gen_1p8a_sys_clk_bus_clk_44] -to [get_clocks $TGP_Notgen_1p8a_sys_clk_bus_clk_44] -hold
set_false_path -from [get_clocks $TGP_Notgen_1p8a_sys_clk_bus_clk_44] -to [get_clocks $TNM_gen_1p8a_sys_clk_bus_clk_44] -hold

 

Supposedly this all works on the original group's non-project flow.   But I need to bring it into the new group's project flow.

 

Full output of report_clocks in attachment report_clocks.txt

Is there an easy way to dump the contents of the TNM* variables for you?

0 Kudos
Highlighted
Guide
Guide
276 Views
Registered: ‎01-23-2009

Wow...

OK. That's a lot of clocks... (over 200 clocks)

From the report_clocks command, there do, indeed, seem to be clocks that have both un-escaped . characters as well as escaped \. characters. I have to admit that I have never seen the latter before... These seem to be automatically generated clocks, but I am not sure what kind of RTL structure is generating these escaped \. characters. If these can be identified and easily removed/changed, that would probably be a good idea...

So, there are a couple of things about the methodology...

You say that in the original flow, this script was run "after synthesis"... Are you sure? If so, then this means that synthesis was done without constraints on these clock domain crossing (CDC) paths. That isn't ideal; the synthesis tool is timing driven so it, too, should have proper constraints.

Next, there are better ways of constraining CDC paths. I am not a fan of blanket constraints of any kind - and, at the end of this process, this is what you are trying to end up with. My methodology is that timing exceptions are needed on clock domain crossing circuits not on the clocks that are the source and destination clocks of these circuits. What you are doing is assuming that every CDC path between two clocks are properly isolated by an appropriate clock domain crossing circuit (CDCC), and are then saying "since they are all covered by CDCCs, I will use blanket 'clock-to-clock' constraints to put exceptions on the CDC paths themselves". This is always a bit dangerous; if you accidentally have a CDC path that does not go through a CDCC, it will be covered by the exception anyway. Vivado gives you lots of mechanisms for applying constraints, and with those (particularly with the proper use of scoped XDC files) it is often possible to relatively cleanly constrain the CDCCs themselves, rather than applying blanket constraints to the clocks. (But I realize that this would be a significant change in methodology, and may not be realistic at this time)

Next, you don't need the set_false_path -hold commands; a set_max_delay -datapath_only automatically disables the hold check on the path (this is not true of the set_max_delay without the -datapath_only). This is documented (somewhere).

But, more directly, I suspect that the "join" command is the root of your problem.

Vivado XDC is built to use Tcl lists. As opposed to "regular Tcl", Tcl lists in Vivado can (at least conceptually) hold objects and lists of objects - they aren't restricted to strings as they are in "regular Tcl". Xilinx has done some deep work under the hood of the Tcl parser to enable this, and it works really really well. But, as well as it is implemented, it is still somewhat of a hack under the hood, and its probably not a good idea to do "really weird things" to it. And that is what you are doing with the join command.

For 1) using a variable name for a pin name (which is nothing more than a string) is fine.

For 2) using a variable to hold the clock object is also fine, as is using a variable to get the PERIOD attribute of that clock. These are both expected and normal ways of using Variables in Vivado.

For 3), this is where you are getting in to problems. There is no problem in using a variable to hold a list of objects - the objects can be put in to the list using the list command (which is probably the best way). Doing it with just the list command makes it clear that you are working with the clock objects and not the clock names.

The clock object and the clock name are not the same thing. The object is something that is returned by the get_* commands. If you attempt to puts it, or even look at the return from a command, it will look like the object's name, since Tcl is built around the concept that everything is a string and can hence be displayed (which it does for every command). However, how do you display an object? To deal with this, whenever Vivado needs to display an object in the Tcl environment (either via a puts command or any other command that expects a true string, or through the regular mechanism that puts the return of a command on "standard out"), Vivado uses the objects name instead of the object (since you can't display an object) - it literally uses the NAME property of the object.

As a result, it is relatively easy to get confused between the object and the object's name. So while the list command is clearly working with the object, the join command only works on strings, so the join command is taking a list of objects, and converting it to a single string that is a space separated string containing the names of each of the objects in the list. This is a weird thing to do.

Later on in 4) and 5), you then treat this variable (that containings a single string that is a space separated list of names of objects) as a list of object names. This is OK since a string containing a space separated bunch of words is considered as a list of words, and using a list of strings to create lists of objects from the list of names (using the get_clocks command with filters) is also OK. Again, this step is probably OK, except for the fact that the original list of names was weirdly constructed and that you are, once again, converting the list of objects back to a string using the join command in step 4).

So, the only weird thing you are doing is using the join command to convert objects back to strings. I would stop doing that.

I suspect that this is done because the command in step 4)

get_clocks $TGP_AllClks -filter <filter>

command normally takes a list of clock names rather than a list of clock objects, although it can also take a list of clock objects. But this can all be avoided by using the filter command (rather than the -filter option of the get_clocks command). So for step 4) (assuming you removed the join command from step 3) use the following command instead:

filter $TGP_AllClks <filter>

This command is "cleaner" since it takes in a list of objects, and returns a list of objects. With this, the objects remain objects from step 2) all the way to the end instead of being converted back and forth between objects and strings - this may fix the problems with the escaped \. characters (which clearly only exist in the objects name).

Avrum