SD Card Interfacing


Created By: Jason Brown & Brady Thornton (Group 12)

Revisions:

References (web links accessed on February 21, 2013):

Introduction:

We attempted two approaches to interfacing with the SD card, both of which are detailed below. Successful reading of the SD card was performed with Altera's University Program core (note that writing was not tested), but ultimately we found that read speeds were not as fast as we needed and so we abandoned this approach. It is included in this write up however, simply to document a few "gotchas" encountered along the way in case others attempt to use the core in the future.

The second interfacing approach (and ultimately the one we will use going forward) is using a 3-wire serial peripheral interface (SPI) for the hardware interfacing, and the Embedded File System Library (EFSL) with a NIOS-II endpoint as a HAL-like wrapper.


Altera University Program SD Card Interface

Altera's University Program cores can be downloaded from their website, however to save others the ~1.5 GB download the SD core and associated HAL for Quartus 10.1 are available here.

With the above files saved in the project directory, SOPC automatically added the core to the tree view on the left (otherwise the core could be imported via the Verilog HDL files). The core can easily be added to your system without the need to configure any parameters.

If your core is called "sd_card_interface" in SOPC, your top level VHDL mappings in the system port map (with all the appropriate signals and pins declared elsewhere) will look something like:

	b_SD_cmd_to_and_from_the_sd_card_interface	=> SD_CMD,
	b_SD_dat3_to_and_from_the_sd_card_interface	=> SD_DAT3,
	b_SD_dat_to_and_from_the_sd_card_interface 	=> SD_DAT,
	o_SD_clock_from_the_sd_card_interface 		=> SD_CLK 
			

The following sample code using the HAL initializes the device, scans the FAT file system, and prints out all the files and associated data (file name, size, contents as chars, etc.):

	#include <altera_up_sd_card_avalon_interface.h>

	int main(void)
	{
	    alt_up_sd_card_dev *device_reference = NULL;
	    int connected = 0;  
	    
	    device_reference = alt_up_sd_card_open_dev(SD_CARD_INTERFACE_NAME);
	    if (device_reference != NULL)
	    {
		printf("Initialized. Waiting for SD card...\n");
		while(1)
		{
		    if ((connected == 0) && (alt_up_sd_card_is_Present()))
		    {
		        printf("Card connected.\n");
		        if (alt_up_sd_card_is_FAT16())
		        {
			    printf("FAT16 file system detected.\n");
			    
			    printf("Looking for first file.\n");
			    char * firstFile = "filenameunchanged";
			    alt_up_sd_card_find_first(".", firstFile);
			    printf("Volume Name: '%s'\n\n", firstFile);
			
			    short file;
			    while((file = alt_up_sd_card_find_next(firstFile)) != -1)
			    {
			        int contentCount = 0;
			        printf("===========================\n");
			        printf("Found file: '%s'\n", firstFile);                        
			        
			        short fileHandle = alt_up_sd_card_fopen(firstFile,false);
			        printf("File handle: %i\n", fileHandle);
			        
			        printf("Contents:\n");
			        short int readCharacter;
			        while ((readCharacter = alt_up_sd_card_read(fileHandle)) != -1)
			        {
			            printf("%c", readCharacter);
			            ++contentCount;
			        }
			        printf("\nContent size: %i", contentCount);
			        printf("\n===========================\n\n");                  
			    }
		        } 
		        else 
		        {
			    printf("Unknown file system.\n");
		        }
		        
		        connected = 1;
		    } 
		    else if ((connected == 1) && (alt_up_sd_card_is_Present() == false)) 
		    {
		        printf("Card disconnected.\n");
		        connected = 0;
		    }
		}
	    }
	    else
	    {
		printf("Initialization failed.\n");
	    }
	} 
			

Known issues:


3-Wire SPI with Embedded File System Library

The hardware interfacing approach here is to use the SOPC built-in 3-wire SPI to communicate with the SD card. This can be instantiated in SOPC by browsing in the tree to "Interface Protocols->Serial->SPI (3 Wire Serial)". Configure it as a master with one slave select (SS_n) signal, and your system's clock rate. Additionally, be sure to set the data width to 8 bits with the MSB first (though this may vary based on your application). All other values were left as the defaults.

With the SPI master named "sd_card_spi_master", your top level VHDL mappings in the system port map (with all the appropriate signals and pins declared elsewhere; note that all signals are outputs excepting the input MISO line) will look something like:

	MISO_to_the_sd_card_spi_master		=>	SD_DAT,
	MOSI_from_the_sd_card_spi_master	=>	SD_CMD,
	SCLK_from_the_sd_card_spi_master	=>	SD_CLK,
	SS_n_from_the_sd_card_spi_master	=>	SD_DAT3
			

The above pin mappings can easily be confirmed via a quick Google search for using SD cards in SPI mode, and regard toward the meanings of master-in-slave-out (MISO) and master-out-slave-in (MOSI) with respect to the SPI interface (recalling that our core is the master here, and the SD card is the slave).

