Reading FAT32 inside a FPGA core

Current FPGA boards have support for a sdcard but not all cores support it. There’re some classic cores (like Acorn Atom or the Spectrum) which do it with an extension ROM. But often the sdcard needs to be in a special format not readable by a PC which is also a problem for the MiST because it needs to load the core from a FAT filesystem and currently its not supported to change the sd card during runtime.
Another solution is to instantiate a second CPU which does the FAT reading and injects the code into RAM addressable by the core. But this comes with extra code because its like a second machine with own bios inside the fpga. Examples for that are the MSX and PC Engine port for the MiST.
Wouldn’t it be nice to keep requirements small and read a FAT filesystem directly in the core?

The FPGAmstrad core from Renaud Hélias raised the idea but I wanted to code my own module for learning purpose. But where to start? First I needed a library to easily read the sdcard block by block. I found code from XESS which was a good start (https://github.com/xesscorp/VHDL_Lib/blob/master/SDCard.vhd).
Next was a comprehend guide to understand FAT. A good summary with the most important bits are Paul’s 8051 Code Library.

The code is written around a FSM (finite state machine). I started with reading the first sector of the sdcard to determine the first partition. Before reading the first sector of the first partition the code checks if its a valid FAT filesystem and what type it is. The current code supports only FAT32 and one partition. But this should cope with most situations as today you can only buy SDHC cards but even SD cards can be formatted with FAT32.

Once we have the LBA of the first partition we read the first sector and derive some important values from there:

sd_factor is 512 for SD cards (byte addressing) and 1 for SDHC cards (block addressing).

With those values we know where to start reading the root directory and can search for the file we want to load. Subdirectories are also located here but the current code only supports reading from the root directory and from small directories as FAT reading for dirs isn’t supported yet 🙂
For FAT32 the directory entries are 32 bytes long and we jump from entry to entry until we’ve found our file.

Once found the entry contains the file size and the starting cluster. From there we can read the file sector by sector and cluster by cluster. For my 4GB SDHC card the cluster size was 4KB (8 sectors * 512byte). After reading a full cluster we need to find the next cluster via the FAT and so on.

The current code is still work in progress as it contains a lot of debug stuff and is missing some important bits:
– Reading longer directories via FAT
– Optimize code as it currently takes up to 12.000 LE’s on a Cyclone III (and I have no idea why)
– Support subdirectories
– Support writing would be nice but I don’t think that I get to it soon

At least it works which was a good start for me 🙂
The code is in my github repo as usual if you want to give it a try.
https://github.com/wsoltys/mist-cores/tree/master/misc/sdcard

Disclaimer: The code was written for learning purpose and might be highly inefficient. I’m open for any suggestions to optimize the code.

7 thoughts on “Reading FAT32 inside a FPGA core

  1. Thanks for the nice tutorial.

    In my experience designs with excessive logic requirements typically include some memory portions which for some reason the sythesis tool doesn’t place in dedicated block ram. The reason often is combinatorics which drive the memory. Running input and output via registers might help as well as the explicit usage of dedicated block ram.

  2. In FPGAmstrad I does use also FSM (finite state machine), but as I have too much states, I do use integer (with a range) and comments 🙂
    But as it does evolve, line numbers are now mixed, like in a Amstrad Basic program listing 😀

    I have several state machines, each time one is master of another slave one. Each state machine is in it’s own process.

    I does use some internal process functions to redondant stuff like… launching a command (in another process (in a slave process, from master process)) or computing a CRC.
    Functions are here for readability, for sample :
    * fillRAM(file_sector_pointer,file_address,conv_integer(file_size));
    * send_cmd(SET_BLOCKLEN,conv_std_logic_vector(BLOCK_SIZE,32-9) & “0” & x”00″);
    That’s the force of my implementation.

    I’ve got four layers :
    * SDRAM_SPIMASTER.vhd->native_send_cmd (execute one SPI CMD)
    * SDRAM_SPIMASTER.vhd->sangoku (read/write one byte at a certain address calling SPI CMD functions)
    * SDRAM_FAT32_LOADER.vhd->process spi_to_loader (read/write several bytes (1 2 4 or transmit to RAM/dump from RAM) at a certain address)
    * SDRAM_FAT32_LOADER.vhd->process tortue_geniale (FAT32 hello and use of files)

    Synchronous between process are done using 2 booleans (one for “do action” and another for “done confirmation”), surrounding each FSM (“do” for slave FSM, “done” for master FSM). I add then a process’s function (in master FSM’s process) about “master asking slave to do something”… in a clear way.

    For debug, I use the state machine key : an integer (with a range), displayed on leds, for sample :
    leds_tortue_geniale<=conv_std_logic_vector(step_var,8);
    (step_* is the name I give in fact to my state machine key (integer with a range))
    For debug, I use also error states in the state machine, for sample :
    when 27=>NULL; — bad root folder cluster
    This is another reason about using integer instead of NAMED_STATES in states machines.

  3. Alberto says:

    Hi, I’ve just found your code, that I’d like to incorporate into my FPGA project to read a file from a FAT32-formatted SDCARD but it’s impossible to fit it into the device, being it too large. After 5 years from the initial release have you figured out why? From some tests of mine I realized that your code blows up when combined with XESS’es SDCARD component, still I can’t find a suitable replacement for the sdcard controller. As your implementation is still the only one available, I’m asking you if you have devised any solution to reduce LE consumption.

    • WiSo says:

      This was a starter project to see if I can “port” software to logic elements and to understand FPGAs a little better. Unfortunately I’m out of programming FPGAs since then and didn’t improve it further. Normally you have a small ARM processor beside the FPGA which does this far better like on the MiST or MiSTer. So I’m sorry but can’t help here further.

  4. Alberto says:

    What I wrote before is confirmed: the growth of LEs is just due to the SdCardCtrl component from XESS you invoked in sdfat.vhd; your code doesn’t need any improvement. To test this, I performed a successful synthesis using “sdfat” with the “sd_controller” component (from “multicomp” by Grant Searle) modified to use SDHC cards (it’s difficult to find a 2GB card today) and a “wrapper” component I designed to implement the different block address logic and the missing signals (busy_o,hndShk_i,hndShk_o) your code is expecting. So, I could also remove all the references in “sdfat” to the XESS libraries and types. As my application is still in design stage, I’m unable to test if it actually works at the moment, so I don’t want to push modifications for the code you published on github. Moreover, as some time has passed since you published “sdfat” and I don’t know if you have already fixed the LE issue, let me know if you want to check my solution involving the replacement of the sdcard controller.

    • WiSo says:

      Nice work! As said I’m out of developing fpga stuff for many years now so I won’t be of much help. But feel free to send a pull request for fixes or additions when you’re ready. You could also send me a link to your repo or your project side so that I can update the post. Then people could at least find two sources of fpga sd card reading 😉

Leave a Reply

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