SPI Programming in P2ASM
in Propeller 2
Hello!
I been working with SPI code a bit and the onboard SPI flash for the P2 Edge. I stumbled upon this code for an SPI programming but ran it through FlexProp and got an error for this line:
https://forums.parallax.com/discussion/165060/spi-flash-programmer-code/p1
testin #spi_do wc
to this
testp #spi_do wc
However, it doesn't seem to blink anything. I was able to try some of the code for reading and changed a few things. When I Compile and Flash a program via FlexProp the program is stored. I can then read/run the program on flash. Not sure why the code in the thread does not seem to work for the writing though.
Comments
testp looks right. Not sure why doesn't work... Maybe post full code?
You can get the spreadsheet with all the P2 assembly instructions here:
https://www.parallax.com/propeller-2/documentation/
Hey there @Rayman!
Sure thing! Here is that complete code:
' Program SPI flash with signed OUTB blinker program ' - Connect SPI flash (M25P80 okay) with pull-ups on spi_cs and spi_ck ' - Blinks OUTB on boot-up CON spi_cs = 61 spi_ck = 60 spi_di = 59 spi_do = 58 DAT org ' ' ' Init SPI pins ' outh #spi_cs dirh #spi_cs dirh #spi_ck dirh #spi_di ' ' ' Erase 1st $1000 bytes ' call #spi_wrena 'write enable mov cmd,cmd_erase 'sector erase call #spi_cmd32 call #spi_wait 'wait for completion ' ' ' Program first $400 bytes ' loc ptra,#\pgmdata 'point to program data .program call #spi_wrena 'write enable mov cmd,cmd_program 'page program or cmd,adr call #spi_cmd32 .byte rdbyte cmd,ptra++ 'get byte mov x,#8 'send byte shl cmd,#24 call #spi_out add adr,#1 'page done? test adr,#$FF wz if_nz jmp #.byte call #spi_wait 'wait for completion testb adr,#10 wz 'another page? if_z jmp #.program ' ' ' Read data back to outa for viewing on logic analyzer ' mov dira,#$1FF .read1k mov cmd,cmd_read 'start read call #spi_cmd32 outh #8 'trigger signal outl #8 decod y,#10 'read byte to outa .read call #spi_in setbyte outa,cmd,#0 djnz y,#.read jmp #.read1k 'loop ' ' ' SPI write enable ' spi_wrena mov cmd,#$06 'write enable call #spi_cmd8 ret ' ' ' SPI wait while busy ' spi_wait mov cmd,#$05 call #spi_cmd8 .wait call #spi_in test cmd,#$01 wc if_c jmp #.wait ret ' ' ' SPI command ' spi_cmd32 mov x,#32 jmp #spi_cmd spi_cmd8 mov x,#8 shl cmd,#24 spi_cmd outh #spi_cs outl #spi_cs ' ' ' SPI long/byte out (x=bits, cmd=msbdata) ' spi_out rep @.r,x shl cmd,#1 wc outc #spi_di outh #spi_ck outl #spi_ck .r ret ' ' ' SPI byte in (cmd) ' spi_in mov y, #8 mov cmd, #0 .rdloop outh #spi_ck testp #spi_do wc rcl cmd, #1 outl #spi_ck djnz y, #.rdloop ret ' ' ' Data ' cmd_erase long $20_00_00_00 cmd_program long $02_00_00_00 cmd_read long $03_00_00_00 adr long 0 ' ' ' Variables ' cmd res 1 x res 1 y res 1 ' ' ' Program Data ' ' first 20 bytes are blinker program: ' ' not dirb '.lp not outb ' waitx ##20_000_000/4 ' jmp #.lp ' ' last 32 bytes are signature (key=0) ' orgh pgmdata byte $FB,$F7,$23,$F6,$FD,$FB,$23,$F6,$25,$26,$80,$FF,$28,$80,$66,$FD 'blinker program byte $F0,$FF,$9F,$FD,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 byte $99,$AA,$44,$98,$86,$E2,$C8,$71,$C3,$1E,$60,$BF,$A3,$36,$19,$7A 'SHA-256/HMAC signature byte $F5,$3D,$53,$97,$5C,$AF,$BA,$BB,$B7,$7F,$C3,$0A,$B4,$24,$02,$40
I only changed that line in FlexProp. But even then it doesn't work as it should i think. I know it's writing something though since it overwrote the program i had in place previously on the spi Flash. I've been having a bit of trouble with this thing having the boot mode on/off. Very weird behavior at times. I got the read working from using the manual a bit and this code.
Yes, testp is the correct instruction. This code is for PNut (FlexProp does inline assembly differently), but this code does work.
pri shiftin(bits) : result | tix tix := ticks ' tix in 1/2 clock org rep #6, bits testp #SF_MISO wc ' scan MISO rcl result, #1 ' move new bit to result.0 drvh #SF_SCLK ' clock next bit waitx tix drvl #SF_SCLK waitx tix end
Hello @JonnyMac!
Thank you. Okay I did get reading working in another program. I think i'll pick apart the writing part a bit under FlexProp and see what I come up with.
I recommend use of SPI clock mode 3 rather than 0. The shared SD card wiring conflicts much less when the clock idles high.
Howdy @evanh
I'll give this a shot as well in the future. Would this wiring conflict cause an SD card to randomly delete itself? I went through some SD card code and created a driver for writing which works based on the reading you had helped with before. I noticed if I ran the code a few times though it blew away the SD card once i tried to read. Off-topic but just curious.
All it would take is the erasure of block zero to stop a SD card from mounting correctly. So, yes, routines built to write blocks can easily accidentally make the card look empty. There is no filesystem management by the card itself.
As for SPI clock mode 0, I expect it would more likely cause lock-ups rather than erasures. But I have no specific evidence of what actually happens though.
Another problem I discovered recently is the historical FSRW SD card FAT filesystem for the Propellers has had deficiencies in its driver layer that did not wait for card busy condition. This is particularly bad thing when writing blocks. The chances of skipped blocks is high.
If you've been using FSRW software then that would explain filesystem corruption.
Well, the CRC checksums are supposed to protect against such corruption, but SPI mode doesn't use them by default... On the P2 nothing should be stopping us from using CMD59 to enable them.
Thanks for this. That narrows things down a bit. I'm doing FAT32 cause that's what I'm most familiar with.
I got a little further but I'm pretty sure the program never moves from hub ram to the flash.
' Program SPI flash with signed program CON spi_cs = 61 spi_ck = 60 spi_di = 59 spi_do = 58 DAT orgh pgmdata byte $02, $00, $00, $FF, $04, $00, $6C, $FC, $02, $00, $00, $FF, $48, $84, $4C, $FC byte $02, $00, $00, $FF, $49, $00, $4C, $FC, $02, $00, $00, $FF, $04, $02, $6C, $FC byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 byte $99, $AA, $44, $98, $86, $E2, $C8, $71, $C3, $1E, $60, $BF, $A3, $36, $19, $7A 'SHA-256/HMAC signature byte $F5, $3D, $53, $97, $5C, $AF, $BA, $BB, $B7, $7F, $C3, $0A, $B4, $24, $02, $40 org asmclk ' ' ' Init SPI pins ' outh #spi_cs dirh #spi_cs dirh #spi_ck dirh #spi_di ' ' ' Erase 1st $1000 bytes ' call #spi_wrena 'write enable mov cmd,cmd_erase 'sector erase call #spi_cmd32 call #spi_wait 'wait for completion ' ' ' Program first $400 bytes ' loc ptra,#\pgmdata 'point to program data .program call #spi_wrena 'write enable mov cmd,cmd_program 'page program or cmd,adr call #spi_cmd32 .byte rdbyte cmd,ptra++ 'get byte mov x,#8 'send byte shl cmd,#24 call #spi_out add adr,#1 'page done? test adr,#$FF wz if_nz jmp #.byte call #spi_wait 'wait for completion cmp adr, ##$100 wz if_nz jmp #.program ' ' ' Read data back to outa for viewing on logic analyzer ' mov dira,#$1FF .read1k mov cmd,cmd_read 'start read call #spi_cmd32 outh #8 'trigger signal outl #8 decod y,#10 'read byte to outa .read call #spi_in setbyte outa,cmd,#0 djnz y,#.read jmp #.read1k 'loop ' ' ' SPI write enable ' spi_wrena mov cmd,#$06 'write enable call #spi_cmd8 ret ' ' ' SPI wait while busy ' spi_wait mov cmd,#$05 call #spi_cmd8 .wait call #spi_in test cmd,#$01 wc if_c jmp #.wait ret ' ' ' SPI command ' spi_cmd32 mov x,#32 jmp #spi_cmd spi_cmd8 mov x,#8 shl cmd,#24 spi_cmd outh #spi_cs outl #spi_cs ' ' ' SPI long/byte out (x=bits, cmd=msbdata) ' spi_out rep @.r,x shl cmd,#1 wc outc #spi_di outh #spi_ck outl #spi_ck .r ret ' ' ' SPI byte in (cmd) ' spi_in rep @.r,#8 outh #spi_ck outl #spi_ck testp #spi_do wc 'due to latencies, 'testin' is from 2 clocks before 'outh' rcl cmd,#1 .r ret ' ' ' Data ' cmd_erase long $20_00_00_00 ' Sector Erase command cmd_program long $02_00_00_00 ' Page Program command cmd_read long $03_00_00_00 ' Read Data command adr long $00_00_00_00 ' 24-bit address, MSB aligned ' ' ' Variables ' cmd res 1 x res 1 y res 1 ' ' ' Program Data ' ' first 20 bytes are blinker program: ' ' not dirb '.lp not outb ' waitx ##20_000_000/4 ' jmp #.lp ' ' last 32 bytes are signature (key=0) '
I may plug away at understanding it a bit more later. Also, I think i might need to generate a new signature. I noticed i was using the one found in the example found here: https://forums.parallax.com/discussion/165060/spi-flash-programmer-code/p1. Though honestly it does work if run from hub ram. I kept in the testp but also replaced the testb with cmp
cmp adr, ##$100 wz if_nz jmp #.program
There's some sample flash programming code in spin2cpp; for example, in Chip's parallax file system (include/filesys/parallax/flash_fs.spin2) and in the littlefs file system code (include/filesys/littlefs/SpiFlash.spin2).
If you're trying to learn how to write programs into the flash, the sources for loadp2 or propeller-loader would be useful. On the P2 booting from flash is a two step process: the ROM loads the first 256 bytes (or so, I may be misremembering the size) from flash into RAM, and then runs that. So the first part of the flash must be a small stand-alone program capable of loading the rest of the program into flash. Chip's code for this is the file "flash_stub.spin2" in the loadp2 source code (https://github.com/totalspectrum/loadp2).
Hiya @ersmith!
Firstly, thank you for all your efforts on FlexProp.
Yes, exactly. I’ll have a look at this stuff before I continue. I would like to finish this before moving onto dual I/O.
+1 to this. Mode 3 or problems with SD cards