EEPROM access on xmega [xmega256A3U]

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

Hi all,
I'm trying to properly understand the way eeprom memory is presented to the application by the above stated uC.

At my current state of knowledge about xmega eeprom, I'm able to perform:

- write access through avrdude programming:

/usr/local/avr/bin//avrdude -p x256a3 -c jtag2 -P usb -C /usr/local/avr/etc/avrdude.conf -u -V -U eeprom:w:./image.bin:r

- read access through avrdude eeprom dump [works]:

/usr/local/avr/bin//avrdude -p x256a3 -c jtag2 -P usb -C /usr/local/avr/etc//avrdude.conf -u -V -U eeprom:r:./imagedump.bin:r

Now, once I've flashed an application (preserving the eeprom data), this last one needs to access the eeprom data in order to fetch its desired information.

I ve'read doc8077 where they state that eeprom resides at the starting address 0x8C000. Soon after, in doc8068 (section 7.4) they mention a 0x1000 offset that should be used to access the eeprom.

I tried reading from address 0x8C0000, 0x8C1000, 0x801000, and probably many others, but could not ever read the data that actually was in the eeprom.

Finally I came across this post https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=85261 which really dazed and confused me.

By chance I tried to read at address 0x810000 (as was described in the post - and I supposed it was a typo in there -) and voilà I got my eeprom data back.

Can anyone explain me the way the eeprom memory is mapped in the xmega and gdb and jtagice and all the orchestra?

Thanks in advance and sorry for my scarce knowledge about the subject.
RM

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

Quote:

I tried reading from address 0x8C0000, 0x8C1000, 0x801000,

How could you? It's almost impossible to generate a 24 bit pointer.

Incidentally a 0x810000 offset is the "trick" that GCC uses to use a flat memory, von Neumann set of tools with a Harvard architecture multi-memory space processor.

EDIT: A typical Xmega datasheet I just looked at said 0x1000 but *ONLY* after the EEMAPEN bit is set in the NVM control register. The EEPROM is not mapped for LDS/STS access by default.

EDIT2: Oh and a 256A3 header file confirms:

#define MAPPED_EEPROM_START     (0x1000)
#define MAPPED_EEPROM_SIZE      (4096)
#define MAPPED_EEPROM_PAGE_SIZE (0)
#define MAPPED_EEPROM_END       (MAPPED_EEPROM_START + MAPPED_EEPROM_SIZE - 1)

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

clawson wrote:
Quote:

I tried reading from address 0x8C0000, 0x8C1000, 0x801000,

How could you? It's almost impossible to generate a 24 bit pointer.

Incidentally a 0x810000 offset is the "trick" that GCC uses to use a flat memory, von Neumann set of tools with a Harvard architecture multi-memory space processor.

Indeed I smelled there was some virtual addressing (that is why I tried the 24 bits addressing); now my question is: where to document myself about this specific number(0x81000) where is this number (and prolly many others) documented?

Thanks,
R

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

Quote:

where to document myself about this specific number(0x81000) where is this number (and prolly many others) documented?

In the linker script but this is a trick for tiny/mega. As Xmega have a different layout I'm not sure it's the same there.

EDIT: nope I'm wrong. It is the same in avrxmega6.x that is used for 256A3:

/* Default linker script, for normal executables */
OUTPUT_FORMAT("elf32-avr","elf32-avr","elf32-avr")
OUTPUT_ARCH(avr:106)
MEMORY
{
  text   (rx)   : ORIGIN = 0, LENGTH = 1024K
  data   (rw!x) : ORIGIN = 0x802000, LENGTH = 0xffa0
  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K
  fuse      (rw!x) : ORIGIN = 0x820000, LENGTH = 1K
  lock      (rw!x) : ORIGIN = 0x830000, LENGTH = 1K
  signature (rw!x) : ORIGIN = 0x840000, LENGTH = 1K
}

