cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
talagam
Newbie
Newbie
712 Views
Registered: ‎12-07-2020

ZYNQ Interrupt - Callback events vs ISR (Interrupt Status Register) reading

Hello and thank you in advance,

I am writing a standalone program on ZYNQ-7000 with an Interrupt based system.

The system includes interrupts from UartPs and is connected to my costume handler function (through GIC->UartPs_handler->my_handler), so to my understanding when an interrupt is occurring instead of running the default UartPs_Handler it will run my_handler.

I used " Status = XScuGic_Connect(&XScugic_Device, UART_INT_IRQ_ID, (Xil_ExceptionHandler) XUartPs_InterruptHandler, (void *) &UartPs)" to connect the GIC to Xilinx UartPs_Handler.

and used "XUartPs_SetHandler(&UartPs, (XUartPs_Handler)My_Handler, &UartPs);" so it would run my costume function upon interrupt.

 

Now, to my understanding, the ISR register holds the flags of which interrupts has occurred; and the IMR holds the mask (which interrupts the user enabled).

the AND product of both will tell me which interrupt has occurred.

In addition to the ISR information, there are the CallBack events and the "Event" and "EventData" that are sent to the handler so that he could check if the right peripheral device raised the interrupt. includes events like: Data receiving done/Data transmission done/Receive error detected etc.

the ISR contains other flags (at least different by name) such as: RX FIFO full interrupt/RX FIFO empty interrupt/Framing error interrupt.....which i can enable or disable through the IMR.

 

My question is how and when do I use each one in my_handler function?
Is the "Data receiving done" event causes an interrupt each time a byte is entering the FIFO? just upon polling with XUartPs_Recv()?
If I set an RX FIFO threshold to raise an interrupt when the FIFO has X bytes and checking the ISR for that interrupt when raised, is it conceptually different?
Are the events causing my_handler to run the same as ISR interrupts? and to check which Event happened I will have to check Xilinx "callback event variable"?

Which one is preferred when reading UART in interrupt mode and what is the difference?

 

Thank you,

Tal 

0 Kudos
9 Replies
derekm_
Voyager
Voyager
676 Views
Registered: ‎01-16-2019

The Event/Event Data method is a higher level of abstraction than working with the UART hardware registers alone. Have a look through the related UART interrupt example code, and also look through the UART files in the BSP that you created in SDK/Vitis. (Let me know if you can't find that code and I will point you in the right direction).

You still set up the interrupts you want by using the hardware register bits, for example:

IntrMask = XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING | XUARTPS_IXR_OVER | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXFULL |XUARTPS_IXR_RXOVR;

XUartPs_SetInterruptMask(UartInstPtr, IntrMask);

 

But then you use your custom handler to 'filter' the events, as follows (this is just a guideline, not necessarily exactly what you should do):

void Handler(void *CallBackRef, u32 Event, unsigned int EventData)
{
	
	/* All of the data has been received */
	if (Event == XUARTPS_EVENT_RECV_DATA){
		XUartPs_Recv(UartInstPtr, RecvBuffer, BUFFER_SIZE);
	}

	if (Event == XUARTPS_EVENT_RECV_ERROR) {
		// Handle error in some way
	}
// etc
// etc
}

 

The EventData is usually the number of bytes in the send/Receive buffer, whichever is relevant.

The advantage of using the Event/EventData method is that the Xilinx drivers do things in the background for you like clearing interrupts. But you might not like having things happening in the background outside of your control, and in that case you could write a custom handler using just the uartps_hw.h file that does exactly what you want. There's more work in that, but even if you use the Event/EventData method I think you will have to experiment with various settings until you get it working the way you want it. Also if you write a custom driver, you might have to write it to do everything for the UART and not just interrupt handling. Mixing and matching your own driver code with Xilinx driver code might lead to difficult bugs. Depends on how much experience you have in writing drivers, I guess.

talagam
Newbie
Newbie
589 Views
Registered: ‎12-07-2020

Thank you very much, I appreciate the help.

I worked with xilinx example files of the UART Interrupt but I still don't fully understand how to figure out which "low level" interrupts are incorporated in the "high-level" callback events.
For example, if I set the "RX FULL" interrupt on the ISR, but in the handler I check for "Data receiving done" event, is it the same? I guess that not exactly; I'm guessing it incorporates a few interrupts to one high-level event, 
and the "Data receiving done" event will trigger the handler every time a byte enters the FIFO. Is it correct?

I am looking for information about when each event triggers my handler and what interrupts need to be active for each event, but can't find this information anywhere (I looked in the .h .c files of the UART and a few other files).

Much obliged.

Tal 

0 Kudos
talagam
Newbie
Newbie
572 Views
Registered: ‎12-07-2020

I have noticed that in the Vitis uart_interrupt_example the application handler does not contain any function that actually receives data (Handler attached).

before the While loop that "runs in the background" during interrupt handling there is a use of "XUartPs_Recv"  (in the example attached) but to my understanding it literally does nothing because the FIFO is empty and it operates in polling mode.

 is there any background use of "XUartPs_ReceiveBuffer" that receives bytes from the FIFO and stores them into the UART instance receive buffer (InstancePtr->ReceiveBuffer.NextBytePtr)?
Does the "ReceiveDataHandler" inside "xuartps_intr.c" gets called upon interrupt/event? where can I found out which one?

Thanks

0 Kudos
derekm_
Voyager
Voyager
557 Views
Registered: ‎01-16-2019

The first question I will try to help with is "how are the events triggered". For this, you can start with the xuartps_intr.c file in the BSP:

xuartps_intr.png

This contains the XUartPs_InterruptHandler() function, and you can map out all the events from that file.

For example, for interrupts XUARTPS_IXR_RXOVR | XUARTPS_IXR_RXEMPTY | XUARTPS_IXR_RXFULL, the ReceiveDataHandler() is called:

void XUartPs_InterruptHandler(XUartPs *InstancePtr)
{
	u32 IsrStatus;

	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * Read the interrupt ID register to determine which
	 * interrupt is active
	 */
	IsrStatus = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
				   XUARTPS_IMR_OFFSET);

	IsrStatus &= XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
				   XUARTPS_ISR_OFFSET);

	/* Dispatch an appropriate handler. */
	if((IsrStatus & ((u32)XUARTPS_IXR_RXOVR | (u32)XUARTPS_IXR_RXEMPTY |
			(u32)XUARTPS_IXR_RXFULL)) != (u32)0) {
		/* Received data interrupt */
		ReceiveDataHandler(InstancePtr);
	}
