Interrupts with the VIC

I have a problem and cant find the solution.

I use a LPC2138, and need to use interrupts, actually i only need one interrupt for now (but that can change in the future), so i want to implement it throught the VIC., but a can’t make it work. But if i dont use the VIC, if i put the address of the isr directly in the exception vectors in the startup file it works great, a longer explanation:

I’m using a falling edge eint0 interrupt.

if in styrtup.s i have:

ldr       pc, irq_addr
.
.
irq_addr:     .long   nm_isr

The interrupt works perfectly. When the interrupt occurs, the program enters the nm_isr and executes it. If i check the VIC registers with my IDE i see (as expected): EXTINT=0x1, VICVectAddr=address of the nm_isr etc.

Then i try implementing it with the VIC:

startup.s:

ldr pc,[pc,#-0x0ff0]

interrupt setup:

EXTMODE=0x1
EXTPOLAR=0x0
EXTINT=0xf

VICIntSelect=0
VICVectAddr1 = (unsigned long)nm_isr;
VICDefVectAddr = (unsigned long)defint;
VICVectCntl1 = 0x20 | 14;

VICIntEnable=0x4000

The device that causes the external interrupt needs to get an istruction by the I2C bus (which is implemented without interrupts), so in the i2c send function i disable the external interrupt. Then i send the instruction, and as soon as the device gets the instruction it pulls the line low, producing an interrupt. I see, that the mcu acknowledges the interrupt (EXTINT=0x1, VICIntRaw=eint0). Then i re-enable the eint0 interrupt in the VICVectEnable register. At this point i should see that the VICVectAddr loads the address of the interrupt routine, but it doesn’t. It just holds the defint routine address like before the interrupt.

As i said the first implementation (address of routine directly into the interrupt vectors) all works fine, the code is identical, except that the second time i want to do it with the VIC.

The interrupts are enabled at the startup, the mcu works in user mode.

Any ideas?

Thank you.

Hmm, looks fine to me, assume you’ve doubled checked that you’ve got all the bits set up properly. Have you told MEMMAP that your vectors are in flash?

        /* interrupt vectors in flash */
        MEMMAP = (unsigned char) (0x01 & MEMMAP_MAP1_0_MASK);

I wrote a module to abstract all of this away for me, anyone is free to use it:

/* ----------------------------------------------------------------------------------------------------*/

#define LPC21XX_IRQ_MASK      ((unsigned long) 0x00000080)
#define LPC21XX_FIQ_MASK      ((unsigned long) 0x00000040)

#define LPC21XX_IRQ_CLEAR     VICVectAddr = ((unsigned long) 0x00000000)

#ifndef SPLINT
#define LPC21XX_CPSR_GET(r)   asm volatile (" mrs %0, cpsr" : "=r" (r) : /* null */ )
#define LPC21XX_CPSR_SET(r)   asm volatile (" msr cpsr, %0" : /* null */ : "r" (r) )
#else
#define LPC21XX_CPSR_GET(r)   /*@i@*/ r = 0
#define LPC21XX_CPSR_SET(r)   /*@i@*/ (void) r
#endif

static inline unsigned long lpc21XX_irq_disable (void)
{
        unsigned long r;
        LPC21XX_CPSR_GET (r);
        LPC21XX_CPSR_SET (r | LPC21XX_IRQ_MASK);
        return r;
}

static inline unsigned long lpc21XX_irq_enable (void)
{
        unsigned long r;
        LPC21XX_CPSR_GET (r);
        LPC21XX_CPSR_SET (r & ~LPC21XX_IRQ_MASK);
        return r;
}

static inline unsigned long lpc21XX_irq_restore (const unsigned long v)
{
        unsigned long r;
        LPC21XX_CPSR_GET (r);
        LPC21XX_CPSR_SET ((r & ~LPC21XX_IRQ_MASK) | (v & LPC21XX_IRQ_MASK));
        return r;
}

#define lpc21XX_irq_complete() LPC21XX_IRQ_CLEAR

static inline unsigned long lpc21XX_fiq_disable (void)
{
        unsigned long r;
        LPC21XX_CPSR_GET (r);
        LPC21XX_CPSR_SET (r | LPC21XX_FIQ_MASK);
        return r;
}

#ifdef LPC21XX_ENABLE_FIQ

static inline unsigned long lpc21XX_fiq_enable (void)
{
        unsigned long r;
        LPC21XX_CPSR_GET (r);
        LPC21XX_CPSR_SET (r & ~LPC21XX_FIQ_MASK);
        return r;
}

static inline unsigned long lpc21XX_fiq_restore (const unsigned long v)
{
        unsigned long r;
        LPC21XX_CPSR_GET (r);
        LPC21XX_CPSR_SET ((r & ~LPC21XX_FIQ_MASK) | (v & LPC21XX_FIQ_MASK));
        return r;
}

#endif

/* ----------------------------------------------------------------------------------------------------*/

/* add others as required */
#define LPC21XX_IRQ_CHANNEL_WDT     ((unsigned char) 0)
#define LPC21XX_IRQ_CHANNEL_TIMER0  ((unsigned char) 4)
#define LPC21XX_IRQ_CHANNEL_TIMER1  ((unsigned char) 5)
#define LPC21XX_IRQ_CHANNEL_UART0   ((unsigned char) 6)
#define LPC21XX_IRQ_CHANNEL_UART1   ((unsigned char) 7)
#define LPC21XX_IRQ_CHANNEL_EINT0   ((unsigned char) 14)
#define LPC21XX_IRQ_CHANNEL_BOD     ((unsigned char) 20)
#define LPC21XX_IRQ_CHANNEL_MAX     ((unsigned char) 22)

#define LPC21XX_IRQ_VECTOR_SIZE     ((unsigned char) 16)
#define LPC21XX_IRQ_VECTOR_ADDR(o)  (*((volatile unsigned long *) (0xFFFFF100 + (((unsigned long) o) << 2))))
#define LPC21XX_IRQ_VECTOR_CTRL(o)  (*((volatile unsigned long *) (0xFFFFF200 + (((unsigned long) o) << 2))))

static void lpc21XX_irq_vector_attach (const unsigned char irq, const unsigned long handler)
{
        unsigned char offset;

        assert (irq < LPC21XX_IRQ_CHANNEL_MAX);
        assert (handler != 0);

        for (offset = (unsigned char) 0; offset < LPC21XX_IRQ_VECTOR_SIZE; offset += (unsigned char) 1)
        {
                if ((LPC21XX_IRQ_VECTOR_CTRL (offset) & 0x20) == 0x00)
                {
                        const unsigned long c = lpc21XX_irq_disable ();

                        LPC21XX_IRQ_VECTOR_ADDR (offset) = handler;
                        LPC21XX_IRQ_VECTOR_CTRL (offset) = (unsigned long) (0x20 | irq);

                        VICIntSelect &= ~(1L << irq); /* set IRQ to be vectored */
                        VICIntEnable |= (1L << irq);  /* enable IRQ */

                        (void) lpc21XX_irq_restore (c);

                        break;
                }
        }

        assert (offset < LPC21XX_IRQ_VECTOR_SIZE);
}

static void lpc21XX_irq_vector_detach (const unsigned char irq)
{
        unsigned char offset;

        assert (irq < LPC21XX_IRQ_CHANNEL_MAX);

        for (offset = (unsigned char) 0; offset < LPC21XX_IRQ_VECTOR_SIZE; offset += (unsigned char) 1)
        {
                if ((unsigned char) (LPC21XX_IRQ_VECTOR_CTRL (offset) & 0x20) != (unsigned char) 0x00 && (unsigned char) (LPC21XX_IRQ_VECTOR_CTRL (offset) & 0x1F) == irq)
                {
                        const unsigned long c = lpc21XX_irq_disable ();

                        LPC21XX_IRQ_VECTOR_CTRL (offset) = 0x00000000;
                        LPC21XX_IRQ_VECTOR_ADDR (offset) = 0x00000000;

                        (void) lpc21XX_irq_restore (c);

                        break;
                }
        }

        VICIntEnClr = (unsigned long) (1L << irq); /* disable IRQ */
}

/* ----------------------------------------------------------------------------------------------------*/

static void lpc21XX_irq_init (void)
{
        /* interrupt vectors in flash */
        MEMMAP = (unsigned char) (0x01 & MEMMAP_MAP1_0_MASK);

        /* interrupt config: nothing */
        VICIntEnClr  = (unsigned long) 0xFFFFFFFF;
        VICIntSelect = (unsigned long) 0x00000000;
        VICIntEnable = (unsigned long) 0x00000000;
        VICDefVectAddr = (unsigned long) irq_handler;

        /* irq enabled, fiq disabled */
        (void) lpc21XX_irq_enable ();
        (void) lpc21XX_fiq_disable ();

        /* EXTWAKE */
}

static void lpc21XX_irq_term (void)
{
        /* irq disabled, fiq disabled */
        (void) lpc21XX_irq_disable ();
        (void) lpc21XX_fiq_disable ();

        /* clear default vector address */
        VICDefVectAddr = (unsigned long) 0x00000000;
}

/* ----------------------------------------------------------------------------------------------------*/

For example:

/* ----------------------------------------------------------------------------------------------------*/

/*@null@*/ static lpc21XX_shutdown_callback_t lpc21XX_shutdown_callback;

#ifdef HARDWARE_24
static void __attribute__ ((interrupt ("IRQ"))) lpc21XX_shutdown_irq_handler (void)
{
        lpc21XX_shutdown_trigger ();
        EXTINT = (unsigned char) ((1<<EXTINT_EINT0_BIT) & EXTINT_EINT0_MASK);
        lpc21XX_irq_complete ();
}
#endif

void lpc21XX_shutdown_trigger (void)
{
        if (lpc21XX_shutdown_callback != NULL)
        {
                (*lpc21XX_shutdown_callback) ();
        }
}

void lpc21XX_shutdown_enable (const lpc21XX_shutdown_callback_t callback)
{
        assert (callback != NULL);

        lpc21XX_shutdown_callback = callback;

#ifdef HARDWARE_24
        PINSEL1 |= (1L<<0);
        IO0DIR &= ~(1L<<16);

        EXTINT = (unsigned char) ((1<<EXTINT_EINT0_BIT) & EXTINT_EINT0_MASK);
        EXTMODE |= (1<<0);
        EXTPOLAR &= ~(1<<0);

        lpc21XX_irq_vector_attach (LPC21XX_IRQ_CHANNEL_EINT0, (unsigned long) lpc21XX_shutdown_irq_handler);
#endif
}

void lpc21XX_shutdown_disable (void)
{
#ifdef HARDWARE_24
        lpc21XX_irq_vector_detach (LPC21XX_IRQ_CHANNEL_EINT0);

        EXTINT = (unsigned char) ((1<<EXTINT_EINT0_BIT) & EXTINT_EINT0_MASK);
        EXTMODE &= ~(1<<0);

        PINSEL1 &= ~(1L<<0);
#endif

        lpc21XX_shutdown_callback = NULL;
}

/* ----------------------------------------------------------------------------------------------------*/

Have you told MEMMAP that your vectors are in flash?

Well i’m pretty sure i didnt do that. Thank you very much!

After setting up the vic you have to write to the VICVectAddr as shown below.

VICVectAddr=0xff;

This releases the priority hardware to work again. You also have to do that after interrupt servicing.

Yep that did the trick. Funny i didnt see that mentioned anywhere (or maybe i just looked badly).

Although i had VICVectAddr=0x0 at the end of the isr (i found this mentioned somewhere).

Anyway thanks for the help, very much appreciated.

I am still having problems getting EINT0 to work. I can poll button 2.10 and when it is pressed EXTINT is set to EINT0. So the interrupt was signaled but the ISR is never being called. Any help? Below is my code. I noticed in an earlier post that startup.s was modified… does it have to be?

   DWORD i = 0;
   DWORD *vect_addr, *vect_cntl;
     
   /* initialize VIC*/
   VICIntEnClr = 0xffffffff;
   VICVectAddr = 0;
   VICIntSelect = 0;
   
   /* set all the vector and vector control register to 0 */
   for ( i = 0; i < VIC_SIZE; i++ )
   {
     vect_addr = (DWORD *)(VIC_BASE_ADDR + VECT_ADDR_INDEX + i*4);
     vect_cntl = (DWORD *)(VIC_BASE_ADDR + VECT_PRIO_INDEX + i*4);
     *vect_addr = 0x0; 
     *vect_cntl = 0xF;
   }

   //setup VIC
   MEMMAP = 0x10;
   PINSEL4 = 0x00100000; /* set P2.10 as EINT0 and
                                                  P2.0~7 GPIO output */
                                                   
   VICVectAddr14  = (U32)EINT0_Handler;
   VICVectCntl14  = 0x1;     // Interrupt Priority 0-15 -- 15 is lowest
   
   IO2_INT_EN_F = U32_BIT10; /* Port2.10 is falling edge. */
   EXTMODE = EINT0_EDGE; /* INT0 edge trigger */
   EXTPOLAR = 0;         /* INT0 is falling edge by default */
   EXTINT = 0xf;

   // Enable EINT0 (bit14)
   VICIntEnable |=  U32_BIT14;

   // Release the Priority Hardware
   VICVectAddr = 0xff;

IF you are using VICVector slot #14 as it apepars that you are with:

VICVectAddr14 = (U32)EINT0_Handler;

you must ENABLE that slot by writing to its matching control register with

the bit number for the IRQ source (EINT0 is 14(Decimal) or 0x0E) in the lower 5 bits, and have the ENABLE bit set as follows:

VICVectCntl14 = 0x2E; // Bits 0-4 contain IRQ bit source #, Bit 5 is enable bit

Otherwise, the VIC controller HW doesn’t have any way to associate the VICVectAddr14 value to any IRQ source. Just remember that VICVectAddrx and VICVectCntlx registers are used in pairs, and that the VICVectCntlx register is what the VIC HW uses to find a valid address to jump to for a given interrupt.