IAP calls from GCC - workaround

Started by viskr July 25, 2006
I think I've resolved the IAP problem and its due to a GCC "feature",
maybe a bug.

Anyway if you call IAP the way it is specified in the doc's, it works
sometimes-

#define iap_entry ((IAP) 0x7FFFFFF1) // IAP entry point

...

iap.cmd = 50; // IAP Command:
Prepare Sectors for Write
iap.par[0] = GET_SECNUM(flash_addr);// start sector
iap.par[1] = iap.par[0]; // end Sector
iap_entry (&iap, results); // call IAP function
if (results[0]){
goto exit; // an error occured?
}

iap.cmd = 52; // IAP command: Erase
Flash
iap.par[0] = GET_SECNUM(flash_addr);// start sector
iap.par[1] = iap.par[0]; // end Sector
iap.par[2] = CLK_FREQ_KHZ; // CPU clock
iap_entry (&iap, results); // call IAP function

...

The problem is this works sometimes, depending on the compiler and the
phase of the moon.
GCC seems to arbitrarily choose registers to use to do this call AND
it assumes that iap_entry() will save those registers.

BUT as it is a vector to a Philips routine IAP, and it does not
guarantee registers to be saved. So sometimes it works and sometimes not.

the proper way to call it is
#define iap_entry ((IAP) 0x7FFFFFF1) // IAP entry point

void iap_call(int *iap, int *results) {
iap_entry (iap, results); // call IAP function
}

...

iap.cmd = 50; // IAP Command:
Prepare Sectors for Write
iap.par[0] = GET_SECNUM(flash_addr);// start sector
iap.par[1] = iap.par[0]; // end Sector
iap_call (&iap, results); // call IAP function
if (results[0]){
goto exit; // an error occured?
}

iap.cmd = 52; // IAP command: Erase
Flash
iap.par[0] = GET_SECNUM(flash_addr);// start sector
iap.par[1] = iap.par[0]; // end Sector
iap.par[2] = CLK_FREQ_KHZ; // CPU clock
iap_call (&iap, results); // call IAP function

...

Bruce
www.coridiumcorp.com
home of the ARMexpress

An Engineer's Guide to the LPC2100 Series

--- In l..., "viskr" wrote:
>
>
> I think I've resolved the IAP problem and its due to a
GCC "feature",
> maybe a bug.
>
> Anyway if you call IAP the way it is specified in the doc's, it
works
> sometimes-
>
> #define iap_entry ((IAP) 0x7FFFFFF1) // IAP entry point
>
> ...
>
> iap.cmd = 50; // IAP Command:
> Prepare Sectors for Write
> iap.par[0] = GET_SECNUM(flash_addr);// start sector
> iap.par[1] = iap.par[0]; // end Sector
> iap_entry (&iap, results); // call IAP

This (recommended) syntax looks questionable to me.

Have you tried something like:

typedef void iap_call_type(int *args, int *results);

static const
void (*iap_call)(int *a, int *r) = (iap_call_type *) 0x7FFFFFF1;

int args[4];
int results[4];

// set up args

args[0] = xx;

// call IAP

(*iap_call)(args, results);

// check results

if (results[0] == )

If it's done like this, the code that makes the call is always using
the correct type (pointer to a function): the forced typecast is
done when the address of that function is assigned.

Note that if "const" is used in declaring the function pointer,
there should be no RAM storage overhead for the pointer.

The alternative way (recommended by Philips) means the forced
typecast is doen at the location of the call. I'm not sure whether
the compiler's behaviour (in getting it "wrong") is "correct" or
not: you'd probably need the language definition in front of you to
figure that one out. I know we've used the syntax I've suggested on
several compilers (including GCC for ARM7) with no problems.
Brendan.





--- In l..., "viskr" wrote:

> I think I've resolved the IAP problem and its due to a GCC
> "feature", maybe a bug.

Have a look at the method used in

http://www.cse.unsw.edu.au/~jayas/esdk/sill.html

In which you I declare iapCall() procedure in a header file as:

> __attribute__((noinline))
> void
> iapCall(unsigned int params[], unsigned int result[])
> {
>
> asm ("ldr\tr3,=0x7ffffff1\n\tbx\tr3");
>
> } // iapCall()

Procedure calls always assume r0-r3 are clobbered and thus you dont
need to tell the compiler you are using "r3" in the procedure.

Also, "noinline" stops the compiler from -O3 optimising it out, and
making incorrect assumption that "r3" is preserved.

Jaya