feat: publish FreeRTOS C FC03 card

This commit is contained in:
2026-07-19 16:36:02 +02:00
commit 34d08ae979
203 changed files with 57403 additions and 0 deletions
+547
View File
@@ -0,0 +1,547 @@
#include <stddef.h>
#include <stdint.h>
#include "FreeRTOS.h"
#include "task.h"
#include "lab_freertos.h"
#include "lab_io.h"
#include "task01_scheduler.h"
#include "task01_scheduler_model.h"
#define MCAUSE_MACHINE_TIMER_INTERRUPT ( 0x80000007UL )
volatile uint32_t g_last_checkpoint;
volatile uintptr_t g_checkpoint_subject;
volatile uint32_t g_switch_task[ SCHED_TRACE_CAPACITY ];
volatile TickType_t g_switch_tick[ SCHED_TRACE_CAPACITY ];
volatile uint32_t g_switch_count;
volatile uint32_t g_last_peer_id = UINT32_MAX;
volatile uint32_t g_peer_changes;
volatile uint32_t g_aba_task[ 3 ];
volatile TickType_t g_aba_tick[ 3 ];
volatile uint32_t g_aba_seen;
volatile uint32_t g_preemption_order[ 3 ];
volatile uint32_t g_preemption_order_count;
volatile uint32_t g_priority_raise_started;
volatile uint32_t g_peer_after_raise;
volatile uint32_t g_stop_peers;
volatile uint32_t g_peers_finished;
volatile uint32_t g_verifier_started;
volatile uint32_t g_tick_hook_count;
volatile TickType_t g_first_tick_count;
volatile uintptr_t g_first_tick_sp;
volatile uintptr_t g_first_tick_mepc;
volatile uint32_t g_first_tick_mcause;
volatile uint32_t g_scheduler_pass;
PeerContext g_peer_a = {
.id = SCHED_PEER_A,
.configured_priority = SCHED_PEER_PRIORITY,
.handle = NULL,
.phase = SCHED_PHASE_PREPARED
};
PeerContext g_peer_b = {
.id = SCHED_PEER_B,
.configured_priority = SCHED_PEER_PRIORITY,
.handle = NULL,
.phase = SCHED_PHASE_PREPARED
};
HighProbeContext g_high_probe = {
.initial_priority = SCHED_PROBE_INITIAL_PRIORITY,
.target_priority = SCHED_PROBE_TARGET_PRIORITY,
.handle = NULL,
.phase = SCHED_PHASE_PREPARED
};
VerifierContext g_verifier = {
.configured_priority = SCHED_VERIFY_PRIORITY,
.handle = NULL,
.phase = SCHED_PHASE_PREPARED
};
static uintptr_t read_sp( void )
{
uintptr_t value;
__asm__ volatile( "mv %0, sp" : "=r"( value ) );
return value;
}
static uintptr_t read_mepc( void )
{
uintptr_t value;
__asm__ volatile( "csrr %0, mepc" : "=r"( value ) );
return value;
}
static uint32_t read_mcause( void )
{
uint32_t value;
__asm__ volatile( "csrr %0, mcause" : "=r"( value ) );
return value;
}
__attribute__( ( noinline, used ) )
void task01_scheduler_checkpoint_committed( uint32_t point,
const void * subject )
{
( void ) point;
( void ) subject;
__asm__ volatile( "" ::: "memory" );
}
__attribute__( ( noinline, used ) )
void task01_scheduler_checkpoint( uint32_t point, const void * subject )
{
g_last_checkpoint = point;
g_checkpoint_subject = ( uintptr_t ) subject;
__asm__ volatile( "" ::: "memory" );
task01_scheduler_checkpoint_committed( point, subject );
}
__attribute__( ( noinline, used ) )
void task01_tick_checkpoint_committed( void )
{
__asm__ volatile( "" ::: "memory" );
}
void vApplicationTickHook( void )
{
++g_tick_hook_count;
if( g_tick_hook_count == 1U )
{
g_first_tick_count = xTaskGetTickCountFromISR();
g_first_tick_sp = read_sp();
g_first_tick_mepc = read_mepc();
g_first_tick_mcause = read_mcause();
g_last_checkpoint = 6U;
g_checkpoint_subject = ( uintptr_t ) &g_tick_hook_count;
__asm__ volatile( "" ::: "memory" );
task01_tick_checkpoint_committed();
}
}
static void capture_peer_runtime( PeerContext * peer )
{
TaskStatus_t info;
uintptr_t first;
uintptr_t last;
vTaskGetInfo( NULL, &info, pdFALSE, eRunning );
first = ( uintptr_t ) info.pxStackBase;
last = ( uintptr_t ) info.pxEndOfStack;
peer->observed_priority = uxTaskPriorityGet( NULL );
peer->tcb_address = ( uintptr_t ) info.xHandle;
peer->entry_sp = read_sp();
peer->stack_low = first < last ? first : last;
peer->stack_high = first < last ? last : first;
}
static void capture_probe_runtime( HighProbeContext * probe )
{
TaskStatus_t info;
uintptr_t first;
uintptr_t last;
vTaskGetInfo( NULL, &info, pdFALSE, eRunning );
first = ( uintptr_t ) info.pxStackBase;
last = ( uintptr_t ) info.pxEndOfStack;
probe->observed_priority = uxTaskPriorityGet( NULL );
probe->tcb_address = ( uintptr_t ) info.xHandle;
probe->entry_sp = read_sp();
probe->stack_low = first < last ? first : last;
probe->stack_high = first < last ? last : first;
}
static int volatile_trace_has_aba( void )
{
uint32_t index;
for( index = 2U; index < g_switch_count; ++index )
{
if( g_switch_task[ index - 2U ] == SCHED_PEER_A &&
g_switch_task[ index - 1U ] == SCHED_PEER_B &&
g_switch_task[ index ] == SCHED_PEER_A )
{
return 1;
}
}
return 0;
}
static BaseType_t record_peer_switch( uint32_t id, TickType_t tick )
{
BaseType_t aba_completed_by_a = pdFALSE;
taskENTER_CRITICAL();
if( g_last_peer_id != id )
{
if( g_last_peer_id != UINT32_MAX )
{
++g_peer_changes;
}
g_last_peer_id = id;
if( g_switch_count < SCHED_TRACE_CAPACITY )
{
g_switch_task[ g_switch_count ] = id;
g_switch_tick[ g_switch_count ] = tick;
++g_switch_count;
}
}
if( id == SCHED_PEER_A &&
g_aba_seen == 0U &&
volatile_trace_has_aba() != 0 )
{
const uint32_t last = g_switch_count - 1U;
g_aba_task[ 0 ] = g_switch_task[ last - 2U ];
g_aba_task[ 1 ] = g_switch_task[ last - 1U ];
g_aba_task[ 2 ] = g_switch_task[ last ];
g_aba_tick[ 0 ] = g_switch_tick[ last - 2U ];
g_aba_tick[ 1 ] = g_switch_tick[ last - 1U ];
g_aba_tick[ 2 ] = g_switch_tick[ last ];
g_aba_seen = 1U;
aba_completed_by_a = pdTRUE;
}
taskEXIT_CRITICAL();
return aba_completed_by_a;
}
static void append_preemption_marker( uint32_t marker )
{
taskENTER_CRITICAL();
if( g_preemption_order_count < 3U )
{
g_preemption_order[ g_preemption_order_count ] = marker;
++g_preemption_order_count;
}
taskEXIT_CRITICAL();
}
static int sp_inside_peer_stack( const PeerContext * peer )
{
return peer->entry_sp >= peer->stack_low &&
peer->entry_sp <= peer->stack_high;
}
static int sp_inside_probe_stack( const HighProbeContext * probe )
{
return probe->entry_sp >= probe->stack_low &&
probe->entry_sp <= probe->stack_high;
}
__attribute__( ( noinline, used ) )
void peer_task_entry( void * pv_parameters )
{
PeerContext * peer = ( PeerContext * ) pv_parameters;
TickType_t observed_tick;
BaseType_t emit_entry = pdFALSE;
if( peer == NULL || peer->id > SCHED_PEER_B )
{
lab_fail( 0xFC030101U );
}
capture_peer_runtime( peer );
peer->phase = SCHED_PHASE_RUNNING;
peer->first_tick = xTaskGetTickCount();
observed_tick = peer->first_tick;
taskENTER_CRITICAL();
if( g_peer_a.iterations == 0U && g_peer_b.iterations == 0U )
{
emit_entry = pdTRUE;
}
taskEXIT_CRITICAL();
if( emit_entry != pdFALSE )
{
task01_scheduler_checkpoint( 5U, peer );
}
for( ; ; )
{
const TickType_t tick = xTaskGetTickCount();
++peer->iterations;
peer->checksum += ( peer->id + 1U ) *
( ( peer->iterations & 0xFFU ) + 1U );
if( tick != observed_tick || g_switch_count == 0U )
{
observed_tick = tick;
if( record_peer_switch( peer->id, tick ) != pdFALSE )
{
task01_scheduler_checkpoint( 7U, peer );
taskENTER_CRITICAL();
if( g_priority_raise_started == 0U )
{
g_priority_raise_started = 1U;
taskEXIT_CRITICAL();
append_preemption_marker( SCHED_ORDER_BEFORE_RAISE );
task01_scheduler_checkpoint( 8U, peer );
vTaskPrioritySet( g_high_probe.handle,
SCHED_PROBE_TARGET_PRIORITY );
append_preemption_marker( SCHED_ORDER_AFTER_RAISE );
g_peer_after_raise = 1U;
g_stop_peers = 1U;
task01_scheduler_checkpoint( 10U, peer );
}
else
{
taskEXIT_CRITICAL();
}
}
}
if( g_stop_peers != 0U )
{
break;
}
if( ( tick - peer->first_tick ) > SCHED_TIMEOUT_TICKS )
{
lab_fail( 0xFC030102U );
}
}
peer->last_tick = xTaskGetTickCount();
peer->phase = SCHED_PHASE_COMPLETED;
peer->handle = NULL;
taskENTER_CRITICAL();
++g_peers_finished;
if( g_peers_finished == 2U )
{
taskEXIT_CRITICAL();
task01_scheduler_checkpoint( 11U, peer );
}
else
{
taskEXIT_CRITICAL();
}
vTaskDelete( NULL );
lab_fail( 0xFC030103U );
}
__attribute__( ( noinline, used ) )
void high_probe_task_entry( void * pv_parameters )
{
HighProbeContext * probe = ( HighProbeContext * ) pv_parameters;
if( probe == NULL )
{
lab_fail( 0xFC030201U );
}
capture_probe_runtime( probe );
probe->phase = SCHED_PHASE_RUNNING;
probe->run_tick = xTaskGetTickCount();
append_preemption_marker( SCHED_ORDER_HIGH_PROBE );
probe->ran_before_caller_returned =
( g_peer_after_raise == 0U ) &&
( g_preemption_order_count == 2U );
probe->verifier_was_not_running = ( g_verifier_started == 0U );
task01_scheduler_checkpoint( 9U, probe );
probe->phase = SCHED_PHASE_COMPLETED;
probe->handle = NULL;
vTaskDelete( NULL );
lab_fail( 0xFC030202U );
}
static int trace_ticks_are_nondecreasing( void )
{
uint32_t index;
for( index = 1U; index < g_switch_count; ++index )
{
if( g_switch_tick[ index ] < g_switch_tick[ index - 1U ] )
{
return 0;
}
}
return 1;
}
__attribute__( ( noinline, used ) )
void verifier_task_entry( void * pv_parameters )
{
VerifierContext * verifier = ( VerifierContext * ) pv_parameters;
if( verifier == NULL )
{
lab_fail( 0xFC030301U );
}
verifier->phase = SCHED_PHASE_RUNNING;
verifier->observed_priority = uxTaskPriorityGet( NULL );
g_verifier_started = 1U;
g_scheduler_pass =
configUSE_PREEMPTION == 1 &&
configUSE_TIME_SLICING == 1 &&
g_peer_a.observed_priority == SCHED_PEER_PRIORITY &&
g_peer_b.observed_priority == SCHED_PEER_PRIORITY &&
g_high_probe.observed_initial_priority ==
SCHED_PROBE_INITIAL_PRIORITY &&
g_high_probe.observed_priority == SCHED_PROBE_TARGET_PRIORITY &&
verifier->observed_priority == SCHED_VERIFY_PRIORITY &&
g_peer_a.phase == SCHED_PHASE_COMPLETED &&
g_peer_b.phase == SCHED_PHASE_COMPLETED &&
g_high_probe.phase == SCHED_PHASE_COMPLETED &&
g_peer_a.handle == NULL &&
g_peer_b.handle == NULL &&
g_high_probe.handle == NULL &&
g_peers_finished == 2U &&
g_peer_a.iterations > 0U &&
g_peer_b.iterations > 0U &&
g_peer_a.checksum != 0U &&
g_peer_b.checksum != 0U &&
g_tick_hook_count >= 2U &&
g_first_tick_mcause == MCAUSE_MACHINE_TIMER_INTERRUPT &&
g_first_tick_sp != 0U &&
g_first_tick_mepc != 0U &&
g_aba_seen == 1U &&
g_aba_task[ 0 ] == SCHED_PEER_A &&
g_aba_task[ 1 ] == SCHED_PEER_B &&
g_aba_task[ 2 ] == SCHED_PEER_A &&
g_aba_tick[ 0 ] <= g_aba_tick[ 1 ] &&
g_aba_tick[ 1 ] <= g_aba_tick[ 2 ] &&
trace_ticks_are_nondecreasing() != 0 &&
g_peer_changes >= 2U &&
g_priority_raise_started == 1U &&
g_peer_after_raise == 1U &&
g_high_probe.ran_before_caller_returned == 1U &&
g_high_probe.verifier_was_not_running == 1U &&
g_preemption_order_count == 3U &&
g_preemption_order[ 0 ] == SCHED_ORDER_BEFORE_RAISE &&
g_preemption_order[ 1 ] == SCHED_ORDER_HIGH_PROBE &&
g_preemption_order[ 2 ] == SCHED_ORDER_AFTER_RAISE &&
g_high_probe.run_tick >= g_aba_tick[ 2 ] &&
g_peer_a.created_handle == g_peer_a.tcb_address &&
g_peer_b.created_handle == g_peer_b.tcb_address &&
g_high_probe.created_handle == g_high_probe.tcb_address &&
sp_inside_peer_stack( &g_peer_a ) != 0 &&
sp_inside_peer_stack( &g_peer_b ) != 0 &&
sp_inside_probe_stack( &g_high_probe ) != 0;
verifier->pass = g_scheduler_pass;
verifier->phase = SCHED_PHASE_COMPLETED;
verifier->handle = NULL;
task01_scheduler_checkpoint( 12U, verifier );
if( g_scheduler_pass == 0U )
{
lab_fail( 0xFC030302U );
}
lab_puts( "PASS FC03: tick + priorities + preemption + time slicing\n" );
lab_put_u32( g_tick_hook_count );
lab_put_u32( g_peer_changes );
lab_put_u32( g_preemption_order_count );
lab_exit( 0U );
}
static void create_task_or_fail( TaskFunction_t entry,
const char * name,
configSTACK_DEPTH_TYPE stack_words,
void * context,
UBaseType_t priority,
TaskHandle_t * handle,
uint32_t failure_code )
{
if( xTaskCreate( entry,
name,
stack_words,
context,
priority,
handle ) != pdPASS )
{
lab_fail( failure_code );
}
}
int main( void )
{
lab_heap_initialize();
task01_scheduler_checkpoint( 1U, &g_peer_a );
create_task_or_fail( high_probe_task_entry,
"high-probe",
SCHED_PROBE_STACK_WORDS,
&g_high_probe,
SCHED_PROBE_INITIAL_PRIORITY,
&g_high_probe.handle,
0xFC030401U );
g_high_probe.created_handle = ( uintptr_t ) g_high_probe.handle;
g_high_probe.phase = SCHED_PHASE_READY;
task01_scheduler_checkpoint( 2U, &g_high_probe );
create_task_or_fail( peer_task_entry,
"peer-a",
SCHED_PEER_STACK_WORDS,
&g_peer_a,
SCHED_PEER_PRIORITY,
&g_peer_a.handle,
0xFC030402U );
g_peer_a.created_handle = ( uintptr_t ) g_peer_a.handle;
g_peer_a.phase = SCHED_PHASE_READY;
create_task_or_fail( peer_task_entry,
"peer-b",
SCHED_PEER_STACK_WORDS,
&g_peer_b,
SCHED_PEER_PRIORITY,
&g_peer_b.handle,
0xFC030403U );
g_peer_b.created_handle = ( uintptr_t ) g_peer_b.handle;
g_peer_b.phase = SCHED_PHASE_READY;
create_task_or_fail( verifier_task_entry,
"verify",
SCHED_VERIFY_STACK_WORDS,
&g_verifier,
SCHED_VERIFY_PRIORITY,
&g_verifier.handle,
0xFC030404U );
g_verifier.phase = SCHED_PHASE_READY;
g_high_probe.observed_initial_priority =
uxTaskPriorityGet( g_high_probe.handle );
g_peer_a.observed_priority = uxTaskPriorityGet( g_peer_a.handle );
g_peer_b.observed_priority = uxTaskPriorityGet( g_peer_b.handle );
g_verifier.observed_priority = uxTaskPriorityGet( g_verifier.handle );
task01_scheduler_checkpoint( 3U, &g_high_probe );
if( g_high_probe.observed_initial_priority !=
SCHED_PROBE_INITIAL_PRIORITY ||
g_peer_a.observed_priority != SCHED_PEER_PRIORITY ||
g_peer_b.observed_priority != SCHED_PEER_PRIORITY ||
g_verifier.observed_priority != SCHED_VERIFY_PRIORITY )
{
lab_fail( 0xFC030405U );
}
task01_scheduler_checkpoint( 4U, &g_peer_a );
vTaskStartScheduler();
lab_fail( 0xFC030406U );
}