• 0

Cora Board Zynq UART Ps (Bare metal operation)


Question

Hello All,

I have a Coraz7-10 board where I have the uart working in send mode, but not in receive mode. The uart is configured to operate on an interrupt, and I'm using the xilinx example code.

I have modified just slightly with some debug messages and the recv handler. Basically, I just want to "set" a signal when then handler occurs, and "reset" it in the main() loop. 

In the handler itself, I am just checking to see if the received data makes it there, confirming in debugger view of SDK with watch variables. However, rx data is never present.

I'm new to Zync, so if there are any suggestion, much appreciated.

Thanks. James

Spoiler

/***************************** Include Files *******************************/

#include "xparameters.h"
#include "xplatform_info.h"
#include "xuartps.h"
#include "xil_exception.h"
#include "xil_printf.h"
#include "stdio.h"

#ifdef XPAR_INTC_0_DEVICE_ID
#include "xintc.h"
#else
#include "xscugic.h"
#endif
/************************** Constant Definitions **************************/

/*
 * The following constants map to the XPAR parameters created in the
 * xparameters.h file. They are defined here such that a user can easily
 * change all the needed parameters in one place.
 */
#ifdef XPAR_INTC_0_DEVICE_ID
#define INTC        XIntc
#define UART_DEVICE_ID        XPAR_XUARTPS_0_DEVICE_ID
#define INTC_DEVICE_ID        XPAR_INTC_0_DEVICE_ID
#define UART_INT_IRQ_ID        XPAR_INTC_0_UARTPS_0_VEC_ID
#else
#define INTC        XScuGic
#define UART_DEVICE_ID        XPAR_XUARTPS_0_DEVICE_ID
#define INTC_DEVICE_ID        XPAR_SCUGIC_SINGLE_DEVICE_ID
#define UART_INT_IRQ_ID        XPAR_XUARTPS_1_INTR
#endif
/*
 * The following constant controls the length of the buffers to be sent
 * and received with the UART,
 */
#define TEST_BUFFER_SIZE    8


/**************************** Type Definitions ******************************/


/************************** Function Prototypes *****************************/

int UartPsIntrExample(INTC *IntcInstPtr, XUartPs *UartInstPtr,
            u16 DeviceId, u16 UartIntrId);


static int SetupInterruptSystem(INTC *IntcInstancePtr,
                XUartPs *UartInstancePtr,
                u16 UartIntrId);

void Handler(void *CallBackRef, u32 Event, unsigned int EventData);


/************************** Variable Definitions ***************************/

XUartPs UartPs    ;        /* Instance of the UART Device */
INTC InterruptController;    /* Instance of the Interrupt Controller */

/*
 * The following buffers are used in this example to send and receive data
 * with the UART.
 */
u8 SendBuffer[TEST_BUFFER_SIZE];    /* Buffer for Transmitting Data */
u8 RecvBuffer[TEST_BUFFER_SIZE];    /* Buffer for Receiving Data */

/*
 * The following counters are used to determine when the entire buffer has
 * been sent and received.
 */
volatile int TotalReceivedCount;
volatile int TotalSentCount;
int TotalErrorCount;

/**************************************************************************/
/**
*
* Main function to call the Uart interrupt example.
*
* @param    None
*
* @return    XST_SUCCESS if successful, XST_FAILURE if unsuccessful
*
* @note        None
*
**************************************************************************/
#ifndef TESTAPP_GEN

int end = 0;
int UART_newData = 0;

int main(void)
{
    int Status;

    /* Run the UartPs Interrupt example, specify the the Device ID */
    Status = UartPsIntrExample(&InterruptController, &UartPs,
                UART_DEVICE_ID, UART_INT_IRQ_ID);
    if (Status != XST_SUCCESS) {
        xil_printf("UART Interrupt Example Test Failed\r\n");
        return XST_FAILURE;
    }

    xil_printf("\r\nSuccessfully ran UART Interrupt Example Test (sent data).\r\n");

 

// Just set / reset UART_newData signal here in the main loop; as it should be "set" by handler

    while(end==0)
    {
        if(UART_newData==1)
        {
            UART_newData = 0;

            //i=0;
        }
    }

    return 0;
}
#endif


