Shop OBEX P1 Docs P2 Docs Learn Events
EEPROM value written using BS2 Functions but not retained/not read properly after restart - Spin, P1 — Parallax Forums

EEPROM value written using BS2 Functions but not retained/not read properly after restart - Spin, P1

JufraJufra Posts: 24
edited 2019-12-31 02:48 in Propeller 1
Hi all,
I've got a P1 spin application that reads water flow rates from an ultrasonic flowmeter. As this code goes into a number of different products (the products differ by maximum flow rate), the software can be configured to set the correct maximum flow rate. This is done via a serial interface, and once the value is received, it is written to EEPROM using the Write_CodeMem routine from BS2 Functions ( https://github.com/parallaxinc/propeller/commit/8113778fa5586512d7282070d20d487ad6a8239f ). It all works fine except that in certain situations it does not read the EEPROM value back when it restarts. The following spin code is there at startup to see if there is an EEPROM value stored, and if not, it assigns the default value of 50.

if (max_flow_rate == 0) 'check if EEPROM value exists, if not, assign default 50l unit size)
max_flow_rate := 50

99% of the times it works, ie, if I have set another value then 50 somewhere else in the code it gets written to EEPROM and upon restart the value is read back. Except in a few niggly cases this doesn't work and it defaults back to 50 (which means it reads EEPROM value as 0 in the above if statement, even though it was written to EEPROM before).
And yes, I've checked the variable size and the size of the BS2 command,
BS2[0].Write_CodeMem(@max_flow_rate, max_flow_rate, 2) 'word size


So my question is: If there is a brown out or loss of power for a short time and the prop restarts, could it be the EEPROM is not read back into RAM but the variable is still somehow cleared? Or something along those lines?
This happens only rarely and I believe it does happen when brown outs occur (but not 100% sure, we got this out in the field).
Thanks a lot for your help

Cheers
Frank

