Forum Discussion

Altera_Forum's avatar
Altera_Forum
Icon for Honored Contributor rankHonored Contributor
16 years ago

Nested interrupts again

Hello Linux masters,

I need to make a small patch inside uClinux kernel for NIOS2. I need to enable one specific IRQ to have absolute priority over all atomic processes running in Linux - over threads, over other ISRs, over instruction traps and so on - simply said - over any atomic section in uClinux means. I know, it is possible, but don't know exactly how to do it.

Fot the first try I want to enable my specific interrupt to nest all the other possible ISRs. I have modified function process_int() as follows:

asmlinkage void process_int(unsigned long vec, struct pt_regs *fp)
{
    //check if special nested interrupt
    if (is_nested_interrupt)
    {
        //process my special interrupt
    }
    else
    {
        //enable special nested interupt
        is_nested_interrupt = 1;
        __asm__ __volatile__(
            "wrctl    ienable,r0\n"
            : /* No output */
            : /* No input */
            : /* No regs */);
        local_irq_enable();
        /* give the machine specific code a crack at it first */
        irq_enter();
        kstat_cpu(0).irqs++;
        latency_cause(-99,~vec);
    
        if (irq_list.handler) {
            if ((irq_list.handler(vec, irq_list.dev_id))==IRQ_NONE)
            ;
        } else
   # ifdef DEBUG
            {
            printk(KERN_ERR "No interrupt handler for level %ld\n", vec);
    ////        asm("trap 5");
            }
   # else
     # if 1
            printk(KERN_ERR "Ignoring interrupt %ld: no handler\n", vec);
     # else
            panic("No interrupt handler for level %ld\n", vec);
     # endif
   # endif
    
        irq_exit();
    
        //disable special nested interrupt
        local_irq_disable();
        __asm__ __volatile__(
            "ldw    r8,0(%0)\n"
            "wrctl    ienable,r8\n"
            : /* No output */
            : "r" (&saved_ienable)
            : "r8");
        is_nested_interrupt = 0;
    }
}

There are two new global variables. "is_nested_interrupt" is used to distinguish if interrupt is nested or not. "saved_ienable" is used to hold ienable register changes made during interrupt exception handling to be sure, that at the end of non-nested interrupt there is correct value stored in ienable register prepared to catch any new interrupt source. These two variables are defined also in irq.c as follows:

unsigned long saved_ienable = 0;
unsigned long is_nested_interrupt = 0;

Last change needed to be done is in "clrimr" and "setimr" functions. "saved_ienable" variable is allways updated according to new required mask, but "ienable" itself is updated only in case, when we are not in interrupt, see the code:

/*
 * Use a zero to clean the bit.
 */
static inline void clrimr(int mask)
{
    int flags;
    local_irq_save(flags);
    __asm__ __volatile__(
    "ldw    r8,0(%1)\n"
    "and    r8,r8,%0\n"
    "stw    r8,0(%1)\n"    
    "ldw    r9,0(%2)\n"
    "beq    r9,r0,1f\n"
    "andi    r8,r8,0\n"    
"1:     wrctl    ienable, r8\n"
    : /* No output */
    : "r" (mask), "r" (&saved_ienable), "r" (&is_nested_interrupt)
    : "r8", "r9");
    local_irq_restore(flags);
}
/*
 * Use a one to set the bit.
 */
static inline void setimr(int mask)
{
    int flags;
    local_irq_save(flags);
    __asm__ __volatile__(
    "ldw    r8,0(%1)\n"
    "or        r8,r8,%0\n"
    "stw    r8,0(%1)\n"    
    "ldw    r9,0(%2)\n"
    "beq    r9,r0,1f\n"
    "andi    r8,r8,0\n"    
"1:     wrctl    ienable, r8\n"    
    : /* No output */
    : "r" (mask), "r" (&saved_ienable), "r" (&is_nested_interrupt)
    : "r8", "r9");
    local_irq_restore(flags);
}

Finally the problem is, that even when ienable is set to ZERO during interrupt service routine, the Linux fails to continue somewhere inside the first interrupt routine. More preciselly I found that some first ISR is beiing called and of course "local_irq_enable()" is called to set-up PIE bit in status register, but note that at the same time ienable is set to zero. Then later during servicing this ISR some instruction trap execption is processed and some uknown time later, but very soon (because of no Linux booting console output) it somewhere hangs. I just can say, that it is almost impossible to debug where and what happened.

And now the question is, if PIE=1 and ienable=0, how much is it different from the situation when PIE=0 and ienable is non-zero..??? In both cases interrupts are disabled, but in the first one the Linux during boot hangs possibly after returning from instruction trap servicing. Thank you very much for the explanation.

jan

11 Replies

  • Altera_Forum's avatar
    Altera_Forum
    Icon for Honored Contributor rankHonored Contributor

    <div class='quotetop'>QUOTE (jan @ Sep 11 2009, 11:20 AM) <{post_snapback}> (index.php?act=findpost&pid=23815)</div>

    --- Quote Start ---

    ... but experiences shows to HDL designers, that using proved components is allways much better option.[/b]

    --- Quote End ---

    I did not vote for not using the "proven HDL design". I just said that if it can be accessed by the CPU via the Avalon bus, it also can be accessed via a (new) dedicated Avalon-bus-mastering HDL (DMA-type) component.

    What I maybe would do, is not enhance the proven hdl component (e.g. by FIFOs), but create an additional HDL component that accesses it via the Avalon bus.

    The Avalon bus is clever enough to allow for multiple accesses from different masters to different slaves at the same time, so just by configuration done by software, you can have your the "DMA-Controller" use the normal Linux RAM (introducing bus conflict that the Avalon bus solves by delays) or a dedicated on-chip ram (no bus conflicts).

    IMHO this is easier to do, better testable and more flexible than a design with dedicated FIFOs.

    -Michael