Listening for SEU Detection - 2021.1 English

Xilinx Standalone Library Documentation OS and Libraries Document Collection (UG643)

Document ID
UG643
Release Date
2021-06-16
Version
2021.1 English

The XilSEM library can maintain SEU mitigation operation without any need for the user design to listen for SEU detections. However, it may be desired maintain an event log, or take design or system level actions in response to an event. To receive notification of error detections in Configuration RAM or NPI Registers, the following steps are required of an RPU or APU application:

  • IPI Initialization
    • IPI Configuration and connection with GIC
    • IPI callback registration with IPI interrupt handler (CRAM and NPI)
  • Register error event notification (CRAM and NPI)
  • Check global variables that hold notified event information (CRAM and NPI)

For IPI configuration and connection with GIC, use the following code snippet:

static XStatus IpiConfigure(XIpiPsu * IpiInst, XScuGic * GicInst)
{
	int Status = XST_FAILURE;
	XIpiPsu_Config *IpiCfgPtr;

	if (NULL == IpiInst) {
		goto END;
	}

	if (NULL == GicInst) {
		xil_printf("%s ERROR GIC Instance is NULL\n", __func__);
		goto END;
	}

	/* Look Up the config data */
	IpiCfgPtr = XIpiPsu_LookupConfig(IPI_TEST_CHANNEL_ID);
	if (NULL == IpiCfgPtr) {
		Status = XST_FAILURE;
		xil_printf("%s ERROR in getting CfgPtr\n", __func__);
		goto END;
	}
	/* Init with the Cfg Data */
	Status = XIpiPsu_CfgInitialize(IpiInst, IpiCfgPtr, \
			IpiCfgPtr->BaseAddress);
	if (XST_SUCCESS != Status) {
		xil_printf("%s ERROR #%d in configuring IPI\n", __func__,
				Status);
		goto END;
	}

	/* Clear Any existing Interrupts */
	XIpiPsu_ClearInterruptStatus(IpiInst, XIPIPSU_ALL_MASK);

	Status = XScuGic_Connect(GicInst, IPI_INT_ID,
			(Xil_ExceptionHandler)IpiIrqHandler, IpiInst);
	if (XST_SUCCESS != Status) {
		xil_printf("%s ERROR #%d in GIC connect\n", __func__, Status);
		goto END;
	}
	/* Enable IPI interrupt at GIC */
	XScuGic_Enable(GicInst, IPI_INT_ID);

END:
	return Status;
}

For IPI callback registration with IPI handler, see the following code snippet:

XStatus IpiRegisterCallback(XIpiPsu *const IpiInst, const u32 SrcMask,
		IpiCallback Callback)
{
	ssize_t idx;

	if (!Callback)
		return XST_INVALID_PARAM;

	/* Get index into IpiChannels array */
	idx = ipimask2idx(SrcMask);
	if (idx < 0)
		return XST_INVALID_PARAM;

	/* Check if callback is already registered, return failure if it is */
	if (IpiCallbacks[idx])
		return XST_FAILURE;

	/* Entry is free, register callback */
	IpiCallbacks[idx] = Callback;

	/* Enable reception of IPI from the SrcMask/CPU */
	XIpiPsu_InterruptEnable(IpiInst, SrcMask);

	return XST_SUCCESS;
}

For IPI callback to receive event messages for CRAM, see the following code snippet:

/*Global variables to hold the event count when notified*/
u8 EventCnt_UnCorEcc = 0U;
u8 EventCnt_Crc = 0U;
u8 EventCnt_CorEcc = 0U;
u8 EventCnt_IntErr = 0U;

void XSem_IpiCallback(XIpiPsu *const InstancePtr)
{
	int Status;
	u32 Payload[PAYLOAD_ARG_CNT] = {0};

	Status = XIpiPsu_ReadMessage(XSem_IpiGetInst(),SRC_IPI_MASK,Payload,\
			PAYLOAD_ARG_CNT, XIPIPSU_BUF_TYPE_MSG);
	if (Status != XST_SUCCESS) {
		xil_printf("ERROR #%d while reading IPI buffer\n", Status);
		return;
	}

	if ((XSEM_EVENT_ERROR == Payload[0]) && \
			(XSEM_NOTIFY_CRAM == Payload[1])) {
		if (XSEM_EVENT_CRAM_UNCOR_ECC_ERR == Payload[2]) {
			EventCnt_UnCorEcc++;
		} else if (XSEM_EVENT_CRAM_CRC_ERR == Payload[2]) {
			EventCnt_Crc++;
		} else if (XSEM_EVENT_CRAM_INT_ERR == Payload[2]) {
			EventCnt_IntErr++;
		} else if (XSEM_EVENT_CRAM_COR_ECC_ERR == Payload[2]) {
			EventCnt_CorEcc++;
		} else {
			xil_printf("%s Some other callback received: %d:%d:%d\n",
					__func__, Payload[0], \
					Payload[1], Payload[2]);
		}
	} else {
		xil_printf("%s Some other callback received: %d\n", \
				__func__, Payload[0]);
	}
}
Note: In the above code snippets, global counters are incremented when an event has been notified. It is up to the user to define and implement any desired design and system response.

