R5 Data Abort Exception Handling

This page describes tips to help improve R5 data abort exceptions for isolation use cases.

Table of Contents

Introduction

A data abort exception occurs when the CPU attempts to access (read or write) an address that is not supported for some reason such as the PL not being loaded or an isolation flow. For most designs this is not an issue as it’s typically a design flaw that is found during early testing and does not occur during normal operation. The concepts of this page apply to all the SoC CPUs but the R5 with bare metal is illustrated as the Cortex A CPUs typically run Linux such that changing exception handling is more complex and untypical.

Isolation Flow

Isolation allows specific memory ranges to be allowed by some AXI masters and denied to others. Default denial of addresses in Versal has changed from MPSoC. On Versal an access to an isolated address that is denied causes a data abort to the AXI master. Read and write operations behave differently with read operations being more catastrophic to the application. A read operation causes the read to be retried infinitely while a write operation does not. Users may want to deal with the data abort in a graceful manner to continue operating. This paragraph illustrates the minor changes to allow the R5 detect the denial and continue operating. This prototype was built and tested with Vitis 2022.2 on a VCK190 board.

Data Abort Exception Details

By default the AMD/Xilinx Standalone (bare metal) BSP handles a data abort for a read operation and then returns to the same data access in the application causing it to be retried indefinitely.

The following code snippet from asm_vectors.S shows that an application handler is called from the exception handler and then the link register has 8 subtracted from it which causes it to return to the offending data access to retry it again.

DataAbortHandler: /* Data Abort handler */ stmdb sp!,{r0-r3,r12,lr} /* state save from compiled code */ ldr r0, =DataAbortAddr sub r1, lr, #8 str r1, [r0] /* Stores instruction causing data abort */ bl DataAbortInterrupt /*DataAbortInterrupt :call C function here */ ldmia sp!,{r0-r3,r12,lr} /* state restore from compiled code */ subs pc, lr, #8 /* adjust return */

The following code snippet illustrates an alternative implementation that returns to the instruction following the offending data access.

DataAbortHandler: /* Data Abort handler */ stmdb sp!,{r0-r3,r12,lr} /* state save from compiled code */ ldr r0, =DataAbortAddr sub r1, lr, #8 str r1, [r0] /* Stores instruction causing data abort */ bl DataAbortInterrupt /*DataAbortInterrupt :call C function here */ ldmia sp!,{r0-r3,r12,lr} /* state restore from compiled code */ #if 0 subs pc, lr, #8 /* adjust return */ #else movs pc, lr /* don't retry the access, allow the app * to catch the error and move on */ #endif

Application Processing

An application that performs a data access is typically coded expecting the data access to be successful and does not allow for a data abort exception. When the exception processing is altered to not retry a failed data access the application must be aware since there is no valid data to be processed. Some applications may be able to easily handle this situation when invalid data can be easily detected. Other applications may require special processing to detect the data abort exception and data access failure.

The following code snippet illustrates the use of an application exception handler which is called by the low level exception handler. The application exception handler notifies the application of the data access failure such that application level processing can continue.

#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "xil_exception.h" #include "xil_io.h" u32 access_failure; void LocalAbortHandler(void *data) { /* Indicate an access failure that the app can read and detect the * failure. */ access_failure = 1; } int main() { u32 test = 0x1; u32 *test_ptr; u32 data; /* Setup a local custom data abort handler that gets called by the exception * processing and indicates that a data abort happened. */ Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_DATA_ABORT_INT, LocalAbortHandler, 0); /* Perform a good access to a good address where there should not be an access failure * and then verify it does not fail. */ access_failure = 0; test_ptr = &test; data = Xil_In32(test_ptr); if (access_failure) printf("data access failure\r\n"); else printf("data: %d\r\n", data); /* Perform a bad access to address 0xA000_0000 where there should be an access failure * and then verify it does fail. */ access_failure = 0; test_ptr = 0xA0000000; data = Xil_In32(test_ptr); if (access_failure) printf("data access failure at 0xA0000000\r\n"); else printf("data at 0xA000_0000: %d\r\n", data); return 0; }

 



 

© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy