3
\$\begingroup\$

I am using freeRTOS in Zedboard. I am able to enable the PL-PS interrupt in bare-metal program. I couldn't really find any documentation/tutorial on how to link the FreeRTOS and the PL interrupt system. I am currently migrating my software to freeRTOS but I am not sure how to "connect" the interrupt between the freeRTOS and the PL interrupt. Here is what I have done and I am sure it is totally wrong.

In the main:

xTaskCreate(InitInterrupt, (const char * )"Init Interrupt",
            configMINIMAL_STACK_SIZE, XPAR_PS7_SCUGIC_0_DEVICE_ID,
            tskIDLE_PRIORITY, NULL);

Interrupt Enable Function:

XScuGic InterruptController;
static XScuGic_Config *GicConfig;
xSemaphoreHandle DMATransfer_trigger = 0;

int SetUpInterruptSystem(XScuGic *XScuGicInstancePtr) {
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
            (Xil_ExceptionHandler) XScuGic_InterruptHandler,
            XScuGicInstancePtr);
    Xil_ExceptionEnable();
    //enable interrupt in ARM
    return XST_SUCCESS;
}

static void InitInterrupt( deviceID) {
    while (1) {
        int Status;

        GicConfig = XScuGic_LookupConfig(deviceID);
        if (NULL == GicConfig) {
            xil_printf("Interrupt Initialization FAILED!!\r\n");
            vTaskDelete(NULL);  //delete InitAXIDMA itself
        }

        Status = XScuGic_CfgInitialize(&InterruptController, GicConfig,
                GicConfig->CpuBaseAddress);
        if (Status != XST_SUCCESS) {
            xil_printf("Interrupt Initialization FAILED!!\r\n");
            vTaskDelete(NULL);  //delete InitAXIDMA itself
        }

        Status = SetUpInterruptSystem(&InterruptController);
        if (Status != XST_SUCCESS) {
            xil_printf("Interrupt Initialization FAILED!!\r\n");
            vTaskDelete(NULL);  //delete InitAXIDMA itself
        }

        Status = XScuGic_Connect(&InterruptController,
        XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR,
                (Xil_ExceptionHandler) InterruptHandler,
                NULL);
        if (Status != XST_SUCCESS) {
            xil_printf("Interrupt Initialization FAILED!!\r\n");
            vTaskDelete(NULL);  //delete InitAXIDMA itself
        }
        XScuGic_Enable(&InterruptController,
        XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR);
#ifdef DEBUG
        xil_printf("Interrupt Initialization Complete\r\n");
#endif
        vTaskDelete(NULL);  //delete InitAXIDMA itself
    }
}

In the interrupt Handler:

void InterruptHandler(void) {
u32 tmpValue;
//xil_printf("Interrupt acknowledged...\n\r);
//clear interrupt just perform a write bit to no 12 ofS2MM_DMASR
tmpValue = Xil_In32(XPAR_AXI_DMA_0_BASEADDR + 0x34);
tmpValue = tmpValue | 0x1000;
Xil_Out32( XPAR_AXI_DMA_0_BASEADDR + 0x34, tmpValue);
//Process Data Here!!

//initialize another transfer
    xSemaphoreGive(DMATransfer_trigger);
}

The interrupt source is from the AXI DMA Controller IP. This is what I have tried but it doesn't work. Can anyone please give me some guide on how to modify the code to handle and setup FreeRTOS interrupt from the PL. Thank you!

\$\endgroup\$
1
\$\begingroup\$

It seems that Xilinx code works as long as the XScuGic is not reinitialized in different place. In other words you need to initialize & use xInterruptController:

extern XScuGic xInterruptController;

int main()
{
    XScuGic_Config *scuGicConfig = XScuGic_LookupConfig(0);
    XScuGic_CfgInitialize(&xInterruptController, scuGicConfig, scuGicConfig->CpuBaseAddress);
    // ...
}

OLD ANSWER: You neither can nor should reinitialize the interrupt handler. It is already initialized by FreeRTOS_SetupTickInterrupt which in turn is called by vTaskStartScheduler. Once FreeRTOS is up & running you can just register interrupts from task. I attached the example below which is kind of a port of Zynq Book tutorial (sorry for hacky code but I just figure it out):

#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include <xgpio.h>
#include <xscugic.h>
#include <xuartps.h>

static void checkX(int result, const char *call, const char *file, int line)
{
    if (result != XST_SUCCESS) {
        xil_printf("%s:%d: Call %s failed with error %d\n", file, line, call, result);
        for(;;);
    }
}

#define CHECKX(call) checkX(call, #call, __FILE__, __LINE__)

static void checkRTOS(BaseType_t result, const char *call, const char *file, int line)
{
    if (result != pdPASS) {
        xil_printf("%s:%d: Call %s failed with error %d\n", file, line, call, result);
        for(;;);
    }
}

#define CHECKRTOS(call) checkRTOS(call, #call, __FILE__, __LINE__)

struct updateVal {
    enum {
        UPDATE_BTNS,
        UPDATE_SWS
    } type;
    union {
        struct {
            int btns;
        } btns;
        struct {
            int sws;
        } sws;
    };
};



struct btnsIntData {
    XGpio *btns;
    QueueHandle_t queue;
};

static void btnsIntHandler(struct btnsIntData *data)
{
    struct updateVal update;
    update.type = UPDATE_BTNS;
    update.btns.btns = XGpio_DiscreteRead(data->btns, 1);
    xQueueSendFromISR(data->queue, &update, NULL);
    XGpio_InterruptClear(data->btns, XGPIO_IR_CH1_MASK);
}

