diff options
Diffstat (limited to 'meta-xilinx-core/classes')
-rw-r--r-- | meta-xilinx-core/classes/amd_spi_image.bbclass | 142 | ||||
-rw-r--r-- | meta-xilinx-core/classes/dfx_user_dts.bbclass | 267 | ||||
-rw-r--r-- | meta-xilinx-core/classes/fpgamanager_custom.bbclass | 24 | ||||
-rw-r--r-- | meta-xilinx-core/classes/gen-machine-conf.bbclass | 6 | ||||
-rw-r--r-- | meta-xilinx-core/classes/image-types-xilinx-qemu.bbclass | 59 | ||||
-rw-r--r-- | meta-xilinx-core/classes/image-wic-utils.bbclass | 54 | ||||
-rw-r--r-- | meta-xilinx-core/classes/kernel-simpleimage.bbclass | 35 | ||||
-rw-r--r-- | meta-xilinx-core/classes/qemuboot-xilinx.bbclass | 140 | ||||
-rw-r--r-- | meta-xilinx-core/classes/xilinx-deprecated.bbclass | 18 | ||||
-rw-r--r-- | meta-xilinx-core/classes/xilinx-fetch-restricted.bbclass | 35 | ||||
-rw-r--r-- | meta-xilinx-core/classes/xilinx-microblaze.bbclass | 11 | ||||
-rw-r--r-- | meta-xilinx-core/classes/xilinx-platform-init.bbclass | 14 | ||||
-rw-r--r-- | meta-xilinx-core/classes/xilinx-testimage.bbclass | 10 | ||||
-rw-r--r-- | meta-xilinx-core/classes/xilinx-vars.bbclass | 32 |
14 files changed, 847 insertions, 0 deletions
diff --git a/meta-xilinx-core/classes/amd_spi_image.bbclass b/meta-xilinx-core/classes/amd_spi_image.bbclass new file mode 100644 index 00000000..ed4c1f87 --- /dev/null +++ b/meta-xilinx-core/classes/amd_spi_image.bbclass @@ -0,0 +1,142 @@ +# +# Copyright (C) 2023, Advanced Micro Devices, Inc. All rights reserved. +# +# SPDX-License-Identifier: MIT +# + +QSPI_SIZE ?= "0x2280000" + +# Register values +IDN_REG ?= "0x4D554241" +VERSION_REG ?= "0x1" +LENGTH_REG ?= "0x4" +PERSISTENT_REG ?= "0x01010000" + +# QSPI Offsets +IMAGE_SELECTOR_OFFSET ?= "0x0" +IMAGE_SELECTOR_BACKUP_OFFSET ?= "0x80000" +PERSISTENT_REG_OFFSET ?= "0x100000" +PERSISTENT_REG_BACKUP_OFFSET ?= "0x120000" +IMAGE_A_OFFSET ?= "0x200000" +IMAGE_A_IMGSEL_OFFSET ?= "0xF00000" +IMAGE_B_OFFSET ?= "0xF80000" +IMAGE_B_IMGSEL_OFFSET ?= "0x1C80000" +IMAGE_RCVRY_OFFSET ?= "0x1E00000" +IMAGE_RCVRY_BACKUP_OFFSET ?= "0x2000000" +VERSION_OFFSET ?= "0x2240000" +CHECKSUM_OFFSET ?= "0x2250000" + +def generate_spi_image(d): + + import io + import hashlib + import time + + qspi_size = int(d.getVar("QSPI_SIZE") or '0', 0) + int(d.getVar("QSPI_SIZE") or '0', 0) + + # Register values + idn_reg = int(d.getVar("IDN_REG") or '0', 0) + version_reg = int(d.getVar("VERSION_REG") or '0', 0) + length_reg = int(d.getVar("LENGTH_REG") or '0', 0) + persistent_reg = int(d.getVar("PERSISTENT_REG") or '0', 0) + + # QSPI Offsets + image_selector_offset = int(d.getVar("IMAGE_SELECTOR_OFFSET") or '0', 0) + image_selector_backup_offset = int(d.getVar("IMAGE_SELECTOR_BACKUP_OFFSET") or '0', 0) + persistent_reg_offset = int(d.getVar("PERSISTENT_REG_OFFSET") or '0', 0) + persistent_reg_backup_offset = int(d.getVar("PERSISTENT_REG_BACKUP_OFFSET") or '0', 0) + image_a_offset = int(d.getVar("IMAGE_A_OFFSET") or '0', 0) + image_a_imgsel_offset = int(d.getVar("IMAGE_A_IMGSEL_OFFSET") or '0', 0) + image_b_offset = int(d.getVar("IMAGE_B_OFFSET") or '0', 0) + image_b_imgsel_offset = int(d.getVar("IMAGE_B_IMGSEL_OFFSET") or '0', 0) + image_rcvry_offset = int(d.getVar("IMAGE_RCVRY_OFFSET") or '0', 0) + image_rcvry_backup_offset = int(d.getVar("IMAGE_RCVRY_BACKUP_OFFSET") or '0', 0) + version_offset = int(d.getVar("VERSION_OFFSET") or '0', 0) + checksum_offset = int(d.getVar("CHECKSUM_OFFSET") or '0', 0) + + # QSPI data + qspi_data = io.BytesIO() + qspi_data.write(b'\xFF' * qspi_size) + + # Image Selector - Primary, Backup, Image A and Image B + imgsel_file = d.getVar("DEPLOY_DIR_IMAGE")+"/imgsel-"+d.getVar("MACHINE")+".bin" + try: + with open(imgsel_file, "rb") as il: + imgsel = il.read(-1) + except OSError as err: + bb.fatal("Unable to open imgsel file: " + str(err)) + + qspi_data.seek(image_selector_offset) + qspi_data.write(imgsel) + qspi_data.seek(image_selector_backup_offset) + qspi_data.write(imgsel) + qspi_data.seek(image_a_imgsel_offset) + qspi_data.write(imgsel) + qspi_data.seek(image_b_imgsel_offset) + qspi_data.write(imgsel) + + # Persistent Registers - Primary and Backup + p_reg = [idn_reg, version_reg, length_reg, persistent_reg, \ + image_a_offset, image_b_offset, image_rcvry_offset] + checksum = 0xffffffff - (0xffffffff & sum(p_reg)) + p_reg.insert(3, checksum) + + qspi_data.seek(persistent_reg_offset) + for value in p_reg: + qspi_data.write(value.to_bytes(4, byteorder="little")) + + qspi_data.seek(persistent_reg_backup_offset) + for value in p_reg: + qspi_data.write(value.to_bytes(4, byteorder="little")) + + # Image A and B - boot.bin + try: + with open(d.getVar("DEPLOY_DIR_IMAGE")+"/boot.bin", "rb") as bo: + bootbin = bo.read(-1) + except OSError as err: + bb.fatal("Unable to open boot.bin file: " + str(err)) + + qspi_data.seek(image_a_offset) + qspi_data.write(bootbin) + qspi_data.seek(image_b_offset) + qspi_data.write(bootbin) + + # Recovery Image & Recovery Image Backup + imgrcry_file = d.getVar("DEPLOY_DIR_IMAGE")+"/imgrcry-"+d.getVar("MACHINE")+".bin" + try: + with open(imgrcry_file, "rb") as iy: + imgrcry = iy.read(-1) + except OSError as err: + bb.fatal("Unable to open imgrcry file: " + str(err)) + + qspi_data.seek(image_rcvry_offset) + qspi_data.write(imgrcry) + qspi_data.seek(image_rcvry_backup_offset) + qspi_data.write(imgrcry) + + # Version string and checksum + version = d.getVar("QSPI_IMAGE_VERSION") + date = time.strftime("%m%d%H%M") + machine = d.getVar("MACHINE")[:3] + image_name = d.getVar("QSPI_IMAGE_NAME") + + qspi_version = f"{image_name}-{machine}-v{version}-{date}\x00" + qspi_data.seek(version_offset) + qspi_data.write(qspi_version.encode()) + + qspi_sha = hashlib.sha256(qspi_data.getbuffer()) + qspi_data.seek(checksum_offset) + qspi_data.write(qspi_sha.digest()) + + # Write the QSPI data to file + with open(d.getVar("B") + "/" + d.getVar("IMAGE_NAME") + ".bin", "wb") as sq: + sq.write(qspi_data.getbuffer()) + +do_compile[depends] += "virtual/boot-bin:do_deploy virtual/imgsel:do_deploy virtual/imgrcry:do_deploy" + +python amd_spi_image_do_compile() { + generate_spi_image(d) +} + +EXPORT_FUNCTIONS do_compile diff --git a/meta-xilinx-core/classes/dfx_user_dts.bbclass b/meta-xilinx-core/classes/dfx_user_dts.bbclass new file mode 100644 index 00000000..4404aa05 --- /dev/null +++ b/meta-xilinx-core/classes/dfx_user_dts.bbclass @@ -0,0 +1,267 @@ +# This bbclass is inherited by flat, DFx Static and DFx RP firmware recipes. +# dfx_user_dts.bbclass expects user to generate pl dtsi for flat, DFx Static +# and DFx RP xsa outside of yocto. + +inherit devicetree + +DEPENDS = "dtc-native bootgen-native" + +# recipes that inherit from this class need to use an appropriate machine +# override for COMPATIBLE_MACHINE to build successfully; don't allow building +# for microblaze MACHINE +COMPATIBLE_MACHINE ?= "^$" +COMPATIBLE_MACHINE:microblaze = "^$" + +PACKAGE_ARCH = "${MACHINE_ARCH}" + +PROVIDES = "" + +do_fetch[cleandirs] = "${B}" + +DT_PADDING_SIZE = "0x1000" +BOOTGEN_FLAGS ?= " -arch ${SOC_FAMILY} -w ${@bb.utils.contains('SOC_FAMILY','zynqmp','','-process_bitstream bin',d)}" + +S ?= "${WORKDIR}" +FW_DIR ?= "" +DTSI_PATH ?= "" +DTBO_PATH ?= "" +DT_FILES_PATH = "${S}/${DTSI_PATH}" +FIRMWARE_NAME_DT_FILE ?= "" +USER_DTS_FILE ?= "" + +FIRMWARE_NAME_DT_FILE[doc] = "DT file which has firmware-name device-tree property" +USER_DTS_FILE[doc] = "Final DTSI or DTS file which is used for packaging final DT overlay" + +python() { + import re + soc_family = d.getVar("SOC_FAMILY") + if "git://" in d.getVar("SRC_URI") or "https://" in d.getVar("SRC_URI"): + d.setVar("S",'${WORKDIR}/git/'+d.getVar("FW_DIR")) + else: + dtsi_found = False + dtbo_found = False + bit_found = False + bin_found = False + pdi_found = False + + # Required Inputs + if '.dtsi' in d.getVar("SRC_URI") or '.dts' in d.getVar("SRC_URI"): + dtsi_found = True + d.setVar("DTSI_PATH",os.path.dirname([a for a in d.getVar('SRC_URI').split() if '.dtsi' in a or '.dts' in a][0].lstrip('file://'))) + + if '.dtbo' in d.getVar("SRC_URI"): + dtbo_found = True + d.setVar("DTBO_PATH",os.path.dirname([a for a in d.getVar('SRC_URI').split() if '.dtbo' in a][0].lstrip('file://'))) + + if '.bit' in d.getVar("SRC_URI") and soc_family != "versal": + bit_found = True + d.setVar("BIT_PATH",os.path.dirname([a for a in d.getVar('SRC_URI').split() if '.bit' in a][0].lstrip('file://'))) + + if '.bin' in d.getVar("SRC_URI") and soc_family != "versal": + bin_found = True + d.setVar("BIT_PATH",os.path.dirname([a for a in d.getVar('SRC_URI').split() if '.bin' in a][0].lstrip('file://'))) + + if '.pdi' in d.getVar("SRC_URI") and soc_family == "versal": + pdi_found = True + d.setVar("PDI_PATH",os.path.dirname([a for a in d.getVar('SRC_URI').split() if '.pdi' in a][0].lstrip('file://'))) + + # Check for valid combination of input files in SRC_URI + if dtsi_found or dtbo_found: + bb.debug(2, "dtsi or dtbo found in SRC_URI") + if bit_found or pdi_found or bin_found: + bb.debug(2, "bitstream or pdi found in SRC_URI") + elif bit_found and bin_found: + raise bb.parse.SkipRecipe("Both '.bit' and '.bin' file found in SRC_URI, either .bit or .bin file is supported but not both.") + else: + raise bb.parse.SkipRecipe("Need one '.bit' or one '.pdi' file added to SRC_URI ") + else: + raise bb.parse.SkipRecipe("Need one '.dtsi' or one '.dtbo' file added to SRC_URI ") + + # Check for valid combination of dtsi and dts files in SRC_URI + # Following file combinations are not supported use case. + # 1. More than one '.dtsi' and zero '.dts' file. + # 2. More than one '.dts' and zero or more than one '.dtsi'file . + pattern_dts = re.compile(r'[.]+dts\b') + pattern_dtsi = re.compile(r'[.]+dtsi\b') + dts_count = len([*re.finditer(pattern_dts, d.getVar('SRC_URI'))]) + dtsi_count = len([*re.finditer(pattern_dtsi, d.getVar("SRC_URI"))]) + + if dtsi_count > 1 and dts_count == 0: + raise bb.parse.SkipRecipe("Recipe has more than one '.dtsi' and zero '.dts' found, this is an unsupported use case") + elif dts_count > 1: + raise bb.parse.SkipRecipe("Recipe has more than one '.dts' and zero or more than one '.dtsi' found, this is an unsupported use case") + + # Optional input + if '.json' in d.getVar("SRC_URI"): + d.setVar("JSON_PATH",os.path.dirname([a for a in d.getVar('SRC_URI').split() if '.json' in a][0].lstrip('file://'))) + + if '.xclbin' in d.getVar("SRC_URI"): + d.setVar("XCL_PATH",os.path.dirname([a for a in d.getVar('SRC_URI').split() if '.xclbin' in a][0].lstrip('file://'))) +} + +# Function to get dts or dtsi file count. +def get_dt_count(d, dt_ext): + import glob + dt_count = sum(1 for f in glob.iglob((d.getVar('S') + (d.getVar('DTSI_PATH')) + '/*.' + dt_ext),recursive=True) if os.path.isfile(f)) + return dt_count + +# Function to search for dt firmware-name property in dts or dtsi file. +python find_firmware_file() { + import glob + pattern_fw = 'firmware-name' + search_count = 0 + for dt_files in glob.iglob((d.getVar('S') + (d.getVar('DTSI_PATH')) + '/*.dts*'),recursive=True): + with open(dt_files, "r") as f: + current_fd = f.read() + if pattern_fw in current_fd: + search_count += 1 + if search_count > 1: + bb.error("firmware-name dt property found in more than one dt files! Please fix the dts or dtsi file.") + break + else: + d.setVar('FIRMWARE_NAME_DT_FILE', os.path.basename(dt_files)) +} + +do_configure[prefuncs] += "find_firmware_file" + +python do_configure() { + import glob, re, shutil + soc_family = d.getVar("SOC_FAMILY") + + if bb.utils.contains('MACHINE_FEATURES', 'fpga-overlay', False, True, d): + bb.warn("Using fpga-manager.bbclass requires fpga-overlay MACHINE_FEATURE to be enabled") + + # Renaming firmware-name using $PN as bitstream/PDI will be renamed using + # $PN when generating the bin/pdi file. + if os.path.isfile(d.getVar('S') + (d.getVar('DTSI_PATH') or '') + '/' + d.getVar('FIRMWARE_NAME_DT_FILE')): + orig_dtsi = glob.glob(d.getVar('S')+ (d.getVar('DTSI_PATH') or '') + '/' + d.getVar('FIRMWARE_NAME_DT_FILE'))[0] + new_dtsi = d.getVar('S') + '/pl.dtsi_firmwarename' + with open(new_dtsi, 'w') as newdtsi: + with open(orig_dtsi) as olddtsi: + for line in olddtsi: + if soc_family == 'versal': + newdtsi.write(re.sub('firmware-name.*\".*\"','firmware-name = \"'+d.getVar('PN')+'.pdi\"',line)) + else: + newdtsi.write(re.sub('firmware-name.*\".*\"','firmware-name = \"'+d.getVar('PN')+'.bit.bin\"',line)) + shutil.move(new_dtsi,orig_dtsi) +} + +do_compile[prefuncs] += "find_firmware_file" + +python devicetree_do_compile:append() { + import glob, subprocess, shutil + soc_family = d.getVar("SOC_FAMILY") + + dtbo_count = sum(1 for f in glob.iglob((d.getVar('S') + '/*.dtbo'),recursive=True) if os.path.isfile(f)) + + # Skip devicetree do_compile task if input file is dtbo in SRC_URI + if not dtbo_count: + # Convert .bit to bit.bin format only if dtsi is input. + # In case of dtbo as input, bbclass doesn't know if firmware-name is .bit or + # .bit.bin format and corresponding file name. Hence we are not doing + # bit.bin conversion. + if soc_family != 'versal' and glob.glob(d.getVar('S') + '/' + d.getVar('FIRMWARE_NAME_DT_FILE')): + pn = d.getVar('PN') + biffile = pn + '.bif' + + with open(biffile, 'w') as f: + f.write('all:\n{\n\t' + glob.glob(d.getVar('S')+(d.getVar('BIT_PATH') or '') + '/*.bit')[0] + '\n}') + + bootgenargs = ["bootgen"] + (d.getVar("BOOTGEN_FLAGS") or "").split() + bootgenargs += ["-image", biffile, "-o", pn + ".bit.bin"] + subprocess.run(bootgenargs, check = True) + + # In Zynq7k using both "-process_bitstream bin" and "-o" in bootgen flag, + # to convert bit file to bin format, "-o" option will not be effective + # and generated output file name is ${S}+${BIT_PATH}/<bit_file_name>.bit.bin + # file, Hence we need to rename this file from <bit_file_name>.bit.bin to + # ${PN}.bit.bin which matches the firmware name in dtbo and move + # ${PN}.bit.bin to ${B} directory. + if soc_family == 'zynq': + src_bitbin_file = glob.glob(d.getVar('S') + (d.getVar('BIT_PATH') or '') + '/*.bit.bin')[0] + dst_bitbin_file = d.getVar('B') + '/' + pn + '.bit.bin' + shutil.move(src_bitbin_file, dst_bitbin_file) + + if not os.path.isfile(pn + ".bit.bin"): + bb.fatal("Couldn't find %s file, Enable '-log trace' in BOOTGEN_FLAGS" \ + "and check bootgen_log.txt" % (d.getVar('B') + '/' + pn + '.bit.bin')) +} + +# If user inputs both dtsi and dts files then device-tree will generate dtbo +# files for each dt file, Hence to package the firmware pick the right user dt +# overlay file. +python find_user_dts_overlay_file() { + import glob + dtbo_count = sum(1 for f in glob.iglob((d.getVar('S') + '/*.dtbo'),recursive=True) if os.path.isfile(f)) + # Skip if input file is dtbo in SRC_URI + if not dtbo_count: + dts_count = get_dt_count(d, 'dts') + dtsi_count = get_dt_count(d, 'dtsi') + if dtsi_count == 1 and dts_count == 0: + dts_file =glob.glob(d.getVar('S')+ (d.getVar('DTSI_PATH') or '') + '/*.dtsi')[0] + elif dtsi_count >=0 and dts_count == 1: + dts_file = glob.glob(d.getVar('S')+ (d.getVar('DTSI_PATH') or '') + '/*.dts')[0] + + d.setVar('USER_DTS_FILE', os.path.splitext(os.path.basename(dts_file))[0]) +} + +do_install[prefuncs] += "find_user_dts_overlay_file" + +do_install() { + install -d ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/ + + # In case of dtbo as input, dtbo will be copied from directly from ${S} + # In case of dtsi as input, dtbo will be copied from directly from ${B} + if [ `ls ${S}/*.dtbo | wc -l` -eq 1 ]; then + install -Dm 0644 ${S}/*.dtbo ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/ + elif [ `ls ${S}/*.dtbo | wc -l` -gt 1 ]; then + bbfatal "Multiple DTBO found, use the right DTBO in SRC_URI from the following:\n$(basename -a ${S}/*.dtbo)" + elif [ -f ${B}/${USER_DTS_FILE}.dtbo ]; then + install -Dm 0644 ${B}/${USER_DTS_FILE}.dtbo ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/${PN}.dtbo + else + bbfatal "A dtbo ending '.dtbo' expected but not found" + fi + + if [ "${SOC_FAMILY}" == "versal" ]; then + # In case of dtbo as input, pdi will be copied from directly from ${S} + # without renaming the pdi name to ${PN}.pdi + if [ `ls ${S}/*.pdi | wc -l` -eq 1 ] && [ `ls ${S}/*.dtbo | wc -l` -eq 1 ]; then + install -Dm 0644 ${S}/*.pdi ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/ + elif [ `ls ${S}/*.pdi | wc -l` -gt 1 ]; then + bbfatal "Multiple PDI found, use the right PDI in SRC_URI from the following:\n$(basename -a ${S}/*.pdi)" + elif [ `ls ${S}/*.pdi | wc -l` -eq 1 ] && [ -f ${B}/${USER_DTS_FILE}.dtbo ]; then + install -Dm 0644 ${S}/*.pdi ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/${PN}.pdi + else + bbfatal "A PDI file with '.pdi' expected but not found" + fi + else + # In case of dtbo as input, .bit or .bin will be copied from directly + # from ${S} without renaming the .bit/.bin name to ${PN}.bit/${PN}.bin + # if more than one .bit/.bin file is found then fail the task. + if [ `ls ${S}/*.bit | wc -l` -gt 1 ]; then + bbfatal "Multiple .bit found, use the right .bit in SRC_URI from the following:\n$(basename -a ${S}/*.bit)" + elif [ `ls ${S}/*.bin | wc -l` -gt 1 ]; then + bbfatal "Multiple .bin found, use the right .bin in SRC_URI from the following:\n$(basename -a ${S}/*.bin)" + elif [ `ls ${S}/*.bit | wc -l` -eq 1 ] && [ `ls ${S}/*.dtbo | wc -l` -eq 1 ]; then + install -Dm 0644 ${S}/*.bit ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/ + elif [ `ls ${S}/*.bin | wc -l` -eq 1 ] && [ `ls ${S}/*.dtbo | wc -l` -eq 1 ]; then + install -Dm 0644 ${S}/*.bin ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/ + elif [ -f ${B}/${PN}.bit.bin ] && [ -f ${B}/${USER_DTS_FILE}.dtbo ]; then + install -Dm 0644 ${B}/${PN}.bit.bin ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/${PN}.bit.bin + else + bbfatal "A bitstream file with '.bit' or '.bin' expected but not found" + fi + fi + + if ls ${S}/${XCL_PATH}/*.xclbin >/dev/null 2>&1; then + install -Dm 0644 ${S}/${XCL_PATH}/*.xclbin ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/${PN}.xclbin + fi + + if [ -f ${S}/${JSON_PATH}/shell.json ] || [ -f ${S}/${JSON_PATH}/accel.json ]; then + install -Dm 0644 ${S}/${JSON_PATH}/*.json ${D}/${nonarch_base_libdir}/firmware/xilinx/${PN}/ + fi +} + +do_deploy[noexec] = "1" + +FILES:${PN} += "${nonarch_base_libdir}/firmware/xilinx/${PN}" diff --git a/meta-xilinx-core/classes/fpgamanager_custom.bbclass b/meta-xilinx-core/classes/fpgamanager_custom.bbclass new file mode 100644 index 00000000..8c8997a1 --- /dev/null +++ b/meta-xilinx-core/classes/fpgamanager_custom.bbclass @@ -0,0 +1,24 @@ +# This class inherits dfx_user_dts.bbclass for below use cases. +# Zynq-7000 and ZynqMP: Full bitstream loading. +# ZynqMP: DFx Static and Partial bitstream loading. +# Versal: DFx Static and Parial pdi loading. +# Versal: Full PDI loading. + +inherit dfx_user_dts + +python fpgamanager_warn_msg () { + if not d.getVar("FPGAMANAGER_NO_WARN"): + arch = d.getVar('SOC_FAMILY') + pn = d.getVar('PN') + warn_msg = 'Users should start using dfx_user_dts bbclass for ' + if arch == 'zynq': + warn_msg += 'Zynq-7000 Full bitstream loading use case.' + elif arch == 'zynqmp': + warn_msg += 'ZynqMP Full or DFx Static or DFx Partial bitstream loading use case.' + elif arch == 'versal': + warn_msg += 'Versal DFx Static or DFx Partial or Full PDI loading use case.' + + bb.warn("Recipe %s has inherited fpgamanager_custom bbclass which will be deprecated in 2024.1 release. \n%s" % (pn, warn_msg)) +} + +do_install[postfuncs] += "fpgamanager_warn_msg"
\ No newline at end of file diff --git a/meta-xilinx-core/classes/gen-machine-conf.bbclass b/meta-xilinx-core/classes/gen-machine-conf.bbclass new file mode 100644 index 00000000..25960cd1 --- /dev/null +++ b/meta-xilinx-core/classes/gen-machine-conf.bbclass @@ -0,0 +1,6 @@ +#Add scripts path with the tools to PATH to be able to use from eSDK +sdk_ext_postinst:append() { + if [ -d $target_sdk_dir/layers/meta-xilinx/meta-xilinx-core/gen-machine-conf ]; then + echo "export PATH=$target_sdk_dir/layers/meta-xilinx/meta-xilinx-core/gen-machine-conf:\$PATH" >> $env_setup_script + fi +} diff --git a/meta-xilinx-core/classes/image-types-xilinx-qemu.bbclass b/meta-xilinx-core/classes/image-types-xilinx-qemu.bbclass new file mode 100644 index 00000000..b4de5ee1 --- /dev/null +++ b/meta-xilinx-core/classes/image-types-xilinx-qemu.bbclass @@ -0,0 +1,59 @@ +# Define the 'qemu-sd' conversion type +# +# This conversion type pads any image to the 512K boundary to ensure that the +# image file can be used directly with QEMU's SD emulation which requires the +# block device to match that of valid SD card sizes (which are multiples of +# 512K). + +CONVERSIONTYPES:append = " qemu-sd qemu-sd-fatimg" +CONVERSION_CMD:qemu-sd () { + cp ${IMGDEPLOYDIR}/${IMAGE_NAME}.${type} ${IMGDEPLOYDIR}/${IMAGE_NAME}.${type}.qemu-sd + # Get the wic.qemu-sd file size + file_size=`stat -c '%s' ${IMGDEPLOYDIR}/${IMAGE_NAME}.${type}.qemu-sd` + powerof2=1 + file_size=${file_size%.*} + # Get the next power of 2 value for the image size value + while [ ${powerof2} -lt ${file_size} ]; do + powerof2=$(expr $powerof2 \* 2) + done + # Resize the image using qemu-img + qemu-img resize -f raw ${IMGDEPLOYDIR}/${IMAGE_NAME}.${type}.qemu-sd ${powerof2} +} + +BOOT_VOLUME_ID ?= "BOOT" +BOOT_SPACE ?= "1047552" +IMAGE_ALIGNMENT ?= "1024" + +# Create SD image in case of INITRAMFS_IMAGE set due to circular dependencies. +# This creates FAT partitioned SD image containing boot.bin,boot.scr and rootfs.cpio.gz.u-boot files. +# This is a workaround fix until we fix the circular dependencies +# Usage: IMAGE_FSTYPES:append = " cpio.gz.u-boot.qemu-sd-fatimg" +CONVERSION_CMD:qemu-sd-fatimg () { + QEMU_IMG="${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type}.qemu-sd-fatimg" + BOOT_SPACE_ALIGNED=$(expr ${BOOT_SPACE} + ${IMAGE_ALIGNMENT} - 1) + BOOT_SPACE_ALIGNED=$(expr ${BOOT_SPACE_ALIGNED} - ${BOOT_SPACE_ALIGNED} % ${IMAGE_ALIGNMENT}) + QEMUIMG_SIZE=$(expr ${IMAGE_ALIGNMENT} + ${BOOT_SPACE_ALIGNED}) + dd if=/dev/zero of=${QEMU_IMG} bs=1024 count=0 seek=${QEMUIMG_SIZE} + parted -s ${QEMU_IMG} mklabel msdos + parted -s ${QEMU_IMG} unit KiB mkpart primary fat32 ${IMAGE_ALIGNMENT} $(expr ${BOOT_SPACE_ALIGNED} \+ ${IMAGE_ALIGNMENT} \- 1) + parted -s ${QEMU_IMG} set 1 boot on + parted ${QEMU_IMG} print + BOOT_BLOCKS=$(LC_ALL=C parted -s ${QEMU_IMG} unit b print | awk '/ 1 / { print substr($4, 1, length($4 -1)) / 512 /2 }') + rm -f ${WORKDIR}/${BOOT_VOLUME_ID}.img + mkfs.vfat -n "${BOOT_VOLUME_ID}" -S 512 -C ${WORKDIR}/${BOOT_VOLUME_ID}.img $BOOT_BLOCKS + if [ -e ${DEPLOY_DIR_IMAGE}/boot.bin ]; then + mcopy -i ${WORKDIR}/${BOOT_VOLUME_ID}.img -s ${DEPLOY_DIR_IMAGE}/boot.bin ::/ + fi + if [ -e ${DEPLOY_DIR_IMAGE}/boot.scr ]; then + mcopy -i ${WORKDIR}/${BOOT_VOLUME_ID}.img -s ${DEPLOY_DIR_IMAGE}/boot.scr ::/ + fi + if [ ${INITRAMFS_IMAGE} = ${IMAGE_BASENAME} ] && [ x"${INITRAMFS_IMAGE_BUNDLE}" != "x1" ]; then + mcopy -i ${WORKDIR}/${BOOT_VOLUME_ID}.img -s ${IMAGE_NAME}${IMAGE_NAME_SUFFIX}.${type} ::rootfs.cpio.gz.u-boot + fi + dd if=${WORKDIR}/${BOOT_VOLUME_ID}.img of=${QEMU_IMG} conv=notrunc seek=1 bs=$(expr ${IMAGE_ALIGNMENT} \* 1024) +} + +CONVERSION_DEPENDS_qemu-sd = "qemu-system-native" +CONVERSION_DEPENDS_qemu-sd-fatimg = "mtools-native:do_populate_sysroot \ + dosfstools-native:do_populate_sysroot \ + parted-native:do_populate_sysroot" diff --git a/meta-xilinx-core/classes/image-wic-utils.bbclass b/meta-xilinx-core/classes/image-wic-utils.bbclass new file mode 100644 index 00000000..41ad8148 --- /dev/null +++ b/meta-xilinx-core/classes/image-wic-utils.bbclass @@ -0,0 +1,54 @@ +# Helper/utility functions to work with the IMAGE_BOOT_FILES variable and its +# expected behvaior with regards to the contents of the DEPLOY_DIR_IMAGE. +# +# The use of these functions assume that the deploy directory is populated with +# any dependent files/etc. Such that the recipe using these functions depends +# on the recipe that provides the files being used/queried. + +def boot_files_split_expand(d): + # IMAGE_BOOT_FILES has extra renaming info in the format '<source>;<target>' + for f in (d.getVar("IMAGE_BOOT_FILES") or "").split(" "): + parts = f.split(";", 1) + sources = [parts[0].strip()] + if "*" in parts[0]: + # has glob part + import glob + deployroot = d.getVar("DEPLOY_DIR_IMAGE") + sources = [] + for i in glob.glob(os.path.join(deployroot, parts[0])): + sources.append(os.path.basename(i)) + + # for all sources, yield an entry + for s in sources: + if len(parts) == 2: + if parts[1].endswith('/'): + yield s, '%s%s' % (parts[1].strip(), s) + else: + yield s, parts[1].strip() + yield s, s + +def boot_files_bitstream(d): + expectedfiles = [("bitstream", True)] + expectedexts = [(".bit", True), (".bin", False)] + # search for bitstream paths, use the renamed file. First matching is used + for source, target in boot_files_split_expand(d): + # skip boot.bin and u-boot.bin, it is not a bitstream + skip = ["boot.bin", "u-boot.bin"] + if source in skip or target in skip: + continue + + for e, t in expectedfiles: + if source == e or target == e: + return target, t + for e, t in expectedexts: + if source.endswith(e) or target.endswith(e): + return target, t + return "", False + +def boot_files_dtb_filepath(d): + dtbs = (d.getVar("IMAGE_BOOT_FILES") or "").split(" ") + for source, target in boot_files_split_expand(d): + if target.endswith(".dtb"): + return target + return "" + diff --git a/meta-xilinx-core/classes/kernel-simpleimage.bbclass b/meta-xilinx-core/classes/kernel-simpleimage.bbclass new file mode 100644 index 00000000..110ee254 --- /dev/null +++ b/meta-xilinx-core/classes/kernel-simpleimage.bbclass @@ -0,0 +1,35 @@ +python __anonymous () { + kerneltypes = set((d.getVar("KERNEL_IMAGETYPE") or "").split()) + kerneltypes |= set((d.getVar("KERNEL_IMAGETYPES") or "").split()) + if any(t.startswith("simpleImage.") for t in kerneltypes): + # Enable building of simpleImage + bb.build.addtask('do_prep_simpleimage', 'do_compile', 'do_configure', d) + uarch = d.getVar("UBOOT_ARCH") + if uarch == "microblaze": + d.appendVarFlag('do_prep_simpleimage', 'depends', ' virtual/dtb:do_populate_sysroot') +} + +do_prep_simpleimage[dirs] += "${B}" +do_prep_simpleimage () { + install -d ${B}/arch/${ARCH}/boot/dts + for type in ${KERNEL_IMAGETYPES} ; do + if [ -z "${type##*simpleImage*}" ] && [ ${ARCH} = "microblaze" ]; then + ext="${type##*.}" + # Microblaze simpleImage only works with dts file + cp ${RECIPE_SYSROOT}/boot/devicetree/${ext}.dts ${B}/arch/${ARCH}/boot/dts/ + fi + done +} + +do_deploy:append () { + for type in ${KERNEL_IMAGETYPES} ; do + if [ -z "${type##*simpleImage*}" ] && [ ${ARCH} = "microblaze" ]; then + base_name=${type}-${KERNEL_IMAGE_NAME} + install -m 0644 ${KERNEL_OUTPUT_DIR}/${type}.strip $deployDir/${base_name}.strip + install -m 0644 ${KERNEL_OUTPUT_DIR}/${type}.unstrip $deployDir/${base_name}.unstrip + symlink_name=${type}-${KERNEL_IMAGE_LINK_NAME} + ln -sf ${base_name}.strip $deployDir/${symlink_name}.strip + ln -sf ${base_name}.unstrip $deployDir/${symlink_name}.unstrip + fi + done +} diff --git a/meta-xilinx-core/classes/qemuboot-xilinx.bbclass b/meta-xilinx-core/classes/qemuboot-xilinx.bbclass new file mode 100644 index 00000000..7466ab5e --- /dev/null +++ b/meta-xilinx-core/classes/qemuboot-xilinx.bbclass @@ -0,0 +1,140 @@ + +# enable the overrides for the context of the conf only +OVERRIDES .= ":qemuboot-xilinx" + +# Default machine targets for Xilinx QEMU (FDT Generic) +# Allow QB_MACHINE to be overridden by a BSP config +QB_MACHINE ?= "${QB_MACHINE_XILINX}" +QB_RNG="" +QB_MACHINE_XILINX:aarch64 = "-machine arm-generic-fdt" +QB_MACHINE_XILINX:arm = "-M arm-generic-fdt-7series" +QB_MACHINE_XILINX:microblaze = "-M microblaze-fdt-plnx" + +QB_SYSTEM_NAME ?= "${@qemu_target_binary(d)}" +QB_DEFAULT_FSTYPE ?= "${@qemu_rootfs_params(d,'fstype')}" +QB_ROOTFS ?= "${@qemu_rootfs_params(d,'rootfs')}" +QB_ROOTFS_OPT ?= "${@qemu_rootfs_params(d,'rootfs-opt')}" +QB_DTB ?= "${@qemu_default_dtb(d)}" + +# defaults +QB_DEFAULT_KERNEL ?= "none" +QB_DEFAULT_KERNEL:zynq ?= "${@'zImage' if \ + d.getVar('INITRAMFS_IMAGE_BUNDLE') != '1' else 'zImage-initramfs-${MACHINE}.bin'}" +QB_DEFAULT_KERNEL:microblaze ?= "${@'simpleImage.mb' if \ + d.getVar('INITRAMFS_IMAGE_BUNDLE') != '1' else 'simpleImage.mb-initramfs-${MACHINE}.bin'}" + +inherit qemuboot + +def qemu_target_binary(data): + package_arch = data.getVar("PACKAGE_ARCH") + qemu_target_binary = (data.getVar("QEMU_TARGET_BINARY_%s" % package_arch) or "") + if qemu_target_binary: + return qemu_target_binary + + target_arch = data.getVar("TARGET_ARCH") + if target_arch == "microblazeeb": + target_arch = "microblaze" + elif target_arch == "aarch64": + target_arch += "-multiarch" + elif target_arch == "arm": + target_arch = "aarch64" + return "qemu-system-%s" % target_arch + +def qemu_add_extra_args(data): + initramfs_image = data.getVar('INITRAMFS_IMAGE') or "" + bundle_image = data.getVar('INITRAMFS_IMAGE_BUNDLE') or "" + deploy_dir = data.getVar('DEPLOY_DIR_IMAGE') or "" + machine_name = data.getVar('MACHINE') or "" + soc_family = data.getVar('SOC_FAMILY') or "" + qb_extra_args = '' + # Add kernel image and boot.scr to qemu boot command when initramfs_image supplied + kernel_name = '' + bootscr_image = '%s/boot.scr' % deploy_dir + if soc_family in ('zynqmp', 'versal'): + kernel_name = 'Image' + bootscr_loadaddr = '0x20000000' + if initramfs_image: + kernel_image = '%s/%s' % (deploy_dir, kernel_name) + if bundle_image == "1": + kernel_image = '%s/%s-initramfs-%s.bin' % (deploy_dir, kernel_name, machine_name) + kernel_loadaddr = '0x200000' + if kernel_name: + qb_extra_args = ' -device loader,file=%s,addr=%s,force-raw=on' % (kernel_image, kernel_loadaddr) + qb_extra_args += ' -device loader,file=%s,addr=%s,force-raw=on' % (bootscr_image, bootscr_loadaddr) + if soc_family == 'versal': + qb_extra_args += ' -boot mode=5' + else: + if soc_family in ('zynqmp', 'versal'): + qb_extra_args = ' -boot mode=5' + return qb_extra_args + +def qemu_rootfs_params(data, param): + initramfs_image = data.getVar('INITRAMFS_IMAGE') or "" + bundle_image = data.getVar('INITRAMFS_IMAGE_BUNDLE') or "" + soc_family = data.getVar('SOC_FAMILY') or "" + tune_features = (data.getVar('TUNE_FEATURES') or []).split() + if 'microblaze' in tune_features: + soc_family = 'microblaze' + soc_variant = data.getVar('SOC_VARIANT') or "" + + if param == 'rootfs': + return 'none' if bundle_image == "1" else '' + + elif param == 'fstype': + fstype_dict = { + "microblaze": "cpio.gz", + "zynq": "cpio.gz", + "zynqmp": "cpio.gz.u-boot", + "versal": "cpio.gz.u-boot.qemu-sd-fatimg" + } + if not initramfs_image: + image_fs = data.getVar('IMAGE_FSTYPES') + if 'wic.qemu-sd' in image_fs: + return 'wic.qemu-sd' + if soc_family not in fstype_dict: + return "" + return fstype_dict[soc_family] + + elif param == 'rootfs-opt': + sd_index = "1" + if soc_family == 'zynq': + sd_index = "0" + if soc_family == 'versal' and soc_variant == 'net': + sd_index = "0" + + # Device is using a disk + if not initramfs_image: + return ' -drive if=sd,index=%s,file=@ROOTFS@,format=raw' % (sd_index) + + # Device is using a ramdisk + if soc_family not in ('zynq', 'microblaze'): + return ' -device loader,file=@ROOTFS@,addr=0x04000000,force-raw=on' + + # Ramdisk must be compiled into the kernel + return '' + +def qemu_default_dtb(data): + if data.getVar("IMAGE_BOOT_FILES", True): + dtbs = data.getVar("IMAGE_BOOT_FILES", True).split(" ") + # IMAGE_BOOT_FILES has extra renaming info in the format '<source>;<target>' + # Note: Wildcard sources work here only because runqemu expands them at run time + dtbs = [f.split(";")[0] for f in dtbs] + dtbs = [f for f in dtbs if f.endswith(".dtb")] + if len(dtbs) != 0: + return dtbs[0] + return "" + +def qemu_default_serial(data): + if data.getVar("SERIAL_CONSOLES", True): + first_console = data.getVar("SERIAL_CONSOLES", True).split(" ")[0] + speed, console = first_console.split(";", 1) + # zynqmp uses earlycon and stdout (in dtb) + if "zynqmp" in data.getVar("MACHINEOVERRIDES", True).split(":"): + return "" + return "console=%s,%s earlyprintk" % (console, speed) + return "" + +def qemu_zynqmp_unhalt(data, multiarch): + if multiarch: + return "-global xlnx,zynqmp-boot.cpu-num=0 -global xlnx,zynqmp-boot.use-pmufw=true" + return "-device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4 -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4" diff --git a/meta-xilinx-core/classes/xilinx-deprecated.bbclass b/meta-xilinx-core/classes/xilinx-deprecated.bbclass new file mode 100644 index 00000000..1aee2fe1 --- /dev/null +++ b/meta-xilinx-core/classes/xilinx-deprecated.bbclass @@ -0,0 +1,18 @@ +# Class to add a deprecated warning from various configuration files. + +# Immediately after the ConfigParsed event handler, warn the user of any +# deprecated files the user has used. +addhandler xilinx_deprecated_config_eventhandler +xilinx_deprecated_config_eventhandler[eventmask] = "bb.event.ConfigParsed" +python xilinx_deprecated_config_eventhandler () { + # Check for BOARD & BOARD_VARIANT usage + if d.getVar('BOARD') or d.getVar('BOARD_VARIANT'): + bb.error("Deprecated BOARD (%s) or BOARD_VARIANT (%s) is being used, they are no longer supported and are ignored." % (d.getVar('BOARD'), d.getVar('BOARD_VARIANT'))) + + msg_list = d.getVarFlags('XILINX_DEPRECATED') or [] + for msg_source in msg_list: + if msg_source == "doc": + continue + msg = d.getVarFlag('XILINX_DEPRECATED', msg_source) or "" + bb.warn('%s: %s' % (msg_source, msg)) +} diff --git a/meta-xilinx-core/classes/xilinx-fetch-restricted.bbclass b/meta-xilinx-core/classes/xilinx-fetch-restricted.bbclass new file mode 100644 index 00000000..a778ec7d --- /dev/null +++ b/meta-xilinx-core/classes/xilinx-fetch-restricted.bbclass @@ -0,0 +1,35 @@ +# This class is setup to override the default fetching for the target recipe. +# When fetching it forces PREMIRROR only fetching so that no attempts are made +# to fetch the Xilinx downloads that are restricted to authenticated users only. +# +# The purpose of this class is to allow for automatation with pre-downloaded +# content or content that is available with curated/user defined pre-mirrors +# and or pre-populated downloads/ directories. + +python do_fetch() { + xilinx_restricted_url = "xilinx.com/member/forms/download" + + src_uri = (d.getVar('SRC_URI') or "").split() + if len(src_uri) == 0: + return + + for i in src_uri: + if xilinx_restricted_url in i: + # force the use of premirrors only, do not attempt download from xilinx.com + d.setVar("BB_FETCH_PREMIRRORONLY", "1") + break + + try: + fetcher = bb.fetch2.Fetch(src_uri, d) + fetcher.download() + except bb.fetch2.NetworkAccess as e: + if xilinx_restricted_url in e.url: + # fatal on access to xilinx.com restricted downloads, print the url for manual download + bb.fatal("The following download cannot be fetched automatically. " \ + "Please manually download the file and place it in the 'downloads' directory (or on an available PREMIRROR).\n" \ + " %s" % (e.url.split(";")[0])) + else: + bb.fatal(str(e)) + except bb.fetch2.BBFetchException as e: + bb.fatal(str(e)) +} diff --git a/meta-xilinx-core/classes/xilinx-microblaze.bbclass b/meta-xilinx-core/classes/xilinx-microblaze.bbclass new file mode 100644 index 00000000..ed231a3a --- /dev/null +++ b/meta-xilinx-core/classes/xilinx-microblaze.bbclass @@ -0,0 +1,11 @@ +# Class to add a deprecated warning from various configuration files. + +# Immediately after the ConfigParsed event handler, warn the user of any +# deprecated files the user has used. +addhandler xilinx_microblaze_config_eventhandler +xilinx_microblaze_config_eventhandler[eventmask] = "bb.event.ConfigParsed" +python xilinx_microblaze_config_eventhandler () { + if d.getVar('DEFAULTTUNE').startswith('microblaze'): + if 'xilinx-microblaze' not in d.getVar('BBFILE_COLLECTIONS').split(): + bb.fatal('You must include the meta-microblaze layer to build for this configuration.') +} diff --git a/meta-xilinx-core/classes/xilinx-platform-init.bbclass b/meta-xilinx-core/classes/xilinx-platform-init.bbclass new file mode 100644 index 00000000..99f7863a --- /dev/null +++ b/meta-xilinx-core/classes/xilinx-platform-init.bbclass @@ -0,0 +1,14 @@ +# This class should be included by any recipe that wants to access or provide +# the platform init source files which are used to initialize a Zynq or ZynqMP +# SoC. + +# Define the path to the xilinx platform init code/headers +PLATFORM_INIT_DIR ?= "/usr/src/xilinx-platform-init" + +PLATFORM_INIT_STAGE_DIR = "${STAGING_DIR_HOST}${PLATFORM_INIT_DIR}" + +# Target files use for platform init +PLATFORM_INIT_FILES ?= "" +PLATFORM_INIT_FILES:zynq = "ps7_init_gpl.c ps7_init_gpl.h" +PLATFORM_INIT_FILES:zynqmp = "psu_init_gpl.c psu_init_gpl.h" + diff --git a/meta-xilinx-core/classes/xilinx-testimage.bbclass b/meta-xilinx-core/classes/xilinx-testimage.bbclass new file mode 100644 index 00000000..74020da1 --- /dev/null +++ b/meta-xilinx-core/classes/xilinx-testimage.bbclass @@ -0,0 +1,10 @@ +inherit testimage + +HOSTTOOLS += 'ip ping ps scp ssh stty' + +python do_testimage:prepend () { + from oeqa.core.target.qemu import supported_fstypes + supported_fstypes.append('wic.qemu-sd') +} + +IMAGE_FSTYPES = "wic.qemu-sd" diff --git a/meta-xilinx-core/classes/xilinx-vars.bbclass b/meta-xilinx-core/classes/xilinx-vars.bbclass new file mode 100644 index 00000000..51d3cd87 --- /dev/null +++ b/meta-xilinx-core/classes/xilinx-vars.bbclass @@ -0,0 +1,32 @@ +# Check variable usage to make sure that the machine is probably configured +# properly. +addhandler xilinx_variables_config_eventhandler +xilinx_variables_config_eventhandler[eventmask] = "bb.event.ConfigParsed" + +# It's up to the base sytem to define the variables being used here, we're +# only going to check them. +python xilinx_variables_config_eventhandler () { + # Verify HDF_MACHINE + hdf_prior = d.getVar('HDF_MACHINE_PRIOR') + hdf_final = d.getVar('HDF_MACHINE') + + if hdf_prior and hdf_prior != hdf_final: + bb.fatal("HDF_MACHINE is set to %s, it appears you intended %s. " \ + "This is usually as a result of specifying it in the local.conf or before the 'require' in the machine .conf file. " \ + "See meta-xilinx-core/conf/machine/README." % (hdf_final, hdf_prior)) + + # Verify DEFAULTTUNE + tune_prior = d.getVar('DEFAULTTUNE_PRIOR') + tune_final = d.getVar('DEFAULTTUNE') + + if tune_prior and tune_prior != tune_final: + bb.fatal("The loaded DEFAULTTUNE is %s, but it appears you intended %s. " \ + "This is usually as a result of specifying it after the 'require' in the machine .conf file. " \ + "See meta-xilinx-core/conf/machine/README." % (tune_prior, tune_final)) + + # Verify 'xilinx' is in LICENSE_FLAGS_ACCEPTED + license_flags = d.getVar('LICENSE_FLAGS_ACCEPTED') or "" + if 'xilinx' not in license_flags.split(): + bb.warn("The ZynqMP pmu-rom is not enabled, qemu may not be able to emulate a ZynqMP system without it. " \ + "To enable this you must add 'xilinx' to the LICENSE_FLAGS_ACCEPTED to indicate you accept the software license.") +} |