Just to reiterate the point again though: This has NOTHING to do with the layout of memory in the chip itself. This is so that a ELF file that holds the contents of "memory" from 0x00000000 to 0xNNNNNNNN can actually hold images of several different "memories" (flash, RAM, EEPROM, locks, fuses, signature) each of which has their own location 0x0000. So the RAM stuff is "moved up" by 0x800000 and the EEPROM stuff is moved up by 0x810000 in the file so they don't "bump into" the flash image that is really based at 0x0000. The offsets used (0x800000 is 8MB, 0x810000 is 8MB+64KB) are picked to be larger than the encompassing memory space is ever likely to get. I doubt we'll see an AVR with 8MB of flash but the 64KB reserved for RAM separation could be a problem when there are already AVRs with 16KB of SRAM.

Also note that this "trick" is only (as far as I know) used by GCC. The other compilers have their own mechanisms for keeping the contents of various memory regions separate in their composite binary formats.

By the way, when building GCC code you may have seen this in the build out to create the .eep (Intel Hex file for EEPROM) from the ELF:

"C:\Program Files\Atmel\Atmel Studio 6.0\extensions\Atmel\AVRGCC\3.4.1.95\AVRToolchain\bin\avr-objcopy.exe" -j .eeprom  --set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0  --no-change-warnings -O ihex "ledblink.elf" "ledblink.eep" || exit 0

There you see it not only extracting the .eeprom data from the ELF (-j .eeprom) but also changing it's address back to 0 for use in the .eep file.

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

clawson wrote:
Quote:

where to document myself about this specific number(0x81000) where is this number (and prolly many others) documented?

In the linker script but this is a trick for tiny/mega. As Xmega have a different layout I'm not sure it's the same there.

First off thanks for the very insight explanation.

Given your answers it would look like if I change the the linker script eeprom memory address 0x810000 to my liking, everything should work smoothly, BUT that's not the case, and regardless what I write in my linker script memory definition, I will always find my eeprom starting at address 0x810000.

So back to my question: where is exactly this number defined and hardcoded? Because after this test I've come to the conclusion that the linker script statement is meaningless to the process of addressing eeprom. It's just giving the developer an information about the address to use. But the definition of the mapping of eeprom into this address must be encoded somewhere else (right?).

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

Quote:

Given your answers it would look like if I change the the linker script eeprom memory address 0x810000 to my liking, everything should work smoothly,

No no and no. The whole point of this 0x810000 offset thing is that you should never actually see it. When you define something in your own code that is in EEPROM:

char text_in_EE[] EEMEM = "Hello world";

the EEMEM macro simply says:

__attribute__((section(".eeprom")))

which puts this object into a section called EEPROM. Then the linker script (as I showed above) says:

  eeprom (rw!x) : ORIGIN = 0x810000, LENGTH = 64K 

and also:

  .eeprom  :
  {
    KEEP(*(.eeprom*))
     __eeprom_end = . ;
  }  > eeprom

So "text_in_EE[]" and anything else that was co-erced into the EEPROM section is placed by the linker at 0x810000 onwards. Finally, when the EEPROM data is later extracted from the ELF, as I also said, it does:

avr-objcopy.exe -j .eeprom  --set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0  --no-change-warnings -O ihex "ledblink.elf" "ledblink.eep"

And that extracts all the EEPROM stuff back out and puts it back to 0x0000 in the .eep file.

You, the programmer need never know anything about any of this.

If you think it's somehow going to make it possible for you to read programmed EEPROM data from your Xmega program it is not. I already told you how to do that. Your only fault is that you had not set the EEMAPEN bit in the NVM controller to actually allow it to be mapped into memory. This is why I said above:

Quote:

Just to reiterate the point again though: This has NOTHING to do with the layout of memory in the chip itself

Do not get confused by the two concepts.

If you have now tried setting EEMAPEN are you saying the data is not visible at 0x1000?

Also note that if I write a typical EEPROM using program for Xmega:

#include 
#include 

char foo[] EEMEM = "hello";

int main(void) {
	PORTB.OUT = eeprom_read_byte(&foo[3]);
}