//etc

 

This reads the buffer, clears the interrupt(s), and sets the XUARTPS_EVENT_RECV_DATA event:

static void ReceiveDataHandler(XUartPs *InstancePtr)
{
	/*
	 * If there are bytes still to be received in the specified buffer
	 * go ahead and receive them. Removing bytes from the RX FIFO will
	 * clear the interrupt.
	 */
	 if (InstancePtr->ReceiveBuffer.RemainingBytes != (u32)0) {
		(void)XUartPs_ReceiveBuffer(InstancePtr);
	}

	 /* If the last byte of a message was received then call the application
	 * handler, this code should not use an else from the previous check of
	 * the number of bytes to receive because the call to receive the buffer
	 * updates the bytes ramained
	 */
	if (InstancePtr->ReceiveBuffer.RemainingBytes == (u32)0) {
		InstancePtr->Handler(InstancePtr->CallBackRef,
				XUARTPS_EVENT_RECV_DATA,
				(InstancePtr->ReceiveBuffer.RequestedBytes -
				InstancePtr->ReceiveBuffer.RemainingBytes));
	}

}

 

You can map out all the events in this way, but I'm not going to do it here!

 

0 Kudos
derekm_
Voyager
Voyager
546 Views
Registered: ‎01-16-2019

In truth, the Xilinx example isn't great, but it's only to get people up and running. Here's an example of a better handler. In this I expect 10 bytes from the host, and when my handler gets called, the 10 bytes will be in the buffer. I read them using XUartPs_Recv(), call my own command handler (not related to the UART), and send a response back to the host using XUartPs_Send().

void UartIntrHandler(void *CallBackRef, uint32_t event, uint32_t event_data)
 {

	// --------------------------------------------------------------------------------- //
	// event == XUARTPS_EVENT_RECV_DATA
	// 10 bytes should now have been received from the host.
	// --------------------------------------------------------------------------------- //
	if (event == XUARTPS_EVENT_RECV_DATA)
	{

		psGpOutSet(PS_GP_OUT6);	/// TEST SIGNAL: SET UART RX INTR


		/* === RX FROM HOST === */
		/* Get the data received from the host */
		XUartPs_Recv(p_XUart1PsInst, RxBuffer, UART_RX_BUFFER_SIZE);

		/* Call function to handle the data */
		handleCommand(RxBuffer, TxBuffer);

		/* === TX TO HOST === */
		/* Send the response data to the host.
		 * Note that XUartPs_Send() will enable some TX interrupts. */
		uint32_t n_bytes_sent = 0;
		n_bytes_sent = XUartPs_Send(p_XUart1PsInst, TxBuffer, UART_TX_BUFFER_SIZE);

		/* Assert if number of sent bytes is incorrect. */
		Xil_AssertVoid(n_bytes_sent == UART_TX_BUFFER_SIZE);


		psGpOutClear(PS_GP_OUT6); /// TEST SIGNAL: CLEAR UART RX INTR

	}

	// --------------------------------------------------------------------------------- //
	// event == XUARTPS_EVENT_SENT_DATA
	// This event should occur when data is sent back to the host.
	// --------------------------------------------------------------------------------- //
	else if (event == XUARTPS_EVENT_SENT_DATA)
	{

		// Set and clear a test signal
		psGpOutSet(PS_GP_OUT7);		/// TEST SIGNAL: SET UART TX INTR
		
		psGpOutClear(PS_GP_OUT7);	/// TEST SIGNAL: CLEAR UART TX INTR

	}



	// --------------------------------------------------------------------------------- //
	// Assert for any other event.
	// --------------------------------------------------------------------------------- //
	else if ( (event == XUARTPS_EVENT_RECV_TOUT)
			| (event == XUARTPS_EVENT_RECV_ERROR)
			| (event == XUARTPS_EVENT_MODEM)
			| (event == XUARTPS_EVENT_PARE_FRAME_BRKE)
			| (event == XUARTPS_EVENT_RECV_ORERR) )
	{
		Xil_AssertVoid(0U);
	}


}

 

Note that in this example, I only set up one interrupt (XUARTPS_IXR_RXOVR), and the system expects exactly 10 bytes, and sends 4 bytes back to the host.

static uint8_t RxBuffer [10];
static uint8_t TxBuffer [4];
...
...
...
XUartPs_SetHandler(p_XUart1PsInst, (XUartPs_Handler)UartIntrHandler, p_XUart1PsInst);
XUartPs_SetInterruptMask(p_XUart1PsInst, XUARTPS_IXR_RXOVR);
XUartPs_SetFifoThreshold(p_XUart1PsInst, UART_RX_BUFFER_SIZE);
XUartPs_SetOperMode(p_XUart1PsInst, XUARTPS_OPER_MODE_NORMAL);

 

 

0 Kudos
talagam
Newbie
Newbie
542 Views
Registered: ‎12-07-2020

Thank you so much, It helped a lot.

It raises another question for me, I appreciate your effort in answering.

After enabling the interrupts and connecting the GIC to the XUartPs_InterruptHandler() I use the function XUartPs_SetHandler() (in xuartps.h) so when an interrupt is received it will run my_handler, like i said before.
Does the XUartPs_InterruptHandler() runs anyways and then my_handler is run? and when it runs my_handler it passes the event that was just assigned in the XUartPs_InterruptHandler()?
if so I really just need to check which flag/event is up and pull my data through  "InstancePtr->ReceiveBuffer.NextBytePtr" which is where the uartps_handler receives the data to.

Or that XUartPs_SetHandler(), while changing InstancePtr->Handler, causes my_handler to run instead of XUartPs_InterruptHandler()?

If that's the case I will need to incorporate the  XUartPs_InterruptHandler() function in my_handler.

 

Thank's again

0 Kudos
derekm_
Voyager
Voyager
524 Views
Registered: ‎01-16-2019

"Does the XUartPs_InterruptHandler() runs anyways and then my_handler is run? and when it runs my_handler it passes the event that was just assigned in the XUartPs_InterruptHandler()?
if so I really just need to check which flag/event is up and pull my data through  "InstancePtr->ReceiveBuffer.NextBytePtr" which is where the uartps_handler receives the data to."

I think what happens is that the Xilinx function XUartPS_InterruptHandler() gets called first. Then it calls the appropriate (Xilinx) function, for example ReceiveDataHandler() for receive data. Then, your custom handler is called. This handler is the one you set using XUartPS_SetHandler(). In your handler, you use the Event (to filter what triggered the interrupt) and the EventData (number of bytes) to handle the interrupt. I don't think you need to use InstancePtr->ReceiveBuffer.NextBytePtr. Your custom interrupt handler should only be called when the required number of bytes are in the buffer, and then you can read them using XUartPs_Recv(). (Note that the example I posted might not be exactly what you need, as it expects a fixed number of bytes. I just set up one interrupt. You may need to set up other interrupts to get your application working the way you want, for example if you expect variable number of bytes to be received.)

I don't think you should "incorporate the  XUartPs_InterruptHandler() function in my_handler". That doesn't make sense to me. You really just need to connect your custom handler using XUartPs_SetHandler(), and then use Event/EventData to handle the event.

 

0 Kudos
talagam
Newbie
Newbie
516 Views
Registered: ‎12-07-2020

Thank you.

Doesn't the XUartPs_Recv() gets data from the FIFO that the XUartPs_InterruptHandler() clears?

using-->

InstancePtr->ReceiveBuffer.NextBytePtr[ReceivedCount] = XUartPs_ReadReg(InstancePtr->Config.BaseAddress, XUARTPS_FIFO_OFFSET);
or that this reg reading doesn't actually pulls data out of the FIFO? I assume the latter.

0 Kudos
derekm_
Voyager
Voyager
480 Views
Registered: ‎01-16-2019

Maybe... I haven't looked through the code in that much detail. Once I get a driver working at a higher level, I don't really care what happens at the lower level. There's never enough time!

0 Kudos