variables in fixed flash address (eg serial number)

Go To Last Post
21 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi again,
I have a quick question. I'm trying to place a variable in a fixed flash address, so that it can easily be altered in the hex file prior to uploading to the avr, without having to recompile in between.
classic application: serial numbers.

anyway, it seemed to work, (I can identify the numbers in the compiled hex file, so I could alter them there). This is how I did it for my ATMEGA328P:

Makefile Part:

PART_NUMBER_ADDRESS   = 0x7FF1
SERIAL_NUMBER_ADDRESS = 0x7FF2

LDFLAGS += -Wl,--section-start=.part_number=$(PART_NUMBER_ADDRESS)
LDFLAGS += -Wl,--section-start=.serial_number=$(SERIAL_NUMBER_ADDRESS)

in my main code (global)

uint8_t part_number __attribute__ ((section (".part_number"))) = 'A';
uint32_t serial_number __attribute__ ((section (".serial_number"))) = 0x30313233;

This is the end of the Hex-file:

...
:1078E000FCCF9093C6009D3011F49AE0F6CF089536
:1078F0008091C00087FFFCCF8091C6000895F89466
:02790000FFCFB7
:087902000070314133323130D5
:017FF100414E
:047FF20033323130C5
:040000030000700089
:00000001FF

you can clearly see the 'A' (0x41) and the 0x30313233 (in reverse).
so that seemed to work, but whenever I try to access the declared variables "part_number" or "serial_number"
I get something completely different!

I know I could just read them out using pgm_read_byte_near(0x7FF1) from pgmspace.h, but I'm trying to learn things, so I'm wondering why the above does not work as it is.

Any Ideas?

Last Edited: Mon. Oct 30, 2017 - 04:36 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

remember that you are in flash, so you must use the pgm_read_xxx functions to retrieve the data.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:
remember that you are in flash, so you must use the pgm_read_xxx functions to retrieve the data.

If that's the only way to access them (which works - I just verified that), then what's the point of declaring it as a variable in the first place?

I wrote:
uint8_t part_number __attribute__ ((section (".part_number"))) = 'A';

that declares the variable "part_number" right? but it seems not to be initialized.

I don't ever want to change it (OK, OK, so I guess I should declare it "const")
but I don't get why I'd explicitly need to read it again from the flash, instead of it being initialized directly during the declaration... can anyone explain?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are going to re-compile for every copy of your program with its unique serial number:

Just write it in as a named variable const char serialno[] = "123456789ABC";

If you want to just add it at flash burning time, then you can just merge a hex record at your fixed address.

And read if with pgm_read_byte() or even printf("#S", (PGM_P)0x7ff0));

n.b. the "#" means percent.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

If you want to just add it at flash burning time, then you can just merge a hex record at your fixed address.

Putting it separately in EEPROM (so it then lives in a separate .eep file) might be another idea worth investigating. This saves trying to merge hex data

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
If you are going to re-compile for every copy of your program with its unique serial number:

Nononono, The point was not to have to recompile :-)

anyway, I know about the alternatives - using pgm_read_byte or eeprom, that wasn't my question:

I was simply wondering why this line:

uint8_t part_number __attribute__ ((section (".part_number"))) = 'A';

does put 'A' in to the Flash (at the right location),
and does declare the variable "part_number"
but DOES NOT initialize part_number with 'A'

can anyone tell me why?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

does put 'A' in to the Flash (at the right location),
and does declare the variable "part_number"
but DOES NOT initialize part_number with 'A'

Works for me?

uint8_t part_number __attribute__ ((section (".part_number"))) = 'A';
uint32_t data2 __attribute__ ((section (".part_number"))) = 0xDEADBEEF;
avr-gcc -mmcu=atmega32 -Wl,-Map=test.map -Wl,-section-start=.part_number=0x2000 test.o    -lm  -o test.elf
D:\test\default>type test.hex
:100000000C942A000C9434000C9434000C943400AA
:100010000C9434000C9434000C9434000C94340090
:100020000C9434000C9434000C9434000C94340080
:100030000C9434000C9434000C9434000C94340070
:100040000C9434000C9434000C9434000C94340060
:100050000C94340011241FBECFE5D8E0DEBFCDBF25
:100060000E9436000C9442000C94000089B3803C3E
:1000700010F48FEF88BB8FEF87BB80EF88BB88B30E
:080080008095FCCFF894FFCF3E
:0520000041EFBEADDE62
:00000001FF

I see the initial values in the code (the "DEADBEEF" is fairly unmistakable in the second last line)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

does put 'A' in to the Flash (at the right location),
and does declare the variable "part_number"
but DOES NOT initialize part_number with 'A'

Works for me?

:0520000041EFBEADDE62
:00000001FF

I see the initial values in the code (the "DEADBEEF" is fairly unmistakable in the second last line)


yes, which is what I meant by "does put 'A' into the Flash.
However, what does not work is if in your main, you do something like:

printf("part_number: #c",(char)part_number); 