Use of the SPI interface to talk to SD cards is difficult without a wrapper of some sort, as certain sequences of commands must be sent to the SD card to set it in SPI mode, and a very specific protocol must be followed to communicate further read/write commands correctly. To save ourselves the trouble, and to provide us with FAT16/32 filesystem support, we utilized the Embedded File System Library (EFSL) with an additional endpoint written for the NIOS-II processor and SD cards. This library and the necessary endpoints for our architecture (with slight modifications to #include paths so the whole of the library can be compiled from a single folder with a flat file structure within Eclipse), as well as some documentation on EFSL are available here.

The following sample code initializes the file system, reads the file "test.txt" into memory, and prints its contents:

	// All EFSL files are in a source folder "efsl" in the Eclipse project
	// with no further directory nesting underneath.
	#include <efsl/efs.h>
	#include <efsl/ls.h>

	int main(void)
	{
	    // Create EFSL containers
	    EmbeddedFileSystem efsl;
	    
	    char fileName[LIST_MAXLENFILENAME] = {"test.txt"};
	    File readFile;
	    
	    // Initialises the filesystem on the SD card, if the filesystem does not
	    // init properly then it displays an error message.
	    printf("Attempting to init filesystem");
	    int ret = efs_init(&efsl, SD_CARD_SPI_MASTER_NAME);
	    
	    // Initialize efsl
	    if(ret != 0)
	    {
		printf("...could not initialize filesystem.\n");
		return(1);
	    }
	    else
		printf("...success!\n");
	    
	    // You could do some scanning of the file system here using the UNIX-like
	    // API functions such as "ls_openDir(...)" and "ls_getNext(...). Reference
	    // the included PDF for the documentation to do such a thing. This example
	    // simply shows reading a file with a known filename.

	    // Open the test file
	    printf("\nAttempting to open file: \"%s\"\n", fileName);

	    if (file_fopen(&readFile, &efsl.myFs, fileName, 'r') != 0)
	    {
		printf("Error:\tCould not open file\n");
		return(1);
	    }
	    else
	    {
		printf("Reading file...\n");   
	    }

	    // Create a memory buffer to read the file into
	    euint8 *fileBuffer = malloc(readFile.FileSize * sizeof(euint8));
	    if (!fileBuffer)
	    {
		printf("malloc failed!\n");
		return(1);
	    }
	    
	    // Read all the file's contents into the buffer. See the file_fread(...) function
	    // for the ability to read chunks of the file at a time, which is desirable for
	    // larger files.
	    unsigned int bytesRead = file_read(&readFile, readFile.FileSize, fileBuffer);
	    
	    printf("%u bytes read from the file\n", bytesRead); 

	    // Close the file
	    if (file_fclose(&readFile) != 0)
	    {
		printf("Error:\tCould not close file properly\n");
		return(1);   
	    }
	    
	    // Print out the contents
	    printf("\nFile contents (in hex):\n");
	    int j;
	    for (j = 0; j < bytesRead; ++j)
	    {
		printf(" 0x%02X", fileBuffer[j]);
	    }
	    
	    // Free the file buffer memory
	    free(fileBuffer);

	    // Unmount the file system
	    fs_umount(&efsl.myFs);

	    return(0);
	} 
			

This next segment of sample code illustrates writing to a new file on the SD card:

	// All EFSL files are in a source folder "efsl" in the Eclipse project
	// with no further directory nesting underneath.
	#include <efsl/efs.h>
	#include <efsl/ls.h>
	
	#define MSG_LEN 6

	int main(void)
	{
	    // Create EFSL containers
	    EmbeddedFileSystem efsl;
	    
	    char fileName[LIST_MAXLENFILENAME] = {"out.txt"};
	    File outFile;
	    
	    // Initialises the filesystem on the SD card, if the filesystem does not
	    // init properly then it displays an error message.
	    printf("Attempting to init filesystem");
	    int ret = efs_init(&efsl, SD_CARD_SPI_MASTER_NAME);
	    
	    // Initialize efsl
	    if(ret != 0)
	    {
		printf("...could not initialize filesystem.\n");
		return(1);
	    }
	    else
		printf("...success!\n");
	    
	    // Open/create the output file. Check the documentation for
	    // possible error codes (e.g. file already exists, etc.)
	    printf("\nAttempting to open file: \"%s\"\n", fileName);

	    if (file_fopen(&outFile, &efsl.myFs, fileName, 'w') != 0)
	    {
		printf("Error:\tCould not open file for output\n");
		return(1);
	    }
	    else
	    {
		printf("File open for output...\n");   
	    }

	    // Set up the data we want to write out. We'll use two strings here.
	    char msg1[MSG_LEN] = {"Hello "};
	    char msg2[MSG_LEN] = {"World!"};

	    // Write each message, taking care to store the return value, which is the
	    // offset for the next write. In real code, be sure to check that the offset
	    // is not 0 each time, indicating a write error.
	    unsigned int offset1 = file_fwrite(&outFile, 0, MSG_LEN, msg1);
	    unsigned int offset2 = file_fwrite(&outFile, offset1, MSG_LEN, msg2);

	    if (!offset1 || !offset2)
            {
                printf("Error:\tWrite did not succeed\n");
                return(1);   
            }
            else
                printf("Output written successfully.\n");

	    // Close the file
	    if (file_fclose(&outFile) != 0)
	    {
		printf("Error:\tCould not close file properly\n");
		return(1);   
	    }

	    // Unmount the file system
	    fs_umount(&efsl.myFs);

	    return(0);
	} 
			

Known issues: