Hello,
While porting legacy code from an LPC2103 to a LPC1765 Cortex processor within the Keil environment I came accross the need to implement the linker zero_init attribute for a single byte variable as described by the following links:
(1) infocenter.arm.com/.../index.jsp (2) http://www.keil.com/support/docs/3480.htm
The purpose of this variable was to be read on a system reset to determine whether the reset event was intentional or not and execute additional code if the reset event was intentional (ie: go into the ISP bootloader). As debugging shows, system reset causes RW variables to be zeroed when performing the BX from statup .asm code into application main().
However, when I implemented the change within a 'C' file as follows:
UINT8 Startup_Flags __attribute__( ( section( "NoInit"), zero_init) );
and linker scatter file as follows:
RW_IRAM1 0x10000000 UNINIT 0x00000100 { ;no init section *(NoInit) }
I was getting inconsistent results with the variable every time the reset event occurred, even though it was placed in the declared UNINIT memory region according to the memory map:
Startup_Flags 0x10000000 Data 1 main.o(NoInit) SystemFrequency 0x10000100 Data 4 system_lpc17xx.o(.data)
As the region was declared to be a size of 256, I decided to add a dummy declaration along with the original to troubleshoot the issue further, as follows:
// Zero initialization section UINT8 Startup_Flags __attribute__( ( section( "NoInit"), zero_init) ); UINT8 NoInitUnused[255] __attribute__( ( section( "NoInit"), zero_init) );
This initially showed promise until it too failed periodically (the variable Startup_Flags was located at 0x10000000 according to the memory map).
Startup_Flags 0x10000000 Data 1 main.o(NoInit) NoInitUnused 0x100000ff Data 255 main.o(NoInit)
I then tried swapping the declarations in order to move the location of the variable to the LAST entry in the region (ie: 0x100000FF) as follows:
// Zero initialization section UINT8 NoInitUnused[255] __attribute__( ( section( "NoInit"), zero_init) ); UINT8 Startup_Flags __attribute__( ( section( "NoInit"), zero_init) );
NoInitUnused 0x10000000 Data 255 main.o(NoInit) Startup_Flags 0x100000ff Data 1 main.o(NoInit)
Now the results I am getting are consistently correct (so far) and I can invoke the ISP bootloader whenever the variable has the proper bit flag set.
The caveat seems to be that this zero_init command CAN be used and function properly, but under certain undocumented? conditions (ie: MUST have 8 declared bytes within the region and declaration order seems to be important). Has anyone else run into this issue? If so, how was it corrected? Or am I just misreading the documentation contained within the posted links? Thank you.
Putting the ZI variable into a UNINIT section means that the variable will not be zeroed (or changed) by the image's startup code. You should see the same value at the "BX from startup" as in main().
But Startup_Flags could be being changed by other things, for example powering off and on the RAMs, other code, if any, running before the image or other code in the image before Startup_Flags is read.
Seems to have been 'my bad'. According to the following explanation:
infocenter.arm.com/.../index.jsp
It seems that in order to use this capability the way I declared it you MUST compile with the --gnu switch.
"Note: #pragma arm section can only be used with ADS 1.2 and later compilers. In RVCT 2.1 and later the GNU __attribute__ ((section ("...")))syntax can be used as an alternative to #pragma arm section, when compiling with --gnu."
Evidently, if you do NOT use the complier #pragma then data less than 8 bytes is treated according to this link (The info was also there but was overlooked):
"Another workaround, for RVCT 2.1 and later only, is to use the new "--gnu" mode attribute:
int global __attribute__ ((section (".bar"))); "
So there it is....
I don't want to prolong this thread unnecessarily, but armcc versions 2.2 and later accept __attribute__((section("..."))) with or with using the --gnu option. (In this case, if the compiler didn't accept it you'd get an error or warning). The link map lines you show above show that it was being accepted.
Is there any limit to the size of NoInit area? I tried an array of 20 bytes,
U8 Db[20] __attribute__( ( section( "NoInit"),zero_init) );
which I initialized such that Db[0] = 0, Db[1] = 1,...Db[19] = 19;
The scatter file looks like:
LR_IROM1 0x00000000 { ; load region ER_IROM1 0x00000000 0x00020000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } GLOBAL_DATA 0x40000000 UNINIT 0x00000014 { ;no init section *(NoInit) } RW_IRAM2 0x40000014 0x0000FFEC { ; RW data .ANY (+RW +ZI) } }
and the map file shows: Db 0x40000000 Data 20 os_init.o(NoInit) which to me seems like the entire array is in NoInit sector. However, when I execute the program, after the soft reset, only the first 16 bytes are preserved, the rest of them Db[16] thru Db[19] are initialized to zero. Is it some kind of default setting which limits the NoInit sector? If it is how can I increase its size?
I am using Keil uVision3 V3.51 linker and the target processor is ARM LPC_2106.