Announcement: FreeRTOS Symmetric Multiprocessing (SMP) +FAT running on Raspberry Pi Pico

I have a project (carlk3/FreeRTOS-FAT-CLI-for-RPi-Pico) running on the official SMP FreeRTOS RP2040 port (FreeRTOS/FreeRTOS-Kernel) on Raspberry Pi Pico.

I have successfully tested running five Tasks running on two cores simultaneously doing I/O to two SD cards. I did this by modifying vMultiTaskStdioWithCWDTest in FreeRTOS-Plus/FreeRTOS-Plus/Demo/Common/FreeRTOS_Plus_FAT_Demos/test/ff_stdio_tests_with_cwd.c to write to two different mountpoints. It looks something like this:

vCreateAndVerifyExampleFiles(pcMountPath=/sd0/0)
prvCreateDemoFilesUsing_ff_fwrite(pcMountPath=/sd0/0)
core0: FS0: Creating file root001.txt in /sd0/0
vCreateAndVerifyExampleFiles(pcMountPath=/sd0/2)
prvCreateDemoFilesUsing_ff_fwrite(pcMountPath=/sd0/2)
core1: FS2: Creating file root001.txt in /sd0/2
vCreateAndVerifyExampleFiles(pcMountPath=/sd1/1)
prvCreateDemoFilesUsing_ff_fwrite(pcMountPath=/sd1/1)
core0: FS1: Creating file root001.txt in /sd1/1
vCreateAndVerifyExampleFiles(pcMountPath=/sd1/3)
prvCreateDemoFilesUsing_ff_fwrite(pcMountPath=/sd1/3)
core0: FS3: Creating file root001.txt in /sd1/3
core1: FS0: Creating file root002.txt in /sd0/0
core0: FS2: Creating file root002.txt in /sd0/2
core1: FS1: Creating file root002.txt in /sd1/1
core0: FS3: Creating file root002.txt in /sd1/3
vCreateAndVerifyExampleFiles(pcMountPath=/sd0/4)
prvCreateDemoFilesUsing_ff_fwrite(pcMountPath=/sd0/4)
core0: FS2: Creating file root003.txt in /sd0/2
core1: FS4: Creating file root001.txt in /sd0/4
core1: FS0: Creating file root003.txt in /sd0/0
core0: FS1: Creating file root003.txt in /sd1/1
core1: FS2: Creating file root004.txt in /sd0/2
core0: FS3: Creating file root003.txt in /sd1/3
core0: FS4: Creating file root002.txt in /sd0/4
core1: FS0: Creating file root004.txt in /sd0/0
core0: FS1: Creating file root004.txt in /sd1/1
core0: FS4: Creating file root003.txt in /sd0/4
core0: FS3: Creating file root004.txt in /sd1/3
core0: FS2: Creating file root005.txt in /sd0/2
core1: FS0: Creating file root005.txt in /sd0/0
core0: FS1: Creating file root005.txt in /sd1/1
core0: FS4: Creating file root004.txt in /sd0/4
core1: FS3: Creating file root005.txt in /sd1/3
prvVerifyDemoFileUsing_ff_fread()
core1: FS2: Reading file root001.txt from /sd0/2
prvVerifyDemoFileUsing_ff_fread()
core0: FS1: Reading file root001.txt from /sd1/1
prvVerifyDemoFileUsing_ff_fread()
core1: FS2: FF_fopen(root001.txt): Success (0)
core1: FS0: Reading file root001.txt from /sd0/0
core0: FS1: FF_fopen(root001.txt): Success (0)
core0: FS0: FF_fopen(root001.txt): Success (0)
core1: FS2: Reading file root002.txt from /sd0/2
core0: FS1: Reading file root002.txt from /sd1/1
core0: FS4: Creating file root005.txt in /sd0/4
prvVerifyDemoFileUsing_ff_fread()
core0: FS0: Reading file root002.txt from /sd0/0
core1: FS1: FF_fopen(root002.txt): Success (0)
core0: FS3: Reading file root001.txt from /sd1/3
core1: FS2: FF_fopen(root002.txt): Success (0)
core0: FS1: Reading file root003.txt from /sd1/1
core1: FS0: FF_fopen(root002.txt): Success (0)
core1: FS3: FF_fopen(root001.txt): Success (0)
core0: FS2: Reading file root003.txt from /sd0/2
core0: FS1: FF_fopen(root003.txt): Success (0)
prvVerifyDemoFileUsing_ff_fread()
core1: FS0: Reading file root003.txt from /sd0/0
core0: FS2: FF_fopen(root003.txt): Success (0)
core0: FS3: Reading file root002.txt from /sd1/3
core0: FS1: Reading file root004.txt from /sd1/1
core0: FS4: Reading file root001.txt from /sd0/4
core0: FS0: FF_fopen(root003.txt): Success (0)
core0: FS2: Reading file root004.txt from /sd0/2
core0: FS3: FF_fopen(root002.txt): Success (0)
core0: FS1: FF_fopen(root004.txt): Success (0)
core1: FS4: FF_fopen(root001.txt): Success (0)
core0: FS0: Reading file root004.txt from /sd0/0
core0: FS2: FF_fopen(root004.txt): Success (0)
core0: FS3: Reading file root003.txt from /sd1/3
core0: FS1: Reading file root005.txt from /sd1/1
core0: FS4: Reading file root002.txt from /sd0/4
core0: FS0: FF_fopen(root004.txt): Success (0)
core0: FS2: Reading file root005.txt from /sd0/2
core0: FS3: FF_fopen(root003.txt): Success (0)
core0: FS1: FF_fopen(root005.txt): Success (0)
core1: FS4: FF_fopen(root002.txt): Success (0)
prvCreateDemoFileUsing_ff_fputc(pcMountPath=/sd1/1)
core1: FS0: Reading file root005.txt from /sd0/0
core0: FS2: FF_fopen(root005.txt): Success (0)
core0: FS3: Reading file root004.txt from /sd1/3
prvCreateDemoFileUsing_ff_fputc(pcMountPath=/sd0/2)
core1: FS4: Reading file root003.txt from /sd0/4
core0: FS1: In directory /sd1/1
core0: FS0: FF_fopen(root005.txt): Success (0)
core0: FS3: FF_fopen(root004.txt): Success (0)
core1: FS2: In directory /sd0/2
core0: FS4: FF_fopen(root003.txt): Success (0)
prvCreateDemoFileUsing_ff_fputc(pcMountPath=/sd0/0)
core1: FS0: In directory /sd0/0
core1: FS4: Reading file root004.txt from /sd0/4
core0: FS3: Reading file root005.txt from /sd1/3
core1: FS4: FF_fopen(root004.txt): Success (0)
core1: FS3: FF_fopen(root005.txt): Success (0)
core1: FS4: Reading file root005.txt from /sd0/4
prvCreateDemoFileUsing_ff_fputc(pcMountPath=/sd1/3)
core1: FS3: In directory /sd1/3
core1: FS4: FF_fopen(root005.txt): Success (0)
prvCreateDemoFileUsing_ff_fputc(pcMountPath=/sd0/4)
core1: FS4: In directory /sd0/4
core0: FS1: In directory /sd1/1/SUB1
core0: FS2: In directory /sd0/2/SUB1
core0: FS0: In directory /sd0/0/SUB1
core0: FS3: In directory /sd1/3/SUB1
core0: FS4: In directory /sd0/4/SUB1
core0: FS1: In directory /sd1/1/SUB1/SUB2
core1: FS1: Writing file SUB2.txt in /sd1/1/SUB1/SUB2
core1: FS2: In directory /sd0/2/SUB1/SUB2
core1: FS2: Writing file SUB2.txt in /sd0/2/SUB1/SUB2
core1: FS0: In directory /sd0/0/SUB1/SUB2
core1: FS0: Writing file SUB2.txt in /sd0/0/SUB1/SUB2
core0: FS3: In directory /sd1/3/SUB1/SUB2
core0: FS3: Writing file SUB2.txt in /sd1/3/SUB1/SUB2
core0: FS2: Back in root directory /sd0/2
prvVerifyDemoFileUsing_ff_fgetc(pcMountPath=/sd0/2)
core0: FS1: Back in root directory /sd1/1
prvVerifyDemoFileUsing_ff_fgetc(pcMountPath=/sd1/1)
core0: FS4: In directory /sd0/4/SUB1/SUB2
core0: FS0: Back in root directory /sd0/0
core0: FS4: Writing file SUB2.txt in /sd0/4/SUB1/SUB2
prvVerifyDemoFileUsing_ff_fgetc(pcMountPath=/sd0/0)
core1: FS2: Back in directory /sd0/2/SUB1/SUB2
core0: FS2: Reading file SUB2.txt in /sd0/2/SUB1/SUB2
core0: FS1: Back in directory /sd1/1/SUB1/SUB2
core1: FS1: Reading file SUB2.txt in /sd1/1/SUB1/SUB2
core0: FS0: Back in directory /sd0/0/SUB1/SUB2
core1: FS0: Reading file SUB2.txt in /sd0/0/SUB1/SUB2
core0: FS3: Back in root directory /sd1/3
prvVerifyDemoFileUsing_ff_fgetc(pcMountPath=/sd1/3)
core1: FS2: Back in root directory /sd0/2
core1: FS2: vStdioWithCWDTest(pcMountPath=/sd0/2)
core1: FS3: Back in directory /sd1/3/SUB1/SUB2
core1: FS3: Reading file SUB2.txt in /sd1/3/SUB1/SUB2
core0: FS1: Back in root directory /sd1/1
core1: FS1: vStdioWithCWDTest(pcMountPath=/sd1/1)
core1: FS4: Back in root directory /sd0/4
prvVerifyDemoFileUsing_ff_fgetc(pcMountPath=/sd0/4)
core0: FS0: Back in root directory /sd0/0
core0: FS3: Back in root directory /sd1/3
core0: FS4: Back in directory /sd0/4/SUB1/SUB2
core0: FS0: vStdioWithCWDTest(pcMountPath=/sd0/0)
core0: FS3: vStdioWithCWDTest(pcMountPath=/sd1/3)
core0: FS4: Reading file SUB2.txt in /sd0/4/SUB1/SUB2
core1: FS4: Back in root directory /sd0/4
core1: FS4: vStdioWithCWDTest(pcMountPath=/sd0/4)
...
task-stats
Task          State  Priority  Stack	#
************************************************
stdio Task     	X	3	453	1
FS2            	R	2	264	7
FS0            	R	2	246	5
FS3            	R	2	246	8
IDLE0          	X	0	107	3
IDLE1          	R	0	109	4
FS4            	B	2	246	9
FS1            	B	2	246	6
Tmr Svc        	B	4	88	2