int UartPsIntrExample(INTC *IntcInstPtr, XUartPs *UartInstPtr,
            u16 DeviceId, u16 UartIntrId)
{
    int Status;
    XUartPs_Config *Config;
    int Index;
    u32 IntrMask;
    //int BadByteCount = 0;

    if (XGetPlatform_Info() == XPLAT_ZYNQ_ULTRA_MP) {
#ifdef XPAR_XUARTPS_1_DEVICE_ID
        DeviceId = XPAR_XUARTPS_1_DEVICE_ID;
#endif
    }

    /*
     * Initialize the UART driver so that it's ready to use
     * Look up the configuration in the config table, then initialize it.
     */
    Config = XUartPs_LookupConfig(DeviceId);
    if (NULL == Config) {
        return XST_FAILURE;
    }

    Status = XUartPs_CfgInitialize(UartInstPtr, Config, Config->BaseAddress);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    /* Check hardware build */
    Status = XUartPs_SelfTest(UartInstPtr);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

        printf("\r\n** UART self test success.\r\n");


    /*
     * Connect the UART to the interrupt subsystem such that interrupts
     * can occur. This function is application specific.
     */
    Status = SetupInterruptSystem(IntcInstPtr, UartInstPtr, UartIntrId);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    /*
     * Setup the handlers for the UART that will be called from the
     * interrupt context when data has been sent and received, specify
     * a pointer to the UART driver instance as the callback reference
     * so the handlers are able to access the instance data
     */
    XUartPs_SetHandler(UartInstPtr, (XUartPs_Handler)Handler, UartInstPtr);

    /*
     * Enable the interrupt of the UART so interrupts will occur, setup
     * a local loopback so data that is sent will be received.
     */
    IntrMask =
        XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING |
        XUARTPS_IXR_OVER | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXFULL |
        XUARTPS_IXR_RXOVR | XUARTPS_EVENT_RECV_DATA    | XUARTPS_EVENT_RECV_TOUT;

    if (UartInstPtr->Platform == XPLAT_ZYNQ_ULTRA_MP) {
        IntrMask |= XUARTPS_IXR_RBRK;
    }

    XUartPs_SetInterruptMask(UartInstPtr, IntrMask);

    //XUartPs_SetOperMode(UartInstPtr, XUARTPS_OPER_MODE_LOCAL_LOOP);
    XUartPs_SetOperMode(UartInstPtr, XUARTPS_OPER_MODE_NORMAL);

    /*
     * Set the receiver timeout. If it is not set, and the last few bytes
     * of data do not trigger the over-water or full interrupt, the bytes
     * will not be received. By default it is disabled.
     *
     * The setting of 8 will timeout after 8 x 4 = 32 character times.
     * Increase the time out value if baud rate is high, decrease it if
     * baud rate is low.
     */
    XUartPs_SetRecvTimeout(UartInstPtr, 8);


    /*
     * Initialize the send buffer bytes with a pattern and the
     * the receive buffer bytes to zero to allow the receive data to be
     * verified
     */
    printf("Now sending data ...\r\n");
    for (Index = 0; Index < TEST_BUFFER_SIZE; Index++) {

        SendBuffer[Index] = (Index % 26) + 'A';

        RecvBuffer[Index] = 0;
    }

    /*
     * Start receiving data before sending it since there is a loopback,
     * ignoring the number of bytes received as the return value since we
     * know it will be zero
     */
    //XUartPs_Recv(UartInstPtr, RecvBuffer, TEST_BUFFER_SIZE);

    /*
     * Send the buffer using the UART and ignore the number of bytes sent
     * as the return value since we are using it in interrupt mode.
     */
    XUartPs_Send(UartInstPtr, SendBuffer, TEST_BUFFER_SIZE);
    XUartPs_Recv(UartInstPtr, RecvBuffer, TEST_BUFFER_SIZE);

    /*
     * Wait for the entire buffer to be received, letting the interrupt
     * processing work in the background, this function may get locked
     * up in this loop if the interrupts are not working correctly.
     */
//    while (1) {
//        if ((TotalSentCount == TEST_BUFFER_SIZE) &&
//            (TotalReceivedCount == TEST_BUFFER_SIZE)) {
//            break;
//        }
//    }

    /* Verify the entire receive buffer was successfully received */
//    for (Index = 0; Index < TEST_BUFFER_SIZE; Index++) {
//        if (RecvBuffer[Index] != SendBuffer[Index]) {
//            BadByteCount++;
//        }
//    }

    /* Set the UART in Normal Mode */
//    XUartPs_SetOperMode(UartInstPtr, XUARTPS_OPER_MODE_NORMAL);


    /* If any bytes were not correct, return an error */
//    if (BadByteCount != 0) {
//        return XST_FAILURE;
//    }

    return XST_SUCCESS;
}

