Forum Discussion
Altera_Forum
Honored Contributor
16 years agoNested 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. jan11 Replies
- Altera_Forum
Honored 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