SD Card Speed Issues
in Accessories
Hello All,
I have an SD Card FAT class I built, and all works fine, but I have noticed that not all cards that have the TRAN_SPEED in the cards CSD set to 32(signaling a max clock of 25MHz) can handle data read at 10MHz and write at 20MHz from the propeller. For now, I have my class setup to auto detect the max speed. Does anyone know why certain cards that say they will support up to 25MHz with the TRAN_SPEED setting do not actually support it? I would like to get rid of the auto speed detection if possible, but I want to know why it is a problem in the first place. BTW, when I use to high of a speed, the R1 response is correct from the card, but the data I receive is all jumbled with no discernible pattern. Also, some cards do work with 10Mhz read and 20MHz Write fine(1GB sandisk microSD), i mostly have the problem with read and write not working at max speed on older 64MB or 256MB cards. Thanks!
I have an SD Card FAT class I built, and all works fine, but I have noticed that not all cards that have the TRAN_SPEED in the cards CSD set to 32(signaling a max clock of 25MHz) can handle data read at 10MHz and write at 20MHz from the propeller. For now, I have my class setup to auto detect the max speed. Does anyone know why certain cards that say they will support up to 25MHz with the TRAN_SPEED setting do not actually support it? I would like to get rid of the auto speed detection if possible, but I want to know why it is a problem in the first place. BTW, when I use to high of a speed, the R1 response is correct from the card, but the data I receive is all jumbled with no discernible pattern. Also, some cards do work with 10Mhz read and 20MHz Write fine(1GB sandisk microSD), i mostly have the problem with read and write not working at max speed on older 64MB or 256MB cards. Thanks!
Comments
For write, the fastest is 20MHz using the counters in NCO single-ended mode
Here is the counter init
'Set Counter registers movs ctrb, spic movi ctrb,#%0_00100_000 movs ctra, spio 'Set counter A data pin and mode movi ctra,#%0_00100_000
Here is the write procedure, note that 'loop' is the main wait loop for the spi cog
:write rdbyte r1, dta shl r1, #24 mov phsa, r1 movi frqb,#%010000000 'Start counting at 20MHz! shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 mov frqb, #0 'Stop counting add dta, #1 djnz ctr,#:write mov phsa, #0 'Leave output low since both cogs control output(required!!) wrlong null, flg 'Clear data flag if done jmp #:loop
Fastest for read is 10MHz since we can't link the counters to get input like we can link them for output
:read mov phsb, #0 :rdloop mov r1,#0 'Clear data buffer movi frqb,#%001000000 'Start counting at 10MHz! test spiimask,ina wc 'Bit 7 rcl r1,#1 test spiimask,ina wc 'Bit 6 rcl r1,#1 test spiimask,ina wc 'Bit 5 rcl r1,#1 test spiimask,ina wc 'Bit 4 rcl r1,#1 test spiimask,ina wc 'Bit 3 rcl r1,#1 test spiimask,ina wc 'Bit 2 rcl r1,#1 test spiimask,ina wc 'Bit 1 rcl r1,#1 test spiimask,ina wc 'Bit 0 movi frqb,#0 'Stop Counting rcl r1,#1 wrbyte r1, dta 'Write to buffer add dta, #1 'Increment pointer djnz ctr,#:rdloop 'Loopback for numbytes wrlong null, flg 'Clear data flag if done jmp #:loop
{{ SPI Management Functions Abstract: This class is built to be a universal SPI Driver }} { ASMDATA data[0]= hub data location ptr for read/write data[1]= num of bytes to write/readfunction data[2]= function flag } CON 'Clocking settings _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 VAR OBJ PUB InitSPI(spioutpin, spiinpin, spiclock) : okay { dloc = -1 if coginit,memory address if other } data[2] := 0 'Start with no function 'Set Cog Vars spiomask := |< spioutpin spicmask := |< spiclock spiimask := |< spiinpin spio := spioutpin spic := spiclock okay := cogon := (cog := cognew(@entry,@data)) > 0 PUB WriteSPI(dataloc,numbytes,wrspeed) { Purpose: Write SPI } repeat until data[2] == 0 'Wait until cog ready data[0] := dataloc data[1] := numbytes data[2] := wrspeed + 3 repeat until data[2] == 0 PUB SendCommandR1(command, argument) repeat until data[2] == 0 'Wait until cog ready data[0] := command data[1] := argument data[2] := 6 repeat until data[2] == 0 return data[0] PUB ReadWriteSPISlow(writedata) { Purpose: Read SPI } repeat until data[2] == 0 'Wait until cog ready data[0] := writedata data[2] := 7 repeat until data[2] == 0 return data[0] PUB ReadSPI(dataloc,numbytes,rdspeed) { Purpose: Read SPI } repeat until data[2] == 0 'Wait until cog ready data[0] := dataloc data[1] := numbytes data[2] := rdspeed + 1 repeat until data[2] == 0 PUB Stop if cogon~ cogstop(cog) DAT org 0 entry mov dt, par 'Get data location mov nbytesloc, dt 'Get number of bytes location add nbytesloc, #4 mov flg, nbytesloc 'Get flag location add flg, #4 andn outa, spiomask 'Set pins low andn outa, spicmask or dira, spiomask 'Set pin directions or dira, spicmask andn dira, spiimask 'Set Counter registers movs ctrb, spic movi ctrb,#%0_00100_000 movs ctra, spio 'Set counter A data pin and mode movi ctra,#%0_00100_000 { TASK LOOP 0=No task, keep looping 1=fastread 2=slowread 3=fastwrite 4=slowwrite 5=BLANK 6=sendcommandr1 7=read write spi slow } :loop rdlong r1, flg 'Get data flag tjz r1, #:loop 'If zero,keep looping, waiting for task rdlong ctr, nbytesloc 'Get bytes to write rdlong dta, dt 'Get data location cmp r1,#1 wz 'Jump to option if_z jmp #:read cmp r1,#2 wz if_z jmp #:readslow cmp r1,#3 wz if_z jmp #:write cmp r1,#4 wz if_z jmp #:writeslow cmp r1, #6 wz if_z jmp #:sendcommandr1 cmp r1, #7 wz if_z jmp #:rdwrspislow wrlong null,flg 'If not valid choice, clear, and loop back jmp #:loop :write rdbyte r1, dta shl r1, #24 mov phsa, r1 movi frqb,#%010000000 'Start counting at 20MHz! shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 mov frqb, #0 'Stop counting add dta, #1 djnz ctr,#:write mov phsa, #0 'Leave output low since both cogs control output(required!!) wrlong null, flg 'Clear data flag if done jmp #:loop :writeslow andn outa, spicmask :wrslowloop mov ctrsub, #8 rdbyte r2, dta :wrslowsubloop mov r1, r2 and r1, #$80 wz if_z andn outa, spiomask if_nz or outa, spiomask or outa, spicmask 'Raise Clock shl r2, #1 andn outa, spicmask 'Drop clock djnz ctrsub, #:wrslowsubloop add dta, #1 djnz ctr,#:wrslowloop andn outa, spiomask 'Leave output low since both cogs control output(required!!) wrlong null, flg 'Clear data flag if done jmp #:loop :readslow mov ctrsub, #8 or outa, spiomask :rdsubloopslow or outa, spicmask 'Raise Clock shl r1, #1 'Shift dta left to get next bit test spiimask,ina wz if_nz or r1, #1 andn outa, spicmask 'lower Clock djnz ctrsub, #:rdsubloopslow 'Keep reading if still bits, else, go back to main loop wrbyte r1, dta add dta, #1 djnz ctr, #:readslow wrlong null, flg 'Clear data flag if done andn outa, spiomask jmp #:loop :read mov phsb, #0 :rdloop mov r1,#0 'Clear data buffer movi frqb,#%001000000 'Start counting at 10MHz! test spiimask,ina wc 'Bit 7 rcl r1,#1 test spiimask,ina wc 'Bit 6 rcl r1,#1 test spiimask,ina wc 'Bit 5 rcl r1,#1 test spiimask,ina wc 'Bit 4 rcl r1,#1 test spiimask,ina wc 'Bit 3 rcl r1,#1 test spiimask,ina wc 'Bit 2 rcl r1,#1 test spiimask,ina wc 'Bit 1 rcl r1,#1 test spiimask,ina wc 'Bit 0 movi frqb,#0 'Stop Counting rcl r1,#1 wrbyte r1, dta 'Write to buffer add dta, #1 'Increment pointer djnz ctr,#:rdloop 'Loopback for numbytes wrlong null, flg 'Clear data flag if done jmp #:loop :sendcommandr1 mov spiwrite, dta 'Copy command call #writebytefast 'Write mov spiwrite, ctr 'Copy argument shr spiwrite, #24 call #writebytefast 'Write argument >> 24 mov spiwrite, ctr 'Copy argument shr spiwrite, #16 call #writebytefast 'Write argument >> 16 mov spiwrite, ctr 'Copy argument shr spiwrite, #8 call #writebytefast 'Write argument >> 8 mov spiwrite, ctr 'Copy argument call #writebytefast 'Write argument >> 0 \ call #getcrc7 'Generate CRC mov spiwrite, crc 'Write CRC call #writebytefast mov r2, r1attempts :sendcommandr1loop mov spiwrite, #$FF call #readwritespidelay cmp spiread, #$40 wz, wc if_nz_and_nc djnz r2, #:sendcommandr1loop if_nz_and_nc mov r1, #$FF if_nz_and_nc wrlong r1, dt if_z_or_c mov spiwrite, #$FF if_z_or_c wrlong spiread, dt if_z_or_c call #writebytefast 'Save result 'Write failure result wrlong null, flg jmp #:loop :rdwrspislow mov spiwrite, dta 'Copy data to write call #readwritespidelay wrlong spiread, dt 'Write read data wrlong null, flg jmp #:loop readwritespidelay mov ctrsub, #8 mov spiread, #0 :readwritespidelayloop test spiwrite, #$80 wz if_z andn outa, spiomask if_nz or outa, spiomask or outa, spicmask 'Raise Clock shl spiwrite, #1 'Rotate instead of shift, so multibyte vars can be written with sequential calls shl spiread, #1 test spiimask, ina wz if_nz or spiread, #1 andn outa, spicmask 'Drop clock djnz ctrsub, #:readwritespidelayloop andn outa, spiomask readwritespidelay_ret ret getcrc7 mov crc, #0 mov r1, dta 'CRC command call #crcsub ror ctr, #24 mov r1,ctr 'CRC argument >> 24 and r1, #$FF call #crcsub rol ctr, #8 'CRC argument >> 16 mov r1, ctr and r1, #$FF call #crcsub rol ctr, #8 'CRC argument >> 8 mov r1, ctr and r1, #$FF call #crcsub rol ctr, #8 'CRC argument >> 0 mov r1, ctr and r1, #$FF call #crcsub and crc, #$7F shl crc, #1 or crc, #1 getcrc7_ret ret crcsub 'Data byte stored in R1 mov ctrsub, #8 :crcsubloop shl crc, #1 mov r2, r1 xor r2, crc and r2, #$80 wz if_nz xor crc, #$9 shl r1, #1 djnz ctrsub, #:crcsubloop crcsub_ret ret writebytefast shl spiwrite, #24 mov phsa, spiwrite movi frqb,#%010000000 'Start counting at 20MHz! shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 shl phsa, #1 mov frqb, #0 'Stop counting mov phsa, #0 'Leave output low since both cogs control output(required!!) writebytefast_ret ret null LONG 0 spiomask LONG 0 'SPI out mask spicmask LONG 0 'SPI clk mask spiimask LONG 0 'SPI in mask spio LONG 0 'SPI out pin spic LONG 0 'SPI clk pin r1attempts long 1000 dta res 1 'Data location dt res 1 'Data location pointer nbytesloc res 1 'Location of number of bytes to write ctr res 1 'Byte counter ctrsub res 1 'Bit counter r1 res 1 r2 res 1 flg res 1 'Loop Flag spiwrite res 1 spiread res 1 crc res 1 FIT 496 cogon long 0 cog byte 0 data long 0[3] { ASMDATA data[0]= hub data location ptr for read/write data[1]= num of bytes to write/readfunction data[2]= function flag }
EDIT: I may just hookup the 100MHz scope and check those setup and hold times to be sure.
What I see in your code as a possible problem is that you don't set the phase of the clock generated by the Counter A. You just enable and disable the counter with movi frqa.... This does not affect the current PHSA value so the SPI-clock has a random phaseoffset to the read time point.
All the counter SPI methodes that I know sets first the PHSA then enables the counter with setting the mode in CTRA. FRQA is only set once at begin. It needs some try and error to find the right PHSA value, I've seen $E000_0000 and $6000_0000 in my objects collection.
You should look at the low level drivers for FSRW (in the OBEX) they do all the counter tricks and also a read-ahead and write-behind of blocks with a buffer in cogram.
There is the sdspi_safe driver with 10 MHz read, but also a faster one with a very nifty code from Kuroneko that can read with 20 MBit/s. (This works not with all cog/pin combinations, it really depends on the location of the cog on the die relative to the pin position).
Andy