03-08-2019 01:22 PM
I'm working on creating input and output constraints for a project using an Artix 7. I'm using the clocking wizard to generate an input delay constraint on a source synchronous signal but in the timing analysis I'm getting a delay that causes it to fail timing.
The input clock has a period of 7.5ns and is source synchronous. It is brought in to an IBUFDS/BUFG without going through a MMCM. My data signal switches on the falling edge of the clock. So I've gone into the clocking wizard and set my input delay constraints by setting it to source synchronous, edge direct, and falling edge. For an initial test I gave the best case scenario and where the data transitions exactly on the falling edge. The clocking wizard produced the following constraints.
set_input_delay -clock [get_clocks VIRTUAL_clk133] -clock_fall -min -add_delay 7.500 [get_ports ZOO_FWD_N] set_input_delay -clock [get_clocks VIRTUAL_clk133] -clock_fall -max -add_delay 7.500 [get_ports ZOO_FWD_N] set_input_delay -clock [get_clocks VIRTUAL_clk133] -clock_fall -min -add_delay 7.500 [get_ports ZOO_FWD_P] set_input_delay -clock [get_clocks VIRTUAL_clk133] -clock_fall -max -add_delay 7.500 [get_ports ZOO_FWD_P]
One of the things I don't understand here is why 7.500ns is used for the delay. I've read somewhere this has to do with which edge you're trying to catch(IE the one that was sent with the data or the following) which is chosen by the 'Edge direct' value. But in the timing analysis in Vivado for this signal this 7.500ns delay doesn't seem to make sense. It seems to me that you're nearly gaurenteed to fail timing with this. Especially considering this is the ideal case. Below is the timing analysis output with these constraints.
Obviously the 7.5ns delay with the 3.75ns(falling edge) delay we cannot meet timing in any case even under perfect conditions. I must be doing something wrong here.
Another thing I don’t understand when looking at the timing summary is why the ZHOLD_DELAY is added. I’ve tried a ton of variations on the constraints but it seems to always be there unless I use MMCM edge(I think). I'm guessing for some reason Vivado thinks I'm going to violate the hold timing and that's why it's adding the ZHOLD_DELAY but it's not really clear to me.
03-08-2019 10:27 PM - edited 03-08-2019 10:38 PM
There are different ways of designing and constraining your single-data-rate (SDR) source-synchronous-input (SSI) interface to an Artix-7 FPGA. Here’s one way.
1) Start from basic concepts (and not from the Vivado Constraints Wizard). The basic SDR SSI interface is the transfer of data, DAT1, from a register, EX1_reg, located outside the FPGA to a register, DAT2_reg, located inside the FPGA as shown in the sketch below.
The implemented design from a Vivado project for this SDR SSI interface would look like the following.
2) Decide how you will shift/delay the interface clock, CLK1, or the data so that the capture register, DAT2_reg, is clocked in the middle of the data-eye. We will use an MMCM for the following reasons:
a. easy to use (when CLK1 is brought into the FPGA via a clock-capable pin)
b. when creating the MMCM with the Vivado Clocking Wizard, a needed
create_clock constraint is automatically written for us.
create_clock -period 7.500 [get_ports CLK1_IN]
c. the MMCM automatically places CLK1 into the FPGA clock-tree
3) Describe characteristics of EX1_reg and of board traces that carry DAT1 and CLK1 to the FPGA using set_input_delay constraints. A general template for doing this is shown below.
#delay-rate (nanoseconds per inch) set dly_ns_per_inch 0.1667 #max CLK1 delay from EX1_reg (trace length * ns/in) set t_pxc_max [expr 1.10 * $dly_ns_per_inch] #min CLK1 delay from EX1_reg (trace length * ns/in) set t_pxc_min [expr 1.00 * $dly_ns_per_inch] #max DAT1 delay from EX1_reg (trace length * ns/in) set t_pxd_max [expr 0.95 * $dly_ns_per_inch] #min DAT1 delay from EX1_reg (trace length * ns/in) set t_pxd_min [expr 0.90 * $dly_ns_per_inch] #max clock-to-output time(ns) for EX1_reg set t_cox_max 0.60 #min clock-to-output time(ns) for EX1_reg set t_cox_min 0.50 #-- set dly_max [expr $t_cox_max + $t_pxd_max - $t_pxc_min] set dly_min [expr $t_cox_min + $t_pxd_min - $t_pxc_max] set_input_delay -clock CLK1_IN -max $dly_max [get_ports DAT1_IN] set_input_delay -clock CLK1_IN -min $dly_max [get_ports DAT1_IN]
4) Consider unusual conditions “My data signal switches on the falling edge of the clock”. This can be described as extra delay added to t_pxd. Specifically, we can add a term, t_pxd_180 = 3.75ns to our constraints template that makes EX1_reg appear to be launching data on the falling-edge of CLK1 (instead of on the rising-edge of CLK1).
5) Add a “set_property PHASESHIFT_MODE” constraint for the MMCM that tells Vivado to interpret phase shifts specified via the Clocking Wizard as delays.
6) The final version of the constraints template for this problem is shown below.
#delay-rate (nanoseconds per inch) set dly_ns_per_inch 0.1667 #max CLK1 delay from EX1_reg (trace length * ns/in) set t_pxc_max [expr 1.10 * $dly_ns_per_inch] #min CLK1 delay from EX1_reg (trace length * ns/in) set t_pxc_min [expr 1.00 * $dly_ns_per_inch] #max DAT1 delay from EX1_reg (trace length * ns/in) set t_pxd_max [expr 0.95 * $dly_ns_per_inch] #min DAT1 delay from EX1_reg (trace length * ns/in) set t_pxd_min [expr 0.90 * $dly_ns_per_inch] #max clock-to-output time(ns) for EX1_reg set t_cox_max 0.60 #min clock-to-output time(ns) for EX1_reg set t_cox_min 0.50 #delay(ns) equal to one-half period of CLK1 set t_pxd_180 3.75 #-- set dly_max [expr $t_cox_max + $t_pxd_max + $t_pxd_180 - $t_pxc_min] set dly_min [expr $t_cox_min + $t_pxd_min + $t_pxd_180 - $t_pxc_max] set_input_delay -clock CLK1_IN -max $dly_max [get_ports DAT1_IN] set_input_delay -clock CLK1_IN -min $dly_max [get_ports DAT1_IN] #-- set_property PHASESHIFT_MODE LATENCY [get_cells MMCM1/inst/mmcm_adv_inst]
7) Run synthesis and implementation. Use Tcl commands like the following to find setup-slack and hold-slack for the timing-path ending at DAT2_reg.
report_timing -to [get_cells DAT2_reg] -setup report_timing -to [get_cells DAT2_reg] -hold
8) Using the Clocking Wizard to manually adjust phase of the MMCM output clock. By trial and error, we seek the output clock phase that causes setup-slack and hold-slack to be both equal and positive for the timing-path ending at DAT2_reg.
If, after doing all this, ZHOLD_DELAY is still a problem - then we can talk.
03-09-2019 07:54 AM
I don't use the constraint wizard, so I can't tell you if it is just wrong or you are using it wrong, but the end result is clear - the constraints are just wrong.
But this interface is really straight forward. You are trying to describe an interface that changes exactly with the falling edge of the clock (and presumably you are capturing it with the rising edge of the clock). Starting with the simplest one (where there is no skew between clock and data - which is unrealistic).
create_clock -name my_clk -period 7.5 [get_ports <clock_port>] set_input_delay 0 -min -clock_fall [get_ports ZOO_FWD_P] set_input_delay 0 -max -clock_fall [get_ports ZOO_FWD_P]
That's it - nothing more complicated than that.
If there is some skew between the clock and data (say +/-100ps), then change the min to -0.1 and the max to 0.1 and your all set.
As for the ZHOLD, I am not sure there is anything you can do about this (this is a bit of a pet peeve of mine). Whenever the tool sees "direct" clocking (the clock comes from a pin and goes directly to a BUFG) it automatically inserts the ZHOLD delay on the assumption that you are trying to do system synchronous clocking with no MMCM. To do this, the ZHOLD is required, otherwise a large positive hold time will result on the interface.
But as you have realized, in a source synchronous interface, this simply isn't valid. In the past I have tried to override this behavior and I believe I failed (but it has been a long time since I last tried).
03-11-2019 09:57 AM
Thank you for the very detailed response. The constraint examples are especially helpful. I had no idea you could create variables like you did in them. That makes organizing this much cleaner. The only difference I have with your example is I’m working with an existing design that doesn’t use an MMCM. But I get the idea of your example and should be able to adapt it for my situation. Or I might just have to use an MMCM.
Yeah, I think using the Timing Wizard is more effort than it’s worth. I recently moved to Vivado from ISE and I was trying to make an effort to learn the new tools but I think in this case it’s just easier to make the constraints manually.
I did notice that the 7.5ns input constraint didn’t make sense but I wasn’t sure if maybe something was happening behind the scenes to make this make sense. But it appears not. I’ll make the manually as you suggest.
That is a little frustrating about the ZHOLD being forced on you.
03-11-2019 05:29 PM
I had no idea you could create variables like you did in them. That makes organizing this much cleaner.
I’m glad you found the template and variables helpful. If you decide to use the template or variables then you must thereafter be very careful using the Vivado Constraints Wizard – because it will erase your variables! -and it will erase your in-line comments!
Yeah, I think using the Timing Wizard is more effort than it’s worth.
Avrum has been patiently and generously helping us with IO interface problems for many years. However, if Avrum retires then many of us (including me) will be in big trouble. -because, we’ll have to figure out these constraints for ourselves – and neither the Vivado Constraints Wizard nor the Xilinx documentation are enough help.
If you too are puzzled by FPGA IO problems/constraints – and are unhappy with the Vivado Constraints Wizard or the Xilinx documentation – then tell Xilinx about it. One way to do this is to open your electronic copy of UG903 to the section about the Timing Constraints Wizard and click on the “Send Feedback” balloon. Thanks!
03-12-2019 01:04 PM
I've been messing with Vivado for a while now and I keep finding that the inserted ZHOLD is causing me problems. The timing problem du jour is I have a register that takes data from a pin and immediately outputs that data on another pin. There is a requirement that I have to place this register in the IOB. This is easy enough to get Vivado to do but again the ZHOLD delay is inserted and causing it to violate the setup time.
So I thought maybe Vivado thought that without this additional delay the hold time would be violated. But I checked and the hold time would still have positive slack if the ZHOLD delay was removed. This still made sense with what avrumw has said about the ZHOLD always being inserted when you clocking directly(IE without an MMCM).
But if I force the register to not be packed in the IOB Vivado stops inserting the ZHOLD. The only thing I can think is happening here is Vivado has some condition that says to add ‘some’ delay to the input signal. Where the delay to the FF in the IOB is too short but the delay to the nearest slice isn’t. Thus it adds the delay when using the FF in the IOB.
I may be wrong as I’m no expert on timing but my understanding is the reason to put the ZHOLD delay in is to help meet the hold timing. Then it seems silly to me to add a delay in to help meet hold timing when the hold timing would pass without that delay. And that same delay causes setup timing to fail.
Is there something special about using the register in the IOB?
03-12-2019 05:25 PM
I feel your frustration. However, Avrum says that ZHOLD is a pet peeve of his and he’s not sure there is anything you can do about it. You and I have very little chance of doing what Avrum can’t.
With your input clock frequency of 133MHz (7.5ns period), the direct-clocking of the data-capture register should not be necessary to make this interface pass timing analysis. So, go ahead and send the forwarded clock to an MMCM (you’ve probably got some unused ones lying around) – and this will get rid of the ZHOLD problem.
If you go this route then be sure to use a XDC constraint for the MMCM that looks something like the following:
set_property PHASESHIFT_MODE LATENCY [get_cells MMCM1/inst/mmcm_adv_inst]
-and remember that you may have to manually adjust phase on the output clock of this MMCM a couple of times to make the interface pass timing analysis.
03-13-2019 01:19 PM
Unfortunately it’s a little difficult for me to switch to a MMCM. I’m working on an existing design (ported from a Spartan 3) and the original designer had a reason for not using a PLL previously. Now I suspect I’ll have to move to a MMCM anyways but I’m trying to see if it could be done without one.
In case it helps anyone in the future, I found what I think is the relevant constraint. It is IOBDELAY. It seems to be exactly what I want but no matter what I do Vivado still inserts the ZHOLD delay if the register is packed.
But again I’m pretty sure I have to move to a MMCM. Thanks again!
04-17-2019 03:25 PM
@dylan.campbell "no matter what I do Vivado still inserts the ZHOLD delay if the register is packed"
Can you post an example of how you are applying IOBDELAY, with HDL snippets showing the specific port/component/net to which you are applying the IOBDELAY, and your .xdc or HDL IOBDELAY attribute/property?
I haven't tried IOBDELAY recently, but UG912 v2018.3 page 246 still says this automatic ZHOLD insertion on directly clocked IOB registers can be disabled with IOBDELAY "NONE" applied to either ports/nets/IBUFs.
AR# 64498 describes a case sensitivity problem for "none" (vs."NONE") when using IOBDELAY attributes in XDC.