//**************************************************************************/
//  HANDLER CODE - PROBLEM AREA
//
//***************************************************************************/
void Handler(void *CallBackRef, u32 Event, unsigned int EventData)

{

    XUartPs_Recv(&UartPs, RecvBuffer, TEST_BUFFER_SIZE);

    //printf("Handler entered.\r\n");
    UART_newData = 1;
    //for (int j = 0; j < TEST_BUFFER_SIZE; j++)

    /* All of the data has been sent */
    if (Event == XUARTPS_EVENT_SENT_DATA) {
        TotalSentCount = EventData;
    }

    /* All of the data has been received */
    if (Event == XUARTPS_EVENT_RECV_DATA) {
        TotalReceivedCount = EventData;
        //UART_newData = 1;
    }

    /*
     * Data was received, but not the expected number of bytes, a
     * timeout just indicates the data stopped for 8 character times
     */
    if (Event == XUARTPS_EVENT_RECV_TOUT) {
        TotalReceivedCount = EventData;

    }

    /*
     * Data was received with an error, keep the data but determine
     * what kind of errors occurred
     */
    if (Event == XUARTPS_EVENT_RECV_ERROR) {
        TotalReceivedCount = EventData;
        TotalErrorCount++;
    }

    /*
     * Data was received with an parity or frame or break error, keep the data
     * but determine what kind of errors occurred. Specific to Zynq Ultrascale+
     * MP.
     */
    if (Event == XUARTPS_EVENT_PARE_FRAME_BRKE) {
        TotalReceivedCount = EventData;
        TotalErrorCount++;
    }

    /*
     * Data was received with an overrun error, keep the data but determine
     * what kind of errors occurred. Specific to Zynq Ultrascale+ MP.
     */
    if (Event == XUARTPS_EVENT_RECV_ORERR) {
        TotalReceivedCount = EventData;
        TotalErrorCount++;
    }
}

static int SetupInterruptSystem(INTC *IntcInstancePtr,
                XUartPs *UartInstancePtr,
                u16 UartIntrId)
{
    int Status;

#ifdef XPAR_INTC_0_DEVICE_ID
#ifndef TESTAPP_GEN
    /*
     * Initialize the interrupt controller driver so that it's ready to
     * use.
     */
    Status = XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }
#endif
    /*
     * Connect the handler that will be called when an interrupt
     * for the device occurs, the handler defined above performs the
     * specific interrupt processing for the device.
     */
    Status = XIntc_Connect(IntcInstancePtr, UartIntrId,
        (XInterruptHandler) XUartPs_InterruptHandler, UartInstancePtr);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

#ifndef TESTAPP_GEN
    /*
     * Start the interrupt controller so interrupts are enabled for all
     * devices that cause interrupts.
     */
    Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }
#endif
    /*
     * Enable the interrupt for uart
     */
    XIntc_Enable(IntcInstancePtr, UartIntrId);

    #ifndef TESTAPP_GEN
    /*
     * Initialize the exception table.
     */
    Xil_ExceptionInit();

    /*
     * Register the interrupt controller handler with the exception table.
     */
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                (Xil_ExceptionHandler) XIntc_InterruptHandler,
                IntcInstancePtr);
    #endif
