Double-Data-Rate (DDR) Interface Constraints
Double-Data-Rate (DDR) Interface Constraints
Most simple links move one data word per clock period. They launch on the rising edge and capture on the next rising edge. The falling edge does nothing useful. Half the clock's edges are wasted. A double-data-rate link uses both edges. It moves one word on the rising edge and another word on the falling edge. So it sends two words per clock period. The word "double" means twice the data for the same clock frequency. This doubling makes the constraints harder. Now you must tell the timing tool about data tied to the rising edge and data tied to the falling edge. That means two sets of delay numbers and the - clock_fall switch. This chapter shows how.
Why Both Edges Carry Data
Picture a clock running at 333.7 MHz. Its period is about 3.00 ns. A single-data-rate link moves one word every 3.00 ns. A DDR link moves a word every half period, about every 1.50 ns. Each half period is a separate timing window. The rising-edge word has its own launch and capture. The falling-edge word has its own launch and capture. The two windows do not overlap. Each must be checked on its own.

Because each half period is short, DDR timing is tight. The whole budget for one word is about half a clock period, not a full one. At 3.00 ns period, each word has roughly 1.50 ns to travel and settle. The timing tool does not assume DDR on its own. By default it relates data to the rising edge of a clock. To make it check the falling edge too, you add a second constraint with -clock_fall . That switch tells the tool to use the falling edge as the reference.
Rising-Edge and Falling-Edge Delays
The core trick of DDR constraints is to write the delay command twice. Once for the rising edge. Once for the falling edge with -clock_fall . Why twice? Because the data tied to the rising edge can have a different arrival profile than the data tied to the falling edge. The sender may not be perfectly balanced. The rising-edge word might arrive 0.07 ns later than the falling-edge word. Two constraints capture this. You also keep -add_delay . Without it, the second constraint overwrites the first. With it, both constraints live side by side. The tool then checks all four cases: rise-max, rise-min, fall-max, fall-min.
# standard SDC — portable across compliant tools
create_clock -name ddr_clk -period 3.00 [get_ports clk_in]
# data tied to the RISING edge
set_input_delay -clock ddr_clk -max 1.14 -add_delay [get_ports {dq[*]}]
set_input_delay -clock ddr_clk -min 0.39 -add_delay [get_ports {dq[*]}]
# data tied to the FALLING edge
set_input_delay -clock ddr_clk -max 1.21 -clock_fall -add_delay [get_ports {dq[*]}]
set_input_delay -clock ddr_clk -min 0.42 -clock_fall -add_delay [get_ports {dq[*]}]
Four lines for one bus. The tool now relates the data to both edges and checks the late and early case for each. This is the heart of DDR constraint writing.
| Constraint line | Edge | Case | Drives |
|---|---|---|---|
| -max (no fall) | rising | late | rise setup |
| -min (no fall) | rising | early | rise hold |
| -max -clock_fall | falling | late | fall setup |
| -min -clock_fall | falling | early | fall hold |
The output side mirrors this. You write set_output_delay four times in the same pattern.

A new learner often asks why one delay value will not do. The reason is that DDR has two launches per period, and each launch may differ. Think of the data eye. The data eye is the open window in time where the data is stable and safe to sample. On each half period there is one eye. The rising-edge eye and the falling-edge eye can be slightly different sizes and slightly shifted. If you wrote only one constraint, the tool would check only one of the two eyes. The other eye would go unchecked. A real failure could hide there. So you constrain both, with -clock_fall for the second. There is a second reason. The clock duty cycle is rarely a perfect 50 percent. Duty cycle is the fraction of the period the clock spends high. If the clock is high for 1.46 ns out of 3.00 ns, the high phase and low phase differ. The rising-edge word then has a slightly different window than the fallingedge word. Two delay values let you model this asymmetry.
| Reason for two values | What it captures |
|---|---|
| Two launches per period | Rising and falling words checked separately |
| Asymmetric sender | Rise and fall paths differ inside the sender |
| Duty cycle not 50 percent | High phase and low phase differ in length |
| Edge-specific board skew | Wire effects differ by edge |
Center-Aligned vs Edge-Aligned DDR
DDR links come in two flavors based on where the clock sits relative to the data window. This choice shapes the delay numbers. In edge-aligned DDR, the clock edge lines up with the start of the data window. The data changes right at the clock edge. The receiver cannot sample at the edge, because the data is changing there. The receiver must delay its clock to land in the middle of the eye. This is common when a sender forwards both clock and data together. In center-aligned DDR, the clock edge sits in the middle of the data window. The data is already stable at the edge. The receiver can sample right at the edge. This is friendlier for the receiver, because the eye is centered on the sampling point.

