feat: publish FreeRTOS C FC03 card
This commit is contained in:
@@ -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 );
|
||||
}
|
||||
Reference in New Issue
Block a user