TACHYON { SD CARD TOOLKIT } : SDCARD_MOD.fth ." Modified SD CARD Toolkit - 120913.2200 " ; { These are the general routines used to initialize and access the SD card and can also be used to load and save blocks directly. There are various tools plus the primitives which allow you to directly interact with the card and it's command set. This module forms the foundation on which other modules for virtual memory and file system will rely. NOTE: The card insertion method used here in CARD? is based on that mentioned in the official SD MEMORY CARD SPECIFICATIONS which detects the pullup that exists on the chip select of a memory card itself. This means that a "safety" pull-up is never required and the card detect will not work if one is used. I will therefore try to modify the target declarations such as P1107, BOE, PPUSB etc to automatically select the required card detect method. You may have to do the same for your target. CHANGELOG: 120913:2200 Modified by N.G. Lordi to work with C3 SPI vocabulary ewhich must be loaded first. 120909:2250 Added CARD detect redefinition vecotr "ucard" 120909:1000 Added extra pin definitions for different boards 120903:1400 Added pin redirection } #512 CONSTANT BLKSIZ \ Create a long aligned array for the SD sector buffer TABLE SDBUF BLKSIZ ALLOT LONG dsize,sdflg,sdwr,sdrd,ocr,sdtimer,timeout WORD ucard \ user vector for card detect BYTE crc TABLE cid #64 ALLOT TABLE csd #64 ALLOT TABLE extbuf #16 ALLOT $FE CONSTANT #datatkn \ data token for single block read/write $FC CONSTANT #datatkn1 \ data token for 1st block read/write $FD CONSTANT #datatkne \ data token for last block read/write \ Set number of bits to transfer with an SPIO operation - change to alias. pub SDCNT ( bits -- ) SPCNT ; ; \ Control the SD chip select - change to alias. pub SDSEL ( on/off -- ) SPSEL ; '' Change the following words to alias. \ Fetch a byte from the SD (clock in 1's) pub SD@ ( -- 8b ) SP@ ; \ Write a byte to the SD pub SD! ( 8b -- ) SP! ; pub SD2@ ( -- word ) SP2@ ; pub SD4@ ( -- long ) SP4@ ; \ Slow SD clock \ cs miso mosi sck pub SLOWCLK ( cnt -- ) @MOSI COGREG@ OUTSET \ Set MOSI high @SCK COGREG@ OUTCLR \ clock low (and as an output) FOR CLOCK CLOCK NEXT \ toggle clock high and low ; \ Faster byte wide clocks (8/count) pub SDCLK ( cnt -- ) FOR SD@ DROP NEXT ; #5000 CONSTANT &sdto \ SD timout period - can be very long \ do what needs to be done if the SD card is busy (LED etc) pri SDBUSY ( state -- ) DROP ; \ Initialize the SD I/O and basic card pub !SDIO [SDRD] \ Load the SDRD module SPI 5 SPSET 5 ms ON SDSEL \ select the card #100 SLOWCLK \ clock it nice and slow (card does not have an internal clock) OFF SDSEL #16 SDCLK ON SDSEL #16 SDCLK OFF SDSEL ; \ Continue reading until a response is received (with timeout) pub RES@ ( -- res ) $FF &sdto 0 \ !!! TIMEOUT PERIOD DO SD@ DUP $FF <> IF NIP LEAVE ELSE DROP THEN LOOP ; pri MARKER? ( marker -- flg ; Find SD marker and return true before timeout ) &sdto BEGIN OVER SD@ <> WHILE 1- DUP 0= IF NIP EXIT THEN REPEAT 2DROP TRUE ; \ Send the command to the SD card and read result pub CMD ( data cmd -- res ) SD@ DROP \ extra clocks ON SDSEL $3F AND $40 OR SD! BL SDCNT SPIO DROP 8 SDCNT crc C@ SD! \ dummy checksum ( is valid for initializing) RES@ ; \ Send an ACMD to the card and return with response pub ACMD ( data acmd -- res ) 0 55d CMD DROP CMD ; pri STAT@ ( -- stat ) 0 13d CMD RES@ 8 SHL OR ; pri SDERR? ( -- flg ; return SD bit flag errors) sdrd @ 0= 2 AND OR sdwr @ 0= 4 AND OR ocr @ 0= 8 AND OR ; pri SDDAT! ( adr -- ; Wait for read token and read SD data into buffer ) #datatkn MARKER? IF 10h BOUNDS DO SD@ I C! LOOP 3 SDCLK ELSE DROP THEN ; : GETCSD 0 9 CMD 0= IF csd SDDAT! THEN ; : GETCID 0 10d CMD 0= IF cid SDDAT! THEN ; \ Initialize the card timeout pri !TIMEOUT CNT@ timeout ! ; pri ?TIMEOUT CNT@ timeout @ - CLKFREQ > IF FALSE R> DROP EXIT THEN ; \ Initialise the SD card (with timeout) : !SD ( -- ocr|false ; Initialise the SD card in SPI mode and return with the OCR ) ocr ~ cid #16 ERASE csd #16 ERASE !SDIO ON IF !TIMEOUT $95 crc C! BEGIN ?TIMEOUT 0 0 CMD 1 = UNTIL $87 crc C! BEGIN $1AA 8 CMD 1 = UNTIL SD4@ $1AA = IF BEGIN ?TIMEOUT $4000.0000 41d ACMD 0= UNTIL BEGIN ?TIMEOUT 0 58d CMD 0= UNTIL SD4@ DUP ocr ! DUP IF GETCID GETCSD THEN ELSE FALSE THEN ELSE FALSE THEN OFF SDSEL ; pri XSHR1 0 extbuf $10 ADO I C@ DUP 2/ ROT OR I C! 1 AND IF $80 ELSE 0 THEN LOOP DROP ; pri XSHR 0 DO XSHR1 LOOP ; pri BEXT ( bith bitl adr -- ) extbuf $10 CMOVE DUP XSHR - 1+ MASK 1- 0 extbuf $0C + 4 BOUNDS DO 8 SHL I C@ OR LOOP AND ; \ Fetch card information fields pub CSD@ ( bith bitl -- n ) csd BEXT ; pub CID@ ( bith bitl -- n ) cid BEXT ; \ Non-aligned long fetch - mainly used for card information fields pri NA@ ( addr -- long ) 0 SWAP 4 0 DO C@++ I 2* 2* 2* SHL ROT + SWAP LOOP DROP ; CREATE tval #00 | #10 | #12 | #13 | #15 | #20 | #25 | #30 | #35 | #40 | #45 | #50 | #55 | #60 | #70 | #80 | \ Card information pub .CARD \ CR ." BYTES: " SDSIZE? . \ 0123456789ABCDEF012345 CR ." MFG: " cid C@ .BYTE CR ." OEM: " cid 1+ 2 TYPE CR ." PROD: " cid 3 + 5 TYPE CR ." REV: " cid 8 + C@ .BYTE CR ." S/N: " cid 9 + NA@ .LONG CR ." DATE: " cid #13 + C@ 8 SHL cid #14 + C@ OR DUP 4 SHR #2000 + #10 .NUM ." /" $0F AND #10 .NUM CR ." CRC: " cid #15 + C@ 2/ .BYTE CR CR ." TACC " 119d 112d CSD@ DUP 3 SHR tval + C@ #10 .NUM 3 AND 1- 0 DO ." 0" LOOP ." ns" CR ." NSAC " 111d 104d CSD@ . ( still more to do ) ; { *** BLOCK BUFFERS *** } { Old slow method - for reference \ 6.564ms write time for a 512 byte block : (SDWR) ( src cnt -- ;write block from memory to SD ) ADO I C@ #24 SHL SPIO DROP LOOP ; } \ Load PASM I/O module into kernel pri (SDWR) ( src cnt -- ) [SDWR] RUNMOD ; pri (SDRD) ( dst cnt -- ) [SDRD] RUNMOD ; pub SDRDBLK ( dst -- crc ) BLKSIZ (SDRD) SD2@ \ read crc 31d MASK OR \ force crc as TRUE flag ; pri PROCESS_TOKEN ( dst token -- flg ) #datatkn = IF SDRDBLK EXIT THEN 2DROP \ drop token and dst 2 SDCLK STAT@ DROP \ discard cycles OFF SDSEL FALSE ; \ SD CARD READ BLOCK pub SDRD ( xadr dst -- crc | false ; Read xadr from SD into dst ) ON SDBUSY SWAP 17d CMD \ read block command IF DROP FALSE ELSE RES@ PROCESS_TOKEN \ process read token THEN OFF SDSEL OFF SDBUSY DUP sdrd ! \ save crc/flg ; \ Write to a sector in the SD card pub SDWR ( src xdst -- flg ; Write from src to xdst in the SD ) ON SDBUSY OFF SDSEL 1 SDCLK ON SDSEL 3 SDCLK 24d CMD ( src res ) 0= IF 3 SDCLK #datatkn SD! BLKSIZ (SDWR) 0 MARKER? $FF MARKER? AND ELSE FALSE THEN OFF SDSEL OFF SDBUSY DUP sdwr ! ; \ Just like a regular memory dump except it's the SD memory and blank sectors are skipped pub XDUMP ( adr cnt -- ) ADO I 7E00 SDRD DROP 0 $7E00 $200 ADO I @ OR 4 +LOOP IF $7E00 $200 ADO CR J I $1FF AND + $4810 .NUM ." : " I $10 ADO I C@ .BYTE SPACE LOOP SPACE SPACE I $10 ADO I C@ DUP BL $7E WITHIN IF EMIT ELSE DROP ." ." THEN LOOP $10 +LOOP THEN KEY? IF $1B = IF LEAVE ELSE KEY DROP THEN ELSE DROP THEN $200 +LOOP ; BYTE sdcard \ Quick terminal command to initialise a card and dislay some information pub SD !SD IF CR ." Card inserted " .CARD CR ELSE ." Card Error " THEN ; \ 192.5ms to save 32K of memory pub XSAVE ( src dst cnt -- ) 0 DO 2DUP SDWR DROP BLKSIZ + SWAP BLKSIZ + SWAP BLKSIZ +LOOP 2DROP ; pub XLOAD ( xsrc dst cnt -- ) 0 DO 2DUP SDRD DROP BLKSIZ + SWAP BLKSIZ + SWAP BLKSIZ +LOOP 2DROP ; \ Quick check of SD bus for any responses pub LR 10 FOR SD@ .BYTE SPACE NEXT ; END