From: Florent Kermarrec Date: Thu, 19 Mar 2020 10:02:15 +0000 (+0100) Subject: software/libbase/bios: rename spi.c/h to spisdcard.h, also rename functions. X-Git-Tag: 24jan2021_ls180~551 X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=37f25ed37a246fc5397f4f3732322084b33477c1;p=litex.git software/libbase/bios: rename spi.c/h to spisdcard.h, also rename functions. --- diff --git a/litex/soc/software/bios/boot.c b/litex/soc/software/bios/boot.c index 355f20a0..4cf698cf 100644 --- a/litex/soc/software/bios/boot.c +++ b/litex/soc/software/bios/boot.c @@ -489,10 +489,10 @@ void romboot(void) #endif // SPI HARDWARE BITBANG -#ifdef CSR_SPI_BASE -#include +#ifdef CSR_SPISDCARD_BASE +#include -void spisdboot(void) +void spisdcardboot(void) { printf("SD Card via SPI Initialising\n"); if(spi_sdcard_goidle() == 0) { diff --git a/litex/soc/software/bios/boot.h b/litex/soc/software/bios/boot.h index fd1d06a2..f9517bad 100644 --- a/litex/soc/software/bios/boot.h +++ b/litex/soc/software/bios/boot.h @@ -6,8 +6,8 @@ void netboot(void); void flashboot(void); void romboot(void); -#ifdef CSR_SPI_BASE -void spisdboot(void); +#ifdef CSR_SPISDCARD_BASE +void spisdcardboot(void); #endif #endif /* __BOOT_H */ diff --git a/litex/soc/software/bios/main.c b/litex/soc/software/bios/main.c index 460563f8..5024e4f4 100644 --- a/litex/soc/software/bios/main.c +++ b/litex/soc/software/bios/main.c @@ -395,8 +395,8 @@ static void help(void) puts("sdram_mpr - read SDRAM MPR"); puts("sdram_mrwr reg value - write SDRAM mode registers"); #endif -#ifdef CSR_SPI_BASE - puts("spisdboot - boot from SDCard via SPI hardware bitbang"); +#ifdef CSR_SPISDCARD_BASE + puts("spisdcardboot - boot from SDCard via SPI hardware bitbang"); #endif } @@ -507,8 +507,8 @@ static void do_command(char *c) sdrhw(); } #endif -#ifdef CSR_SPI_BASE - else if(strcmp(token, "spisdboot") == 0) spisdboot(); +#ifdef CSR_SPISDCARD_BASE + else if(strcmp(token, "spisdcardboot") == 0) spisdcardboot(); #endif else if(strcmp(token, "") != 0) diff --git a/litex/soc/software/include/base/spi.h b/litex/soc/software/include/base/spi.h deleted file mode 100644 index db6ade28..00000000 --- a/litex/soc/software/include/base/spi.h +++ /dev/null @@ -1,6 +0,0 @@ -int spi_sdcard_init(uint32_t device); -int spi_sdcard_read_sector(uint32_t device, unsigned long lba,unsigned char *buf); - -unsigned char spi_sdcard_goidle(void); -unsigned char spi_sdcard_readMBR(void); -unsigned char spi_sdcard_readFile(char *, char *, unsigned long); diff --git a/litex/soc/software/include/base/spisdcard.h b/litex/soc/software/include/base/spisdcard.h new file mode 100644 index 00000000..db6ade28 --- /dev/null +++ b/litex/soc/software/include/base/spisdcard.h @@ -0,0 +1,6 @@ +int spi_sdcard_init(uint32_t device); +int spi_sdcard_read_sector(uint32_t device, unsigned long lba,unsigned char *buf); + +unsigned char spi_sdcard_goidle(void); +unsigned char spi_sdcard_readMBR(void); +unsigned char spi_sdcard_readFile(char *, char *, unsigned long); diff --git a/litex/soc/software/libbase/Makefile b/litex/soc/software/libbase/Makefile index 7ccb4d0b..e3a80a03 100755 --- a/litex/soc/software/libbase/Makefile +++ b/litex/soc/software/libbase/Makefile @@ -2,7 +2,7 @@ include ../include/generated/variables.mak include $(SOC_DIRECTORY)/software/common.mak OBJECTS=exception.o libc.o errno.o crc16.o crc32.o console.o \ - system.o id.o uart.o time.o qsort.o strtod.o spiflash.o spi.o strcasecmp.o mdio.o + system.o id.o uart.o time.o qsort.o strtod.o spiflash.o spisdcard.o strcasecmp.o mdio.o all: crt0-$(CPU)-ctr.o crt0-$(CPU)-xip.o libbase.a libbase-nofloat.a diff --git a/litex/soc/software/libbase/spi.c b/litex/soc/software/libbase/spi.c deleted file mode 100644 index b602860a..00000000 --- a/litex/soc/software/libbase/spi.c +++ /dev/null @@ -1,585 +0,0 @@ -// SD CARD bitbanging code for loading files from a FAT16 forrmatted partition into memory -// -// 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 -#include -#include - -#include -#include -#include -#include - -#ifdef CSR_SPI_BASE -// Import prototypes for the functions -#include - -// SPI -// cs line - high to indicate DESELECT -// - low to indicate SELECT -#define CS_HIGH 0x00 -#define CS_LOW 0x01 - -// control register values -// 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 SUCCESS 0x01 -#define FAILURE 0x00 - -// spi_write_byte -// 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(unsigned char char_to_send); -void spi_write_byte(unsigned char char_to_send) -{ - // Place data into MOSI register - // Pulse the START bit and set LENGTH=8 - spi_mosi_write(char_to_send); - spi_control_write(ONEBYTE | SPI_START); - - // Wait for DONE - while( (spi_status_read() != SPI_DONE)) {} - - // Signal end of transfer - spi_control_write( 0x00 ); -} - - -// spi_read_rbyte -// Read a command response from the SD CARD - Equivalent to and R1 response or first byte of an R7 response -// Sequence -// Read MISO -// If MSB != 0 then send dsummy byte and re-read MISO -// -// Return value is the response from the SD CARD -// If the MSB is not 0, this would represent an ERROR -// 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" -unsigned char spi_read_rbyte(void); -unsigned char spi_read_rbyte(void) -{ - int timeout=32; - unsigned char r=0; - - // Check if MISO is 0x0xxxxxxx as MSB=0 indicates valid response - r = spi_miso_read(); - while( ((r&0x80)!=0) && timeout>0) { - spi_mosi_write( 0xff ); - spi_control_write(ONEBYTE | SPI_START); - while( (spi_status_read() != SPI_DONE)) {} - r = spi_miso_read(); - spi_control_write( 0x00 ); - timeout--; - } - -// printf("Done\n"); - return r; -} - -// spi_read_byte -// 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" -unsigned char spi_read_byte(void); -unsigned char spi_read_byte(void) -{ - unsigned char r=0; - - spi_write_byte( 0xff ); - r = spi_miso_read(); - - return r; -} - -// 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" -unsigned char spi_setspimode(void); -unsigned char spi_setspimode(void) -{ - unsigned int i, r, timeout=32; - - // Initialise SPI mode - // set CS to HIGH - // Send pulses - do { - // set CS HIGH and send pulses - spi_cs_write(CS_HIGH); - for (i=0; i<10; i++) { - spi_write_byte( 0xff ); - } - - // set CS LOW and send pulses - spi_cs_write(CS_LOW); - r = spi_read_rbyte(); - - timeout--; - } while ( (timeout>0) && (r==0) ); - - if(timeout==0) return FAILURE; - - return SUCCESS; -} - -// SPI_SDCARD_GOIDLE -// Function exposed to BIOS to initialise SPI mode -// -// 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" -unsigned char spi_sdcard_goidle(void) -{ - unsigned char r; // Response from SD CARD - int i, timeout; // TIMEOUT loop to send CMD55+ACMD41 repeatedly - - r = spi_setspimode(); // Set SD CARD to SPI mode - if( r != 0x01 ) return FAILURE; - - // CMD0 - Software reset - SD CARD IDLE - // Command Sequence is DUMMY=0xff CMD0=0x40 0x00 0x00 0x00 0x00 CRC=0x95 - // 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 - // Command sequence is DUMMY=0xff CMD8=0x48 0x00 0x00 0x01 0xaa CRC=0x87 - // 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 ); - 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 - for(i=0; i<4; i++) - r=spi_read_byte(); - - // CMD55+ACMD41 - Force SD CARD READY - prepare card for reading/writing - // Command sequence is CMD55 followed by ACMD41 - // 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 { - 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 ); - r = spi_read_rbyte(); - - 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 ); - r = spi_read_rbyte(); - timeout--; - } while ((r != 0x00) && (timeout>0)); - if(r!=0x00) return FAILURE; - - // CMD58 - Read SD CARD OCR (status register) - // FIXME - Find details on expected response from CMD58 to allow accurate checking of SD CARD R3 response - // Command sequence is DUMMY=0xff CMD58=0x7a 0x00 0x00 0x01 0xaa CRC=0xff - // Expected R3 response is 0x00 OR 0x01 followed by 4 (unchecked) trailing bytes - 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 ); - r = spi_read_rbyte(); - if(r>0x01) return FAILURE; - // // Receive the trailing 4 bytes for R3 response - for(i=0; i<4; i++) - r=spi_read_byte(); - - // CMD16 - Set SD CARD block size to 512 - Sector Size for the SD CARD - // Command Sequence is DUMMY=0xff (512 as unsigned long = 0x00000200) 0x00 0x00 0x02 0x00 CRC=0xff - // Expected R1 response is 0x00 indicating SD CARD is READY - 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 ); - r=spi_read_rbyte(); - if(r!=0x00) return FAILURE; - - return SUCCESS; -} - -// READSECTOR -// Read a 512 byte sector from the SD CARD -// Given SECTORNUMBER and memory STORAGE -// -// 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" -unsigned char readSector(unsigned int sectorNumber, unsigned char *storage); -unsigned char readSector(unsigned int sectorNumber, unsigned char *storage) -{ - unsigned int n,timeout; // Number of bytes loop, timeout loop awaiting response bytes - unsigned char r; // Response bytes from SD CARD - - // 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 - // Expected R1 response is 0x00 indicating SD CARD is processing - 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 ); - r=spi_read_rbyte(); - if( r!=0x00 ) return FAILURE; - - // Await 0xfe to indicate BLOCK START - r=spi_read_byte(); - timeout=16384; - while( (r!=0xfe) && (timeout>0) ) { - r=spi_read_byte(); - timeout--; - } - if( r!=0xfe ) return FAILURE; - - // Read 512 bytes into storage - for(n=0; n<512; n++) - storage[n]=spi_read_byte(); - - // Read 8 dummy bytes - for(n=0; n<8; n++) - r=spi_read_byte(); - - return SUCCESS; -} - - -// FAT16 Specific code starts here -// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ - -// Structure to store SD CARD partition table -typedef struct { - unsigned char first_byte; - unsigned char start_chs[3]; - unsigned char partition_type; - unsigned char end_chs[3]; - unsigned long start_sector; - unsigned long length_sectors; -} __attribute((packed)) PartitionTable; - -PartitionTable sdCardPartition; - -// Structure to store SD CARD FAT16 Boot Sector (boot code is ignored, provides layout of the FAT16 partition on the SD CARD) -typedef struct { - unsigned char jmp[3]; - char oem[8]; - unsigned short sector_size; - unsigned char sectors_per_cluster; - unsigned short reserved_sectors; - unsigned char number_of_fats; - unsigned short root_dir_entries; - unsigned short total_sectors_short; // if zero, later field is used - unsigned char media_descriptor; - unsigned short fat_size_sectors; - unsigned short sectors_per_track; - unsigned short number_of_heads; - unsigned long hidden_sectors; - unsigned long total_sectors_long; - - unsigned char drive_number; - unsigned char current_head; - unsigned char boot_signature; - unsigned long volume_id; - char volume_label[11]; - char fs_type[8]; - char boot_code[448]; - unsigned short boot_sector_signature; -} __attribute((packed)) Fat16BootSector; - -Fat16BootSector sdCardFatBootSector; - -// Structure to store SD CARD FAT16 Root Directory Entries -// Allocated to MAIN RAM - hence pointer -typedef struct { - unsigned char filename[8]; - unsigned char ext[3]; - unsigned char attributes; - unsigned char reserved[10]; - unsigned short modify_time; - unsigned short modify_date; - unsigned short starting_cluster; - unsigned long file_size; -} __attribute((packed)) Fat16Entry; - -Fat16Entry *sdCardFat16RootDir; - -// Structure to store SD CARD FAT16 Entries -// Array of UNSIGNED SHORTS (16bit integers) -unsigned short *sdCardFatTable; - -// Calculated sector numbers on the SD CARD for the FAT16 Entries and ROOT DIRECTORY -unsigned int fatSectorStart, rootDirSectorStart; - -// Storage for SECTOR read from SD CARD -unsigned char sdCardSector[512]; - -// SPI_SDCARD_READMBR -// Function exposed to BIOS to retrieve FAT16 partition details, FAT16 Entry Table, FAT16 Root Directory -// MBR = Master Boot Record - Sector 0x00000000 on SD CARD - Contains Partition 1 details at 0x1be -// -// FIXME only checks partition 1 out of 4 -// -// Return 0 success, 1 failure -// -// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ -unsigned char spi_sdcard_readMBR(void) -{ - int i, n; - - // Read Sector 0x00000000 - printf("Reading MBR\n"); - if( readSector(0x00000000, sdCardSector)==SUCCESS ) { - // Copy Partition 1 Entry from byte 0x1be - // FIXME should check 0x55 0xaa at end of sector - memcpy(&sdCardPartition, &sdCardSector[0x1be], sizeof(PartitionTable)); - - // Check Partition 1 is valid, FIRST_BYTE=0x00 or 0x80 - // Check Partition 1 has type 4, 6 or 14 (FAT16 of various sizes) - printf("Partition 1 Information: Active=0x%02x, Type=0x%02x, LBAStart=0x%08x\n", sdCardPartition.first_byte, sdCardPartition.partition_type, sdCardPartition.start_sector); - if( (sdCardPartition.first_byte!=0x80) && (sdCardPartition.first_byte!=0x00) ) { - printf("Partition 1 Not Valid\n"); - return FAILURE; - } - if( (sdCardPartition.partition_type==4) || (sdCardPartition.partition_type==6) || (sdCardPartition.partition_type==14) ) { - printf("Partition 1 is FAT16\n"); - } - else { - printf("Partition 1 Not FAT16\n"); - return FAILURE; - } - } - else { - printf("Failed to read MBR\n"); - return FAILURE; - } - - // Read Parition 1 Boot Sector - Found from Partion Table - printf("\nRead FAT16 Boot Sector\n"); - if( readSector(sdCardPartition.start_sector, sdCardSector)==SUCCESS ) { - memcpy(&sdCardFatBootSector, &sdCardSector, sizeof(Fat16BootSector)); - } - else { - printf("Failed to read FAT16 Boot Sector\n"); - return FAILURE; - } - - // Print details of Parition 1 - printf(" Jump Code: 0x%02x 0x%02x 0x%02x\n",sdCardFatBootSector.jmp[0],sdCardFatBootSector.jmp[1],sdCardFatBootSector.jmp[2]); - printf(" OEM Code: ["); - for(n=0; n<8; n++) - printf("%c",sdCardFatBootSector.oem[n]); - printf("]\n"); - printf(" Sector Size: %d\n",sdCardFatBootSector.sector_size); - printf(" Sectors Per Cluster: %d\n",sdCardFatBootSector.sectors_per_cluster); - printf(" Reserved Sectors: %d\n",sdCardFatBootSector.reserved_sectors); - printf(" Number of Fats: %d\n",sdCardFatBootSector.number_of_fats); - printf(" Root Dir Entries: %d\n",sdCardFatBootSector.root_dir_entries); - printf(" Total Sectors Short: %d\n",sdCardFatBootSector.total_sectors_short); - printf(" Media Descriptor: 0x%02x\n",sdCardFatBootSector.media_descriptor); - printf(" Fat Size Sectors: %d\n",sdCardFatBootSector.fat_size_sectors); - printf(" Sectors Per Track: %d\n",sdCardFatBootSector.sectors_per_track); - printf(" Number of Heads: %d\n",sdCardFatBootSector.number_of_heads); - printf(" Hidden Sectors: %d\n",sdCardFatBootSector.hidden_sectors); - printf(" Total Sectors Long: %d\n",sdCardFatBootSector.total_sectors_long); - printf(" Drive Number: 0x%02x\n",sdCardFatBootSector.drive_number); - printf(" Current Head: 0x%02x\n",sdCardFatBootSector.current_head); - printf(" Boot Signature: 0x%02x\n",sdCardFatBootSector.boot_signature); - printf(" Volume ID: 0x%08x\n",sdCardFatBootSector.volume_id); - printf(" Volume Label: ["); - for(n=0; n<11; n++) - printf("%c",sdCardFatBootSector.volume_label[n]); - printf("]\n"); - printf(" Volume Label: ["); - for(n=0; n<8; n++) - printf("%c",sdCardFatBootSector.fs_type[n]); - printf("]\n"); - printf(" Boot Sector Signature: 0x%04x\n\n",sdCardFatBootSector.boot_sector_signature); - - // Check Partition 1 is valid, not 0 length - if(sdCardFatBootSector.total_sectors_long==0) { - printf("Error reading FAT16 Boot Sector\n"); - return FAILURE; - } - - // Read in FAT16 File Allocation Table, array of 16bit unsinged integers - // Calculate Storage from TOP of MAIN RAM - sdCardFatTable = (unsigned short *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors); - printf("sdCardFatTable = 0x%08x Reading Fat16 Table (%d Sectors Long)\n\n",sdCardFatTable,sdCardFatBootSector.fat_size_sectors); - - // Calculate Start of FAT16 File Allocation Table (start of partition plus reserved sectors) - fatSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors; - for(n=0; n0)) { - printf(" File %d [",n); - for( i=0; i<8; i++) { - if( (sdCardFat16RootDir[n].filename[i]>31) && (sdCardFat16RootDir[n].filename[i]<127) ) - printf("%c",sdCardFat16RootDir[n].filename[i]); - else - printf(" "); - } - printf("."); - for( i=0; i<3; i++) { - if( (sdCardFat16RootDir[n].ext[i]>31) && (sdCardFat16RootDir[n].ext[i]<127) ) - printf("%c",sdCardFat16RootDir[n].ext[i]); - else - printf(" "); - } - printf("] @ Cluster %d for %d bytes\n",sdCardFat16RootDir[n].starting_cluster,sdCardFat16RootDir[n].file_size); - } - } - - printf("\n"); - return SUCCESS; -} - -// SPI_SDCARD_READFILE -// Function exposed to BIOS to retrieve FILENAME+EXT into ADDRESS -// -// FIXME only checks UPPERCASE 8+3 filenames -// -// Return 0 success, 1 failure -// -// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ -unsigned char spi_sdcard_readFile(char *filename, char *ext, unsigned long address) -{ - int i, n, sector; - unsigned short fileClusterStart; - unsigned long fileLength, bytesRemaining, clusterSectorStart; - unsigned short nameMatch; - printf("Reading File [%s.%s] into 0x%08x : ",filename, ext, address); - - // Find FILENAME+EXT in Root Directory - // Indicate FILE found by setting the starting cluster number - fileClusterStart=0; n=0; - while( (fileClusterStart==0) && (n1) - for(i=0; i<1+((fileLength/sdCardFatBootSector.sectors_per_cluster)/sdCardFatBootSector.sector_size); i++) { - printf("%d ",fileClusterStart); - - // Locate start of cluster on SD CARD and read appropraite number of sectors - clusterSectorStart=rootDirSectorStart+(fileClusterStart-1)*sdCardFatBootSector.sectors_per_cluster; - for(sector=0; sectorsdCardFatBootSector.sector_size) { - if( readSector(clusterSectorStart+sector,(unsigned char *)address) == FAILURE ) { - printf("Read Error\n"); - return FAILURE; - } - bytesRemaining=bytesRemaining-sdCardFatBootSector.sector_size; - address=address+sdCardFatBootSector.sector_size; - } else { - if( readSector(clusterSectorStart+sector,sdCardSector) == FAILURE ) { - printf("Read Error\n"); - return FAILURE; - } - memcpy((unsigned char *)address, sdCardSector, bytesRemaining); - bytesRemaining=0; - } - } - - // Move to next cluster - fileClusterStart=sdCardFatTable[fileClusterStart]; - } - printf("\n\n"); - return SUCCESS; -} -#endif diff --git a/litex/soc/software/libbase/spisdcard.c b/litex/soc/software/libbase/spisdcard.c new file mode 100644 index 00000000..eafbf2f2 --- /dev/null +++ b/litex/soc/software/libbase/spisdcard.c @@ -0,0 +1,585 @@ +// SD CARD bitbanging code for loading files from a FAT16 forrmatted partition into memory +// +// 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 +#include +#include + +#include +#include +#include +#include + +#ifdef CSR_SPISDCARD_BASE +// Import prototypes for the functions +#include + +// SPI +// cs line - high to indicate DESELECT +// - low to indicate SELECT +#define CS_HIGH 0x00 +#define CS_LOW 0x01 + +// control register values +// 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 SUCCESS 0x01 +#define FAILURE 0x00 + +// spi_write_byte +// 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(unsigned char char_to_send); +void spi_write_byte(unsigned char 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 + while( (spisdcard_status_read() != SPI_DONE)) {} + + // Signal end of transfer + spisdcard_control_write( 0x00 ); +} + + +// spi_read_rbyte +// Read a command response from the SD CARD - Equivalent to and R1 response or first byte of an R7 response +// Sequence +// Read MISO +// If MSB != 0 then send dsummy byte and re-read MISO +// +// Return value is the response from the SD CARD +// If the MSB is not 0, this would represent an ERROR +// 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" +unsigned char spi_read_rbyte(void); +unsigned char spi_read_rbyte(void) +{ + int timeout=32; + unsigned char 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 +// 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" +unsigned char spi_read_byte(void); +unsigned char spi_read_byte(void) +{ + unsigned char r=0; + + spi_write_byte( 0xff ); + r = spisdcard_miso_read(); + + return r; +} + +// 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" +unsigned char spi_setspimode(void); +unsigned char spi_setspimode(void) +{ + unsigned int i, r, timeout=32; + + // Initialise SPI mode + // set CS to HIGH + // Send pulses + do { + // set CS HIGH and send pulses + 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--; + } while ( (timeout>0) && (r==0) ); + + if(timeout==0) return FAILURE; + + return SUCCESS; +} + +// SPI_SDCARD_GOIDLE +// Function exposed to BIOS to initialise SPI mode +// +// 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" +unsigned char spi_sdcard_goidle(void) +{ + unsigned char r; // Response from SD CARD + int i, timeout; // TIMEOUT loop to send CMD55+ACMD41 repeatedly + + r = spi_setspimode(); // Set SD CARD to SPI mode + if( r != 0x01 ) return FAILURE; + + // CMD0 - Software reset - SD CARD IDLE + // Command Sequence is DUMMY=0xff CMD0=0x40 0x00 0x00 0x00 0x00 CRC=0x95 + // 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 + // Command sequence is DUMMY=0xff CMD8=0x48 0x00 0x00 0x01 0xaa CRC=0x87 + // 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 ); + 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 + for(i=0; i<4; i++) + r=spi_read_byte(); + + // CMD55+ACMD41 - Force SD CARD READY - prepare card for reading/writing + // Command sequence is CMD55 followed by ACMD41 + // 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 { + 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 ); + r = spi_read_rbyte(); + + 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 ); + r = spi_read_rbyte(); + timeout--; + } while ((r != 0x00) && (timeout>0)); + if(r!=0x00) return FAILURE; + + // CMD58 - Read SD CARD OCR (status register) + // FIXME - Find details on expected response from CMD58 to allow accurate checking of SD CARD R3 response + // Command sequence is DUMMY=0xff CMD58=0x7a 0x00 0x00 0x01 0xaa CRC=0xff + // Expected R3 response is 0x00 OR 0x01 followed by 4 (unchecked) trailing bytes + 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 ); + r = spi_read_rbyte(); + if(r>0x01) return FAILURE; + // // Receive the trailing 4 bytes for R3 response + for(i=0; i<4; i++) + r=spi_read_byte(); + + // CMD16 - Set SD CARD block size to 512 - Sector Size for the SD CARD + // Command Sequence is DUMMY=0xff (512 as unsigned long = 0x00000200) 0x00 0x00 0x02 0x00 CRC=0xff + // Expected R1 response is 0x00 indicating SD CARD is READY + 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 ); + r=spi_read_rbyte(); + if(r!=0x00) return FAILURE; + + return SUCCESS; +} + +// READSECTOR +// Read a 512 byte sector from the SD CARD +// Given SECTORNUMBER and memory STORAGE +// +// 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" +unsigned char readSector(unsigned int sectorNumber, unsigned char *storage); +unsigned char readSector(unsigned int sectorNumber, unsigned char *storage) +{ + unsigned int n,timeout; // Number of bytes loop, timeout loop awaiting response bytes + unsigned char r; // Response bytes from SD CARD + + // 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 + // Expected R1 response is 0x00 indicating SD CARD is processing + 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 ); + r=spi_read_rbyte(); + if( r!=0x00 ) return FAILURE; + + // Await 0xfe to indicate BLOCK START + r=spi_read_byte(); + timeout=16384; + while( (r!=0xfe) && (timeout>0) ) { + r=spi_read_byte(); + timeout--; + } + if( r!=0xfe ) return FAILURE; + + // Read 512 bytes into storage + for(n=0; n<512; n++) + storage[n]=spi_read_byte(); + + // Read 8 dummy bytes + for(n=0; n<8; n++) + r=spi_read_byte(); + + return SUCCESS; +} + + +// FAT16 Specific code starts here +// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ + +// Structure to store SD CARD partition table +typedef struct { + unsigned char first_byte; + unsigned char start_chs[3]; + unsigned char partition_type; + unsigned char end_chs[3]; + unsigned long start_sector; + unsigned long length_sectors; +} __attribute((packed)) PartitionTable; + +PartitionTable sdCardPartition; + +// Structure to store SD CARD FAT16 Boot Sector (boot code is ignored, provides layout of the FAT16 partition on the SD CARD) +typedef struct { + unsigned char jmp[3]; + char oem[8]; + unsigned short sector_size; + unsigned char sectors_per_cluster; + unsigned short reserved_sectors; + unsigned char number_of_fats; + unsigned short root_dir_entries; + unsigned short total_sectors_short; // if zero, later field is used + unsigned char media_descriptor; + unsigned short fat_size_sectors; + unsigned short sectors_per_track; + unsigned short number_of_heads; + unsigned long hidden_sectors; + unsigned long total_sectors_long; + + unsigned char drive_number; + unsigned char current_head; + unsigned char boot_signature; + unsigned long volume_id; + char volume_label[11]; + char fs_type[8]; + char boot_code[448]; + unsigned short boot_sector_signature; +} __attribute((packed)) Fat16BootSector; + +Fat16BootSector sdCardFatBootSector; + +// Structure to store SD CARD FAT16 Root Directory Entries +// Allocated to MAIN RAM - hence pointer +typedef struct { + unsigned char filename[8]; + unsigned char ext[3]; + unsigned char attributes; + unsigned char reserved[10]; + unsigned short modify_time; + unsigned short modify_date; + unsigned short starting_cluster; + unsigned long file_size; +} __attribute((packed)) Fat16Entry; + +Fat16Entry *sdCardFat16RootDir; + +// Structure to store SD CARD FAT16 Entries +// Array of UNSIGNED SHORTS (16bit integers) +unsigned short *sdCardFatTable; + +// Calculated sector numbers on the SD CARD for the FAT16 Entries and ROOT DIRECTORY +unsigned int fatSectorStart, rootDirSectorStart; + +// Storage for SECTOR read from SD CARD +unsigned char sdCardSector[512]; + +// SPI_SDCARD_READMBR +// Function exposed to BIOS to retrieve FAT16 partition details, FAT16 Entry Table, FAT16 Root Directory +// MBR = Master Boot Record - Sector 0x00000000 on SD CARD - Contains Partition 1 details at 0x1be +// +// FIXME only checks partition 1 out of 4 +// +// Return 0 success, 1 failure +// +// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ +unsigned char spi_sdcard_readMBR(void) +{ + int i, n; + + // Read Sector 0x00000000 + printf("Reading MBR\n"); + if( readSector(0x00000000, sdCardSector)==SUCCESS ) { + // Copy Partition 1 Entry from byte 0x1be + // FIXME should check 0x55 0xaa at end of sector + memcpy(&sdCardPartition, &sdCardSector[0x1be], sizeof(PartitionTable)); + + // Check Partition 1 is valid, FIRST_BYTE=0x00 or 0x80 + // Check Partition 1 has type 4, 6 or 14 (FAT16 of various sizes) + printf("Partition 1 Information: Active=0x%02x, Type=0x%02x, LBAStart=0x%08x\n", sdCardPartition.first_byte, sdCardPartition.partition_type, sdCardPartition.start_sector); + if( (sdCardPartition.first_byte!=0x80) && (sdCardPartition.first_byte!=0x00) ) { + printf("Partition 1 Not Valid\n"); + return FAILURE; + } + if( (sdCardPartition.partition_type==4) || (sdCardPartition.partition_type==6) || (sdCardPartition.partition_type==14) ) { + printf("Partition 1 is FAT16\n"); + } + else { + printf("Partition 1 Not FAT16\n"); + return FAILURE; + } + } + else { + printf("Failed to read MBR\n"); + return FAILURE; + } + + // Read Parition 1 Boot Sector - Found from Partion Table + printf("\nRead FAT16 Boot Sector\n"); + if( readSector(sdCardPartition.start_sector, sdCardSector)==SUCCESS ) { + memcpy(&sdCardFatBootSector, &sdCardSector, sizeof(Fat16BootSector)); + } + else { + printf("Failed to read FAT16 Boot Sector\n"); + return FAILURE; + } + + // Print details of Parition 1 + printf(" Jump Code: 0x%02x 0x%02x 0x%02x\n",sdCardFatBootSector.jmp[0],sdCardFatBootSector.jmp[1],sdCardFatBootSector.jmp[2]); + printf(" OEM Code: ["); + for(n=0; n<8; n++) + printf("%c",sdCardFatBootSector.oem[n]); + printf("]\n"); + printf(" Sector Size: %d\n",sdCardFatBootSector.sector_size); + printf(" Sectors Per Cluster: %d\n",sdCardFatBootSector.sectors_per_cluster); + printf(" Reserved Sectors: %d\n",sdCardFatBootSector.reserved_sectors); + printf(" Number of Fats: %d\n",sdCardFatBootSector.number_of_fats); + printf(" Root Dir Entries: %d\n",sdCardFatBootSector.root_dir_entries); + printf(" Total Sectors Short: %d\n",sdCardFatBootSector.total_sectors_short); + printf(" Media Descriptor: 0x%02x\n",sdCardFatBootSector.media_descriptor); + printf(" Fat Size Sectors: %d\n",sdCardFatBootSector.fat_size_sectors); + printf(" Sectors Per Track: %d\n",sdCardFatBootSector.sectors_per_track); + printf(" Number of Heads: %d\n",sdCardFatBootSector.number_of_heads); + printf(" Hidden Sectors: %d\n",sdCardFatBootSector.hidden_sectors); + printf(" Total Sectors Long: %d\n",sdCardFatBootSector.total_sectors_long); + printf(" Drive Number: 0x%02x\n",sdCardFatBootSector.drive_number); + printf(" Current Head: 0x%02x\n",sdCardFatBootSector.current_head); + printf(" Boot Signature: 0x%02x\n",sdCardFatBootSector.boot_signature); + printf(" Volume ID: 0x%08x\n",sdCardFatBootSector.volume_id); + printf(" Volume Label: ["); + for(n=0; n<11; n++) + printf("%c",sdCardFatBootSector.volume_label[n]); + printf("]\n"); + printf(" Volume Label: ["); + for(n=0; n<8; n++) + printf("%c",sdCardFatBootSector.fs_type[n]); + printf("]\n"); + printf(" Boot Sector Signature: 0x%04x\n\n",sdCardFatBootSector.boot_sector_signature); + + // Check Partition 1 is valid, not 0 length + if(sdCardFatBootSector.total_sectors_long==0) { + printf("Error reading FAT16 Boot Sector\n"); + return FAILURE; + } + + // Read in FAT16 File Allocation Table, array of 16bit unsinged integers + // Calculate Storage from TOP of MAIN RAM + sdCardFatTable = (unsigned short *)(MAIN_RAM_BASE+MAIN_RAM_SIZE-sdCardFatBootSector.sector_size*sdCardFatBootSector.fat_size_sectors); + printf("sdCardFatTable = 0x%08x Reading Fat16 Table (%d Sectors Long)\n\n",sdCardFatTable,sdCardFatBootSector.fat_size_sectors); + + // Calculate Start of FAT16 File Allocation Table (start of partition plus reserved sectors) + fatSectorStart=sdCardPartition.start_sector+sdCardFatBootSector.reserved_sectors; + for(n=0; n0)) { + printf(" File %d [",n); + for( i=0; i<8; i++) { + if( (sdCardFat16RootDir[n].filename[i]>31) && (sdCardFat16RootDir[n].filename[i]<127) ) + printf("%c",sdCardFat16RootDir[n].filename[i]); + else + printf(" "); + } + printf("."); + for( i=0; i<3; i++) { + if( (sdCardFat16RootDir[n].ext[i]>31) && (sdCardFat16RootDir[n].ext[i]<127) ) + printf("%c",sdCardFat16RootDir[n].ext[i]); + else + printf(" "); + } + printf("] @ Cluster %d for %d bytes\n",sdCardFat16RootDir[n].starting_cluster,sdCardFat16RootDir[n].file_size); + } + } + + printf("\n"); + return SUCCESS; +} + +// SPI_SDCARD_READFILE +// Function exposed to BIOS to retrieve FILENAME+EXT into ADDRESS +// +// FIXME only checks UPPERCASE 8+3 filenames +// +// Return 0 success, 1 failure +// +// Details from https://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ +unsigned char spi_sdcard_readFile(char *filename, char *ext, unsigned long address) +{ + int i, n, sector; + unsigned short fileClusterStart; + unsigned long fileLength, bytesRemaining, clusterSectorStart; + unsigned short nameMatch; + printf("Reading File [%s.%s] into 0x%08x : ",filename, ext, address); + + // Find FILENAME+EXT in Root Directory + // Indicate FILE found by setting the starting cluster number + fileClusterStart=0; n=0; + while( (fileClusterStart==0) && (n1) + for(i=0; i<1+((fileLength/sdCardFatBootSector.sectors_per_cluster)/sdCardFatBootSector.sector_size); i++) { + printf("%d ",fileClusterStart); + + // Locate start of cluster on SD CARD and read appropraite number of sectors + clusterSectorStart=rootDirSectorStart+(fileClusterStart-1)*sdCardFatBootSector.sectors_per_cluster; + for(sector=0; sectorsdCardFatBootSector.sector_size) { + if( readSector(clusterSectorStart+sector,(unsigned char *)address) == FAILURE ) { + printf("Read Error\n"); + return FAILURE; + } + bytesRemaining=bytesRemaining-sdCardFatBootSector.sector_size; + address=address+sdCardFatBootSector.sector_size; + } else { + if( readSector(clusterSectorStart+sector,sdCardSector) == FAILURE ) { + printf("Read Error\n"); + return FAILURE; + } + memcpy((unsigned char *)address, sdCardSector, bytesRemaining); + bytesRemaining=0; + } + } + + // Move to next cluster + fileClusterStart=sdCardFatTable[fileClusterStart]; + } + printf("\n\n"); + return SUCCESS; +} +#endif