Shop OBEX P1 Docs P2 Docs Learn Events
SPIN RAM / EEPROM question — Parallax Forums

SPIN RAM / EEPROM question

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?

Comments

  • Instead of putting in DAT blocks, hardcode their locations to be the top several locations of memory.

    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.
  • I like it! So its safe to poke data directly up at the end of RAM 0x7FFF and down? I know it is reserved for stack space and variable data but I have never written directly to it.
  • Yes, it's safe to store data at the end of RAM for Spin programs. The Spin stack starts immediately after the VAR data space, and grows toward the end of RAM. The stack normally never reaches the end of RAM.
  • @Electrodude and @Dave hein you guys are the best. I'm banging away on the new solution as we speak. Thanks for the quick response!! I love this Forum!!
  • Also, when you are copying the new code from EEPROM high memory to low memory, stop short of overwriting your configuration. That way, you don't risk losing it if the system restarts after the initial copy, but before you've re-written the configuration.
  • As long as you know the source locations (in the lower EEPROM/RAM) and the destination locations (in the upper), no worries. Keep in mind that if you make modifications to your program (the one you want to update to), those locations could change.
  • JonnyMac wrote: »
    As long as you know the source locations (in the lower EEPROM/RAM) and the destination locations (in the upper), no worries. Keep in mind that if you make modifications to your program (the one you want to update to), those locations could change.

    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!

  • RaymanRayman Posts: 14,651
    I guess you need to also modify the loader to read those 5 longs and copy them to lower eeprom before you boot. Otherwise, I think they'd get erased...
  • If you define a constant "_free" in a CON block to have some value, the compiler will complain if the top whatever value longs are intruded upon by code or variables (but not by stack). There's also _stack, which reserves longs of stack space. They're both documented in more detail in the Propeller Manual.
  • If your EEPROM is > 32Kb, you can write your variables above the 32Kb area and have the Prop grab them on startup. I've used this for persistent configuration in the past, since a lot of Prop boards have a 64Kb chip on them.
  • Okay got a working model. Here is what I did.

    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.
  • Rayman wrote: »
    I guess you need to also modify the loader to read those 5 longs and copy them to lower eeprom before you boot. Otherwise, I think they'd get erased...

    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.
  • Seairth wrote: »
    Also, when you are copying the new code from EEPROM high memory to low memory, stop short of overwriting your configuration. That way, you don't risk losing it if the system restarts after the initial copy, but before you've re-written the configuration.

    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!!

  • JohnR2010 wrote: »
    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.

    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.
  • Cluso99Cluso99 Posts: 18,069
    Since you have it working I presume you are recalculating the checksum that is held in hub at about address $0005.
  • JohnR2010 wrote: »
    JonnyMac wrote: »
    As long as you know the source locations (in the lower EEPROM/RAM) and the destination locations (in the upper), no worries. Keep in mind that if you make modifications to your program (the one you want to update to), those locations could change.

    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!

    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.
    con
    
      Locate1 = ("n" << 24) | ("n" << 16) | ("o" << 8) | ("J" << 0)
      Locate2 = ("c" << 24) | ("a" << 16) | ("M" << 8) | ("y" << 0)   
    
    
    dat
    
      Locater               long    Locate1      
                            long    Locate2    
    
      Value1                long    $0001
      Value2                long    $0002
      Value3                long    $0003
    
    
    pub find_locater(addr, n) | check                                ' ram demo -- update for EE
    
      repeat n
        if ((long[addr+0] == Locate1) and (long[addr+4] == Locate2)) 
          return addr+8 
        else
          addr += 4
    
  • Dave HeinDave Hein Posts: 6,347
    edited 2015-09-15 12:25
    Another approach would be to put your data at the beginning of the DAT section in the top object. You can determine the beginning of the DAT section in the top object by using the number of methods and objects listed in the method table. This is computed as follows:
    dat_address := (byte[18] + byte[19])*4 + 16
    
    The number of methods (plus one) contained in an object is located 2 bytes after the beginning of the object. The number of objects is 3 bytes after the beginning of the object. Each entry for a method or object takes four bytes in the method table. Therefore the address of the DAT section is
    (byte[object_address+2] + byte[object_address+3])*4 + object_address.

    The top object starts at address 16.
  • JohnR2010JohnR2010 Posts: 431
    edited 2015-09-15 13:16
    Dave Hein wrote: »
    Another approach would be to put your data at the beginning of the DAT section in the top object. You can determine the beginning of the DAT section in the top object by using the number of methods and objects listed in the method table. This is computed as follows:
    dat_address := (byte[18] + byte[19])*4 + 16
    
    The number of methods (plus one) contained in an object is located 2 bytes after the beginning of the object. The number of objects is 3 bytes after the beginning of the object. Each entry for a method or object takes four bytes in the method table. Therefore the address of the DAT section is
    (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!!

  • JonnyMac wrote: »
    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.

    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.

  • JohnR2010JohnR2010 Posts: 431
    edited 2015-09-15 14:22
    Cluso99 wrote: »
    Since you have it working I presume you are recalculating the checksum that is held in hub at about address $0005.

    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!!
  • The boot program only looks at the checksum when the code is downloaded over the serial port. It ignores the checksum when booting from EEPROM.
  • JohnR2010JohnR2010 Posts: 431
    edited 2015-09-15 15:36
    Dave Hein wrote: »
    The boot program only looks at the checksum when the code is downloaded over the serial port. It ignores the checksum when booting from EEPROM.
    Ahh that makes sense so it uses it to verify the code uploaded correctly to RAM before it is copied to EEPROM.
  • Dave Hein wrote: »
    The boot program only looks at the checksum when the code is downloaded over the serial port. It ignores the checksum when booting from EEPROM.

    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


  • JohnR2010JohnR2010 Posts: 431
    edited 2015-09-16 13:55
    msrobots wrote: »
    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.

    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!!
  • Cluso99Cluso99 Posts: 18,069
    edited 2015-09-16 13:52
    Ah yes! Seems we all missed it.

    BTW have you seen the Tips and Traps thread. Some good points here.
  • Cluso99 wrote: »
    Ah yes! Seems we all missed it.

    BTW have you seen the Tips and Traps thread. Some good points here.

    That can be a bugger to find. Attached is the PDF.

  • Cluso99 wrote: »
    Ah yes! Seems we all missed it.

    BTW have you seen the Tips and Traps thread. Some good points here.

    Just shot through it. Lots of tips on PASM, good stuff. Thanks.
Sign In or Register to comment.