Porting embeddedsw components to system device tree (SDT) based flow
- 1 Introduction
- 2 Assumptions and Prerequisites
- 3 What’s New in EmbeddedSW for this Flow
- 4 Updates in 2025.1
- 5 Updates in 2024.2
- 6 Updates in 2024.1
- 7 Porting to the System Device Tree based flow
- 7.1 Porting a driver
- 7.1.1 Changes in data folder
- 7.1.2 Changes in src Folder
- 7.1.3 Changes in examples Folder
- 7.2 Porting a Library
- 7.2.1 Changes in data Folder
- 7.2.2 Changes in src Folder
- 7.2.3 Changes in examples Folder
- 7.3 Porting a Template application:
- 7.3.1 Changes in data Folder
- 7.3.2 Changes in src Folder
- 7.1 Porting a driver
- 8 FAQs
- 9 Appendix A: System Device Tree (SDT) Generation using SDTGen
- 10 Appendix B: Usage of "compatible" under "properties"
- 11 Appendix C: Available options under "required" section
- 12 Appendix D: A Comprehensive sample YAML for drivers
- 13 Appendix E: A Driver example in the Peripheral Test App
- 14 Appendix F: CMAKE_MACHINE and CMAKE_SYSTEM_PROCESSOR
- 15 Appendix G: Apps: Hardware Dependency and Linker Script Generation
- 16 Known Gaps (2025.1)
- 17 Known Gaps (2024.2)
- 18 Known Gaps (2024.1)
- 19 Known Gaps (2023.2)
Introduction
The legacy methodology of building AMD embeddedsw components takes .xsa as a handoff file from hardware persona and uses mdd, mld and mss files for different software configurations. This makes the legacy approach dependent on AMD proprietary tools such as the software command-line tool (XSCT) and hardware software interface (HSI). Without these proprietary tools, the embeddedsw components can't be compiled with the legacy approach.
The proposed system device tree based flow aims to avoid such dependencies and embrace the open-source industry standards without compromising the performance and the functionality. It uses the system device tree, CMAKE and a Python-based open source tool named Lopper to organize the required Build System.
The system device tree is a super set of regular Linux device tree. Unlike regular Linux device tree which represents hardware information that is only needed for Linux/APU, system device tree represents complete hardware information in device tree format. It contains information about all processors (ex: PMC, PSM, RPU, APU) and all peripherals in the system. Details of steps to generate a system device tree can be found under Appendix A.
The System Device Tree based flow uses Lopper to extract the required hardware metadata from the system device tree. The software configurations earlier done via mld/mss files in the legacy approach is now replaced with the CMAKE-based infrastructure where the CMAKE variables are set to generate the required headers. All the component specific info which were kept earlier in mdd/mld/mss files in the legacy flow are now kept inside a YAML file. There are python scripts that read these YAML files, configures the component's sources using Lopper and CMAKE commands and populates the standalone BSP specific data accordingly. This way, the whole build system replaces the usage of proprietary AMD files and tools with the available open-source infrastructure.
Porting existing embeddedsw bare-metal and RTOS components is necessary to ensure legacy software components work seamlessly in this System Device Tree based flow. The following sections explain the steps and changes required to port a legacy bare-metal driver/library/application to the System Device Tree based flow. Also, please note, though the migration to this new flow has been done with the intent to avoid backward compatibility issues to the possible extent, there can still be certain cases where it needs some minor changes in user applications to work with this new flow a part from the changes mentioned in this porting guide.
Assumptions and Prerequisites
This document makes the following assumptions from the users:
Familiarity with AMD embeddedsw drivers and libraries.
Familiarity with CMAKE and yaml.
Python basics.
What’s New in EmbeddedSW for this Flow
The Open-Source System Device Tree specifications borrows heavily from Linux device tree specifications. The specifications do not have concepts of Device IDs to distinguish between similar peripherals.
As an example, in the legacy flow, if there are two Ethernet MACs in a system from the same vendor, each of the MACs would be assigned a separate Device ID. The Device IDs are positive integers starting from 0. The Driver Config structure always has a field "u16 DeviceId" that represents the assigned Device ID.
The System Device Tree based flow gets rid of Device IDs and hence Driver Config structures do not have an entry for Device ID. Instead, Driver Config structures have an entry "char *Name" that stores the Compatible String(s)" for the corresponding device. This Compatible property along with the base address of the device are used the uniquely identify a device in a system.
The System Device Tree based flow abstracts out interrupt handling through a wrapper. In the legacy flow the driver users had the responsibility to figure out the interrupt controller to be initialized (GIC or Soft AXI INTC). The new flow provides generic APIs that are to be called with the generated interrupt ID available in the Driver Config structures. The new flow introduces a new entry in the Driver Config structure with the name IntrParent that identifies whether the peripheral device is connected to GIC or AXI INTC for interrupt management For more details refer this section.
The System Device Tree based flow makes the xiltimer library inclusion mandatory. The xiltimer library abstracts out the timer and sleep handling in baremetal environments. A typical system can have multiple timer devices which can be used as timers or to implement sleep functionality. The xiltimer provides uniform implementation where the users do not have to worry about the underlying hardware timer. For more details of xiltimer, please refer to UG-643 Chapter 13.
Updates in 2025.1
SDTGen is ported away from XSCT and is now a separate tool. Instructions to generate SDT are updated accordingly.
Code and Data segments are separated in the Linker scripts.
NOC Memory segments are added in the Memory section names in Linker scripts.
Support for ZynqUS+ A53 32-bit
Support for armclang, armcc and IAR compilers.
Support for all the available TTC (timer) instances
Support for XXV Ethernet + EOE
Support for Hypervisor Guest option under Standalone BSP configurations
Updates in 2024.2
The default CMake generator has been updated to Ninja across all operating systems to enhance build performance
A new
--langoption has been added to thecreate_appscript, enabling support for C++ compilers.In the lopper the Linux device tree generation has been updated for Soft IP Linux video drivers.
Support for HBM memory in linux
Updates in 2024.1
New support added for Microblaze RISC-V processor and 64 bit Soft Microblaze processor
Incremental Build support for BSP
Enhanced Hardware related error mechanism
Improvement in BSP creation and build timing over Windows with the usage of Ninja generator
Fixed known gaps from 2023.2:
The BSP workspace and the Application Workspaces are now relocatable.
Microblaze designs no longer need to have "microblaze" in their processor names.
Latest source versions are picked irrespective of alphabetical order.
STDIN and STDOUT configuration selection are available for FreeRTOS BSP.
Addition of Linear flash devices as an available memory section in the linker script.
Addition of LwIP library to a FreeRTOS platform BSP settings
Addition of new lopper assists:
gen_domain_dts : to generate the domain specific dts (i.e. cortexa53.dts, cortexr5.dts, linux.dts etc.). Earlier it was done using lops.
baremetal_validate_comp_xlnx : to facilitate the hardware related error mechanism.
Updates in YAML spec:
device_type : Helps in giving meaningful error when there is a dependency of one kind of IP over a template application, a library or a driver. Available values are ethernet, serial, interrupt-controller, ipi and timer.
xparam_prefix : To specify a name different than that of ip-name in canonical macros
Updates in xparameters.h :
Addition of XPAR_CPU_ID macro
Addition of macros for generic interrupt IDs as available in legacy flow (in addition to the available macros used by interrupt wrapper)
Addition of macros for all the available addresses (from reg property) and interrupts (from interrupt property) in the device node.
Updated NOC related macros to follow the same names as that in legacy flow
Removal of the Interrupt ID macro for PS peripherals from xparameters.h (except for IPI). These macros have to be included from xparameters_ps.h
If driver is not compatible with SDT or the driver YAML doesn't have the "required" section, config file will not be generated. Earlier empty config file used to be generated.
A new property "is-hierarchy" is added to Hierarchical IP nodes. This is used to distinguish the IP sub-cores which are part of a subsystem and not accessible from AXI bus. Such IP sub-cores will not be treated as system level peripheral (like timer, gpio etc)
The Interrupt Controller example is made the first example in the peripheral tests sequence.
Updated logic to preserve the system device tree node order while generating metadata in xparameters.h and config files.
Updated generate_config_object assist to facilitate user-driven customization of xilpm library options.
Porting to the System Device Tree based flow
The following sections provide instructions to port the existing bare-metal drivers, libraries, and applications to work with the System Device Tree based flow.
Porting a driver
Every driver in AMD embeddedsw contains 4 folders as mentioned below
data
src
examples (optional)
doc (Not Relevant for this Porting Guide)
Changes in data folder
Addition of a new YAML file:
A YAML file named <driver_name>.yaml must be added inside data folder. This YAML file is a replacement of the older AMD proprietary metadata files (*.mdd, *.tcl). For example, data folder under CSUDMA driver must contain a file named csudma.yaml.
The adopted yaml specification borrows heavily from open source Linux yaml device tree bindings with some customizations to work with AMD embeddedsw baremetal stack. The yaml file contains the following sections:
Headers, copyrights, and generic information such as maintainer and type.
# SPDX-License-Identifier: MIT %YAML 1.2 --- title: <Description of the yaml file> maintainers: - Name(s) of the maintainer(s) type: <Describes the type of baremetal module the yaml file describes, e.g, driver> device_type: <Describes the type of devices that the driver supports> # Helps in giving meaningful error when there is a dependency of one kind of IP over # a template application, a library or a driver. # Available values are ethernet, serial, interrupt-controller, ipi and timer.Specification(s) that help(s) picking up the corresponding driver for an IP. The corresponding driver would get picked up into BSP if the IP is present in the system device tree and its compatible property in the system device tree node matches with this "compatible" string specified in the driver yaml. It replaces the mdd file-based approach and instead relies on Linux-like compatible property. Detailed usage of compatible property can be found under Appendix B.
properties: compatible: items: - const: <Typically the name of the HW IP as seen in xsa file, e.g. xlnx,zynqmp-csudma-1.0> reg: description: <Physical base address and size of the controller register map> other_properties_as_needed: description: <Description of the property>Specifications that help in generation of driver config file (*_g.c file) containing the driver config structure alongside the required xparameters.h. More Data on the usage of below section can be found in below snippet and under Appendix C, Appendix D.
config: - <The name of the driver config structure, e.g. XCsuDma_Config> xparam_prefix: xcsu # Specifies a custom name different than that of ip-name in canonical macros # General canonical macros follow the XPAR_X<driver_name>_<instance_number>_<PROPERTY> syntax # X<driver_name> will be replaced with the value of xparam_prefix in the above defined syntax required: # Describes the elements that will show up in the driver config structure # As an example, csudma driver *_g.c file will contain the XCsuDma_Config structure with following elements. #- compatible #- reg #- xlnx,dma-type #- interrupts #- interrupt-parent # The order mentioned under the "required" section must be same as that the driver config structure. optional: # Entries that are needed in the config structure but not in xparameters.h will be generated using this option. # Same keys and rules as that under the required section additionalProperties: # Entries that are needed only in xparameters.h and not in the config structure will be generated using this option. # Same keys and rules as that under required section.Specifications that describe driver example and their dependencies. Examples can depend on other supporting .h or .c files. Examples can have dependency on HW IP properties (like interrupts). Few examples can be valid only for few platforms. Please refer to the sample csudma.yaml below and in Appendix D for more info. Examples listed here will be listed under the import example section in the Vitis v2023.2 GUI.
examples: <Example name>: - <supported_platforms>: <has to be mentioned only when example is applicable to specific platform> - <dependency_files>: <List of supported files that need to be pulled while creating the example> - <Which HW parameters, it is dependent on. As an example xcsudma_intr_example.c needs interrupts to be enabled through HW.>Specifications that help in defining dependencies. A driver can be dependent on other drivers or a library (e.g. libmetal).
depends: # Create dict keys with the driver name on which the driver depends # Source files of the driver mentioned here will be pulled in libsrc <drv_name>: [] depends_libs: # Create dict keys with the library name on which the driver depends libmetal: {}Specifications that help in generating peripheral tests. Examples mentioned here will be included in peripheral tests. For more insights, please refer Appendix E.
tapp: <Example name>: - declaration: <Declaration to be used for the example> - hwproperties: - <HW property that should be available for the example to be picked as peripheral test>Below is a sample yaml file of an existing csudma driver. The yaml file name is csudma.yaml. More info on how the yaml takes the system device tree data and convert them into xparameters and Config structure can be found in Appendix D.
# SPDX-License-Identifier: MIT %YAML 1.2 --- title: Bindings for CSUDMA controller maintainers: - Abc Xyz <abc.xyz@domain.com> type: driver properties: compatible: items: - const: xlnx,zynqmp-csudma-1.0 reg: description: Physical base address and size of the controller register map xlnx,dma-type: description: | Differentiates the dma controller that driver supports 0 - CSUDMA controller 1 - PMCDMA-0 controller 2 - PMCDMA-1 controller config: - XCsuDma_Config required: - compatible - reg - xlnx,dma-type - interrupts - interrupt-parent examples: xcsudma_intr_example.c: - interrupts xcsudma_polled_example.c: - reg xcsudma_selftest_example.c: - reg tapp: xcsudma_selftest_example.c: declaration: XCsuDma_SelfTestExample xcsudma_intr_example.c: declaration: XCsuDma_IntrExample hwproperties: - interrupts ...
Changes in src Folder
Introduction of a new CMakeLists.txt File:
Instead of makefile, every driver should have a CMakeLists.txt file. Almost all driver CMakeLists.txt look similar. Below is a typical driver CMakeLists.txt.
cmake_minimum_required(VERSION 3.15) project(<driver name>) find_package(common) collector_create (PROJECT_LIB_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}") collector_create (PROJECT_LIB_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}") include_directories(${CMAKE_BINARY_DIR}/include) collect (PROJECT_LIB_SOURCES <source file>) collect (PROJECT_LIB_HEADERS <header file>) collector_list (_sources PROJECT_LIB_SOURCES) collector_list (_headers PROJECT_LIB_HEADERS) file(COPY ${_headers} DESTINATION ${CMAKE_BINARY_DIR}/include) add_library(<driver name> STATIC ${_sources}) set_target_properties(<driver name> PROPERTIES LINKER_LANGUAGE C) install(TARGETS <driver name> LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/build ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})Sample CMakeLists.txt file for CSUDMA driver:
# SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 3.15) project(csudma) find_package(common) collector_create (PROJECT_LIB_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}") collector_create (PROJECT_LIB_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}") include_directories(${CMAKE_BINARY_DIR}/include) collect (PROJECT_LIB_SOURCES xcsudma.c) collect (PROJECT_LIB_HEADERS xcsudma_hw.h) collect (PROJECT_LIB_HEADERS xpmcdma.h) collect (PROJECT_LIB_SOURCES xcsudma_sinit.c) collect (PROJECT_LIB_SOURCES xcsudma_selftest.c) collect (PROJECT_LIB_HEADERS xcsudma.h) collect (PROJECT_LIB_SOURCES xcsudma_g.c) collect (PROJECT_LIB_SOURCES xcsudma_intr.c) collector_list (_sources PROJECT_LIB_SOURCES) collector_list (_headers PROJECT_LIB_HEADERS) file(COPY ${_headers} DESTINATION ${CMAKE_BINARY_DIR}/include) add_library(csudma STATIC ${_sources}) set_target_properties(csudma PROPERTIES LINKER_LANGUAGE C)
Changes in existing source files (.c and .h):
AMD drivers have to be generic and the driver sources should not include platform specific configuration details through xparameters.h.
The existing embeddedsw driver uses a AMD proprietary parameter "DeviceId" in the driver config structure. Since the System Device Tree based flow "truly" aspires to be open source, it no more uses "DeviceId" and instead uses "BaseAddress". If a new driver is being created then developers need to use "BaseAddress" and not "DeviceId". If an old driver is being ported to this System Device Tree based flow, changes are needed to support both legacy ("DeviceId" based) and System Device Tree based flow ("BaseAddress" based).
A char * (for Name) in the driver *_Config structure has to be used to support this System Device Tree based flow. This can also be used later for differentiating between different versions of IP in the driver.
These lead to the below changes in source files:
typedef struct {
+#ifndef SDT
u16 DeviceId; /**< DeviceId is the unique ID of the
* device */
+#else
+ char *Name; /**< Unique name of the device */
+#endif
UINTPTR BaseAddress; /**< BaseAddress is the physical base address
* of the device's registers */ |
################################################################################
# Changes in *_sinit.c file
################################################################################
+#ifndef SDT
X<Driver>_Config *X<Driver>_LookupConfig(u16 DeviceId);
+#else
+X<Driver>_Config *X<Driver>_LookupConfig(UINTPTR BaseAddress);
+#endif
+#ifndef SDT
#include "xparameters.h"
+#endif
/************************** Constant Definitions *****************************/
@@ -65,6 +67,7 @@
*
* @note None.
******************************************************************************/
+#ifndef SDT
X<Driver>_Config *X<Driver>_LookupConfig(u16 DeviceId)
{
/* Legacy code */
}
+#else
+X<Driver>_Config *X<Driver>_LookupConfig(UINTPTR BaseAddress)
+{
+ X<Driver>_Config *CfgPtr = NULL;
+ u32 Index;
+
+ /* Checks all the instances */
+ for (Index = (u32)0x0; X<Driver>_ConfigTable[Index].Name != NULL; Index++) {
+ if ((X<Driver>_ConfigTable[Index].BaseAddress == BaseAddress) ||
+ !BaseAddress) {
+ CfgPtr = &X<Driver>_ConfigTable[Index];
+ break;
+ }
+ }
+
+ return CfgPtr;
+}
+#endif
|
Sample changes in CSUDMA driver:
+#ifndef SDT extern XCsuDma_Config XCsuDma_ConfigTable[XPAR_XCSUDMA_NUM_INSTANCES]; +#else +extern XCsuDma_Config XCsuDma_ConfigTable[]; +#endif +#ifndef SDT XCsuDma_Config *XCsuDma_LookupConfig(u16 DeviceId); +#else +XCsuDma_Config *XCsuDma_LookupConfig(UINTPTR BaseAddress); +#endif +#ifndef SDT #include "xparameters.h" +#endif /************************** Constant Definitions *****************************/ @@ -65,6 +67,7 @@ * * @note None. ******************************************************************************/ +#ifndef SDT XCsuDma_Config *XCsuDma_LookupConfig(u16 DeviceId) { XCsuDma_Config *CfgPtr = NULL; @@ -81,4 +84,22 @@ XCsuDma_Config *XCsuDma_LookupConfig(u16 DeviceId) return (XCsuDma_Config *)CfgPtr; } +#else +XCsuDma_Config *XCsuDma_LookupConfig(UINTPTR BaseAddress) +{ + XCsuDma_Config *CfgPtr = NULL; + u32 Index; + + /* Checks all the instances */ + for (Index = (u32)0x0; XCsuDma_ConfigTable[Index].Name != NULL; Index++) { + if ((XCsuDma_ConfigTable[Index].BaseAddress == BaseAddress) || + !BaseAddress) { + CfgPtr = &XCsuDma_ConfigTable[Index]; + break; + } + } + + return (XCsuDma_Config *)CfgPtr; +} +#endif
Changes in examples Folder
LookUp_Config API has to be updated in all the driver examples to use base address instead of Device ID.
In the System Device Tree based flow, interrupt registration has been simplified and simpler APIs are provided to offload interrupt registration process.
All references to Interrupt handling in the example must be removed. Use XSetupInterruptSystem() for registering the driver/application handler and use XDisconnectInterruptCntrl for disable in the interrupts
"interrupts" and "interrupt-parent" fields must be added to the Driver Config structure.
Please refer the changes in xcsudma_intr_example.c file below to get more detail.
In the xparameters.h file, two definitions for each interrupt are generated
A definition ending with _INTR, which is used in the legacy code. This is the raw interrupt number or ID that is used with the old interrupt handling functions. For PS peripherals except IPI, this macro is available in the xparameters_ps.h file, it won’t be generated in xparameters.h file.
A definition ending with _INTERRUPTS, which is used with the new interrupt wrapper API. This is a u16 variable which contains more information about the interrupt, such as the interrupt number, the priority, and the interrupt sensitivity information.
For interrupt wrapper definition refer here
Note: FreeRTOS port Interrupt calls like vPortEnableInterrupt and vPortEnableInterrupt are using Interrupt wrapper API calls, It expects interrupt ID's ending with _INTERRUPTS macro.
More Data on the usage can be found in below snippet and under Appendix C, Appendix D.
Sample changes for porting CSUDMA driver examples:
--- a/XilinxProcessorIPLib/drivers/csudma/data/csudma.yaml
+++ b/XilinxProcessorIPLib/drivers/csudma/data/csudma.yaml
@@ -31,4 +31,12 @@ required:
- xlnx,dma-type
- interrupts
- interrupt-parent
+
+examples:
+ xcsudma_intr_example.c:
+ - interrupts
+ xcsudma_polled_example.c:
+ - reg
+ xcsudma_selftest_example.c:
+ - reg |
--- a/XilinxProcessorIPLib/drivers/csudma/examples/
+++ b/XilinxProcessorIPLib/drivers/csudma/examples/xcsudma_intr_example.c
@@ -35,6 +35,7 @@
* 1.9 sk 12/23/20 Add the documentation for XCsuDma_IntrExample() function
* parameters to fix the doxygen warning.
* 1.11 sk 12/20/21 Add interrupt device id support for A78 and R52 processors.
+* 1.14 adk 05/04/23 Added support for system device-tree flow.
* </pre>
*
******************************************************************************/
@@ -44,11 +45,15 @@
#include "xcsudma.h"
#include "xparameters.h"
#include "xil_exception.h"
+#ifndef SDT
#ifdef XPAR_INTC_0_DEVICE_ID
#include "xintc.h"
#else
#include "xscugic.h"
#endif
+#else
+#include "xinterrupt_wrap.h"
+#endif
/************************** Function Prototypes ******************************/
@@ -57,6 +62,7 @@
* xparameters.h file. They are defined here such that a user can easily
* change all the needed parameters in one place.
*/
+#ifndef SDT
#define CSUDMA_DEVICE_ID XPAR_XCSUDMA_0_DEVICE_ID /* CSU DMA device Id */
#ifdef XPAR_INTC_0_DEVICE_ID
#define INTC XIntc
@@ -81,6 +87,9 @@
* of CSU DMA device ID */
#endif
#endif
+#else
+#define CSUDMA_BASEADDR XPAR_XCSUDMA_0_BASEADDR /* CSU DMA base address */
+#endif
#define CSU_SSS_CONFIG_OFFSET 0x008 /**< CSU SSS_CFG Offset */
#define CSUDMA_LOOPBACK_CFG 0x00000050 /**< LOOP BACK configuration
@@ -110,11 +119,15 @@ u32 SrcBuf[SIZE] __attribute__ ((aligned (64))); /**< Source buffer */
/************************** Function Prototypes ******************************/
+#ifndef SDT
int XCsuDma_IntrExample(INTC *IntcInstancePtr, XCsuDma *CsuDmaInstance,
u16 DeviceId, u16 IntrId);
static int SetupInterruptSystem(INTC *IntcInstancePtr,
XCsuDma *CsuDmaInstance,
u16 CsuDmaIntrId);
+#else
+int XCsuDma_IntrExample(XCsuDma *CsuDmaInstance, UINTPTR BaseAddress);
+#endif
void IntrHandler(void *CallBackRef);
static void SrcHandler(void *CallBackRef, u32 Event);
@@ -124,8 +137,10 @@ static void DstHandler(void *CallBackRef, u32 Event);
#ifndef TESTAPP_GEN
XCsuDma CsuDma; /**<Instance of the Csu_Dma Device */
+#ifndef SDT
static INTC Intc; /* Instance of the Interrupt Controller */
#endif
+#endif
volatile u32 DstDone = 0;
#ifndef TESTAPP_GEN
@@ -146,8 +161,12 @@ int main(void)
int Status;
/* Run the selftest example */
+#ifndef SDT
Status = XCsuDma_IntrExample(&Intc, &CsuDma, (u16)CSUDMA_DEVICE_ID,
INTG_CSUDMA_INTR_DEVICE_ID);
+#else
+ Status = XCsuDma_IntrExample(&CsuDma, CSUDMA_BASEADDR);
+#endif
if (Status != XST_SUCCESS) {
xil_printf("CSU_DMA Interrupt Example Failed\r\n");
return XST_FAILURE;
@@ -178,8 +197,12 @@ int main(void)
* @note None.
*
******************************************************************************/
+#ifndef SDT
int XCsuDma_IntrExample(INTC *IntcInstancePtr, XCsuDma *CsuDmaInstance,
u16 DeviceId, u16 IntrId)
+#else
+int XCsuDma_IntrExample(XCsuDma *CsuDmaInstance, UINTPTR BaseAddress)
+#endif
{
int Status;
XCsuDma_Config *Config;
@@ -194,7 +217,11 @@ int XCsuDma_IntrExample(INTC *IntcInstancePtr, XCsuDma *CsuDmaInstance,
* look up the configuration in the config table,
* then initialize it.
*/
+#ifndef SDT
Config = XCsuDma_LookupConfig(DeviceId);
+#else
+ Config = XCsuDma_LookupConfig(BaseAddress);
+#endif
if (NULL == Config) {
return XST_FAILURE;
}
@@ -221,8 +248,15 @@ int XCsuDma_IntrExample(INTC *IntcInstancePtr, XCsuDma *CsuDmaInstance,
/*
* Connect to the interrupt controller.
*/
+#ifndef SDT
Status = SetupInterruptSystem(IntcInstancePtr, CsuDmaInstance,
IntrId);
+#else
+ Status = XSetupInterruptSystem(CsuDmaInstance, &IntrHandler,
+ CsuDmaInstance->Config.IntrId,
+ CsuDmaInstance->Config.IntrParent,
+ XINTERRUPT_DEFAULT_PRIORITY);
+#endif
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
@@ -311,6 +345,7 @@ int XCsuDma_IntrExample(INTC *IntcInstancePtr, XCsuDma *CsuDmaInstance,
*
****************************************************************************/
+#ifndef SDT
static int SetupInterruptSystem(INTC *IntcInstancePtr,
XCsuDma *InstancePtr,
u16 IntrId)
@@ -415,7 +450,7 @@ static int SetupInterruptSystem(INTC *IntcInstancePtr,
return XST_SUCCESS;
}
-
+#endif |
--- a/XilinxProcessorIPLib/drivers/csudma/examples/xcsudma_polled_example.c
+++ b/XilinxProcessorIPLib/drivers/csudma/examples/xcsudma_polled_example.c
@@ -22,6 +22,7 @@
* ----- ------ -------- -----------------------------------------------------
* 1.0 vnsld 22/10/14 First release
* 1.4 adk 04/12/17 Added support for PMC DMA.
+* 1.14 adk 04/05/23 Added support for system device-tree flow.
* </pre>
*
******************************************************************************/
@@ -38,7 +39,11 @@
* xparameters.h file. They are defined here such that a user can easily
* change all the needed parameters in one place.
*/
+#ifndef SDT
#define CSUDMA_DEVICE_ID XPAR_XCSUDMA_0_DEVICE_ID /* CSU DMA device Id */
+#else
+#define CSUDMA_BASEADDR XPAR_XCSUDMA_0_BASEADDR /* CSU DMA Baseaddress */
+#endif
#define CSU_SSS_CONFIG_OFFSET 0x008 /**< CSU SSS_CFG Offset */
#define CSUDMA_LOOPBACK_CFG 0x00000050 /**< LOOP BACK configuration
* macro */
@@ -61,7 +66,11 @@
/************************** Function Prototypes ******************************/
+#ifndef SDT
int XCsuDma_PolledExample(u16 DeviceId);
+#else
+int XCsuDma_PolledExample(UINTPTR BaseAddress);
+#endif
/************************** Variable Definitions *****************************/
@@ -84,7 +93,11 @@ int main(void)
int Status;
/* Run the selftest example */
+#ifndef SDT
Status = XCsuDma_PolledExample((u16)CSUDMA_DEVICE_ID);
+#else
+ Status = XCsuDma_PolledExample(CSUDMA_BASEADDR);
+#endif
if (Status != XST_SUCCESS) {
xil_printf("CSU_DMA Polled Example Failed\r\n");
return XST_FAILURE;
@@ -110,7 +123,11 @@ int main(void)
* @note None.
*
******************************************************************************/
+#ifndef SDT
int XCsuDma_PolledExample(u16 DeviceId)
+#else
+int XCsuDma_PolledExample(UINTPTR BaseAddress)
+#endif
{
int Status;
XCsuDma_Config *Config;
@@ -125,7 +142,11 @@ int XCsuDma_PolledExample(u16 DeviceId)
* look up the configuration in the config table,
* then initialize it.
*/
+#ifndef SDT
Config = XCsuDma_LookupConfig(DeviceId);
+#else
+ Config = XCsuDma_LookupConfig(BaseAddress);
+#endif |
--- a/XilinxProcessorIPLib/drivers/csudma/examples/xcsudma_selftest_example.c
+++ b/XilinxProcessorIPLib/drivers/csudma/examples/xcsudma_selftest_example.c
@@ -22,6 +22,7 @@
* ms 04/10/17 Modified filename tag to include the file in doxygen
* examples.
* 1.2 adk 11/22/17 Added peripheral test app support.
+* 1.14 adk 05/04/23 Added support for system device-tree flow.
* </pre>
*
******************************************************************************/
@@ -38,7 +39,11 @@
* xparameters.h file. They are defined here such that a user can easily
* change all the needed parameters in one place.
*/
+#ifndef SDT
#define CSUDMA_DEVICE_ID XPAR_XCSUDMA_0_DEVICE_ID /* CSU DMA device Id */
+#else
+#define CSUDMA_BASEADDR XPAR_XCSUDMA_0_BASEADDR /* CSU DMA base address */
+#endif
/**************************** Type Definitions *******************************/
@@ -48,7 +53,11 @@
/************************** Function Prototypes ******************************/
+#ifndef SDT
int XCsuDma_SelfTestExample(u16 DeviceId);
+#else
+int XCsuDma_SelfTestExample(UINTPTR BaseAddress);
+#endif
/************************** Variable Definitions *****************************/
@@ -72,7 +81,11 @@ int main(void)
int Status;
/* Run the selftest example */
+#ifndef SDT
Status = XCsuDma_SelfTestExample((u16)CSUDMA_DEVICE_ID);
+#else
+ Status = XCsuDma_SelfTestExample(CSUDMA_BASEADDR);
+#endif
if (Status != XST_SUCCESS) {
xil_printf("CSU_DMA Selftest Example Failed\r\n");
return XST_FAILURE;
@@ -99,7 +112,11 @@ int main(void)
* @note None.
*
******************************************************************************/
+#ifndef SDT
int XCsuDma_SelfTestExample(u16 DeviceId)
+#else
+int XCsuDma_SelfTestExample(UINTPTR BaseAddress)
+#endif
{
int Status;
XCsuDma_Config *Config;
@@ -109,7 +126,11 @@ int XCsuDma_SelfTestExample(u16 DeviceId)
* look up the configuration in the config table,
* then initialize it.
*/
+#ifndef SDT
Config = XCsuDma_LookupConfig(DeviceId);
+#else
+ Config = XCsuDma_LookupConfig(BaseAddress);
+#endif
if (NULL == Config) {
return XST_FAILURE;
}
|
Porting a Library
Every library in AMD embeddedsw contains below 4 folders
data
src
examples (Optional)
doc (Not Relevant for this Porting Guide)
Changes in data Folder
Addition of a new YAML file:
A YAML file named <library_name>.yaml must be added inside data folder. This YAML file is a replacement of the older AMD proprietary metadata files (*.mld, *.tcl). For example, data folder under XILFPGA library must contain a file named xilfpga.yaml.
The yaml file contains the following sections:
Headers, Copyrights, Versions, Generic information (maintainer, type etc)
# SPDX-License-Identifier: MIT %YAML 1.2 --- title: <Description of the yaml file> maintainers: - Name(s) of the maintainer(s) type: <Describes the type of baremetal module the yaml file describes, e.g, library> description: < A brief description of the library >Specification(s) that help(s) picking up the library properly for a given platform (processor/OS configuration).
supported_processors: - <The list of CPUs the library supports> supported_os: - <The list of OSes the library supports>Specification(s) that describe(s) the dependencies of the library. A library can depend on certain driver/IP configurations which must be present in the system device tree for the library to be used. In addition, a library can also depend upon some other libraries.
depends: # Used in libraries like xilmailbox which depends on ipi or xiltimer which depends on timer related drivers <driver_name1> - <List of properties of driver/IP1 that library relies upon> <driver_name2> - <List of properties of driver/IP2 that library relies upon> depends_libs: <Library 1 that should be picked>: {} <Library 2 that should be picked>: {}Specifications that describe library example and their dependencies. Examples can depend on other supporting .h or .c files. There can be few examples only valid for few platforms. Examples listed here will be listed under the import example section in the Vitis 2023.2 GUI. Please refer sample xilfpga.yaml below for more info.
examples: <Example name>: - <supported_platforms>: - <has to be mentioned only when example is applicable to specific platform> - <dependency_files>: - <List of supported files that need to be pulled while creating the example>A sample library yaml (xilfpga.yaml):
# SPDX-License-Identifier: MIT %YAML 1.2 --- title: Bindings for xilfpga library. maintainers: - Zxcv Yuiop <zxcv.yuiop@domain.com> type: library description: |- XilFPGA library provides an interface to the Linux or bare-metal users for configuring the PL over PCAP from PS. supported_processors: - psu_cortexa53 - psu_cortexr5 - psu_pmu - psv_cortexa72 - psv_cortexr5 supported_os: - standalone - freertos10_xilinx depends_libs: xilsecure: {} xilmailbox: {} examples: xfpga_partialbitstream_load_example.c: [] xfpga_readback_example.c: - supported_platforms: - ZynqMP xfpga_reg_readback_example.c: - supported_platforms: - ZynqMP xfpga_load_bitstream_example.c: - supported_platforms: - ZynqMP - Versal
Changes in src Folder
Introduction of a new CMakeLists.txt File:
Instead of makefile, every library should have a CMakeLists.txt file. Below is a typical library CMakeLists.txt.
# SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 3.15) project(<library name>) find_package(common) # More details on <library name>.cmake follow in the next sub-section include(${CMAKE_CURRENT_SOURCE_DIR}/<library name>.cmake NO_POLICY_SCOPE) collector_create (PROJECT_LIB_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}") collector_create (PROJECT_LIB_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}") include_directories(${CMAKE_BINARY_DIR}/include) # Add subdirectories if there are any within src folder # Note that the subdirectories ought to have their own CMakeLists.txt to collect the sources and headers add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/<subdir_1>) collect (PROJECT_LIB_SOURCES <source file>) collect (PROJECT_LIB_HEADERS <header file>) collector_list (_sources PROJECT_LIB_SOURCES) collector_list (_headers PROJECT_LIB_HEADERS) file(COPY ${_headers} DESTINATION ${CMAKE_BINARY_DIR}/include) add_library(<library name> STATIC ${_sources}) set_target_properties(<library name> PROPERTIES LINKER_LANGUAGE C) install(TARGETS <library name> LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/build ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) get_headers(${_headers}) set_target_properties(<library name> PROPERTIES ADDITIONAL_CLEAN_FILES "${CMAKE_LIBRARY_PATH}/lib<library name>.a;${CMAKE_INCLUDE_PATH}/<rel_headers>;${clean_headers}") install(TARGETS <library name> LIBRARY DESTINATION ${CMAKE_LIBRARY_PATH} ARCHIVE DESTINATION ${CMAKE_LIBRARY_PATH}) install(DIRECTORY ${CMAKE_BINARY_DIR}/include DESTINATION ${CMAKE_INCLUDE_PATH}/..)As an example, the already available xilfpga library has a file CMakeLists.txt that has the following content. More details on Usage of CMAKE_MACHINE and CMAKE_SYSTEM_PROCESSOR can be found under Appendix F.
# SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 3.15) project(xilfpga) include(${CMAKE_CURRENT_SOURCE_DIR}/xilfpga.cmake NO_POLICY_SCOPE) find_package(common) collector_create (PROJECT_LIB_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}") collector_create (PROJECT_LIB_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}") include_directories(${CMAKE_BINARY_DIR}/include) if("${CMAKE_MACHINE}" STREQUAL "Versal") add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/interface/versal/) else() add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/interface/zynqmp/) endif() collect (PROJECT_LIB_SOURCES xilfpga.c) collect (PROJECT_LIB_HEADERS xilfpga.h) collector_list (_sources PROJECT_LIB_SOURCES) collector_list (_headers PROJECT_LIB_HEADERS) file(COPY ${_headers} DESTINATION ${CMAKE_BINARY_DIR}/include) if (${NON_YOCTO}) file(COPY ${CMAKE_BINARY_DIR}/include/xfpga_config.h DESTINATION ${CMAKE_INCLUDE_PATH}/) endif() add_library(xilfpga STATIC ${_sources}) set_target_properties(xilfpga PROPERTIES LINKER_LANGUAGE C) get_headers(${_headers}) set_target_properties(xilfpga PROPERTIES ADDITIONAL_CLEAN_FILES "${CMAKE_LIBRARY_PATH}/libxilfpga.a;${CMAKE_INCLUDE_PATH}/xfpga_config.h;${clean_headers}") install(TARGETS xilfpga LIBRARY DESTINATION ${CMAKE_LIBRARY_PATH} ARCHIVE DESTINATION ${CMAKE_LIBRARY_PATH}) install(DIRECTORY ${CMAKE_BINARY_DIR}/include DESTINATION ${CMAKE_INCLUDE_PATH}/..)
If the library has software configuration it is facilitated by addition of two more files (<library_name>.cmake and x<library_name>_config.h.in):
It is typical for a library to have software configurations for users to make use of.
The System Device Tree based flow makes use of open source CMake based flow and replaces the existing mld/tcl based flow to configure the library.
Combination of <library_name>.cmake and x<library_name>_config.h.in generates the required software configuration header file <library_name>_config.h based on the user configuration.
x<library_name>_config.h.in:
Using cmakedefine macro in config.h.in file, a series of define statements (macros) can be generated.
This config file when processed in conjunction with <library name>.cmake results into a .h (header) file which contains all the generated #define statements in C syntax
Sample xfpga_config_h_in file
#ifndef _XFPGA_CONFIG_H #define _XFPGA_CONFIG_H #include <xilfpga.h> @PCAP_INCLUDE@ @PCAP_COMMON_INCLUDE@ @VERSAL_INCLUDE@ #cmakedefine XFPGA_OCM_ADDRESS @XFPGA_OCM_ADDRESS@U #cmakedefine XFPGA_BASE_ADDRESS @XFPGA_BASE_ADDRESS@U #cmakedefine XFPGA_SECURE_MODE @XFPGA_SECURE_MODE@ #cmakedefine01 XFPGA_DEBUG @XFPGA_DEBUG@ #cmakedefine XFPGA_SECURE_READBACK_MODE @XFPGA_SECURE_READBACK_MODE@ #cmakedefine XFPGA_SECURE_IPI_MODE_EN @XFPGA_SECURE_IPI_MODE_EN@ #endif /* XFPGA_CONFIG_H */Please refer to https://gitlab.kitware.com/cmake/community/wikis/doc/tutorials/How-To-Write-Platform-Checks for more details on how to use the configuration files with CMAKE.
<library_name>.cmake :
The Software configuration options can be of variety of data types. For example, these parameters can be a boolean, a hex value, an entry among a list of possible options or a number among a large range of numbers. All these software configurations tend to have a default value which can later be changed.
All the possible choices defined above can be described using below CMAKE commands:
For configuring parameters that have boolean properties
option(<option_variable> "help string describing option" [initial value])
For more info on options, please refer:
option — CMake 3.13.5 Documentation
For configuring parameters whose values could be one in a list of values or a range of values
set(<variable> <value> [[CACHE <type> <docstring> [FORCE]] | PARENT_SCOPE])
For more info on set command, please refer:
set — CMake 3.0.2 Documentation
Once the cmake options and variables are set, configure_file() command has to be called. It calls the earlier described config.h.in file and generates the <libray name>_config.h
Below is a sample xilfpga.cmake (More details on Usage of CMAKE_MACHINE and CMAKE_SYSTEM_PROCESSOR can be found under Appendix F)
# SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 3.15) option(XILFPGA_secure_mode "Enable secure Bitstream loading support" ON) option(XILFPGA_secure_environment "Which is used to Enable the secure PL configuration" OFF) option(XILFPGA_secure_readback "Which is used to Enable the secure PL configuration Read-back support" OFF) option(XILFPGA_debug_mode "Which is used to Enable the Debug messages in the library" OFF) SET(XILFPGA_ocm_address 0xfffc0000 CACHE STRING "OCM Address which is used for Bitstream Authentication") SET(XILFPGA_base_address 0x80000 CACHE STRING "Bitstream Image Base Address") set(zynqmp_secure_env 0) if("${CMAKE_MACHINE}" STREQUAL "Versal") set(VERSAL_INCLUDE "#include <xilfpga_versal.h>") elseif("${CMAKE_MACHINE}" STREQUAL "ZynqMP") set(PLATFORM_ZYNQMP " ") if((NOT("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "pmu_microblaze")) AND ${XILFPGA_secure_environment}) set(XFPGA_SECURE_IPI_MODE_EN " ") set(zynqmp_secure_env 1) else() set(PCAP_INCLUDE "#include <xilfpga_pcap.h>") endif() set(PCAP_COMMON_INCLUDE "#include <xilfpga_pcap_common.h>") endif() set(XFPGA_OCM_ADDRESS ${XILFPGA_ocm_address}) set(XFPGA_BASE_ADDRESS ${XILFPGA_base_address}) if (${XILFPGA_secure_mode}) set(XFPGA_SECURE_MODE " ") endif() if (${XILFPGA_debug_mode}) set(XFPGA_DEBUG " ") endif() if (${XILFPGA_secure_readback}) set(XFPGA_SECURE_READBACK_MODE " ") endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/xfpga_config.h.in ${CMAKE_BINARY_DIR}/include/xfpga_config.h)
Introduction of a new header file:
As seen in the last sub-section, a new file named <libray name>_config.h is originated by the configure_file() API when the <library_name>.cmake and x<library_name>_config.h.in files are processed together.
This separates out the library specific configurations into <libray name>_config.h file which earlier used to reside within xparameters.h in the existing flow.
This file will be auto-generated as mentioned before.
Sample xfpga_config.h :
#ifndef _XFPGA_CONFIG_H #define _XFPGA_CONFIG_H #include <xilfpga.h> /* Empty line as the PCAP_INCLUDE couldn't be set in xilfpga.cmake */ /* Empty line as PCAP_COMMON_INCLUDE couldn't be set in xilfpga.cmake */ #include <xilfpga_versal.h> #define XFPGA_OCM_ADDRESS 0xfffc0000U #define XFPGA_BASE_ADDRESS 0x80000U #define XFPGA_SECURE_MODE #define XFPGA_DEBUG 0 /* #undef XFPGA_SECURE_READBACK_MODE */ /* #undef XFPGA_SECURE_IPI_MODE_EN */ #endif /* XFPGA_CONFIG_H */
Changes in the existing source files (.c and .h):
Changes mentioned under the Driver Section's "Changes in existing source files (.c and .h)" must be complied to in library sources as well.
All the calls of LookupConfig() API of every driver must be updated to use BaseAddress instead of DeviceId
All the references to XPAR_<drv_name>_DEVICE_ID macro must be replaced with the macro defined under x<library_name>_config.h .
Include the newly generated x<library_name>_config.h in the .c files wherever needed
Below is a sample change:
--- a/lib/sw_services/xilloader/src/common/xloader_auth_enc.c +++ b/lib/sw_services/xilloader/src/common/xloader_auth_enc.c @@ -110,6 +110,7 @@ * am 06/19/23 Added KAT error code for failure cases * sk 07/06/23 Added Jtag DAP config support for Non-Secure Debug * am 07/03/23 Added authentication optimization support +* ng 07/13/23 Added support for system device tree flow * * </pre> * @@ -135,6 +136,7 @@ #include "xsecure_init.h" #include "xloader_plat_secure.h" #include "xloader_plat.h" +#include "xplmi_config.h" /************************** Constant Definitions ****************************/ @@ -668,7 +670,7 @@ int XLoader_ImgHdrTblAuth(XLoader_SecureParams *SecurePtr) SecurePtr->AcPtr = AuthCert; /** - Get DMA instance */ - SecurePtr->PmcDmaInstPtr = XPlmi_GetDmaInstance((u32)PMCDMA_0_DEVICE_ID); + SecurePtr->PmcDmaInstPtr = XPlmi_GetDmaInstance(PMCDMA_0_DEVICE); if (SecurePtr->PmcDmaInstPtr == NULL) { Status = XPlmi_UpdateStatus(XLOADER_ERR_IHT_GET_DMA, 0); goto END; @@ -790,7 +792,7 @@ int XLoader_ReadAndVerifySecureHdrs(XLoader_SecureParams *SecurePtr, XPlmi_Printf(DEBUG_INFO, "Loading secure image headers and partition headers\n\r"); /* Get DMA instance */ - SecurePtr->PmcDmaInstPtr = XPlmi_GetDmaInstance((u32)PMCDMA_0_DEVICE_ID); + SecurePtr->PmcDmaInstPtr = XPlmi_GetDmaInstance(PMCDMA_0_DEVICE); if (SecurePtr->PmcDmaInstPtr == NULL) { Status = XPlmi_UpdateStatus(XLOADER_ERR_HDR_GET_DMA, 0); goto ERR_END; @@ -3218,7 +3220,7 @@ static int XLoader_AuthJtag(u32 *TimeOut) goto END; } - SecureParams.PmcDmaInstPtr = XPlmi_GetDmaInstance((u32)PMCDMA_0_DEVICE_ID); + SecureParams.PmcDmaInstPtr = XPlmi_GetDmaInstance(PMCDMA_0_DEVICE); if (SecureParams.PmcDmaInstPtr == NULL) { Status = XPlmi_UpdateStatus(XLOADER_ERR_AUTH_JTAG_GET_DMA, 0); goto END;
Changes in examples Folder
Changes needed in library examples are similar to that needed for driver examples. Please refer to the "Changes in examples Folder" under "Porting a driver" section for more details. Below is a sample change:
--- a/lib/sw_services/xilloader/data/xilloader.yaml
+++ b/lib/sw_services/xilloader/data/xilloader.yaml
@@ -25,4 +25,12 @@ depends_libs:
xilpm: {}
xilpdi: {}
xilffs: {}
+
+examples:
+ xilloader_add_image_store_pdi_example.c:
+ - supported_platforms:
+ - Versal
+ xilloader_load_pdi_example.c:
+ - supported_platforms:
+ - Versal
+ xilloader_update_multiboot.c:
+ - supported_platforms:
+ - Versal |
--- a/lib/sw_services/xilloader/examples/xilloader_add_image_store_pdi_example.c
+++ b/lib/sw_services/xilloader/examples/xilloader_add_image_store_pdi_example.c
@@ -20,6 +20,7 @@
* bsv 08/18/2022 Fix typo in CmdId
* 1.1 sk 03/10/2023 Updated changes to command format
* sk 04/18/2023 Added support for versalnet
+ * 1.9 ng 06/21/2023 Added support for system device-tree flow
*/
#include <stdio.h>
#include "xil_printf.h"
@@ -27,6 +28,12 @@
#include "xipipsu.h"
#include "xil_cache.h"
+#ifdef SDT
+#define IOMODULE_DEVICE (XPAR_XIOMODULE_0_BASEADDR)
+#else
+#define IOMODULE_DEVICE (XPAR_IOMODULE_0_DEVICE_ID)
+#endif
+
#if defined(VERSAL_NET)
#define TARGET_IPI_INT_MASK XPAR_XIPIPS_TARGET_PSX_PMC_0_CH0_MASK
#else
@@ -82,7 +89,7 @@ static int DoIpiTest(void)
Xil_DCacheDisable();
/* Look Up the config data */
- IpiCfgPtr = XIpiPsu_LookupConfig(0U);
+ IpiCfgPtr = XIpiPsu_LookupConfig(IOMODULE_DEVICE);
if (NULL == IpiCfgPtr) {
goto END;
} |
Porting a Template application:
Every Template application contains below folders
data
src
Changes in data Folder
Addition of a new YAML file:
A YAML file named <app_name>.yaml must be added inside data folder. This YAML file is a replacement of the older AMD proprietary metadata files (*.mss, *.tcl). For example, data folder under hello_world template folder must contain a file named hello_world.yaml.
The yaml file contains the following sections:
Headers, Copyrights, Versions, Generic information (maintainer, type etc)
# SPDX-License-Identifier: MIT %YAML 1.2 --- title: <Description of the yaml file> maintainers: - Name(s) of the maintainer(s) type: <Describes the type of baremetal module the yaml file describes, e.g, apps> description: < A brief description of the Application >Specification(s) that help(s) picking up the application properly for a given platform (processor/OS configuration).
supported_processors: - <The list of CPUs the library supports> supported_os: - <The list of OSes the library supports>Specification(s) that describes the dependencies of the Application. An application can depend on certain driver/IP configurations which must be present in the system device tree for the application to be compiled. In addition, an application can also depend on the libraries and can by default need specific library configurations depending upon the use case.
depends: # e.g. lwip applications need atleast one ethernet IP in the design <driver_name1> - <List of properties of driver/IP1 that the application relies upon> <driver_name2> - <List of properties of driver/IP2 that the application relies upon> depends_libs: <Library 1 that should be configured>: <lib1 param1>: <lib1 val1> <lib1 param2>: <lib1 param2_0> <Family Name from bsp.yaml>: <lib1 param2>: <lib1 param2_1> <Library 2 that should be picked>: <lib2 param1>: <lib2 val1>Specification(s) that describes the os-level and linker related requirements of the Application. These will be used when the application requires some change in the os level parameter or the linker heap and stack size. For more details on linker generation, please refer Appendix G.
os_config: <os name i.e. freertos or standalone>: <os param name e.g. freertos_total_heap_size>: <os param value e.g. 262140> linker_constraints: # Default Stack and Heap size is set to 0x2000 for Platforms other than Microblaze. # For Microblaze, the sizes are set to 0x400. stack: <Required Stack size e.g. 0xA000> heap: <Required Heap size e.g. 0xA000>A sample Application yaml (freertos_lwip_udp_perf_client.yaml):
# SPDX-License-Identifier: MIT %YAML 1.2 --- title: FreeRTOS lwIP UDP Perf Client maintainers: - Klmn rtyu <klmn.rtyu@domain.com> type: apps description: The FreeRTOS LwIP UDP Perf Client application is used for creating UDP client and measure UDP uplink performance using light-weight IP stack (lwIP). This application sets up the board to use default IP address 192.168.1.10, with MAC address 00:0a:35:00:01:02. This application creates UDP client on board and make connection with UDP server running on host machine. It will display connection information along with interim and average UDP statistics for data transfer. supported_processors: - psu_cortexa53 - psu_cortexr5 - psv_cortexa72 - psv_cortexr5 - psx_cortexa78 - psx_cortexr52 - ps7_cortexa9 - microblaze supported_os: - freertos10_xilinx depends: emaclite: - reg - interrupts axiethernet: - reg - interrupts emacps: - reg - interrupts depends_libs: lwip213: lwip213_api_mode: SOCKET_API lwip213_dhcp_does_arp_check: true lwip213_dhcp: true lwip213_ipv6_enable: false lwip213_pbuf_pool_size: 16384 lwip213_memp_n_pbuf: 1024 lwip213_mem_size: 524288 lwip213_n_tx_descriptors: 512 xiltimer: XILTIMER_en_interval_timer: true os_config: freertos: freertos_total_heap_size: 262140 linker_constraints: stack: 0xA000 heap: 0xA000
Changes in src Folder
Introduction of a new file CMakeLists.txt:
Instead of Makefile, every application should have a CMakeLists.txt file. Below is a sample CMakeLists.txt for hello_world template.
# SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 3.15) # For more details on <App_name>Example.cmake, please refer Appendix G include(${CMAKE_CURRENT_SOURCE_DIR}/Hello_worldExample.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/UserConfig.cmake) set(APP_NAME hello_world) project(${APP_NAME}) enable_language(C ASM CXX) find_package(common) # Describe the dependency of this application on multiple archivers collect(PROJECT_LIB_DEPS xilstandalone) collect(PROJECT_LIB_DEPS xil) collect(PROJECT_LIB_DEPS xiltimer) collect(PROJECT_LIB_DEPS gcc) collect(PROJECT_LIB_DEPS c) collect (PROJECT_LIB_SOURCES platform.c) collect (PROJECT_LIB_SOURCES helloworld.c) collector_list (_sources PROJECT_LIB_SOURCES) foreach (source ${_sources}) get_filename_component(ext ${source} EXT) list(APPEND src_ext ${ext}) endforeach() find_project_type ("${src_ext}" PROJECT_TYPE) if("${PROJECT_TYPE}" STREQUAL "c++") collect(PROJECT_LIB_DEPS stdc++) endif() collector_list (_deps PROJECT_LIB_DEPS) list (APPEND _deps ${USER_LINK_LIBRARIES}) if("${PROJECT_TYPE}" STREQUAL "c++") string (REPLACE ";" ",-l" _deps "${_deps}") endif() if(CMAKE_EXPORT_COMPILE_COMMANDS) set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) set(CMAKE_C_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}) endif() linker_gen("${CMAKE_CURRENT_SOURCE_DIR}/linker_files/") string(APPEND CMAKE_C_FLAGS ${USER_COMPILE_OPTIONS}) string(APPEND CMAKE_CXX_FLAGS ${USER_COMPILE_OPTIONS}) string(APPEND CMAKE_C_LINK_FLAGS ${USER_LINK_OPTIONS}) string(APPEND CMAKE_CXX_LINK_FLAGS ${USER_LINK_OPTIONS}) set_source_files_properties(${_sources} OBJECT_DEPENDS "${CMAKE_LIBRARY_PATH}/*.a") add_executable(${APP_NAME}.elf ${_sources}) set_target_properties(${APP_NAME}.elf PROPERTIES LINK_DEPENDS ${CMAKE_SOURCE_DIR}/lscript.ld) target_link_libraries(${APP_NAME}.elf -Wl,-T -Wl,\"${CMAKE_SOURCE_DIR}/lscript.ld\" -L\"${CMAKE_SOURCE_DIR}/\" -L\"${CMAKE_LIBRARY_PATH}/\" -L\"${USER_LINK_DIRECTORIES}/\" -Wl,--start-group,-l${_deps} -Wl,--end-group) target_compile_definitions(${APP_NAME}.elf PUBLIC ${USER_COMPILE_DEFINITIONS}) target_include_directories(${APP_NAME}.elf PUBLIC ${USER_INCLUDE_DIRECTORIES}) print_elf_size(CMAKE_SIZE ${APP_NAME})
Changes in the existing source files (.c and .h):
Changes mentioned under the Driver Section's "Changes in existing source files (.c and .h)" must be complied to in application sources as well.
All the calls of LookupConfig() API of every driver must be updated to use BaseAddress instead of DeviceId
Sample Change:
--- a/lib/sw_apps/srec_spi_bootloader/src/bootloader.c +++ b/lib/sw_apps/srec_spi_bootloader/src/bootloader.c @@ -101,7 +102,11 @@ extern void init_stdout(); uint8 grab_hex_byte (uint8 *buf); int FlashReadID(void); +#if defined (SDT) +#define SPI_DEVICE_BASEADDR XPAR_AXI_QUAD_SPI_0_BASEADDR +#else #define SPI_DEVICE_ID XPAR_SPI_0_DEVICE_ID +#endif /* * The instances to support the device drivers are global such that they @@ -172,7 +177,11 @@ int main() * Initialize the SPI driver so that it's ready to use, * specify the device ID that is generated in xparameters.h. */ +#if defined (SDT) + Status = XSpi_Initialize(&Spi, SPI_DEVICE_BASEADDR); +#else Status = XSpi_Initialize(&Spi, SPI_DEVICE_ID); +#endif if(Status != XST_SUCCESS) { return XST_FAILURE; } --- a/lib/sw_apps/srec_spi_bootloader/src/platform.c +++ b/lib/sw_apps/srec_spi_bootloader/src/platform.c @@ -1,11 +1,14 @@ #include "xparameters.h" +#if !defined (SDT) #include "platform_config.h" +#endif -#ifdef STDOUT_IS_16550 +#if ( defined (STDOUT_IS_16550) || ( defined (SDT) && defined (XPAR_STDIN_IS_UARTNS550))) #include "xuartns550_l.h" #endif @@ -13,7 +16,7 @@ void init_stdout() { /* if we have a uart 16550, then that needs to be initialized */ -#ifdef STDOUT_IS_16550 +#if ( defined (STDOUT_IS_16550) || ( defined (SDT) && defined (XPAR_STDIN_IS_UARTNS550))) XUartNs550_SetBaud(STDOUT_BASEADDR, XPAR_XUARTNS550_CLOCK_HZ, 9600); XUartNs550_SetLineControlReg(STDOUT_BASEADDR, XUN_LCR_8_DATA_BITS); #endif
FAQs
Why is AMD shifting to this System Device Tree based flow?
Earlier approach of using mdd, mld and mss files along with the .xsa as a handoff file involved a lot of AMD proprietary tools to build our software stack. This approach uses all the open source file formats and tools once the handoff file is generated by the Hardware Persona.
How is the System Device Tree different than the Linux Device tree?
Linux Device Tree contains only the Linux/APU (or Microblaze) related peripheral info at a time. System Device Tree contains the whole system info (includes info related to everything i.e. APU, RPU, Microblaze, PMC etc.)
How to differentiate your component whether it is a driver, library or an application?
"type" key in every YAML determines this. Available options for this key are: driver, library, apps, os
How is the driver probed/picked while building libxil?
Lopper backend checks for a given node compatible against the compatible specified in the driver yaml file under the properties section. if there is a match it picks the driver and generates _g.c file in the subsequent process.
How are the _g.c entries and the xparameters generated for drivers?
Lopper backend generates the _g.c entries based on the properties specified in yaml file "required" section. Please make sure these entries are following the driver config structure order. Refer to Appendix C and Appendix D for more details.
If the config structure entry has dependency on the other node base address, then how to handle it?
The meta-data representation for the same is <property-name>: phandle (if the property is dependent on child node is available, it will read the reg property of that node and generates it's value in this entry otherwise generates zero). Refer to Appendix C and Appendix D for more details.
If the config structure has more than one base address in sequence, then how to add that info in meta-data to generate proper addresses in config structure?
The meta-data representation for the above requirement is reg:<number of entries>. Refer to Appendix C for more details.
If the property mentioned in the required section is not present in the device-tree node, then what value will get generated in the _g.c file?
Zero (0)
How to add multiple configurations for a library?
Addition of <library_name>.cmake and x<library_name>_config.h.in facilitates the library software configuration. Please refer "Changes in src folder" under "Porting a Library" Section for more info.
How to use platform or a processor specific piece of code?
CMAKE_MACHINE and CMAKE_SYSTEM_PROCESSOR variables inside cmake can be used to differentiate a platform or a processor specific piece of code. Please refer Appendix E to know the available values for this cmake variable.
Where are the Library related configuration specific Macros located? It is missing in xparameters.h
Those have been relocated to the <library>_config.h file. Please refer "Changes in src folder" under "Porting a Library" Section for more info.
How to define dependencies of a library or an application?
"depends" and "depends_libs" keys under a library YAML describes the Hardware and Software dependency of the library respectively. Please refer "Changes in data folder" under "Porting a Library" (or under "Porting a template Application") Section in conjunction with Appendix F for more details.
How to define the archiver dependencies of the application?
collect(PROJECT_LIB_DEPS xil) kind of statements in Application CMakeLists defines this dependency. This statement means that the application depends on libxil.a.
How is the linker script generated?
Intermediary file lscript.ld.in kept within the embeddedsw repository is copied into the application source folder which in conjunction with the <Application>Example.cmake generates the final lscript.ld. Refer to Appendix G for more details.
How to set a different stack and heap size for the application that doesn't fit in the default size?
"linker_constraints" key in the application YAMLs is used to modify the default stack and heap sizes. Please refer Appendix G for more details.
Appendix A: System Device Tree (SDT) Generation using SDTGen
Refer this README for more details on SDTGen.
System Device Tree:
Represents complete hardware information in the form of device trees.
All peripheral information and its properties, memories, clusters, soc peripheral information, soft IP information etc. available in the hardware design are represented in Linux-like device tree structure.
SDTGEN (Also known as DTG++):
A TCL based tool that uses Hardware HSI APIs to read the hardware information from XSA and put it in device tree format.
The TCL source files for this package can be found under <Installed Vitis Path>/data/system-device-tree-xlnx
The package sources the device_tree.tcl file from <above_path>/device_tree/data/device_tree.tcl and exports the following three procs as commands:
set_dt_param
get_dt_param
generate_sdt
Generation steps:
Launch the installed sdtgen
set_dt_param:
Takes the user inputs like the Vivado XSA file and the system device tree output directory
It can also be used to set the system device tree parameters such as the board file, custom dts file etc.
Usage:
# Setting the system device tree parameters. Multiple parameter setting in one line is also allowed. sdtgen% set_dt_param -dir output_dts sdtgen% set_dt_param -xsa design_1_wrapper.xsa -board_dts zcu102-rev1.0 # Enabling the trace i.e. the flow of tcl procs that are getting invoked during sdt generation. The default trace option is disabled sdtgen% set_dt_param -trace enable #Command Help sdtgen% set_dt_param -help Usage: set/get_dt_param [OPTION] -xsa Vivado hw design file -board_dts board specific file -dir Directory where the dt files will be generated -trace Enable sdtgen traces
get_dt_param:
Returns the value set for a given parameter.
Usage:
# Checking the dt parameters sdtgen% get_dt_param -board_dts zcu102-rev1.0 sdtgen% get_dt_param -dir output_dts sdtgen% get_dt_param -xsa design_1_wrapper.xsa sdtgen% get_dt_param -help Usage: set/get_dt_param [OPTION] -xsa Vivado hw design file -board_dts board specific file -dir Directory where the dt files will be generated -trace Enable sdtgen traces
generate_sdt:
Generates the system device tree with the set parameters
Overall Usage Example:
# Generic command for building any SDT targeting Bare-metal use cases. Launch sdtgen by sourcing Installed Vitis/Vivado settings.sh linux# sdtgen sdtgen% set_dt_param -dir dts -xsa design_1_wrapper.xsa sdtgen% generate_sdt # Command for building Zynq SDT targeting Linux use cases Launch sdtgen by sourcing Installed Vitis/Vivado settings.sh linux# sdtgen sdtgen% set_dt_param -board_dts zc702 -dir dts -xsa design_1_wrapper.xsa sdtgen% generate_sdt
System device tree output directory:
The generated dts directory can contain following files:
soc.dtsi, which is static soc specific file (e.g.: for versal: versal.dtsi, for zynqmp: zynqmp.dtsi, for zynq: zynq-7000.dtsi, for microblaze: there is no such soc specific file)
pl.dtsi which contains soft IPs information
board.dtsi, which is board file (Is pulled in only when -board_dts option is set)
Sample e.g.: For versal: versal-vck190-rev1.1.dtsi / For zynqmp: zcu102-rev1.0.dtsi / For zynq: zc702.dtsi / For microblaze: kc705-lite.dtsi
© 2025 Advanced Micro Devices, Inc. Privacy Policy