Comments

  • RaymanRayman Posts: 13,798
    Maybe do a read check right after you write to make sure it was properly written?
  • Are you using 32K or 64K EEPROM? Are you writing to the upper 32K on a 64K EEPROM?
  • yisiguroyisiguro Posts: 52
    edited 2020-01-02 10:26
    Is it assumed that invoking Write_CodeMem() is atomic ?
    Is DIRA[29] accidentally set to high by another cog ?
    BS2[0].Write_CodeMem(@max_flow_rate, max_flow_rate, 2) 'word size
    I wonder why you did not say "BS2" but "BS2[0]"...
    Are there Some BS2 objects at once ?
  • JufraJufra Posts: 24
    edited 2020-01-03 06:14
    Hi guys, thanks for this. Yes, Rayman, good idea, I may do a read check straight after the write.
    @Publison I am using a 32K EEPROM
    @yisiguro I will check if another COG can change pin 29 at the same time, I don't think so, but will double check. YEs, there are a few BS2 objects there for other things, but writecodemem is not used concurrently
  • Cluso99Cluso99 Posts: 18,066
    Aha, the 32KB EEPROM cannot easily save variables because there is a checksum. All the routines I’ve seen for saving variables saved above 32KB.
    What you’re probably seeing is leftover values in RAM.
  • RaymanRayman Posts: 13,798
    I'm pretty sure I've used this idea to change values in the lower 32kB.
    Does the prop check this checksum when booting? I don't think it does...
  • Cluso99Cluso99 Posts: 18,066
    Yes it does check the checksum. Its a byte at $0005 IIRC.
  • yisiguroyisiguro Posts: 52
    edited 2020-01-04 04:55
    I've gone through "booter.src", it seems that there is no sum-checking:
    boot			mov	smode,#0		'clear mode in case error
    
    			call	#ee_read		'send read command
    :loop			call	#ee_receive		'get eeprom byte
    			wrbyte	eedata,address		'write to ram
    			add	address,#1		'inc address
    			djnz	count,#:loop		'loop until done
    			call	#ee_stop		'end read (followed by launch)
    
    Though it also seems that there is validity-checking of pBase address before launching interpreter:
    launch			rdword	address,#$0004+2	'if pbase address invalid, shutdown
    			cmp	address,#$0010	wz
    	if_nz		jmp	#shutdown
    
    On the other hand, I wonder if variables are initialized by data stored in the eeprom, but PorpellerManual says like below:
    In Spin, all variables defined in VAR blocks are initialized to zero. Though it is rarely implemented, it is possible to force these variables to initialize to a non-zero value upon the next boot up by including code that updates the application's global variable area in the external EEPROM.
    I think that this is a reason why no check-sum is there for data in the eeprom.
  • Cluso99Cluso99 Posts: 18,066
    I am fairly certain you missed the checksum check as i recall that i had to modify it when i used a 24C64 which remapped 4x. Its just a quick xor iirc and a compare with the byte at $0005.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2020-01-04 09:31
    There is no check at boot, only for the Spin tool when it loads a binary, which it won't if the checksum is not correct.

    I got Tachyon to check the EEPROM, write a zero to 5 and then rebooted.
    ...  0 EE QD --> 
    0000.0000:   5A 00 5A 00  6E 62 10 00  94 7E 9C 7E  74 7E A0 7E    Z.Z.nb...~.~t~.~
    0000.0010:   84 7E 02 00  64 7E 00 00  B6 02 00 00  1F 1E 00 00    .~..d~.......... ok
    ...  0 5 EC! -->  ok
    ...  0 EE QD --> 
    0000.0000:   5A 00 5A 00  6E 00 10 00  94 7E 9C 7E  74 7E A0 7E    Z.Z.n....~.~t~.~
    0000.0010:   84 7E 02 00  64 7E 00 00  B6 02 00 00  1F 1E 00 00    .~..d~.......... ok
    ...  
    
      Propeller .:.:--TACHYON--:.:. Forth V5r7 NEON 570190926.2300
    CODE:$50E6 = 20198 bytes 
    NAME:$55AA = 7766 bytes 
    DATA:$7920 = 1040 bytes 
    FREE:      = 1220 bytes 
     Data Stack (0)
    
    *** Tachyon Forth EASYNET Network Servers and EASYFILE File Server *** 
    
  • JufraJufra Posts: 24
    edited 2020-01-04 12:08
    Hi guys, I'm a tad lost here, are you saying there's a systemic issue? So writing the value 0 to 5 seems to have worked ok by the look of it?
    I have never written any values to EEPROMS > 32k, in fact I 've never used >32K chips with propeller chips. Writing to EEPROM always worked the way I described... well, except in this case.
    So is it picking up artefacts in RAM as Cluso99 said?
  • RaymanRayman Posts: 13,798
    edited 2020-01-04 12:13
    No, writing updates to variables in lower eeprom, like you've done, works.
    I've done it before.

    I'm not really sure what your issue is. Somebody suggested multiple cogs might be using the same pins?

    I would define a maximum write attempt at say 10.
    Then, try to write. Read it back and see if OK.
    Repeat trying this 10 times or until successful and then give up... Until next time...
  • RaymanRayman Posts: 13,798
    Actually, this should work 100% every time.

    Something must be wrong... If it's your own board, I'd check solder connections. Also, make sure you have 10k pullups on SDA and SCL.
    Also, double check for code errors, crystal settings, etc.
  • Cluso99Cluso99 Posts: 18,066
    Jufra wrote: »
    Hi guys, I'm a tad lost here, are you saying there's a systemic issue? So writing the value 0 to 5 seems to have worked ok by the look of it?
    I have never written any values to EEPROMS > 32k, in fact I 've never used >32K chips with propeller chips. Writing to EEPROM always worked the way I described... well, except in this case.
    So is it picking up artefacts in RAM as Cluso99 said?

    Peter is usually correct, so I guess that the checksum doesn't need updating (I haven't had time to check yet).
    Anyway, I note your mention of 0 to 5.

    What address(es) in EEPROM are you writing to?
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2020-01-04 23:06
    Cluso99 wrote: »
    Peter is usually correct, so I guess that the checksum doesn't need updating (I haven't had time to check yet).
    Anyway, I note your mention of 0 to 5.

    What address(es) in EEPROM are you writing to?

    I was simply setting address 5 in EEPROM to 0. Here is another quick test dumping EE memory (without the EE it would access hub), writing location 5, reading it back and checking that pbase = $0010. If I changed location 6 from $0010 then the booter will shut the cog down.
    ...  0 $10 EE DUMP --> 
    0000.0000:   5A 00 5A 00  6E 00 10 00  94 7E 9C 7E  74 7E A0 7E    Z.Z.n....~.~t~.~ ok
    ...  $A5 5 EC! -->  ok
    ...  0 $10 EE DUMP --> 
    0000.0000:   5A 00 5A 00  6E A5 10 00  94 7E 9C 7E  74 7E A0 7E    Z.Z.n....~.~t~.~ ok
    ...  5 EC@ .BYTE --> A5 ok
    ...  6 EW@ .WORD --> 0010 ok
    
  • it is a common practice in spin on the P1 to write EEPROM locations in the lower 32k in SPIN.
    the EEPROM lower 32k are exactly at the same address as a Spin DAT variable, VAR will not work as well.

    So if you write the EEPROM at address @datLocation with a new value of datLocation it will be there at the next reboot.

    But it has to be a DAT declared Variable not VAR. So it is stored in the code/binary and has a initial value at boot time.

    The Checksum is just verified when programming the binary into EEPROM, not when booting from it, as far as I experienced it.

    Enjoy!

    Mike
  • Hey Mike, that is interesting, I did not know that. And guess what, the variable I'm writing is declared in VAR, not as DAT, as are all the other ones I've used in the past. So if this isn't working, how come it delivers results most of the time, ie, the value survives the restart? Does it mean that variables declared in VAR can move to different addresses at restart in certain scenarios and hence the value is not restored? Sorry for the questions, I never drilled down that much into those areas...

    So going forward, I would need to declare two variables (one in VAR, the other in DAT) like that
    VAR
    word max_flow_rate_var

    DAT
    max_flow_rate_dat word 0


    and in the code:
    if (max_flow_rate_dat == 0) 'check if EEPROM value exists, if not, assign default 50l unit size)
    max_flow_rate_var := 50
    else
    max_flow_rate_var := max_flow_rate_dat

    And I'd be writing the DAT as before:
    BS2[0].Write_CodeMem(@max_flow_rate_dat, max_flow_rate_var, 2) 'word size

    Is that what you're saying? I guess I could also only have the DAT variable and assign values to it in the code using @max_flow_rate := <newvalue> which would write to RAM and use the WriteCodeMem to commit to EEPROM

    Thanks
    Frank
  • msrobotsmsrobots Posts: 3,701
    edited 2020-01-05 07:16
    No, just declare the variable in DAT with a standard value and use it as is. No need for a VAR variable.

    The main difference between VAR and DAT is that DAT has predefined values and is saved in the program binary. Var is different as if you use more then one copy of the object, each object shares (has the same) code and DAT section(s), but each instance of a object has its own VAR section(s).

    Say two copies of FullduplexSerial will share the code in RAM but have their own buffer in RAM (in VAR)

    And yes you would write it as before.

    But if you reprogram it will be lost. If you write in the upper 32K (if you have 64K EEPROM) then it stays there because programming just writes the first 32K.

    Mike
  • yisiguroyisiguro Posts: 52
    edited 2020-01-06 12:05
    According to the PropellerManual and related comments posted at some discussions past, GLOBAL variables defined in VAR sections are to be initialized with values stored in the EEPROM at boot time. Do I misunderstand ?
  • When I used to do a lot of Spin code it was simply a standard VAR variable and I would write the default value of the variable to the address of the variable in memory.
    VAR
      long pulsewidth,holdoff,feature
    
    PUB
    ' code snippet to update default value of feature so that at reset it was automatically loaded
    
      i2c.eewr(@feature,feature)
    
  • RaymanRayman Posts: 13,798
    I'm with msrobots. I'd always use DAT section for this.
    VAR is strange. I don't think you can count on initialization... Is there a "Global" VAR? I don't think so...
  • yisiguroyisiguro Posts: 52
    edited 2020-01-06 12:27
    I think, every DAT of same objects is shared among all instances of same feather, but each VAR is separated statically. [s]I will check later.[/s] I checked.

    Also I think the term "global" means the symbols which has global scope per-file basis.

    Sorry, I overlooked some posts.
  • Guys, I told you what works, it wasn't a guess. This write to @var method was clearly outlined in the forum in the heyday of Spin and has always worked for me and works in commercial product.
  • Maybe the problem I am seeing is not that the value isn't properly written to the EEPROM, but not properly read back into the variable after reboot:
    so maybe before the if statement I would need to read the value back from EEPROM to make sure I'm checking the EEPROM value?:

    max_flow_rate := BS2.Read_CodeMem(@max_flow_rate, 2) 'word size
    if (max_flow_rate == 0)

    Haven't got time to test this now as I'm about to go for 2 weeks of camping ;-)
    But I will once I'm back
  • localrogerlocalroger Posts: 3,451
    edited 2020-01-07 18:44
    Like Peter I've often written new default values to VARs. It works fine, as long as you don't expect them to be zero at startup. VARs are just as memory-deterministic as DAT blocks, it's just that the Spin compiler puts zeroes there to initialize them. Change the zeroes, change the initial value. Since the problem is intermittent, I wonder if it's because the I2C bus isn't initialized properly? I do my eeprom access with the basic_i2c_driver object, and it has an Initialize PUB which you are supposed to call once when the program starts: "Sometimes an I2C device is left in an invalid state. This will reset the device to a known state so it will respond to the I2C start transition."
  • RaymanRayman Posts: 13,798
    Even if it does work, it's probably not clear to very many people...
    If somebody inherits that code, they may have a hard time understanding what is going on, unless it's well documented in the code...

    Actually, the Manual does say this is OK:
    In Spin, all variables defined in VAR blocks are initialized to zero.  Though it is rarely implemented, it is possible to force these variables to initialize to a non-zero value upon the next boot up by including code that updates the application's global variable area in the external EEPROM.
    

    Still, what if you compile with Fastspin or BST? Will it still work? What about if you do Spin2Cpp on it? There are a lot of questions about this usage...

    Checking that "Initialize" is a good idea. That might cause low frequency failures...
  • localrogerlocalroger Posts: 3,451
    edited 2020-01-07 20:51
    I can verify that the EE write to vars technique works in BST. It should also work in FastSpin, but on P2 you would have to write the update to the flash or boot SD instead of EE. The key is that the variables are allocated statically at compile time so they're always in the same place. I'm not sure how Spin2cpp would even handle the @ operator, which you need in order to place the data in the var in EE. If you can get that address, and it's static, it should even work in C, but it won't work if variables are allocated on a heap at runtime. (It also doesn't work with Spin local vars, even though you can @ them, because they are dynamically allocated on the stack at runtime.)
  • Rayman wrote: »
    Even if it does work, it's probably not clear to very many people...
    If somebody inherits that code, they may have a hard time understanding what is going on, unless it's well documented in the code...

    Actually, the Manual does say this is OK:
    In Spin, all variables defined in VAR blocks are initialized to zero.  Though it is rarely implemented, it is possible to force these variables to initialize to a non-zero value upon the next boot up by including code that updates the application's global variable area in the external EEPROM.
    
    .

    I'm guessing that means it is initialized to zero because it was initialized to zero before it was loaded to EEPROM, not because it tries to do this every time it boots.
    So changing the VAR in EEPROM is all that is required to "preset" that variable. I used to use the Spin tool for this method before BST so it has nothing to do with the compiler.
  • Peter, that's exactly right. There is no code that runs to set the VARs to zero at program init, they start at zero because the compiler initializes the memory image that way. Change the memory image in the EE, and they will init at whatever value you set.
Sign In or Register to comment.