Thanks @artvvb. As the error message indicated, somehow the driver version for that one GPIO item was set to 4.9. I changed in on the drivers tab to 4.10 to match the others (on both the zynq_fsbl and the ps_cortexta9_0 items). Then a 'clean' and 'build' worked. I'll have to do some research to try to find out how this was changed because as mentioned, I certainly don't remember doing anythingn to explicitly change them.
Your help, as always, very much appreciated!
Have you looked at the collection of documentation about the AXI Interrupt Controller?
It looks like this documentation... 'Describes the AXI Interrupt Controller (AXI INTC) core which receives multiple interrupt inputs from peripheral devices and merges them to a single interrupt output to the system processor.'
The PG099 document has a nice feature list. Perhaps start with that doc if you haven't already read it.
1 & 2 - boy do I feel stupid.
3 - thanks! I always wondered where this was. Your link and comments helped me understand.
I should have also noted I found it odd that the hardware worked anyway and I could interact with the Zybo switches, buttons, and leds even with the critical warnings.
I'm still struggling to understand the intended function. Will the PS be driving the behavior (a timer - or some other mechanism - in the PS that interacts with the PL to cause an ADC conversion and filter and then reads the filtered value)? Will the PL be driving the behavior (a timer running in the PL that causes an ADC conversion and filter and then interrupts the PS app to do something with the filtered value)?
Regarding memory mapped peripherals - I learned a lot from this article (series) about making an Axi-lite memory mapped peripheral.
hey @artvvb - I must have posted my 'language templates' response at the same time as you! Thank you for your reply though! :-)
Interesting suggestion. I certainly wasn't aware of that but it could prove informative...
@artvvb - thanks for the aside response. I thought I commented on it just after you posted but likely never hit the 'submit' reply button. I don't get to do a lot of designs (unfortunately) right now but when I am making something I hope to turn into IP, I usually start with a RTL module like you mentioned and then turn it into IP after it seems to be working correctly as an RTL module.
Wow, I totally missed this when looking at UG480. in my design XADC is instantiated on the PL side but I was using the XAdcPs drivers (I should have suspected something with the 'Ps' in the name). As always, your help greatly appreciated. I'll review the SysMon v7.8 and go that route since that makes more sense with my design.
Boy did I sorely misunderstand the intent of the Xilinx code related to interrupt processing on the Uarts. Here's what I learned through debugging (though I still see a few odd things with the debugger). Again, I imagine most people figured this out on their own but just in case someone is reading in the future, maybe they find this useful. Much of the code is similar or identical to what is in the example interrupt application, but I may have formatted it differently or made minor changes (to the interrupt mask, for example).
At the top of my file...
#define TEST_BUFFER_SIZE 12
static u8 DebugRecvBuffer[TEST_BUFFER_SIZE]; /* Buffer for Receiving Data */
The first code window below is my function that initializes the Uart.
Note in step 4 the stuff to set the format that is commented out. Those are the default settings anyway so they aren't needed, just there to show how to reset and to comment that the Uart needs to be disabled before changing the baud rate. Also note that mask that is being set which will interrupt on XUARTPS_IXR_RXFULL | XUARTPS_IXR_RXOVR (so when the receive FIFO is full or when the receive FIFO is above the interrupt threshold (which is set to 5 in the call to XUartPs_SetFifoThreshold)).
Note in step 5 that the XUartPs_InterruptHandler mentioned is part if the XUartPs software - not the user ISR and it this line shouldn't be changed. Some comments about that function (XUartPs_InterruptHandler) later.
Note in step 6 that this is where the user ISR is set (my ISR function is called UartISR).
Finally note in step 8 that the function XUartPs_Recv() is called. Calling this function resets the XUartPs ReceiveBuffer items (RequestedBytes, RemainingBytes, and NextPtr). This is important because the user ISR is only called when the ReceiveBuffer is full.
So with this init function, every time 5 bytes are received, the XUartPs_InterruptHandler is called by the processor. And every time 12 bytes are received (TEST_BUFFER_SIZE), the user ISR is called (essentially by the XUartPs_InterruptHandler() function).
int InitUart(XScuGic *ptrGic, XUartPs *ptrUart, u16 UartDeviceId, u16 UartInterruptId)
{
int Status;
u32 IntrMask;
XUartPs_Config *ptrConfig;
/* ---------------------------------------------------------------------
* ------------ STEP 1: DEVICE LOOK-UP ------------
* -------------------------------------------------------------------- */
ptrConfig = XUartPs_LookupConfig(UartDeviceId);
if (ptrConfig == NULL) {return XST_FAILURE;}
/* ---------------------------------------------------------------------
* ------------ STEP 2: DRIVER INITIALIZATION ------------
* -------------------------------------------------------------------- */
Status = XUartPs_CfgInitialize(ptrUart, ptrConfig, ptrConfig->BaseAddress);
if (Status != XST_SUCCESS) {return XST_FAILURE;}
/* ---------------------------------------------------------------------
* ------------ STEP 3: SELF TEST ------------
* -------------------------------------------------------------------- */
Status = XUartPs_SelfTest(ptrUart);
if (Status != XST_SUCCESS) {return XST_FAILURE;}
/* ---------------------------------------------------------------------
* ------------ STEP 4: PROJECT-SPECIFIC CONFIGURATION ------------
* -------------------------------------------------------------------- */
XUartPs_DisableUart(ptrUart); // UG5985 says to make sure the UART is disabled before writing to baud rate gen
// XUartPsFormat psFormat;
// psFormat.BaudRate = 115200;
// psFormat.DataBits = XUARTPS_FORMAT_8_BITS;
// psFormat.Parity = XUARTPS_MR_PARITY_NONE;
// psFormat.StopBits = XUARTPS_MR_STOPMODE_1_BIT;
// XUartPs_SetDataFormat(ptrUart, &psFormat);
//IntrMask = XUARTPS_IXR_RXFULL | XUARTPS_IXR_RXEMPTY | XUARTPS_IXR_RXOVR;
IntrMask = XUARTPS_IXR_RXFULL | XUARTPS_IXR_RXOVR;
XUartPs_SetInterruptMask(ptrUart, IntrMask);
XUartPs_SetOperMode(ptrUart, XUARTPS_OPER_MODE_NORMAL);
XUartPs_SetFifoThreshold(ptrUart, 5); // After this value, the RXOVR interrupt will hit the XUartPs_InterruptHandler
/* ---------------------------------------------------------------------
* ------------ STEP 5: ADD TO INTERUPT SYSTEM ------------
* -------------------------------------------------------------------- */
Status = XScuGic_Connect(ptrGic, UartInterruptId, (Xil_ExceptionHandler)XUartPs_InterruptHandler, (void *)ptrUart);
if (Status != XST_SUCCESS) {return Status;}
/* ---------------------------------------------------------------------
* ------------ STEP 6: SET A HANDLER TO BE CALLED(back) WHEN EVENT OCCURS ------------
* -------------------------------------------------------------------- */
XUartPs_SetHandler(ptrUart, (XUartPs_Handler)UartISR, (void *)ptrUart);
/* ---------------------------------------------------------------------
* ------------ STEP 7: ENABLE THE INTERRUPT ------------
* -------------------------------------------------------------------- */
XScuGic_Enable(ptrGic, UartInterruptId);
/* ---------------------------------------------------------------------
* ------------ STEP 8: ENABLE THE UART ------------
* -------------------------------------------------------------------- */
XUartPs_EnableUart(ptrUart);
/* ---------------------------------------------------------------------
* ------------ STEP 8: RESET RECEIVEBUFFER ITEMS ------------
* -------------------------------------------------------------------- */
// Reset the ptrUart.ReceiveBuffer items (RequestedBytes, RemainingBytes, and NextPtr)
// This is needed to eventually allow the user ISR to be called.
XUartPs_Recv(ptrUart, DebugRecvBuffer, TEST_BUFFER_SIZE); // After TEST_BUFFER_SIZE bytes are received, the user ISR will hit.
return XST_SUCCESS;
}
Quick look at XUartPs_Recv()... The main thing to note (at least to me) is that before calling the XUartPs_ReceiveBuffer() function, this function resets the RequestedBytes , RemainingBytes, and NextBytePtr values). This is important because the user ISR is only called when the RemainingBytes counter reaches 0.
u32 XUartPs_Recv(XUartPs *InstancePtr,
u8 *BufferPtr, u32 NumBytes)
{
u32 ReceivedCount;
u32 ImrRegister;
/* Assert validates the input arguments */
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(BufferPtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
#if defined (XCLOCKING)
Xil_ClockEnable(InstancePtr->Config.RefClk);
#endif
/*
* Disable all the interrupts.
* This stops a previous operation that may be interrupt driven
*/
ImrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
XUARTPS_IMR_OFFSET);
XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_IDR_OFFSET,
XUARTPS_IXR_MASK);
/* Setup the buffer parameters */
InstancePtr->ReceiveBuffer.RequestedBytes = NumBytes;
InstancePtr->ReceiveBuffer.RemainingBytes = NumBytes;
InstancePtr->ReceiveBuffer.NextBytePtr = BufferPtr;
/* Receive the data from the device */
ReceivedCount = XUartPs_ReceiveBuffer(InstancePtr);
/* Restore the interrupt state */
XUartPs_WriteReg(InstancePtr->Config.BaseAddress, XUARTPS_IER_OFFSET,
ImrRegister);
return ReceivedCount;
}
The XUartPs_ReceiveBuffer() function isn't that interesting. It really just fills the buffer from the Uart's FIFO (and it will call the user ISR if there is a frame error) so I didn't post it here (easy enough to look at yourself).
Similarly, the XUartPs_InterruptHandler() function isn't worth posting here - it's pretty straightforward and just looks at the interrupt that happened before calling one of several other functions depending on the interrupt type. One of those functions I do show next though: ReceiveDataHandler().
Note that this function - besides getting any characters that are new since the interrupt happened with the call to XUartPs_ReceiveBuffer() - is responsible for calling the user ISR once the remaining bytes int he user's receive buffer (size TEST_BUFFER_SIZE in my case) reaches 0. Also to note that no additional calls to the user ISR will happen unless the ReceiveBuffer items are reset with a new call to XUartPs_Recv(). The skeleton of my user ISR is shown at the end.
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));
}
}
My user ISR skeleton. Note the call at the end to XUartPs_Recv() to reset the ReceiveBuffer items so this ISR will eventually be called again. Also note the //! lines which is the place something should be done with the info in the user ReceiveBuffer before we reset it.
void UartISR(void *CallBackRef, u32 Event, unsigned int EventData)
{
XUartPs *ptrSenderXUart = (XUartPs *) CallBackRef;
// Send some relevant info about the event to the stdio
xil_printf("ISR: Event: %i, EventData: %i, Remaining: %i, NextPtr: 0x%X\r\n", Event, EventData, ptrSenderXUart->ReceiveBuffer.RemainingBytes, &ptrSenderXUart->ReceiveBuffer.NextBytePtr);
// Do something with the DebugRecvBuffer because we are about to reset it...
//!
//!
//!
// Reset the ReceiveBuffer items so this ISR will be called again
XUartPs_Recv(ptrSenderXUart, DebugRecvBuffer, TEST_BUFFER_SIZE);
}
Now I haven't yet decided how best to use this info for the circumstance I mentioned with my 5 and 27 byte expected messages. But I'm closer. I hope someone else finds this useful. It was useful for me to type, either way. :-)
Thanks, Arthur. Adding the files to the File Groups as you suggested fixed the issue and allowed the instantiation and at least got by the initial errors with synthesis (I had already added them to the Sources but that hadn't fixed the problem). There are other errors - but I can move on to tackling those. Your help much appreciated.
This subject is near and dear to my Zybo learning exercise lately so I thought I'd add an additional comment. The Zynq PS block does have an unused UART (mentioned by @zygot with the mentioned FIFO limitations) that can be connected to MIO pins exposed on the Zybo JF connector (bypassing the PL, if desired). Of course, to use xil_printf for both UART0 and UART1, some Vitis code changes are needed (seemingly straightforward since the xil_printf functions are easy to duplicate).