| name | vivado |
| description | Xilinx/AMD Vivado FPGA build workflow -- batch-mode TCL scripting, ADI HDL reference design integration, Zynq-7000/MPSoC block design, IP builds, synthesis, implementation, and bitstream generation. Use for any Zynq or UltraScale+ design that incorporates ADI transceivers or follows the ADI HDL reference design pattern.
|
Environment Setup
| Item | Notes |
|---|
| Vivado | AMD Vivado 2024.x or 2025.x — batch mode preferred for CI |
| ADI HDL | Clone from https://github.com/analogdevicesinc/hdl |
| Version mismatch | Set ADI_IGNORE_VERSION_CHECK=1 for minor version mismatches |
| Windows paths | Use forward slashes in TCL; backslashes cause parser errors |
| Linux | Preferred for CI builds; AMD tools are more stable on Linux |
vivado -mode batch -source scripts/system_project.tcl 2>&1 | tee build.log
"C:/AMD/Vivado/2025.1/bin/vivado.bat" -mode batch -source scripts/system_project.tcl
cd fpga/build/
export ADI_IGNORE_VERSION_CHECK=1
vivado -mode batch -source ../scripts/system_project.tcl
ADI HDL Reference Design Pattern
ADI's open-source HDL repo provides reference designs and reusable IP for
AD9361, ADRV9002, ADRV9006, AD9371, AD9375, AD9364, ADAS1000, and many others.
The infrastructure (TCL procs, AXI IP cores, DMA, CDC utilities) is the same
across all parts.
Repository Layout (ADI HDL)
hdl/
library/ -- Reusable IP cores (axi_ad9361, axi_adrv9001, axi_dmac, etc.)
projects/ -- Reference designs (one per eval board)
scripts/ -- adi_board.tcl, adi_project.tcl (key infrastructure)
Key TCL Procedures
# In adi_board.tcl / adi_project.tcl:
adi_project_create <name> 0 {} {<device_part>} # Create project for device
ad_ip_instance <type> <name> # Instantiate IP in block design
ad_ip_parameter <name> <param> <value> # Set IP parameter
ad_connect <a> <b> # Connect pins/nets/interfaces
ad_cpu_interconnect <addr> <periph> # Add to GP AXI interconnect
ad_mem_hp_interconnect <clk> <periph> # Add to HP memory interconnect
ad_cpu_interrupt <ps-N> <mb-N> <irq> # Connect interrupt
Build a Custom Project Using ADI IPs
1. Clone ADI HDL: git clone https://github.com/analogdevicesinc/hdl
2. Create project directory: fpga/scripts/
3. Write system_project.tcl — calls adi_project_create, sources system_bd.tcl
4. Write system_bd.tcl — block design: PS7/PSU + ADI IPs + DMA + interconnects
5. Write system_top.v — top-level Verilog instantiating the BD wrapper + LVDS/CMOS IO
6. Write system_constr.xdc — pin constraints + timing
7. Build: cd build/ && vivado -mode batch -source ../scripts/system_project.tcl
Required IP Builds (one-time per ADI HDL clone)
Build IP libraries before referencing them in a project. On Linux use make:
cd hdl/library && make
cd hdl/library/axi_dmac && vivado -mode batch -source axi_dmac_ip.tcl
cd hdl/library/util_cdc && vivado -mode batch -source util_cdc_ip.tcl
cd hdl/library/axi_ad9361 && vivado -mode batch -source axi_ad9361_ip.tcl
On Windows make is unavailable — build IPs individually:
for ip in axi_adrv9001 axi_dmac util_cdc util_cpack2 util_upack2 axi_sysid sysid_rom util_axis_fifo; do
vivado.bat -mode batch -source hdl/library/${ip}/${ip}_ip.tcl
done
Zynq-7000 (PS7) Block Design
PS7 Clock Domains
# Standard 3-clock setup (matches ADI reference designs)
ad_ip_parameter sys_ps7 CONFIG.PCW_FPGA0_PERIPHERAL_FREQMHZ 100 ;# sys_cpu_clk
ad_ip_parameter sys_ps7 CONFIG.PCW_FPGA1_PERIPHERAL_FREQMHZ 200 ;# sys_dma_clk
ad_ip_parameter sys_ps7 CONFIG.PCW_FPGA2_PERIPHERAL_FREQMHZ 200 ;# sys_iodelay_clk
# Connect clocks (must be named bd_nets, not bd_pins, for ad_mem_hp_interconnect)
ad_connect sys_cpu_clk sys_ps7/FCLK_CLK0
ad_connect sys_dma_clk sys_ps7/FCLK_CLK1
ad_connect sys_iodelay_clk sys_ps7/FCLK_CLK2
set sys_cpu_clk [get_bd_nets sys_cpu_clk]
set sys_dma_clk [get_bd_nets sys_dma_clk]
set sys_iodelay_clk [get_bd_nets sys_iodelay_clk]
MIO Peripheral Assignment
MIO ranges are fixed per peripheral — the Zynq-7000 TRM Table B.26 defines valid ranges.
| Peripheral | Valid MIO ranges |
|---|
| QSPI | 0..5 (single), 0..9 (dual) |
| SD0 | 16..21, 28..33, 40..45 (select via PCW_SD0_SD0_IO) |
| SD1 | 10..15, 22..27, 34..39, 46..51 |
| UART0 | 10..15, 18..19, 22..27, 30..31, 34..39, 46..51 |
| UART1 | 8..13, 16..17, 20..21, 24..29, 32..37, 40..45, 48..53 |
| I2C0 | 10..15, 22..27, 34..39, 46..51 (pairs: 10-11, 14-15, etc.) |
| I2C1 | 12..13, 16..17, 20..21, 24..25, 28..29, 32..33, 36..37, 40..41 |
| SPI0 | 16..21, 28..33, 40..45 |
| SPI1 | 10..15, 22..27, 34..39, 46..51 |
| GPIO | Any MIO not used by peripherals |
Key rules:
- Enable peripherals in TCL order: SD0 before SPI0 if they share valid ranges
- SPI on MIO = no EMIO ports needed; don't wire SPI ports in system_top.v
- UART must have TX+RX assigned to the same MIO range pair
Interrupt Wiring
# Pre-connect all interrupt concat inputs to GND before wiring peripherals
# (ad_cpu_interrupt does disconnect+reconnect — fails on floating inputs)
ad_connect GND xlconcat_0/In0
ad_connect GND xlconcat_0/In1
# ... repeat for all inputs
ad_cpu_interrupt ps-13 mb-13 axi_adrv9001/irq
ad_cpu_interrupt ps-12 mb-12 axi_adrv9001_rx1_dma/irq
MPSoC / UltraScale+ (PSU)
Same ADI infrastructure, different IP name (sys_psu instead of sys_ps7),
HP ports become HPM ports, and interrupt numbering changes. The ADI reference
designs for ZCU102/ZCU104 show the pattern.
# MPSoC uses PSU instead of PS7
ad_ip_instance zynq_ultra_ps_e sys_psu
ad_ip_parameter sys_psu CONFIG.PSU__USE__M_AXI_GP0 1
ad_ip_parameter sys_psu CONFIG.PSU__USE__S_AXI_HP0 1
AXI Address Map
# Assign base addresses for peripherals on GP0 (CPU-accessible)
# Standard ADI layout (customize as needed):
assign_bd_address -target_address_space sys_ps7/Data \
[get_bd_addr_segs axi_ip_name/s_axi/reg0] -range 0x10000 -offset 0x44A00000
Typical ADI address ranges:
0x44A00000 — transceiver core (axi_ad9361, axi_adrv9001, etc.)
0x44A30000+ — DMA channels (4 × 0x10000 each for RX1, RX2, TX1, TX2)
0x45000000 — axi_sysid
Build Artifacts
build/
<project>.xpr -- Vivado project file
<project>.runs/
synth_1/ -- Synthesis outputs
impl_1/ -- Implementation outputs + bitstream
<project>.sdk/ -- XSA + FSBL sources (post-implementation)
write_hw_platform -fixed -force -file system_top.xsa
BOOT.bin Generation
vitis -s build_fsbl.tcl
bootgen -image boot.bif -arch zynq -o BOOT.bin -w
boot.bif format:
the_ROM_image:
{
[bootloader] fsbl.elf
system_top.bit
u-boot.elf
}
Simulation (Vivado Simulator / ModelSim)
# Add simulation sources
add_files -fileset sim_1 tb/tb_top.v
# Run behavioral simulation
set_property top tb_top [get_filesets sim_1]
launch_simulation -simset sim_1 -mode behavioral
For ADI IP simulation, use ADI's hdl/testbenches/ directory which provides
self-checking testbenches for DMA, CDC, and transceiver data paths.
ILA Debug Cores
# Add ILA after synthesis to probe internal signals
create_debug_core u_ila_0 ila
set_property C_DATA_DEPTH 4096 [get_debug_cores u_ila_0]
set_property C_NUM_OF_PROBES 4 [get_debug_cores u_ila_0]
connect_debug_port u_ila_0/probe0 [get_nets {axi_data[7:0]}]
implement_debug_core
write_bitstream -force system_top.bit
Common Gotchas
- DDR part number format: Vivado uses a space before the speed grade:
MT41K256M16 HA-125 not MT41K256M16HA-125. Wrong format → DDR interface fails.
- Clock/reset must be named nets: Use
set sys_cpu_clk [get_bd_nets sys_cpu_clk] — using get_bd_pins breaks ad_mem_hp_interconnect.
- Interrupt concat inputs must pre-connect to GND:
ad_cpu_interrupt disconnects then reconnects — floating inputs cause TCL errors.
- PS7 DDR wrapper is always 32-bit: DQ[31:0], DM[3:0], DQS[3:0] — even if the physical bus is 16-bit the Vivado wrapper adds both bytes.
flock unavailable on Windows: ADI Makefile uses it; build IPs individually with vivado batch on Windows.
adi_project_create sources system_bd.tcl from CWD: Put a thin wrapper in build/ that sources ../scripts/system_bd.tcl — do not source from the scripts/ dir directly.
ADI_IGNORE_VERSION_CHECK=1: Required when using a slightly newer Vivado than ADI targets — ADI validates exact version and errors out otherwise.
- SPI MIO ranges are fixed per peripheral: Wrong range (e.g. SPI0 on MIO 10..15) → PS7 validation error at block design validation. Consult TRM Table B.26.
- IDELAYCTRL requires 200 MHz:
sys_iodelay_clk must be exactly 200 MHz — not 199 or 201. Drives LVDS capture IDELAY elements.
- Vivado 2025.x
EXCEPTION_ACCESS_VIOLATION after IP synthesis report_utilization — benign, checkpoint is written. Continue; retry uses cache.
Post-Synthesis Checklist
Platform Guidance
- Claude Code: Present synthesis results inline. Use AskUserQuestion for constraint decisions.
- Codex/OpenCode: Present results as text. Ask user for confirmation on timing constraints.
- CLI: Vivado operations run via Xilinx Vivado CLI tools, not circuit-weaver.