Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slow read/write speed of microSD card via SPI and FatFS #22906

Closed
EminYagmahan opened this issue Feb 18, 2020 · 17 comments
Closed

Slow read/write speed of microSD card via SPI and FatFS #22906

EminYagmahan opened this issue Feb 18, 2020 · 17 comments
Assignees
Labels
area: File System Enhancement Changes/Updates/Additions to existing features platform: STM32 ST Micro STM32

Comments

@EminYagmahan
Copy link

To determine the read and write speed on my SD card, I wrote a test function that writes 1 Megabyte of data to my SD card, then reads it and determines the required time and speed.
According to my measurement results I get the following speeds:
Read: 35 KByte/s
Write: 27 KByte/s

According to ELM CHAN's benchmarking tests, I get similar speeds with my 16GB SD card as he did with W:1 and R:1. According to the API reference from Zephyr, there is no possibility to do a Multi_Block_Write with the high level API. I guess every fs_write function calls a Single_Block_Write (CMD 24) command. How is it efficient to stream the data to the Micro SD card? An "fs_multiwrite(...)" does not exist in the API.

How can I still increase the speed?

Test environment:
Discovery Kit: Disco_L475_IOT
MicroSD Card via SPI at 20 MHz

Benchmarking Results from ELM CHAN:

ELM_CHAN_BENCH

My test function write x byte on sd card:

// Open File. If file alreaddy exists, it will be overwritten
    if (fs_open(&file, "/SD:/TEST") != 0) {
        printk("Test 1 Error: FS_OPEN failed!\n");
        return -1;
    }

    // Create test char array with custom file size
    memset(buf_test, 'X', TEST_FILE_SIZE);

    // Add 1000x 1024 Junk to end of file. Avg Speed 27 KByte/s
    for (int i = 0; i < KILO; ++i) {
        // Search for End of File
        if (fs_seek(&file, 0, FS_SEEK_END) != 0) {
            printk("Test 1 Error: FS_SEEK failed!\n");
            return -1;
        }
        if (fs_write(&file, buf_test, TEST_FILE_SIZE) != TEST_FILE_SIZE) {
            fs_close(&file);
            printk("write error\n");
            return -1;
        }
    }
    // The f_sync function flushes the cached information of a writing file.
    if (fs_sync(&file) != 0) {
        printk("sync error\n");
        return -1;
    }
    // The f_close function closes an open file.
    fs_close(&file);
@FRASTM
Copy link
Collaborator

FRASTM commented Feb 20, 2020

