IAP EEPROM Anomaly LPC11U68

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

IAP EEPROM Anomaly LPC11U68

Jump to solution
2,283 Views
mattferrari
Contributor III

This causes a Hard Fault IRQ when iap_entry() is called (only partial code shown, the other provisions are coded):

uint32_t      clock_speed;

clock_speed = 12000000;


iap_command[0] = IAP_EEPROM_WRITE;
iap_command[1] = eeprom_start_address;
iap_command[2] = (uint32_t)byte_ptr;
iap_command[3] = byte_count;
iap_command[4] = clock_speed / 1000;

iap_entry( iap_command, iap_result );

But this works fine:

clock_speed = 12000000 / 1000;


iap_command[0] = IAP_EEPROM_WRITE;
iap_command[1] = eeprom_start_address;
iap_command[2] = (uint32_t)byte_ptr;
iap_command[3] = byte_count;
iap_command[4] = clock_speed;

iap_entry( iap_command, iap_result );

This makes no sense to me.  Is this a compiler bug, possibly due to optimization?

I'm using LPCXpresso IDE v7.6.2 [Build 326] for LPC11U68.

 

Thanks,

Matt

Labels (2)
1 Solution
1,724 Views
pavelhudecek
Contributor III

Strange.
The division on 11U68 is realized via ROM functions, that may be required some initialization. Is this problem occurs, if you called your function after ResetISR()?
The division may fail and:
1. Content of iap_command[4] is possibly incorrect.
2. Stack or some system variables is possibly incorrect.

View solution in original post

9 Replies
1,724 Views
mattferrari
Contributor III

Thanks Werner, but I'm using the LPCXpresso IDE.  How do I generate a mixed listing from my build using the LPCXpresso IDE?

Thanks.

0 Kudos
1,724 Views
mattferrari
Contributor III

Thank you for your replies.  The mystery has been solved.

The problem is due to performing a division operation prior to initializing the pDivRom_uidiv variable.  

The ROM divide variables are initialized in ResetISR() which is normally called directly from the Reset Vector:

unsigned int *div_ptr = (unsigned int *)((unsigned int *)*(PTR_ROM_DRIVER_TABLE))[4];
pDivRom_idiv = (unsigned int *)div_ptr[0];
pDivRom_uidiv = (unsigned int *)div_ptr[1];

But since I am performing a divide function prior to calling ResetISR() the variables are not yet initialized.

The failure wasn't actually in the iap_entry() function as I had thought, but rather the code crashes when attempting to perform the divide function for the value that is to be loaded into iap_command[4].

Here's the assembly code that's called when the division is to occur:

__aeabi_uidiv:
push {r4, lr}
ldr r3, =pDivRom_uidiv
ldr r3, [r3, #0] // Load address of function
blx r3 // Call divide function
pop {r4, pc}

.endfunc

The Hard Fault occurs when the "blx" command is executed, obviously because "r3" got a bad value since pDivRom_uidiv had never been set.

Thank you for your help with resolving this problem.  Understanding this situation is very important because now I know not to perform any division operations prior to this initialization.  Usually, this initialization happens right away and no thought is given to it, but in a situation like mine, that initialization is missing and that's the problem.

One last question...  If I want to generate a "mixed listing" from my build which includes the assembly code interspersed with the source code, is there a setting in one of the "Project" > "Properties" panels for generating such an output file from the build?

Thanks again,

Matt

0 Kudos
1,724 Views
wernerf
Contributor II

Glad to hear you solved your problem.

In Keil there is a simple check box for this. For every source file an assembly file (*.txt) is generated containing the source code as comments.

0 Kudos
1,725 Views
pavelhudecek
Contributor III

Strange.
The division on 11U68 is realized via ROM functions, that may be required some initialization. Is this problem occurs, if you called your function after ResetISR()?
The division may fail and:
1. Content of iap_command[4] is possibly incorrect.
2. Stack or some system variables is possibly incorrect.

1,724 Views
mattferrari
Contributor III

Here's the complete function:

#define PREVENT_IRQS { \
reenable_irq = !(__get_PRIMASK()); \
__disable_irq(); \
}

#define RESTORE_IRQS { \
if( reenable_irq ) \
__enable_irq(); \
}

#define SYSTEM_CLOCK_SPEED_UPON_RESET      12000000


