Updated: October 28, 2024 |
For an IST (Interrupt Service Thread), when you call InterruptAttachEvent() to associate a sigevent with a specific interrupt, the sigevent is delivered to the calling thread by default. You may want the handler to run on a specific CPU (or core) for various reasons (e.g., reliability). To accomplish this, you call ThreadCtl() with the argument _NTO_TCTL_RUNMASK_GET_AND_SET and the runmask to specify the CPU for a thread before you set up the event [call InterruptAttachEvent()]. The runmask specifies the core or CPU that the sigevent runs on. It’s important to note that you may incur a system performance impact when you use this design and it can also result in partition inversion if you use adaptive partitions (APS).
For ISTs, you'd still typically want to change the PIC routing as in the Example of modifying the interrupt controller in a BSP when using InterruptAttach() as well as setting the runmask. Without the routing change, the hardware will deliver the interrupt to CPU X and then the kernel will have to IPI CPU Y to run the IST code.
#include <stdio.h> #include <sys/neutrino.h> #define HW_SERIAL_IRQ 3 #define REG_RX 0 #define REG_II 2 #define REG_LS 5 #define REG_MS 6 #define IIR_MASK 0x07 #define IIR_MSR 0x00 #define IIR_THE 0x02 #define IIR_RX 0x04 #define IIR_LSR 0x06 #define IIR_MASK 0x07 static int base_reg = 0x2f8; int main (int argc, char **argv) { int intId; // interrupt id int iir; // interrupt identification register int serial_msr; // saved contents of Modem Status Reg int serial_rx; // saved contents of RX register int serial_lsr; // saved contents of Line Status Reg struct sigevent event; /* Initialize the sigevent */ SIGEV_INTR_INIT(&event); /* Set interrupt thread to run on CPU 1 */ int cpu = 1; unsigned runmask = (1 << cpu); /* Set runmask so that the interrupt always runs on the specified CPU */ if (ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET, &runmask) == -1) { printf("Unable to specify runmask for CPU %d", cpu); return -1; } // Set up the event intId = InterruptAttachEvent (HW_SERIAL_IRQ, &event, 0); for (;;) { // Wait for an interrupt event (could use MsgReceive() instead) // Note: If you want to use MsgReceive(), you must make the sigevent a pulse // sigevent, create a private channel, and then create a connection to // the channel to send the pulse (to yourself). For production code, // consider also using MsgRegisterEvent() InterruptWait (0, NULL); /* * Determine the source of the interrupt * reading the Interrupt Identification Register * to clear it * iir = in8 (base_reg + REG_II) & IIR_MASK; // Unmask the interrupt, so that we can get the next event InterruptUnmask (HW_SERIAL_IRQ, intId); /* no interrupt? */ if (iir & 1) { /* Then wait again for next */ continue; } /* * Determine which interrupt source caused the interrupt * and determine if we need to do with it */ switch (iir) { case IIR_MSR: serial_msr = in8 (base_reg + REG_MS); /* * Perform whatever processing you would've done in * the other example... */ break; case IIR_THE: /* do nothing */ break; case IIR_RX: /* Note the character */ serial_rx = in8 (base_reg + REG_RX); break; case IIR_LSR: /* Note the line status reg. */ serial_lsr = in8 (base_reg + REG_LS); break; } } /* You won't get here. */ return (0); }