EmbeddedRelated.com
Forums

Boot Monitors and remapping interrupt vectors

Started by Darcy Williams April 27, 2007
I'm using an LPC2138/2148 with Rowley CrossWorks and GCC 4.1.1

Essentially, I have a boot monitor in the first two sectors of flash
that checks the primary application image against another
application image, and if required copies the upgrade over the
primary application.

Addr: 0x0000 0000 - 0x0000 1FFF (sect: 0 - 1) - Custom boot monitor
Addr: 0x0000 4000 - 0x0001 FFFF (sect: 4 - 10) - Primary Application
Addr: 0x0002 0000 - 0x0003 FFFF (sect: 11 - 14) - Upgrade Image

The boot monitor then performs a jump to the primary application,
using the method:

typedef void (*func_ptr)(void);

func_ptr func = (void*)0x4000;
func();
The primary application then reconfigures the device, trashing all
the memory used by the boot monitor - reconfiguring stack sizes and
all that jazz. After jumping to the application, interrupts no
longer function. I had a suspicion this would be the case before
starting on this, but wasn't totally sure of that until I got this
far.

I think my solutions are along the lines of...
a) Have the primary application place it's interrupt vectors in RAM
and configure MEMMAP to RAM mode.
b) Configure the boot monitor interrupt vectors to jump ahead into
the vectors of the application (i'm unsure about how suitable this
is). Interrupts are NOT used by the boot monitor.

There are bound to be other people that have implemented similar
solutions, are there any pros/cons to each method?
Is this a reasonable method in which to implement a boot monitor
(not to be confused with the boot loader of course)?

Is there a better way to jump to the main application?

Thanks very much
Cheers
Darcy

An Engineer's Guide to the LPC2100 Series

I just started going through the pregenerated "startup.s" provided
by Rowley and found a preprocessor definition I hadn't noticed
before. The answer to my main question was to simply
define "SRAM_EXCEPTIONS" in the project settings. This has the
effect of copying the 64 byte table(s) from flash to RAM.

I'm still interested to hear anyone's opinions on this matter
though, especially about the method I used to jump to the primary
application.

thanks!
--- In l..., "Darcy Williams" wrote:
>
> I'm using an LPC2138/2148 with Rowley CrossWorks and GCC 4.1.1
>
> Essentially, I have a boot monitor in the first two sectors of
flash
> that checks the primary application image against another
> application image, and if required copies the upgrade over the
> primary application.
>
> Addr: 0x0000 0000 - 0x0000 1FFF (sect: 0 - 1) - Custom boot monitor
> Addr: 0x0000 4000 - 0x0001 FFFF (sect: 4 - 10) - Primary
Application
> Addr: 0x0002 0000 - 0x0003 FFFF (sect: 11 - 14) - Upgrade Image
>
> The boot monitor then performs a jump to the primary application,
> using the method:
>
> typedef void (*func_ptr)(void);
>
> func_ptr func = (void*)0x4000;
> func();
> The primary application then reconfigures the device, trashing all
> the memory used by the boot monitor - reconfiguring stack sizes
and
> all that jazz. After jumping to the application, interrupts no
> longer function. I had a suspicion this would be the case before
> starting on this, but wasn't totally sure of that until I got this
> far.
>
> I think my solutions are along the lines of...
> a) Have the primary application place it's interrupt vectors in
RAM
> and configure MEMMAP to RAM mode.
> b) Configure the boot monitor interrupt vectors to jump ahead into
> the vectors of the application (i'm unsure about how suitable this
> is). Interrupts are NOT used by the boot monitor.
>
> There are bound to be other people that have implemented similar
> solutions, are there any pros/cons to each method?
> Is this a reasonable method in which to implement a boot monitor
> (not to be confused with the boot loader of course)?
>
> Is there a better way to jump to the main application?
>
> Thanks very much
> Cheers
> Darcy
>
Darcy Williams wrote:
>
> I'm using an LPC2138/2148 with Rowley CrossWorks and GCC 4.1.1
>
> Essentially, I have a boot monitor in the first two sectors of flash
> that checks the primary application image against another
> application image, and if required copies the upgrade over the
> primary application.
>
> Addr: 0x0000 0000 - 0x0000 1FFF (sect: 0 - 1) - Custom boot monitor
> Addr: 0x0000 4000 - 0x0001 FFFF (sect: 4 - 10) - Primary Application
> Addr: 0x0002 0000 - 0x0003 FFFF (sect: 11 - 14) - Upgrade Image
>
> The boot monitor then performs a jump to the primary application,
> using the method:
>
> typedef void (*func_ptr)( void);
>
> func_ptr func = (void*)0x4000;
> func();
>
> The primary application then reconfigures the device, trashing all
> the memory used by the boot monitor - reconfiguring stack sizes and
> all that jazz. After jumping to the application, interrupts no
> longer function. I had a suspicion this would be the case before
> starting on this, but wasn't totally sure of that until I got this
> far.
>

