Simple SD Card Interfacing

Recently I had to log some data to an SD card using an Altera FPGA on a Terasic DE4, and I was pleasantly surprised at how simple it was. Definitely room for improvement, as I’ll discuss later, but for now this seems good enough!

Requirements

First things first, you’ll need your DE board (I was using a DE4, but a DE0 will work just as well), with some recent version of Quartus (I was using Quartus II 11.0 full, but the web editions work too), Nios SBT for Eclipse (coming with the Quartus install), and the University Program files installed. If you don’t have those, you can get them for your version of Quartus here.

Nios II System

The basic Nios II setup is straightforward. You’ll need your clock (of course), the core itself (e core works fine), a reasonable amount of on-chip memory (if you’re only reading/write to SD 32kB is sufficient. Alternatively, use the off-chip memory/SDRAM), a JTAG/UART module for STDIN/OUT, an interval timer for timing transfer rates, if you like, and the sys id peripheral (so you know your code is running on the right processor build!). That gives you a basic Nios II system.

On top of this, add the SD Card Interface (University Program > Memory > SD Card Interface), Wire up the avalon_sdcard_slave to the CPU data master, the clock_sink to your clock source, and the clock_reset_sink to the clock source reset signal. Finally, export the conduit (you might want to change the name to something a little shorter…). Save yours system and generate. Your system should look like this:

nios_qsys_sd_setup

Well that was easy! Now to create our top level file, instantiate the system, and write a little C code (and I do mean a little!) to get our SD access going.

The HDL

For demo purposes, the top level file only instantiates the Nios processor. I’m tying reset_n high here for simplicity, you can always assign that to a DIP switch or push button.

module SDC_Top (	
			input 	CLOCK_50, 
			inout 	SD_DAT, 
			inout 	SD_CMD, 
			inout 	SD_DAT3, 
			output 	SD_CLOCK
		);

	sdc_sys0 u0 (
        .clk_clk            (CLOCK_50),
        .reset_reset_n      (1'b1),
        .sd_card_b_SD_dat   (SD_DAT),
        .sd_card_o_SD_clock (SD_CLOCK),
        .sd_card_b_SD_cmd   (SD_CMD),
        .sd_card_b_SD_dat3  (SD_DAT3)
    );

endmodule

Run analysis/synthesis, assign the appropriate pins, and then run a full compilation. If all goes well, program your board.

The Software

The final step is to take your system and write some software in Eclipse. Once again you’ll be using the Altera UP header file for SD Card access. Here’s a sample C file that does just that, and shows off a couple extra function calls to get some information about the card. (main.h just defines SD_BUFFER_SIZE as 512).

#include <stdio.h>
#include <system.h>
#include <altera_up_sd_card_avalon_interface.h>

#include "main.h"

short int sd_fileh;

char buffer[SD_BUFFER_SIZE] = "WELCOME TO THE INTERFACE!!\r\n\0";

int main()
{
  printf("SD Card Access Test\n");

  alt_up_sd_card_dev *sd_card_dev = alt_up_sd_card_open_dev(ALTERA_UP_SD_CARD_AVALON_INTERFACE_0_NAME);

  if(sd_card_dev != 0)
  {
	  if(alt_up_sd_card_is_Present())
	  {
		  if(alt_up_sd_card_is_FAT16())
			  printf("Card is FAT16\n");
		  else
			  printf("Card is not FAT16\n");

		  sd_fileh = alt_up_sd_card_fopen("file.txt", true);

		  if (sd_fileh < 0)
			  printf("Problem creating file. Error %i", sd_fileh);
		  else
		  {
			  printf("SD Accessed Successfully, writing data...");
			  int index = 0;
			  while (buffer[index] != '\0')
			  {
				  alt_up_sd_card_write(sd_fileh, buffer[index]);
				  index = index + 1;
			  }
			  printf("Done!\n");

			  printf("Closing File...");
			  alt_up_sd_card_fclose(sd_fileh);
			  printf("Done!\n");
		  }
	  }
  }

  return 0;
}

Assuming everything else is set up correctly, you should be good to go! Just launch (run as Nios II hardware), and once it finishes, you should be able to remove the card and see the textfile test.txt on another system. Cool!

Common Problems

I said “assuming everything else is set up correctly” …. but what does that mean? Well, for one thing, you need the SD card pre-formatted as FAT16. Archaic, I suppose, but that’s what is implemented in the UP header files.

You can have directories in the filename, but a leading \\ or / will cause the code to fail. Root directory by default doesn’t have a /. (i.e. it’s just “test.txt”, not “/test.txt”). For subdirectories, write something like “root/sub0/sub1/test.txt”.

Also, make sure your filename is at most 8 characters long. There’s a hard 8B limit here, so watch out for long file names.

Other problems might be the fopen command returning an integer less than 0. Two possibilities are -1 and -2; -1 means the file cannot be opened or created, -2 means it’s already open. For more info, refer to the SD IP Core documentation.

Don’t expect much speed out of the built-in subroutines. The read function returns a byte of data at the current file position. The core itself supports a variety of commands, including reading a 512 byte block (512 bytes is size of TX/RX buffer in the SD card), so only one command needs to be issued for a cluster, instead of for each byte. That’ll take a little more work, and may be the subject of future blog posts, if the project requires it :).

