Flash FS - a filesystem for the FLASH chip on the P2 Edge Module.
The following files can be found at the top level of this repository:
Key Files | Description |
---|---|
flash_fs.spin2 |
The complete FLASH driver object |
flash_fs.txt |
Object Interface Document for the FLASH driver |
cgsm_flash_file_demo.zip |
Jon McPhalens latest Interactive Filesystem Demo |
flash_fs_demo.spin2 |
Chip's original light-weight non-interactive demo |
On this Page:
- Flash Filesystem Features - key features of this filesystem / design goals
- Adding the flash fs to your own project
- Contributing - you can contribute to this project
Additional pages:
- The flash_fs Object I/F Documentation - the object interface with documentation for each public method
- SPI FLASH Datasheet - our FLASH Chip Datasheet
- FS Theory of Operations - a detailed description of key concepts of this filesystem (See v1.x FS Theory of... for earlier version)
- Regression Testing Status - regression test code and output logs - growing as we certify each of the features (900+ tests so far)
Key features of this Flash Filesystem for the P2 Edge Flash chip:
- Wear-leveling write mechanism
- Writes are stuctured to facilitate recovery of file structure after unplanned power failure
- Block identification is independent of a blocks physical location
- Filenames are 127 characters plus zero terminator
- Seeks supported
- File Append supported
- Read-Modify-Write supported
- Circular file writes supported
- Supports multi-cog use (first cog to call mount() mounts the filesystem for all cogs to use.)
- Coming soon Directory Support
In addition to standard file operations this filesystem contains a couple features worth noting:
Circulr files are useful when you want to record the most recent journal entries (or event log entries) in a file.
One writes to circular files by appending to them. At file open, one specifies a maximum limit to the file size in bytes, for example:
status := flash.open_circular(@"filename", "a", 8_192)
... many writes ...
status := flash.close()
In this example you see a file being limited to 8k bytes. This means keep track of the last 8k bytes worth of journal or log entries. Upon close the journal will be reduced to the requested limit.
NOTE: Remember to call flash.flush()
periodically to keep the pending writes small enough so that you don't run out of flash space.
An open_circular(..., "r", 8_192)
is provided so that one can open the journal file and start reading at the front of the last 8_192 bytes in the file.
You can now modiify existing files on flash. Think of a file which contains fixed size records, maybe the state of an array of sensors where each sensor (or group of sensors) is in their own record within the file.
status := flash.open(@"filename", %"r+") ' alternatively use FILEMODE_READ_EXTENDED for %"r+"
... many reads and/or replacing writes ...
status := flash.close()
Open the file with the new %"r+" mode., then seek to the record you wish to replace and write it (replacing the record already there.) Continue by seeking to locations and writing new records. Of course you can seek to and read the existing records, too. Lastly close the file as normal.
We also provide an open mode of flash.open(@"filename", %"w+")
where you first have to write the new records before you can seek to them or replace them with new records.
Lastly we've added a flash.create_file()
method which you can use to create an initialized file with empty records. For example:
' create empty file
status := flash.create_file(@"filename", $00, 4_096)
' now do updates to the file
status := flash.open(@"filename", %"r+")
... many reads and/or replacing writes ...
status := flash.close()
In this example we are initilizing a file with 4k bytes of zeros. You can use any byte value you wish for the fill value.
There is NO migration needed if you are not using Circular Files.
If you are using Circular files and you need to preseve them instead of just creating new ones, then you will first want to copy your exising circular file to a new one. This creates the new file head block supporting circular seeks.
Example copy file code:
PUB exampleCopy(pOldFilename, pNewFilename) | status, rdHandle, rdLength, wrHandle, rdStatus, wrStatus, wrLength
' remove existing file if it exists
if flash.exists(pNewFilename)
status := flash.delete(pNewFilename)
ifnot flash.exists(pNewFilename)
debug("Abort: no file to copy")
' open existng file in basic "read" mode
rdHandle := flash.open(pOldFilename, "r")
' open new file with exact circular length of your orignal file
wrHandle := flash.open_circular(pNewFilename, "a", CIRCULAR_LEN_IN_BYTES)
repeat
rdLength := flash.read(rdHandle, @record, RECORD_LEN)
rdStatus := flash.error()
if rdStatus <> flash.SUCCESS and rdStatus <> flash.E_END_OF_FILE
debug("Abort: read error")
quit
wrLength := flash.write(wrHandle, @record, rdLength)
wrStatus := flash.error()
if wrStatus <> flash.SUCCESS
debug("Abort: write error")
quit
if rdStatus == flash.E_END_OF_FILE
debug("DONE: copy complete")
quit
while wrStatus == flash.SUCCESS
' and close our files
flash.close(rdHandle)
flash.close(wrHandle)
This section describes how to quickly get the flash filesystem working in your project.
Every time you wish to start a project using the flash filesystem you will want to download the latest version from the repository ironsheep/P2-FLASH-FS. In the list of files on the top page you will see the file flash_fs.spin2
. You can download this single file to get the latest or you can navigate to the releases page where you can download the latest release which includes demo files as well as the filesystem object.
Place the downloaded flash_fs.spin2
in your project and in your top-level object include the flash object:
OBJ { Objects Used by this Object }
flash : "flash_fs" ' the Flash Filesystem
The driver keeps about ~4250 bytes per open file. The space is allocated for two open files by default. You can override this in your top-level file by changing the include to something like:
OBJ { Objects Used by this Object }
flash : "flash_fs" | MAX_FILES_OPEN = 4 ' the Flash Filesystem
In this case we are telling the compiler to allocate room for 4 simultaneously accessable files instead of the default two files.
At the start of your application will need to mount() the filesystem.
PRI main() | status
status := flash.mount() ' start up the FLASH filesystem
if status <> flash.SUCCESS
'... you know that some blocks were corrupted and freed ...
If you know that that the flash chip in your P2 Edge module has never been formatted then you can call format instead of mount. (The format will then do the mount for you.)
PRI main()
flash.format() ' start up the FLASH filesystem
Now that your filesystem is mounted you are free to do normal file operations. The filesystem API is very similar to the ANSI-C file handling API so this all should feel pretty natural to you.
Review the Flash F/S Object I/F Documentation then start reading and writing files!
Please Enjoy! Stephen
This is a project supporting our P2 Development Community. Please feel free to contribute to this project. You can contribute in the following ways:
- File Feature Requests or Issues (describing things you are seeing while using our code) at the Project Issue Tracking Page
- Fork this repo and then add your code to it. Finally, create a Pull Request to contribute your code back to this repository for inclusion with the projects code. See CONTRIBUTING
Thank you to Chip Gracey for the excellent Flash File System 16MB written for the P2 which is the core of this fileysystem.
Thank you to Jon McPhalen for helping to specify the customer facing API for this filesystem and for guidance in our future directions for the upcoming dual filesystem driver as well. Jon also contributed his interactive filesystem demo.
Thank you also to members of the forum thread - "On-Board Flash File System" for also contributing ideas and guidance for implementation of this driver.
If you like my work and/or this has helped you in some way then feel free to help me out for a couple of ☕'s or 🍕 slices!
Parallax, Propeller Spin, and the Parallax and Propeller Hat logos are trademarks of Parallax Inc., dba Parallax Semiconductor
This project is a community project not for commercial use.
This project is in no way affiliated with, authorized, maintained, sponsored or endorsed by Parallax Inc., dba Parallax Semiconductor or any of its affiliates or subsidiaries.
Licensed under the MIT License.
Follow these links for more information: