Shop OBEX P1 Docs P2 Docs Learn Events
SPI Flash spin2 code — Parallax Forums

SPI Flash spin2 code

Is there any spin2 code out there yet, to read/write from Flash memory, such as the Flash on the EVAL board ?

something as simple as this would be plenty:

Init
Write(addr, value)
Read(addr, value)

«1

Comments

  • Surely, there must be something out there but I haven't found anything on the GIT, so far. I have written a very simple SPI flash driver. It offers the following functions:
    PUB Erase (flashAdr, size)
    ' size can be 4kB or 64kB
    
    PUB WritePage (hubAdr, flashAdr)
    ' writes a page of 256 bytes
    
    PUB Read (hubAdr, flashAdr, size)
    ' read any number of bytes
    
    PUB Verify (hubAdr, flashAdr, size) : ok
    ' compare HUBRAM to flash contents, true means match
    
    It uses smartpins and is tested on serveral flash ICs including the Winbond chip on the P2EVAL board. If nobody else offers something better I could upload it to GITHUB.
  • evanhevanh Posts: 15,916
    edited 2020-07-06 08:36
    Loadp2 has had a couple of flavours over the years - Both bit-bashed and smartpin based. Pnut has a streamer based one. All are pure pasm and undocumented but the sources are there to read.

  • ManAtWork wrote: »
    Surely, there must be something out there but I haven't found anything on the GIT, so far. I have written a very simple SPI flash driver. It offers the following functions:
    PUB Erase (flashAdr, size)
    ' size can be 4kB or 64kB
    
    PUB WritePage (hubAdr, flashAdr)
    ' writes a page of 256 bytes
    
    PUB Read (hubAdr, flashAdr, size)
    ' read any number of bytes
    
    PUB Verify (hubAdr, flashAdr, size) : ok
    ' compare HUBRAM to flash contents, true means match
    
    It uses smartpins and is tested on serveral flash ICs including the Winbond chip on the P2EVAL board. If nobody else offers something better I could upload it to GITHUB.

    That seems like a perfect addition to GitHub, regardless of any other possible options. If you attach the code file here I could get it added to GitHub. Or submit at Github in the usual way if you prefer that route.

    Oh, and thanks for sharing!


  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-07-06 13:19
    I am almost there. The trick with the flash is that you cannot write to it if it has been previously written to; in the latter case you must erase the entire 4K sector before writing anything in that sector. What this means, then, is that the object needs a 4K buffer to work with. When a new sector is needed, the old is flushed (erase sector, write buffer to sector) to the physical device and the new one read into the buffer.
  • RaymanRayman Posts: 14,646
    edited 2020-07-06 15:17
    Another way is to use FSRW for Flash:
    http://forums.parallax.com/discussion/171517/

    You can read and write files, as if were uSD card...
  • RaymanRayman Posts: 14,646
    Actually, you can write to a sector that has already been written to... Just not to the same exact addresses that are written...

    Way I remember it is that formatting makes it all ones. Writing just turns some ones into zeros...
  • Wuerfel_21Wuerfel_21 Posts: 5,053
    edited 2020-07-06 16:26
    Yes, that's what a lot of flash does, it just ANDs whatever you write with what is already there (and erasing just sets all the bits again). So in many cases, a read/erase/write cycle can be replaced with just a write by designing the stored data format in a way that is conducive to that.
  • JonnyMacJonnyMac Posts: 9,104
    edited 2020-07-06 16:35
    Actually, you can write to a sector that has already been written to... Just not to the same exact addresses that are written...
    I understand that. My goal is to use the flash like I use the EE on the P1. I have a P1 client project that needs more storage than we can squeeze into 1 or 2 EEs, so I'm writing generic code that I can back-port to the P1 for that project.
  • VonSzarvas wrote: »
    > If nobody else offers something better I could upload it to GITHUB.

    That seems like a perfect addition to GitHub, regardless of any other possible options. If you attach the code file here I could get it added to GitHub. Or submit at Github in the usual way if you prefer that route.

    I've just created a pull request. Should be available within some days...

  • Nice work, ManAtWork :)

    I'm going to test the code tomorrow morning.

  • @ManAtWork

    Having an issue with the WritePage function, as it appears to block and not return from Spi_Wait()

    I have the Dip-Switch FLASH setting on, so that CS is connected to P61. And removed the SD card.
    (This is with an Eval RevB and on-board flash)

    Code attempted (which I expected from the code comments to copy 256 bytes from hub to flash):

    WritePage($400, 0) ' hubAdr, flashAdr


    I'm wondering if we need to call erase before write, or some other thing.
    Would you have a example program you could share, perhaps that writes and verifies a 256 byte block?
  • Are you writing on top of the program space? In my experiments, I used addresses ($10_0000) and higher to make sure I was clear of the P2 program memory space.
  • That WritePage command copies the hub to flash, so I think the answer is no, as I understood the program to be running from hub, and not flash.

    In case, I tried the suggestion : spi.WritePage($400, $10_0000) ' hubAdr, flashAdr
    However, same hang issue.
  • Ooops. I have modified the code so that it compiles with both Pnut and FastSpin. As I've only replaced asm with ORG and inserted some empty brackets after PUB function names I haven't tested it, again. Something got screwed up :neutral: Read seems to work but write locks up the flash device. Spi_Wait does not hang, here because DO is stuck at 0 instead of 1. I have to check what's going on...
  • Test code:
      testAdr = $60000
    
    OBJ
      com: "FullDuplexSerial2"
      spi: "SpiFlash"
    
    PUB main 
    ...
      com.str ("erase...")
      spi.Erase (testAdr, $1000)
      com.str ("read before:")
      spi.Read (@buffer, testAdr, 256)
      repeat i from 0 to 7
        com.hex (buffer[i], 2)
        com.tx (" ")
      com.tx(10)
      repeat i from 0 to 255
        buffer[i]:= i
      spi.WritePage (@buffer, testAdr)
      com.str ("read after write:")
      spi.Read (@buffer, testAdr, 256)
      repeat i from 0 to 7
        com.hex (buffer[i], 2)
        com.tx (" ")
      com.tx(10)
    
  • As a quick test I just checked out the old version from GIT. This one works, but unfortunatelly only with Fastspin. I have to find out what went wrong.verwirrt.gif
  • Hmm, strange... I replaced all occurences of asm/endasm with ORG/END and added empty bracket pairs to all function calls and definitions without parameters, again, and it still works. Don't know what went wrong with the latest version on GITHUB. Maybe the indentication was mixed up by saving in Pnut. The attached version should work. I can't compile with Pnut/PropTool at the moment.
  • One thing I noticed on the GitHub version, was that the comment block at the end of the file seemed to have some control chars at the start and end of some of the lines, which the Propeller Tool highlighted in white. And also the entire comment block didn't seem to be treated as a comment. I added curly braces around that block to allow compilation.

    You might also be onto something about the indentation. Will be able to take a look in the morning- maybe something will stand out with another pair of eyes on the code.
  • VonSzarvasVonSzarvas Posts: 3,450
    edited 2020-07-09 10:05
    This archive works with PropellerTool (v.2.2.0.0 Alpha)

    Instructions:

    Hardware : P2 Eval board, set the FLASH dip switch to ON, connect to PC with USB cable
    Software : Open Propeller Tool and Parallax Serial Terminal

    Load/run the file "FlashDemo"
    Within 2 seconds, Enable the Parallax Serial Terminal (or equivalent)

    Expected output on serial terminal :
    erase...
    
    read before: -1 -1 -1 -1 -1 -1 -1 -1
    
    read after write: 0 1 2 3 4 5 6 7
    



    Note: This archive includes a modified version of the @ManAtWork SpiFlash library, which works with PNut/PropellerTool
    Caveat: The library works with the FlashDemo, but not tested beyond that. This submission not formatted, documented nor proven ready for GitHub yet.
  • Thanks VonSzarvas, I'll check it this evening and send an update to GITHUB when everything works.
  • There's stills something a bit random. After a few calls the code will block again. Then it's fine after a powercycle.

    Maybe cs or clock is getting held in smartpin mode, and so ignoring the drive low or high pin actions to release when needed. <guess> <guess>

  • Hmm, some quotes from Murphy's law for programmers:
    1. If you don't test it it won't work no matter how trivial the task may seem.

    2. A library that works in all cases is always at least 10 times harder to write than a short test that works on only one board.

    I just ran a test with a repeat 100 loop of a erase-write-verify cycles for 5 times. It did 500 cycles without an error. Unfortunatelly I don't have the original P2EVAL board. I think I had connected the flash IC the samy way but maybe some pullup/pulldowns are different.
    Maybe cs or clock is getting held in smartpin mode, and so ignoring the drive low or high pin actions to release when needed. <guess> <guess>

    That shouldn't matter as long as the /CS signal is held high. Anyway, I don't reset or disable the clock smartpin at the end of an operation. I just output a finite sequence of 16 transitions = 8 pulses with wypin #16,#spi_ck. SPI_CK should stay stable when the counter expires.

    I also checked the phase relation between CK and DI (P2 out) with the scope and it looks good. The only thing that is a bit uncertain is the delay between the CK edge and the sample point of the DO (P2 in) signal. But that shouldn't cause a hang. If the setup or hold time is violated, false data is read what only can cause a verify or read to fail or a Spi_Wait() finish too early or too late.
  • Schematic (couldn't be simpler)
    549 x 424 - 7K
  • Just in the case that the input synchronisation is the problem...
    VonSzarvas, could you please test this one?
    PRI Spi_RdByte (): b
    ' read 8 bits
      ORG
    	wypin	#16,#spi_ck		'start 16 clock transitions
    	mov	b,#0
    	waitx	#2			' 1..4 works for me
    	'nop				'read later to compensate input/output delay
    	'nop				' 2 or 3 nops works, 1+4 nops don't
    	'nop
    	rep	#.loop,#8
    	testp   #spi_do wc
    	rcl	b,#1
    .loop
      END
    
    the original 4 nops (without rep) were probably a bit close to the edge. Waitx #2 + rep should be equivalent to 3 nops and might work better.
  • I did some further investigations. I replaced the code in the working file from the posting above function by function with the code from the GITHUB that doesn't work. I can replace all functions except Spi_RdByte() and it still works. The code looks exactly the same but one works and the other doesn't.

    Strange... and even more weired: If I take the bad code and remove one NOP it works, again! I have to disassemble the machine code to find the difference
    kopfkratz.gif
  • Don't use "rep #.loop, #8". I think fastspin does the "right" thing for that in inline assembly, but it's kind of by accident. Use "rep @.loop, #8" instead, both fastspin and pnut should like that.
  • VonSzarvasVonSzarvas Posts: 3,450
    edited 2020-07-09 20:46
    WritePage, Erase and Read seem ok now.

    Verify still misbehaving. That seems to work when verifying 64 bytes, but not 128 or 256

    Not looked any further yet.

    ps. Thanks for the update @ManAtWork , and for the extra tip @ersmith
    By fixed, I mean that I replaced the RdByte code with that 3 posts up, and edited the rep.loop line ersmith suggested.
  • This archive includes the latest SpiFlash.spin2 object mentioned in the last few posts, and the FlashDemo.spin2 file with some improved debugging.

    The buffer is cleared to -2 before each operation.

    Only 0-63 can be read back at the moment.

    <guess> Spi_RdByte doesn't understand values above 6 bits, or requires an extra pause or cs toggle each X bytes read ?
  • ManAtWorkManAtWork Posts: 2,176
    edited 2020-07-13 12:01
    Huh!? This makes absolutely no sense. It's definitely not a problem of the hardware timing or the smartpin config. Just for fun I have modified the Demo to
      spi.Read (@buffer, FLASH_ADR, 80)
    ...
      spi.Read (@buffer, FLASH_ADR, 32)
    
    and the it reads only 20 and 8 bytes. Somehow the parameter size is divided by 4 or shifted left by 2 bits.
    PUB Read (hubAdr, flashAdr, size)
    ' read any number of bytes
      Spi_Init ()
      Spi_Cmd32 (Read_Data, flashAdr)
      repeat size
        byte[hubAdr++]:= Spi_RdByte ()
      ORG
        drvh        #spi_cs
      END
    
    I've seen many such mistakes when mixing up cog and hub adresses or using post-increment on long pointers. But here I definitely use byte[] and the parameter size is not used anywhere except for the repeat loop counter. Even if the adresses were calculated wrong the loop MUST be executed 256 (or 80 or 32) times.
Sign In or Register to comment.