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 2024.2
- 5 Updates in 2024.1
- 6 Porting to the System Device Tree based flow
- 6.1 Porting a driver
- 6.1.1 Changes in data folder
- 6.1.2 Changes in src Folder
- 6.1.3 Changes in examples Folder
- 6.2 Porting a Library
- 6.2.1 Changes in data Folder
- 6.2.2 Changes in src Folder
- 6.2.3 Changes in examples Folder
- 6.3 Porting a Template application:
- 6.3.1 Changes in data Folder
- 6.3.2 Changes in src Folder
- 6.1 Porting a driver
- 7 FAQs
- 8 Appendix A: System Device Tree (SDT) Generation at XSCT
- 9 Appendix B: Usage of "compatible" under "properties"
- 10 Appendix C: Available options under "required" section
- 11 Appendix D: A Comprehensive sample YAML for drivers
- 12 Appendix E: A Driver example in the Peripheral Test App
- 13 Appendix F: CMAKE_MACHINE and CMAKE_SYSTEM_PROCESSOR
- 14 Appendix G: Apps: Hardware Dependency and Linker Script Generation
- 15 Known Gaps (2024.2)
- 16 Known Gaps (2024.1)
- 17 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 2024.2
The default CMake generator has been updated to Ninja across all operating systems to enhance build performance
A new
--lang
option has been added to thecreate_app
script, enabling support for C++ compilers.In the lopper the Linux device tree generation has been updated for Soft IP Linux video drivers.
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> <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 at XSCT
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.
XSCT:
XSCT (Xilinx Software Command-Line) is a tool that allows us to create complete Xilinx SDK workspaces using the batch mode, investigate the hardware and software, debug and run the project, all from the command line.
More details on this tool can be accessed at UG-1208. System Device Tree Generation has been a recent addition to this tool.
SDTGEN (Also known as DTG++):
An XSCT package that uses tcls and 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>/2023.2/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 xsct
set_dt_param:
SDTGEN command that 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. xsct% sdtgen set_dt_param -dir output_dts xsct% 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 xsct% sdtgen set_dt_param -trace enable #Command Help xsct% 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:
SDTGEN command that can return the value set for a given parameter.
Usage:
# Checking the dt parameters xcst% sdtgen get_dt_param -board_dts zcu102-rev1.0 xcst% sdtgen get_dt_param -dir output_dts xsct% sdtgen get_dt_param -xsa design_1_wrapper.xsa xsct% 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:
SDTGEN command that generates the system device tree with the set parameters
Overall Usage Example:
# Generic command for building any SDT targeting Bare-metal use cases. Launch xsct from daily latest linux# xsct xsct% sdtgen set_dt_param -dir dts -xsa design_1_wrapper.xsa xsct% sdtgen generate_sdt # Command for building Zynq SDT targeting Linux use cases Launch xsct from daily latest linux# xsct xsct% sdtgen set_dt_param -board_dts zc702 -dir dts -xsa design_1_wrapper.xsa xsct% 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.dtsiclk.dtsi, which is clock information
Ex: For versal: versal-clk.dtsi/ For zynqmp: zynqmp-clk-ccf.dtsi / For zynq, microblaze: There is no clock framework filesystem-top.dts, which is top level system information which contains memory, clusters, aliases etc.
pcw.dtsi, which contains peripheral configuration wizard information of the peripherals
psu_init* files for Zynq and ZU+, pdi file for Versal
For Versal designs, a folder with design name that contains the pdi_files contents
Appendix B: Usage of "compatible" under "properties"
Different ways to show compatible string under properties key of driver yamls:
When there is one IP and one driver :
properties:
compatible:
items:
- const: xlnx,zynqmp-csudma-1.0 |
When multiple IPs are having the same driver:
properties:
compatible:
OneOf:
- items:
- enum:
- xlnx,zynqmp-8.9a
- arasan,sdhci-8.9a
- enum:
- xlnx,versal-8.9a
- arasan,sdhci-8.9a
- const:
- xlnx,versal-net-emmc |
When same IP is having different version:
properties:
compatible:
OneOf:
- items:
- enum:
- xlnx,axi-cdma-4.1
- xlnx,axi-cdma-1.00.a |
Appendix C: Available options under "required" section
Key | Notes | Config structure | XPARAMETERS | Usage Of Padding |
compatible | The existing embedded software driver uses "DEVICE_ID" in the driver config structure to get the peripheral instance. This approach is now being replaced with the usage of Base Address. Base Address of the peripheral instance is derived used the reg property. More details follow in the next point. | Filler for DEVICE_ID
| #define XPAR_{node_label_name}_COMPATIBLE {value}
Canonical define: #define XPAR_X{driver_name}_{index}_COMPATIBLE {value} | Not Allowed |
reg | System device tree nodes usually have reg property in <base_addr size> format. This reg property will be used to derive BASE_ADDRESS and HIGH_ADDRESS of the IP instance. | Filler for Base Address | #define XPAR_{node_label_name}_BASEADDR {hex(base_addr)} #define XPAR_{node_label_name}_HIGHADDR {hex(base_addr + size -1)}
Canonical define: #define XPAR_X{driver_name}_{index}_BASEADDR {hex(base_addr)} #define XPAR_X{driver_name}_{index}_HIGHADDR {hex(base_addr + size -1)} | e.g.: reg: 2 Needed if the config structure has more than one base address in sequence (Check GIC node and its config structure for more info)
|
interrupts | Interrupt property format for a mapped interrupt in a system device tree looks like: <PPI/SPI interrupt_id trigger_type> Above Interrupt property format will be converted to generate the Baremetal Interrupt Property Format. Baremetal Interrupt Property format:
| Generated Baremetal Interrupt hex value | #define XPAR_{node_label_name}_INTERRUPTS {hex(derived_intr_format)} #define XPAR_FABRIC_{node_label_name}_INTR {hex(derived_intr_format)} (For PL peripherals) #define XPAR_{node_label_name}_INTR {hex(derived_intr_format)} (For ipipsu driver) Canonical define: #define XPAR_X{driver_name}_{index}_INTERRUPTS {hex(derived_intr_format)} #define XPAR_FABRIC_X{driver_name}_{index}_INTR {hex(derived_intr_format)}
| e.g. interrupts: 2 Needed when a peripheral can have multiple interrupts attached.
|
interrupt-parent | Adds the base address of the interrupt-parent (Usually the base address of GIC or INTC) | Base address of the interrupt-parent | #define XPAR_{node_label_name}_INTERRUPT_PARENT {hex(intr_parent_addr)}
Canonical define: #define XPAR_X{driver_name}_{index}_INTERRUPT_PARENT {hex(intr_parent_addr)} | Not Allowed |
child,required | Generates the sub nodes/arrays | A C array of child node's property values | #define XPAR_{node_label_name}_{j}_{<property>.upper()} {val} Canonical define: #define XPAR_X{driver_name}_{index}_{j}_{<property>.upper()} {val} |
Not Allowed |
Any other property (<property> or xlnx,<property>) | If the property value is of type
| the value will be used as it is | #define XPAR_{node_label_name}_{<property>.upper()} {string value}
Canonical define: #define XPAR_X{driver_name}_{index}_{<property>.upper()} {string value} | e.g. xlnx,num_slcr_addr: 2
|
| Base Address of the IP that this property depends on will be used | #define XPAR_{node_label_name}_{prop.upper()} {hex(base_addr of the new IP)}
Canonical define: #define XPAR_X{driver_name}_{index}_{prop.upper()} {hex(base_addr of the new IP)} | ||
| convert into hex and then the hex value will be used | #define XPAR_{node_label_name}_{<property>.upper()} {hex(int_value)}
Canonical define: #define XPAR_X{driver_name}_{index}_{<property>.upper()} {hex(int_value)} | ||
| List values will be put in the form of a C list, int value will be converted to hex, string value will be used as is. | #define XPAR_{node_label_name}_{<property>.upper()}_<x> {hex(int_value)}
Canonical define: #define XPAR_X{driver_name}_{index}_{<property>.upper()}_<x> {hex(int_value)} | ||
A Linux specific way to show certain properties. | Hex value kept at the end of the property value will be converted into int with base 16. | #define XPAR_{node_label_name}_{<property>.upper()}_<x> {hex(int_value)}
Canonical define: #define XPAR_X{driver_name}_{index}_{<property>.upper()}_<x> {hex(int_value)} |
Note: Do check the canonical macros coming in the xparameters.h. In the legacy flow, few drivers had defined the canonical macros in their own way. This has now been made uniform across the driver. it now follows XPAR_X<driver_name>_<instance_number>_<PROPERTY> syntax. Thus, the corresponding changes are now needed in the source files. In case the driver owner wants to explicitly have a name different than that of IP name inside Canonical definition “xparam_prefix” key has to be defined with that name. X<driver_name> will be replaced with the value of xparam_prefix in the above defined syntax.
Appendix D: A Comprehensive sample YAML for drivers
A comprehensive Sample YAML file for drivers (Data points are only for syntax related understanding and is not related to any IP in real):
# SPDX-License-Identifier: MIT
%YAML 1.2
---
title: Bindings for IPI controller available in the zynqMP and Versal platform
maintainers:
- ASDF GHJKL <asdf.ghjkl@domain.com>
- ZXCV VBNM <zxcv.vbnm@domain.com>
type: driver
device_type: ipi
properties:
compatible:
items:
OneOf:
- items:
- enum:
- xlnx,zynqmp-ipi-mailbox
- xlnx,psu-ipi-1.0
reg:
description: Standard reg property
ipi-bitmask:
description: foo0 bar0 abc0 xyz0
interrupts:
description: foo1 bar1 abc1 xyz1
interrupt-parent:
description: foo2 bar2 abc2 xyz2
num_slcr_addr:
description: foo3 bar3 abc3 xyz3
axistream-connected:
description: foo4 bar4 abc4 xyz4
child-required:
description: foo5 bar5 abc5 xyz5
ipi-id:
description: foo6 bar6 abc6 xyz6
max-intr-size:
description: foo7 bar7 abc7 xyz7
config:
- XIpiPsu_Config
required:
- compatible
- reg
- xlnx,ipi-bitmask
- interrupts
- interrupt-parent
- xlnx,num_slcr_addr: 2
- axistream-connected: phandle
- child,required:
- xlnx,ipi-bitmask
- xlnx,ipi-buf-index
optional:
- xlnx,ipi-id
additionalProperties:
- xlnx,max-intr-size
depends:
clockps: []
resetps: []
depends_libs:
libmetal: {}
examples:
xdrv1_selftest_example.c::
- reg
xdrv1_polltimeout_interrupt_example.c:
- dependency_files:
- xdrv1_flash_config.h
- interrupts
- compatible : xlnx,versal-wwdt-1.0
xdrv1_generic_interrupt_example.c:
- interrupts
|
A Sample Device Tree data for IP:
ipi3: mailbox@ff330000 {
status = "okay";
compatible = "xlnx,zynqmp-ipi-mailbox";
interrupt-parent = <&gic_a72>;
interrupts = <0x0 0x1e 0x4>;
reg = <0x0 0xff330000 0x0 0x20>;
xlnx,ipi-bitmask = <0x4>;
xlnx,ipi-id = <0x2>;
xlnx,ipi-buf-index = <0x2>;
xlnx,ip-name = "psv_ipi";
xlnx,num_slcr_addr = <0x00000006 0x00000000>;
xlnx,max-intr-size = <0x20>;
xlnx,name = "CIPS_0_pspmc_0_psv_ipi_0";
axistream-connected = <&axi_dma_0>;
phandle = <0x6d>;
ipi0_0: child@0 {
xlnx,ipi-bitmask = <0x4>;
xlnx,ipi-rsp-msg-buf = <0xff3f04a0>;
xlnx,ipi-id = <0x2>;
xlnx,ipi-buf-index = <0x2>;
xlnx,ipi-req-msg-buf = <0xff3f0480>;
phandle = <0xe1>;
};
ipi0_1: child@1 {
xlnx,ipi-bitmask = <0x8>;
xlnx,ipi-rsp-msg-buf = <0xff3f04e0>;
xlnx,ipi-id = <0x3>;
xlnx,ipi-buf-index = <0x3>;
xlnx,ipi-req-msg-buf = <0xff3f04c0>;
};
}
axi_dma_0: axi_dma@a4050000 {
xlnx,num-s2mm-channels = <1>;
xlnx,ip-name = "axi_dma";
reg = <0x0 0xa4050000 0x0 0x10000>;
xlnx,s2mm-burst-size = <16>;
}; |
Generated Config structure File:
#include "xipipsu.h"
XIpiPsu_Config XIpiPsu_ConfigTable[] __attribute__ ((section (".drvcfg_sec"))) = {
{
"xlnx,zynqmp-ipi-mailbox", /* compatible */
0xff330000, /* reg */
0x4, /* xlnx,ipi-bitmask */
0x401e, /* interrupts */
0xf9000000, /* interrupt-parent */
0x600000000, /* xlnx,num_slcr_addr */
0xa4050002, /* axistream-connected */
{
{
4, /* xlnx,ipi-bitmask */
2 /* xlnx,ipi-buf-index */
},
{
8, /* xlnx,ipi-bitmask */
3 /* xlnx,ipi-buf-index */
}
},
0x2 /* xlnx,ipi-id */
},
{
NULL
}
};
|
Generated xparameters.h for this driver:
/* Definitions for peripheral IPI3 */
#define XPAR_IPI3_COMPATIBLE xlnx,zynqmp-ipi-mailbox
#define XPAR_IPI3_BASEADDR 0xff330000
#define XPAR_IPI3_HIGHADDR 0xff33001f
#define XPAR_IPI3_IPI_BITMASK 0x4
#define XPAR_IPI3_INTR 0x3e
#define XPAR_IPI3_INTERRUPTS 0x401e
#define XPAR_IPI3_INTERRUPT_PARENT 0xf9000000
#define XPAR_IPI3_IPI_NUM_SLCR_ADDR 0x600000000
#define XPAR_IPI3_0_AXISTREAM-CONNECTED 0xa4050002
#define XPAR_IPI3_0_IPI_BITMASK 0x4
#define XPAR_IPI3_0_IPI_BUF_INDEX 0x2
#define XPAR_IPI3_1_IPI_BITMASK 0x8
#define XPAR_IPI3_1_IPI_BUF_INDEX 0x3
#define XPAR_IPI3_MAX_INTR_SIZE 0x20
/* Canonical definitions for peripheral IPI3 */
#define XPAR_XIPIPSU_3_BASEADDR 0xff330000
#define XPAR_XIPIPSU_3_HIGHADDR 0xff33001f
#define XPAR_XIPIPSU_3_COMPATIBLE xlnx,zynqmp-ipi-mailbox
#define XPAR_XIPIPSU_3_IPI_BITMASK 0x4
#define XPAR_XIPIPSU_3_INTR 0x3e
#define XPAR_XIPIPSU_3_INTERRUPTS 0x401e
#define XPAR_XIPIPSU_3_INTERRUPT_PARENT 0xf9000000
#define XPAR_XIPIPSU_3_IPI_NUM_SLCR_ADDR 0x600000000
#define XPAR_XIPIPSU_3_AXISTREAM-CONNECTED 0xa4050002
#define XPAR_XIPIPSU_3_MAX_INTR_SIZE 0x20 |
Note: The Canonical entries follow XPAR_X<driver_name>_<instance_number>_<PROPERTY> syntax.
Appendix E: A Driver example in the Peripheral Test App
How to include a Driver example in the Peripheral Test App:
Changes in data folder of the driver:
Add a tapp section in the <driver>.yaml as mentioned earlier under "Porting a driver" Section. In existing flow, <driver>_tapp.tcl used to facilitate the same.
Sample "tapp" section in a <driver>.yaml contains:
tapp: x<driver>_selftest_example.c: declaration: X<Driver>_SelfTestExample x<driver>_generic_interrupt_example.c: declaration: X<Driver>_IntrExample hwproperties: - interrupts
All the function declarations mentioned in the above tapp section must be available in the <driver>_header.h file.
For Self Tests, example function declaration contains only one argument i.e BaseAddress
For other example functions, declaration contains two arguments i.e instance name and BASEADDRESS
Sample changes:
--- a/XilinxProcessorIPLib/drivers/csudma/data/csudma.yaml
+++ b/XilinxProcessorIPLib/drivers/csudma/data/csudma.yaml
@@ -39,4 +39,13 @@ examples:
- reg
xcsudma_selftest_example.c:
- reg
+
+tapp:
+ xcsudma_selftest_example.c:
+ declaration: XCsuDma_SelfTestExample
+ xcsudma_intr_example.c:
+ declaration: XCsuDma_IntrExample
+ hwproperties:
+ - interrupts
+... |
--- a/XilinxProcessorIPLib/drivers/csudma/data/csudma_header.h
+++ b/XilinxProcessorIPLib/drivers/csudma/data/csudma_header.h
@@ -11,6 +11,10 @@
#include "xil_assert.h"
#include "xstatus.h"
+#ifdef SDT
+int XCsuDma_SelfTestExample(UINTPTR BaseAddress);
+int XCsuDma_IntrExample(XCsuDma *CsuDmaInstance, UINTPTR BaseAddress);
+#else
int XCsuDma_SelfTestExample(u16 DeviceId);
#ifdef XPAR_SCUGIC_0_DEVICE_ID
int XCsuDma_IntrExample(XScuGic *IntcInstancePtr, XCsuDma *CsuDmaInstance,
@@ -22,3 +26,4 @@ int XCsuDma_IntrExample(XIntc *IntcInstancePtr, XCsuDma *CsuDmaInstance,
#endif
#endif
#endif
+#endif
|
Changes in example folder of the driver:
Please refer to the "Changes in examples Folder" Under "Porting a driver" section
Appendix F: CMAKE_MACHINE and CMAKE_SYSTEM_PROCESSOR
To differentiate a platform or a processor specific piece of code in cmake, CMAKE_MACHINE and CMAKE_SYSTEM_PROCESSOR variable can be used.
List of available CMAKE_MACHINEs:
Platform NameCMAKE_MACHINEZYNQ ZynqZynq Ultrscale+ZynqMPVersalVersal
Kintex or Microblaze boards
Name depends on family of the design.
It can be kintex7, kintexu, virtex etc
List of available CMAKE_SYSTEM_PROCESSORs:
Processor NameCMAKE_SYSTEM_PROCESSOR (value)Soft MicroblazemicroblazePMU Microblazepmu_microblazePSM MicroblazemicroblazePLM Microblazeplm_microblazeCortexA53
cortexa53
CortexR5cortexr5CortexA72cortexa72CortexA9cortexa9
Appendix G: Apps: Hardware Dependency and Linker Script Generation
Generation of <Component>Example.cmake file under src folder and the linker script
Significance:
It is an auto generated file inside library and application's src folders. It contains the required peripheral info in cmake format.
The Lopper backends deduce the required peripherals data mentioned under "depends" section from system device tree and keep them in these Example.cmake file.
The CMakeLists.txt kept under src folder then include these data into the Build System and generates the required Software level configurations.
For Applications:
This cmake also contains the available memory related info which will translate into the linker script.
This cmake in conjunction with lscript.ld.in results into the final application's lscript.ld
Sample depends and linker sections:
depends:
emaclite:
- reg
- interrupts
axiethernet:
- reg
- interrupts
emacps:
- reg
- interrupts
tmrctr:
- reg
- interrupts
ttcps:
- reg
- interrupts
# Linker Constraints belong only to Application.
linker_constraints:
stack: 0xA000
heap: 0xA000 |
Sample Lwip_echo_serverExample.cmake:
set(AXIETHERNET_NUM_DRIVER_INSTANCES "")
set(EMACLITE_NUM_DRIVER_INSTANCES "")
set(EMACPS_NUM_DRIVER_INSTANCES "CIPS_0_pspmc_0_psv_ethernet_0;CIPS_0_pspmc_0_psv_ethernet_1")
set(EMACPS0_PROP_LIST "0xff0c0000;0x4038")
list(APPEND TOTAL_EMACPS_PROP_LIST EMACPS0_PROP_LIST)
set(EMACPS1_PROP_LIST "0xff0d0000;0x403a")
list(APPEND TOTAL_EMACPS_PROP_LIST EMACPS1_PROP_LIST)
set(TMRCTR_NUM_DRIVER_INSTANCES "")
set(TTCPS_NUM_DRIVER_INSTANCES "")
set(DDR axi_noc_0)
set(axi_noc_DDR_CH_1 "0x50000000000;0x200000000")
set(axi_noc_1 "0x800000000;0x180000000")
set(axi_noc_0 "0x0;0x80000000")
set(psv_ocm_0 "0xfffc0000;0x40000")
set(TOTAL_MEM_CONTROLLERS "axi_noc_DDR_CH_1;axi_noc_1;axi_noc_0;psv_ocm_0")
set(MEMORY_SECTION "MEMORY
{
psv_pmc_ram_psv_pmc_ram : ORIGIN = 0xF2000000, LENGTH = 0x20000
psv_r5_0_atcm_global_MEM_0 : ORIGIN = 0xFFE00000, LENGTH = 0x40000
psv_r5_1_atcm_global_MEM_0 : ORIGIN = 0xFFE90000, LENGTH = 0x10000
psv_r5_1_btcm_global_MEM_0 : ORIGIN = 0xFFEB0000, LENGTH = 0x10000
axi_noc_DDR_CH_1 : ORIGIN = 0x50000000000, LENGTH = 0x200000000
axi_noc_1 : ORIGIN = 0x800000000, LENGTH = 0x180000000
axi_noc_0 : ORIGIN = 0x0, LENGTH = 0x80000000
psv_ocm_0 : ORIGIN = 0xfffc0000, LENGTH = 0x40000
}")
set(STACK_SIZE 0xa000)
set(HEAP_SIZE 0xa000) |
Sample lscript.ld.in file:
_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : @STACK_SIZE@;
_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : @HEAP_SIZE@;
_EL0_STACK_SIZE = DEFINED(_EL0_STACK_SIZE) ? _EL0_STACK_SIZE : 1024;
_EL1_STACK_SIZE = DEFINED(_EL1_STACK_SIZE) ? _EL1_STACK_SIZE : 2048;
_EL2_STACK_SIZE = DEFINED(_EL2_STACK_SIZE) ? _EL2_STACK_SIZE : 1024;
@MEMORY_SECTION@
/* Specify the default entry point to the program */
ENTRY(_vector_table)
/* Define the sections, and where they are mapped in memory */
SECTIONS
{
.text : {
KEEP (*(.vectors))
*(.boot)
*(.text)
*(.text.*)
*(.gnu.linkonce.t.*)
*(.plt)
*(.gnu_warning)
*(.gcc_execpt_table)
*(.glue_7)
*(.glue_7t)
*(.ARM.extab)
*(.gnu.linkonce.armextab.*)
} > @DDR@
.init (ALIGN(64)) : {
KEEP (*(.init))
} > @DDR@
.fini (ALIGN(64)) : {
KEEP (*(.fini))
} > @DDR@
.interp : {
KEEP (*(.interp))
} > @DDR@
.note-ABI-tag : {
KEEP (*(.note-ABI-tag))
} > @DDR@
.rodata : {
. = ALIGN(64);
__rodata_start = .;
*(.rodata)
*(.rodata.*)
*(.gnu.linkonce.r.*)
__rodata_end = .;
} > @DDR@
.rodata1 : {
. = ALIGN(64);
__rodata1_start = .;
*(.rodata1)
*(.rodata1.*)
__rodata1_end = .;
} > @DDR@
.sdata2 : {
. = ALIGN(64);
__sdata2_start = .;
*(.sdata2)
*(.sdata2.*)
*(.gnu.linkonce.s2.*)
__sdata2_end = .;
} > @DDR@
.sbss2 : {
. = ALIGN(64);
__sbss2_start = .;
*(.sbss2)
*(.sbss2.*)
*(.gnu.linkonce.sb2.*)
__sbss2_end = .;
} > @DDR@
.data : {
. = ALIGN(64);
__data_start = .;
*(.data)
*(.data.*)
*(.gnu.linkonce.d.*)
*(.jcr)
*(.got)
*(.got.plt)
__data_end = .;
} > @DDR@
.data1 : {
. = ALIGN(64);
__data1_start = .;
*(.data1)
*(.data1.*)
__data1_end = .;
} > @DDR@
.got : {
*(.got)
} > @DDR@
.got1 : {
*(.got1)
} > @DDR@
.got2 : {
*(.got2)
} > @DDR@
.ctors : {
. = ALIGN(64);
__CTOR_LIST__ = .;
___CTORS_LIST___ = .;
KEEP (*crtbegin.o(.ctors))
KEEP (*(EXCLUDE_FILE(*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
__CTOR_END__ = .;
___CTORS_END___ = .;
} > @DDR@
.dtors : {
. = ALIGN(64);
__DTOR_LIST__ = .;
___DTORS_LIST___ = .;
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE(*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
__DTOR_END__ = .;
___DTORS_END___ = .;
} > @DDR@
.fixup : {
__fixup_start = .;
*(.fixup)
__fixup_end = .;
} > @DDR@
.eh_frame : {
*(.eh_frame)
} > @DDR@
.eh_framehdr : {
__eh_framehdr_start = .;
*(.eh_framehdr)
__eh_framehdr_end = .;
} > @DDR@
.gcc_except_table : {
*(.gcc_except_table)
} > @DDR@
.mmu_tbl0 (ALIGN(4096)) : {
__mmu_tbl0_start = .;
*(.mmu_tbl0)
__mmu_tbl0_end = .;
} > @DDR@
.mmu_tbl1 (ALIGN(4096)) : {
__mmu_tbl1_start = .;
*(.mmu_tbl1)
__mmu_tbl1_end = .;
} > @DDR@
.mmu_tbl2 (ALIGN(4096)) : {
__mmu_tbl2_start = .;
*(.mmu_tbl2)
__mmu_tbl2_end = .;
} > @DDR@
.ARM.exidx : {
__exidx_start = .;
*(.ARM.exidx*)
*(.gnu.linkonce.armexidix.*.*)
__exidx_end = .;
} > @DDR@
.preinit_array : {
. = ALIGN(64);
__preinit_array_start = .;
KEEP (*(SORT(.preinit_array.*)))
KEEP (*(.preinit_array))
__preinit_array_end = .;
} > @DDR@
.init_array : {
. = ALIGN(64);
__init_array_start = .;
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
__init_array_end = .;
} > @DDR@
.fini_array : {
. = ALIGN(64);
__fini_array_start = .;
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array))
__fini_array_end = .;
} > @DDR@
.drvcfg_sec : {
. = ALIGN(8);
__drvcfgsecdata_start = .;
KEEP (*(.drvcfg_sec))
__drvcfgsecdata_end = .;
__drvcfgsecdata_size = __drvcfgsecdata_end - __drvcfgsecdata_start;
} > @DDR@
.ARM.attributes : {
__ARM.attributes_start = .;
*(.ARM.attributes)
__ARM.attributes_end = .;
} > @DDR@
.sdata : {
. = ALIGN(64);
__sdata_start = .;
*(.sdata)
*(.sdata.*)
*(.gnu.linkonce.s.*)
__sdata_end = .;
} > @DDR@
.sbss (NOLOAD) : {
. = ALIGN(64);
__sbss_start = .;
*(.sbss)
*(.sbss.*)
*(.gnu.linkonce.sb.*)
. = ALIGN(64);
__sbss_end = .;
} > @DDR@
.tdata : {
. = ALIGN(64);
__tdata_start = .;
*(.tdata)
*(.tdata.*)
*(.gnu.linkonce.td.*)
__tdata_end = .;
} > @DDR@
.tbss : {
. = ALIGN(64);
__tbss_start = .;
*(.tbss)
*(.tbss.*)
*(.gnu.linkonce.tb.*)
__tbss_end = .;
} > @DDR@
.bss (NOLOAD) : {
. = ALIGN(64);
__bss_start__ = .;
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(64);
__bss_end__ = .;
} > @DDR@
_SDA_BASE_ = __sdata_start + ((__sbss_end - __sdata_start) / 2 );
_SDA2_BASE_ = __sdata2_start + ((__sbss2_end - __sdata2_start) / 2 );
/* Generate Stack and Heap definitions */
.heap (NOLOAD) : {
. = ALIGN(64);
_heap = .;
HeapBase = .;
_heap_start = .;
. += _HEAP_SIZE;
_heap_end = .;
HeapLimit = .;
} > @DDR@
.stack (NOLOAD) : {
. = ALIGN(64);
_el3_stack_end = .;
. += _STACK_SIZE;
__el3_stack = .;
_el2_stack_end = .;
. += _EL2_STACK_SIZE;
. = ALIGN(64);
__el2_stack = .;
_el1_stack_end = .;
. += _EL1_STACK_SIZE;
. = ALIGN(64);
__el1_stack = .;
_el0_stack_end = .;
. += _EL0_STACK_SIZE;
. = ALIGN(64);
__el0_stack = .;
} > @DDR@
_end = .;
} |
Known Gaps (2024.2)
Generic Workflow Gaps | Baremetal/RTOS | Missing Support
| Support for armclang, armcc and IAR compilers. |
---|---|---|---|
Few Kria SOM specific applications like Image Selector and Image Recovery are not yet ported. | |||
Support for ZynqUS+ A53 32-bit | |||
| No support to build baremetal hypervisor guest | ||
Missing Features | Some of the standalone BSP configurations | ||
Each TTC timer is supposed to have three timer instances. This flow is treating them as a single instance. The last two timer instances for each timer are not available for use. | |||
Running the peripheral test with UART tests included may lead to junk characters on console for UART specific tests. | |||
|
| The default address selection in the linker may vary in Vitiis Unified(SDT flow) in comparison to XSCT flow in some cases. Workaround is to go to linker script and select the memory to which user wants to compile his application | |
|
| When a DDR region in Versal and VersalNet have multiple memory segments in it with a hole in between those, the baremetal SW stack isnt considering the holes and ended up generating lowest of low address and highest of high address as a single region. Due to this, the corresponding macros in xparameters.h, config table in memory test template app and linker gen will show it as single DDR memory segement in that DDR region. Workaround for memory test: Manually update the config table in memory test _g.c file to reflect the actual DDR segements and perform memory test accordingly. Workaround for linker: Update linker source manually to reflect correct DDR segments in it. | |
Linux | Missing Support
| User may see some extra IP related properties (not available in the respective linux dt-binding) within the device node in the Lopper generated Linux device tree. | |
Support for Linux Device tree generation for Microblaze, Microblaze RISC-V platforms. Only supports Zynq, ZynqMP and Versal (or Versal-like) platforms. | |||
TSN Subsystem is not supported in SDT flow | |||
Isolation | Missing Support | Isolation support for ZynqMP platforms. Only supports Versal platforms. | |
SDTGen Specific Gaps | Overall | Missing Support | Support for HBM memory |
Vitis Unified specific Gaps | Baremetal/RTOS | Missing Support | The AMD Vitis™ unified software still takes design file (.xsa) as an user input for some of the debugging purposes. It internally converts the .xsa into system device tree and use the generated SDT directory for rest of the build flow. Paths with spaces arent supported in the flow. Please ensure that there are no spaces in the path. |
Petalinux/Yocto specific Gaps | Linux | Missing Support | Linux support for Microblaze. Only supports Zynq, ZynqMP and Versal (or Versal-like) platforms. |
Vivado Specific Gaps | Baremetal | Missing Support | Vivado generates PLM which is part of the exported hardware platform in the form of a PDI for versal/versal-like platforms. These are still generated using the Legacy flow. |
Known Gaps (2024.1)
Generic Workflow Gaps | Baremetal/RTOS | Missing Support
| Support for armclang, armcc and IAR compilers. |
---|---|---|---|
Few Kria SOM specific applications like Image Selector and Image Recovery are not yet ported. | |||
Support for XilPKI library | |||
Support for ZynqUS+ A53 32-bit | |||
Support for Libmetal and OpenAM FreeRTOS apps | |||
No support to build baremetal hypervisor guest | |||
Support of OpenAMP for Zynq Cortex-A9 and Versal, ZU+ R5_1 processsors | |||
Missing Features | Some of the standalone BSP configurations | ||
Each TTC timer is supposed to have three timer instances. This flow is treating them as a single instance. The last two timer instances for each timer are not available for use. | |||
Running the peripheral test with UART tests included may lead to junk characters on console for UART specific tests. | |||
Linux | Missing Support
| User may see some extra IP related properties (not available in the respective linux dt-binding) within the device node in the Lopper generated Linux device tree. | |
Support for Linux Device tree generation for Microblaze, Microblaze RISC-V and Zynq platforms. Only supports ZynqMP and Versal (or Versal-like) platforms. | |||
Support for Linux use cases of Video IPs. Device nodes generated for Video IPs are not compatible with existing Linux bindings. | |||
Support for Linux AIE drivers | |||
Support for XRT and ZOCL | |||
Isolation | Missing Support | Isolation support for ZynqMP platforms. Only supports Versal platforms. | |
SDTGen Specific Gaps | Overall | Missing Support | Support for HBM memory |
Vitis Unified specific Gaps | Baremetal/RTOS | Missing Support | The AMD Vitis™ unified software still takes design file (.xsa) as an user input for some of the debugging purposes. It internally converts the .xsa into system device tree and use the generated SDT directory for rest of the build flow. |
Linux | Missing Support | Support for Linux Device Tree generation through UI | |
Petalinux/Yocto specific Gaps | Linux | Missing Support | Linux support for Microblaze, Zynq. Only supports ZynqMP and Versal (or Versal-like) platforms. |
Vivado Specific Gaps | Baremetal | Missing Support | Vivado generates PLM which is part of the exported hardware platform in the form of a PDI for versal/versal-like platforms. These are still generated using the Legacy flow. |
Known Gaps (2023.2)
Incremental Build for the BSP and the application is not yet available. In case of any source change in BSP, the whole BSP has to be compiled again and the same goes for the application workspace.
Hardware related errors for a template application or a library have to be explicitly added via cmake files. Earlier it was handled in tcl but the same is not translated to yaml.
The BSP workspace and the Application Workspaces are not completely relocatable. Build folders (for BSP, it is <bsp_ws>/libsrc/build_configs and for application, it is <app_ws>/build) have to be deleted before relocating.
All the microblaze designs are expected to have "microblaze" in their processor names. This issue will be resolved in the coming release.
In case of Embeddedsw repo having the multiple versions of the same component (say csudma_v3_5 and csudma_v3_6 as it used to be in the legacy flow), the build system will pick the first source sequenced alphabetically by default (csudma_v3_5 in this case). The source can later be changed (csudma_v_6 path can be chosen among the available options) once the BSP is created.
Not all drivers/libraries/apps/examples in embeddedsw are ported to this new flow. Incase if a driver/library/app isnt ported, it wouldn't be considered while creating BSP/application and the example wouldnt showup for selection in GUI.
IAR/armcc compilers are not supported
ZynqUS+ A53 32-bit is not supported
64 bit Soft Microblaze is not supported
Few of the BSP/Library configuration settings have been relocated and some of them have been removed during this transition to new SDT based flow.
Libmetal and OpenAM FreeRTOS apps arent supported yet and support of OpenAMP for Zynq Cortex-A9 and Versal, ZU+ R5_1 processsors isnt present yet in this new flow
STDIN and STDOUT configuration selection for FreeRTOS BSP isnt present.
Memory region names are slightly modified in the linker script from the legacy flow. Linear_qspi/EMC memories are currently missing in the linker script.
LwIP library can't be added to a FreeRTOS platform BSP settings. Workaround is to create a FreeRTOS lwIP template application and place the files needed for the custom application in the source folder. Default lwIP FreeRTOS apps work as expected and are unaffected by this issue.
Related content
© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy