Artix-7 timing closure gets easier when the constraints describe what the design does and the build refuses to produce a bitstream that misses timing. This article walks through the Vivado timing closure work on IronKernel 64, our open-source SID music machine on a Digilent Nexys Video with an AMD (formerly Xilinx) Artix-7 XC7A200T. The release build closes with a worst negative slack (WNS) of 0.083 ns, a worst hold slack (WHS) of 0.012 ns and 0 failing endpoints in Vivado 2026.1. The constraints quoted here are in the public repository.
The clock domains on the Artix-7
One 100 MHz oscillator feeds the system MMCM. Two more MMCMs make the video and audio clocks, and the DDR3 controller and the Ethernet PHY add their own.
| Clock | Frequency | Use |
|---|---|---|
cpu_clk | 100 MHz | Both MicroBlaze V cores and the register blocks |
sid_clk | 50 MHz | The SID core and network protocol logic |
clk200 | 200 MHz | DDR3 controller IDELAY reference |
ui_clk | 100 MHz | DDR3 controller user side |
clk125, clk125_90 | 125 MHz | RGMII transmit and its 90-degree forwarded clock |
axis_clk | 62.5 MHz | Network client side |
pix_clk, pix_clk_x5 | 74.25 / 371.25 MHz | 720p60 video and the TMDS serializers |
aud_clk | 12.288 MHz | Audio codec, 256 × 48 kHz |
eth_rxck | 125 / 25 / 2.5 MHz | RGMII receive, from the PHY |
The design uses synchronous resets, one reset synchronizer per clock domain, no derived clocks and no clock gating.
One primary clock and no blanket clock groups
The board XDC declares one primary clock, the 100 MHz oscillator. Vivado derives the MMCM outputs and the DDR3 controller clocks from it. Only the two forwarded I/O clocks, for RGMII transmit and the QSPI flash, get a hand-written generated clock.
The XDC declares no set_clock_groups. Declaring two clocks asynchronous with set_clock_groups stops Vivado from timing any path between them, including a crossing added later without a synchronizer. IronKernel 64 constrains each crossing on its own instead.
Clock domain crossings: synchronizers first, then scoped constraints
Every crossing in the top level goes through one of a few blocks, and a Tcl script gives each class the constraint its protocol needs after synthesis:
- Single-bit levels use a 3-stage synchronizer chain marked
ASYNC_REG, with a false path only to the first flip-flop. - Gray-coded counters get a datapath-only max delay and a bus skew limit of one source clock period.
- The AXI4-Lite register crossing from
cpu_clktosid_clkholds its data steady while a toggle crosses, and the data gets a max delay of one destination period. - The audio and SID stream FIFOs are AMD
xpm_fifo_asyncmacros, and the DDR3 path uses the AMD AXI clock converter IP. Both carry their own constraints.
The script fails the build when an expected crossing is missing or the number of crossings changes, so a renamed register cannot lose its constraint without a message. The build also runs report_cdc and fails on any Warning or Critical finding that has no written waiver.
False paths only where no clock relationship exists
The board-level false paths cover pins with no timing relationship to an internal clock: the reset button, UART, PS/2, HDMI hot-plug, the I2C bus, LEDs, the OLED and slow Ethernet PHY side-band signals. The audio I2S outputs are also false paths, because they leave from IOB flip-flops and the codec bit clock runs at only 3.072 MHz.
When the RTL does not close: change the clock
The SID core is the open-source reDIP-SID. At 100 MHz its unmodified filter multiply-add misses timing with a WNS of -0.187 ns. The multiply-add is combinational and the filter logic relies on that zero latency, so the design keeps it unchanged. The core needs at least 20 clocks per SID clock phase, so the design runs it on a 50 MHz sid_clk, which gives 25 or 26 clocks per phase.
Pipelining is the usual fix for a long combinational path. When the logic around it depends on the latency, a slower clock for that block can be the cleaner fix.
I/O timing: RGMII and QSPI
On RGMII transmit, the forwarded clock first had its generated clock rooted on the ODDR output, which left the output buffer out of the clock path. The result was a false setup violation of -1.46 ns. Rooting the generated clock on the output pad times clock and data through the same path, and the release build shows positive setup and hold margins on transmit and receive.
The QSPI configuration flash runs through the AXI Quad SPI IP and the STARTUPE2 primitive. With its output-enable registers left in the fabric, the build missed timing on those paths by 2.19 ns. Placing them in the IOBs and following the constraint recipe in AMD's product guide for the IP closed the interface.
The implementation flow and its gates
The build is a non-project Vivado Tcl script. After synthesis it runs opt_design, place_design, phys_opt_design and route_design with the Explore and AggressiveExplore directives, and runs phys_opt_design again if routed WNS is negative.
Each failure class has its own exit code, and nothing is written after a failed gate:
- Setup, hold or pulse-width slack below 0, or any failing endpoint.
- Unclocked or unconstrained endpoints, or loops, from
check_timing. - LUT, flip-flop, block RAM or DSP use above 80 %.
- Latches, or asynchronous resets outside a named list.
- Critical warnings in implementation, or methodology findings without a written waiver.
Block RAM sits at 78.49 % of the device, so the small OLED framebuffer uses distributed RAM on purpose.
A Vivado timing closure checklist
The IronKernel 64 build follows these steps:
- Run
check_timingand fix unconstrained endpoints before reading slack. - Read
report_timing_summaryfor setup, hold and pulse-width slack. - Read
report_clock_interactionto see which clocks exchange data. - Put a synchronizer or an asynchronous FIFO on every crossing, and constrain each one by cell instead of grouping whole clocks.
- Run
report_cdcandreport_methodology, and fail the build on their findings. - Pipeline long paths where latency allows, or move the block to a slower clock.
- Use placement and routing directives and
phys_opt_designonce the constraints are right. - Check I/O timing against the data sheet of the device on the other side of the pins.
Our FPGA design service covers this work on AMD devices, from the first constraint file to a gated build.