You pretty much have the general sense of how it would be done. The
devil IS in the details though. On the system that I am building, there
are three components: bootstrap, loader and runtime apps. The bootstrap
conditions the system, contains a SWI handler and is resident in all
versions. The bootstrap is what the factory would program in as the
default image. The loader is responsible for determining what program
to run, pull it into RAM and execute it. The runtime apps (programs)
run in external SRAM (0x8101.0000) and reside on an SD card.

Since the system is copy protected, the bootstrap contains validation
code for the loader + runtime images. The bootstrap looks to see if the
loader is in Flash, and that is the most current version (against that
of the SD card copy). Then it programs the loader into Flash, or jumps
to it via the function pointer (as you used). Once the loader comes up,
it checks to see what program is to be loaded into RAM and executed. It
does this by pulling the program into the external RAM, then invoking a
Software Interrupt (SWI opcode) back down into the bootstrap. Bootstrap
then checks the image an jumps to it if it is good.

The bootstrap uses the SVC (supvervisory) stack and the other two
programs use the SYS (system) stack. Supervisor stack is locate in the
SRAM 0x4000.0000 area, the system stack is located in external RAM
0x8101.7FF0. Interrupt vectors are in the bootstrap flash. I do not
remap the interrupt vectors to RAM. The bootstrap code is merely on an
extended call to the loader...

The trick is to keep the stacks straight. The exception stacks are
initialized into the bootstrap area of RAM, the loader and runtime apps
map only their main stack into the external RAM. So, when an exception
occurs (SWI, abort, IRQ, FIQ), these all get vectored into the bootstrap
program flash. The exception is handled there. Software Interrupt
exception is handled by code within the bootstrap and results are passed
back via the r0..r3 registers.

IRQ exception is also handled by the bootstrap program flash, but the
address of the actual interrupt routine is taken from the VIC
vectoring. This allows the runtime programs to setup their own
interrupt handlers by merely programming the Interrupt function address
into the VIC. The runtime programs are not catching the exception, the
bootstrap does that and passes control to the corresponding function.
Here is the IRQ exception code in the bootstrap:

========= begin IRQ ===========__irq:
stmfd sp!,{lr} // save return address.
mrs lr,spsr // save status reg.
stmfd sp!,{r4-r5,lr} // save work regs and status reg.
ldr r4,=VICVectAddr // get ISR address from vic.
ldr r5,[r4]
msr cpsr_c,#MODE_SVC // Supervisor Mode and re-enable
interrupts.
stmfd sp!,{r0-r3,r12,lr} // save work registers and return on
stack.
mov lr,pc // set return to common exit of ISR.
mov pc,r5 // execute the proper ISR handler.
ldmfd sp!,{r0-r3,r12,lr} // restore work regs and return.
msr cpsr_c,#MODE_IRQ|I_BIT // switch back to IRQ mode,
interrupt off.
ldr r4,=VICVectAddr // ack the interrupt.
mov r5,#0xff
str r5,[r4] // touch VIC to update priorities.
ldmfd sp!,{r4-r5,lr} // restore work regs and IRQ return.
msr spsr_cxsf,lr // restore spsr of irq mode.
ldmfd sp!,{lr} // restore return address.
subs pc,lr,#0x4 // do the return from IRQ interrupt.
======== snip ================
Note that it reads the VICVectAddr which was set by the runtime program,
then it calls that function? The bootstrap does initially do some
interrupt vectoring to itself for the timers, etc., when it starts up.
But, when the runtime app executes, it overwrites those vectors,
effectively disabling the interrupt routines in the bootstrap.

