# Advanced: project structure and customization This section is intended for users who want to modify the reference designs — adding IP to the block design, changing constraints, modifying the standalone application, or adding packages or drivers to the PetaLinux project. It describes how the design works, how the repository is laid out, how the Make-driven build flow works, how the Vitis and PetaLinux sides are organised, and what modifications have been added on top of the stock AMD BSPs. The actual *build* instructions are in [build_instructions](build_instructions); this section is about understanding the project well enough to modify it. ## How the design works ### Data path Each used port of the Ethernet FMC Max is driven by a hard PS GEM controller. The GEM's GMII interface is routed into the programmable logic through EMIO, where an Ethernet PCS/PMA or SGMII core ([PG047](https://docs.amd.com/r/en-US/pg047-gig-eth-pcs-pma)) converts the GMII to SGMII over a gigabit transceiver lane connected to the port's DP83867 PHY. The PCS/PMA cores are configured in **"Ethernet MAC: GEM"** mode with auto-negotiation enabled. In this mode the core generates the GMII TX and RX clocks for the GEM and adapts the data rate to the speed resolved by SGMII auto-negotiation with the PHY, so 10M/100M/1G links all work once the link is up. The cores do, however, power up with the IEEE ISOLATE bit set and pass no data until it is cleared over MDIO — see *PCS/PMA ISOLATE bit and the `pcs-unisolate` service* below. On the Zynq UltraScale+ targets all four GEMs are used, one per port (GEM*n* → port *n*). The Versal PS has only two GEMs, so the VCK190 design drives ports 0 and 1 and holds the PHYs of ports 2 and 3 in reset. ### MDIO The Ethernet FMC Max has a single shared MDIO bus for its four PHYs, at addresses 1, 3, 12 and 15 for ports 0-3. **GEM0 masters that external bus** — the block design routes GEM0's EMIO MDIO interface out to the FMC pins — so GEM0's driver manages all four PHYs. GEM0 also masters every PL PCS/PMA core: each core's MDIO management interface is wired onto GEM0's bus at address `8 + port` (8, 9, 10, 11), alongside the external PHYs. This access is **required**, not optional — the cores power up isolated and must be un-isolated over MDIO (see the next section). The MDIO masters of GEM1-3 are unused. ### PCS/PMA ISOLATE bit and the `pcs-unisolate` service Each PL PCS/PMA core is an MDIO slave at address `8 + port` on GEM0's bus (port 0 → 8, port 1 → 9, …). Per IEEE 802.3 clause 22, a PHY/PCS strapped to a non-zero MDIO address powers up **isolated** (control register 0, bit 10 = 1). While isolated the core still completes SGMII auto-negotiation — so the link reports "Up - 1Gbps/Full" — but the GMII datapath to the GEM is cut, so no traffic passes: RX stays at zero and DHCP never completes. The bit must be cleared over MDIO; this is the standard step in AMD's own software (`get_Xilinx_pcs_pma_phy_speed()`). * **Bare-metal:** `xemacpsif_physpeed.c` clears it (`pcs_pma_release_isolate()`) before bringing each port up. * **U-Boot:** the `pcs_isolate_clr` environment block clears it before `distro_bootcmd`. * **Linux:** the `macb` driver does not manage the PCS (the GEM's `phy-handle` points at the external DP83867, and this kernel has no external-PCS support), so a small systemd one-shot — `pcs-unisolate` (`recipes-apps/pcs-unisolate`) — brings GEM0 up and clears ISOLATE on every responding core (MDIO 8–11) before the network is configured. It is force-installed via `IMAGE_INSTALL` in each board's `petalinuxbsp.conf`. ```{note} **The Linux service is a stop-gap and should be removed once the kernel can own the PCS.** The upstream-intended mechanism is Sean Anderson's `pcs-xilinx` PCS driver plus Cadence-`macb` external-PCS support, which model the core as an `xlnx,pcs` MDIO node referenced by the GEM's `pcs-handle`; phylink then clears ISOLATE and runs SGMII auto-negotiation with no userspace help. That series was **not merged** as of linux-xlnx 6.12 / 2025.2 (still in net-next review). When a future kernel ships it: describe the PCS cores in the device tree (an `xlnx,pcs` node + a `pcs-handle` on each `gem`), delete the `pcs-unisolate` recipe and its `IMAGE_INSTALL` line, and remove this note. ``` ### Clocking * **GT reference clock:** 125 MHz from the Si511 oscillator on the Ethernet FMC Max, wired to the FMC connector's GBTCLK0 pins. Port 0's PCS/PMA core is built with the shared logic ("Include Shared Logic in Core") on ZynqMP and distributes `userclk`/`userclk2`/`rxuserclk` etc. to the other three cores. On Versal the reference clock enters through an IBUFDSGTE utility buffer into the `gt_quad_base`, and each PCS/PMA core's user clocks are generated by per-channel BUFG_GTs. * **Independent (DRP) clock:** 50 MHz from the PS (`pl_clk1` on ZynqMP, `pl1_ref_clk` on Versal), connected to each core's `independent_clock_bufg` input (`DrpClkRate` = 50). * **GMII clocks:** generated by the PCS/PMA cores in GEM mode and carried to the GEMs by the EMIO GMII interface connections. * **System clock:** 100 MHz PS fabric clock (`pl_clk0` / `pl0_ref_clk`) for the AXI interfaces. ```{note} On the UltraZed-EV target the 50 MHz pl_clk1 is sourced from the IOPLL rather than the default RPLL — with the RPLL source the clock was not set to the correct frequency under PetaLinux on that board (see the comment in `bd_zynqmp.tcl`). ``` ### PHY resets and GPIO * **ZynqMP:** the PHY reset outputs are driven by the `proc_sys_reset` block, so the PHYs are released automatically when the PS comes up. * **Versal:** the PHY resets are driven by PMC GPIO EMIO bits 0 and 1, giving software control of the PHY resets (the standalone application and PetaLinux release them through the GPIO). * Unused ports' PHY resets are tied low (held in reset). * An AXI GPIO (10-bit, input only) is connected to the FMC power-good signals (2 bits) and the PHY GPIO signals (2 per port) for optional monitoring by software. ## Repository layout ``` . ├── Makefile <- Top-level build entry point ├── README.md ├── config/ <- Source-of-truth design metadata and auto-generation │ ├── data.json │ └── update.py ├── docs/ <- This documentation (Sphinx + Read the Docs) ├── EmbeddedSw/ <- Patched lwIP sources used by the Vitis build ├── PetaLinux/ │ ├── Makefile <- PetaLinux build orchestration │ └── bsp/ <- Per-board and per-port-config BSP fragments │ ├── uzev/, zcu102/, vck190/, … <- board-specific overlays │ └── ports-0123/, ports-01xx/ <- port-config overlays ├── submodules/ <- Vendor board definition files (BDFs) ├── Vivado/ │ ├── Makefile <- Vivado build orchestration │ ├── scripts/ │ │ ├── build.tcl <- Project creation + block design assembly │ │ └── xsa.tcl <- Synthesis, implementation, XSA export │ └── src/ │ ├── bd/ │ │ ├── bd_zynqmp.tcl <- Block design for Zynq UltraScale+ targets │ │ ├── bd_versal.tcl <- Block design for Versal targets │ │ └── gt_locs.tcl <- Per-target GT placement constants (ZynqMP) │ └── constraints/ │ └── .xdc <- One XDC per target (pin assignments, timing) └── Vitis/ ├── Makefile <- Vitis workspace + boot-image orchestration ├── py/ │ ├── args.json <- Repo-specific Vitis flow configuration │ ├── build-vitis.py <- Universal Vitis Python build driver │ ├── make-boot.py <- BOOT.BIN packaging │ ├── pre_build.py <- Per-build hook (Ethernet port selection patch) │ └── pre_platform_build.py ├── common/ │ └── src/ <- Standalone application source (echo_server + VADJ) └── _workspace/ <- Per-target Vitis workspace (generated) ``` Per-target build outputs are written to `Vivado//`, `Vitis/_workspace/`, and `PetaLinux//`; packaged boot-image zips are written to `bootimages/`. None of these are committed. ## Target naming A `TARGET` is the canonical handle for a single design and is the only parameter passed through the build flow. It encodes the board and, for boards with multiple FMC connectors, the connector: ``` [_] ``` Examples: `uzev`, `vck190_fmcp1`, `zcu102_hpc0`, `zcu106_hpc0`, `zcu111`. The first underscore-delimited token is taken as the *target board* and is what `PetaLinux/Makefile` uses to select the BSP under `PetaLinux/bsp//`. The complete list of valid targets is in the `UPDATER START` block of each Makefile and is generated from `config/data.json` (see below). ## `config/data.json` and `config/update.py` `config/data.json` is the canonical source of truth for the set of supported designs and their per-target metadata (board name, processor family, FMC connector, port lane mapping, baremetal-vs-PetaLinux support, etc.). `config/update.py` reads `data.json` and regenerates the auto-managed sections of the four Makefiles, the top-level `README.md`, and `.gitignore` — the sections delimited by `UPDATER START` / `UPDATER END` comment markers. When adding or modifying a target, edit `data.json` and re-run `update.py`. Do not hand-edit content between the `UPDATER START` / `UPDATER END` markers; it will be overwritten on the next regeneration. ## Make-driven build flow There are four Makefiles in the repository, each scoped to a stage of the build: | Makefile | Scope | |-----------------------|------------------------------------------------------------------------------------------------| | `./Makefile` | Top-level orchestration; assembles boot-image zips for one or all targets. | | `./Vivado/Makefile` | Creates the Vivado project, runs synthesis and implementation, exports the XSA. | | `./Vitis/Makefile` | Creates the Vitis workspace and platform from the XSA, builds the standalone application, packages BOOT.BIN. | | `./PetaLinux/Makefile`| Creates the PetaLinux project from the XSA, applies BSP overlays, builds, packages. | A `make bootimage TARGET=` invocation at the top level cascades: ``` make bootimage TARGET=t -> Vitis side: Vitis/Makefile workspace TARGET=t -> bootfile TARGET=t -> ensures Vivado XSA exists Vivado/Makefile xsa TARGET=t -> vivado -mode batch -source scripts/build.tcl (creates project) -> vivado -mode batch -source scripts/xsa.tcl (synth, impl, XSA export) -> python3 py/build-vitis.py ... (creates platform + app, builds) -> python3 py/make-boot.py ... (packages BOOT.BIN) -> PetaLinux side: PetaLinux/Makefile petalinux TARGET=t -> petalinux-create --template --name t -> petalinux-config --get-hw-description -> copy bsp//project-spec/* into the project (board BSP) -> copy bsp//project-spec/* into the project (overlay) -> petalinux-config --silentconfig -> petalinux-build -> petalinux-package boot ... -> zip the resulting boot files into bootimages/ ``` Per-target lock files (`..lock` in each Makefile's directory) prevent two concurrent builds of the same target from clobbering each other. ## Vivado side ### Block design The block-design scripts live under `Vivado/src/bd/`, one per processor family, plus a GT-placement helper: * `bd_zynqmp.tcl` — Zynq UltraScale+ targets. * `bd_versal.tcl` — Versal targets. * `gt_locs.tcl` — Tcl dictionary mapping each ZynqMP target to its per-port GT coordinates (e.g. `dict get $gt_loc_dict uzev 0` returns `X0Y8`), used to set the `GT_Location` parameter of each PCS/PMA core. The Versal targets do not use this dictionary — on Versal the GT quad placement is derived from the package pin constraints of the GT serial port in the target's XDC. Edit this dictionary when adding a new ZynqMP target or changing its GT placement. The ZynqMP script enables one GEM (EMIO GMII + EMIO MDIO) per port and instantiates one PCS/PMA core per port; port 0's core contains the shared logic and clocks the others. The Versal script configures the CIPS for GEM0/GEM1 on EMIO and builds the transceiver side from a `gt_quad_base`, per-channel BUFG_GTs, and an AXI APB bridge that gives the PS access to the GT quad's APB3 interface via `M_AXI_LPD` (through an AXI SmartConnect that also serves the AXI GPIO). Each script contains per-board conditional blocks where a target needs to deviate from the family defaults — for example the UltraZed-EV's IOPLL source for `pl_clk1`. After sourcing the BD script, `scripts/build.tcl` runs `validate_bd_design -force`, which triggers parameter propagation and fills in connection-automation rules. As a result the final implemented design may contain nets that aren't visible in the BD TCL source — to see the actual netlist as built, inspect the saved `.bd` file under `Vivado//.srcs/sources_1/bd//` or use `write_bd_tcl` to export a complete script from an open project. ### Constraints `Vivado/src/constraints/.xdc` contains pin assignments and any target-specific timing constraints. Constraints common to all targets of a given family are not factored out — each target's XDC is self-contained. ### Build scripts * `Vivado/scripts/build.tcl` creates the Vivado project, adds the target's XDC, sources the appropriate `bd_*.tcl`, and validates the block design. Invoked via `make project TARGET=`. * `Vivado/scripts/xsa.tcl` opens the existing project, runs synthesis and implementation, exports the XSA, and writes the bitstream into the implementation run directory. Invoked via `make xsa TARGET=`. Both scripts check `XILINX_VIVADO` to confirm the installed Vivado version matches the `version_required` constant at the top of the file. Bumping the project to a new Vivado release means changing those constants and re-testing — the BD TCL APIs are not stable across major releases. ### Modifying the block design Edit the block-design script for the appropriate processor family directly. If the change applies only to some targets in the family, wrap the additions in the appropriate per-board conditional block. Once the script is edited, delete any existing per-target Vivado project directory (`rm -rf Vivado/`) and re-run the Vivado build through the Makefile: ``` make -C Vivado xsa TARGET= ``` This re-creates the project, sources the modified BD script, runs `validate_bd_design`, synthesises, implements, and re-exports the XSA. Downstream Vitis / PetaLinux / boot-image steps will pick up the new XSA on the next `make` at the top level. ### Adding or modifying constraints Edit `Vivado/src/constraints/.xdc` directly. If a constraint applies to all targets in a family, it still needs to be replicated to each target's XDC — there is no shared XDC. ## Vitis side The standalone (baremetal) build runs the lwIP echo-server example on the target, exercising one Ethernet FMC Max port at a time through its PS GEM. The application source is shared across all targets; per-target specialisation is handled by the build driver, not by per-target source. The Ethernet FMC Max requires the FMC adjustable I/O voltage rail (VADJ) to be programmed to 1.5V before the on-board PHYs come out of reset. On the Versal target (VCK190), the standalone application performs that programming at startup via the `vadj.c` / `vadj.h` files in `common/src` (the entry point in `main.c` calls `vadj_enable(VADJ_1V5)`). The ZynqMP boards rely on the FSBL (ZCU102/ZCU106) or board-default rails to bring VADJ up. ### Layout ``` Vitis/ ├── Makefile ├── py/ │ ├── args.json │ ├── build-vitis.py <- Universal Vitis Python build driver │ ├── make-boot.py <- BOOT.BIN packaging │ ├── pre_build.py <- Hook run before each app build │ └── pre_platform_build.py <- Hook run before each platform build ├── common/ │ └── src/ <- Application source: main.c + vadj.c/h ├── boot// <- Per-target packaged boot files └── _workspace/ <- Generated Vitis workspace per target ``` ### `args.json` `Vitis/py/args.json` is the repo-specific configuration that drives the universal `build-vitis.py` driver. The key fields are: * `bd_name` — block-design name (`psgem`). * `app_name` — name of the Vitis application (`echo_server`). * `app_template` — the Vitis app template the build driver uses to scaffold the application (`lwip_echo_server`). * `bsp_libs` — BSP libraries to add and configure (`lwip220` with DHCP + ACD check + an enlarged pbuf pool, and `xiltimer` with the interval timer enabled). * `src` — application source mapping. `"all": "common/src"` means every target uses the same source directory. * `pre_platform_build_script` / `pre_build_script` — hooks invoked at the appropriate point in the workspace build. ### The `pre_build.py` port-selection patch The stock `lwip_echo_server` template generates `platform_config.h` from `platform_config.h.in` via cmake and always picks the first GEM instance, so the echo server would always run on GEM0. The `py/pre_build.py` hook patches `platform_config.h.in` in the workspace application sources, replacing the cmake-generated `PLATFORM_EMAC_BASEADDR` with a port-selection block driven by an `ETHERNET_PORT` define (see [echo_server](echo_server) for usage). ### The `EmbeddedSw/` lwIP patches `EmbeddedSw/` mirrors the directory structure of the AMD `embeddedsw` repository and contains one patched file, `ThirdParty/sw_services/lwip220_v1_3/src/lwip-2.2.0/contrib/ports/xilinx/netif/xemacpsif_physpeed.c`, which the build driver copies over the stock sources in the workspace's local `embeddedsw` repository. The patches: * **`detect_phy` restriction for GEM0** — GEM0 masters the shared MDIO bus with all four PHYs (addresses 1, 3, 12, 15). The stock scan would find all four and block on auto-negotiation of ports with no cable attached, so GEM0 is restricted to the port 0 PHY at address 1. GEM1-3 each see only their PCS/PMA core's management interface (address 9) on their own bus, so the generic scan is fine for them. * **TI PHYs always take the SGMII speed path** — the DP83867 PHYs are always in SGMII mode in these designs, but the hardware handoff reports the MAC-side interface (`gmii`), so the TI speed routine is forced to the SGMII variant. * **PCS/PMA speed path** — when the Xilinx PCS/PMA core is the managed "PHY", any core that is not in 1000BASE-X mode is treated as SGMII. ### Modifying the standalone application Edit `Vitis/common/src/*.c` directly. The next `make -C Vitis bootfile TARGET=` rebuilds the application against the existing platform; if you've changed the hardware (XSA) you'll need a fresh workspace (`make -C Vitis clean TARGET=` first). ### Modifying BSP libraries or build hooks Adjust the corresponding entry in `Vitis/py/args.json`. Configuration changes propagate through the next `pre_platform_build` run. The two hook scripts in `py/` are repo-specific Python; edit them when the change is more elaborate than a `bsp_libs` config tweak. ## PetaLinux side ### BSP composition The PetaLinux project for a given target is composed at build time from two BSP fragments copied into the target's project directory: 1. A **board BSP** at `PetaLinux/bsp//` (for example `uzev/`, `zcu102/`, `vck190/`). Provides board-specific kernel and U-Boot configuration, the system device-tree fragment for the board, and any board-specific patches. 2. A **port-config overlay** at `PetaLinux/bsp//` (one of `ports-0123/` or `ports-01xx/`). Provides `port-config.dtsi` — the device-tree fragment that wires up the GEMs and PHYs active on this target. The mapping from target to (board BSP, port-config overlay) is encoded in `PetaLinux/Makefile`'s `UPDATER` block. The last column names the port-config overlay; the board BSP is derived from the first token of the target name. The two port-config variants are: * `ports-0123` — four-port ZynqMP designs. Enables `gem0`-`gem3` with `phy-mode = "gmii"`, declares the four DP83867 PHY nodes (addresses 1, 3, 12, 15) on `gem0`'s MDIO bus, and cross-references them from `gem1`-`gem3` via `phy-handle`. * `ports-01xx` — two-port Versal design (`vck190_fmcp1`). Same scheme for `gem0`/`gem1` and the port 0/1 PHYs only. Neither port-config declares the PL PCS/PMA cores in the device tree: this kernel's `macb` driver has no external-PCS support, so the cores are un-isolated at boot by the `pcs-unisolate` service instead (see *PCS/PMA ISOLATE bit and the `pcs-unisolate` service* above). The GEMs use `phy-mode = "gmii"` and manage the DP83867 PHYs directly, as in a direct-attach GMII design. ### Layout of a board BSP ``` PetaLinux/bsp//project-spec/ ├── configs/ │ ├── config <- petalinux-config: bootargs, rootfs, hostname │ ├── rootfs_config <- petalinux-config -c rootfs: included packages │ ├── init-ifupdown/ │ │ └── interfaces <- /etc/network/interfaces │ └── busybox/ │ └── inetd.conf └── meta-user/ ├── conf/ │ ├── user-rootfsconfig <- declares additional rootfs config options │ ├── petalinuxbsp.conf │ └── layer.conf ├── meta-xilinx-tools/ │ └── recipes-bsp/ │ └── uboot-device-tree/ <- U-Boot device-tree override ├── recipes-bsp/ │ ├── device-tree/ │ │ ├── device-tree.bbappend │ │ └── files/ │ │ └── system-user.dtsi <- board-specific DT additions │ └── u-boot/ │ ├── u-boot-xlnx_%.bbappend │ └── files/ │ ├── bsp.cfg <- U-Boot Kconfig additions │ ├── platform-top.h │ └── *.patch <- U-Boot source patches └── recipes-kernel/ └── linux/ ├── linux-xlnx_%.bbappend └── linux-xlnx/ └── bsp.cfg <- kernel Kconfig additions ``` ### Adding a package to the root filesystem 1. Append the new option to `bsp//project-spec/configs/rootfs_config`: ``` CONFIG_=y ``` 2. If the package is not in the default `petalinux-config -c rootfs` menu, also append a declaration line to `bsp//project-spec/meta-user/conf/user-rootfsconfig`. 3. If the package is not provided by an existing meta-layer, add a recipe under `bsp//project-spec/meta-user/recipes-apps//.bb`. ### Adding a kernel config option Append the option to `bsp//project-spec/meta-user/recipes-kernel/linux/linux-xlnx/bsp.cfg`: ``` CONFIG_=y ``` The corresponding bbappend at `recipes-kernel/linux/linux-xlnx_%.bbappend` registers `bsp.cfg` as a kernel configuration fragment. ### Adding a device-tree fragment For per-board fragments, edit `bsp//project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi`. For per-port-config fragments, edit the corresponding `bsp//project-spec/meta-user/recipes-bsp/device-tree/files/port-config.dtsi`. If you add new files, ensure they are listed in `SRC_URI:append` in `device-tree.bbappend`. ### Adding a kernel patch or out-of-tree driver 1. Drop the patch file into `bsp//project-spec/meta-user/recipes-kernel/linux/linux-xlnx/`. 2. Add a line to `recipes-kernel/linux/linux-xlnx_%.bbappend`: ``` SRC_URI:append = " file://.patch" ``` ### Modifying U-Boot The same pattern as the kernel, under `bsp//project-spec/meta-user/recipes-bsp/u-boot/`. `bsp.cfg` adds U-Boot Kconfig options; `platform-top.h` overrides the U-Boot platform header; patches are listed in `SRC_URI:append` in `u-boot-xlnx_%.bbappend`. ## Modifications layered on the stock BSPs The board BSPs in this repository started as the corresponding stock AMD reference BSPs and have been modified in the following ways. This list is the answer to *"what would I lose if I overwrote the BSP with the stock one?"* — it is what to re-apply if you ever do that. ### All BSPs * **Root filesystem additions** in `configs/rootfs_config`: `ethtool`, `iperf3`, `phytool`. * **Hostname / product name** set in `configs/config` via `CONFIG_SUBSYSTEM_HOSTNAME` and `CONFIG_SUBSYSTEM_PRODUCT` (e.g. `zcu106-psgem-sgmii-2025-2`). * **`system-user.dtsi`** includes `port-config.dtsi`. The matching `device-tree.bbappend` adds both files to `SRC_URI:append`. * **Kernel config fragment** in `linux-xlnx/bsp.cfg` (board-specific options needed by the design). * **`meta-xilinx-tools/recipes-bsp/uboot-device-tree/`** overlay that overrides the U-Boot device tree, so U-Boot's view of the Ethernet ports matches this design (the GEMs are on EMIO, not the onboard MIO ports). * **SD-card root filesystem** configured in `configs/config`: `CONFIG_SUBSYSTEM_ROOTFS_EXT4`, `CONFIG_SUBSYSTEM_SDROOT_DEV`, `CONFIG_SUBSYSTEM_USER_CMDLINE`. * **U-Boot distro-boot patch** (`0001-ubifs-distroboot-support.patch` on ZynqMP boards, `0001-xilinx_versal.h-ubifs-distroboot-support.patch` on the VCK190) under `recipes-bsp/u-boot/files/`. ### UltraZed-EV (uzev) BSP * **`CONFIG_YOCTO_MACHINE_NAME="zynqmp-generic"`** in `configs/config` (the UZ-EV is not a stock Xilinx eval board). * **SD-card device set to `/dev/mmcblk1p2`** rather than the ZynqMP default `mmcblk0p2`. * **`PRIMARY_SD_PSU_SD_1_SELECT=y`** to route the boot SD interface through PSU SD1 instead of SD0. * **Custom `system-user.dtsi`** with UZ-EV-specific peripheral configuration (overwrites the file copied in from a stock UZ-EV BSP). ### Port-config overlays The two overlays in `PetaLinux/bsp/ports-*/` are not derived from any stock BSP — they exist solely to add the device-tree fragment that wires up the GEMs and PHYs. Each contains a single `port-config.dtsi` (the surrounding directory structure is needed so that Yocto picks it up via the `SRC_URI:append = " file://port-config.dtsi"` line in `device-tree.bbappend`). ## Where build outputs land | Path | Contents | |-------------------------------------|--------------------------------------------------------------------------------| | `Vivado//` | Vivado project. `_wrapper.xsa` is the export. | | `Vivado//.runs/impl_1/_wrapper.bit` | Bitstream. | | `Vivado/logs/` | Per-target Vivado build logs (xpr + xsa). | | `Vitis/_workspace/` | Per-target Vitis workspace (platform + application + BSP). | | `Vitis/boot//` | Packaged Vitis boot files (`BOOT.BIN`). | | `PetaLinux//` | PetaLinux project. All Yocto build state lives here. | | `PetaLinux//images/linux/` | `BOOT.BIN`, `image.ub`, `boot.scr`, `rootfs.tar.gz`, etc. | | `PetaLinux//build/build.log`| PetaLinux build log. | | `bootimages/` | Per-target zipped boot files (`__petalinux-.zip` and `__standalone-.zip`). | None of these directories are committed to the repository.