software/liblitesdcard/spisdcard: simplify/rewrite for consistency with the others parts of the project.

- Improve code readability, remove un-needed or duplicate comments.
- Only use a spi_xfer function for both write/read.
- Set the SDCard to low clk freq before init and increase it when initialized.
This commit is contained in:
Florent Kermarrec 2020-06-05 09:07:19 +02:00
parent 860ac1e212
commit 7d141258be
7 changed files with 267 additions and 321 deletions

View File

@ -538,37 +538,43 @@ void romboot(void)
void sdcardboot(void) void sdcardboot(void)
{ {
printf("Booting from SDCard...\n");
#ifdef CSR_SPISDCARD_BASE
printf("Initializing SDCard in SPI-Mode...\n");
if(spi_sdcard_goidle() == 0) {
printf("SD Card Timeout\n");
return;
}
#endif
#ifdef CSR_SDCORE_BASE
printf("Initializing SDCard in SD-Mode...\n");
sdcard_init(); // FIXME : check returned value
#endif
if(sdcard_readMBR() == 0) {
printf("SD Card MBR Timeout\n");
return;
}
unsigned int result; unsigned int result;
printf("Booting from SDCard...\n");
/* Initialize SDCard */
#ifdef CSR_SPISDCARD_BASE
printf("Initializing SDCard in SPI-Mode...\n");
result = spisdcard_init();
#endif
#ifdef CSR_SDCORE_BASE
printf("Initializing SDCard in SD-Mode...\n");
result = sdcard_init();
#endif
if (result == 0) {
printf("SDCard initialization failed.\n");
return;
}
/* Read MBR */
result = fat16_read_mbr();
if (result == 0) {
printf("SDCard MBR read failed.\n");
return;
}
/* Copy files to RAM */
#if defined(CONFIG_CPU_TYPE_VEXRISCV) && defined(CONFIG_CPU_VARIANT_LINUX) #if defined(CONFIG_CPU_TYPE_VEXRISCV) && defined(CONFIG_CPU_VARIANT_LINUX)
result = sdcard_readFile("IMAGE", "", MAIN_RAM_BASE + KERNEL_IMAGE_RAM_OFFSET); result = fat16_read_file("IMAGE", "", MAIN_RAM_BASE + KERNEL_IMAGE_RAM_OFFSET);
if(result) if(result)
result &= sdcard_readFile("ROOTFS~1", "CPI", MAIN_RAM_BASE + ROOTFS_IMAGE_RAM_OFFSET); result &= fat16_read_file("ROOTFS~1", "CPI", MAIN_RAM_BASE + ROOTFS_IMAGE_RAM_OFFSET);
if(result) if(result)
result &= sdcard_readFile("RV32", "DTB", MAIN_RAM_BASE + DEVICE_TREE_IMAGE_RAM_OFFSET); result &= fat16_read_file("RV32", "DTB", MAIN_RAM_BASE + DEVICE_TREE_IMAGE_RAM_OFFSET);
if(result) if(result)
result &= sdcard_readFile("EMULATOR", "BIN", MAIN_RAM_BASE + EMULATOR_IMAGE_RAM_OFFSET); result &= fat16_read_file("EMULATOR", "BIN", MAIN_RAM_BASE + EMULATOR_IMAGE_RAM_OFFSET);
if(result) { if(result) {
boot(0, 0, 0, MAIN_RAM_BASE + EMULATOR_IMAGE_RAM_OFFSET); boot(0, 0, 0, MAIN_RAM_BASE + EMULATOR_IMAGE_RAM_OFFSET);
@ -576,10 +582,10 @@ void sdcardboot(void)
} }
#endif #endif
result = sdcard_readFile("BOOT", "BIN", MAIN_RAM_BASE); result = fat16_read_file("BOOT", "BIN", MAIN_RAM_BASE);
if(result) if(result)
boot(0, 0, 0, MAIN_RAM_BASE); boot(0, 0, 0, MAIN_RAM_BASE);
else else
printf("SD Card SPI boot failed\n"); printf("SDCard boot failed\n");
} }
#endif #endif

View File