It is a somewhat complex system to intially setup, however, once done,
it is easy to use. The trick is the management of the stack(s). At
first I thought that I could simply use a single (SYS) stack, this
proved to be complex, the use of two stack "realms" simplified things a lot.

Regards,

TomW
--
Tom Walsh - WN3L - Embedded Systems Consultant
http://openhardware.net http://cyberiansoftware.com http://openzipit.org
"Windows? No thanks, I have work to do..."
----------------
That's an excellent description! Thanks! That sounds like it could
have been very complicated to set up for the first time.

I'm wondering if I can do without having to worry about multiple
stacks... Or is this simply a problem I haven't encountered yet...?

In this case, when the boot monitor (aka bootstrap) has jumped to
the primary application, that's it for the boot monitor and we never
need to worry about it again... until reset. Its sole purpose is to
validate the working image against a CRC, and check the upgrade
process. If an upgrade needs to be performed, it overwrites the
existing working image... if, previously, an upgrade had started
but was interrupted (power failure), it begins again. That's the
purpose of this boot monitor in a nut shell. Very simple stuff.

Only one primary application, and always running from the same
location. Once the primary application starts, the idea is to
completely forget that the boot monitor ever existed... the
application shouldn't need to worry about it. Hopefully :)

Is there something I'm not thinking of?

Thanks again... I'll read through that again to see if I missed
anything.