this generates:

	PORTB.OUT = eeprom_read_byte(&foo[3]);
 22a:	83 e0       	ldi	r24, 0x03	; 3
 22c:	90 e0       	ldi	r25, 0x00	; 0
 22e:	06 d0       	rcall	.+12     	; 0x23c <__eerd_byte_x256a3>
 230:	e0 e2       	ldi	r30, 0x20	; 32
 232:	f6 e0       	ldi	r31, 0x06	; 6
 234:	84 83       	std	Z+4, r24	; 0x04

and

0000023c <__eerd_byte_x256a3>:
 23c:	03 d0       	rcall	.+6      	; 0x244 
 23e:	80 81       	ld	r24, Z
 240:	99 27       	eor	r25, r25
 242:	08 95       	ret

00000244 :
 244:	e0 ec       	ldi	r30, 0xC0	; 192
 246:	f1 e0       	ldi	r31, 0x01	; 1
 248:	37 85       	ldd	r19, Z+15	; 0x0f
 24a:	37 fd       	sbrc	r19, 7
 24c:	fd cf       	rjmp	.-6      	; 0x248 
 24e:	34 85       	ldd	r19, Z+12	; 0x0c
 250:	38 60       	ori	r19, 0x08	; 8
 252:	34 87       	std	Z+12, r19	; 0x0c
 254:	fc 01       	movw	r30, r24
 256:	e0 50       	subi	r30, 0x00	; 0
 258:	f0 4f       	sbci	r31, 0xF0	; 240
 25a:	08 95       	ret

You can see the source for the library function here:

http://svn.savannah.nongnu.org/v...

where it is doing this:

  ; Wait until NVM is not busy.
1:	ldd	r19, Z + NVM_STATUS - NVM_BASE
	sbrc	r19, NVM_NVMBUSY_bp
	rjmp	1b

  ; Enable EEPROM mapping into data space.
	ldd	r19, Z + NVM_CTRLB - NVM_BASE
	ori	r19, NVM_EEMAPEN_bm
	std	Z + NVM_CTRLB - NVM_BASE, r19

  ; Load Z with correct EEPROM address to read from data space.
	movw	ZL, addr_lo
	subi	ZL, lo8(-MAPPED_EEPROM_START)
	sbci	ZH, hi8(-MAPPED_EEPROM_START)

So you can either stick to using eeprom_read_byte() just as you would for any AVR program written with avr-gcc or you could copy what they are doing here and set NVM_EEMAPEN_bm within NVM_CTRLB then read the data with a 0x1000 offset added.
Something like:

#include 
#include 

char foo[] EEMEM = "hello";

int main(void) {
	PORTB.OUT = eeprom_read_byte(&foo[3]);
	NVM_CTRLB |= NVM_EEMAPEN_bm;
	PORTB.OUT = *(&foo[3] + 0x1000);
}

But I'm not sure which of those two actually seems the more complex!

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

I got the point of using arbitrary non-conflicting address spaces in the linker script to just match the different memory types of the avr arch.

I got the point of mapping back to address 0 the eeprom stripped section with objcopy.

And now that I set EEMAPEN bit as you suggested I managed to see data at the correct address (0x1000).

This last one was my missing point. I really got confused by that address from the post and the linker script and the confusion became even bigger when I saw that I could access the eeprom content through that address in the non-memory mapped case! I didn't even focus on enabling the mapping as I could access the eeprom from that address.

Thanks for the effort.
R

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

Do note however that the linker's addresses (my foo[] above etc.) are going to be 0 based but the mapped view is offset by 0x1000 so you are going to have to do nasty things like:

PORTB.OUT = *(&foo[3] + 0x1000); 

to refer to data by linker supported names. Also my foo[] example only works because I used byte-width char so the +0x1000 added to the pointer is 0x1000 bytes and not something else. If you did this to objects of a different width you'd presumably have to cast the item to a byte wide pointer first, add the offset (then interpreted as a byte offset) then cast back to the pointer type for the object itself.