For IPI callback to receive event messages for NPI, see the following code snippet:

/*Global variables to hold the event count when notified*/
u8 NPI_CRC_EventCnt = 0U;
u8 NPI_INT_EventCnt = 0U;

void XSem_IpiCallback(XIpiPsu *const InstancePtr)
{
	int Status;
	u32 Payload[PAYLOAD_ARG_CNT] = {0};

	Status = XIpiPsu_ReadMessage(&IpiInst, SRC_IPI_MASK, Payload, PAYLOAD_ARG_CNT,
			XIPIPSU_BUF_TYPE_MSG);
	if (Status != XST_SUCCESS) {
		xil_printf("ERROR #%d while reading IPI buffer\n", Status);
		return;
	}

	if ((XSEM_EVENT_ERROR == Payload[0]) && (XSEM_NOTIFY_NPI == Payload[1])) {
		if (XSEM_EVENT_NPI_CRC_ERR == Payload[2]) {
			NPI_CRC_EventCnt++;
		} else if (XSEM_EVENT_NPI_INT_ERR == Payload[2]) {
			NPI_INT_EventCnt++;
		} else {
			xil_printf("%s Some other callback received: %d:%d:%d\n",
					__func__, Payload[0], Payload[1], Payload[2]);
		}
	} else {
		xil_printf("%s Some other callback received: %d\n", __func__, Payload[0]);
	}
}

For IPI initialization (includes IPI configuration and connection with GIC, IPI callback registration with IPI handler), see the following code snippet:

XStatus IpiInit(XIpiPsu * InstancePtr, XScuGic * GicInst)
{
	int Status;

	Status = IpiConfigure(InstancePtr, GicInst);
	if (XST_SUCCESS != Status) {
		xil_printf("IpiConfigure() failed with error: %d\r\n",
				Status);
	}

	Status = IpiRegisterCallback(InstancePtr, SRC_IPI_MASK, \
			XSem_IpiCallback);
	return Status;
}
Note: CRAM and NPI should run one at time.

For register error event notification with CRAM, see the following code snippet:

XSem_Notifier Notifier = {
        .Module = XSEM_NOTIFY_CRAM,
        .Event = XSEM_EVENT_CRAM_UNCOR_ECC_ERR | XSEM_EVENT_CRAM_CRC_ERR | \
		 XSEM_EVENT_CRAM_INT_ERR | XSEM_EVENT_CRAM_COR_ECC_ERR,
	.Flag = 1U,
};
int Status;
Status = XSem_RegisterEvent(&IpiInst, &Notifier);
if (XST_SUCCESS == Status) {
	xil_printf("Success: Event registration \n\r");
} else {
	xil_printf("Error: Event registration failed \n\r");
	goto END;
}

For register error event notification with NPI, see the following code snippet:

XSem_Notifier Notifier = {
        .Module = XSEM_NOTIFY_NPI,
        .Event = XSEM_EVENT_NPI_CRC_ERR | XSEM_EVENT_NPI_INT_ERR,
	.Flag = 1U,
};

int Status;
Status = XSem_RegisterEvent(&IpiInst, &Notifier);
if (XST_SUCCESS == Status) {
	xil_printf("Success: Event registration \n\r");
} else {
	xil_printf("Error: Event registration failed \n\r");
	goto END;
}

To check global variables that holds the count of the notified event for CRAM, use the following code snippet:

if(EventCnt_UnCorEcc > 0){
	xil_printf("Uncorrectable error has been detected in CRAM\n\r");
}else if(EventCnt_Crc > 0){
	xil_printf("CRC error has been detected in CRAM\n\r");
} else if(EventCnt_CorEcc > 0){
	xil_printf("Correctable error has been detected and corrected in CRAM\n\r");
} else if(EventCnt_IntErr > 0){
	xil_printf("Internal error has occurred in CRAM\n\r");
}

To check global variables that holds the count of the notified event for NPI, use the following code snippet:

if(NPI_CRC_EventCnt > 0){
	xil_printf("CRC error has been detected in NPI\n\r");
}else if(NPI_INT_EventCnt > 0){
	xil_printf("Internal error has occurred in NPI\n\r");
}

An additional method exists for the user design to listen for SEU detections. Like the IPI method, it is not required. Unlike the IPI method which is used for RPU / APU user software, this method is used for PL user logic. It involves PMC_PL_GPO outputs of the PMC routed into the PL, supplying quick access to key event information. To access this feature, it must be enabled during XilSEM library configuration.

  • Bit 0: CRAM Correctable error
  • Bit 1: CRAM/NPI Uncorrectable error
  • Bit 2: CRAM/NPI/XilSEM Internal error
  • Bit 3: Reserved

The following diagram provides a XilSEM-centric view of information conduits to user software and user logic implemented in a Versal ACAP.

Figure 1. Flow of Information on a Versal ACAP using a XilSEM Subsystem