--- In l..., Tom Walsh wrote:
> You pretty much have the general sense of how it would be done.
The
> devil IS in the details though. On the system that I am building,
there
> are three components: bootstrap, loader and runtime apps. The
bootstrap
> conditions the system, contains a SWI handler and is resident in
all
> versions. The bootstrap is what the factory would program in as
the
> default image. The loader is responsible for determining what
program
> to run, pull it into RAM and execute it. The runtime apps
(programs)
> run in external SRAM (0x8101.0000) and reside on an SD card.
>
> Since the system is copy protected, the bootstrap contains
validation
> code for the loader + runtime images. The bootstrap looks to see
if the
> loader is in Flash, and that is the most current version (against
that
> of the SD card copy). Then it programs the loader into Flash, or
jumps
> to it via the function pointer (as you used). Once the loader
comes up,
> it checks to see what program is to be loaded into RAM and
executed. It
> does this by pulling the program into the external RAM, then
invoking a
> Software Interrupt (SWI opcode) back down into the bootstrap.
Bootstrap
> then checks the image an jumps to it if it is good.
>
> The bootstrap uses the SVC (supvervisory) stack and the other two
> programs use the SYS (system) stack. Supervisor stack is locate
in the
> SRAM 0x4000.0000 area, the system stack is located in external RAM
> 0x8101.7FF0. Interrupt vectors are in the bootstrap flash. I do
not
> remap the interrupt vectors to RAM. The bootstrap code is merely
on an
> extended call to the loader...
>
> The trick is to keep the stacks straight. The exception stacks
are
> initialized into the bootstrap area of RAM, the loader and runtime
apps
> map only their main stack into the external RAM. So, when an
exception
> occurs (SWI, abort, IRQ, FIQ), these all get vectored into the
bootstrap
> program flash. The exception is handled there. Software
Interrupt
> exception is handled by code within the bootstrap and results are
passed
> back via the r0..r3 registers.
>
> IRQ exception is also handled by the bootstrap program flash, but
the
> address of the actual interrupt routine is taken from the VIC
> vectoring. This allows the runtime programs to setup their own
> interrupt handlers by merely programming the Interrupt function
address
> into the VIC. The runtime programs are not catching the
exception, the
> bootstrap does that and passes control to the corresponding
function.
> Here is the IRQ exception code in the bootstrap:
>
> ========= begin IRQ ===========> __irq:
> stmfd sp!,{lr} // save return address.
> mrs lr,spsr // save status reg.
> stmfd sp!,{r4-r5,lr} // save work regs and status reg.
> ldr r4,=VICVectAddr // get ISR address from vic.
> ldr r5,[r4]
> msr cpsr_c,#MODE_SVC // Supervisor Mode and re-enable
> interrupts.
> stmfd sp!,{r0-r3,r12,lr} // save work registers and
return on
> stack.
> mov lr,pc // set return to common exit of
ISR.
> mov pc,r5 // execute the proper ISR handler.
> ldmfd sp!,{r0-r3,r12,lr} // restore work regs and
return.
> msr cpsr_c,#MODE_IRQ|I_BIT // switch back to IRQ mode,
> interrupt off.
> ldr r4,=VICVectAddr // ack the interrupt.
> mov r5,#0xff
> str r5,[r4] // touch VIC to update priorities.
> ldmfd sp!,{r4-r5,lr} // restore work regs and IRQ
return.
> msr spsr_cxsf,lr // restore spsr of irq mode.
> ldmfd sp!,{lr} // restore return address.
> subs pc,lr,#0x4 // do the return from IRQ
interrupt.
> ======== snip ================>
> Note that it reads the VICVectAddr which was set by the runtime
program,
> then it calls that function? The bootstrap does initially do some
> interrupt vectoring to itself for the timers, etc., when it starts
up.
> But, when the runtime app executes, it overwrites those vectors,
> effectively disabling the interrupt routines in the bootstrap.
>
> It is a somewhat complex system to intially setup, however, once
done,
> it is easy to use. The trick is the management of the stack(s).
At
> first I thought that I could simply use a single (SYS) stack, this
> proved to be complex, the use of two stack "realms" simplified
things a lot.
>
> Regards,
>
> TomW
> --
> Tom Walsh - WN3L - Embedded Systems Consultant
> http://openhardware.net http://cyberiansoftware.com
http://openzipit.org
> "Windows? No thanks, I have work to do..."
> ----------------
>
--- In l..., "Darcy Williams" wrote:
>
> That's an excellent description! Thanks! That sounds like it
could
> have been very complicated to set up for the first time.
>
> I'm wondering if I can do without having to worry about multiple
> stacks... Or is this simply a problem I haven't encountered
yet...?
>
> In this case, when the boot monitor (aka bootstrap) has jumped to
> the primary application, that's it for the boot monitor and we
never
> need to worry about it again... until reset. Its sole purpose is
to
> validate the working image against a CRC, and check the upgrade
> process. If an upgrade needs to be performed, it overwrites the
> existing working image... if, previously, an upgrade had started
> but was interrupted (power failure), it begins again. That's the
> purpose of this boot monitor in a nut shell. Very simple stuff.
>
> Only one primary application, and always running from the same
> location. Once the primary application starts, the idea is to
> completely forget that the boot monitor ever existed... the
> application shouldn't need to worry about it. Hopefully :)
>
> Is there something I'm not thinking of?
>
> Thanks again... I'll read through that again to see if I missed
> anything.

You'll probably find that your requirements are somewhat simpler than
Tom's, and you can use one stack quite happily: we certainly do.

On the LPC2xxx, as you have vectored interrupts in any case, you can
use the VIC to do the vectoring, as Tom suggested. The boot loader
can have the main vectors (reset, IRQ, abort etc.), and the IRQ
assembler stub code (I posted an example of this here recently). This
will end up as one small assembler file: the rest of your system can
be in 'C' or whatever.

One refinement I would put in is to put a good CRC check on the code
you're jumping to (we use PPP's) in order to verify the image before
you jump to it.

We keep the boot loader as simple as possible, on the basis the more
that's in it the more potential problems it'll have, and the more
tweaking it will need. As it's never changed in the field, this isn't
really on. For example, we never call the boot-loader directly: the
only route into it is through the reset vector. If you want to switch
images the application writes to a (non-volatile) location and uses
the watchdog to reset the system.

