1#
2# Copyright 2017 Ettus Research, A National Instruments Company
3# SPDX-License-Identifier: LGPL-3.0
4#
5# Timing analysis is performed in "usrp3/top/n3xx/dboards/rh/doc/rh_timing.xlsx".
6# See this spreadsheet for more details and explanations.
7
8#*******************************************************************************
9## Asynchronous clock groups
10
11# MGT reference clocks are also async to everything.
12set_clock_groups -asynchronous -group [get_clocks mgt_clk_dba -include_generated_clocks]
13set_clock_groups -asynchronous -group [get_clocks mgt_clk_dbb -include_generated_clocks]
14
15# fpga_clk_a and fpga_clk_b are related to one another after synchronization.
16# However, we do need to declare that these clocks (both a and b) and their children
17# are async to the remainder of the design. Use the wildcard at the end to grab the
18# virtual clock as well as the real ones.
19set_clock_groups -asynchronous -group [get_clocks {fpga_clk_a* fpga_clk_b*} -include_generated_clocks]
20
21# The SPI readback and write clocks cannot be active at the same time, as they
22# originate from the same pin.
23set_clock_groups -physically_exclusive \
24    -group [get_clocks pl_spi_rb_clk_a] \
25    -group [get_clocks pl_spi_clk_a]
26set_clock_groups -physically_exclusive \
27    -group [get_clocks pl_spi_rb_clk_b] \
28    -group [get_clocks pl_spi_clk_b]
29
30#*******************************************************************************
31## PS SPI: since these lines all come from the PS and I don't have access to the
32# driving clock (or anything for that matter), I'm left with constraining the maximum
33# and minimum delay on these lines, per a Xilinx AR:
34# https://www.xilinx.com/support/answers/62122.html
35set CPLD_SPI_OUTS [get_ports {DB*_CPLD_PS_SPI_SCLK \
36                              DB*_CPLD_PS_SPI_MOSI \
37                              DB*_CPLD_PS_SPI_CS_B \
38                              DB*_CLKDIS_SPI_CS_B \
39                              DB*_PHDAC_SPI_CS_B \
40                              DB*_ADC_SPI_CS_B \
41                              DB*_DAC_SPI_CS_B}]
42
43# The actual min and max path delays before applying constraints were (from report_timing):
44#    3.332 ns (Min at Fast Process Corner)
45#   10.596 ns (Max at Slow Process Corner)
46# Therefore, we round those number to their immediate succesor respectively.
47# After implementation, the tools were unable to meet timing when leaving a 11 ns max
48# delay value, so it was incremented.
49set MIN_OUT_DELAY  3.0
50set MAX_OUT_DELAY 12.0
51
52set_max_delay $MAX_OUT_DELAY -to $CPLD_SPI_OUTS
53set_min_delay $MIN_OUT_DELAY -to $CPLD_SPI_OUTS
54
55# report_timing -to $CPLD_SPI_OUTS -max_paths 20 -delay_type min_max -name CpldSpiOutTiming
56
57# The actual min and max path delays before applying constraints were (from report_timing):
58#   2.733 ns (Min at Fast Process Corner)
59#   6.071 ns (Max at Slow Process Corner)
60# Therefore, we round those number to their immediate succesor respectively.
61set MIN_IN_DELAY   2.0
62set MAX_IN_DELAY   10.0
63
64set PS_SPI_INPUTS_0 [get_pins -hierarchical -filter {NAME =~ "*/PS7_i/EMIOSPI0MI"}]
65set PS_SPI_INPUTS_1 [get_pins -hierarchical -filter {NAME =~ "*/PS7_i/EMIOSPI1MI"}]
66
67set_max_delay $MAX_IN_DELAY -to $PS_SPI_INPUTS_0
68set_min_delay $MIN_IN_DELAY -to $PS_SPI_INPUTS_0
69set_max_delay $MAX_IN_DELAY -to $PS_SPI_INPUTS_1
70set_min_delay $MIN_IN_DELAY -to $PS_SPI_INPUTS_1
71
72# report_timing -to $PS_SPI_INPUTS_0 -max_paths 30 -delay_type min_max -nworst 30 -name Spi0InTiming
73# report_timing -to $PS_SPI_INPUTS_1 -max_paths 30 -delay_type min_max -nworst 30 -name Spi1InTiming
74
75
76
77#*******************************************************************************
78## PL SPI to the CPLD
79#
80# All of these lines are driven or received from flops in simple_spi_core. The CPLD
81# calculations assume the FPGA has less than 6 ns of skew between the SCK and
82# SDI/CS_n. Pretty easy constraint to write! See above for the clock definition.
83# Do this for DBA and DBB independently.
84set MAX_SKEW 6.0
85set SETUP_SKEW [expr {$MAX_SKEW / 2}]
86set HOLD_SKEW  [expr {$MAX_SKEW / 2}]
87# Do not set the output delay constraint on the clock line!
88set PORT_LIST_A [get_ports {DBA_CPLD_PL_SPI_CS_B \
89                            DBA_CPLD_PL_SPI_MOSI \
90                            DBA_TXLO_SPI_CS_B    \
91                            DBA_RXLO_SPI_CS_B    \
92                            DBA_LODIS_SPI_CS_B  }]
93set PORT_LIST_B [get_ports {DBB_CPLD_PL_SPI_CS_B \
94                            DBB_CPLD_PL_SPI_MOSI \
95                            DBB_TXLO_SPI_CS_B    \
96                            DBB_RXLO_SPI_CS_B    \
97                            DBB_LODIS_SPI_CS_B  }]
98# Then add the output delay on each of the ports.
99set_output_delay                        -clock [get_clocks pl_spi_clk_a] -max -$SETUP_SKEW $PORT_LIST_A
100set_output_delay -add_delay -clock_fall -clock [get_clocks pl_spi_clk_a] -max -$SETUP_SKEW $PORT_LIST_A
101set_output_delay                        -clock [get_clocks pl_spi_clk_a] -min  $HOLD_SKEW  $PORT_LIST_A
102set_output_delay -add_delay -clock_fall -clock [get_clocks pl_spi_clk_a] -min  $HOLD_SKEW  $PORT_LIST_A
103set_output_delay                        -clock [get_clocks pl_spi_clk_b] -max -$SETUP_SKEW $PORT_LIST_B
104set_output_delay -add_delay -clock_fall -clock [get_clocks pl_spi_clk_b] -max -$SETUP_SKEW $PORT_LIST_B
105set_output_delay                        -clock [get_clocks pl_spi_clk_b] -min  $HOLD_SKEW  $PORT_LIST_B
106set_output_delay -add_delay -clock_fall -clock [get_clocks pl_spi_clk_b] -min  $HOLD_SKEW  $PORT_LIST_B
107# Finally, make both the setup and hold checks use the same launching and latching edges.
108set_multicycle_path -setup -from [get_clocks radio_clk] -to [get_clocks pl_spi_clk_a] -start 0
109set_multicycle_path -hold  -from [get_clocks radio_clk] -to [get_clocks pl_spi_clk_a] -1
110set_multicycle_path -setup -from [get_clocks radio_clk] -to [get_clocks pl_spi_clk_b] -start 0
111set_multicycle_path -hold  -from [get_clocks radio_clk] -to [get_clocks pl_spi_clk_b] -1
112
113# For SDO input timing (MISO), we need to look at the CPLD's constraints on turnaround
114# time plus any board propagation delay.
115# CPLD clk-to-q is 20 ns, then add 1.2 ns for board delay (once for clock, once for data)
116# For hold time, assume zero delay (likely overconstraining here, due to board delays)
117set MISO_INPUT_A [get_ports DBA_CPLD_PL_SPI_MISO]
118set MISO_INPUT_B [get_ports DBB_CPLD_PL_SPI_MISO]
119set_input_delay -clock [get_clocks pl_spi_rb_clk_a] -clock_fall -max  22.400  $MISO_INPUT_A
120set_input_delay -clock [get_clocks pl_spi_rb_clk_a] -clock_fall -min   0.000  $MISO_INPUT_A
121set_input_delay -clock [get_clocks pl_spi_rb_clk_b] -clock_fall -max  22.400  $MISO_INPUT_B
122set_input_delay -clock [get_clocks pl_spi_rb_clk_b] -clock_fall -min   0.000  $MISO_INPUT_B
123
124# Since the input delay span is clearly more than a period of the radio_clk, we need to
125# add a multicycle path here as well to define the clock divider ratio. The MISO data
126# is driven on the falling edge of the SPI clock and captured on the rising edge, so we
127# only have one half of a SPI clock cycle for our setup. Hold is left alone and is OK
128# as-is due to the delays in the CPLD and board.
129set SETUP_CYCLES [expr {$PL_SPI_RB_DIVIDE_VAL / 2}]
130set HOLD_CYCLES 0
131set_multicycle_path -setup -from [get_clocks pl_spi_rb_clk_a] -through $MISO_INPUT_A \
132  $SETUP_CYCLES
133set_multicycle_path -hold  -from [get_clocks pl_spi_rb_clk_a] -through $MISO_INPUT_A -end \
134  [expr {$SETUP_CYCLES + $HOLD_CYCLES - 1}]
135set_multicycle_path -setup -from [get_clocks pl_spi_rb_clk_b] -through $MISO_INPUT_B \
136  $SETUP_CYCLES
137set_multicycle_path -hold  -from [get_clocks pl_spi_rb_clk_b] -through $MISO_INPUT_B -end \
138  [expr {$SETUP_CYCLES + $HOLD_CYCLES - 1}]
139
140#*******************************************************************************
141## SYSREF/SYNC JESD Timing
142#
143# SYNC is async, SYSREF is tightly timed.
144
145# The SYNC output (to ADC) for both DBs is governed by the JESD cores, which are solely
146# driven by DB-A clock... but it is an asynchronous signal so we use the async_out_clk.
147set_output_delay -clock [get_clocks async_out_clk] 0.000 [get_ports DB*_ADC_SYNCB_P]
148set_max_delay -to [get_ports DB*_ADC_SYNCB_P] 50.000
149set_min_delay -to [get_ports DB*_ADC_SYNCB_P] 0.000
150
151# The SYNC input (from DAC) for both DBs is received by the DB-A clock inside the JESD
152# cores... but again, it is asynchronous and therefore uses the async_in_clk.
153set_input_delay -clock [get_clocks async_in_clk] 0.000 [get_ports DB*_DAC_SYNCB_P]
154set_max_delay -from [get_ports DB*_DAC_SYNCB_P] 50.000
155set_min_delay -from [get_ports DB*_DAC_SYNCB_P] 0.000
156
157# SYSREF is driven by the LMK directly to the FPGA. Timing analysis was performed once
158# for the worst-case numbers across both DBs to produce one set of numbers for both DBs.
159# Since we easily meet setup and hold in Vivado, then this is an acceptable approach.
160# SYSREF is captured by the local clock from each DB, so we have two sets of constraints.
161set_input_delay -clock fpga_clk_a_v -min -0.479 [get_ports DBA_FPGA_SYSREF_*]
162set_input_delay -clock fpga_clk_a_v -max  0.661 [get_ports DBA_FPGA_SYSREF_*]
163
164set_input_delay -clock fpga_clk_b_v -min -0.479 [get_ports DBB_FPGA_SYSREF_*]
165set_input_delay -clock fpga_clk_b_v -max  0.661 [get_ports DBB_FPGA_SYSREF_*]
166
167
168#*******************************************************************************
169## PPS Timing
170
171# Due to the N3xx synchronization and clocking structure, the PPS output is driven from
172# the Sample Clock domain instead of the input Reference Clock. Constrain the output as
173# tightly as possible to accurately mimic the internal Sample Clock timing.
174set SETUP_SKEW  2.0
175set HOLD_SKEW  -0.5
176set_output_delay -clock [get_clocks fpga_clk_a_v] -max -$SETUP_SKEW [get_ports REF_1PPS_OUT]
177set_output_delay -clock [get_clocks fpga_clk_a_v] -min  $HOLD_SKEW  [get_ports REF_1PPS_OUT]
178set_multicycle_path -setup -to [get_ports REF_1PPS_OUT] -start 0
179set_multicycle_path -hold  -to [get_ports REF_1PPS_OUT] -1
180
181#*******************************************************************************
182### Async I/Os
183set DB_ASYNC_OUTPUTS [get_ports {
184    DB*_MODULE_PWR_ENABLE
185    DB*_RF_PWR_ENABLE
186    DB*_CLKDIST_SYNC
187    DB*_ATR_TX
188    DB*_ATR_RX
189    DB*_TXRX_SW_CTRL_1
190    DB*_TXRX_SW_CTRL_2
191    DB*_LED_RX
192    DB*_LED_RX2
193    DB*_LED_TX
194    QSFP_I2C_*
195}]
196set_output_delay -clock [get_clocks async_out_clk] 0.000 $DB_ASYNC_OUTPUTS
197set_max_delay -to $DB_ASYNC_OUTPUTS 50.000
198set_min_delay -to $DB_ASYNC_OUTPUTS 0.000
199
200set_input_delay -clock [get_clocks async_in_clk] 0.000 [get_ports QSFP_I2C_*]
201set_max_delay -from [get_ports QSFP_I2C_*] 50.000
202set_min_delay -from [get_ports QSFP_I2C_*] 0.000
203
204#*******************************************************************************
205## JTAG
206
207## MAX 10 JTAG TDI setup: 2 ns
208## MAX 10 JTAG TMS setup: 3 ns
209## MAX 10 JTAG hold: 10 ns
210## MAX 10 JTAG clk-to-q: 18 ns
211## Board delay: < 1.5 ns
212##
213## Setup time = Board delay + TMS setup = 3 ns + 1.5 ns = 4.5 ns
214## Hold time = Board delay + TMS hold = 1.5 ns + 10 ns = 11.5 ns
215## Overconstrain output delay and keep skew to +/- 8 ns
216##
217## Input delay = 2x Board delay + clk-to-q = 3 ns + 18 ns = 21 ns
218
219# Constrain outputs for skew, with same latch/launch edge:
220set_output_delay                        -clock [get_clocks dba_jtag_tck] -max -4.0 \
221    [get_ports {DBA_CPLD_JTAG_TDI DBA_CPLD_JTAG_TMS}]
222set_output_delay -add_delay -clock_fall -clock [get_clocks dba_jtag_tck] -max -4.0 \
223    [get_ports {DBA_CPLD_JTAG_TDI DBA_CPLD_JTAG_TMS}]
224set_output_delay                        -clock [get_clocks dba_jtag_tck] -min  4.0 \
225    [get_ports {DBA_CPLD_JTAG_TDI DBA_CPLD_JTAG_TMS}]
226set_output_delay -add_delay -clock_fall -clock [get_clocks dba_jtag_tck] -min  4.0 \
227    [get_ports {DBA_CPLD_JTAG_TDI DBA_CPLD_JTAG_TMS}]
228set_output_delay                        -clock [get_clocks dbb_jtag_tck] -max -4.0 \
229    [get_ports {DBB_CPLD_JTAG_TDI DBB_CPLD_JTAG_TMS}]
230set_output_delay -add_delay -clock_fall -clock [get_clocks dbb_jtag_tck] -max -4.0 \
231    [get_ports {DBB_CPLD_JTAG_TDI DBB_CPLD_JTAG_TMS}]
232set_output_delay                        -clock [get_clocks dbb_jtag_tck] -min  4.0 \
233    [get_ports {DBB_CPLD_JTAG_TDI DBB_CPLD_JTAG_TMS}]
234set_output_delay -add_delay -clock_fall -clock [get_clocks dbb_jtag_tck] -min  4.0 \
235    [get_ports {DBB_CPLD_JTAG_TDI DBB_CPLD_JTAG_TMS}]
236# Finally, make both the setup and hold checks use the same launching and latching edges.
237set_multicycle_path -setup -from [get_clocks clk40] -to [get_clocks dba_jtag_tck] -start 0
238set_multicycle_path -hold  -from [get_clocks clk40] -to [get_clocks dba_jtag_tck] -1
239set_multicycle_path -setup -from [get_clocks clk40] -to [get_clocks dbb_jtag_tck] -start 0
240set_multicycle_path -hold  -from [get_clocks clk40] -to [get_clocks dbb_jtag_tck] -1
241
242set_input_delay -clock [get_clocks dba_jtag_tck] -clock_fall -max 21 \
243    [get_ports DBA_CPLD_JTAG_TDO]
244set_input_delay -clock [get_clocks dba_jtag_tck] -clock_fall -min 0 \
245    [get_ports DBA_CPLD_JTAG_TDO]
246set_input_delay -clock [get_clocks dbb_jtag_tck] -clock_fall -max 21 \
247    [get_ports DBB_CPLD_JTAG_TDO]
248set_input_delay -clock [get_clocks dbb_jtag_tck] -clock_fall -min 0 \
249    [get_ports DBB_CPLD_JTAG_TDO]
250# Inputs have setup checks relative to half a period of TCK (launch on fall,
251# latch on rise). Actual latch clock is faster, so push back setup and hold
252# checks to match.
253set_multicycle_path -setup -from [get_clocks dba_jtag_tck] \
254    -through [get_ports DBA_CPLD_JTAG_TDO] \
255    [expr {$DB_JTAG_DIVISOR / 2}]
256set_multicycle_path -end -hold -from [get_clocks dba_jtag_tck] \
257    -through [get_ports DBA_CPLD_JTAG_TDO] \
258    [expr {$DB_JTAG_DIVISOR - 1}]
259set_multicycle_path -setup -from [get_clocks dbb_jtag_tck] \
260    -through [get_ports DBB_CPLD_JTAG_TDO] \
261    [expr {$DB_JTAG_DIVISOR / 2}]
262set_multicycle_path -end -hold -from [get_clocks dbb_jtag_tck] \
263    -through [get_ports DBB_CPLD_JTAG_TDO] \
264    [expr {$DB_JTAG_DIVISOR - 1}]
265