I am using the global interrupt controller in a modified rckmb (now sccmb), and it works fine. Here are some things to watch out for:
1) The global interrupt controller uses a single (configurable) LINT input on each core. Therefore, all drivers that want to receive interrupt requests via the global controller must also register for a single shared interrupt line. For example, under Linux with the default sccLinux kernel, you can invoke request_irq() with irq=4, and configure the controller to use LINT0 (specify request_irq() to use shared interrupts). The rckemac/sccemac driver is a good example of how to configure the controller and connect an interrupt.
2) Drivers need to agree on which LINT pin to use. If one driver sets the controller to send interrupts on LINT0, and later another driver reconfigures for LINT1, the first one will stop receiving anything.
3) Individual interrupt lines can be masked, on a per-core basis, in the global interrupt controller. Unmask your interrupt line, otherwise the controller will not interrupt you. I assume you are using one of the IPI lines, so please notice the different bit numbering between the status+mask+reset registers on the one hand and the request register on the other. Bit #0 in the IPI request register maps to Bit #6 in the others.
4) If a driver got an interrupt and finished processing, it must clear the LINT bit (corresponding to that LINT pin the global controller uses) in its core's configuration register. After clearing that bit, the driver must acknowledge the interrupt at the global controller, by doing a *single* 32-bit write to the interrupt reset register of its core.
The "write" to the interrupt reset register is treated as an End-of-Interrupt (EOI) command. Therefore, if the interrupt status register still contains set bits (after clearing the bits specified in the write command), a new "interrupt packet" is sent out *immediately*. This "interrupt packet" sets the corresponding LINT bit in the core's configuration register. For this reason, you must clear the LINT bit yourself, before sending the EOI. If you do not do this, you will receive exactly one interrupt from the global interrupt controller; afterwards, the line will be stuck and interrupt indefinitely (showing symptoms of an interrupt storm), thus locking up your core.
Another caveat: I know of no way to reliably use the global interrupt controller at the same time than software-triggered interrupts via direct writes to the core's configuration registers. This also applies to the default sccLinux image... Specifically: you may be able to use rckemac and rckmb (in interrupt mode) for some time, but there are race conditions that will lock up your interrupt lines at some point. You can detect that situation easily, because your SCC core will no longer perform any user-mode work (any SSH or VUART login session will freeze), sccGui shows the core not entering the "HALT" state any more, but for some reason it will (usually) still answer ping requests...
Thank you very much for the clear explanation, let follow your guidance to the letter, because it seems to cover all my worries.
I managed to get the interrupts working, however i have one setback. Each core can successfully interrupts itself, however when i request for interrupts to other cores, the appropriate status bit is never set, and the interrupt is not triggered. Yet each core can successfully do this if it has itself as the destination of the interrupt.
1 of 1 people found this helpful
glad to hear you are making progress. I had the same issue with IPIs; turns out these request registers work exactly the opposite as one might expect.
IPI Request Register #X should really be interpreted as "IPI Request on line #X". So, to assert IPI#X of core #Y, set the Y-th bit in the X-th IPI request register.
I'm assuming you are currently doing the opposite, setting the X-th bit in the Y-th register, thus interpreting each register as a sink for 48 IPI lines. I would expect that your code is correct, because you get interrupts if the numbers are identical. Otherwise, your interrupts should be triggered as well, they just end up on the wrong core.
If you want to check that your interrupts are indeed delivered, you can use sccGui to read the status of the global interrupt controller. If the cores you are sending interrupts to are inactive (e.g., not started, hold in reset), these interrupts are never acknowledged and the corresponding status bits remain set. You can display register values as follows:
- Use menu item Widgets\Generic packet transfer widget (or the toolbar button).
- Select "GRB_PORT" for "System IF Su[...]" (sorry; but for me, it cuts off the description at this point. The font is too large).
- Enter the 8-byte-aligned "Address" of register, and select "CUSTOM" in the list. For example, this is "D000" for Interrupt Status of Core #0.
- Enter "0F" or "F0" for "Byte Enable", depending on whether you want to read the lower (0F) or upper (F0) 32 bits. These register do not support 64-bit reads, so "FF" is not valid here.
- For "Command", select NOGEN, NCRD.
- Hit "Transfer Now". The result of the read operation appears in the right-most field.
You can also use this same widget to write to the registers: use NCWR instead of NCRD, input your data, then submit the packet.
Thanks for the quick response, i actually got it after i discovered that the scckit documentation is totally confusing on this issue, however your interpretation is spot on. Just to quote what I followed from the scckit documentation.
A core can interrupt another core by setting the corresponding bit in that core’s request register. If that core’s interrupt mask bit is not set, the interrupt occurs. The interrupted core then jumps to its service routine and reads its status register to determine where the interrupt came from.
When the interrupted core completes its service processing, it must clear its local interrupt and write to its reset register in the FPGA to clear its status and pending bits.