Cluso's SD Card Cog Driver for P2

Release to come shortly after I test it out.
Note the attached file is a placeholder. I hope that the forum software will then let me add files.

Here is a brief description (from the code)...
'' +-----------------------------------------------------------------------------------------------+
'' + * This driver is designed to run in its' own cog.                                             +
'' + * Commands and Parameters are passed from other Cog(s) via the HUB Parameter Block (mailbox)  +
'' + * The hub address of the HUB Parameter Block must be passed to this driver in PTRA on         +
'' +   startup. This is achieved by performing a SETQ ptra_val immediatly before the COGINIT that  +
'' +   loads and starts this cog.                                                                  +
'' + * The driver is available for use if the command byte of the mailbox is read as zero ($0)     +
'' + * It is essential that the Command byte is set last, after the hub buffer pointer and the     +
'' +   sector# have been written.                                                                  +
'' + * The result  (status) of the Command will be returned via the mailbox and the command will   +
'' +   be cleared to zero ($0).                                                                    +
'' + * Read and Write Block Commands (ReadSector, WriteSector, ReadCSDCID) will read/write the     +
'' +   data to/from HUB as pointed to in the mailbox ptrbuffer long at the sector# in the mailbox  +
'' +   sector long. Note sector will be ignored for ReadCSDCID.                                    +
'' + * A sector is always 512 bytes. The CSDCID command will return 32 bytes.                      +
'' +-----------------------------------------------------------------------------------------------+
'' + * It will be presumed that an SD Card presides on the designated pins.                        +
'' + * No checks other than a quick read of the /CS pin to ensure it is HI.                        +
'' +-----------------------------------------------------------------------------------------------+
'' +   PTRA = pointer to HUB Parameter Block (mailbox) - passed by SETQ before COGINIT             +
'' +   PTRB = pointer to Hub Buffer (for readCSDCID, readSector, writeSector data)                 +
'' +-----------------------------------------------------------------------------------------------+
  SD_Idle       = "0"           ' no command
  SD_Init       = "M"           ' initialise the SD card (mount)
  SD_CSDCID     = "C"           ' read CSD & CID
  SD_Read       = "R"           ' read a sector
  SD_Write      = "W"           ' write a sector
  SD_Stop       = "S"           ' stop card
' the following 4 longs are read from the mailbox atomically using SETQ
  command       long    0       '\ command/status/xxx/xxx
  reserv        long    0       '| reserved (later SD Pin definition)
  ptrbuffer     long    0       '| hub address of data buffer
  blocknr       long    0       '/ sector# to read/write
'       |      +3       |      +2       |      +1       |      +0       |      
'       +---------------------------------------------------------------+
'  +$0C | <--------------------- hubDataAddress ----------------------> | Parameter3
'       +---------------------------------------------------------------+
'  +$08 | <--------------------- hubDataAddress ----------------------> | Parameter2
'       +---------------------------------------------------------------+
'  +$04 | SDpin_CS      | SDpin_CLK     | SDpin_DI      | SDpin_DO      | Parameter1
'       +---------------------------------------------------------------+
'  +$00 | LOCKID+1      | COGID+1       | Status (from) | Command (to)  | Parameter0
'       +---------------------------------------------------------------+


  • roglohrogloh Posts: 2,361
    edited 2019-09-20 - 14:40:15
    Seems like this would be handy especially if it is shareable amongst multiple COGs with the lock. Only question with this design is how to request a transfer of more than one sector at a time if you want higher throughput?
  • Cluso99Cluso99 Posts: 16,270
    edited 2019-09-20 - 15:01:45
    I wasn’t going for multiple block reads or writes, at least for now. There is a lot of time wasted by the cards so I’m not sure how much of a boost you’d get.

    I’ve been thinking of locks for multiple cogs accessing the driver. Depends on how the mailbox works out. Currently I do a setq #4 rdlong to read the whole mailbox in 4 clocks after setup time. With this method locks should be unnecessary. Just write the 4 and read them back to ensure you were the one who got the job. Otherwise wait until the mailbox is free again. Tho thinking, locks would chew less power, and use cogatn so the sd cog could sleep.

    BTW I don’t think locks on sad sectors or files will happen. I know we used them years ago on minis that were less powerful than this chip.

    And I’ve been trying to convert Kyes SD file access from spin to PASM using Eric’s spin2cpp. It’s looking interesting.
  • roglohrogloh Posts: 2,361
    edited 2019-09-20 - 23:16:25
    Fair enough, just start out with single block and work it from there. I think sometime later you might be able to add in a single block count byte into the hub address long since these hub addresses are only 19 or 20 bits. That could work if there are any transfer gains to be had down the track.

    For simple read only filesystem cases, there may be techniques where you can get away with no mailbox locks as you pointed out. Once there are multiple writers we'll probably want to use a locking mechanism when we write to the FAT or directories, or when anything needs to do a read/modify/write cycle within a single block for example and that same block can potentially be accessed by another COG in the meantime.

    This looks good. Seems to me we are going to need an shareable object format very soon! Relocatable PASM code is one way I guess.
  • Peter JakackiPeter Jakacki Posts: 9,225
    edited 2019-09-21 - 04:58:53
    I read in a 300kB bitmap file in using block read in about 100ms. It would take too long sector by sector, maybe even a second, and all you need is a starting sector, a destination, and a byte count.

    Here's my multi-block routine for reference:

    --- read multiple sectors in continuous multiblock mode
    pub SDRDS ( sector dst bytes -- crc | false )
    ---	convert bytes to sectors '
    ---	multiblock read
    	-ROT SWAP 18 CMD
    	IF --- command not accepted 
    	  2DROP FALSE
    	ELSE --- process read token and read block if available '
    	  IF --- data available, read in a block
    	  ELSE --- no more data available, terminate
    ---	update the read index	
    	DUP @sdrd !
    ---	cancel multiblock read on error
    	  2000 BEGIN 1- 0 12 CMD 0= OVER 0= OR UNTIL DROP

    A quick example of how to use it and how long it takes:
    TAQOZ# 0 SCR 300,000 LAP SDRDS LAP .LAP --- 29,262,105 cycles= 97,540,350ns @300MHz ok

    EDIT: just to convince you that block mode is worthwhile here are random sector read times including a sequential read of the next block.
    TAQOZ# $1000 LAP SECTOR LAP .LAP --- 750,233 cycles= 2,500,776ns @300MHz ok
    TAQOZ# $8000 LAP SECTOR LAP .LAP --- 752,345 cycles= 2,507,816ns @300MHz ok
    TAQOZ# $8001 LAP SECTOR LAP .LAP --- 714,329 cycles= 2,381,096ns @300MHz ok
    So a 300k file would take 1.5 seconds to read, about 15 times longer than the multi-block mode.
Sign In or Register to comment.