There are a number of ways to do this so this is some I have used. Sorry I only have Zynq MMU code at hand to paste here. The ZynqMP should similar.
MMU Configuration
For direct AXI access you need to add the address space into the MMU. Unlike Vitis bare metal we run the MMU to support caching and relaxed memory alignment. We do not populate the MMU with any of the possible AXI bus interfaces to avoid opening windows to hardware that does not exist. As a result you need to add your IP address spaces.
You could make some calls or you can do what I normally do, provide an mmu.c file similar to this (this is for a Zynq, ie ARM) linked into my app:
/*
* MMU regions for our design
*/
#include <bsp.h>
#include <bsp/start.h>
#if __arm__
#include <bsp/arm-cp15-start.h>
#include <bsp/arm-a9mpcore-start.h>
BSP_START_DATA_SECTION static const arm_cp15_start_section_config
zynq_mmu_config_table[] = {
ARMV7_CP15_START_DEFAULT_SECTIONS,
{
.begin = 0x0080000U,
.end = 0x0100000U,
.flags = ARMV7_MMU_DEVICE
},
#if defined(RTEMS_SMP)
{
.begin = 0xffff0000U,
.end = 0xffffffffU,
.flags = ARMV7_MMU_DEVICE
},
#endif
{
.begin = 0xe0000000U,
.end = 0xe0200000U,
.flags = ARMV7_MMU_DEVICE
}, {
.begin = 0xf8000000U,
.end = 0xf9000000U,
.flags = ARMV7_MMU_DEVICE
}, {
.begin = 0x40000000U,
.end = 0x80000000U,
.flags = ARMV7_MMU_DEVICE
}
};
BSP_START_TEXT_SECTION void zynq_setup_mmu_and_cache(void)
{
uint32_t ctrl = arm_cp15_start_setup_mmu_and_cache(
ARM_CP15_CTRL_A,
ARM_CP15_CTRL_AFE | ARM_CP15_CTRL_Z
);
arm_cp15_start_setup_translation_table_and_enable_mmu_and_cache(
ctrl,
(uint32_t *) bsp_translation_table_base,
ARM_MMU_DEFAULT_CLIENT_DOMAIN,
&zynq_mmu_config_table[0],
RTEMS_ARRAY_SIZE(zynq_mmu_config_table)
);
}
#endif /* __arm__ */
Accessing the IP
I use FDT blobs I load with the PL image at runtime. We agree as a team on a register model and format to document the registers and that data generates RTL code with the address map built into the bitfile and a FDT DTS source compiled to a DTB. This avoids software/FPGA mismatches when registers move or get removed.
IP driver initialization asks rtems_fdt for the register address using a string path and that is saved as a pointer to access the register in the driver. For example:
uintptr_t address = 0;
int r = rtems_fdt_get_value(path, "reg", sizeof(address), &address);
if (r != 0) {
printf("fw:bitfile: invalid address path: %s: %s",
path, rtems_fdt_strerror(r));
return;
}
uint32_t val = *((volatile uint32_t*) address);
Note: I always load the bitfiles at runtime within RTEMS and do not use BOOT.BIN to hold them. I do not like the idea of needing to update boot blocks in flash in fielded equipment. We have Platform Manager (pm) support for ZynqMP to load bitfiles at runtime.