(#=%)
you will not receive "part_number: A" as expected, but sometimes "part_number: \" or "part_number: Û" or some other unexpected (seemingly uninitialized) value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But C has no concept of memory spaces (as such GCC does not), so while it places and initializes the variable in flash (because you told it to), when you access it by name directly, it does not know that the address of the variable is located in flash, so it is trying to fetch it from RAM. This is one of the unfortunate side-effects of working with a Harvard architecture micro and C. (some compilers are better at hiding this than GCC, but the problem never goes away completely)

Technically you can skip the declaration part, and just use hard-coded addresses, but the declaration bit makes the whole thing a bit cleaner.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

Last Edited: Fri. Jun 26, 2009 - 04:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
However, what does not work is if in your main, you do something like:

But you obviously cannot do that - if you don't use pgm_read_byte() then the address of "part_number" is interpreted as an SRAM address. What will work is:

printf("part_number: #c",(char)pgm_read_byte(&part_number));

Remember the AVR is a Harvard architecture and there are different address spaces. code flash has an address 0x0000, EEPROM has an address 0x0000 and SRAM has an address 0x0000 and they are all different.

I guess it's true that some AVR C compilers will "hide" the memory space access. In Codevision you could:

flash uint8_t part_number = 'A';
printf("part_number: #c",(char)part_number);

and some "hidden" flash access code is generated to pick up 'part_number' from code flash rather than RAM.

But GCC is a compiler for 50+ different architectures and it does not have "hidden" multiple memory space access like this. You have to explicitly use pgm_read_byte() or eeprom_read_byte() to indicate how the variable's address should be interpreted.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
But you obviously cannot do that - if you don't use pgm_read_byte() then the address of "part_number" is interpreted as an SRAM address. What will work is:

printf("part_number: #c",(char)pgm_read_byte(&part_number));

Remember the AVR is a Harvard architecture and there are different address spaces. code flash has an address 0x0000, EEPROM has an address 0x0000 and SRAM has an address 0x0000 and they are all different.

Aaah, that's the answer I was hoping to get :-)
Obviously, to me it was not "obvious" and I did not know about the harvard architecture, Thanks for that info.

what I did for now was this:

uint8_t part_number __attribute__ ((section (".part_number"))) = 'A';

main()
{
 part_number=pgm_read_byte_near(PART_NUMBER_ADDRESS);
 printf("part_number: #c",part_number);
}

This works, too, but I guess it is not quite as elegant, or maybe even unsafe?
So I'll stick to pgm_read_byte_near(&part_number);, that way I'll know for sure I'm dealing with a variable in flash, and won't be tempted to do anything with it I shouldn't do.

Thanks again for the explanation!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

whoa there! Unless you changed the location of the part_number section to be in RAM, do not do what you are doing. Because part_number as a variable, while it has an address, the address is supposed to be in flash, and therefore does not exist in the SRAM map. So if you write to it, you could be writing over another variable that has the same address, but has been allocated in RAM. Remember that your code has no idea which memory space the variable resides in at runtime, and it always assumes RAM.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:
whoa there! Unless you changed the location of the part_number section to be in RAM, do not do what you are doing. Because part_number as a variable, while it has an address, the address is supposed to be in flash, and therefore does not exist in the SRAM map. So if you write to it, you could be writing over another variable that has the same address, but has been allocated in RAM. Remember that your code has no idea which memory space the variable resides in at runtime, and it always assumes RAM.

Thanks! I did think it might be unsafe to do this, and you've just given me the confirmation that it definitely is unsafe!
I'll stick pgm_read_byte_near(&part_number) or copy that into a new variable called part_number_ram or something like that.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

much better.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Obviously, to me it was not "obvious" and I did not know about the harvard architecture,

I'd read the section in the user manual about "memory sections" and I'd also read Dean's tutorial about PROGMEM in the Tutorial Forum

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Useful information..
I would like to have one or two flag bytes at a fixed location in EEMEM, but for the rest of the EEMEM simply used with named addresses.

Reason - I need a flag between runtime code and bootloader code that remains unaltered when switching between them, so I can have the bootloader which doesn't use EEMEM for anything - simply checking and maybe clearing them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stupid question from the side.
Since the GCC is a C++ compiler I would think there
would be a way to hide where data is comming from,
with some overloads !! But I'm just a lowlevel guy :)

Jens

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I would like to have one or two flag bytes at a fixed location in EEMEM
If those flag byte(s) don't need to be initialized when programming (other than 0xFF), then I would probably do-
#define MY_FLAGS1 E2END
//#define MY_FLAGS2 E2END-1

The compiler/linker doesn't have to know anything about this, and there will be no conflicts unless you happen to use enough eeprom in your app to run into these 'hidden' bytes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi thefool,

 

Where did you put these settings?

 

PART_NUMBER_ADDRESS = 0x7FF1

SERIAL_NUMBER_ADDRESS = 0x7FF2

LDFLAGS += -Wl,--section-start=.part_number=$(PART_NUMBER_ADDRESS)
LDFLAGS += -Wl,--section-start=.serial_number=$(SERIAL_NUMBER_ADDRESS)
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You woke up an 8 year dormant thread and I doubt the OP is still here

Traditionally I put serial numbers on eeprom. Easy to edit as needed.

Jim

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looks like it's the same question as here: http://www.avrfreaks.net/forum/how-write-fixed-flash-address-starting-code-using-attiny416

 

But, to answer the hijacker's question, the OP clearly stated they are in the makefile.

Last Edited: Mon. Oct 30, 2017 - 02:46 PM
Topic locked