Reply by rtstofer January 15, 20122012-01-15
--- In l..., Cristi Neagu wrote:
>
> Thanks for the explanations, Richard.
>
> About that project, a couple of questions:
>
> 1. What development chain is it written in?
Any GCC toolchain should work. That would include a Windows package like YAGARTO.

The Makefile indicates to me that I wrote that on a Linux box with GNUARM. You would need to change the paths to the tools. To use the lastest version of YAGARTO, all references to 'arm-elf-' need to be changed to 'arm-none-eabi-'. The YAGARTO tools would need to be on the path or fully specified in the Makefile.
> 2. If i understand it correctly, all I need to do is add IRQ_Wrapper
> to my startup.s (or crt.s), and point IRQ_Addr to the wrapper, and
> interrupts should work correctly and be nested?

Yes, you will see where the wrapper reads the VIC and gets the vector address. It reenables interrupts before branching to the handler address and this allow the VIC to interrupt again if a higher priority interrupt should occur.

Having all of this wrapper code, the need for the prolog and epilog code created by the __irq spec is no longer needed and all handler functions are ordinary C functions. Of course this is one of the advantages of the Cortex-M3 devices. Everything can be written in C.

Good luck and enjoy! Several years ago I was working through the very same issues.

Richard

An Engineer's Guide to the LPC2100 Series

Reply by Cristi Neagu January 15, 20122012-01-15
Thanks for the explanations, Richard.

About that project, a couple of questions:

1. What development chain is it written in?
2. If i understand it correctly, all I need to do is add IRQ_Wrapper
to my startup.s (or crt.s), and point IRQ_Addr to the wrapper, and
interrupts should work correctly and be nested?

Also, with regard to non-vectored interrupts, I agree. I made them
work pretty much like normal interrupts. But unless you plan on using
more than 16 interrupts in your project... they are pretty useless.

Thanks again for all the help, and for the example you provided.
--
Cristi Neagu
Reply by rtstofer January 15, 20122012-01-15
--- In l..., "Cristi Neagu" wrote:
> Two more things to add:
> 1. I don't know why these things work, but they do. Maybe someone can explain further.

They work because the VIC is pretty smart. For FIQs there is only one non-vectored vector address and the handler has to separate multiple FIQ interrupt sources. FIQ was really intended for ONE very fast interrupt. The vector is placed in such a way that the FIQ handler could be placed at the end of the vector table with no branching required. The VIC causes the CPU to branch to the FIQ entry point, the code does whatever as fast as it can (no branching around) and returns.