Brendan
--- In l..., Tom Walsh wrote:
> ========= begin IRQ ===========> __irq:
> stmfd sp!,{lr} // save return address.
> mrs lr,spsr // save status reg.
> stmfd sp!,{r4-r5,lr} // save work regs and status reg.
> ldr r4,=VICVectAddr // get ISR address from vic.
> ldr r5,[r4]
> msr cpsr_c,#MODE_SVC // Supervisor Mode and re-enable
> interrupts.
> stmfd sp!,{r0-r3,r12,lr} // save work registers and
return on
> stack.
> mov lr,pc // set return to common exit of ISR.
> mov pc,r5 // execute the proper ISR handler.
> ldmfd sp!,{r0-r3,r12,lr} // restore work regs and return.
> msr cpsr_c,#MODE_IRQ|I_BIT // switch back to IRQ mode,
> interrupt off.
> ldr r4,=VICVectAddr // ack the interrupt.
> mov r5,#0xff
> str r5,[r4] // touch VIC to update priorities.
> ldmfd sp!,{r4-r5,lr} // restore work regs and IRQ return.
> msr spsr_cxsf,lr // restore spsr of irq mode.
> ldmfd sp!,{lr} // restore return address.
> subs pc,lr,#0x4 // do the return from IRQ interrupt.
> ======== snip ================
For the benefit of those who are looking at abstracting a general
purpose re-entrant IRQ handler from the above solution, I like to
point out that the above solution assumes FIQ is always enabled on
entry into IRQ handler.

In general this is not true and I find it useful useful to de-couple
as much as possible exception handling routines from each other so as
to avoid problems later on that are very hard to debug. So I would
preserve the FIQ bit rather than clear (or set) based on apriori
assumptions.

> It is a somewhat complex system to intially setup, however, once done,
> it is easy to use. The trick is the management of the stack(s). At
> first I thought that I could simply use a single (SYS) stack, this
> proved to be complex, the use of two stack "realms" simplified
things a lot.

In general one cannot avoid having a separate stack for each kind of
exception, but the trick I found is to switch to the working (SYS/USR)
stack as soon as practicable, so as to keep the size of the peripheral
stacks trivially small.

Jaya
Hi Darcy,

Looks like a lot of LPC users have given this a lot of thought. I kept
things a lot simpler than the other replies I read.

For the Software Interrupt vector (SWI instruction) I use a fixed
address in the application sectors.

The IRQ address is loaded directly from the VIC.

For the FIQ the "MOV PC, R12" is used. Before using the FIQ you can call
a assembly function to set the R12 in the FIQ register bank. R12 will be
preserved by an IRQ function implemented in C or an assembly function
can leave another address in R12 depending on the state of the hardware
at the end of the interrupt. I used this for a high priority ADC
interrupt to remember wich ADC channel was selected. This saved me a
"switch (adc_channel) { .... }" like construct at the start of the FIQ.

The other exceptions are handled by the custom boot loader. They usually
just reset the device, but they can be used to dump the registers to a
serial port.

This way it is usually not needed to remap the vectors to SRAM.

Regards,
Richard.

Darcy Williams schreef:
> I'm using an LPC2138/2148 with Rowley CrossWorks and GCC 4.1.1
>
> Essentially, I have a boot monitor in the first two sectors of flash
> that checks the primary application image against another
> application image, and if required copies the upgrade over the
> primary application.
>
> Addr: 0x0000 0000 - 0x0000 1FFF (sect: 0 - 1) - Custom boot monitor
> Addr: 0x0000 4000 - 0x0001 FFFF (sect: 4 - 10) - Primary Application
> Addr: 0x0002 0000 - 0x0003 FFFF (sect: 11 - 14) - Upgrade Image
>
> The boot monitor then performs a jump to the primary application,
> using the method:
>
> typedef void (*func_ptr)(void);
>
> func_ptr func = (void*)0x4000;
> func();
> The primary application then reconfigures the device, trashing all
> the memory used by the boot monitor - reconfiguring stack sizes and
> all that jazz. After jumping to the application, interrupts no
> longer function. I had a suspicion this would be the case before
> starting on this, but wasn't totally sure of that until I got this
> far.
>
> I think my solutions are along the lines of...
> a) Have the primary application place it's interrupt vectors in RAM
> and configure MEMMAP to RAM mode.
> b) Configure the boot monitor interrupt vectors to jump ahead into
> the vectors of the application (i'm unsure about how suitable this
> is). Interrupts are NOT used by the boot monitor.
>
> There are bound to be other people that have implemented similar
> solutions, are there any pros/cons to each method?
> Is this a reasonable method in which to implement a boot monitor
> (not to be confused with the boot loader of course)?
>
> Is there a better way to jump to the main application?
>
> Thanks very much
> Cheers
> Darcy
>
>
--- In l..., "jayasooriah" wrote:
>
> For the benefit of those who are looking at abstracting a general
> purpose re-entrant IRQ handler from the above solution, I like to
> point out that the above solution assumes FIQ is always enabled on
> entry into IRQ handler.
>
> In general this is not true and I find it useful useful to de-couple
> as much as possible exception handling routines from each other so
as
> to avoid problems later on that are very hard to debug. So I would
> preserve the FIQ bit rather than clear (or set) based on apriori
> assumptions.
>

