I'm trying to place a variable in EEPROM such that the address of the variable shows up at the EEPROM's address but the linker does not try to initialize the var. The idea is that when code reads the variable it's reading from EEPROM and any writes are done via EEPROM_xxx() functions.
I realize this would be easier done with a pointer to a structure that has all the EEPROM-backed values but it's kind of nice to just have the compiler and linker take care of everything.
Here's what I have in my C code:
__attribute ((section ("EEPROM"))) uint8_t Bias_Current; __attribute ((section ("EEPROM"))) bool Auto_test;
I set up sections in the "Target" dialog and I'm using the following scatter load file: LR_IROM1 0x08000000 0x00020000 { ; load region size_region ER_IROM1 0x08000000 0x00020000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00004000 { ; RW data icc_measure_Ram.o (+RO) .ANY (+RW +ZI) } ER_ROM1 0x00808000 UNINIT 0x00001000 { ; load address = execution address *(EEPROM) } }
However, when I run the code the scatter load algorithm crashes because it's trying to initialize the target address (0x808000), even though I marked it as "UNINIT".
What's the best way to use scatter loading to place variables at a target address but make sure the target address doesn't get written to by the scatter loader?
Thanks, Andrew
What are you trying to do? Is that a NOR flash that you are mapping code into? What's your interface toward the non-volatile memory? Note that you can only scatter load from memories that are mapped into the memory address of the processor (so, forget about NAND flashes, i2c EEPROMs etc.).
It's an EEPROM internal to the micro and it shows up as a normal, contiguous address space. You can read from it just like normal flash or RAM. I was hoping to get the toolchain to simply place uninitialized variables in that address space, e.g. it's not strictly speaking a scatter load since the variables would not show up in the image file that's burned into flash, just appear at the target address when the program starts.
If I set break points in the scatter load routine and manually skip over the loop that tries to initialize the EEPROM segment everything is fine and the variables are at the right address. Unfortunately, I haven't found a way to avoid the scatter load routines trying to write to the EEPROM area.
I can't say I like your approach.
You are trying to place some variables in EEPROM, making them non-volatile. But that makes it very hard to read the source code and realize their unique property.
Personally, I would prefer to access them using a pointer to a struct where it was 100% visible that these are no ordinary varibles.
So the code would look like:
eeprom_t *eeprom; ... eeprom = address_of_eeprom; ... for (i = 0; i < eeprom->num_configured_alarm_receivers; i++) { if (send_alarm(eeprom->alarm_receivers[i].address,&alarm)) { eeprom->alarm_receivers[i].num_answered++; break; } else { eeprom->alarm_receivers[i].num_failed_attempts++; } }
Then it would be 100% clear that accesses are made to a nonvolatile memory region.
I agree, I can't say I really like the approach either, which is why I mentioned the pointer approach would probably be easier in my original post.
The code in questions is written by someone else and I'm porting it from IAR to Keil. I wanted to understand why it isn't working for me but it's small enough to rewrite with pointers. It'll be better in other ways, e.g. support for multiple unrelated modules sharing EEPROM is easier. With the linker approach I'd be relying on the sct file to make sure things are always placed in the same order. With the pointer approach that can be made explicit in the code. Also, I usually put sentries and some kind of checksum in my non-volatile structures to make sure things are ok.
Andrew
Answering my own question for future reference, although I already decided to abandon this approach:
In RVCT 2.1 and later global variables smaller than 8 bytes are silently moved into the a normal RW data segment instead of the zero-init section. You need to add zero_init to the attributes to force the variable back into .bss. In my case it's extra confusing because I want to actually suppress the zero-fill completely but to get everything working you need to force the variable into a .bss-type segment and use the UNINIT directive in the .sct file to suppress the zero-fill.
__attribute ((section ("EEPROM"),zero_init)) uint8_t Bias_Current;
and
ER_ROM1 0x00808000 UNINIT 0x00001000 { ; load address = execution address *(EEPROM) }
Details here:
infocenter.arm.com/.../ka3947.html