en:pfw:file_systems
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
en:pfw:file_systems [2023-09-04 18:14] – gelöscht - Externe Bearbeitung (Unbekanntes Datum) 127.0.0.1 | en:pfw:file_systems [2024-05-09 16:46] (current) – [NOF structure] willem | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ===== NOF file system, for small Flash chips ===== | ||
+ | |||
+ | ---- | ||
+ | |||
+ | ==== NOF idea ==== | ||
+ | |||
+ | Read and store data from/to source files on some sort of background medium. | ||
+ | For these purposes a file system could be used. | ||
+ | There are lots of variants present: '' | ||
+ | This design is about a compact file system for small controllers and Flash memory chips. | ||
+ | It uses [[en: | ||
+ | NOF was developed on top of noForth and stands for **nO**Forth **F**ile system. | ||
+ | It can ofcourse be used on any system with limited resources. | ||
+ | ---- | ||
+ | |||
+ | ==== NOF structure ==== | ||
+ | |||
+ | * The reading is done on a per-byte basis | ||
+ | * Write goes in 256-byte sectors | ||
+ | * Erasure is done with sectors of 4096 bytes, or ... the entire chip at once | ||
+ | * The first 4096 bytes is the '' | ||
+ | * The '' | ||
+ | * A file has a size of 4096 bytes or a multiple of that | ||
+ | * Each file-ID is 8-bytes, a directory-ID is 16-bytes | ||
+ | * The first 32 bytes of a file contains the file name, '' | ||
+ | |||
+ | A 2-Mbyte Flash has 512 pages of 4096 bytes. | ||
+ | When the first sector is used for the '' | ||
+ | The '' | ||
+ | This means there is space for 384 files and 64 directories. | ||
+ | When you are in need for more files and directories, | ||
+ | of the Flash chip for administration. | ||
+ | |||
+ | **File ID record** | ||
+ | < | ||
+ | 2 Bytes 2 Bytes 2 Bytes 2 Bytes (8 bytes) | ||
+ | ---------------------------------------------------------- | ||
+ | | sector number | dir hash | filename hash | file length | ( max. 384 files ) | ||
+ | </ | ||
+ | **Directory ID record** | ||
+ | < | ||
+ | 2 Bytes 2 Bytes 12 bytes (16 bytes) | ||
+ | ------------------------------------------------- | ||
+ | | Dir-hash | Working-Dir-hash | Dir-name string | ( max. 64 directories ) | ||
+ | </ | ||
+ | **File header & first file record** | ||
+ | < | ||
+ | Byte Byte 30 bytes 4064 or more bytes | ||
+ | |-------------------------------| --------------------- | | ||
+ | | FIT | Aux | Filename 30 bytes | File data | | ||
+ | </ | ||
+ | |||
+ | ==== NOF basic implementation parts ==== | ||
+ | |||
+ | - An SPI-driver & setup for the Flash chip | ||
+ | - Operating commands for the Flash chip (Write, Read, Erase, etc.) | ||
+ | - Some tools ('' | ||
+ | - Multiple file buffers | ||
+ | - Directory stack | ||
+ | - '' | ||
+ | - Convert a string to 16-bits CRC | ||
+ | - Choose & show directory | ||
+ | - Find file & include file | ||
+ | - Write, Read & Erase given sectors | ||
+ | - Build '' | ||
+ | |||
+ | Later on some more additions, like: | ||
+ | |||
+ | - Store ASCII files | ||
+ | - File viewer | ||
+ | - Remove files, wipe complete disk | ||
+ | - Make directory and a directory '' | ||
+ | |||
+ | {{pfw: | ||
+ | ==== Pseudo code for the NOF SPI layer ==== | ||
+ | |||
+ | More detailed info on SPI all numbers in hexadecimal | ||
+ | |||
+ | <code forth> | ||
+ | Function: FSPI-OUT | ||
+ | Function: FSPI-IN | ||
+ | Function: FSPI-SETUP ( +f -- ) \ Initialise SPI-bus to Flash with frequency +f | ||
+ | Function: {FL ( b -- ) \ Enable Flash access & send first byte | ||
+ | Function: FL} ( -- ) \ Close Flash access | ||
+ | |||
+ | Function: {FREAD | ||
+ | {fl split 32bit into two 16bit hi part on top | ||
+ | split 16bit into two 8bit part hi part on top | ||
+ | fspi-out | ||
+ | |||
+ | Function: FREADY? | ||
+ | 5 {fl | ||
+ | |||
+ | Function: WRITE-ON | ||
+ | Function: BUSY ( -- ) | ||
+ | Function: CHIP-ERASE ( -- ) | ||
+ | Function: POWER-UP | ||
+ | Function: FC@ ( a -- b ) 3 | ||
+ | Function: FC@+ ( a -- a+1 b ) dup 1 + swap fc@ | ||
+ | Function: F@ ( a -- x ) 3 {fread | ||
+ | Function: F@+ ( a -- a+2 x ) dup 2 + swap F@ | ||
+ | Function: FTYPE ( a u -- ) 0 ?DO fc@+ emit LOOP drop | ||
+ | |||
+ | Function: ID. ( -- ) 0 90 {fread | ||
+ | Save number base & set it to decimal | ||
+ | |||
+ | Function: FDUMP ( a u -- ) | ||
+ | < | ||
+ | 0 ?DO new line and print address followed by ':' | ||
+ | show 10 bytes from flash memory in hexadecimal | ||
+ | show 10 bytes as ASCII or a space when not a valid character | ||
+ | Add 10 to address | ||
+ | Stop when a key was pressed | ||
+ | LOOP drop address | ||
+ | |||
+ | </ | ||
+ | |||
+ | ==== Pseudo code for the NOF structure layer ==== | ||
+ | {{{{pfw: | ||
+ | < | ||
+ | |||
+ | Function: RWDATA | ||
+ | Function: !DATA ( x +n -- ) Store x op position +n in RWDATA | ||
+ | Function: @DATA ( +n -- x ) Read x from position +n in RWDATA | ||
+ | |||
+ | 0000 value # | ||
+ | 0000 value BUF ( Buffer in use 0/1 ) | ||
+ | 0000 value #N ( Byte index in sector buffer ) | ||
+ | | ||
+ | 0000 constant #FID ( Begin address of file ID block ) | ||
+ | 0C00 constant #DID ( Begin address of directory ID block ) | ||
+ | 1000 constant # | ||
+ | |||
+ | Function: INIT-RWDATA | ||
+ | Write flag is zero, collected data is zero, | ||
+ | store zero in #n and select buffer-0 | ||
+ | |||
+ | Function: ' | ||
+ | create DID and reserve 0C bytes RAM after it | ||
+ | value DPT ( Directory nesting pointer ) | ||
+ | value ' | ||
+ | value ' | ||
+ | value HASH> ( Hash working space ) | ||
+ | |||
+ | Function: .DFREE | ||
+ | Save number base & set it to decimal | ||
+ | Subtract #SECTOR from #FLASH and divide by 4 | ||
+ | to convert from sectors to kBytes, print it & restore base | ||
+ | |||
+ | Function: ROOT ( -- ) 0 did h! 0 to dpt | ||
+ | Function: DID> | ||
+ | |||
+ | Function: $> | ||
+ | Store string a1 u1 at 'name, save length | ||
+ | Convert string in 'name to uppercase | ||
+ | leave address of converted string & length | ||
+ | | ||
+ | Function: IHASH ( h -- ) Store h in HASH> | ||
+ | Function: > | ||
+ | Function: HASH ( a u h0 -- h1 ) Generate file hash h1 from given string a u & hash seed h0 | ||
+ | Function: DHASH ( 'did h0 -- h1 ) Generate dir hash h1 from string stored in 'did+2 & hash seed h0 | ||
+ | |||
+ | value FADDR | ||
+ | Function: FKEY ( -- c ) | ||
+ | faddr fc@ increase faddr ?dup ?exit -2 to source-id | ||
+ | |||
+ | Function: MOUNT ( -- ) | ||
+ | < | ||
+ | Token of FKEY to alternative KEY-vector | ||
+ | | ||
+ | | ||
+ | | ||
+ | store it in # | ||
+ | </ | ||
+ | |||
+ | ==== Pseudo code for the NOF directory mechanism ==== | ||
+ | {{pfw: | ||
+ | < | ||
+ | Function: CHECK-DHASH | ||
+ | Save current DIR-entry address | ||
+ | #did 'did over - bounds DO ( Loop true directory space ) | ||
+ | dup i f@ = IF ( Is there a hash match? ) | ||
+ | replace dir-entry address | ||
+ | Leave true and leave loop immediately | ||
+ | THEN | ||
+ | 10 +LOOP ( To next DIR-entry ) | ||
+ | drop false ( No directory found ) | ||
+ | | ||
+ | Function: .DIR ( did -- ) | ||
+ | When did is zero its the root show that ans ready | ||
+ | check-dhash IF | ||
+ | Read dir-entry address and print it's name | ||
+ | THEN | ||
+ | |||
+ | Function: .PATH ( -- ) | ||
+ | Get DID read DPT pointer and print | ||
+ | the current directory nesting in correct order | ||
+ | |||
+ | Function: > | ||
+ | Limit string to 0E characters ( 00 to 0D ) | ||
+ | Generate root hash value h0 and current DIR hash value h1, leave both | ||
+ | |||
+ | Function: CHOOSE-DIR | ||
+ | > | ||
+ | Is it a dot then go back one directory nesting when possible | ||
+ | check-dhash and abort when it's an invalid directory | ||
+ | Otherwise extend directory nesting and save current DIR record address | ||
+ | |||
+ | Function: CD ( " | ||
+ | Set a new directory nesting by unraveling the given string by parsing it with | ||
+ | backslashes. When there is a backslash in front start in the root. | ||
+ | When the string is done, show the new directory path using .PATH | ||
+ | |||
+ | Function: .FIT ( +n -- ) | ||
+ | Print file type +n as an ASCII string, +n comes from the 20 bytes file header | ||
+ | |||
+ | Function: DIR ( -- ) | ||
+ | 1) Print current dir path with .PATH | ||
+ | 2) Read current directory entry from DID and print all | ||
+ | | ||
+ | Print the directory names that give a valid hash code | ||
+ | 3) Now print the files names, by checking if it's dir-hash code is the | ||
+ | same as the one stored in DID if so print the file type and the name | ||
+ | from the 20 bytes file header | ||
+ | 4) Finally show the free space on the Flash disk using .DFREE | ||
+ | </ | ||
+ | |||
+ | ==== Pseudo code for the NOF file search function ==== | ||
+ | {{pfw: | ||
+ | < | ||
+ | Function: CHECK-FHASH | ||
+ | #fid 'fid bounds ?DO ( Setup search range ) | ||
+ | Read dir hash value from second cell in FID entry | ||
+ | is it equal to the contents of DID | ||
+ | IF Read the the file hash from the third cell | ||
+ | when equal, save this FID record | ||
+ | leave true and were ready | ||
+ | THEN | ||
+ | THEN | ||
+ | 8 +LOOP ( To next FID entry ) | ||
+ | file hash not found, leave false | ||
+ | | ||
+ | Function: > | ||
+ | Limit string to 1D chars, do: $>upc DID h@ dhash | ||
+ | | ||
+ | Function: ' | ||
+ | > | ||
+ | Read the files start sector, leave true & ready | ||
+ | THEN print "File not found" & leave false | ||
+ | | ||
+ | Function: LOADFILE | ||
+ | source-id greater then zero, nest file address pointer | ||
+ | nest source-id | ||
+ | set source-id to 1 | ||
+ | call interpreter, | ||
+ | unnest source-id | ||
+ | when source-id is greater then zero unnest faddr & save | ||
+ | |||
+ | Function: SEEK ( " | ||
+ | read blank delimited string " | ||
+ | ' | ||
+ | | ||
+ | Function: INCLUDE | ||
+ | abort when used in a definition | ||
+ | read blank delimited string " | ||
+ | check if it's a forth file ans issue an error message when not | ||
+ | Calculate the files address & call: loadfile | ||
+ | | ||
+ | Function: FILE ( " | ||
+ | seek convert sector to file address | ||
+ | and compile it as a literal | ||
+ | | ||
+ | </ | ||
+ | |||
+ | ==== Pseudocode for creating a file header ==== | ||
+ | |||
+ | {{pfw: | ||
+ | < | ||
+ | Create: ' | ||
+ | Define: ADDR-SECTOR | ||
+ | split sa in low- & high-byte | ||
+ | | ||
+ | Define: BUFFER | ||
+ | 1 and 100 * ' | ||
+ | | ||
+ | Define: READ-SECTOR | ||
+ | >r 3 {fl addr-sector | ||
+ | 100 0 do fspi-in | ||
+ | |||
+ | Define: WRITE-SECTOR | ||
+ | >r write-on | ||
+ | 100 0 do count fspi-out | ||
+ | |||
+ | Define: ERASE-SECTOR | ||
+ | write-on | ||
+ | |||
+ | Define: B, ( b -- ) | ||
+ | #n 100 = IF set buffer full flag, select next buffer and clear #n THEN | ||
+ | store b in current buffer & increase pointer #n | ||
+ | |||
+ | Define: WR-BUFFER | ||
+ | Write buffer-0 to #sector and increase #sector | ||
+ | |||
+ | Define: < | ||
+ | When buffer overflow has occured do: wr-buffer | ||
+ | and clear buffer full flag, copy buffer overflow | ||
+ | from buffer-1 to buffer-0, slect buffer-0 again | ||
+ | |||
+ | Define: !FLENGTH | ||
+ | Read last used sector, round it to next 10th sector | ||
+ | Read current part of FID sector to buffer-1 | ||
+ | Patch calculated file length into it | ||
+ | Write buffer-1 back to current FID sector | ||
+ | |||
+ | Define: FILE-NAME | ||
+ | init-rwdata | ||
+ | Calculate & check file hash value, abort when the hash value is not unique | ||
+ | Calculate, save & read current FID-sector in buffer-0 | ||
+ | Save address of new FID record too | ||
+ | Store DID in record, after that the file-hash | ||
+ | Now write FID sector back & add 8 to 'FID | ||
+ | Now store file type on address 0 in buffer-0 | ||
+ | after that the (limited) file name string | ||
+ | Set #n to 20 and ready | ||
+ | |||
+ | Define: ADD-FID | ||
+ | Read next word from input stream and build a new file entry with it | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | |||
+ | ==== NOF files ==== | ||
+ | |||
+ | The added NOF example files are for noForth R on the GD32VF103, especially the [[https:// | ||
+ | |||
+ | | File name | Purpose | in Dropbox (external link) | | ||
+ | | [[https:// | ||
+ | | [[https:// | ||
+ | | [[https:// | ||
+ | | [[https:// | ||
+ | |||
+ | [[en: | ||