#else
#ifndef TESTAPP_GEN
    XScuGic_Config *IntcConfig; /* Config for interrupt controller */

    /* Initialize the interrupt controller driver */
    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    if (NULL == IntcConfig) {
        return XST_FAILURE;
    }

    Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
                    IntcConfig->CpuBaseAddress);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    /*
     * Connect the interrupt controller interrupt handler to the
     * hardware interrupt handling logic in the processor.
     */
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                (Xil_ExceptionHandler) XScuGic_InterruptHandler,
                IntcInstancePtr);
#endif

    /*
     * Connect a device driver handler that will be called when an
     * interrupt for the device occurs, the device driver handler
     * performs the specific interrupt processing for the device
     */
    Status = XScuGic_Connect(IntcInstancePtr, UartIntrId,
                  (Xil_ExceptionHandler) XUartPs_InterruptHandler,
                  (void *) UartInstancePtr);
    if (Status != XST_SUCCESS) {
        return XST_FAILURE;
    }

    /* Enable the interrupt for the device */
    XScuGic_Enable(IntcInstancePtr, UartIntrId);

#endif
#ifndef TESTAPP_GEN
    /* Enable interrupts */
     Xil_ExceptionEnable();
#endif

    return XST_SUCCESS;
}

 

Link to post
Share on other sites

6 answers to this question

Recommended Posts

  • 0

Thank you for the feedback jpeyron, I appreciate it.

I have looked through the SDK files for the Zedboard uart interrupt project that you sent. This is for uartlite, and I am wondering what I have to do in the Cora to use the uartlite versus the regular uart driver? If you have any thoughts there, would appreciate it.

Kind Regards,

James

Link to post
Share on other sites
  • 0
On 3/29/2019 at 12:12 PM, TeslaCrytpo said:

One additional comment is that I am attempting to use the uart completely in the PS portion of the Zynq/Cora versus having to use the uart in the fpga logic. Thanks, James

Same problem here, I have a Cora board and need to use the PS UART in interrupt mode to receive data.

I use some sample code by Xilinx and from other Digilent projects but it doesn't work.

Something I notice is that after I configure the UART and the GIC, I read the uart's IER and IDR registers, which should hold the enabled and disabled interrupt masks, and they are both 0. Furthermore, I tried to use XSCT to manually write them but they remain at 0. Either they are locker or, could it be, they don't exist because they must be enabled in the HW design. Yet I can't find any part of the Zynq PS configuration where the PS UART irqs could be enabled. There's a section to enable PL to PS and PS to PL irqs, but that's not the same as enabling the internal PS irqs.

I notice little or no support for Cora boards at Digilent's website, even on Cora's page, the examples and resources are just links to those for other boards.

 

Link to post
Share on other sites
  • 0

Hi @edge30

Using Vivado/SDK 2019.1, I tested out the Xilinx interrupt mode example for xuartps using a Zybo Z7-20 (I'm working from home, and don't have all of the hardware I would normally have access to). The Zynq PS on this board is functionally the same as the one on the Cora Z7-10. Note that this is the same example project used by the original poster in this thread, and is available through the system.mss file in a compatible project. My block design contains only the Zynq PS with the default configuration from the board files, with AXI GP0 and FCLK 0 disabled. The PL is not used whatsoever.

As long as a UART and the GIC are enabled in the Zynq configuration, the PS interrupts should work.

The Zynq has two MIO UART peripherals, and the one used differs between the Cora and Zybo presets. The interrupt example project picks up the right device ID from xparameters.h regardless of the UART used, but it looks like the interrupt ID is specified as the one for UART1. As such, XPAR_XUARTPS_1_INTR in the Constant Definitions section of xuartps_intr_example.c likely needs to be changed to XPAR_XUARTPS_0_INTR for the Cora.

Documentation, guides, and examples for the Cora can be found linked from its Resource Center on our wiki.

Thanks,

Arthur

Link to post
Share on other sites
  • 0

Finally I was able to use the UART and the GIC, but for the UART I had to write a custom driver to directly access the configuration registers. It took me some time because the Zynq TRM is very hard to follow. It doesn't have a proper register specification section, I had to deduct the registers and bit definitions from the text.

I still find using Xilinx's BSP unconvenient, one has to follow the methods decided  by Xilinx's engineers which are not so well documented in first place. If a design departs from the pre-written examples it is a challenge to understand how the APIs interoperate.

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now