Vectored interrupts are different. Each interrupt source can be assigned a priority and a handler address. When an interrupt occurs, the code at the IRQ vector reads the handler address from the VIC. For vectored interrupts you have to clear the interrupt source and write something (it doesn't matter what) to the VICVectAddr registers to acknowledge the interrupt.

For non-vectored interrupts, the VIC will provide the default handler address and the handler will have to sort out the sources. The interrupt source must be cleared and the interrupt acknowledged by writing something to VICVectAddr. NOTE: I'm pretty sure this is true but I have never used non-vectored interrupts. Seems pretty useless to me!

Grab that Blinking LED code before I delete it. I don't leave code lying around that I might have to support. The reason you want it is that it shows how to use a wrapper around vectored interrupts such that nesting will function properly and interrupts based on priority can actually work. BTW, I would increase the stack size for the IRQ and FIQ stacks. Looking at it, I'm pretty sure the stacks are only working by magic.

> 2. I still haven't got around non-vectored interrupts, but I think they should be easy to figure out.

See above. I don't know why you would want to use non-vectored interrupts.

[QUOTE] -- from LPC2148 User Manual

The VIC ORs the requests from all the vectored and non-vectored IRQs to produce the IRQ signal to the ARM processor. The IRQ service routine can start by reading a register from the VIC and jumping there. If any of the vectored IRQs are requesting, the VIC provides the address of the highest-priority requesting IRQs service routine, otherwise it provides the address of a default routine that is shared by all the non-vectored IRQs. The default routine can read another VIC register to see what IRQs are active.

[/QUOTE]

Richard

Reply by Cristi Neagu January 15, 20122012-01-15
Here it is: How to make interrupts work in Keil uVision4.

Software interrupts:

The interrupt function should be declared like this:

void __swi(0) mySWIfunction (void);
void __SWI_0(void)
{
//do something here;
}

And when it's called, you use mySWIfunction(); This is all documented in Keil's help. Just look for __swi in help. But what they don't tell you is that the SWI_table.s file they want you to include doesn't work properly because it lacks a handler. Instead, include in your project the file H:\Programming\Keil\ARM\Boards\Keil\MCB2300\USBCDC\SWI.s and import and declare your functions in that file. Everything should now work. To my untrained eye it doesn't matter if it's meant for another board, the handler works just the same.

Vectored interrupts:

You need to declare the interrupt function like this:

__irq void intr_handler(void)
{
//do something;
//make sure you clear the interrupt and
//write to VICVectAddr before exiting
}

and when you're setting up the VIC:

VICVectAddrn = (unsigned long) intr_handler;

Just replace n with whatever interrupt channel you desire.

Next, go into startup.s and make sure this line is commented out:

; LDR PC, IRQ_Addr

Everything should work.

Fast interrupts:

There are 2 ways of doing this:

1. declare your function like this:

__irq void FIQ_Handler(void)
{
//do something
//and remember to clear the interrupt before exit
}

then go in startup.s and add this line before the DCD part of the table:

IMPORT FIQ_Handler

and comment out this line:

;FIQ_Handler B FIQ_Handler

2. declare your function with whatever name:

__irq void my_FIQ_Handler(void)
{
//do something
//and remember to clear the interrupt before exit
}

then import your function before the table in statup.s:

IMPORT my_FIQ_handler

and change this line:

FIQ_Handler B FIQ_Handler

to

FIQ_Handler B my_FIQ_Handler

either of these methods work, but I prefer the first.

That should be it.

Two more things to add:
1. I don't know why these things work, but they do. Maybe someone can explain further.
2. I still haven't got around non-vectored interrupts, but I think they should be easy to figure out.

Thanks again for all the help, guys.

Reply by Cristi Neagu January 15, 20122012-01-15
Ok... With you'r help, I finally managed to figure out interrupts, and I'll post what I found for others to find.

First of all, I'd like to thanks you all for the help, especially Richard (rtstofer). Your 3 suggestions all worked, mainly:

>So, write it like this:
>
> FIQ_Handler B myFIQ_Handler
>
> Then rename your handler to 'myFIQ_Handler'.

and

>The problem you have is that you should NOT have the code
>
> FIQ_Handler B FIQ_Handler
>
> This is just a tight loop and this is where your CPU will hang.
>
> One solution is to just comment this line and make sure your >handler is named FIQ_Handler. The linker will then insert the >address of this function into the indirection table and all should >link fine.

(with the observation that you need to put in IMPORT FIQ_Handler before the table) and

>From a couple of NXP App notes I have read on FIQ interrupts, you >don't need to acknowledge the interrupt. You do need to clear the >interrupt source.

I'll post what I found in my next message, just to keep things a bit clearer.

Reply by rtstofer January 14, 20122012-01-14
--- In l..., "rtstofer" wrote:
From a couple of NXP App notes I have read on FIQ interrupts, you don't need to acknowledge the interrupt. You do need to clear the interrupt source.

So, I suspect your handler code will work fine after you fix up crt0.s

Richard

Reply by rtstofer January 14, 20122012-01-14
--- In l..., "Cristi Neagu" wrote:
> I can't open the .pdf, and the other link, while being a good example, doesn't cover my problem. There is nothing in there about setting up the vector table, and that's where the problem is.
>
> My handlers are set up like in the example on the page you indicated, but the program never reaches that point because the vector table isn't set up properly. And before you suggest, I did search Google for how to set up the vector table :P
>

These are the vectors and they are a CONSTANT - Do Not Modify Them

Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP ; Reserved Vector
; LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
LDR PC, FIQ_Addr

Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler

Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler <===== THIS needs to go away!
There's really nothing wrong with your vector table and you shouldn't be looking to change it.

When the VIC sees an FIQ, it causes the hardware to branch to the 8th vector - ALWAYS.

The LDR PC, FIQ_Addr causes the CPU to grab the FIQ_Handler address from the table entry at FIQ_Addr and put it in the PC. This is essentially an indirect jump through the table to the handler.

The problem you have is that you should NOT have the code

FIQ_Handler B FIQ_Handler

This is just a tight loop and this is where your CPU will hang.

One solution is to just comment this line and make sure your handler is named FIQ_Handler. The linker will then insert the address of this function into the indirection table and all should link fine.

Richard

Reply by David Smead January 14, 20122012-01-14
I don't use other peoples project code because you end up with stuff you
don't need, and are forced to fit your own code around their very
generalized approach.

I write my own crt.s file. Somewhere near the end up it I call a
function named "initialize_application". That's where all the I/O is
configured along with thinks like the MAM and RTC. At the bottom of it
is the function init_vic, shown below.

void init_vic( void )
{
typedef void (*func)(void);

/* enabling control slot with irq number 5. */
(*((volatile uint32_t *)(0xfffff200))) = 0x25;

/* setting vector 0 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff100))) = (uint32_t)sys_timr_isr;

/* enabling control slot with irq number 6. */
(*((volatile uint32_t *)(0xfffff204))) = 0x26;

/* setting vector 1 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff104))) = (uint32_t)uart0_isr;

/* enabling control slot with irq number 7. */
(*((volatile uint32_t *)(0xfffff208))) = 0x27;

/* setting vector 2 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff108))) = (uint32_t)uart1_isr;

/* enabling control slot with irq number 14. */
(*((volatile uint32_t *)(0xfffff20c))) = 0x2e;

/* setting vector 3 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff10c))) = (uint32_t)pwr_fail_isr;

/* enabling control slot with irq number 13. */
(*((volatile uint32_t *)(0xfffff210))) = 0x2d;

/* setting vector 4 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff110))) = (uint32_t)rtc_isr;

/* setting vic_vect_default to service routine (spurious_irq) */
(*((volatile uint32_t *)(0xfffff034))) = (uint32_t)spurious_irq;
} /* end of function init_vic */
That function is actually written by a Python program that I use to
generate the C code from a small table.

Hope this helps.

DaveS

On Sat, Jan 14, 2012 at 4:02 AM, Cristi Neagu wrote:

> **
>
> --- In l..., David Smead wrote:
> >
> > At the very least, you need to embed some asm code to return from the
> > interrupt in a C interrupt handler. That C isr function also has to be
> > declared "naked" so the compiler doesn't do the usual stack manipulations
> > or a normally called function.
> >
> > For me it's just as easy to write the ISR in asm and be done with.
> >
> > DaveS
> > Would you be willing to post an example of that?
>
> My problem, as I see it, is that I don't know how to tell the
> whatever-it-is-that-has-to-know-where-the-interrupt-handler-is where the
> interrupt handler is.
>
> I was trying to get software interrupts to work by following the example
> in Keil's help files , and I ran in the same problem. The handler function
> compiles OK, it is called OK, but everything hangs when it gets to the
> vector table. Could you please give me a small example of how the vector
> table in startup.s is set up?
>
> Thanks.
>
>
>


Reply by Phil Young January 14, 20122012-01-14
Hi again,

Sorry I was reading 2 emails and mistakenly thought you were using the
LPC1766.

The same applies though, in keil after compiling double click the project to
open the .map file, in this you can see the names that the compiler
generated, search for your ISR and note the name.

Then go to the startup file and replace the name that it branches to with
the name of your handler.

Then for ARM you will have to declare the function as interrupt which tells
the compiler to modify the entry / exit code so that it is an interrupt
handler.

You may also need to add an import statement in the startup file to import
your handlers symbol, this is the same as the say that the entry point is
imported.

So replace

LDR PC, FIQ_Addr

With

import myFIQ_Handler

LDR PC,myFIQ_Handler

And change your function declaration to

void myFIQ_Handler(void) __irq

Regards

Phil.

From: l... [mailto:l...] On Behalf Of
rtstofer
Sent: 14 January 2012 19:14
To: l...
Subject: [lpc2000] Re: Can't understand interrupts

--- In l... , "Cristi
Neagu" wrote:
>
> Hello. I'm a newb on these forums and on ARM programming, so any help
would be appreciated.
>
> I've been trying to get my head around interrupts, and i'm not making any
progress. I've been looking over the LPC2119 manual, as well as over "The
Insider's Guide for Philips" book, and I can't figure out what i'm doing
wrong.
>
> What I'm trying to do is to generate a fast interrupt via EXTINT1 that
will turn on a LED (it's inverted, so clearing the pin will turn the LED
on).
>
> Here's some code (I use Keil, btw):
>
> Interrupt handler:
>
> void FIQ_Handler(void) __irq
> {
> IOCLR0 = 0x10000; //Set the LED pins
> EXTINT = 0x00000002; //Clear the peripheral interrupt flag
> }
>
> Interrupt initialization:
>
> void initFiq(void)
> {
> IODIR0 = 0x10000; //Set the LED pins as outputs
> PINSEL0 |= 0xC0; //Enable the EXTINT1 interrupt
> VICIntSelect = 0x00008000; //Enable a Vic Channel as FIQ
> VICIntEnable = 0x00008000;
> }
>
> Main program:
>
> int main (void)
> {
> init_serial0(0x30);
> initFiq(); //Initialize the Fast interrupt source
> IOSET0 = 0x10000;
> while(1)
> {
> printf("Waiting for interrupt\n");
> }
> }
>
> And vectors in startup.s
>
> Vectors LDR PC, Reset_Addr
> LDR PC, Undef_Addr
> LDR PC, SWI_Addr
> LDR PC, PAbt_Addr
> LDR PC, DAbt_Addr
> NOP ; Reserved Vector
> ; LDR PC, IRQ_Addr
> LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
> LDR PC, FIQ_Addr
>
> Reset_Addr DCD Reset_Handler
> Undef_Addr DCD Undef_Handler
> SWI_Addr DCD SWI_Handler
> PAbt_Addr DCD PAbt_Handler
> DAbt_Addr DCD DAbt_Handler
> DCD 0 ; Reserved Address
> IRQ_Addr DCD IRQ_Handler
> FIQ_Addr DCD FIQ_Handler
>
> Undef_Handler B Undef_Handler
> SWI_Handler B SWI_Handler
> PAbt_Handler B PAbt_Handler
> DAbt_Handler B DAbt_Handler
> IRQ_Handler B IRQ_Handler
> FIQ_Handler B FIQ_Handler
>

Notice that the FIQ vector entry point branches to FIQ_Handler and that
FIQ_Handler branches to itself (loops). Unless FIQ_Handler is defined as a
'weak' symbol, your code will never get called. In fact, I think that if you
named your handler as FIQ_Handler, you would get a duplicate definition at
link time.

So, write it like this:

FIQ_Handler B myFIQ_Handler

Then rename your handler to 'myFIQ_Handler'.

Your handler MAY have to write VICVectAddr (any value) to tell the VIC that
the interrupt has been handled. I have never used FIQ so I'm not sure about
this.

VICVectAddr = 0x00000000; // just before leaving the handler

Richard



Reply by Cristi Neagu January 14, 20122012-01-14
>
> You can also use Google and look for 'arm fiq example' and you will find a number of references. Among them:
>
> http://cache.freescale.com/files/32bit/doc/app_note/AN2411.pdf
>
> http://winarm.scienceprog.com/arm-mcu-types/interrupt-system-of-arm-lpc2000-microcontrollers.html
>
> Note that crt.s may not even allocate stack space for FIQ mode. You need to check and see how much stack has been allocated for IRQ and FIQ mode.
>
> Richard
>

Thanks for the reply.

I can't open the .pdf, and the other link, while being a good example, doesn't cover my problem. There is nothing in there about setting up the vector table, and that's where the problem is.

My handlers are set up like in the example on the page you indicated, but the program never reaches that point because the vector table isn't set up properly. And before you suggest, I did search Google for how to set up the vector table :P