The fs_write function gives a nb of bytes to be written, then will the driver (SD card or SPI) convert this nb of bytes into a nb of blocks (based on a usual card block size of 512B).
At lower level, the SPI card driver will then send CMD24, looping on the expected size ( or use a CMD25.
Also erasing the card before writing could speed up the write operation.

@EminYagmahan
Copy link
Author

A closer look at the low-level write functions shows that the SDHC_WRITE_BLOCK (CMD24 for Single Block) is constantly used, even with large amounts of data.

...
err = sdhc_spi_cmd_r1(data, SDHC_WRITE_BLOCK, addr);
if (err < 0) {
goto error;
}
...

disk_access_spi_sdhc.c

Instead of sending CMD24 and looping on the expected size wouldn't it be better to write a handler, which manages the amount of data to be transferred and transmits it as CMD25.
Unfortunately, transferring "SDHC_WRITE_MULTIPLE_BLOCK" (CMD25) instead of CMD24 does not work right away. For this I have not looked at the deeper layers of transfer and responses in sufficient detail.

CMD25_vsCMD_24

@FRASTM
Copy link
Collaborator

FRASTM commented Feb 20, 2020

right, this is explicitly mentioned in the sdhc_spi_write function: /* Write the blocks one-by-one */
The multiple block read/write operations require start and stop token to be inserted and monitoring the 'busy' status of the card.
This is clearly a new feature to support, with probable better performances.

image

@erwango erwango assigned FRASTM and unassigned erwango Feb 24, 2020
@FRASTM FRASTM added the Waiting for response Waiting for author's response label May 13, 2020
@carlescufi carlescufi added Enhancement Changes/Updates/Additions to existing features and removed Waiting for response Waiting for author's response labels May 27, 2020
@carlescufi
Copy link
Member

I have converted this to an Enhancement, let us know if you plan to work on this @FRASTM

@vaussard
Copy link
Contributor

Hello. I did a pull request to improve the read/write speed on nrf52: #26319. I believe that the same issue affects the other platforms. Would you be able to test the changes and report your results in the PR? Thanks

@FRASTM
Copy link
Collaborator

FRASTM commented Jun 25, 2020

  1. step : the function sdhc_spi_tx_blocks() to write a number of blocks on a card. It loops the existing sdhc_spi_tx_block() function aligned on SD card block unit. The CMD16 is send first to set the right block size, then a loop is sending CMD24.

  2. step : use the actual multiple block for WRITE operation with CMD25 and a stop token : PR Improve write speed of microSD card via SPI and FatFS #26757

  3. use the actual multiple block READ operation with CMD18 and a stop token: already done in the sdhc_spi_read function.

@FRASTM
Copy link
Collaborator

FRASTM commented Jul 7, 2020

@EminYagmahan can you please share your full test application which measures the SD write speed. So that I can check if any improvement is possible.

@EminYagmahan
Copy link
Author

@FRASTM Thank you. I have just uploaded all the necessary test functions. They are located in the following repository: zephyr_spi_sdcard. It should be mentioned that the calculation of the time in milliseconds does not fully reflect the write/read time. The time for required fat fs operations is also included in the output.

@FRASTM
Copy link
Collaborator

FRASTM commented Jul 8, 2020

Testing the write speed on a SD card on nucleo F411 + SD card on SPI (through cishield)
Performance Result - write time for 1024000 byte: 32225 ms

With PR #26319 : improving the SPI clock,
Performance Result - write time for 1024000 byte: 7537 ms

@FRASTM
Copy link
Collaborator

FRASTM commented Jul 9, 2020

Testing the write speed on a SD card on nucleo F411 + SD card on SPI (through cishield)
With SPI clock improvement and PR #26757 :
Performance Result - write time for 1024000 byte: 5511 ms
is below 7000ms

@FRASTM
Copy link
Collaborator

FRASTM commented Jul 15, 2020

@EminYagmahan can you please check if PR #26757 improves the results of write speed

@EminYagmahan
Copy link
Author

EminYagmahan commented Jul 15, 2020

@FRASTM Many thanks for the implementation. Enclosed are the measurement results with PR #26757 . Each measurement series was repeated three times and the average was recorded. One MB is written and afterwards read. Since the Multiwrite command is only executed at >2 blocks, there are no differences in writing block sizes 512 Byte and 1024 Byte. The implementation has definitely helped. Since the write rate was almost doubled at quadruple block size.

(For completeness I have added the reading rate anyway. Although there is no implementation for the multiread yet, there are improvements. I explain this by the fact that the Transmit function of SPI must be called less often by larger buffers to transfer the same amount of data.)

Test setup:

  • Device: STM32 B-L475-IOT
  • SPI: 20 MHz
  • SD Card: 16GB microSD Samsung Class 6
  • Total File Size: 1 MByte Read and Write

Results:

Block Size Read Write
512 Byte 214.58 KByte/s 66.98 KByte/s
1024 Byte 217.31 KByte/s 66.66 KByte/s
2048 Byte 244.85 KByte/s 93.84 KByte/s
4096 Byte 262.55 KByte/s 117.83 KByte/s

@lowlander
Copy link
Collaborator

Could the slow performance be due to the SPI bus speed being low? See bug reports #28092 and #28093

@FRASTM
Copy link
Collaborator

FRASTM commented Sep 7, 2020

@EminYagmahan "Although there is no implementation for the multiread yet"

The sdhc_spi_read() in the ./subsys/disk/disk_access_spi_sdhc.c is actually starting read command with the SDHC_READ_MULTIPLE_BLOCK. So reading operation is already a multiread.

@EminYagmahan
Copy link
Author

@lowlander Correct, but this problem has already been addressed and solved in PR #26319. With this PR the maximum clock frequency is now taken from the definition in the Device Tree Layer spi-max-frequency. In combination with the PR #26757 the performance problems are no longer present. I have verified the behavior with a logic analyzer and can confirm the correct execution of the driver.

@nashif nashif removed the question label Sep 11, 2020
@nashif
Copy link
Member

nashif commented Sep 11, 2020

Can this be considered resolved and can it be closed?

@EminYagmahan
Copy link
Author

Since PR #26319 had a big impact on performance and that is still open, my thought was to do a final test and close the issue after successful merge of the PR. But this can be closed also now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: File System Enhancement Changes/Updates/Additions to existing features platform: STM32 ST Micro STM32
Projects
None yet
Development

No branches or pull requests

7 participants