Finally, remember you’ve got a 2GB limit here. The cards must be formatted FAT16, as I said, but the cluster size is 32kB. With 16 address bits, you have 65536 regions * 32kB = 2GB. I imagine this will be sufficient for most embedded applications. I’ve seen some neat photoviewers using this SD access technique on the NEEK. Maybe I’ll save up for it (actually, $499 is a bit much…)

Conclusion

That’s it! Overall a painless process. Took under an hour to figure out and get working. For once, the documentation was actually clear! That being said, Eclipse does have its quirks. Maybe I’ll write about those later…

Hope this was helpful!

Comments 24

  • So, how do you go about opening and reading chunks of data of an entire 32MB file on the USB drive?

    • In general you will need to open the file to get the file handle, then use a read function. If using C this will just be fread or similar. If using Nios 2 with the UP driver as above, the read function prototype is:

      short int alt_up_sd_card_read(short int file_handle)

      This reads a single byte of data from the file and increments the position in the file. If the read is unsuccessful it will return a negative value, so to read the entire file just keep reading while the return value is >0.

      Hope that helps!

      • I got it working..now the bigger challenge for me is getting the DDR2 SIM that comes with my Altera TeraAsic DE3 board working..got it compiled into a Qsys system and burned the FPGA but for some reason my elf file isn’t downloading (and I also didn’t meet the DDR2 timing says Quartus)…tech support from Terasic is not so good, they only have alteraforums linked to their site..

  • My project didn’t require that, but on a different platform I’ve used sdfatlib for SD access. It’s an Arduino library, but it is in C, and they have samples of raw sector read and write that might come in handy!

  • University SDCard controller it can access JPEG image from sdcard it possible to display the image on VGA Monitor.

    • Yes – you can read the JPEG image off the SD card using this method. To display the JPEG you will need to first decode it. You may want to take a look at the OpenCores projects , I haven’t checked, but something there may be of use, maybe in the Video Cores section. Hope that helps!

  • Okay first of All I must thank you so much!
    Because I have been searching on how to access an SD card on DE2
    And I could not find anything useful until now.
    But I have a question though, if you do not mind.
    My project is about playing different songs from different switches or keys
    ie, we want to produce the sound of the drums from different keys which means assigning each switch to play one file from the SD card.
    Do you know any useful tip that I can use there?
    Thank you so much in advance.

    • You’re very welcome! Glad I could help. Now for the audio…if I recall correctly, I think in the DE2 CD (the one that ships with the board) there are demos for a karaoke machine, SD card music player, and audio synthesizer… all would be good starting points. If you don’t have the CD, check the Terasic website – I’ve downloaded it from there in the past :). Good luck with your project!

  • I m using DE0 board…. i’m using SDRAM instead of SRAM. I have been used SDCARD controller for reading the Bmp image . it successfully written to the SDRAM almost got the output but the image displayed it on reverse in the VGA.
    Image size is 320 *240 and 24 bit image … . this is my coding. what will be the problem not getting the exact image….

    // BMP Image….
    printf(“\nReading Bmp file”);
    for(x=0;x<54;x++){
    header=(unsigned char)(alt_up_sd_card_read(sd_fileh));
    }
    //printf("%ld",image_size);

    for(i=0;i<240;i++){for(j=0;j>3)<>2)< >3)<<0);
    IOWR_16DIRECT(SDRAM_BASE,(((i+80)*640+(j))),pixel);
    }

    • Did you ever have any luck with this? If you know the VGA driver is working correctly (hardware, I mean), and it’s just flipping the image, I’d check your addressing, array bounds, and file format to make sure everything is consistent.

  • hi. i used this tutorial in accessing sd card through sopc builder and nios2 and i was successful in doing so. Now, i want to read a .bmp image from sd card and save it to sdram for further processing. how can i achieve this? can you give a sample code? please i need you help. thanks in advance. :)

    • Glad to hear you were successful! Is the SDRAM being used as the main memory in your system? If so, when you read the bmp image from the SD card, you will be reading it into the SDRAM and you can process as you wish. The bitmap format is pretty easy to work with, here’s a wikipedia article giving an . There’s a good amount of open source C code online for reading a bitmap file, too. Let me know how it goes, if you get stuck I may be able to help.

  • Nice post. I learn something totally new and challenging on websites I stumbleupon every day.
    It’s always interesting to read content from other authors and
    practice something from other sites.

  • Hello Rob! Thank you so much for this information. I just have one question though. With a cluster size of 32kB, there is a 2GB limit to the card. So, if I format the SD card and change the cluster size to 64kB, does that make the limit 4GB ?

    • You’re welcome! The short answer (I believe) is yes, but I’m not positive if the HAL driver would support that, you may want to look in the is_fat_16() function to see how that works internally. The 2GB limit comes from FAT16 using an 8-bit signed number to represent the number of sectors, and by default this is 64 (why they used signed for the number of sectors is beyond me…). For a standard FAT16 implementation,(as I believe the HAL driver is), that’s what will be supported. But..you never know!

      If it’s not supported, another option (although, I’ve never tried it on Altera/Nios, but I have had success with Arduino/Teensy), could be using sdfatlib. If you have the code memory for it (the library can be quite large), and require several GB of storage, it might be the way to go! (If you do try this, I’d love to hear about your results!)

  • Thanks for the awesome post!! I was searching around for SD-Card interfacing this is practically the best tutorial i could get :)
    Btw is it possible to do this without using the niosII system. I don’t mind using SD-Card Interface module but the niosII processor is a bit too much for my system, ie. I need space for some other heavy computation and implementing niosII just for sd interface is a bit overkill

    • You’re welcome, and thank you! :)

      It is possible without the Nios system! The Altera UP SD card module actually comes with a “stand alone” version that doesn’t require processor input…but I find it much easier to use the Nios system. If speed isn’t an issue, can you use just the E core? It doesn’t take up too much room (< 700 logic elements). What FPGA are you using? If it's a Cyclone III (DE0 board) there are over 15k logic elements, so the nios 2/e core is <5% resource consumption. Either way best of luck with your project!

  • Hi Rob,
    Thank u so much for this great tutorial .Based on this i’ve learned so much. I did it access the BMP image from SDcard it get displayed on the VGA perfectly..

    • Wonderful! Great to hear you were successful! :) I’m curious, the image in the SD card, is the resolution the same as VGA resolution? Or do you have some kind of image scaling enabled?

      • The image in the SDcard having the same resolution as VGA. I hve tried to use scalar bt not working properly..

        • Hi Balakumar, i can’t access the BMP image from SDcard it get displayed on the VGA perfectly. So you can help you this problem, i need your project, how i can contact you? Please help me…

  • There go my additional 5 cents on a couple of issues concerning FAT16 formatting and reading binary files.

    BTW, documentation talks about setting up tco and tsu time constraints. No need for me, worked like a charm.

    I tried to format the SD Card as FAT16 telling Windows 7 to format as FAT (both in Explorer and Command line), but the Altera UP driver did not identify a valid FAT16 partition.

    Diskpart utility worked for me, by creating the partition from scratch after deleting whatever was on the disk.

    0. Open an elevated CMD (“Run as administrator”)
    1. From Command prompt type diskpart
    2.Within Diskpart, use the List Disk command to check which disk the USB device is. The USB device should be obvious; in my case, it’s the 14GB disk.
    This was copy pasted from somewhere else, I was using a 512MB SD Card which showed up as disk 1.
    DISKPART> list disk
    Disk ### Status Size Free Dyn Gpt
    ——– ————- ——- ——- — —
    Disk 0 Online 232 GB 1024 KB
    Disk 1 Online 149 GB 0 B
    Disk 2 Online 149 GB 7168 KB
    * Disk 3 Online 14 GB 0 B
    3 Once the disk is known, select the disk and run the rest of the displayed commands (change “disk 3” to the number of your disk):
    DISKPART> select disk 3
    Be very careful with what you select, as the next clean will wipe the disk.
    Disk 3 is now the selected disk.
    DISKPART> clean
    DiskPart succeeded in cleaning the disk.
    DISKPART> create partition primary
    DiskPart succeeded in creating the specified partition.
    DISKPART> select partition 1
    Partition 1 is now the selected partition.
    DISKPART> active
    DiskPart marked the current partition as active.
    DISKPART> format fs=fat quick
    100 percent completed
    DiskPart successfully formatted the volume.
    I actually did not use quick for a 512MB SD Card (which was a Micro SD on a much cheapness DX.com SD card USB reader)
    DISKPART> assign
    DiskPart successfully assigned the drive letter or mount point.
    DISKPART> exit

    If you intend to read binary files (that is, containing bytes from 0x00 to 0xFF and not only the ASCII characters from 0x20 to 0x79), be careful with function
    short int alt_up_sd_card_read(short int file_handle);
    which is found in altera_up_sd_card_avalon_interface.c of the BSP project.
    After doing all the sector, cluster and offset required magic, it reads the character with
    ch = (char) IORD_8DIRECT(buffer_memory, (active_files[file_handle].current_byte_position % 512));
    This means than any value from 0x80 to 0xFF will be returned as a negative value, which should be considered as EOF (-1) or an error (from -128 to -2)
    The easy woraround is to just remove the (char) type cast and get the actual value from 0x00 to 0xFF. It is compatible with the -1 for EOF, and in general worked for me.

Leave a Reply to Balakumar Cancel reply

Your email address will not be published. Required fields are marked *