struct swsIntData {
    XGpio *sws;
    QueueHandle_t queue;
};

static void swsIntHandler(struct swsIntData *data)
{
    struct updateVal update;
    update.type = UPDATE_SWS;
    update.sws.sws = XGpio_DiscreteRead(data->sws, 1);
    xQueueSendFromISR(data->queue, &update, NULL);
    XGpio_InterruptClear(data->sws, XGPIO_IR_CH1_MASK);
}

struct ledTaskData {
    XGpio *btns, *sws, *leds;
    QueueHandle_t queue;
};

static void updateLedsTask(struct ledTaskData *data)
{
    unsigned cnt = 0, mask = 0;

    extern XScuGic xInterruptController;

    struct btnsIntData btnIntData = { .btns = data->btns, .queue = data->queue };
    CHECKX(XScuGic_Connect(&xInterruptController, XPAR_FABRIC_AXI_GPIO_BTNS_IP2INTC_IRPT_INTR, (Xil_ExceptionHandler)btnsIntHandler, &btnIntData));
    struct swsIntData swsIntData = { .sws = data->sws, .queue = data->queue };
    CHECKX(XScuGic_Connect(&xInterruptController, XPAR_FABRIC_AXI_GPIO_SWS_IP2INTC_IRPT_INTR, (Xil_ExceptionHandler)swsIntHandler, &swsIntData));

    XGpio_InterruptEnable(data->btns, XGPIO_IR_CH1_MASK);
    XGpio_InterruptEnable(data->sws, XGPIO_IR_CH1_MASK);
    XGpio_InterruptGlobalEnable(data->btns);
    XGpio_InterruptGlobalEnable(data->sws);

    XScuGic_Enable(&xInterruptController, XPAR_FABRIC_AXI_GPIO_BTNS_IP2INTC_IRPT_INTR);
    XScuGic_Enable(&xInterruptController, XPAR_FABRIC_AXI_GPIO_SWS_IP2INTC_IRPT_INTR);

    struct updateVal update;
    update.type = UPDATE_SWS;
    update.sws.sws = XGpio_DiscreteRead(data->sws, 1);
    xQueueSend(data->queue, &update, 0);

    while (1) {
        struct updateVal update;
        if (xQueueReceive(data->queue, &update, portMAX_DELAY)) {
            switch (update.type) {
            case UPDATE_BTNS:
                cnt += update.btns.btns;
                break;
            case UPDATE_SWS:
                mask = update.sws.sws;
                break;
            default:
                xil_printf("%s:%d: Unexpected update type: %d\n", __FILE__, __LINE__, update.type);
                for (;;);
            }
            XGpio_DiscreteWrite(data->leds, 1, cnt ^ mask);
        }
    }
}

int main()
{
    const size_t QUEUE_SIZE = 16;

    XGpio btns, sws, leds;
    QueueHandle_t queue;

    queue = xQueueCreate(QUEUE_SIZE, sizeof(struct updateVal));
    struct ledTaskData ledTaskData = {
        .btns = &btns,
        .sws = &sws,
        .leds = &leds,
        .queue = queue
    };
    CHECKRTOS(xTaskCreate((TaskFunction_t)updateLedsTask, "updateLedsTask", (short)configMINIMAL_STACK_SIZE, &ledTaskData, (BaseType_t)2 | portPRIVILEGE_BIT, NULL));

    CHECKX(XGpio_Initialize(&btns, XPAR_AXI_GPIO_BTNS_DEVICE_ID));
    CHECKX(XGpio_Initialize(&sws, XPAR_AXI_GPIO_SWS_DEVICE_ID));
    CHECKX(XGpio_Initialize(&leds, XPAR_AXI_GPIO_LEDS_DEVICE_ID));

    XGpio_SetDataDirection(&btns, 1, 0xFF);
    XGpio_SetDataDirection(&sws, 1, 0xFF);
    XGpio_SetDataDirection(&leds, 1, 0x00);

    vTaskStartScheduler();
    for (;;);
}
\$\endgroup\$
1
\$\begingroup\$

Assuming this is a Cortex-A9 Zynq 7000, you will find documentation on how to use interrupts with FreeRTOS on the following link: http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html - with worked examples in the official FreeRTOS download. Information on locating the example within the official FreeRTOS download can be found on the following link: http://www.freertos.org/RTOS-Xilinx-Zynq.html

\$\endgroup\$
  • 1
    \$\begingroup\$ Ya I have saw that before this, but it doesn't really show how to link the interrupt from the PL to PS. I have found some code online to use the prvGetInterruptControllerInstance() function for the XScuGic instance pointer, but when I type the code into the sdk, it says the function was not found. \$\endgroup\$ – user9870 Jul 22 '16 at 11:32
  • \$\begingroup\$ forums.xilinx.com/t5/Embedded-Development-Tools/… \$\endgroup\$ – user9870 Jul 22 '16 at 11:36
  • \$\begingroup\$ The example code is not very useful as a) it is 200-400 kLOC IIRC and b) it doesn't use Xilinx libraries which are at the core of OP problem and it handles creation of interrupt vector itself. The problem is that Xilinx port overwrites said vector on initialization of different XScuGic instance which i not used within the example. \$\endgroup\$ – Maciej Piechotka Feb 16 '18 at 17:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.