• AVR Freaks

PIC32 USB Device - SD Card reader thoughput woes

Author
jonm
Senior Member
  • Total Posts : 119
  • Reward points : 0
  • Joined: 2010/06/23 15:51:53
  • Location: 0
  • Status: offline
2011/10/04 13:47:24 (permalink)
0

PIC32 USB Device - SD Card reader thoughput woes

http://www.microchip.com/...=572924&mpage=2#589542 This thread has touched on the issue of SD-USB read/write speeds but after having successfully hacked FatFS with multiblock support into the SD Card reader firmware, I still have troubles with transfer rates. This I suspect is due to the USB packet size, 64 bytes. And I do not know enough about the USB stack to modify without breaking the firmware. Anyone run into this issue, and/or have tips on how to squeeze more speed out of the USB? Right now I am only getting around 250kb/s read and 150kb/s write. Thanks, Jon
#1

9 Replies Related Threads

    chinzei
    Super Member
    • Total Posts : 2250
    • Reward points : 0
    • Joined: 2003/11/07 12:39:02
    • Location: Tokyo, Japan
    • Status: offline
    Re:PIC32 USB Device - SD Card reader thoughput woes 2011/10/05 09:18:15 (permalink)
    0

    PC host issues Read10 / Write10 requests of the cluster size (usually, 4KB), when large data is read/written on MSC (Mass-Storage Class) device. Sometimes, greater size of data block is exchanged.

    Microchip MSC device implementation (usb_function_msd.c) processes the request by splitting it into a number of single sector (block) process. In the single sector process for Read, the data is read out from SD to a sector buffer (msd_buffer[]), first. And then, the data is sent over the bulk IN endpoint, packet by packet (64 bytes). For Write, the process is similar to Read, but the order of SD / USB and IN/OUT direction is reversed.

    In this process, each step is sequentially aligned.
        
    --- USB READ10 -- SD single block read -- packet TX (bulk IN) x 8 -- SD single block read -- packet TX (bulk IN) x 8 ...


    The idea is that,
    Using multi-block read/write on SD, and ping-pong / endpoint interrupt on USB, SD process and USB process are overlapped.

    USB --- READ10 --------------------packet TX (bulk IN) x n -- packet TX (bulk IN) x n ... -------------------------- CSW
    SD  --------- multi-block read -- Data -- Response -- Data -- Response -- Data -- Response -- ... -- STOP -- Response


    To synchronize SD and USB process each other, they should be freely paused for a while.
    Here is the background.

    SD Specifications Part 1
    Physical Layer Simplified Specification Version 3.01
    https://www.sdcard.org/do..._3.01_Final_100518.pdf

    4.4 Clock Control
    The SD Memory Card bus clock signal can be used by the host to change the cards to energy saving mode or to control the data flow (to avoid under-run or over-run conditions) on the bus. The host is allowed to lower the clock frequency or shut it down. For example, in the case that a host with 512 Bytes of data buffer would like to transfer data to a card with 1 KByte write blocks. So, to preserve a continuous data transfer, from the card's point of view, the clock to the card shall be stopped after the first 512 Bytes. Then the host will fill its internal buffer with another 512 Bytes. After the second half of the write block is ready in the host, it will continue the data transfer to the card by re-starting the clock supply. In such a way, the card does not recognize any interruptions in the data transfer.


    On the USB side, NAK flow control on bulk IN/OUT endpoint gives enough delay for synchronization.


    I implemented this idea on an ARM chip with 700-800KB/s read and 300KB-400KB/s write on full-speed USB, ordinary 512 bytes/sector SD card. Required buffer size reduces into 256 bytes.
    I believe this idea is applied to USB PICs, too.

    Tsuneo
    post edited by chinzei - 2011/10/05 09:21:50
    #2
    jonm
    Senior Member
    • Total Posts : 119
    • Reward points : 0
    • Joined: 2010/06/23 15:51:53
    • Location: 0
    • Status: offline
    Re:PIC32 USB Device - SD Card reader thoughput woes 2011/10/05 09:44:24 (permalink)
    0

    Awesome, thanks Tsuneo!

    Sorry, I am still trying to make sense out of the communication structure... you are basically saying that running multi-sector read/writes on the SD card does not help because of the latency of the USB?  Right now I have implemented exactly what you describe in the second comm chart--


    case MSD_READ10_BLOCK:
                if(TransferLength.Val == 0)
                {
                    MSDReadState = MSD_READ10_WAIT;
                    break;
                }
       else if (TransferLength.Val >= 64){
                Nop();
                TransferLength.Val-=64;
    sectorReadCount = 64;
                }
    .
    .
    .


    so that if the USB READ10 asks for 4k as you describe, the device is going to buffer the full 4k before transferring to USB via USBTxOnePacket(), instead of looping through the number of TransferLength.Val and transferring each 512 byte sector 64 bytes at a time as the stock code had done (shown in that first comm chart).

    *edit* but I have only come part way to what you describe, because I only run multi-sector requests without pausing the CLK to send out the first set of data.  Would the latency really improve if I did this?
    */edit*

    Reading over some general USB threads, I am seeing that in the 'WinUSB - High Bandwidth' example, it looks like the firmware utilizes multiple Endpoints with Odd and Even arms to increase the throughput.

    http://www.microchip.com/forums/m435877-print.aspx

    Do you think doing this in the MSD code will increase transfer bandwidth?  Do you have any resources that I could read up on this method?  Otherwise, the implementation seems pretty straightforward.

    Jon

    post edited by jonm - 2011/10/05 10:14:43
    #3
    chinzei
    Super Member
    • Total Posts : 2250
    • Reward points : 0
    • Joined: 2003/11/07 12:39:02
    • Location: Tokyo, Japan
    • Status: offline
    Re:PIC32 USB Device - SD Card reader thoughput woes 2011/10/06 04:34:22 (permalink)
    0


    you are basically saying that running multi-sector read/writes on the SD card does not help because of the latency of the USB?
     
    No, multi-sectors read/write is useful, because we can save protocol over head of repeated single-sector read/write. 

    The idea is that, 
    Either single- or multi-sector read/write, we can pause SPI clock at any time, even at the middle of 512 bytes sector read/write. We don't need to finish one sector at a time. 
    For example, this sequence is allowed. 

    1) First 64 bytes of a sector is read from SD card. 
    2) Pause SPI clock 
    3) This 64 bytes is sent over bulk IN EP. 
    4) When USB transaction completes, restart SPI clock and another 64 bytes is read out from the SD sector. 
    5) back to 2) 

    In this sequence, the SD process and USB process still run alternately. 
    To overlap both processes, we split them, with a cyclic buffer between them. 

    SD read out  --> cyclic buffer --> USB packet send 

    SD process reads out sectors until the cyclic buffer goes full, in a unit of 64 bytes. 
    While the cyclic buffer is full, SD process pauses. 
    USB process sends 64 bytes packets from the cyclic buffer, until it goes empty. 
    While the buffer is empty, this process pauses. 

    The USB transaction is processed by the USB engine. The USB firmware code works just at the start and end of a transaction. While the USB engine is sending a transaction, the firmware can run SD process. 

    In this way, both processes are overlapped. 

    These options speed up the transfer more. 
    - ping-pong on USB EP 
    - SPI with DMA 

    Tsuneo 
    post edited by chinzei - 2011/10/06 08:20:51
    #4
    jonm
    Senior Member
    • Total Posts : 119
    • Reward points : 0
    • Joined: 2010/06/23 15:51:53
    • Location: 0
    • Status: offline
    Re:PIC32 USB Device - SD Card reader thoughput woes 2011/10/06 13:16:09 (permalink)
    0
    Okay, I have implemented pausing the SCK as you describe. Actually, just to start out I am buffering 512 bytes for each SD loop instead of 64. So the process is 

    1) Execute read multiple sector command for TransferLength.Val sectors 
    2) Read 512 bytes from SD, pause SCK 
    3) Send 64 byte bursts from USB until entire 512 byte buffer sent 
    4) Repeat 2-4 until TransferLength.Val sectors transmitted 

    So there is a bit of extra overhead on the SD side, but I thought I would at least get some speed boost by doing that (as opposed to buffering the entire TransferLength.Val*512 bytes in one blocking function, as I was doing before). However, the speeds are pretty much the same at 250kb/s.

     I will continue tweaking the code to make sure there isn't any extra overhead I may be missing.  I will also try ping-ponging multiple USB Endpoints to see if that helps at all. Thanks again. 


     Jon


    post edited by jonm - 2011/10/06 13:17:40
    #5
    bnell
    Starting Member
    • Total Posts : 74
    • Reward points : 0
    • Status: offline
    Re:PIC32 USB Device - SD Card reader thoughput woes 2011/10/06 13:31:33 (permalink)
    0
    Dumb question: How do you pause the SCK?

    Thanks in advance-
    #6
    erupter
    Senior Member
    • Total Posts : 179
    • Reward points : 0
    • Joined: 2011/05/31 15:15:52
    • Location: Rome, Italy
    • Status: offline
    Re:PIC32 USB Device - SD Card reader thoughput woes 2011/10/06 15:29:54 (permalink)
    +1 (1)
    I wouldn't mind if anyone shared some code ;)
    #7
    chinzei
    Super Member
    • Total Posts : 2250
    • Reward points : 0
    • Joined: 2003/11/07 12:39:02
    • Location: Tokyo, Japan
    • Status: offline
    Re:PIC32 USB Device - SD Card reader thoughput woes 2011/10/07 09:27:06 (permalink)
    0

    bnell:

    How do you pause the SCK?

    On SPI master mode (PIC18), SPI peripheral starts SCK clock when firmware writes to SSPBUF. SCK clock stops when the 8th bit (LSB) shifts out (MOSI) / shifts in (MISO). SCK doesn't starts until firmware writes new value to SSPBUF.

    SPI on PIC24/PIC32 has FIFO. It keeps SCK clock until the last value on FIFO shifts out.

    Tsuneo
    #8
    chinzei
    Super Member
    • Total Posts : 2250
    • Reward points : 0
    • Joined: 2003/11/07 12:39:02
    • Location: Tokyo, Japan
    • Status: offline
    Re:PIC32 USB Device - SD Card reader thoughput woes 2011/10/08 11:08:46 (permalink)
    0
    Here is ping-pong modification for MSC device (on v2011-07-14)
    This mod speeds up data transport phase of READ10, WRITE10, by ping-pong.
    In this mod, above SD access mod is not applied yet. Only the USB side is touched.
    Attached modified file to this post (usb_function_msd_PPmod.c)
     
    For PIC18 and PIC24F, make sure FULL_PING_PONG is enabled (PIC32 has just FULL_PING_PONG mode)
     
    usb_config.h           //#define USB_PING_PONG_MODE USB_PING_PONG__NO_PING_PONG
    #define USB_PING_PONG_MODE USB_PING_PONG__FULL_PING_PONG       // <----------
    //#define USB_PING_PONG_MODE USB_PING_PONG__EP0_OUT_ONLY
    //#define USB_PING_PONG_MODE USB_PING_PONG__ALL_BUT_EP0

     
    MSDReadHandler() and MSDWriteHandler() on usb_function_msd.c is the target of this mod.
    Copy usb_function_msd.c (under C:\Microchip Solutions v2011-07-14\Microchip\USB\MSD Device Driver\) to your project folder, and modify it as follows. On the project pane of MPLAB IDE, the reference to this file is replaced to modified one.
     
    usb_function_msd.c           #include "../USB/usb_device_local.h"           #define USBAdvancePingPongBuffer(buffer) ((BYTE_VAL*)buffer)->Val ^= USB_NEXT_PING_PONG     //<---------           USB_HANDLE USBMSDNextOutHandle;                                 //<---------
    USB_HANDLE USBMSDLastOutHandle;                                 //<---------
    USB_HANDLE USBMSDNextInHandle;                                  //<---------   BYTE MSDReadHandler(void)
    {
        switch(MSDReadState)
        {
            ...
            case MSD_READ10_SECTOR:
            ...
                ptrNextData=(BYTE *)&msd_buffer[0];             USBMSDNextInHandle = USBMSDInHandle;        //<-----------
               
                MSDReadState = MSD_READ10_TX_SECTOR;
               
            case MSD_READ10_TX_PACKET:
                /* Write next chunk of data to EP Buffer and send */
               
                //Make sure the endpoint is available before using it.
    //            if(USBHandleBusy(USBMSDInHandle))
                if(USBHandleBusy(USBMSDNextInHandle))       //<-----------
                {
                    break;
                }
                //Prepare the USB module to send an IN transaction worth of data to the host.
    //            USBMSDInHandle = USBTxOnePacket(MSD_DATA_IN_EP,ptrNextData,MSD_IN_EP_SIZE);
                USBMSDNextInHandle = USBTxOnePacket(MSD_DATA_IN_EP,ptrNextData,MSD_IN_EP_SIZE); //<-----------
                USBAdvancePingPongBuffer(&USBMSDNextInHandle);                                  //<-----------             MSDReadState = MSD_READ10_TX_SECTOR;
            ...     }//switch(MSDReadState)
       
        return MSDReadState;
    } BYTE MSDWriteHandler(void)
    {
        switch(MSDWriteState)
        {
            ...
            case MSD_WRITE10_BLOCK:
            ...
                msd_csw.dCSWDataResidue=BLOCKLEN_512;
                USBMSDNextOutHandle = USBMSDOutHandle;              // <-------------
               
                //Fall through to MSD_WRITE10_RX_SECTOR
            case MSD_WRITE10_RX_SECTOR:
            {
                /* Read 512B into msd_buffer*/
                if(msd_csw.dCSWDataResidue>0)
                {
    //                if(USBHandleBusy(USBMSDOutHandle) == TRUE)    // <-------------
                    if(USBHandleBusy(USBMSDNextOutHandle) == TRUE)  // <-------------
                    {
                        break;
                    } //                USBMSDOutHandle = USBRxOnePacket(MSD_DATA_OUT_EP,ptrNextData,MSD_OUT_EP_SIZE);    // <-------------
                    USBMSDLastOutHandle = USBRxOnePacket(MSD_DATA_OUT_EP,ptrNextData,MSD_OUT_EP_SIZE);  // <-------------
                    USBMSDNextOutHandle = USBMSDLastOutHandle;                                          // <-------------
                    USBAdvancePingPongBuffer( USBMSDNextOutHandle );                                    // <-------------                 MSDWriteState = MSD_WRITE10_RX_PACKET;
                    //Fall through to MSD_WRITE10_RX_PACKET
                }
                else
                {
                    if(USBHandleBusy(USBMSDLastOutHandle) == TRUE)                  // <-------------
                    {                                                               // <-------------
                        break;                                                      // <-------------
                    }                                                               // <-------------                 //We finished receiving a sector worth of data from the host.
                    //Check if the media is write protected before deciding what
                    //to do with the data.
                    if(LUNWriteProtectState())
                    {
                        ...
                    }
                    MSDWriteState = MSD_WRITE10_SECTOR;    
                    break;
                }
            }
            //Fall through to MSD_WRITE10_RX_PACKET
            case MSD_WRITE10_RX_PACKET:
    //            if(USBHandleBusy(USBMSDOutHandle) == TRUE)                        // <-------------
    //            {                                                                 // <-------------
    //                break;                                                        // <-------------
    //            }                                                                 // <-------------
               
    //          gblCBW.dCBWDataTransferLength-=USBHandleGetLength(USBMSDOutHandle); // <-------------
    //          msd_csw.dCSWDataResidue-=USBHandleGetLength(USBMSDOutHandle);       // <-------------
                gblCBW.dCBWDataTransferLength -= MSD_OUT_EP_SIZE;                   // <-------------
                msd_csw.dCSWDataResidue       -= MSD_OUT_EP_SIZE;                   // <-------------
                ptrNextData                   += MSD_OUT_EP_SIZE;
               
                MSDWriteState = MSD_WRITE10_RX_SECTOR;
                break;
            case MSD_WRITE10_SECTOR:
            ...
        }
       
        return MSDWriteState;
    }

     
    Tsuneo
    #9
    jonm
    Senior Member
    • Total Posts : 119
    • Reward points : 0
    • Joined: 2010/06/23 15:51:53
    • Location: 0
    • Status: offline
    Re:PIC32 USB Device - SD Card reader thoughput woes 2011/10/10 12:38:46 (permalink)
    0
    I have implemented a Ping-Pong style of writing TO_HOST similar to the code you posted, Tsuneo which actually did improve transfer rates to around 280k/s.  However, I have not been able to gather 64 bytes from the SD card at a time and successfully initialize the card (mass storage device) on windows.  I have followed this spec (3.6 Bus Protocol):

    https://www.sdcard.org/do..._3.01_Final_100518.pdf

    Where I send the multi-block read command to the SD card and call rcvr_datablock() function 8 times for each sector (8*64 bytes), but only collect the response token on the first 64 bytes and send CRC on the last 64 bytes.  This should work exactly as it was originally implemented with 512 bytes, unless the SCK is not stopping correctly, or if something is happening on the card in between 64 byte reads.  Sorry, this is more of an SD question, but I would not bother with SCK pausing if I did not think it would speed up USB transfers.  I will check again to make sure the SSPBUF is clear before looping through rcvr_datablock() each time.  Thanks again!

    Jon
    #10
    Jump to:
    © 2019 APG vNext Commercial Version 4.5