> run-time-stats
Task            Abs Time      % Time
****************************************
stdio Task     	80524		5%
FS2            	210801		13%
FS0            	200522		13%
FS3            	196209		12%
IDLE0          	237457		15%
IDLE1          	222973		14%
FS4            	197284		12%
FS1            	193478		12%
Tmr Svc        	129		<1%

:partying_face:

Nothing to see here, just need enough characters to post.

Carl, congratulations, and thanks for posting this.

As you probably know, the +FAT test ff_stdio_tests_with_cwd.c is a very good test for task synchronisation. Every little mistake about synchronicity will lead to an ASSERT.

For those who don’t know: the test starts up N tasks to work on the same disk. These tasks all run at the idle priority. Preemption and time-slicing are enabled ( see configUSE_PREEMPTION, configUSE_TIME_SLICING, and configIDLE_SHOULD_YIELD ).

As a result of this, a task can be disrupted at any moment, except during a critical section, or while the scheduler is suspended.

On a CPU with a single core, only once FAT task will be active at the time, in Carl’s setup there are 2 cores, and 2 FAT tasks can be active simultaneously. That is no problem because they synchronise their access to the disk with mutexes, see ff_locking.c.

And just to be sure: a file handle should belong to a single task, it may not be shared. The locking mechanism doesn’t protect against multiple access using the same file handle.
However: there may be multiple READ handles to the same file, or only a single handle with WRITE permission.