@ -24,6 +24,14 @@
#if defined(CSR_SPISDCARD_BASE) || defined(CSR_SDCORE_BASE) #if defined(CSR_SPISDCARD_BASE) || defined(CSR_SDCORE_BASE)
#if defined(CSR_SPISDCARD_BASE)
#define read_block spisdcard_read_block
#endif
#if defined(CSR_SDCORE_BASE)
#define read_block sdcard_read_block
#endif
// FAT16 Specific code starts here // FAT16 Specific code starts here
// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ // Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
@ -102,13 +110,13 @@ uint8_t sdCardSector[512];
// Return 0 success, 1 failure // Return 0 success, 1 failure
// //
// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ // Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
uint8_t sdcard_readMBR(void) uint8_t fat16_read_mbr(void)
{ {
int i, n; int i, n;
// Read Sector 0x00000000 // Read Sector 0x00000000
printf("Reading MBR\n"); printf("Reading MBR\n");
if( readSector(0x00000000, sdCardSector)==SUCCESS ) { if( read_block(0x00000000, sdCardSector)==SUCCESS ) {
// Copy Partition 1 Entry from byte 0x1be // Copy Partition 1 Entry from byte 0x1be
// FIXME should check 0x55 0xaa at end of sector // FIXME should check 0x55 0xaa at end of sector
memcpy(&sdCardPartition, &sdCardSector[0x1be], sizeof(PartitionTable)); memcpy(&sdCardPartition, &sdCardSector[0x1be], sizeof(PartitionTable));
@ -135,7 +143,7 @@ uint8_t sdcard_readMBR(void)
// Read Parition 1 Boot Sector - Found from Partion Table // Read Parition 1 Boot Sector - Found from Partion Table
printf("\nRead FAT16 Boot Sector\n"); printf("\nRead FAT16 Boot Sector\n");
if( readSector(sdCardPartition.start_sector, sdCardSector)==SUCCESS ) { if( read_block(sdCardPartition.start_sector, sdCardSector)==SUCCESS ) {
memcpy(&sdCardFatBootSector, &sdCardSector, sizeof(Fat16BootSector)); memcpy(&sdCardFatBootSector, &sdCardSector, sizeof(Fat16BootSector));
} }
else { else {
@ -181,19 +189,6 @@ uint8_t sdcard_readMBR(void)
return FAILURE; return FAILURE;
} }
#ifdef USE_SPISCARD_RECLOCKING
// Reclock the card
// Calculate 16MHz as an integer divider from the CONFIG_CLOCK_FREQUENCY
// Add 1 as will be rounded down
// Always ensure divider is at least 2 - half the processor speed
int divider;
divider = (int)(CONFIG_CLOCK_FREQUENCY/(16e6)) + 1;
if( divider<2 )
divider=2;
printf("Reclocking from %dKHz to %dKHz\n\n", CONFIG_CLOCK_FREQUENCY/(int)spisdcard_clk_divider_read()/1000, CONFIG_CLOCK_FREQUENCY/divider/1000);
spisdcard_clk_divider_write(divider);
#endif
// Read in FAT16 File Allocation Table, array of 16bit unsinged integers // Read in FAT16 File Allocation Table, array of 16bit unsinged integers
// Calculate Storage from TOP of MAIN RAM // Calculate Storage from TOP of MAIN RAM
sdCardFatTable = (uint16_t *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors); sdCardFatTable = (uint16_t *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors);
@ -202,7 +197,7 @@ uint8_t sdcard_readMBR(void)
// Calculate Start of FAT16 File Allocation Table (start of partition plus reserved sectors) // Calculate Start of FAT16 File Allocation Table (start of partition plus reserved sectors)
fatSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors; fatSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors;
for(n=0; n<sdCardFatBootSector.fat_size_sectors; n++) { for(n=0; n<sdCardFatBootSector.fat_size_sectors; n++) {
if( readSector(fatSectorStart+n, (uint8_t *)((uint8_t*)sdCardFatTable)+sdCardFatBootSector.sector_size*n)==FAILURE ) { if( read_block(fatSectorStart+n, (uint8_t *)((uint8_t*)sdCardFatTable)+sdCardFatBootSector.sector_size*n)==FAILURE ) {
printf("Error reading FAT16 table - sector %d\n",n); printf("Error reading FAT16 table - sector %d\n",n);
return FAILURE; return FAILURE;
} }
@ -216,7 +211,7 @@ uint8_t sdcard_readMBR(void)
// Calculate Start of FAT ROOT DIRECTORY (start of partition plues reserved sectors plus size of File Allocation Table(s)) // Calculate Start of FAT ROOT DIRECTORY (start of partition plues reserved sectors plus size of File Allocation Table(s))
rootDirSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors+sdCardFatBootSector.number_of_fats*sdCardFatBootSector.fat_size_sectors; rootDirSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors+sdCardFatBootSector.number_of_fats*sdCardFatBootSector.fat_size_sectors;
for(n=0; n<sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry)/sdCardFatBootSector.sector_size; n++) { for(n=0; n<sdCardFatBootSector.root_dir_entries*sizeof(Fat16Entry)/sdCardFatBootSector.sector_size; n++) {
if( readSector(rootDirSectorStart+n, (uint8_t *)(sdCardFatBootSector.sector_size*n+(uint8_t *)(sdCardFat16RootDir)))==FAILURE ) { if( read_block(rootDirSectorStart+n, (uint8_t *)(sdCardFatBootSector.sector_size*n+(uint8_t *)(sdCardFat16RootDir)))==FAILURE ) {
printf("Error reading Root Dir - sector %d\n",n); printf("Error reading Root Dir - sector %d\n",n);
return FAILURE; return FAILURE;
} }
@ -258,7 +253,7 @@ uint8_t sdcard_readMBR(void)
// Return 0 success, 1 failure // Return 0 success, 1 failure
// //
// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ // Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/
uint8_t sdcard_readFile(char *filename, char *ext, unsigned long address) uint8_t fat16_read_file(char *filename, char *ext, unsigned long address)
{ {
int i, n, sector; int i, n, sector;
uint16_t fileClusterStart; uint16_t fileClusterStart;
@ -311,14 +306,14 @@ uint8_t sdcard_readFile(char *filename, char *ext, unsigned long address)
// If whole sector to be read, read directly into memory // If whole sector to be read, read directly into memory
// Otherwise, read to sdCardSector buffer and transfer appropriate number of bytes // Otherwise, read to sdCardSector buffer and transfer appropriate number of bytes
if(bytesRemaining>sdCardFatBootSector.sector_size) { if(bytesRemaining>sdCardFatBootSector.sector_size) {
if( readSector(clusterSectorStart+sector,(uint8_t *)address) == FAILURE ) { if( read_block(clusterSectorStart+sector,(uint8_t *)address) == FAILURE ) {
printf("\nRead Error\n"); printf("\nRead Error\n");
return FAILURE; return FAILURE;
} }
bytesRemaining=bytesRemaining-sdCardFatBootSector.sector_size; bytesRemaining=bytesRemaining-sdCardFatBootSector.sector_size;
address=address+sdCardFatBootSector.sector_size; address=address+sdCardFatBootSector.sector_size;
} else { } else {
if( readSector(clusterSectorStart+sector,sdCardSector) == FAILURE ) { if( read_block(clusterSectorStart+sector,sdCardSector) == FAILURE ) {
printf("\nRead Error\n"); printf("\nRead Error\n");
return FAILURE; return FAILURE;
} }

View File

@ -4,7 +4,7 @@
#ifndef __FAT16_H #ifndef __FAT16_H
#define __FAT16_H #define __FAT16_H
uint8_t sdcard_readMBR(void); uint8_t fat16_read_mbr(void);
uint8_t sdcard_readFile(char *, char *, unsigned long); uint8_t fat16_read_file(char *, char *, unsigned long);
#endif /* __FAT16_H */ #endif /* __FAT16_H */

View File

@ -622,7 +622,7 @@ int sdcard_init(void) {
/* set block length */ /* set block length */
sdcard_app_set_blocklen(SD_BLOCK_SIZE); sdcard_app_set_blocklen(SD_BLOCK_SIZE);
return 0; return 1;
} }
extern void dump_bytes(unsigned int *ptr, int count, unsigned long addr); extern void dump_bytes(unsigned int *ptr, int count, unsigned long addr);
@ -713,15 +713,14 @@ int sdcard_test(unsigned int blocks)
return 0; return 0;
} }
uint8_t readSector(uint32_t sectorNumber, uint8_t *storage) uint8_t sdcard_read_block(uint32_t addr, uint8_t *buf) {
{
int n; int n;
// FIXME: handle errors, avoid recopy. // FIXME: handle errors, avoid recopy.
sdcard_read(sectorNumber, 1); sdcard_read(addr, 1);
for(n=0; n<SD_BLOCK_SIZE; n++) for(n=0; n<SD_BLOCK_SIZE; n++)
storage[n] = sdread_buf[n]; buf[n] = sdread_buf[n];
return 1; return 1;
} }

View File

@ -108,7 +108,7 @@ int sdcard_sddatawriter_wait(void);
int sdcard_sddatareader_wait(void); int sdcard_sddatareader_wait(void);
int sdcard_test(unsigned int blocks); int sdcard_test(unsigned int blocks);
uint8_t readSector(uint32_t sectorNumber, uint8_t *storage); uint8_t sdcard_read_block(uint32_t addr, uint8_t *buf);
#endif /* CSR_SDCORE_BASE */ #endif /* CSR_SDCORE_BASE */

View File

@ -1,294 +1,245 @@
// This file is Copyright (c) 2020 Rob Shelton <rob.s.ng15@googlemail.com> // This file is Copyright (c) 2020 Rob Shelton <rob.s.ng15@googlemail.com>
// This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
// License: BSD // License: BSD
// //
// SD CARD bitbanging code for loading files from a FAT16 forrmatted partition into memory // SDCard SPI-Mode support for LiteX's SPIMaster.
//
// Code is known to work on a de10nano with MiSTer SDRAM and IO Boards - IO Board has a secondary SD CARD interface connected to GPIO pins
// SPI signals CLK, CS and MOSI are configured as GPIO output pins, and MISO is configued as GPIO input pins
//
// Protocol details developed from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/
//
// FAT16 details developed from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ and https://codeandlife.com/2012/04/07/simple-fat-and-sd-tutorial-part-2/
// Import LiteX SoC details that are generated each time the SoC is compiled for the FPGA
// csr defines the SPI Control registers
// soc defines the clock CONFIG_CLOCK_FREQUENCY (50MHz for the VexRiscV processor on the MiSTer FPGA
// mem defines the addresses for the SDRAM MAIN_RAM_BASE and MAIN_RAM_SIZE
#include <generated/csr.h>
#include <generated/soc.h>
#include <generated/mem.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h>
#include <string.h> #include <string.h>
#include <generated/csr.h>
#include <generated/mem.h>
#include <system.h> #include <system.h>
#ifdef CSR_SPISDCARD_BASE
// Import prototypes for the functions
#include "spisdcard.h" #include "spisdcard.h"
// SPI #ifdef CSR_SPISDCARD_BASE
// cs line - high to indicate DESELECT
// - low to indicate SELECT
#define CS_HIGH 0x00
#define CS_LOW 0x01
// control register values /* SPI Master flags */
// onebyte to indicate 1 byte being transferred
// spi_start to indicate START of transfer
// spi_done to indicate transfer DONE
#define ONEBYTE 0x0800
#define SPI_START 0x01
#define SPI_DONE 0x01
// Return values #define SPI_CS_HIGH (0 << 0)
#define SUCCESS 0x01 #define SPI_CS_LOW (1 << 0)
#define FAILURE 0x00 #define SPI_START (1 << 0)
#define SPI_DONE (1 << 0)
#define SPI_LENGTH (1 << 8)
// spi_write_byte /* SPI Master low-level functions */
// Send a BYTE (8bits) to the SD CARD
// Seqeunce
// Set MOSI
// Set START bit and LENGTH=8
// Await DONE
//
// No return values
//
// Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "SD Commands"
void spi_write_byte(uint8_t char_to_send);
void spi_write_byte(uint8_t char_to_send)
{
// Place data into MOSI register
// Pulse the START bit and set LENGTH=8
spisdcard_mosi_write(char_to_send);
spisdcard_control_write(ONEBYTE | SPI_START);
// Wait for DONE static void spi_set_clk_freq(uint32_t clk_freq) {
while( (spisdcard_status_read() != SPI_DONE)) {} uint32_t divider;
divider = CONFIG_CLOCK_FREQUENCY/clk_freq + 1;
// Signal end of transfer printf("divider: %d\n", divider);
spisdcard_control_write( 0x00 ); if (divider >= 65535) /* 16-bit hardware divider */
divider = 65535;
if (divider <= 2) /* At least half CPU speed */
divider = 2;
printf("Setting SDCard clk freq to ");
if (clk_freq > 1000000)
printf("%d MHz\n", (CONFIG_CLOCK_FREQUENCY/divider)/1000000);
else
printf("%d KHz\n", (CONFIG_CLOCK_FREQUENCY/divider)/1000);
spisdcard_clk_divider_write(divider);
} }
static uint8_t spi_xfer(uint8_t byte) {
// spi_read_rbyte /* Write byte on MOSI */
// Read a command response from the SD CARD - Equivalent to and R1 response or first byte of an R7 response spisdcard_mosi_write(byte);
// Sequence /* Initiate SPI Xfer */
// Read MISO spisdcard_control_write(8*SPI_LENGTH | SPI_START);
// If MSB != 0 then send dsummy byte and re-read MISO /* Wait SPI Xfer to be done */
// while(spisdcard_status_read() != SPI_DONE);
// Return value is the response from the SD CARD /* Read MISO and return it */
// If the MSB is not 0, this would represent an ERROR return spisdcard_miso_read();
// Calling function to determine if the correct response has been received
//
// Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "SD Commands"
uint8_t spi_read_rbyte(void);
uint8_t spi_read_rbyte(void)
{
int timeout=32;
uint8_t r=0;
// Check if MISO is 0x0xxxxxxx as MSB=0 indicates valid response
r = spisdcard_miso_read();
while( ((r&0x80)!=0) && timeout>0) {
spisdcard_mosi_write( 0xff );
spisdcard_control_write(ONEBYTE | SPI_START);
while( (spisdcard_status_read() != SPI_DONE)) {}
r = spisdcard_miso_read();
spisdcard_control_write( 0x00 );
timeout--;
}
// printf("Done\n");
return r;
} }
// spi_read_byte /* SPI SDCard functions */
// Sequence
// Send dummy byte
// Read MISO
//
// Read subsequenct bytes from the SD CARD - MSB first
// NOTE different from the spi_read_rbyte as no need to await an intial 0 bit as card is already responsing
// Used to read additional response bytes, or data bytes from the SD CARD
//
// Return value is the byte read
// NOTE no error status as assumed bytes are read via CLK pulses
//
// Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "SD Commands"
uint8_t spi_read_byte(void);
uint8_t spi_read_byte(void)
{
uint8_t r=0;
spi_write_byte( 0xff ); static uint8_t spisdcard_wait_response(void) {
r = spisdcard_miso_read(); uint8_t timeout;
uint8_t response;
return r; timeout = 32;
} /* Do SPI Xfers on SDCard until MISO MSB's is 0 (valid response) or timeout is expired */
// SETSPIMODE
// Signal the SD CARD to switch to SPI mode
// Pulse the CLK line HIGH/LOW repeatedly with MOSI and CS_N HIGH
// Drop CS_N LOW and pulse the CLK
// Check MISO for HIGH
// Return 0 success, 1 failure
//
// Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "Initializing the SD Card"
uint8_t spi_setspimode(void);
uint8_t spi_setspimode(void)
{
uint32_t r;
int i, timeout=32;
// Initialise SPI mode
// set CS to HIGH
// Send pulses
do { do {
// set CS HIGH and send pulses response = spi_xfer(0xff);
spisdcard_cs_write(CS_HIGH);
for (i=0; i<10; i++) {
spi_write_byte( 0xff );
}
// set CS LOW and send pulses
spisdcard_cs_write(CS_LOW);
r = spi_read_rbyte();
timeout--; timeout--;
} while ( (timeout>0) && (r==0) ); } while(((response & 0x80) !=0) && timeout > 0);
return response;
if(timeout==0) return FAILURE;
return SUCCESS;
} }
// SPI_SDCARD_GOIDLE static uint8_t spisdcard_set_mode(void) {
// Function exposed to BIOS to initialise SPI mode uint8_t timeout;
// uint8_t response;
// Sequence
// Set 100KHz timer mode
// Send CLK pulses to set SD CARD to SPI mode
// Send CMD0 - Software RESET - force SD CARD IDLE
// Send CMD8 - Check SD CARD type
// Send CMD55+ACMD41 - Force SD CARD READY
// Send CMD58 - Read SD CARD OCR (status register)
// Send CMD16 - Set SD CARD block size to 512 - Sector Size for the SD CARD
// NOTE - Each command is prefixed with a dummy set of CLK pulses to prepare SD CARD to receive a command
// Return 0 success, 1 failure
//
// Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "Initializing the SD Card"
uint8_t spi_sdcard_goidle(void)
{
uint8_t r; // Response from SD CARD
int i, timeout; // TIMEOUT loop to send CMD55+ACMD41 repeatedly
r = spi_setspimode(); // Set SD CARD to SPI mode timeout = 32;
if( r != 0x01 ) return FAILURE; do {
int i;
/* Set CS and send 80 clock pulses to set the SDCard in SPI Mode */
spisdcard_cs_write(SPI_CS_HIGH);
for (i=0; i<10; i++)
spi_xfer(0xff);
/* Clear CS and read response, if 0 the SDCard has been initialized to SPI Mode */
spisdcard_cs_write(SPI_CS_LOW);
response = spisdcard_wait_response();
// CMD0 - Software reset - SD CARD IDLE timeout--;
// Command Sequence is DUMMY=0xff CMD0=0x40 0x00 0x00 0x00 0x00 CRC=0x95 } while ((timeout > 0) && (response == 0));
// Expected R1 response is 0x01 indicating SD CARD is IDLE
spi_write_byte( 0xff ); spi_write_byte( 0x40 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x95 );
r = spi_read_rbyte();
if(r!=0x01) return FAILURE;
// CMD8 - Check SD CARD type if(timeout == 0)
// Command sequence is DUMMY=0xff CMD8=0x48 0x00 0x00 0x01 0xaa CRC=0x87 return 0;
// Expected R7 response is 0x01 followed by 0x00 0x00 0x01 0xaa (these trailing 4 bytes not currently checked)
spi_write_byte( 0xff ); spi_write_byte( 0x48 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x01 ); spi_write_byte( 0xaa ); spi_write_byte( 0x87 ); return 1;
r = spi_read_rbyte(); }
if(r!=0x01) return FAILURE;
// Receive the trailing 4 bytes for R7 response - FIXME should check for 0x00 0x00 0x01 0xaa uint8_t spisdcard_init(void) {
uint8_t i;
uint8_t r;
uint8_t timeout;
/* Set SPI clk freq to 400KHz */
spi_set_clk_freq(400000);
/* Set SDCard in SPI Mode */
r = spisdcard_set_mode();
if(r != 0x01)
return 0;
/* Send SD CARD IDLE */
/* CMD0 */
spi_xfer(0xff);
spi_xfer(0x40);
spi_xfer(0x00);
spi_xfer(0x00);
spi_xfer(0x00);
spi_xfer(0x00);
spi_xfer(0x95);
/* R1 response, expects 0x1 */
r = spisdcard_wait_response();
if(r != 0x01)
return 0;
/* Send Check SD CARD type */
/* CMD8 */
spi_xfer(0xff);
spi_xfer(0x48);
spi_xfer(0x00);
spi_xfer(0x00);
spi_xfer(0x01);
spi_xfer(0xaa);
spi_xfer(0x87);
/* R7, expects 0x1 */
r = spisdcard_wait_response();
if(r != 0x01)
return 0;
/* Reveice the 4 trailing bytes */
for(i=0; i<4; i++) for(i=0; i<4; i++)
r=spi_read_byte(); r = spi_xfer(0xff); /* FIXME: add check? */
// CMD55+ACMD41 - Force SD CARD READY - prepare card for reading/writing /* Send Force SD CARD READY (CMD55 + ACMD41), expects 0x00 R1 response */
// Command sequence is CMD55 followed by ACMD41 timeout = 32;
// Send commands repeatedly until SD CARD indicates READY 0x00
// CMD55 Sequence is DUMMY=0xff CMD55=0x77 0x00 0x00 0x00 0x00 CRC=0x00
// ACMD41 Sequence is DUMMY=0xff ACMD41=0x69 0x40 0x00 0x00 0x00 CRC=0x00
// Expected R1 response is 0x00 indicating SD CARD is READY
timeout=32;
do { do {
spi_write_byte( 0xff ); spi_write_byte( 0x77 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); /* CMD55 */
r = spi_read_rbyte(); spi_xfer(0xff);
spi_xfer(0x77);
spi_write_byte( 0xff ); spi_write_byte( 0x69 ); spi_write_byte( 0x40 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_xfer(0x00);
r = spi_read_rbyte(); spi_xfer(0x00);
spi_xfer(0x00);
spi_xfer(0x00);
spi_xfer(0x00);
r = spisdcard_wait_response();
/* ACMD41 */
spi_xfer(0xff);
spi_xfer(0x69);
spi_xfer(0x40);
spi_xfer(0x00);
spi_xfer(0x00);
spi_xfer(0x00);
spi_xfer(0x00);
/* R1 */
r = spisdcard_wait_response();
timeout--; timeout--;
/* 20ms delay */
busy_wait(20); busy_wait(20);
} while ((r != 0x00) && (timeout>0)); } while ((r != 0x00) && (timeout > 0));
if(r!=0x00) return FAILURE; if(r != 0x00)
return 0;
// CMD58 - Read SD CARD OCR (status register) /* Send Read SD CARD OCR (status register) */
// FIXME - Find details on expected response from CMD58 to allow accurate checking of SD CARD R3 response /* CMD58 */
// Command sequence is DUMMY=0xff CMD58=0x7a 0x00 0x00 0x01 0xaa CRC=0xff spi_xfer(0xff);
// Expected R3 response is 0x00 OR 0x01 followed by 4 (unchecked) trailing bytes spi_xfer(0x7a);
spi_write_byte( 0xff ); spi_write_byte( 0x7a ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0xff ); spi_xfer(0x00);
r = spi_read_rbyte(); spi_xfer(0x00);
if(r>0x01) return FAILURE; spi_xfer(0x00);
// // Receive the trailing 4 bytes for R3 response spi_xfer(0x00);
spi_xfer(0xff);
/* R3, expects 0x1 */
r = spisdcard_wait_response();
if(r > 0x01)
return 0;
/* Reveice the 4 trailing bytes */
for(i=0; i<4; i++) for(i=0; i<4; i++)
r=spi_read_byte(); r = spi_xfer(0xff); /* FIXME: add check? */
// CMD16 - Set SD CARD block size to 512 - Sector Size for the SD CARD /* Send Set SD CARD block size */
// Command Sequence is DUMMY=0xff CMD16=0x50 (512 as unsigned 32bit = 0x00000200) 0x00 0x00 0x02 0x00 CRC=0xff /* CMD16 */
// Expected R1 response is 0x00 indicating SD CARD is READY spi_xfer(0xff);
spi_write_byte( 0xff ); spi_write_byte( 0x50 ); spi_write_byte( 0x00 ); spi_write_byte( 0x00 ); spi_write_byte( 0x02 ); spi_write_byte( 0x00 ); spi_write_byte( 0xff ); spi_xfer(0x50);
r=spi_read_rbyte(); spi_xfer(0x00);
if(r!=0x00) return FAILURE; spi_xfer(0x00);
spi_xfer(0x02);
spi_xfer(0x00);
spi_xfer(0xff);
/* RI, expects 0x00 */
r = spisdcard_wait_response();
if(r != 0x00)
return 0;
return SUCCESS; /* Set SPI clk freq to 16MHz */
spi_set_clk_freq(16000000);
return 1;
} }
// READSECTOR uint8_t spisdcard_read_block(uint32_t addr, uint8_t *buf) {
// Read a 512 byte sector from the SD CARD int i;
// Given SECTORNUMBER and memory STORAGE uint32_t timeout;
// uint8_t r;
// Sequence
// Send CMD17 - Read Block
// Command Sequence is DUMMY=0xff CMD17=0x51 SECTORNUMBER (32bit UNSIGNED as bits 32-25,24-17, 16-9, 8-1) CRC=0xff
// Wait for SD CARD to send 0x00 indicating SD CARD is processing
// Wait for SD CARD to send 0xfe indicating SD CARD BLOCK START
// Read 512 bytes
// Read 8 DUMMY bytes
// Return 0 success, 1 failure
//
// Details from https://openlabpro.com/guide/interfacing-microcontrollers-with-sd-card/ section "Read/Write SD Card"
uint8_t readSector(uint32_t sectorNumber, uint8_t *storage);
uint8_t readSector(uint32_t sectorNumber, uint8_t *storage)
{
int n, timeout; // Number of bytes loop, timeout loop awaiting response bytes
uint8_t r; // Response bytes from SD CARD
// CMD17 - Read Block /* Send Read Block */
// Command Sequence is DUMMY=0xff CMD17=0x51 SECTORNUMBER (32bit UNSIGNED as bits 32-25,24-17, 16-9, 8-1) CRC=0xff /* CMD17 */
// Expected R1 response is 0x00 indicating SD CARD is processing spi_xfer(0xff);
spi_write_byte( 0xff ); spi_write_byte( 0x51 ); spi_write_byte( (sectorNumber>>24)&0xff ); spi_write_byte( (sectorNumber>>16)&0xff ); spi_write_byte( (sectorNumber>>8)&0xff ); spi_write_byte( (sectorNumber)&0xff ); spi_write_byte( 0xff ); spi_xfer(0x51);
r=spi_read_rbyte(); spi_xfer((addr >> 24) & 0xff);
if( r!=0x00 ) return FAILURE; spi_xfer((addr >> 16) & 0xff);
spi_xfer((addr >> 8) & 0xff);
spi_xfer((addr >> 0) & 0xff);
spi_xfer(0xff);
/* R1, expects 0x00 that indicates the SDCard is processing */
r = spisdcard_wait_response();
if(r != 0x00)
return 0;
// Await 0xfe to indicate BLOCK START /* Do SPI Xfers on SDCard until 0xfe is received (block start) or timeout is expired */
r=spi_read_byte(); r = spi_xfer(0xff);
timeout=16384; timeout = 16384;
while( (r!=0xfe) && (timeout>0) ) { do {
r=spi_read_byte(); r = spi_xfer(0xff);
timeout--; timeout--;
} } while((r != 0xfe) && (timeout>0));
if( r!=0xfe ) return FAILURE; if(r != 0xfe)
return 0;
// Read 512 bytes into storage /* Read the block from the SDCard and copy it to the buffer */
for(n=0; n<512; n++) for(i=0; i<512; i++)
storage[n]=spi_read_byte(); buf[i] = spi_xfer(0xff);
// Read 8 dummy bytes /* Read the 8 dummy bytes */
for(n=0; n<8; n++) for(i=0; i<8; i++)
r=spi_read_byte(); r = spi_xfer(0xff);
return SUCCESS; return 1;
} }
#endif #endif

View File

@ -1,4 +1,5 @@
// This file is Copyright (c) 2020 Rob Shelton <rob.s.ng15@googlemail.com> // This file is Copyright (c) 2020 Rob Shelton <rob.s.ng15@googlemail.com>
// This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
// License: BSD // License: BSD
#ifndef __SPISDCARD_H #ifndef __SPISDCARD_H
@ -8,14 +9,8 @@
#ifdef CSR_SPISDCARD_BASE #ifdef CSR_SPISDCARD_BASE
#define USE_SPISCARD_RECLOCKING uint8_t spisdcard_init(void);
uint8_t spisdcard_read_block(uint32_t addr, uint8_t *buf);
int spi_sdcard_init(uint32_t device);
int spi_sdcard_read_sector(uint32_t device, uint32_t lba,uint_least8_t *buf);
uint8_t spi_sdcard_goidle(void);
uint8_t readSector(uint32_t sectorNumber, uint8_t *storage);
#endif /* CSR_SPISDCARD_BASE */ #endif /* CSR_SPISDCARD_BASE */