feat: add lab-rv32i-freertos-isr-drivers card

This commit is contained in:
user
2026-07-21 19:14:19 +02:00
commit e923e1c3b0
174 changed files with 56091 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
#include <stddef.h>
#include <stdint.h>
extern "C"
{
#include "FreeRTOS.h"
}
extern "C"
{
volatile size_t g_cpp_allocation_count;
volatile size_t g_cpp_deallocation_count;
volatile size_t g_cpp_live_allocations;
volatile uintptr_t g_cpp_allocation_addresses[ 4 ];
volatile uintptr_t g_cpp_deallocation_addresses[ 4 ];
}
namespace
{
void * allocate_or_fail( size_t bytes )
{
void * const memory = pvPortMalloc( bytes == 0U ? 1U : bytes );
configASSERT( memory != nullptr );
if( g_cpp_allocation_count < 4U )
{
g_cpp_allocation_addresses[ g_cpp_allocation_count ] =
reinterpret_cast< uintptr_t >( memory );
}
++g_cpp_allocation_count;
++g_cpp_live_allocations;
return memory;
}
void release( void * memory ) noexcept
{
if( memory == nullptr )
{
return;
}
if( g_cpp_deallocation_count < 4U )
{
g_cpp_deallocation_addresses[ g_cpp_deallocation_count ] =
reinterpret_cast< uintptr_t >( memory );
}
++g_cpp_deallocation_count;
--g_cpp_live_allocations;
vPortFree( memory );
}
} // namespace
void * operator new( size_t bytes )
{
return allocate_or_fail( bytes );
}
void * operator new[]( size_t bytes )
{
return allocate_or_fail( bytes );
}
void operator delete( void * memory ) noexcept
{
release( memory );
}
void operator delete[]( void * memory ) noexcept
{
release( memory );
}
void operator delete( void * memory, size_t ) noexcept
{
release( memory );
}
void operator delete[]( void * memory, size_t ) noexcept
{
release( memory );
}
+23
View File
@@ -0,0 +1,23 @@
.section .text
.global _start
.type _start, @function
_start:
.option push
.option norelax
la gp, __global_pointer$
.option pop
/* Hazard3 init.S has already installed the bootstrap stack. */
la a0, __bss_start
la a1, __bss_end
1:
bgeu a0, a1, 2f
sb zero, 0(a0)
addi a0, a0, 1
j 1b
2:
call main
tail _exit
.size _start, .-_start
+27
View File
@@ -0,0 +1,27 @@
.section .text.hazard3_freertos_traps, "ax", @progbits
/*
* Hazard3 init.S owns mtvec and exposes weak vector targets. Strong symbols
* below route exceptions (including ecall/yield) and the machine timer IRQ to
* the official FreeRTOS RISC-V trap handler.
*/
.global handle_exception
.type handle_exception, @function
handle_exception:
j freertos_risc_v_trap_handler
.size handle_exception, .-handle_exception
.global isr_machine_timer
.type isr_machine_timer, @function
isr_machine_timer:
j freertos_risc_v_trap_handler
.size isr_machine_timer, .-isr_machine_timer
/* Route a real machine-external interrupt through the FreeRTOS full-context
* trap path. The application handler runs on xISRStack and may request a
* context switch before portcontextRESTORE_CONTEXT selects the next task. */
.global isr_external_irq
.type isr_external_irq, @function
isr_external_irq:
j freertos_risc_v_trap_handler
.size isr_external_irq, .-isr_external_irq
+48
View File
@@ -0,0 +1,48 @@
#include <stdint.h>
#include "FreeRTOS.h"
#include "lab_freertos.h"
#include "lab_io.h"
/* Official heap_4.c uses this application-owned arena. */
__attribute__( ( aligned( 16 ) ) ) uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
volatile uint32_t g_failure_code;
int lab_address_in_heap( const volatile void * address )
{
const uintptr_t value = ( uintptr_t ) address;
const uintptr_t first = ( uintptr_t ) &ucHeap[ 0 ];
const uintptr_t past_last = ( uintptr_t ) &ucHeap[ configTOTAL_HEAP_SIZE ];
return ( value >= first ) && ( value < past_last );
}
void lab_heap_initialize( void )
{
void * probe = pvPortMalloc( 1U );
if( probe == NULL )
{
lab_fail( 0xA6000002UL );
}
vPortFree( probe );
}
void lab_fail( uint32_t code )
{
g_failure_code = code;
lab_puts( "FAIL\n" );
lab_put_u32( code );
lab_exit( code );
}
void lab_assert_fail( const char * file, int line )
{
( void ) file;
lab_fail( 0xA5000000UL | ( ( uint32_t ) line & 0xFFFFUL ) );
}
void vApplicationMallocFailedHook( void )
{
lab_fail( 0xA6000001UL );
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef LAB_FREERTOS_H
#define LAB_FREERTOS_H
#include <stddef.h>
#include <stdint.h>
#include "FreeRTOS.h"
extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];
extern volatile uint32_t g_failure_code;
int lab_address_in_heap( const volatile void * address );
void lab_heap_initialize( void );
void lab_fail( uint32_t code ) __attribute__( ( noreturn ) );
#endif
+30
View File
@@ -0,0 +1,30 @@
#include <stdint.h>
#include "lab_io.h"
#define H3_IO_BASE ( 0xC0000000UL )
#define H3_IO_PRINT_CHAR ( *( ( volatile uint32_t * ) ( H3_IO_BASE + 0x0UL ) ) )
#define H3_IO_PRINT_U32 ( *( ( volatile uint32_t * ) ( H3_IO_BASE + 0x4UL ) ) )
#define H3_IO_EXIT ( *( ( volatile uint32_t * ) ( H3_IO_BASE + 0x8UL ) ) )
void lab_puts( const char * text )
{
while( *text != '\0' )
{
H3_IO_PRINT_CHAR = ( uint32_t ) ( unsigned char ) *text;
++text;
}
}
void lab_put_u32( uint32_t value )
{
H3_IO_PRINT_U32 = value;
}
void lab_exit( uint32_t code )
{
H3_IO_EXIT = code;
for( ; ; )
{
__asm volatile ( "wfi" );
}
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef LAB_IO_H
#define LAB_IO_H
#include <stdint.h>
void lab_puts( const char * text );
void lab_put_u32( uint32_t value );
void lab_exit( uint32_t code ) __attribute__( ( noreturn ) );
#endif
+34
View File
@@ -0,0 +1,34 @@
#include <stddef.h>
void * memcpy( void * destination, const void * source, size_t count )
{
unsigned char * out = ( unsigned char * ) destination;
const unsigned char * in = ( const unsigned char * ) source;
for( size_t index = 0; index < count; ++index )
{
out[ index ] = in[ index ];
}
return destination;
}
void * memset( void * destination, int value, size_t count )
{
unsigned char * out = ( unsigned char * ) destination;
for( size_t index = 0; index < count; ++index )
{
out[ index ] = ( unsigned char ) value;
}
return destination;
}
size_t strlen( const char * text )
{
size_t length = 0;
while( text[ length ] != '\0' )
{
++length;
}
return length;
}
+273
View File
@@ -0,0 +1,273 @@
#include <cstddef>
#include <cstdint>
extern "C"
{
#include "FreeRTOS.h"
#include "task.h"
#include "hazard3_irq.h"
#include "lab_freertos.h"
#include "lab_io.h"
}
#include "freertos/isr_drivers.hpp"
namespace
{
constexpr std::uintptr_t io_base = 0xC0000000UL;
constexpr std::uintptr_t io_set_irq = io_base + 0x020UL;
constexpr std::uintptr_t io_clear_irq = io_base + 0x030UL;
constexpr std::uint32_t irq_number = 5U;
constexpr std::uint32_t irq_mask = 1UL << irq_number;
constexpr std::uint32_t gpio_led_mask = 1U;
constexpr std::uint32_t stack_words = 256U;
volatile std::uint32_t & io_register( std::uintptr_t address ) noexcept
{
return *reinterpret_cast< volatile std::uint32_t * >( address );
}
std::uintptr_t read_sp() noexcept
{
std::uintptr_t value;
__asm__ volatile( "mv %0, sp" : "=r"( value ) );
return value;
}
std::uint32_t read_mcause() noexcept
{
std::uint32_t value;
__asm__ volatile( "csrr %0, mcause" : "=r"( value ) );
return value;
}
using RxQueue = freertos::StaticIsrQueue< std::uint8_t, 1U >;
RxQueue * g_rx_queue;
TaskHandle_t g_receiver_handle;
}
extern "C"
{
freertos::UartRegisters g_uart_registers;
freertos::GpioRegisters g_gpio_registers;
StaticQueue_t g_rx_queue_control;
std::uint8_t g_rx_queue_storage[ 1U ];
StaticTask_t g_receiver_tcb;
StaticTask_t g_stimulus_tcb;
StaticTask_t g_verifier_tcb;
StackType_t g_receiver_stack[ stack_words ];
StackType_t g_stimulus_stack[ stack_words ];
StackType_t g_verifier_stack[ stack_words ];
volatile std::uint32_t g_mcause;
volatile std::uint32_t g_irq_after_clear;
volatile std::uint32_t g_uart_status_before_isr;
volatile std::uint32_t g_uart_status_after_isr;
volatile std::uint32_t g_gpio_edge_before_isr;
volatile std::uint32_t g_gpio_edge_after_isr;
volatile std::uint32_t g_rx_queued;
volatile std::uint32_t g_rx_dropped;
volatile std::uint32_t g_queued_byte;
volatile std::uint32_t g_dropped_byte;
volatile std::uint32_t g_notification_sent;
volatile std::uint32_t g_notification_taken;
volatile std::uint32_t g_higher_priority_task_woken;
volatile std::uint32_t g_yield_requested;
volatile std::uint32_t g_isr_yield_calls;
volatile std::uint32_t g_receiver_byte;
volatile std::uint32_t g_receiver_tick;
volatile std::uint32_t g_stimulus_after_seen_by_receiver;
volatile std::uint32_t g_receiver_done;
volatile std::uint32_t g_stimulus_after;
volatile std::uint32_t g_receiver_done_at_stimulus_resume;
volatile std::uintptr_t g_isr_sp;
volatile std::uintptr_t g_receiver_sp;
volatile std::uintptr_t g_stimulus_sp;
volatile std::size_t g_heap_before_queue;
volatile std::size_t g_heap_after_queue;
volatile std::uint32_t g_isr_drivers_pass;
__attribute__( ( noinline, used ) )
void isr_drivers_debug_checkpoint( std::uint32_t point )
{
( void ) point;
__asm__ volatile( "" ::: "memory" );
}
__attribute__( ( noinline, used ) )
void freertos_risc_v_application_interrupt_handler()
{
freertos::IsrContext context;
freertos::Uart uart{ g_uart_registers };
freertos::GpioPin gpio{ g_gpio_registers };
auto const uart_isr = uart.isr_view();
auto const gpio_isr = gpio.isr_view();
g_mcause = read_mcause();
g_isr_sp = read_sp();
g_uart_status_before_isr = g_uart_registers.status;
g_gpio_edge_before_isr = g_gpio_registers.edge_status;
isr_drivers_debug_checkpoint( 2U );
for( std::uint32_t index = 0U;
index < 2U && uart_isr.rx_ready_from_isr();
++index )
{
std::uint8_t const byte = uart_isr.read_rx_from_isr();
auto const result = g_rx_queue->send_from_isr( byte, context );
if( result == freertos::IsrSendResult::queued )
{
++g_rx_queued;
g_queued_byte = byte;
}
else
{
++g_rx_dropped;
g_dropped_byte = byte;
}
}
if( gpio_isr.edge_pending_from_isr( gpio_led_mask ) )
{
gpio_isr.clear_edge_from_isr( gpio_led_mask );
freertos::TaskNotificationRef notification{ g_receiver_handle };
g_notification_sent = notification.give_from_isr( context ) ? 1U : 0U;
}
g_uart_status_after_isr = g_uart_registers.status;
g_gpio_edge_after_isr = g_gpio_registers.edge_status;
io_register( io_clear_irq ) = irq_mask;
g_irq_after_clear = io_register( io_clear_irq );
g_higher_priority_task_woken = context.wake_requested() ? 1U : 0U;
g_yield_requested = context.yield_if_needed() ? 1U : 0U;
g_isr_yield_calls = context.yield_calls();
isr_drivers_debug_checkpoint( 3U );
}
}
namespace
{
extern "C" void receiver_entry( void * )
{
g_receiver_sp = read_sp();
std::uint8_t byte = 0U;
isr_drivers_debug_checkpoint( 1U );
if( !g_rx_queue->receive( byte, portMAX_DELAY ) )
{
lab_fail( 0x4B150101U );
}
g_receiver_byte = byte;
g_receiver_tick = xTaskGetTickCount();
g_notification_taken = ulTaskNotifyTake( pdTRUE, 0U );
g_stimulus_after_seen_by_receiver = g_stimulus_after;
freertos::GpioPin gpio{ g_gpio_registers };
gpio.task_view().write_output( gpio_led_mask, ( byte & 1U ) != 0U );
g_receiver_done = 1U;
isr_drivers_debug_checkpoint( 4U );
vTaskDelete( nullptr );
lab_fail( 0x4B150102U );
}
extern "C" void stimulus_entry( void * )
{
g_stimulus_sp = read_sp();
vTaskDelay( 1U );
freertos::Uart uart{ g_uart_registers };
freertos::GpioPin gpio{ g_gpio_registers };
uart.task_view().inject_rx_pair_for_test( 0x41U, 0x42U );
gpio.task_view().inject_edge_for_test( gpio_led_mask );
isr_drivers_debug_checkpoint( 5U );
io_register( io_set_irq ) = irq_mask;
__asm__ volatile( "nop" ::: "memory" );
g_stimulus_after = 1U;
g_receiver_done_at_stimulus_resume = g_receiver_done;
isr_drivers_debug_checkpoint( 6U );
vTaskDelete( nullptr );
lab_fail( 0x4B150201U );
}
extern "C" void verifier_entry( void * )
{
TickType_t const started = xTaskGetTickCount();
while( g_stimulus_after == 0U || g_receiver_done == 0U )
{
if( xTaskGetTickCount() - started > 20U )
{
lab_fail( 0x4B150301U );
}
vTaskDelay( 1U );
}
g_isr_drivers_pass =
g_mcause == 0x8000000bU &&
g_irq_after_clear == 0U &&
g_uart_status_before_isr == 1U &&
g_uart_status_after_isr == 0U &&
g_gpio_edge_before_isr == 1U &&
g_gpio_edge_after_isr == 0U &&
g_rx_queued == 1U &&
g_rx_dropped == 1U &&
g_queued_byte == 0x41U &&
g_dropped_byte == 0x42U &&
g_notification_sent == 1U &&
g_notification_taken == 1U &&
g_higher_priority_task_woken == 1U &&
g_yield_requested == 1U &&
g_isr_yield_calls == 1U &&
g_receiver_byte == 0x41U &&
g_receiver_tick == 1U &&
g_stimulus_after_seen_by_receiver == 0U &&
g_receiver_done_at_stimulus_resume == 1U &&
g_gpio_registers.direction == gpio_led_mask &&
g_gpio_registers.data == gpio_led_mask &&
g_isr_sp != g_receiver_sp &&
g_isr_sp != g_stimulus_sp &&
g_heap_before_queue == g_heap_after_queue ? 1U : 0U;
isr_drivers_debug_checkpoint( 7U );
if( g_isr_drivers_pass == 0U )
{
lab_fail( 0x4B150302U );
}
lab_puts( "PASS task01: real IRQ, UART queue, GPIO notify, one yield\n" );
lab_put_u32( g_mcause );
lab_put_u32( g_receiver_byte );
lab_put_u32( g_dropped_byte );
lab_put_u32( g_higher_priority_task_woken );
lab_put_u32( g_isr_yield_calls );
lab_exit( 0U );
}
}
int main()
{
lab_heap_initialize();
g_heap_before_queue = xPortGetFreeHeapSize();
RxQueue queue{ g_rx_queue_control, g_rx_queue_storage };
if( !queue.valid() ) { lab_fail( 0x4B150401U ); }
g_rx_queue = &queue;
g_heap_after_queue = xPortGetFreeHeapSize();
freertos::Uart uart{ g_uart_registers };
freertos::GpioPin gpio{ g_gpio_registers };
uart.task_view().enable_rx_irq();
gpio.task_view().configure_output( gpio_led_mask );
h3irq_enable( irq_number, true );
h3irq_set_priority( irq_number, 8U );
g_receiver_handle =
xTaskCreateStatic( receiver_entry, "rx-worker", stack_words, nullptr, 3U,
g_receiver_stack, &g_receiver_tcb );
TaskHandle_t const stimulus =
xTaskCreateStatic( stimulus_entry, "irq-source", stack_words, nullptr, 2U,
g_stimulus_stack, &g_stimulus_tcb );
TaskHandle_t const verifier =
xTaskCreateStatic( verifier_entry, "verifier", stack_words, nullptr, 1U,
g_verifier_stack, &g_verifier_tcb );
if( g_receiver_handle == nullptr || stimulus == nullptr || verifier == nullptr )
{
lab_fail( 0x4B150402U );
}
vTaskStartScheduler();
lab_fail( 0x4B150403U );
}