feat: add lab-rv32i-freertos-static-task card

This commit is contained in:
user
2026-07-21 19:14:20 +02:00
commit 62efc0c91f
65 changed files with 33788 additions and 0 deletions
+258
View File
@@ -0,0 +1,258 @@
#include <stddef.h>
#include <stdint.h>
#include <type_traits>
extern "C"
{
#include "FreeRTOS.h"
#include "lab_freertos.h"
#include "lab_io.h"
#include "task.h"
}
extern "C" void task_storage_debug_checkpoint( uint32_t point,
const void * object );
#define FREERTOS_STORAGE_CHECKPOINT( point, object ) \
task_storage_debug_checkpoint( ( point ), ( object ) )
#include "freertos/task_storage.hpp"
volatile uint32_t g_last_checkpoint;
volatile uintptr_t g_checkpoint_object;
volatile size_t g_heap_baseline;
volatile size_t g_heap_after_dynamic_start;
volatile size_t g_heap_after_static_start;
volatile size_t g_heap_after_verifier_create;
volatile size_t g_heap_before_idle_cleanup;
volatile size_t g_heap_after_idle_cleanup;
volatile size_t g_dynamic_heap_cost;
volatile size_t g_idle_cleanup_recovered;
volatile uintptr_t g_dynamic_tcb;
volatile uintptr_t g_dynamic_stack_low;
volatile uintptr_t g_dynamic_stack_high;
volatile uintptr_t g_static_tcb;
volatile uintptr_t g_static_stack_low;
volatile uintptr_t g_static_stack_high;
volatile uintptr_t g_static_tcb_storage;
volatile uintptr_t g_static_stack_storage;
volatile size_t g_static_stack_words;
volatile size_t g_static_stack_bytes;
volatile uint32_t g_dynamic_result;
volatile uint32_t g_static_result;
volatile uint32_t g_workers_completed;
volatile uint32_t g_static_storage_survived;
volatile uint32_t g_task_storage_pass;
extern "C" __attribute__( ( noinline, used ) )
void task_storage_debug_checkpoint( uint32_t point, const void * object )
{
g_last_checkpoint = point;
g_checkpoint_object = reinterpret_cast< uintptr_t >( object );
__asm__ volatile( "" ::: "memory" );
}
namespace
{
constexpr size_t worker_stack_words = 256U;
constexpr UBaseType_t worker_priority = 2U;
constexpr UBaseType_t verifier_priority = 1U;
constexpr uint32_t work_limit = 1000U;
constexpr uint32_t expected_result = 500500U;
void mark_completed() noexcept
{
taskENTER_CRITICAL();
++g_workers_completed;
taskEXIT_CRITICAL();
}
class DynamicWorker final : public freertos::DynamicTask< DynamicWorker >
{
friend class freertos::DynamicTask< DynamicWorker >;
public:
constexpr DynamicWorker() noexcept
: DynamicTask { "dynamic", worker_stack_words, worker_priority }
{
}
private:
void run() noexcept
{
TaskStatus_t status {};
vTaskGetInfo( nullptr, &status, pdTRUE, eRunning );
g_dynamic_tcb = reinterpret_cast< uintptr_t >( status.xHandle );
g_dynamic_stack_low =
reinterpret_cast< uintptr_t >( status.pxStackBase );
g_dynamic_stack_high =
reinterpret_cast< uintptr_t >( status.pxEndOfStack );
uint32_t result = 0U;
for( uint32_t value = 1U; value <= work_limit; ++value )
{
result += value;
}
g_dynamic_result = result;
mark_completed();
}
};
using StaticWorkerStorage =
freertos::StaticTaskStorage< worker_stack_words >;
class StaticWorker final
: public freertos::StaticTask< StaticWorker, worker_stack_words >
{
friend class freertos::StaticTask< StaticWorker, worker_stack_words >;
public:
constexpr explicit StaticWorker( StaticWorkerStorage & storage ) noexcept
: StaticTask { "static", worker_priority, storage }
{
}
private:
void run() noexcept
{
TaskStatus_t status {};
vTaskGetInfo( nullptr, &status, pdTRUE, eRunning );
g_static_tcb = reinterpret_cast< uintptr_t >( status.xHandle );
g_static_stack_low =
reinterpret_cast< uintptr_t >( status.pxStackBase );
g_static_stack_high =
reinterpret_cast< uintptr_t >( status.pxEndOfStack );
uint32_t result = 0U;
for( uint32_t value = 1U; value <= work_limit; ++value )
{
result += value;
}
g_static_result = result;
mark_completed();
}
};
static_assert( !std::is_copy_constructible< DynamicWorker >::value );
static_assert( !std::is_move_constructible< DynamicWorker >::value );
static_assert( !std::is_polymorphic< DynamicWorker >::value );
static_assert( !std::is_copy_constructible< StaticWorker >::value );
static_assert( !std::is_move_constructible< StaticWorker >::value );
static_assert( !std::is_polymorphic< StaticWorker >::value );
static_assert( StaticWorker::stack_bytes() ==
worker_stack_words * sizeof( StackType_t ) );
DynamicWorker g_dynamic_worker;
StaticWorkerStorage g_static_worker_storage;
StaticWorker g_static_worker { g_static_worker_storage };
freertos::StaticTaskStorage< worker_stack_words > g_verifier_storage;
void verifier( void * )
{
task_storage_debug_checkpoint( 9U, nullptr );
g_heap_before_idle_cleanup = xPortGetFreeHeapSize();
const bool lifecycle_before_idle =
g_workers_completed == 2U &&
g_dynamic_worker.state() ==
freertos::StorageTaskState::completed &&
g_static_worker.state() ==
freertos::StorageTaskState::completed &&
g_dynamic_worker.native_handle() == nullptr &&
g_static_worker.native_handle() == nullptr;
/* Let the idle task reclaim the dynamically allocated TCB and stack. */
vTaskDelay( 2U );
g_heap_after_idle_cleanup = xPortGetFreeHeapSize();
g_idle_cleanup_recovered =
g_heap_after_idle_cleanup - g_heap_before_idle_cleanup;
task_storage_debug_checkpoint( 10U, nullptr );
g_static_storage_survived =
reinterpret_cast< uintptr_t >(
g_static_worker.tcb_storage() ) == g_static_tcb_storage &&
reinterpret_cast< uintptr_t >(
g_static_worker.stack_storage() ) == g_static_stack_storage;
const bool dynamic_storage_proved =
g_dynamic_heap_cost > 0U &&
lab_address_in_heap( reinterpret_cast< const void * >(
g_dynamic_tcb ) ) != 0 &&
lab_address_in_heap( reinterpret_cast< const void * >(
g_dynamic_stack_low ) ) != 0 &&
g_heap_before_idle_cleanup == g_heap_after_verifier_create &&
g_idle_cleanup_recovered == g_dynamic_heap_cost &&
g_heap_after_idle_cleanup == g_heap_baseline;
const bool static_storage_proved =
g_heap_after_static_start == g_heap_after_dynamic_start &&
g_heap_after_verifier_create == g_heap_after_static_start &&
g_static_tcb == g_static_tcb_storage &&
g_static_stack_low == g_static_stack_storage &&
lab_address_in_heap( reinterpret_cast< const void * >(
g_static_tcb ) ) == 0 &&
lab_address_in_heap( reinterpret_cast< const void * >(
g_static_stack_low ) ) == 0 &&
g_static_storage_survived != 0U &&
g_static_stack_words == worker_stack_words &&
g_static_stack_bytes ==
worker_stack_words * sizeof( StackType_t );
g_task_storage_pass =
lifecycle_before_idle && dynamic_storage_proved &&
static_storage_proved &&
g_dynamic_result == expected_result &&
g_static_result == expected_result;
task_storage_debug_checkpoint( 11U, nullptr );
if( g_task_storage_pass == 0U )
{
lab_fail( 0xE801U );
}
lab_puts( "PASS task01: dynamic/static task storage + idle cleanup\n" );
lab_put_u32( static_cast< uint32_t >( g_dynamic_heap_cost ) );
lab_put_u32( static_cast< uint32_t >( g_heap_before_idle_cleanup ) );
lab_put_u32( static_cast< uint32_t >( g_heap_after_idle_cleanup ) );
lab_put_u32( static_cast< uint32_t >( g_static_stack_words ) );
lab_put_u32( static_cast< uint32_t >( g_static_stack_bytes ) );
lab_exit( 0U );
}
} // namespace
int main()
{
lab_heap_initialize();
g_heap_baseline = xPortGetFreeHeapSize();
task_storage_debug_checkpoint( 1U, &g_dynamic_worker );
if( !g_dynamic_worker.start() )
{
lab_fail( 0xE802U );
}
g_heap_after_dynamic_start = xPortGetFreeHeapSize();
g_dynamic_heap_cost = g_heap_baseline - g_heap_after_dynamic_start;
task_storage_debug_checkpoint( 3U, &g_static_worker );
g_static_tcb_storage = reinterpret_cast< uintptr_t >(
g_static_worker.tcb_storage() );
g_static_stack_storage = reinterpret_cast< uintptr_t >(
g_static_worker.stack_storage() );
g_static_stack_words = g_static_worker.stack_words();
g_static_stack_bytes = g_static_worker.stack_bytes();
if( !g_static_worker.start() )
{
lab_fail( 0xE803U );
}
g_heap_after_static_start = xPortGetFreeHeapSize();
if( xTaskCreateStatic(
verifier, "verify", worker_stack_words, nullptr,
verifier_priority, g_verifier_storage.stack,
&g_verifier_storage.tcb ) == nullptr )
{
lab_fail( 0xE804U );
}
g_heap_after_verifier_create = xPortGetFreeHeapSize();
vTaskStartScheduler();
lab_fail( 0xE8FFU );
}