Hi all,
I’ve been working on storing data in the Serial EEPROM (E2P) so that I can save variables across power cycles (ignition on / off). It's taken a lot of trial and error and I thought I'd share the results below. By no means is this the definitive guide on how to do this, but maybe it might help someone working on a similar idea. As always, take with a pinch of salt and back it up with your own research and experimentation
1) The E2P is not accessed directly, rather the contents are mirrored to a section of RAM when the ecu starts up. The applications then read/write this section of RAM. When the ecu is powered down, the section of RAM is copied back into the E2P which saves the state of the variables. I can see the rationale behind this RAM mirror – accessing serial eeproms can be slow, plus there are only a finite amount of read/write cycles before the eeprom wears out! If the RAM contents have not been changed, then you don’t even need to store them back in the E2P. This is where checksums come in handy – if the checksum hasn’t changed, neither has the data.
2) First I had to find this RAM mirror. I think there are two ranges of RAM in the MED9.1. Internal RAM from 0x7F8000 – 0x7FFFFF and External RAM from 0x800000. I’ve attached a memory map to show what I mean. I read out the contents of the Internal RAM using the KWP commands DynamicallyDefineLocalIdentifier #2C, ReadDataByLocalIdentifier #21. I read out 16 bytes at a time and looped until I’d read the full range.
MPC500_MEMORY_MAP.pdf3) I compared this dump of the Internal RAM contents to the E2P read I had done previously using BDM and there it was. Nestled away at 0x7FAADC is a faithful mirror of the E2P! Attached are the dumps for those curious enough to stare at the hex
It’s easy enough to spot though, just look for the VIN which jumps out since it’s in ASCII. Contents may differ slightly since the BDM read is older than the RAM read, but you’ll get the idea. The address of the RAM is offset from 0x7F8000, so add 0x7F8000 to get the absolute address i.e:
Address 0x0 in file = 0x7F8000, 0x1 in file = 0x7F8001, 0x2 in file = 0x7F8002 etc
Internal_RAM_dump.binE2P_BDM_read.bin4) Now that I’ve found this RAM mirror, compare it to the EEPROM as described in the FR -
FU EEP_CONF 5.150.0 EEPROM-Layout on page 4529. The EEPROM is split up into logical blocks. Some blocks are more redundant than others, where the same block may have two copies in the EEPROM. However, you don’t need to worry about any of that, simply work with the RAM mirror and the RAM mirror <-> E2P low level stuff happens in the background. Here is block CALIB (0x0801). This block is mirrored at 0x7FABDC in the RAM mirror.
EEPROM_Layout.pdf0x7FABDC
08 01 00 80 80 80 80 00 00 80 00 80 80 FF 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 F6 FA 8108 01 – Block Number
00 – Free bytes
F6 – Counter
FA 81 - Checksum
5) Each block has a little bit of free space and that’s what I wanted to use. In block CALIB above, there is a byte of free space at 0x7FABF7. I coded my assembly to write to this byte and then flashed my code onto the ecu and switched the ignition on and off to see what happens.
Before ignition on/off
08 01 00 80 80 80 80 00 00 80 00 80 80 FF 00 00 00 00 00 00 00 00 00 00 00 00 00
02 00 F7 FA 80
After ignition on/off
08 01 00 80 80 80 80 00 00 80 00 80 80 FF 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 F7 FA 80
It didn’t work! Even though I’ve got the correct byte and my code is indeed changing it from 0x00 to 0x02, the changed byte is not being committed from the RAM mirror to the E2P. Hmmm
6) Examining the block again, the last two bytes are a checksum. Thankfully this is a really straightforward checksum. Add all the bytes in the block together and then negate the result (negate means flip ones to zeroes and zeroes to ones). So I coded a bit more and created a little routine that does exactly that. In the code below, after I set the byte I want to save, I then update the checksum of the block. The checksum will change since I’ve changed the contents of the byte I want to use for storage.
stb r11, byte_7FABF7 byte I want to use for persistent storage
li r7, 0 loop initialized
li r29, 0 sum of bytes
addi r11, r13, 0xFFFFABEC start at 0x7FABDC
lbzx r11, r11, r7
add r29, r29, r11
addi r7, r7, 1
cmpwi r7, 0x1E loop 30 times
blt loc_47C7A8
xori r29, r29, 0xFFFF negate (flip 1->0, 0->1)
sth r29, word_7FABFA store the new checksum
Flash again and test.
Before ignition on/off
08 01 00 80 80 80 80 00 00 80 00 80 80 FF 00 00 00 00 00 00 00 00 00 00 00 00 00
02 00 F8
FA 7DAfter ignition on/off
08 01 00 80 80 80 80 00 00 80 00 80 80 FF 00 00 00 00 00 00 00 00 00 00 00 00 00
02 00 F9
FA 7CAnd look at that, it's now being committed
In summary, find the RAM mirror. Find some free space in one of the blocks therein. Every time you write to a byte (stb) in the block, update the checksum as well so that the block (containing your byte set to your desired value) will be committed to the E2P when the ignition is turned off and the housekeeping routines copy the RAM mirror to the E2P.
There are probably better ways of doing this and I’m sure there’s a routine that already exists to update the checksum. But my coding and abilities to read and understand the assembly are only basic.
Hope it helps.