In the general case, this is of course true: best not make any
assumptions about the rest of the system, and to always restore it to
the state you left it.

However, to my mind if you ever disable FIQs, you loose one of the
benefits of using them: a predictable latency with the worst case
response of standard overhead plus maximum instruction length. If you
disable them, you have an unknown upper bound on the response time.
There's little difference between having something as the highest
priority IRQ interrupt and an FIQ in this situation (assuming you
support nested interrupts). If you take the approach of never
disabling FIQs, things are somewhat simpler (in particular you don't
have to worry about the ARM's quirks relating to enabling/disabling
IRQ/FIQs).

Before jumping down my throat, Jaya, I'm simply suggesting this
approach as an alternative: it's not a criticism of what you say
(which as I mention, I'd tend to agree with in general).

Brendan
--- In l..., "Brendan Murphy"
wrote:
> However, to my mind if you ever disable FIQs, you loose one of the
> benefits of using them: a predictable latency with the worst case
> response of standard overhead plus maximum instruction length. If you
> disable them, you have an unknown upper bound on the response time.
> There's little difference between having something as the highest
> priority IRQ interrupt and an FIQ in this situation (assuming you
> support nested interrupts). If you take the approach of never
> disabling FIQs, things are somewhat simpler (in particular you don't
> have to worry about the ARM's quirks relating to enabling/disabling
> IRQ/FIQs).

If it is true that one "loses the benefits of FIQ" by disabling FIQ,
then the same should apply to disabling IRQ. Clearly it is not true.
If you could not think of a scenario where this is necessary you could
just ask an open question.

Off the cuff, one example is when FIQ is used in a manner similar to
DMA, but for peripherals that do not support DMA. You *must* suspend
this "DMA" handled by FIQ handler when you fiddle with the buffers.

There are many examples of this kind of use. The FIQ and IRQ bits are
independent in the PSR precisely for this purpose.

> Before jumping down my throat, Jaya, I'm simply suggesting this
> approach as an alternative: it's not a criticism of what you say
> (which as I mention, I'd tend to agree with in general).

I trust pointing out that your "alternative approach" is based on
false premises does not tantamount to "jumping down your throat".

> Brendan

Jaya
--- In l..., "jayasooriah" wrote:
> If it is true that one "loses the benefits of FIQ" by disabling FIQ,
> then the same should apply to disabling IRQ. Clearly it is not
true.

I rather think you've missed the point. If you need to have an
interrupt that needs on occasion to be disabled, just define it as an
IRQ.

> Off the cuff, one example is when FIQ is used in a manner similar to
> DMA, but for peripherals that do not support DMA. You *must*
suspend
> this "DMA" handled by FIQ handler when you fiddle with the buffers.

To use your example, I'd simply use the highest priority IRQ to
handle the DMA and enable and disable it as required.

>
> There are many examples of this kind of use. The FIQ and IRQ bits
are
> independent in the PSR precisely for this purpose.

FIQs are primarily needed to support very high priority interrupts
that are handled imediately. If you ever disable them, the latency of
the handler is unknown, which as I said obviates one of the main
reasons for using them (predictable worst case performance).

I see no reason to use an FIQ when a high-priority IRQ can be used in
its place.