EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

Min. Requirements for VIC in Crossworks w/2478

Started by Kevin Townsend June 20, 2009
There are a lot of example out there of how to configure vectored interrupts with GCC, but I'm a bit confused about what exactly needs to be done when using Crossworks (with the 23xx/24xx CPU package in my case). The "irq_handler" code, for example, is already provided, and I know that you need to add "VECTORED_IRQ_INTERRUPTS" to the preprocessor definitions. Do I still require something like VIClowlevel.c (which contains the following method the enable IRQ), though?:

static inline unsigned asm_get_cpsr(void)
{
unsigned long retval;
__asm volatile(mrs %0, cpsr" : "=r" (retval) : /* no inputs */);
return retval;
}

unsigned enableIRQ(void)
{
unsigned _cpsr;
_cpsr = asm_get_cpsr();
asm_set_cpsr(_cpsr & ~IRQ_MASK);
return _cpsr;
}

// There may be typos ... I just typed that in manually from
// something I had in a notebook, but you get the idea I think.

Is it as simple in Crossworks as adding "VECTORED_IRQ_INTERRUPTS" to the preprocessor definitions, and that's it? I haven't had any muck getting this to work, and I'm not sure how I can check if the VIC is enabled or not to know why my Timer 0 interrupt isn't firing.

void timer0_isr(void) __attribute__ ((interrupt("naked")));

int main( void )
{
#ifdef VECTORED_IRQ_INTERRUPTS
debug_printf("Vectored Interrupts Enabled");
#endif

cpuSetupHardware();
enableIRQ(); // Is this necessary, or is it already configured in Rowley startup code?

// Configure Timer0
SCB_PCONP |= SCB_PCONP_PCTIM0; // Add power to Timer 0

T0_TCR = T_TCR_CR; // Reset timer
T0_MR0 = 60000000 / 1; // Set timer match register (1 sec)
T0_MCR = T_MCR_MR0R | T_MCR_MR0I; // Raise an interrupt and reset on match/MR0
T0_TCR = T_TCR_CE; // Enable Timer0 interrupt

// Configure interrupt
VIC_VectPriority0 = VIC_Channel_Timer0; // Set Timer0 to Priority 0 (highest)
VIC_VectAddr0 = (unsigned)timer0_isr; // Pass the address of the IRQ handler into the VIC slot
VIC_IntEnable = VIC_IntEnable_Timer0; // Enable Timer0 interrupt in the VIC (See lpc214x U.Manual, p.54, table 42)

while (1);

return 0;
}

// Handle TIMER0 interrupt
void timer0_isr(void)
{
debug_printf("Timer Interrupt\r\n");

T0_IR = 0x01; // Clear timer interrupt
VIC_Address = 0; // Signal end of interrupt
}

An Engineer's Guide to the LPC2100 Series

Rather than trying to write the interrupt code myself, I've rewritten this to use the Crossworks CTL (as best I could understand the API documentation). Just to confirm, I've also already added the following the the "Preprocessor Definitions" in Project Configuration/Preprocessor:

SUPERVISOR_START
VECTORED_IRQ_INTERRUPTS

... but alas, I'm still not getting any love from the compiler. Presumably there is either something wrong with my timer, or my understanding of how to configure interrupts with the CTL is way out in left field. Or both.

I'm using the ARM Simulator for the moment, though I seem to recall that it worked with interrupts on the 2148 so a 2478 project should be the same(?).

-----------------------------

#include
#include

#include "cpu.h"

void timer0_isr(void);

int main(void)
{
cpuSetupHardware();

// Configure Timer0
SCB_PCONP |= SCB_PCONP_PCTIM0; // Add power to Timer 0
T0_TCR = T_TCR_CR; // Reset timer
T0_MR0 = 60000000 / 1; // Set timer match register (1 sec)
T0_MCR = T_MCR_MR0R | T_MCR_MR0I; // Raise an interrupt and reset on match/MR0
T0_TCR = T_TCR_CE; // Enable Timer0 interrupt

// Enable interrupts using Crossworks CTL
ctl_global_interrupts_enable();

// Set interrupt (vector, priority, trigger, isr, oldisr)
// VIC_Channel_Timer0 = 4 for reference sake
ctl_set_isr(VIC_Channel_Timer0, 1, CTL_ISR_TRIGGER_FIXED, timer0_isr, 0);

// Unmask interrupt
ctl_unmask_isr(VIC_Channel_Timer0);

while (1);

return 0;
}

// Handle TIMER0 interrupt
void timer0_isr(void)
{
ctl_global_interrupts_re_enable_from_isr();

debug_printf("Timer0 Interrupt\r\n");
T0_IR = 0x01; // Clear timer interrupt

ctl_global_interrupts_un_re_enable_from_isr();
}

Kevin Townsend wrote:

You code won't work for a number of reasons.
> Rather than trying to write the interrupt code myself, I've rewritten this to use the Crossworks CTL (as best I could understand the API documentation). Just to confirm, I've also already added the following the the "Preprocessor Definitions" in Project Configuration/Preprocessor:
>
> SUPERVISOR_START
> VECTORED_IRQ_INTERRUPTS
>
> ... but alas, I'm still not getting any love from the compiler. Presumably there is either something wrong with my timer, or my understanding of how to configure interrupts with the CTL is way out in left field. Or both.
>
> I'm using the ARM Simulator for the moment, though I seem to recall that it worked with interrupts on the 2148 so a 2478 project should be the same(?).
>
> -----------------------------
>
> #include
> #include #include "cpu.h"
>
> void timer0_isr(void);
>
> int main(void)
> {
> cpuSetupHardware();
>
> // Configure Timer0
> SCB_PCONP |= SCB_PCONP_PCTIM0; // Add power to Timer 0
> T0_TCR = T_TCR_CR; // Reset timer
> T0_MR0 = 60000000 / 1; // Set timer match register (1 sec)
> T0_MCR = T_MCR_MR0R | T_MCR_MR0I; // Raise an interrupt and reset on match/MR0
> T0_TCR = T_TCR_CE; // Enable Timer0 interrupt
>
> // Enable interrupts using Crossworks CTL
> ctl_global_interrupts_enable();
>
> // Set interrupt (vector, priority, trigger, isr, oldisr)
> // VIC_Channel_Timer0 = 4 for reference sake
> ctl_set_isr(VIC_Channel_Timer0, 1, CTL_ISR_TRIGGER_FIXED, timer0_isr, 0);
>
> // Unmask interrupt
> ctl_unmask_isr(VIC_Channel_Timer0);
>
> while (1);
>
> return 0;
> }
>
> // Handle TIMER0 interrupt
> void timer0_isr(void)
> {
> ctl_global_interrupts_re_enable_from_isr();
>
You haven't cleared the interrupt flag before enabling interrupts.
Hence, you will re-enter the ISR.
> debug_printf("Timer0 Interrupt\r\n");
>
debug_printf with a multi-tasking system will wait for an event; and you
can't wait for an event in an ISR. In essence, you cannot use
debug_printf in an ISR (what did you expect would happen?)

> T0_IR = 0x01; // Clear timer interrupt
>
> ctl_global_interrupts_un_re_enable_from_isr();
> }
>
CrossWorks ships with timer examples.

-- Paul.

Paul:

Thanks for the reply. I've tried digging around in the documentation, but I'll take a look at the timer examples as well. Curiously, I can get the interrupts (modified below) to work on the actual hardware with a JTAG debugger, but the interrupt never fires in the ARM Simulator.

#include
#include

#include "cpu.h"

void timer0_isr(void);

int main(void)
{
cpuSetupHardware();

// Configure Timer0
SCB_PCONP |= SCB_PCONP_PCTIM0; // Add power to Timer 0
T0_TCR = T_TCR_CR; // Reset timer
T0_MR0 = 60000000 / 1; // Set timer match register (1 sec)
T0_MCR = T_MCR_MR0R | T_MCR_MR0I; // Raise an interrupt and reset on match/MR0
T0_TCR = T_TCR_CE; // Enable Timer0 interrupt

// Configure interrupt using Crossworks CTL
ctl_global_interrupts_enable();
// Set interrupt (vector, priority, trigger, isr, oldisr)
ctl_set_isr(VIC_Channel_Timer0, 1, CTL_ISR_TRIGGER_FIXED, timer0_isr, 0);
// Unmask interrupt
ctl_unmask_isr(VIC_Channel_Timer0);

while (1);

return 0;
}

// Handle TIMER0 interrupt
void timer0_isr(void)
{
int setBreakpoint = 0;
// Clear timer interrupt
T0_IR = 0x01;
// Signal end of interrupt
VIC_Address = 0;
// reenable interrupt
ctl_global_interrupts_re_enable_from_isr();
}

Kevin Townsend wrote:
> Paul:
>
> Thanks for the reply. I've tried digging around in the documentation, but I'll take a look at the timer examples as well. Curiously, I can get the interrupts (modified below) to work on the actual hardware with a JTAG debugger, but the interrupt never fires in the ARM Simulator.
>

It never will. This is a core simulator, not a peripheral simulator.

-- Paul.

Is there something special that you need to do to clear/reset the interrupts when working with the CTL (I've looked at the documentation and nothing stood out to me, other than some routines to handle higher priority interrupts)?

Once I go into the ISR, it continually fires (every 'tick', rather than during the specified delay of 1 second). With manually handled interrupts it's usually just a matter of signaling the end of the interrupt (in Timer/EINT/etc.) and clearing 'VIC_Address' as follows, but that doesn't seem to do it in this case:

#define T0_IR (*(pREG32 (0xe0004000)))
#define T_IR_MR0 (0x00000001)
#define VIC_Address (*(pREG32 (0xffffff00)))

// Handle TIMER0 interrupt
void timer0_isr(void)
{
T0_IR = T_IR_MR0; // Clear timer interrupt
VIC_Address = 0; // Signal end of interrupt
}

I think the timer initialisation is OK (it's set to raise an interrupt and reset when a match occurs), and it's more or less the same code that has worked for me on the 2100s (with the same timer I believe):

// From page 627 of UM10237 v.2
#define T0_MCR (*(pREG32 (0xe0004014)))
#define T_MCR_MR0R (0x00000002)
#define T_MCR_MR0I (0x00000001)
...
// Configure Timer0
SCB_PCONP |= SCB_PCONP_PCTIM0; // Add power to Timer 0
T0_TCR = T_TCR_CR; // Reset timer
T0_MR0 = 60000000 / 1; // Set timer match register (1 sec)
T0_MCR = T_MCR_MR0R | T_MCR_MR0I; // Raise an interrupt and reset on match/MR0
T0_TCR = T_TCR_CE; // Enable Timer0 interrupt

Feel free to tell me to go crawl back under a rock, though, if it's just something plainly and obviously 'wrong' with my code. I'm used to feeling like a bit of an idiot by now. :-)

--- In l..., "Kevin Townsend" wrote:

Curiously, I can get the interrupts (modified below) to work on the actual hardware with a JTAG debugger, but the interrupt never fires in the ARM Simulator.

Hi Kevin,

the only simulator that includes peripherals (as far as I know) is the Keil simulator. You can test the logic of your code with the instruction set simulator but there is no interaction with hardware components possible. The Keil simulator simulates the whole device. You always need to check whether the particular MCU is fully supported by the Keil simulator, afaIk, the LPC2478 is supported.

Bob
http://www.mcu-related.com

Mate! Welcome to the group of the timer issue! I got exactually the same
issue as you did, still found no solution! This topic will be of high use to
me!

2009/6/21 Kevin Townsend

> Is there something special that you need to do to clear/reset the
> interrupts when working with the CTL (I've looked at the documentation and
> nothing stood out to me, other than some routines to handle higher priority
> interrupts)?
>
> Once I go into the ISR, it continually fires (every 'tick', rather than
> during the specified delay of 1 second). With manually handled interrupts
> it's usually just a matter of signaling the end of the interrupt (in
> Timer/EINT/etc.) and clearing 'VIC_Address' as follows, but that doesn't
> seem to do it in this case:
>
> #define T0_IR (*(pREG32 (0xe0004000)))
> #define T_IR_MR0 (0x00000001)
> #define VIC_Address (*(pREG32 (0xffffff00)))
>
> // Handle TIMER0 interrupt
> void timer0_isr(void)
> {
> T0_IR = T_IR_MR0; // Clear timer interrupt
> VIC_Address = 0; // Signal end of interrupt
> }
>
> I think the timer initialisation is OK (it's set to raise an interrupt and
> reset when a match occurs), and it's more or less the same code that has
> worked for me on the 2100s (with the same timer I believe):
>
> // From page 627 of UM10237 v.2
> #define T0_MCR (*(pREG32 (0xe0004014)))
> #define T_MCR_MR0R (0x00000002)
> #define T_MCR_MR0I (0x00000001)
> ...
> // Configure Timer0
> SCB_PCONP |= SCB_PCONP_PCTIM0; // Add power to Timer 0
> T0_TCR = T_TCR_CR; // Reset timer
> T0_MR0 = 60000000 / 1; // Set timer match register (1 sec)
> T0_MCR = T_MCR_MR0R | T_MCR_MR0I; // Raise an interrupt and reset on
> match/MR0
> T0_TCR = T_TCR_CE; // Enable Timer0 interrupt
>
> Feel free to tell me to go crawl back under a rock, though, if it's just
> something plainly and obviously 'wrong' with my code. I'm used to feeling
> like a bit of an idiot by now. :-)
>
>
>


