How to trigger a software generated interrupt on core1 from core1 on bare metal?

66 Views Asked by At

I'm trying to trigger a software generated interrupt on core1 from core0 on the Zynq Ultrascale+ Platform. Sadly however no interrupt ever reaches core1.

I tried multiple approaches also using the xscugic driver example. But it only worked from core0 to itself (see driver example) never to core1. This post is my last resort. Can anyone help? What am I missing?

My code currently looks like this:

int ScuGicInit(XScuGic *IntcInstancePtr, u16 DeviceId) {
    XScuGic_Config *IntcConfig;
    int status;

    // Get the configuration for the GIC
    IntcConfig = XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);

    if (NULL == IntcConfig) {
        return XST_FAILURE;
    }

    // Initialize the GIC
    status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
                                   IntcConfig->CpuBaseAddress);
    if (status != XST_SUCCESS) {
        return status;
    }

    // Register the GIC with the exception framework
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
                                 (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                                 IntcInstancePtr);

    // Enable interrupts in the ARM
    Xil_ExceptionEnable();

    return XST_SUCCESS;
}
#define INTC_DEVICE_ID      XPAR_SCUGIC_0_DEVICE_ID
#define INTC_DEVICE_INT_ID  0x0E

void core1(void){
     int Status;
    XScuGic IntcInstance;

    // Initialize GIC
    Status = ScuGicInit(&IntcInstance, XPAR_SCUGIC_SINGLE_DEVICE_ID);
    if (Status != XST_SUCCESS) {
        // Handle error condition
        //return XST_FAILURE;
    }

    // Connect the SGI handler provided by Core1
    Status = XScuGic_Connect(&IntcInstance, INTC_DEVICE_INT_ID,
                             (Xil_InterruptHandler)DeviceDriverHandler, &IntcInstance);
    if (Status != XST_SUCCESS) {
        // Handle error condition
        //return XST_FAILURE;
    }

    // Enable the SGI interrupt
    XScuGic_Enable(&IntcInstance, INTC_DEVICE_INT_ID);

    // Core1 main loop
    while (1) {
        // Core1's tasks or sleep
    }

    while (1);
}


int main() {
    int Status;
    XScuGic IntcInstance;

    // Initialize GIC
    Status = ScuGicInit(&IntcInstance, XPAR_SCUGIC_SINGLE_DEVICE_ID);
    if (Status != XST_SUCCESS) {
        // Handle error condition
        return XST_FAILURE;
    }

    set_start_addr();
    release_acpus();

    sleep(1);

    // Core0 main loop
    while (1) {
        // Trigger an SGI to Core1
        // Assuming Core1 has been configured to handle SGI ID 0
        XScuGic_SoftwareIntr(&IntcInstance, INTC_DEVICE_INT_ID, XSCUGIC_SPI_CPU1_MASK);
        sleep(1);

        // Add a delay or triggering condition here as necessary to prevent continuous triggering
    }

    return XST_SUCCESS;
}

set_start_addr() and release_acpus() are verified to work properly. The Main function runs on core0 while the function core1 runs on core1.

If found the solution. Core 0 initalizes its SCR_EL3 Register like so:

    mov      w1, #0                 //; Initial value of register is unknown
    orr      w1, w1, #(1 << 11)     //; Set ST bit (Secure EL1 can access CNTPS_TVAL_EL1, CNTPS_CTL_EL1 & CNTPS_CVAL_EL1)
    orr      w1, w1, #(1 << 10)     //; Set RW bit (EL1 is AArch64, as this is the Secure world)
    orr      w1, w1, #(1 << 3)      //; Set EA bit (SError routed to EL3)
    orr      w1, w1, #(1 << 2)      //; Set FIQ bit (FIQs routed to EL3)
    orr      w1, w1, #(1 << 1)      //; Set IRQ bit (IRQs routed to EL3)
    msr      SCR_EL3, x1

Especially the IRQ bit is of interest. Apparently this needed to be set on all CPUs that are running bare metal (EL3) to receive any kind of IRQ. After setting this bit on Core1, Core0 was able to trigger a interrupt.

0

There are 0 best solutions below