[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: [avr-gcc-list] eicall on ATmega2561

From: Stu Bell
Subject: RE: [avr-gcc-list] eicall on ATmega2561
Date: Thu, 7 Aug 2008 16:04:54 -0600

*sigh*  Okay, now you're forcing me to work. ;-)

I'll start with EIND.  The return value is a bit of a "red herring" as
it doesn't really have much to do with the problem but is instead an
artifact of how you are trying to solve the problem.  EIND is used in
only two instructions: EIJMP and EICALL.  You don't really care what the
value is after you return from the EICALL, since you will need to set it
up next time for your *next* EICALL.  Same argument for EIJMP.  In other
words, stop obsessing about EIND.  So long as it is set to 1 before you
call the bootloader function from app space, you're golden.
Second, stop assigning C variables to registers.  It doesn't really
improve anything and, as you found out, makes your life miserable.  If
you think it makes faster code, I counter with the mantra, "*FIRST* make
it work, *THEN* make it fast".  You have yet to make it *work*, so you
don't pass step one of the mantra.
By the way, the "register" keyword is considered a hint by GCC, and
barely that.  GCC tends to ignore you, but then pays heed at the most
inopportune time.  Especially do not consider the register keyword to
apply across separate compile modules.
Okay, if I may restate the problem:  
- You have set of functions in your bootloader.
- You would to access those functions by index number, something like
MyCoolFunctions[1](foo, bar);
- You would like to access those functions from both inside and outside
the bootloader.

First, setting up the jump table is easy.  I set it up at the end of
memory, minus 128 bytes.  That should give:
---- inside BootLoader.h ------
/* Where the jump table resides, in bytes, for GCC */
#define BOOT_JUMP_TABLE_GCC  (0x3FF00UL)

/* Where the jump table resides, in words, minus the top bit. */
#define BOOT_JUMP_TABLE    ((uint16_t)( (BOOT_JUMP_TABLE_GCC >> 1UL) &
0xFFFFUL ) )

#define BOOT_VERSION   0  // return boot version
#define BOOT_MONITOR   1  // monitor entry point
#define BOOT_START_APP 2  // Start Application entry point
#define BOOT_FUNCTION3 3  // Boot function 3
#define BOOT_FUNCTION4 4  // Boot function 3
#define BOOT_FUNCTION5 5  // Boot function 3

---- inside Bootloader.c -----
void BootJumpTable ( void )
    asm volatile (
        "jmp BootVersion         \n\t"
        "jmp Monitor             \n\t"
        "jmp StartApp            \n\t"
        "jmp DownloadFirmware    \n\t"
        "jmp AppDownloadFirmware \n\t"

You should *always* include a BootVersion as the first call.  This
allows the calling app to determine if it is compatible with the boot,
and what extended services may exist.  Even if you don't think you'll
ever use this, trust me, you will.
In addition, I needed to add the following to my Makefile (generated
from Mfile, naturally):
    LDFLAGS += -Wl,--section-start=.bootjump=0x3FF00

I cannot use BOOT_JUMP_TABLE_GCC in the makefile since that's defined in
the wrong place.  You *could* define the symbol in the makefile, though,
and pass it in to the code as a defined symbol -- that's actually a good

If you look at the finished code (*after* linking), you see that the
full 17-bit jmp is generated:
void BootJumpTable ( void )
   3ff00:    0d 94 42 f9     jmp    0x3f284    ; 0x3f284 <BootVersion>
   3ff04:    0d 94 00 f0     jmp    0x3e000    ; 0x3e000 <Monitor>
   3ff08:    0d 94 00 f5     jmp    0x3ea00    ; 0x3ea00 <StartApp>
   3ff0c:    0d 94 33 f5     jmp    0x3ea66    ; 0x3ea66
   3ff10:    0d 94 00 f4     jmp    0x3e800    ; 0x3e800
Pretty cool! All of the jumps are fully qualified 17-bit jumps!

I will argue that in the Bootloader, there is no need for accessing the
jump table.  After all, the entry points are directly available and GCC
is fully capable of generating calls to them.  I therefore reject the
jump table usafe in the bootloader, "legislating this problem out of

Now, let's look at calling through this jump table from the application
space.  Here's my CallBootFunction and a couple of examples in main:

#include <stdlib.h>
#include <stdint.h>
#include "BootLoader.h"

void CallBootFunction( uint16_t size, uint16_t* p_data, uint16_t
address, uint16_t function_index )
    /* On entry to this routine, the following registers are set:
     *   r25, r24: size
     *   r23, r22: p_data
     *   r21, r20: address
     *   r19, r18: function index
     *   We will use function_index (r19:r18) to compute the address in 
     *   the jump table.  Of course, we can let the compiler do the
     *   heavy lifting of bringing the jump table address in and doing
     *   the math.  We then pass function_index into the assembly to
     *   copy the value to r31:r30, then use r18 to load a 1 into EIND.
     *   We can then call EICALL, which will jump to the proper jump
     *   location.
     *   Before we make the call, though, we need to copy the parameters
     *   and back out of a temporary location so the compiler's
     *   doesn't get frisky with the registers.
     *   We probably could have put function_index first, since we are
     *   rearranging the parameters anyway.

    function_index += BOOT_JUMP_TABLE;

    asm volatile(
                "movw r30, %0            \n\t"   // Set lo16 of EIJUMP
                "ldi  r18, 0x01          \n\t"   // set up EIND
                "out  0x3C, r18          \n\t"
                "movw r18, %1            \n\t"   // set up parameters
                "movw r24, r18           \n\t"
                "movw r18, %2            \n\t"
                "movw r22, r18           \n\t"
                "movw r18, %3            \n\t"
                "movw r20, r18           \n\t"
                "eicall                  \n\t"   // indirect jump to
jump table
                :                        // outputs
                : "r" (function_index),  // inputs    
                  "r" (size),
                  "r" (p_data),
                  "r" (address)
                : "r18", "r19"                    // clobber list    

int main(void)
    uint16_t size;
    uint16_t* p_data;
    uint16_t address;

    size = 32;
    p_data = (uint16_t*) 0x100;
    address = 0x200;

        CallBootFunction( 0, 0, 0, BOOT_FUNCTION5 );
        CallBootFunction( size, p_data, address, BOOT_MONITOR);
    return 0;

The generated code looks like:

int main(void)
 146:   cf 93           push    r28
 148:   df 93           push    r29
         *   address pushed on the stack when this routine was called.

        function_index += BOOT_JUMP_TABLE;

        asm volatile(
 14a:   c5 e8           ldi     r28, 0x85       ; 133
 14c:   df ef           ldi     r29, 0xFF       ; 255
 14e:   80 e0           ldi     r24, 0x00       ; 0
 150:   90 e0           ldi     r25, 0x00       ; 0
 152:   a1 e8           ldi     r26, 0x81       ; 129
 154:   bf ef           ldi     r27, 0xFF       ; 255
 156:   e0 e2           ldi     r30, 0x20       ; 32
 158:   f0 e0           ldi     r31, 0x00       ; 0
 15a:   60 e0           ldi     r22, 0x00       ; 0
 15c:   71 e0           ldi     r23, 0x01       ; 1
 15e:   40 e0           ldi     r20, 0x00       ; 0
 160:   52 e0           ldi     r21, 0x02       ; 2
 162:   fe 01           movw    r30, r28
 164:   21 e0           ldi     r18, 0x01       ; 1
 166:   2c bf           out     0x3c, r18       ; 60
 168:   9c 01           movw    r18, r24
 16a:   c9 01           movw    r24, r18
 16c:   9c 01           movw    r18, r24
 16e:   b9 01           movw    r22, r18
 170:   9c 01           movw    r18, r24
 172:   a9 01           movw    r20, r18
 174:   19 95           eicall
 176:   fd 01           movw    r30, r26
 178:   21 e0           ldi     r18, 0x01       ; 1
 17a:   2c bf           out     0x3c, r18       ; 60
 17c:   9f 01           movw    r18, r30
 17e:   c9 01           movw    r24, r18
 180:   9b 01           movw    r18, r22
 182:   b9 01           movw    r22, r18
 184:   9a 01           movw    r18, r20
 186:   a9 01           movw    r20, r18
 188:   19 95           eicall

If you trace the logic, it appears to be working.

No, I haven't loaded this into a processor, but the code appears to be
right.  I hope this helps you.  It cleared my head after I was done.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]