This is my working Crossworks timer interrupt using CTL, if it's any
help.
The only major difference is that I'm clearing all the interrupt flags
by setting TxIR to 0xFF (because I am not using any other interrupts on
this timer) whereas you are clearing a specific interrupt bit.

#include
#include
#include

static void T1Isr (void)
{
static unsigned char x;

T1IR = 0xff; //reset all interrrupt flags

x++;
/* other code */
}

And this is the setup for it...

//set up timer 1 interrupt
T1TCR = 0x02; //stop and reset timer
T1PR = 0x00; //set prescaler to zero
T1MR0 = (14400000 / 1000); //set int every 1ms
T1IR = 0xff; //reset all interrrupt flags
T1MCR = 0x03; //interrupt and reset counter on match MR0
ctl_set_isr(5, 0, CTL_ISR_TRIGGER_FIXED, T1Isr, 0);
ctl_unmask_isr(5);
T1TCR = 0x01; //start timer

--
Tim Mitchell

--- In l..., "Kevin Townsend" wrote:
>
> Is there something special that you need to do to clear/reset the interrupts when working with the CTL (I've looked at the documentation and nothing stood out to me, other than some routines to handle higher priority interrupts)?
>

Well, there is something special that needs to happen in the timer interrupt in addition to the usual reschedule after an interrupt that can change thread state - the OS has to increment its tick timer and timeout waits and sleeps. Somewhere in your timer initialization, something should set up a call to something like 'ctl_increment_tick_from_isr()', perhaps by loading this function into a static so that it can be later called from the timer interrupt handler.

Are you doing this OK?

Rgds,
Martin


The 2024 Embedded Online Conference