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 [2025-08-07 14:20] (current) – [Pseudo code for the NOF structure layer] format fixed mka | ||
|---|---|---|---|
| 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: | ||