uint32_t ReadEEPROM( bool pre_clock_config, uint32_t eeprom_start_address, uint32_t *dst_ptr, uint32_t byte_count )
{

   uint32_t             iap_command[6], iap_result[6], clock_speed_kHz;
   volatile uint8_t    *byte_ptr, reenable_irq, retry = 50;


   byte_ptr = (uint8_t *)dst_ptr;

   if( pre_clock_config )
      clock_speed_kHz = SYSTEM_CLOCK_SPEED_UPON_RESET / 1000;
   else
      clock_speed_kHz = SystemCoreClock / 1000;


   PREVENT_IRQS;

try_again:

   // Load IAP Command Buffer
   iap_command[0] = IAP_EEPROM_READ;
   iap_command[1] = eeprom_start_address;
   iap_command[2] = (uint32_t) byte_ptr;
   iap_command[3] = byte_count;
   iap_command[4] = clock_speed_kHz;

   iap_entry( iap_command, iap_result );

   if( (iap_result[0] != IAP_CMD_SUCCESS) && retry-- )
   goto try_again;

   RESTORE_IRQS;

   return( iap_result[0] );

}

The above code seems to work just fine.  But if I don't do the "divide by 1000" in the upper code and instead try to do it right there at the iap_entry[4] assignment (as in my prior email) I crash upon entering iap_entry().  I set up my LPCXpresso11U68 board as a debug interface to my target and single stepped the code using the LPCXpresso IDE.  I confirmed the value of the variable used at the [4] assignment - it is correct immediately prior to calling iap_entry().  When I set a breakpoint immediately after the call to iap_entry() and run the code, I immediately trap to the HardFault ISR.  

If I put all constants at the [4] assignment (i.e. 12000000 / 1000) it works.  If I assign the single (predivided) value at the [4] assignment it works.  But if I use:

iap_command[4] = clock_speed / 1000; (Where clock_speed is confirmed to have a value of 12000000 as observed in the debugger) I crash.

As you can see in the above code, I am disabling interrupts and my "clock_speed" variable is an automatic/local variable on the stack, so it shouldn't be getting overwritten.

I am doing this function right after reset, before calling the ResetISR() initialization function.  Are there any resources that I need to have configured or enabled (e.g. clocking) in order to use the IAP call?  The Stack Pointer would have been initialized via the Vector Table as specified in the Linker Script:  PROVIDE(_vStackTop = __top_Ram0_32 - 64);

As you can see, I am leaving the uppermost 32 bytes of the Stack free for the IAP resource (as required) and an additional 32 Bytes as well...  

Now that you can see the whole code and you know that I'm calling this function right after reset, does anything come to mind explaining why:

iap_command[4] = clock_speed_full / 1000;    // FAILS (clock_speed_full = 12000000)

iap_command[4] = clock_speed_alone;      // WORKS   (clock_speed_alone = 12000)

iap_command[4] = 12000000 / 1000;   // WORKS

   

My concern is that if I don't understand what's happening, there could be an issue which might manifest elsewhere, so it's important to understand what's happening here - it could be pointing to a bigger problem that needs to be addressed.

Thanks for your help with this.

Matt

0 Kudos
1,724 Views
wernerf
Contributor II

Hi Matt,

the only difference in the three lines of code is that only the first line has the division at run-time. In the third line the division is performed by the compiler's preprocessor and a simple constant is loaded into iap_command[4].

Can you set the debuggers breakpoint right before the iap call and confim what value is in iap_command[4] ?

Alternatively, can you look into the assembly code the compiler has produced?

1,724 Views
pavelhudecek
Contributor III

That's weird. Optimization possibly might cause it, but:

I had a similar strange problem:
1. clock_speed set at program startup.
2. Overwriting of clock_speed occured, due to array index overflow in a completely different part of the program.
3. I use clock_speed to set iap_command[4] and the IAP crashed.

0 Kudos
1,724 Views
mattferrari
Contributor III

Please have another look at my code.  In BOTH cases, the value being assigned to iap_command[4] is the same, 12000.  In the first case, I divide the variable by 1000 at the assignment, in the second case, I divide the variable first, then assign it as a single entity.  The problem here in not the value (kHz).  I understand that, and in both cases, the value being assigned is 12000.  So why does one work and the other not?

0 Kudos
1,724 Views
pavelhudecek
Contributor III

This is normal. IAP functions require the frequency in kHz.

0 Kudos