Skip to content

Embedded Filesystems

BCG edited this page Apr 17, 2020 · 9 revisions

Background

Sometimes in an embedded application you might like to be able to read/write from/to some non-volatile memory. It is common for microcontrollers to be paired with SPI flash devices for this purpose, and while it is certainly possible to use such a device without a filesystem, files are a useful and familiar abstraction that can help free up a program from having to manage things like wear leveling, block/sector level erasure, etc.

Request for SPI flash and filesystem support was mentioned in this issue: https://github.com/tinygo-org/drivers/issues/45

Low level I/O driver for SPI NOR flash is available in TinyGo drivers since v0.12.0: https://github.com/tinygo-org/drivers/pull/124

SPI Flash

The tinygo.org/x/drivers/flash package provides a driver for initializing and performing read-write operations on a SPI flash device. In general, most applications should not need to use this package directly except to initialize a device and pass it on to a more high-level API such as a filesystem. This packages supports using either SPI (uses a single data line and is available on most/all targets) or QSPI (uses 4 data lines - "quad" - currently only supported on specific pins on ATSAMD51 targets). The package also has functionality to auto-detect the features of some SPI flash parts based on its JEDEC ID (see devices.go), such as maximum speed and whether or not QSPI is supported. If the part you're trying to use is not one that is known to the package, it is also possible to pass your own function to the library for provide details about the part's feature. See the DeviceIdentifier interface and the DefaultDeviceIdentifier variable in flash/devices.go for more information.

SPI

The flash package can be used with SPI on nearly any target that has a flash chip wired up to the right pins. Many development board have an integrated flash chip; for example any of the Adafruit "Express" M0 or nRF52 "Express" boards should be able to support this, as well as various Particle boards, and others. To interface with the chip via SPI takes the following steps:

import "tinygo.org/x/drivers/flash"

// SPI1 pins below should work for Itsy Bitsy M0 board; may need to adjust for other targets

var (
	// constructor for *flash.Device
	flashdev = flash.NewSPI(
		&machine.SPI1,
		machine.SPI1_MOSI_PIN,
		machine.SPI1_MISO_PIN,
		machine.SPI1_SCK_PIN,
		machine.SPI1_CS_PIN,
	)
)

func main() {

	// Initializes the flash chip and reads the JEDEC ID to determine its capabilities.
	// We didn't have to call machine.SPI1.Configure() because it is invoked internally
	// by flashdev.Configure().  Initially the package with configure the SPI interface
	// to run at 5 MHz to get the JEDEC ID; and once the max speed of the chip is known
	// it will be reconfigured to run at the maximum speed or 24 MHz, whichever is less.
	flashdev.Configure(&flash.DeviceConfig{
		Identifier: flash.DefaultDeviceIdentifier,
	})

}

(TODO: illustrate how to initialize a flash device using the flash driver package, also explain difference between SPI and QSPI peripherals)

Implementations

With low-level IO functions available, it is possible to begin experimenting with more high-level filesystem support. So far I've experimented with 2 filesystem implementations on top of the SPI flash driver:

Of the two, LittleFS is more full-featured and is what I would recommended for use with SPI flash, as it is far superior with respect to wear leveling and resiliency in case of power loss. FAT is not a great filesystem for use with flash memory, especially if you will be constantly be writing/overwriting files, because in that case you would be constantly erasing the same sectors/blocks on the device which will eventually destroy those sectors. That said, FAT is kind of the "lowest common denominator" when it comes to compatibility - Windows, OS X, and Linux all support creating/editing FAT filesystems without special drivers for instance, and nearly all SD cards are formatted this way, so it is useful to have support for FAT, especially for read-only or low-write use cases for which wear leveling is little or no concern.

LittleFS

LittleFS (https://github.com/ARMmbed/littlefs/blob/master/DESIGN.md) is a filesystem designed and optimized for embedded applications and flash memory, and is maintained as part of the ARM Mbed OS. I created CGo wrappers around most of the important functions in the C implementation provided by Mbed to provide a os package style interface from Go, which can be found at https://github.com/bgould/go-littlefs. The library is designed to be usable from both from TinyGo as well as standard Go, so the use of some CGo features that are not yet supported in TinyGo needed to be worked around. I would consider the status of this library to be "beta" at the moment (could be some bugs and/or may rework some of the API at some point) but it works and should be usable for common use cases like reading/writing config files or logging.

(to be continued...)

Clone this wiki locally