| Trait | Edge-aligned | Center-aligned |
|---|---|---|
| Clock edge position | At data transition | At center of data eye |
| Receiver action | Must shift clock into the eye | Can sample at the edge |
| Typical setup delay value | Larger | Smaller |
| Where seen | Clock forwarded with data | Pre-aligned interfaces |
The delay numbers reflect the flavor. For center-aligned input, the rise and fall delays are small and roughly balanced, because the eye is centered. For edge-aligned input, the delays are larger and may be more lopsided, because the receiver must account for the shift it adds. Here is a worked check for one edge of a center-aligned input.
# Worked illustrative example — rising-edge setup, center-aligned DDR
Half period (capture window) = 3.00 / 2 = 1.50 ns
Input delay rise (-max) = 1.14 ns
Internal path to capture flop = 0.19 ns
Capture flop setup time = 0.13 ns
Data ready at capture flop = 1.14 + 0.19 = 1.33 ns
Edge needs data by = 1.50 - 0.13 = 1.37 ns
Rising-edge setup slack = 1.37 - 1.33 = 0.04 ns
A slim 0.04 ns of slack. DDR margins are tight because each word lives in only half a period. Now the falling edge, using its own delay value.
# Worked illustrative example — falling-edge setup, center-aligned DDR
Half period = 1.50 ns
Input delay fall (-max) = 1.21 ns
Internal path to capture flop = 0.19 ns
Capture flop setup time = 0.13 ns
Data ready at capture flop = 1.21 + 0.19 = 1.40 ns
Edge needs data by = 1.50 - 0.13 = 1.37 ns
Falling-edge setup slack = 1.37 - 1.40 = -0.03 ns
The falling edge fails by 0.03 ns. This is exactly why both values matter. The rising edge looked fine, but the falling edge was hiding a small violation. One delay value would have missed it.
Common DDR Constraint Mistakes
DDR constraints trip up many engineers. A few mistakes show up over and over. The first is forgetting -add_delay . Without it, the falling-edge constraint replaces the rising-edge one. The tool then checks only the falling edge. Half the data goes unchecked. Always add it on every DDR delay line after the first. The second is using the same number for both edges out of habit. That throws away the whole point. If rise and fall truly differ by 0.07 ns, copying one value into both hides the difference. Use the real edgespecific numbers. The third is mixing up the alignment flavor. If the interface is center-aligned but you write edge-aligned numbers, your slacks are wrong. Know which flavor the link uses before you pick values.
| Mistake | Symptom | Fix |
|---|---|---|
| Missing -add_delay | Only one edge checked | Add it to each extra delay line |
| Same value both edges | Hidden edge-specific failure | Use real rise and fall numbers |
| Wrong alignment flavor | Slacks off by a fixed shift | Confirm edge vs center first |
| Forgetting -clock_fall | Falling edge never modeled | Add the switch on fall lines |
After constraining, you report each edge on its own to confirm both pass.

one word on the rising edge and one on the falling edge, so two words per period. At a 333.7 MHz clock with a 3.00 ns period, a word goes out roughly every 1.50 ns instead of every 3.00 ns. The clock frequency stays the same, but throughput doubles.
falling-edge data are separate timing events, each with its own eye. The first line, with no switch, ties data to the rising edge. The second line, with -clock_fall , ties data to the falling edge. Without the second line, the tool never checks the falling-edge word. For example, a 1.14 ns rise delay and a 1.21 ns fall delay are both needed.
of the named clock instead of the rising edge. By default a delay constraint uses the rising edge. Adding -clock_fall makes the tool launch or capture against the falling edge, which is exactly what a DDR link's second word needs.
constraint on the same port replaces the earlier one. So the falling-edge line would erase the risingedge line, and only the falling edge would be checked. With -add_delay , both constraints coexist, and the tool checks all four cases: rise-max, rise-min, fall-max, fall-min.
the clock edge lands at the data transition, so the receiver must shift its clock into the eye to sample safely. In center-aligned DDR, the clock edge sits in the middle of the data eye, so the receiver can sample right at the edge. Center-aligned gives smaller, more balanced delay values; edge-aligned gives larger ones.
period and 0.13 ns setup, the deadline is 1.37 ns. A 1.14 ns rise delay plus 0.19 ns internal path is
ready at 1.33 ns, so rise slack is +0.04 ns. But a 1.21 ns fall delay plus the same path is ready at 1.40 ns, so fall slack is -0.03 ns. The falling edge fails. One shared value would have hidden it.
Key Takeaways
- DDR moves data on both clock edges, so each word lives in only half a clock period and the margins are tight.
- Write each delay constraint twice, once for the rising edge and once with
-clock_fallfor the falling edge. - Always use
-add_delayso the falling-edge line adds to rather than replaces the rising-edge line. - Two delay values capture real asymmetry from duty cycle, sender imbalance, and edge-specific eyes; one value can hide a failure.
- Center-aligned puts the edge in the eye center for easy sampling, while edge-aligned puts the edge at the transition and forces the receiver to shift its clock.
ChipBuddy
← Home
Comments
Leave a Reply