SPIN RAM / EEPROM question
JohnR2010
Posts: 431
in Propeller 1
About a year ago I wrote a SPIN based XModem upload object that works with Digi's XCTU built in XModem Transfer. I have used it countless times to wirelessly (over ZigBee network) update my device's firmware. It copies an EEPROM image file to my EEPROM's upper memory and then if the upload is successful copies it over the lower EEPROM and reboots to load up the new firmware.
In my current project I have 5 longs I use to calibrate the sensors on my board. The value in these longs are unique to the board and when I do a firmware upgrade I don't want to write over those 5 longs but keep them. I thought well that's not a big deal I will just update them after I have updated my EEPROM. After all I have access to the values in RAM so I could just read the values and poke them down into the EEPROM before I reboot the propeller. Didn't work as their original address changed in the new firmware upload.
So I thought I would ask the forum and see if there is a trick I'm missing. The 5 longs I'm trying to save are in DAT blocks and I can get their old addresses and values before I reboot but I have no idea what their new addresses are in the new firmware. Any thoughts or creative ways I can get the variables new addresses out of the EEPROM?
In my current project I have 5 longs I use to calibrate the sensors on my board. The value in these longs are unique to the board and when I do a firmware upgrade I don't want to write over those 5 longs but keep them. I thought well that's not a big deal I will just update them after I have updated my EEPROM. After all I have access to the values in RAM so I could just read the values and poke them down into the EEPROM before I reboot the propeller. Didn't work as their original address changed in the new firmware upload.
So I thought I would ask the forum and see if there is a trick I'm missing. The 5 longs I'm trying to save are in DAT blocks and I can get their old addresses and values before I reboot but I have no idea what their new addresses are in the new firmware. Any thoughts or creative ways I can get the variables new addresses out of the EEPROM?
Comments
If you don't want to access them via long[MY_PERSISTENT_VAR_ADDR], then on boot and save you can use an EEPROM library to read and write them from the top of EEPROM into a normally placed DAT block.
Spin seriously needs a way to allow the programmer to manually specify addresses of things.
Jon, are you responding to my first post at the top of this tread or to Seairth? If your responding to my top post I'm not following what your telling me. There is no way I'm going to know the address of my longs stored in a DAT variables once the new firmware has been copied over the lower EEPROM. Right?? You will need to read the top post for this to make sense. Thanks!
If my app receives an uploaded firmware image it places it in upper EEPROM as I explained at the top of this thread. I then copy the upper 32k of EEPROM memory to the lower 32k of EEPROM. But, before I reboot the prop I place my five longs that I want to keep in the highest five locations of memory and also write that to the EEPROM lower 32k. After a reboot my program looks to see if there is any data in the upper part of memory and if there is it populates the dat values with that data and saves them.
The following cost me several hours today:
I was trying to pick the starting address for my 5 longs with this command x := @long[$7FFF][-5]. Extra points for the first person who catches why I had to use x := @long[$7FFC][-5]. Kicked my Smile.
Yea that was the original plan. But the address of the variables change after I copy the upper EEPROM to the lower EEPROM. I no longer know the address of the Dat variables (in EEPROM) at this point so I cant copy them to the lower EEPROM. But I still know the values. So what I ended up doing was to copy the dat variables to upper RAM and save to EEPROM for the next reboot. Then after a reboot my code looks to see if there is any data in upper RAM and if so it knows a firmware update has occurred. I then copy the data to the Dat variables and save to EEPROM.
I like this idea!! Easy to do. When I'm copying my EEPROM from upper to lower just stop short of the last page or so.
Thanks!!
Hah. Yeah, I can see how this would have bit you. And it's so innocuous! It made me wonder if there was another, more obvious way to state it. But I couldn't think of one. Maybe just x := $8000 - (4*5) . But I think it would still deserve a code comment.
Well, if you have no idea where your new DAT table is, you're kind of hosed. It's dicey, but you might create a flag value to look for in the code. If you do this, I would suggest putting the DAT table at the top of your listing so it gets compiled into a low section of memory. Read through the newly downloaded block in upper memory until you find your flag values -- your table can follow.
I'm missed the part of you not knowing where your table is located in the new code.
This is a simplistic approach, but it may be workable. I tested with RAM and it worked as I had hoped.
(byte[object_address+2] + byte[object_address+3])*4 + object_address.
The top object starts at address 16.
Thanks Dave this is very good to know. My program has several objects each with their own Dat block. I do this so I can swap the objects between projects and all the variables for that object are in one location.
So what I did inside of my EEPROM / XModem object is create a method that populates a table of addresses. So my main object during IPL populates this table with the variables I want to save if I ever do a firmware upgrade. This way I can save any variable in any object as long as my Main method knows its address.
But this is awesome information and I know I will refer back to this some day so I wanted to comment on it so I can find it <smile>!!
Thanks very much!!
Thanks Jon I like this I may use it in the future.
I ended up just doing a reboot with the new values in upper part of the main memory. Once I reboot during IPL I look in upper memory for saved values if they exist I poke them down where they belong delete them from upper memory, store to EEPROM and reboot one more time.
oooh no, I didn't do that. Hope I'm not missing something. I think this thread is getting confusing as we have a few conversations going on and you may be commenting on another suggestion. But just to be safe, here is what I have working. This is the process I go through to upgrade my prop based projects wirelessly over ZigBee.
1) save my EEPROM file to my PC's local hard drive (I use the Propeller Tool and click on "Save EEPROM File" button from the Object Info window).
2) Load Digi's X-CTU program (old version) and click on the XModem to bring up its XModem Transfer program. The X-CTU is talking to a Digi USB dongle connected to my local PC.
3) Out on the ZigBee network my XModem SPIN object listens for commands from the X-CTU on ZigBee endpont 0x08, Cluster 0x0011. If I type a "?" in the X-CTU terminal window my XModem SPIN object sends a menu to the terminal window. One of the options in that menu is to start a XModem upload. I select that and click on send back on the X-CTU. This copies my EEPROM file to the upper half of my over-sized EEPROM. Once I do that I bring the menu back up and select the verify option. This causes the process to be repeated but this time instead of copying the data to the upper half of EEPROM it does a compare to make sure I have clean copy.
4) At this point I have my original code in the lower half of EEPROM and the new code in the upper half of EEPROM. There is one more command on my menu called "RstFctry". What it does is simply copy the upper half of EEPROM to lower half and reboot. I have been using this process for over a year works great as long as its okay to replace all the code in the lower half of the EEPROM. The project I'm currently working on has a few sensors that are calibrated on the bench and cant be re-calibrated in the field. So I need to save those calibration settings that are stored in the lower half of the EEPROM before I copy the the upper EEPROM over the top of it. So here is what I did: I go ahead and copy the upper EEPROM over the lower EEPROM but, before I reset the propeller I poke the five calibration settings I need to keep into upper RAM starting at $7FDC ($7FFC - 5 longs). I then save those 5 longs to the lower EEPROM. So now my lower EEPROM now has the new firmware and the old calibration settings poked in at 0x7FDC. Now I reboot and I have a new method that is part of my IPL that takes a look at $7FDC for data and if there is some it pokes it back down into the correct location erases it out of memory and reboots a second time. Now I have the new firmware with the old Calibration settings as Dat variables just like before.
I don't think this process requires a recalculation of the check-sum at address 0x0005 does it? I don't get any errors and everything looks fine.
Sorry for the winded response but I just want to make sure I catch everything!! If you still think I need to recalculate the Check-sum I will start looking into it. Doesn't sound like fun!
Thanks!!
Yeah, I stumbled over this gem a couple of times. Have not found any use for it yet. But basically you can overwrite this EEPROM location to save some value used by your program even with just a 32K EEPROM.
Another thing to keep in mind is that if you have some item in - say - a DAT block the address in RAM is the same as in the EEPROM. So @whatever gives you the location of that whatever in the EEPROM also. Neat way to put standard values into your code but save/overwrite them with actual settings to be available after the next reboot.
@Electrodudes comment about _free and _stack had me pull out that propeller book again and put it back into my 'restroom'.
Totally forgot about them. Very useful.
What I do not get is this x := @long[$7FFF][-5] versus x := @long[$7FFC][-5] versus x := $8000 - (4*5) thing.
Why not just say x := $7FEC. It is a fixed address, isn't it? Or to get the value x := long[$7FEC]. Confusing to put arithmetic into the source for a fixed value. Use a defined constant with a proper name and be done with it. They do not cost any space and are very readable.
So doing something like
CON
setting1 = $7FEC
setting2 = $7FF0
setting3 = $7FF4
setting4 = $7FF8
setting5 = $7FFC
and access as x := long[setting5] or long[setting5] := x.
This way you save a lot of spin byte codes since constants do not need space, but computations do.
just my 2 cents.
Mike
The reason I "put arithmetic" into finding the starting address of my longs I poke into upper memory is I may need to save more than 5 longs in another application. I use this statement in my generic firmware update object that I include in almost all my projects. It upgrades the application's firmware wirelessly, it also has a bunch of other methods for Xmodem transfer etc. The -5 is actually a Dat variable in my real code. Some applications have 5 longs I need to save during a firmware upgrade others have more. This way I have one object to maintain but works everywhere.
The reason I posted this:
The following cost me several hours today:
I was trying to pick the starting address for my 5 longs with this command x := @long[$7FFF][-5]. Extra points for the first person who catches why I had to use x := @long[$7FFC][-5]. Kicked my Smile.
Was to point out you can't start a long at $7FFF - 4 as its not a legal starting address for a long. I didn't know that long aligned address have their lowest two bits of the BaseAddress cleared to make sure it is properly aligned in memory. It took me hours to figure out why my data was not aligned correctly in memory. I found the lowest two bits are cleared in the 2nd paragraph on page 132 of the manual as I reread the LONG command for the nth time.
I do have to say I think the Spin Language Reference "Propeller manual" is one of the best written reference guides I have read!! It is one of the reasons the propeller is so successful and time enduring!!
BTW have you seen the Tips and Traps thread. Some good points here.
That can be a bugger to find. Attached is the PDF.
Just shot through it. Lots of tips on PASM, good stuff. Thanks.