Add full automotive RTOS project
Add kernel (Cortex-M0/M3/M4, Tricore, S32K, RISC-V ports), drivers, middleware (CAN stack, diagnostics, safety), applications, board support, build/test tooling, and documentation.
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* @file port.c
|
||||
* @brief ARM Cortex-M0 architecture specific port
|
||||
* @note Cortex-M0 is used in low-power automotive applications
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include "scheduler.h"
|
||||
#include "portmacro.h"
|
||||
|
||||
/* Global variables for context switching */
|
||||
uint32_t* current_task_sp = NULL;
|
||||
uint32_t* next_task_sp = NULL;
|
||||
|
||||
/* Initialize Architecture Port */
|
||||
void port_init(void) {
|
||||
/* Configure SysTick for 1ms interrupts */
|
||||
SysTick->LOAD = (SystemCoreClock / 1000) - 1;
|
||||
SysTick->VAL = 0;
|
||||
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
|
||||
SysTick_CTRL_TICKINT_Msk |
|
||||
SysTick_CTRL_ENABLE_Msk;
|
||||
|
||||
/* Set lowest priority for SysTick and PendSV */
|
||||
NVIC_SetPriority(SysTick_IRQn, 3);
|
||||
NVIC_SetPriority(PendSV_IRQn, 3);
|
||||
|
||||
/* Enable interrupts */
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
/* Start First Task */
|
||||
void port_start_first_task(void) {
|
||||
TaskHandle_t first_task = scheduler_get_current_task();
|
||||
|
||||
if (first_task == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set PSP to task stack pointer */
|
||||
__set_PSP((uint32_t)first_task->stack_pointer);
|
||||
|
||||
/* Switch to using PSP */
|
||||
__set_CONTROL(0x02);
|
||||
__ISB();
|
||||
|
||||
/* Restore context and start task */
|
||||
__asm volatile (
|
||||
"POP {R4-R7}\n"
|
||||
"MOV R8, R4\n"
|
||||
"MOV R9, R5\n"
|
||||
"MOV R10, R6\n"
|
||||
"MOV R11, R7\n"
|
||||
"POP {R4-R7}\n"
|
||||
"POP {R0-R3}\n"
|
||||
"POP {R12}\n"
|
||||
"POP {LR}\n"
|
||||
"POP {PC}\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* Context Switch Trigger */
|
||||
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||
current_task_sp = current_context;
|
||||
next_task_sp = next_context;
|
||||
|
||||
/* Trigger PendSV */
|
||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||
}
|
||||
|
||||
/* Yield from ISR */
|
||||
void port_yield_from_isr(void) {
|
||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||
}
|
||||
|
||||
/* Enter Critical Section */
|
||||
void port_disable_interrupts(void) {
|
||||
__disable_irq();
|
||||
}
|
||||
|
||||
/* Exit Critical Section */
|
||||
void port_enable_interrupts(void) {
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
/* Get Current Exception Number */
|
||||
uint32_t port_get_current_exception(void) {
|
||||
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos;
|
||||
}
|
||||
|
||||
/* Initialize Task Stack */
|
||||
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||
void* parameters,
|
||||
uint32_t* stack_top) {
|
||||
uint32_t* stack_ptr = stack_top;
|
||||
|
||||
/* Align stack to 8 bytes */
|
||||
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x7);
|
||||
|
||||
/* Initial stack frame for Cortex-M0 */
|
||||
*(--stack_ptr) = 0x01000000; /* xPSR */
|
||||
*(--stack_ptr) = (uint32_t)task_function; /* PC */
|
||||
*(--stack_ptr) = 0xFFFFFFFD; /* LR (return to thread mode) */
|
||||
*(--stack_ptr) = 0x00000000; /* R12 */
|
||||
*(--stack_ptr) = 0x00000003; /* R3 */
|
||||
*(--stack_ptr) = 0x00000002; /* R2 */
|
||||
*(--stack_ptr) = 0x00000001; /* R1 */
|
||||
*(--stack_ptr) = (uint32_t)parameters; /* R0 */
|
||||
|
||||
/* Additional registers */
|
||||
*(--stack_ptr) = 0x0000000B; /* R11 */
|
||||
*(--stack_ptr) = 0x0000000A; /* R10 */
|
||||
*(--stack_ptr) = 0x00000009; /* R9 */
|
||||
*(--stack_ptr) = 0x00000008; /* R8 */
|
||||
*(--stack_ptr) = 0x00000007; /* R7 */
|
||||
*(--stack_ptr) = 0x00000006; /* R6 */
|
||||
*(--stack_ptr) = 0x00000005; /* R5 */
|
||||
*(--stack_ptr) = 0x00000004; /* R4 */
|
||||
|
||||
return stack_ptr;
|
||||
}
|
||||
|
||||
/* SysTick Handler */
|
||||
void SysTick_Handler(void) {
|
||||
/* Increment tick count */
|
||||
extern void kernel_tick_handler(void);
|
||||
kernel_tick_handler();
|
||||
}
|
||||
|
||||
/* PendSV Handler */
|
||||
void PendSV_Handler(void) {
|
||||
/* Save current context */
|
||||
__asm volatile (
|
||||
"MRS R0, PSP\n"
|
||||
"SUBS R0, R0, #32\n"
|
||||
"STMIA R0!, {R4-R7}\n"
|
||||
"MOV R4, R8\n"
|
||||
"MOV R5, R9\n"
|
||||
"MOV R6, R10\n"
|
||||
"MOV R7, R11\n"
|
||||
"STMIA R0!, {R4-R7}\n"
|
||||
"SUBS R0, R0, #32\n"
|
||||
"LDR R1, =current_task_sp\n"
|
||||
"STR R0, [R1]\n"
|
||||
);
|
||||
|
||||
/* Load next context */
|
||||
__asm volatile (
|
||||
"LDR R0, =next_task_sp\n"
|
||||
"LDR R1, [R0]\n"
|
||||
"LDMIA R1!, {R4-R7}\n"
|
||||
"MOV R8, R4\n"
|
||||
"MOV R9, R5\n"
|
||||
"MOV R10, R6\n"
|
||||
"MOV R11, R7\n"
|
||||
"LDMIA R1!, {R4-R7}\n"
|
||||
"MSR PSP, R1\n"
|
||||
"BX LR\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* SVC Handler */
|
||||
void SVC_Handler(void) {
|
||||
/* Handle system calls */
|
||||
__asm volatile (
|
||||
"TST LR, #4\n"
|
||||
"ITE EQ\n"
|
||||
"MRSEQ R0, MSP\n"
|
||||
"MRSNE R0, PSP\n"
|
||||
"LDR R0, [R0, #24]\n"
|
||||
"LDRB R0, [R0, #-2]\n"
|
||||
"BX LR\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* Hard Fault Handler */
|
||||
void HardFault_Handler(void) {
|
||||
/* Save fault information */
|
||||
uint32_t fault_address;
|
||||
uint32_t fault_status;
|
||||
|
||||
__asm volatile (
|
||||
"MRS %0, BFAR\n"
|
||||
"MRS %1, BFSR\n"
|
||||
: "=r" (fault_address), "=r" (fault_status)
|
||||
);
|
||||
|
||||
/* Call fault handler */
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(1, fault_address, fault_status);
|
||||
|
||||
/* Infinite loop */
|
||||
while(1);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* @file port_asm.s
|
||||
* @brief ARM Cortex-M0 assembly routines
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.thumb
|
||||
.arch armv6-m
|
||||
|
||||
.section .text
|
||||
|
||||
/* Global symbols */
|
||||
.global current_task_sp
|
||||
.global next_task_sp
|
||||
.global port_context_switch
|
||||
.global port_start_first_task
|
||||
.global port_initialize_task_stack
|
||||
|
||||
/* Variables */
|
||||
.section .bss
|
||||
.align 2
|
||||
current_task_sp: .word 0
|
||||
next_task_sp: .word 0
|
||||
|
||||
.section .text
|
||||
.thumb_func
|
||||
|
||||
/* Start First Task */
|
||||
port_start_first_task:
|
||||
/* Load task stack pointer */
|
||||
ldr r0, =next_task_sp
|
||||
ldr r1, [r0]
|
||||
|
||||
/* Set PSP */
|
||||
msr PSP, r1
|
||||
|
||||
/* Switch to PSP */
|
||||
movs r0, #2
|
||||
msr CONTROL, r0
|
||||
isb
|
||||
|
||||
/* Restore context */
|
||||
pop {r4-r7}
|
||||
mov r8, r4
|
||||
mov r9, r5
|
||||
mov r10, r6
|
||||
mov r11, r7
|
||||
pop {r4-r7}
|
||||
pop {r0-r3}
|
||||
pop {r12}
|
||||
pop {lr}
|
||||
pop {pc}
|
||||
|
||||
/* PendSV Handler */
|
||||
PendSV_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
subs r0, r0, #32
|
||||
stmia r0!, {r4-r7}
|
||||
mov r4, r8
|
||||
mov r5, r9
|
||||
mov r6, r10
|
||||
mov r7, r11
|
||||
stmia r0!, {r4-r7}
|
||||
subs r0, r0, #32
|
||||
ldr r1, =current_task_sp
|
||||
str r0, [r1]
|
||||
|
||||
/* Load next context */
|
||||
ldr r0, =next_task_sp
|
||||
ldr r1, [r0]
|
||||
ldmia r1!, {r4-r7}
|
||||
mov r8, r4
|
||||
mov r9, r5
|
||||
mov r10, r6
|
||||
mov r11, r7
|
||||
ldmia r1!, {r4-r7}
|
||||
msr PSP, r1
|
||||
bx lr
|
||||
|
||||
/* SVC Handler */
|
||||
SVC_Handler:
|
||||
tst lr, #4
|
||||
ite eq
|
||||
mrseq r0, MSP
|
||||
mrsne r0, PSP
|
||||
ldr r0, [r0, #24]
|
||||
ldrb r0, [r0, #-2]
|
||||
bx lr
|
||||
|
||||
/* Hard Fault Handler */
|
||||
HardFault_Handler:
|
||||
/* Save registers */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r7}
|
||||
mov r4, r8
|
||||
mov r5, r9
|
||||
mov r6, r10
|
||||
mov r7, r11
|
||||
stmdb r0!, {r4-r7}
|
||||
|
||||
/* Get fault information */
|
||||
mrs r0, BFAR
|
||||
mrs r1, BFSR
|
||||
|
||||
/* Call fault handler */
|
||||
movs r2, #1
|
||||
bl fault_handler_process
|
||||
|
||||
/* Infinite loop */
|
||||
b .
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file portmacro.h
|
||||
* @brief ARM Cortex-M0 specific macros
|
||||
*/
|
||||
|
||||
#ifndef PORTMACRO_H
|
||||
#define PORTMACRO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Data Types */
|
||||
typedef uint32_t port_stack_type_t;
|
||||
typedef uint32_t port_base_type_t;
|
||||
|
||||
/* Architecture Constants */
|
||||
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||
#define PORT_BYTE_ALIGNMENT 8
|
||||
#define PORT_MAX_SYSCALL_INTERRUPT_PRIORITY 3
|
||||
|
||||
/* Critical Section Macros */
|
||||
#define portENTER_CRITICAL() __disable_irq()
|
||||
#define portEXIT_CRITICAL() __enable_irq()
|
||||
|
||||
/* Interrupt Control */
|
||||
#define portENABLE_INTERRUPTS() __enable_irq()
|
||||
#define portDISABLE_INTERRUPTS() __disable_irq()
|
||||
|
||||
/* Context Switch */
|
||||
#define portYIELD() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||
#define portYIELD_FROM_ISR() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||
|
||||
/* Memory Barriers */
|
||||
#define portMEMORY_BARRIER() __asm volatile("DMB")
|
||||
#define portSYNC_BARRIER() __asm volatile("DSB")
|
||||
#define portINSTRUCTION_BARRIER() __asm volatile("ISB")
|
||||
|
||||
/* NOP */
|
||||
#define portNOP() __asm volatile("NOP")
|
||||
|
||||
#endif /* PORTMACRO_H */
|
||||
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* @file port.c
|
||||
* @brief ARM Cortex-M3 architecture specific port
|
||||
* @note Cortex-M3 is widely used in automotive ECUs
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include "scheduler.h"
|
||||
#include "portmacro.h"
|
||||
|
||||
/* Context storage */
|
||||
uint32_t* current_task_sp = NULL;
|
||||
uint32_t* next_task_sp = NULL;
|
||||
|
||||
/* Exception priorities */
|
||||
#define SYSTICK_PRIORITY 0xFF
|
||||
#define PENDSV_PRIORITY 0xFF
|
||||
#define SVC_PRIORITY 0x00
|
||||
|
||||
/* Initialize Architecture Port */
|
||||
void port_init(void) {
|
||||
/* Set priority grouping - 4 bits preemption, 0 bits sub-priority */
|
||||
NVIC_SetPriorityGrouping(4);
|
||||
|
||||
/* Configure SysTick */
|
||||
SysTick->LOAD = (SystemCoreClock / 1000) - 1;
|
||||
SysTick->VAL = 0;
|
||||
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
|
||||
SysTick_CTRL_TICKINT_Msk |
|
||||
SysTick_CTRL_ENABLE_Msk;
|
||||
|
||||
/* Set exception priorities */
|
||||
NVIC_SetPriority(SysTick_IRQn, SYSTICK_PRIORITY);
|
||||
NVIC_SetPriority(PendSV_IRQn, PENDSV_PRIORITY);
|
||||
NVIC_SetPriority(SVCall_IRQn, SVC_PRIORITY);
|
||||
|
||||
/* Enable faults */
|
||||
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk |
|
||||
SCB_SHCSR_BUSFAULTENA_Msk |
|
||||
SCB_SHCSR_USGFAULTENA_Msk;
|
||||
}
|
||||
|
||||
/* Start First Task */
|
||||
void port_start_first_task(void) {
|
||||
TaskHandle_t first_task = scheduler_get_current_task();
|
||||
|
||||
if (first_task == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set PSP */
|
||||
__set_PSP((uint32_t)first_task->stack_pointer);
|
||||
|
||||
/* Switch to PSP */
|
||||
__set_CONTROL(0x02);
|
||||
__ISB();
|
||||
|
||||
/* Restore context */
|
||||
__asm volatile (
|
||||
"LDMIA R0!, {R4-R11}\n"
|
||||
"MSR PSP, R0\n"
|
||||
"MOV LR, #0xFFFFFFFD\n"
|
||||
"BX LR\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* Context Switch */
|
||||
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||
current_task_sp = current_context;
|
||||
next_task_sp = next_context;
|
||||
|
||||
/* Trigger PendSV */
|
||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||
|
||||
/* Data synchronization barrier */
|
||||
__DSB();
|
||||
__ISB();
|
||||
}
|
||||
|
||||
/* Yield */
|
||||
void port_yield(void) {
|
||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||
}
|
||||
|
||||
/* Yield from ISR */
|
||||
void port_yield_from_isr(void) {
|
||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||
}
|
||||
|
||||
/* Enter Critical Section */
|
||||
void port_disable_interrupts(void) {
|
||||
__disable_irq();
|
||||
}
|
||||
|
||||
/* Exit Critical Section */
|
||||
void port_enable_interrupts(void) {
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
/* Get Current Exception Number */
|
||||
uint32_t port_get_current_exception(void) {
|
||||
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos;
|
||||
}
|
||||
|
||||
/* Initialize Task Stack */
|
||||
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||
void* parameters,
|
||||
uint32_t* stack_top) {
|
||||
uint32_t* stack_ptr = stack_top;
|
||||
|
||||
/* 8-byte alignment */
|
||||
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x7);
|
||||
|
||||
/* Exception frame */
|
||||
*(--stack_ptr) = 0x01000000; /* xPSR */
|
||||
*(--stack_ptr) = (uint32_t)task_function; /* PC */
|
||||
*(--stack_ptr) = 0xFFFFFFFD; /* LR */
|
||||
*(--stack_ptr) = 0x00000000; /* R12 */
|
||||
*(--stack_ptr) = 0x00000003; /* R3 */
|
||||
*(--stack_ptr) = 0x00000002; /* R2 */
|
||||
*(--stack_ptr) = 0x00000001; /* R1 */
|
||||
*(--stack_ptr) = (uint32_t)parameters; /* R0 */
|
||||
|
||||
/* Additional context */
|
||||
*(--stack_ptr) = 0x0000000B; /* R11 */
|
||||
*(--stack_ptr) = 0x0000000A; /* R10 */
|
||||
*(--stack_ptr) = 0x00000009; /* R9 */
|
||||
*(--stack_ptr) = 0x00000008; /* R8 */
|
||||
*(--stack_ptr) = 0x00000007; /* R7 */
|
||||
*(--stack_ptr) = 0x00000006; /* R6 */
|
||||
*(--stack_ptr) = 0x00000005; /* R5 */
|
||||
*(--stack_ptr) = 0x00000004; /* R4 */
|
||||
|
||||
return stack_ptr;
|
||||
}
|
||||
|
||||
/* SysTick Handler */
|
||||
void SysTick_Handler(void) {
|
||||
extern void kernel_tick_handler(void);
|
||||
kernel_tick_handler();
|
||||
}
|
||||
|
||||
/* PendSV Handler */
|
||||
__attribute__((naked)) void PendSV_Handler(void) {
|
||||
__asm volatile (
|
||||
"MRS R0, PSP\n"
|
||||
"STMDB R0!, {R4-R11}\n"
|
||||
"LDR R1, =current_task_sp\n"
|
||||
"STR R0, [R1]\n"
|
||||
"LDR R0, =next_task_sp\n"
|
||||
"LDR R1, [R0]\n"
|
||||
"LDMIA R1!, {R4-R11}\n"
|
||||
"MSR PSP, R1\n"
|
||||
"BX LR\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* SVC Handler */
|
||||
__attribute__((naked)) void SVC_Handler(void) {
|
||||
__asm volatile (
|
||||
"TST LR, #4\n"
|
||||
"ITE EQ\n"
|
||||
"MRSEQ R0, MSP\n"
|
||||
"MRSNE R0, PSP\n"
|
||||
"LDR R0, [R0, #24]\n"
|
||||
"LDRB R0, [R0, #-2]\n"
|
||||
"PUSH {LR}\n"
|
||||
"BL svc_handler\n"
|
||||
"POP {LR}\n"
|
||||
"BX LR\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* Memory Management Fault Handler */
|
||||
void MemManage_Handler(void) {
|
||||
uint32_t fault_address;
|
||||
uint32_t fault_status;
|
||||
|
||||
fault_address = SCB->MMFAR;
|
||||
fault_status = SCB->CFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(2, fault_address, fault_status);
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
/* Bus Fault Handler */
|
||||
void BusFault_Handler(void) {
|
||||
uint32_t fault_address;
|
||||
uint32_t fault_status;
|
||||
|
||||
fault_address = SCB->BFAR;
|
||||
fault_status = SCB->CFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(3, fault_address, fault_status);
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
/* Usage Fault Handler */
|
||||
void UsageFault_Handler(void) {
|
||||
uint32_t fault_status = SCB->CFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(4, 0, fault_status);
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
/* Hard Fault Handler */
|
||||
void HardFault_Handler(void) {
|
||||
uint32_t fault_address = 0;
|
||||
uint32_t fault_status = SCB->HFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(1, fault_address, fault_status);
|
||||
|
||||
while(1);
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* @file port_asm.s
|
||||
* @brief ARM Cortex-M3 assembly routines
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.thumb
|
||||
.arch armv7-m
|
||||
|
||||
.section .text
|
||||
|
||||
/* Global symbols */
|
||||
.global current_task_sp
|
||||
.global next_task_sp
|
||||
.global port_context_switch
|
||||
.global port_start_first_task
|
||||
.global port_initialize_task_stack
|
||||
.global port_disable_interrupts
|
||||
.global port_enable_interrupts
|
||||
|
||||
/* Variables */
|
||||
.section .bss
|
||||
.align 2
|
||||
current_task_sp: .word 0
|
||||
next_task_sp: .word 0
|
||||
|
||||
.section .text
|
||||
.thumb_func
|
||||
|
||||
/* Start First Task */
|
||||
port_start_first_task:
|
||||
/* Load task stack pointer */
|
||||
ldr r0, =next_task_sp
|
||||
ldr r1, [r0]
|
||||
|
||||
/* Set PSP */
|
||||
msr PSP, r1
|
||||
|
||||
/* Switch to PSP */
|
||||
mov r0, #2
|
||||
msr CONTROL, r0
|
||||
isb
|
||||
|
||||
/* Restore context */
|
||||
ldmia r1!, {r4-r11}
|
||||
msr PSP, r1
|
||||
mov lr, #0xFFFFFFFD
|
||||
bx lr
|
||||
|
||||
/* PendSV Handler */
|
||||
PendSV_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
ldr r1, =current_task_sp
|
||||
str r0, [r1]
|
||||
|
||||
/* Load next context */
|
||||
ldr r0, =next_task_sp
|
||||
ldr r1, [r0]
|
||||
ldmia r1!, {r4-r11}
|
||||
msr PSP, r1
|
||||
bx lr
|
||||
|
||||
/* SVC Handler */
|
||||
SVC_Handler:
|
||||
tst lr, #4
|
||||
ite eq
|
||||
mrseq r0, MSP
|
||||
mrsne r0, PSP
|
||||
ldr r0, [r0, #24]
|
||||
ldrb r0, [r0, #-2]
|
||||
push {lr}
|
||||
bl svc_handler
|
||||
pop {lr}
|
||||
bx lr
|
||||
|
||||
/* Disable Interrupts */
|
||||
port_disable_interrupts:
|
||||
cpsid i
|
||||
bx lr
|
||||
|
||||
/* Enable Interrupts */
|
||||
port_enable_interrupts:
|
||||
cpsie i
|
||||
bx lr
|
||||
|
||||
/* Memory Management Fault */
|
||||
MemManage_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault information */
|
||||
ldr r1, =0xE000ED34 /* MMFAR */
|
||||
ldr r1, [r1]
|
||||
ldr r2, =0xE000ED28 /* CFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #2
|
||||
bl fault_handler_process
|
||||
|
||||
/* Infinite loop */
|
||||
b .
|
||||
|
||||
/* Bus Fault Handler */
|
||||
BusFault_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault information */
|
||||
ldr r1, =0xE000ED38 /* BFAR */
|
||||
ldr r1, [r1]
|
||||
ldr r2, =0xE000ED28 /* CFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #3
|
||||
bl fault_handler_process
|
||||
|
||||
/* Infinite loop */
|
||||
b .
|
||||
|
||||
/* Usage Fault Handler */
|
||||
UsageFault_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault status */
|
||||
ldr r2, =0xE000ED28 /* CFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #4
|
||||
movs r1, #0
|
||||
bl fault_handler_process
|
||||
|
||||
/* Infinite loop */
|
||||
b .
|
||||
|
||||
/* Hard Fault Handler */
|
||||
HardFault_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault status */
|
||||
ldr r2, =0xE000ED2C /* HFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #1
|
||||
movs r1, #0
|
||||
bl fault_handler_process
|
||||
|
||||
/* Infinite loop */
|
||||
b .
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file portmacro.h
|
||||
* @brief ARM Cortex-M3 specific macros
|
||||
*/
|
||||
|
||||
#ifndef PORTMACRO_H
|
||||
#define PORTMACRO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Data Types */
|
||||
typedef uint32_t port_stack_type_t;
|
||||
typedef uint32_t port_base_type_t;
|
||||
|
||||
/* Architecture Constants */
|
||||
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||
#define PORT_BYTE_ALIGNMENT 8
|
||||
#define PORT_MAX_SYSCALL_INTERRUPT_PRIORITY 0x50
|
||||
|
||||
/* Critical Section Macros */
|
||||
#define portENTER_CRITICAL() __asm volatile("CPSID I" ::: "memory")
|
||||
#define portEXIT_CRITICAL() __asm volatile("CPSIE I" ::: "memory")
|
||||
|
||||
/* Interrupt Control */
|
||||
#define portENABLE_INTERRUPTS() __asm volatile("CPSIE I" ::: "memory")
|
||||
#define portDISABLE_INTERRUPTS() __asm volatile("CPSID I" ::: "memory")
|
||||
|
||||
/* Context Switch */
|
||||
#define portYIELD() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||
#define portYIELD_FROM_ISR() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||
|
||||
/* Memory Barriers */
|
||||
#define portMEMORY_BARRIER() __asm volatile("DMB" ::: "memory")
|
||||
#define portSYNC_BARRIER() __asm volatile("DSB" ::: "memory")
|
||||
#define portINSTRUCTION_BARRIER() __asm volatile("ISB" ::: "memory")
|
||||
|
||||
/* NOP */
|
||||
#define portNOP() __asm volatile("NOP")
|
||||
|
||||
/* Endian Definition */
|
||||
#define portBYTE_ORDER LITTLE_ENDIAN
|
||||
|
||||
#endif /* PORTMACRO_H */
|
||||
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* @file port.c
|
||||
* @brief ARM Cortex-M4 architecture specific port with FPU support
|
||||
* @note Cortex-M4 with FPU is common in modern automotive MCUs
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include "scheduler.h"
|
||||
#include "portmacro.h"
|
||||
|
||||
/* Context storage */
|
||||
uint32_t* current_task_sp = NULL;
|
||||
uint32_t* next_task_sp = NULL;
|
||||
|
||||
/* FPU context storage */
|
||||
uint32_t current_fpu_context[32];
|
||||
uint32_t next_fpu_context[32];
|
||||
|
||||
/* Exception priorities */
|
||||
#define SYSTICK_PRIORITY 0xFF
|
||||
#define PENDSV_PRIORITY 0xFF
|
||||
#define SVC_PRIORITY 0x00
|
||||
|
||||
/* Initialize Architecture Port */
|
||||
void port_init(void) {
|
||||
/* Set priority grouping - 4 bits preemption, 0 bits sub-priority */
|
||||
NVIC_SetPriorityGrouping(4);
|
||||
|
||||
/* Enable FPU */
|
||||
SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2));
|
||||
|
||||
/* Configure SysTick */
|
||||
SysTick->LOAD = (SystemCoreClock / 1000) - 1;
|
||||
SysTick->VAL = 0;
|
||||
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
|
||||
SysTick_CTRL_TICKINT_Msk |
|
||||
SysTick_CTRL_ENABLE_Msk;
|
||||
|
||||
/* Set exception priorities */
|
||||
NVIC_SetPriority(SysTick_IRQn, SYSTICK_PRIORITY);
|
||||
NVIC_SetPriority(PendSV_IRQn, PENDSV_PRIORITY);
|
||||
NVIC_SetPriority(SVCall_IRQn, SVC_PRIORITY);
|
||||
|
||||
/* Enable faults */
|
||||
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk |
|
||||
SCB_SHCSR_BUSFAULTENA_Msk |
|
||||
SCB_SHCSR_USGFAULTENA_Msk;
|
||||
|
||||
/* Enable divide by zero trap */
|
||||
SCB->CCR |= SCB_CCR_DIV_0_TRP_Msk;
|
||||
|
||||
/* Enable unaligned access trap */
|
||||
SCB->CCR |= SCB_CCR_UNALIGN_TRP_Msk;
|
||||
}
|
||||
|
||||
/* Start First Task */
|
||||
void port_start_first_task(void) {
|
||||
TaskHandle_t first_task = scheduler_get_current_task();
|
||||
|
||||
if (first_task == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Restore FPU context if task uses FPU */
|
||||
if (first_task->context[31] & 0x10) {
|
||||
/* FPU was used - restore FPU registers */
|
||||
__asm volatile (
|
||||
"VLDM R0!, {S16-S31}\n"
|
||||
"VLDM R0!, {S0-S15}\n"
|
||||
:
|
||||
: "r" (first_task->context)
|
||||
);
|
||||
}
|
||||
|
||||
/* Set PSP */
|
||||
__set_PSP((uint32_t)first_task->stack_pointer);
|
||||
|
||||
/* Switch to PSP */
|
||||
__set_CONTROL(0x02);
|
||||
__ISB();
|
||||
|
||||
/* Restore context */
|
||||
__asm volatile (
|
||||
"LDMIA R0!, {R4-R11}\n"
|
||||
"MSR PSP, R0\n"
|
||||
"MOV LR, #0xFFFFFFFD\n"
|
||||
"BX LR\n"
|
||||
:
|
||||
: "r" (first_task->stack_pointer)
|
||||
);
|
||||
}
|
||||
|
||||
/* Context Switch */
|
||||
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||
current_task_sp = current_context;
|
||||
next_task_sp = next_context;
|
||||
|
||||
/* Trigger PendSV */
|
||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||
|
||||
/* Data synchronization barrier */
|
||||
__DSB();
|
||||
__ISB();
|
||||
}
|
||||
|
||||
/* Initialize Task Stack with FPU support */
|
||||
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||
void* parameters,
|
||||
uint32_t* stack_top) {
|
||||
uint32_t* stack_ptr = stack_top;
|
||||
|
||||
/* 8-byte alignment */
|
||||
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x7);
|
||||
|
||||
/* Exception frame with FPU */
|
||||
*(--stack_ptr) = 0x01000000; /* xPSR */
|
||||
*(--stack_ptr) = (uint32_t)task_function; /* PC */
|
||||
*(--stack_ptr) = 0xFFFFFFFD; /* LR */
|
||||
*(--stack_ptr) = 0x00000000; /* R12 */
|
||||
*(--stack_ptr) = 0x00000003; /* R3 */
|
||||
*(--stack_ptr) = 0x00000002; /* R2 */
|
||||
*(--stack_ptr) = 0x00000001; /* R1 */
|
||||
*(--stack_ptr) = (uint32_t)parameters; /* R0 */
|
||||
|
||||
/* Additional context */
|
||||
*(--stack_ptr) = 0x0000000B; /* R11 */
|
||||
*(--stack_ptr) = 0x0000000A; /* R10 */
|
||||
*(--stack_ptr) = 0x00000009; /* R9 */
|
||||
*(--stack_ptr) = 0x00000008; /* R8 */
|
||||
*(--stack_ptr) = 0x00000007; /* R7 */
|
||||
*(--stack_ptr) = 0x00000006; /* R6 */
|
||||
*(--stack_ptr) = 0x00000005; /* R5 */
|
||||
*(--stack_ptr) = 0x00000004; /* R4 */
|
||||
|
||||
/* FPU context (S0-S31) */
|
||||
for (int i = 0; i < 32; i++) {
|
||||
*(--stack_ptr) = 0;
|
||||
}
|
||||
|
||||
/* FPSCR */
|
||||
*(--stack_ptr) = 0;
|
||||
|
||||
return stack_ptr;
|
||||
}
|
||||
|
||||
/* SysTick Handler */
|
||||
void SysTick_Handler(void) {
|
||||
extern void kernel_tick_handler(void);
|
||||
kernel_tick_handler();
|
||||
}
|
||||
|
||||
/* PendSV Handler with FPU context saving */
|
||||
__attribute__((naked)) void PendSV_Handler(void) {
|
||||
__asm volatile (
|
||||
/* Check if FPU was used */
|
||||
"TST LR, #0x10\n"
|
||||
"IT EQ\n"
|
||||
"BEQ 1f\n"
|
||||
|
||||
/* Save FPU context */
|
||||
"MRS R0, CONTROL\n"
|
||||
"TST R0, #0x04\n"
|
||||
"IT EQ\n"
|
||||
"BEQ 1f\n"
|
||||
|
||||
/* Save FPU registers */
|
||||
"VSTMDB R0!, {S16-S31}\n"
|
||||
"VSTMDB R0!, {S0-S15}\n"
|
||||
|
||||
"1:\n"
|
||||
/* Save core context */
|
||||
"MRS R0, PSP\n"
|
||||
"STMDB R0!, {R4-R11}\n"
|
||||
"LDR R1, =current_task_sp\n"
|
||||
"STR R0, [R1]\n"
|
||||
|
||||
/* Load next context */
|
||||
"LDR R0, =next_task_sp\n"
|
||||
"LDR R1, [R0]\n"
|
||||
"LDMIA R1!, {R4-R11}\n"
|
||||
"MSR PSP, R1\n"
|
||||
|
||||
/* Restore FPU context if needed */
|
||||
"TST LR, #0x10\n"
|
||||
"IT EQ\n"
|
||||
"BEQ 2f\n"
|
||||
|
||||
/* Restore FPU registers */
|
||||
"VLDMIA R0!, {S0-S15}\n"
|
||||
"VLDMIA R0!, {S16-S31}\n"
|
||||
|
||||
"2:\n"
|
||||
"BX LR\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* SVC Handler */
|
||||
__attribute__((naked)) void SVC_Handler(void) {
|
||||
__asm volatile (
|
||||
"TST LR, #4\n"
|
||||
"ITE EQ\n"
|
||||
"MRSEQ R0, MSP\n"
|
||||
"MRSNE R0, PSP\n"
|
||||
"LDR R0, [R0, #24]\n"
|
||||
"LDRB R0, [R0, #-2]\n"
|
||||
"PUSH {LR}\n"
|
||||
"BL svc_handler\n"
|
||||
"POP {LR}\n"
|
||||
"BX LR\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* Fault Handlers */
|
||||
void MemManage_Handler(void) {
|
||||
uint32_t fault_address = SCB->MMFAR;
|
||||
uint32_t fault_status = SCB->CFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(2, fault_address, fault_status);
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
void BusFault_Handler(void) {
|
||||
uint32_t fault_address = SCB->BFAR;
|
||||
uint32_t fault_status = SCB->CFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(3, fault_address, fault_status);
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
void UsageFault_Handler(void) {
|
||||
uint32_t fault_status = SCB->CFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(4, 0, fault_status);
|
||||
|
||||
while(1);
|
||||
}
|
||||
|
||||
void HardFault_Handler(void) {
|
||||
uint32_t fault_status = SCB->HFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(1, 0, fault_status);
|
||||
|
||||
while(1);
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* @file port_asm.s
|
||||
* @brief ARM Cortex-M4 assembly routines with FPU support
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.thumb
|
||||
.arch armv7e-m
|
||||
.fpu fpv4-sp-d16
|
||||
|
||||
.section .text
|
||||
|
||||
/* Global symbols */
|
||||
.global current_task_sp
|
||||
.global next_task_sp
|
||||
.global port_context_switch
|
||||
.global port_start_first_task
|
||||
.global port_initialize_task_stack
|
||||
.global port_disable_interrupts
|
||||
.global port_enable_interrupts
|
||||
.global port_enable_fpu
|
||||
.global port_disable_fpu
|
||||
|
||||
/* Variables */
|
||||
.section .bss
|
||||
.align 3
|
||||
current_task_sp: .word 0
|
||||
next_task_sp: .word 0
|
||||
|
||||
.section .text
|
||||
.thumb_func
|
||||
|
||||
/* Start First Task */
|
||||
port_start_first_task:
|
||||
/* Load task stack pointer */
|
||||
ldr r0, =next_task_sp
|
||||
ldr r1, [r0]
|
||||
|
||||
/* Check if FPU context needs restoring */
|
||||
tst lr, #0x10
|
||||
beq 1f
|
||||
|
||||
/* Restore FPU registers */
|
||||
add r1, r1, #64
|
||||
vldmia r1!, {s16-s31}
|
||||
vldmia r1!, {s0-s15}
|
||||
sub r1, r1, #128
|
||||
|
||||
1:
|
||||
/* Set PSP */
|
||||
msr PSP, r1
|
||||
|
||||
/* Switch to PSP */
|
||||
mov r0, #2
|
||||
msr CONTROL, r0
|
||||
isb
|
||||
|
||||
/* Restore core context */
|
||||
ldmia r1!, {r4-r11}
|
||||
msr PSP, r1
|
||||
mov lr, #0xFFFFFFFD
|
||||
bx lr
|
||||
|
||||
/* PendSV Handler */
|
||||
PendSV_Handler:
|
||||
/* Check if using PSP */
|
||||
mrs r0, CONTROL
|
||||
tst r0, #2
|
||||
beq 1f
|
||||
|
||||
/* Check if FPU was used */
|
||||
tst lr, #0x10
|
||||
beq 1f
|
||||
|
||||
/* Save FPU context */
|
||||
mrs r0, PSP
|
||||
add r0, r0, #64
|
||||
vstmdb r0!, {s16-s31}
|
||||
vstmdb r0!, {s0-s15}
|
||||
|
||||
1:
|
||||
/* Save core context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
ldr r1, =current_task_sp
|
||||
str r0, [r1]
|
||||
|
||||
/* Load next context */
|
||||
ldr r0, =next_task_sp
|
||||
ldr r1, [r0]
|
||||
ldmia r1!, {r4-r11}
|
||||
msr PSP, r1
|
||||
|
||||
/* Restore FPU context if needed */
|
||||
tst lr, #0x10
|
||||
beq 2f
|
||||
|
||||
/* Restore FPU registers */
|
||||
add r1, r1, #64
|
||||
vldmia r1!, {s0-s15}
|
||||
vldmia r1!, {s16-s31}
|
||||
|
||||
2:
|
||||
/* Return from exception */
|
||||
bx lr
|
||||
|
||||
/* SVC Handler */
|
||||
SVC_Handler:
|
||||
tst lr, #4
|
||||
ite eq
|
||||
mrseq r0, MSP
|
||||
mrsne r0, PSP
|
||||
ldr r0, [r0, #24]
|
||||
ldrb r0, [r0, #-2]
|
||||
push {lr}
|
||||
bl svc_handler
|
||||
pop {lr}
|
||||
bx lr
|
||||
|
||||
/* Enable FPU */
|
||||
port_enable_fpu:
|
||||
/* Enable CP10 and CP11 */
|
||||
ldr r0, =0xE000ED88 /* CPACR */
|
||||
ldr r1, [r0]
|
||||
orr r1, r1, #(0xF << 20)
|
||||
str r1, [r0]
|
||||
dsb
|
||||
isb
|
||||
bx lr
|
||||
|
||||
/* Disable FPU */
|
||||
port_disable_fpu:
|
||||
/* Disable CP10 and CP11 */
|
||||
ldr r0, =0xE000ED88 /* CPACR */
|
||||
ldr r1, [r0]
|
||||
bic r1, r1, #(0xF << 20)
|
||||
str r1, [r0]
|
||||
dsb
|
||||
isb
|
||||
bx lr
|
||||
|
||||
/* Disable Interrupts */
|
||||
port_disable_interrupts:
|
||||
cpsid i
|
||||
bx lr
|
||||
|
||||
/* Enable Interrupts */
|
||||
port_enable_interrupts:
|
||||
cpsie i
|
||||
bx lr
|
||||
|
||||
/* Memory Management Fault */
|
||||
MemManage_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault information */
|
||||
ldr r1, =0xE000ED34 /* MMFAR */
|
||||
ldr r1, [r1]
|
||||
ldr r2, =0xE000ED28 /* CFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #2
|
||||
bl fault_handler_process
|
||||
|
||||
/* Infinite loop */
|
||||
b .
|
||||
|
||||
/* Bus Fault Handler */
|
||||
BusFault_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault information */
|
||||
ldr r1, =0xE000ED38 /* BFAR */
|
||||
ldr r1, [r1]
|
||||
ldr r2, =0xE000ED28 /* CFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #3
|
||||
bl fault_handler_process
|
||||
|
||||
/* Infinite loop */
|
||||
b .
|
||||
|
||||
/* Usage Fault Handler */
|
||||
UsageFault_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault status */
|
||||
ldr r2, =0xE000ED28 /* CFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #4
|
||||
movs r1, #0
|
||||
bl fault_handler_process
|
||||
|
||||
/* Infinite loop */
|
||||
b .
|
||||
|
||||
/* Hard Fault Handler */
|
||||
HardFault_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault status */
|
||||
ldr r2, =0xE000ED2C /* HFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #1
|
||||
movs r1, #0
|
||||
bl fault_handler_process
|
||||
|
||||
/* Infinite loop */
|
||||
b .
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* @file portmacro.h
|
||||
* @brief ARM Cortex-M4 specific macros with FPU support
|
||||
*/
|
||||
|
||||
#ifndef PORTMACRO_H
|
||||
#define PORTMACRO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "stm32f4xx.h" /* Adjust for specific MCU */
|
||||
|
||||
/* Data Types */
|
||||
typedef uint32_t port_stack_type_t;
|
||||
typedef uint32_t port_base_type_t;
|
||||
|
||||
/* Architecture Constants */
|
||||
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||
#define PORT_BYTE_ALIGNMENT 8
|
||||
#define PORT_MAX_SYSCALL_INTERRUPT_PRIORITY 0x50
|
||||
|
||||
/* Critical Section Macros */
|
||||
#define portENTER_CRITICAL() __asm volatile("CPSID I" ::: "memory")
|
||||
#define portEXIT_CRITICAL() __asm volatile("CPSIE I" ::: "memory")
|
||||
|
||||
/* Interrupt Control */
|
||||
#define portENABLE_INTERRUPTS() __asm volatile("CPSIE I" ::: "memory")
|
||||
#define portDISABLE_INTERRUPTS() __asm volatile("CPSID I" ::: "memory")
|
||||
|
||||
/* Context Switch */
|
||||
#define portYIELD() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||
#define portYIELD_FROM_ISR() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||
|
||||
/* Memory Barriers */
|
||||
#define portMEMORY_BARRIER() __asm volatile("DMB" ::: "memory")
|
||||
#define portSYNC_BARRIER() __asm volatile("DSB" ::: "memory")
|
||||
#define portINSTRUCTION_BARRIER() __asm volatile("ISB" ::: "memory")
|
||||
|
||||
/* NOP */
|
||||
#define portNOP() __asm volatile("NOP")
|
||||
|
||||
/* FPU Control */
|
||||
#define portENABLE_FPU() \
|
||||
do { \
|
||||
SCB->CPACR |= ((3UL << 10*2) | (3UL << 11*2)); \
|
||||
__DSB(); \
|
||||
__ISB(); \
|
||||
} while(0)
|
||||
|
||||
#define portDISABLE_FPU() \
|
||||
do { \
|
||||
SCB->CPACR &= ~((3UL << 10*2) | (3UL << 11*2)); \
|
||||
__DSB(); \
|
||||
__ISB(); \
|
||||
} while(0)
|
||||
|
||||
/* Endian Definition */
|
||||
#define portBYTE_ORDER LITTLE_ENDIAN
|
||||
|
||||
/* Optimization */
|
||||
#define portFORCE_INLINE __attribute__((always_inline)) inline
|
||||
|
||||
/* Task Utilities */
|
||||
#define portTASK_RETURN_ADDRESS (0xFFFFFFFD)
|
||||
|
||||
#endif /* PORTMACRO_H */
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @file port_common.h
|
||||
* @brief Common port interface definitions
|
||||
*/
|
||||
|
||||
#ifndef PORT_COMMON_H
|
||||
#define PORT_COMMON_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/* Common port function declarations */
|
||||
void port_common_init(void);
|
||||
void port_common_context_switch(TaskHandle_t current, TaskHandle_t next);
|
||||
uint32_t* port_common_init_stack(TaskFunction_t function,
|
||||
void* parameters,
|
||||
uint32_t* stack_top,
|
||||
uint32_t stack_size);
|
||||
void port_common_fault_handler(uint32_t fault_type, uint32_t fault_address,
|
||||
uint32_t fault_status);
|
||||
void port_enter_safe_state(void);
|
||||
void port_set_safe_outputs(void);
|
||||
void port_log_fault(void* fault_info);
|
||||
|
||||
/* Architecture-specific functions that must be implemented */
|
||||
void port_init(void);
|
||||
void port_start_first_task(void);
|
||||
void port_context_switch(uint32_t* current_context, uint32_t* next_context);
|
||||
uint32_t* port_initialize_task_stack(TaskFunction_t function,
|
||||
void* parameters,
|
||||
uint32_t* stack_top);
|
||||
void port_save_context(uint32_t* context);
|
||||
void port_restore_context(uint32_t* context);
|
||||
void port_disable_interrupts(void);
|
||||
void port_enable_interrupts(void);
|
||||
uint32_t port_get_current_exception(void);
|
||||
void port_service_watchdog(void);
|
||||
void port_yield(void);
|
||||
void port_yield_from_isr(void);
|
||||
|
||||
#endif /* PORT_COMMON_H */
|
||||
@@ -0,0 +1,262 @@
|
||||
/**
|
||||
* @file port.c
|
||||
* @brief Infineon TriCore TC3xx architecture specific port
|
||||
* @note TriCore is widely used in automotive powertrain and safety applications
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include "scheduler.h"
|
||||
#include "portmacro.h"
|
||||
#include "Ifx_Types.h"
|
||||
#include "IfxCpu.h"
|
||||
#include "IfxStm.h"
|
||||
#include "IfxScuWdt.h"
|
||||
|
||||
/* Context storage */
|
||||
uint32_t* current_task_sp = NULL;
|
||||
uint32_t* next_task_sp = NULL;
|
||||
|
||||
/* STM configuration for system tick */
|
||||
static IfxStm_CompareConfig stmCompareConfig;
|
||||
static volatile uint32_t stmTickCount = 0;
|
||||
|
||||
/* CSA (Context Save Area) management */
|
||||
#define MAX_CSA_AREAS 64
|
||||
static uint32_t csa_areas[MAX_CSA_AREAS][16] __attribute__((aligned(64)));
|
||||
static uint32_t csa_index = 0;
|
||||
|
||||
/* Interrupt priorities */
|
||||
#define SYSTICK_PRIORITY 255
|
||||
#define PENDSV_PRIORITY 255
|
||||
#define SVC_PRIORITY 0
|
||||
|
||||
/* Initialize Architecture Port */
|
||||
void port_init(void) {
|
||||
/* Initialize CPU configuration */
|
||||
IfxCpu_init();
|
||||
|
||||
/* Disable global interrupts during initialization */
|
||||
IfxCpu_disableInterrupts();
|
||||
|
||||
/* Initialize CSA areas */
|
||||
for (uint32_t i = 0; i < MAX_CSA_AREAS; i++) {
|
||||
/* Initialize PCXI register for each CSA */
|
||||
uint32_t pcxi_value = ((uint32_t)&csa_areas[i][0]) & 0xFFFFF000;
|
||||
pcxi_value |= (i << 16); /* Set previous context pointer */
|
||||
csa_areas[i][0] = pcxi_value;
|
||||
}
|
||||
|
||||
/* Configure STM for 1ms tick */
|
||||
IfxStm_initCompareConfig(&stmCompareConfig);
|
||||
stmCompareConfig.triggerPriority = SYSTICK_PRIORITY;
|
||||
stmCompareConfig.typeOfService = IfxSrc_Tos_cpu0;
|
||||
stmCompareConfig.ticks = IfxStm_getFrequency(&MODULE_STM0) / 1000;
|
||||
|
||||
/* Initialize STM compare */
|
||||
IfxStm_initCompare(&MODULE_STM0, &stmCompareConfig);
|
||||
|
||||
/* Enable safety watchdog */
|
||||
IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
|
||||
IfxScuWdt_clearCpuEndinit(IfxScuWdt_getCpuWatchdogPassword());
|
||||
IfxScuWdt_enableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword(),
|
||||
100, 200);
|
||||
IfxScuWdt_setCpuEndinit(IfxScuWdt_getCpuWatchdogPassword());
|
||||
|
||||
/* Enable global interrupts */
|
||||
IfxCpu_enableInterrupts();
|
||||
}
|
||||
|
||||
/* Start First Task */
|
||||
void port_start_first_task(void) {
|
||||
TaskHandle_t first_task = scheduler_get_current_task();
|
||||
|
||||
if (first_task == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get task context from CSA */
|
||||
uint32_t* context = (uint32_t*)first_task->context;
|
||||
uint32_t pcxi = context[0];
|
||||
uint32_t pc = context[1];
|
||||
uint32_t sp = context[2];
|
||||
|
||||
/* Set up context for first task execution */
|
||||
__asm volatile (
|
||||
"mov.a %%sp, %0\n" /* Set stack pointer */
|
||||
"mov %%a11, %1\n" /* Set return address */
|
||||
"mtsv %%pcxi, %2\n" /* Set context */
|
||||
"rslcx\n" /* Restore lower context */
|
||||
"rfe\n" /* Return from exception */
|
||||
:
|
||||
: "r" (sp), "r" (pc), "r" (pcxi)
|
||||
: "a11", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* Context Switch */
|
||||
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||
current_task_sp = current_context;
|
||||
next_task_sp = next_context;
|
||||
|
||||
/* Trigger software interrupt for context switch */
|
||||
__asm volatile (
|
||||
"syscall 0\n" /* System call for context switch */
|
||||
);
|
||||
}
|
||||
|
||||
/* Initialize Task Stack */
|
||||
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||
void* parameters,
|
||||
uint32_t* stack_top) {
|
||||
uint32_t* stack_ptr = stack_top;
|
||||
|
||||
/* Align to 64 bytes */
|
||||
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x3F);
|
||||
|
||||
/* Allocate CSA for task */
|
||||
if (csa_index >= MAX_CSA_AREAS) {
|
||||
return NULL; /* Out of CSA areas */
|
||||
}
|
||||
|
||||
uint32_t* csa = csa_areas[csa_index];
|
||||
csa_index++;
|
||||
|
||||
/* Initialize CSA */
|
||||
csa[0] = 0; /* PCXI will be set during first context switch */
|
||||
csa[1] = (uint32_t)task_function; /* PC */
|
||||
csa[2] = (uint32_t)stack_ptr; /* SP */
|
||||
csa[3] = (uint32_t)parameters; /* A4 (first argument) */
|
||||
|
||||
/* Initialize upper context registers */
|
||||
csa[4] = 0; /* A5 */
|
||||
csa[5] = 0; /* A6 */
|
||||
csa[6] = 0; /* A7 */
|
||||
csa[7] = 0; /* A8 */
|
||||
csa[8] = 0; /* A9 */
|
||||
csa[9] = 0; /* A10 */
|
||||
csa[10] = 0; /* A11 */
|
||||
csa[11] = 0; /* A12 */
|
||||
csa[12] = 0; /* A13 */
|
||||
csa[13] = 0; /* A14 */
|
||||
csa[14] = 0; /* A15 */
|
||||
csa[15] = 0; /* D8 */
|
||||
|
||||
/* Store CSA pointer in task context */
|
||||
TaskHandle_t task = scheduler_get_current_task();
|
||||
if (task != NULL) {
|
||||
task->context[0] = (uint32_t)csa;
|
||||
}
|
||||
|
||||
return stack_ptr;
|
||||
}
|
||||
|
||||
/* System Timer Interrupt Handler */
|
||||
IFX_INTERRUPT(stm_compare_match_isr, 0, SYSTICK_PRIORITY) {
|
||||
/* Clear interrupt flag */
|
||||
IfxStm_clearCompareFlag(&MODULE_STM0, stmCompareConfig.comparator);
|
||||
|
||||
/* Update compare value for next interrupt */
|
||||
IfxStm_increaseCompare(&MODULE_STM0, stmCompareConfig.comparator,
|
||||
IfxStm_getFrequency(&MODULE_STM0) / 1000);
|
||||
|
||||
/* Call kernel tick handler */
|
||||
extern void kernel_tick_handler(void);
|
||||
kernel_tick_handler();
|
||||
|
||||
/* Refresh watchdog */
|
||||
IfxScuWdt_clearSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
|
||||
}
|
||||
|
||||
/* Software Interrupt Handler for Context Switch */
|
||||
IFX_INTERRUPT(software_context_switch_isr, 0, PENDSV_PRIORITY) {
|
||||
__asm volatile (
|
||||
/* Save current context */
|
||||
"svlcx\n" /* Save lower context */
|
||||
"mov %%d15, %%a11\n" /* Save return address */
|
||||
|
||||
/* Store current stack pointer */
|
||||
"mov.a %%a15, %%sp\n"
|
||||
"mov.aa %0, %%a15\n"
|
||||
|
||||
/* Load next stack pointer */
|
||||
"mov.aa %%a15, %1\n"
|
||||
"mov.a %%sp, %%a15\n"
|
||||
|
||||
/* Restore lower context */
|
||||
"rslcx\n"
|
||||
"mov %%a11, %%d15\n" /* Restore return address */
|
||||
"rfe\n" /* Return from exception */
|
||||
:
|
||||
: "m" (current_task_sp), "m" (next_task_sp)
|
||||
: "a15", "d15", "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/* System Call Handler */
|
||||
IFX_INTERRUPT(syscall_handler, 0, SVC_PRIORITY) {
|
||||
/* Get system call number */
|
||||
uint32_t syscall_number;
|
||||
__asm volatile (
|
||||
"mov %0, %%d15\n" /* Get syscall number from D15 */
|
||||
: "=r" (syscall_number)
|
||||
);
|
||||
|
||||
/* Handle system calls */
|
||||
switch (syscall_number) {
|
||||
case 0: /* Context switch */
|
||||
software_context_switch_isr();
|
||||
break;
|
||||
case 1: /* Yield */
|
||||
scheduler_yield();
|
||||
break;
|
||||
case 2: /* Task exit */
|
||||
task_delete(scheduler_get_current_task());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Trap Handlers */
|
||||
IFX_INTERRUPT(trap_handler, 0, 0) {
|
||||
/* Get trap information */
|
||||
uint32_t trap_class;
|
||||
uint32_t trap_id;
|
||||
|
||||
__asm volatile (
|
||||
"mov %0, %%d15\n" /* Trap class */
|
||||
"mov %1, %%d14\n" /* Trap ID */
|
||||
: "=r" (trap_class), "=r" (trap_id)
|
||||
);
|
||||
|
||||
/* Call fault handler */
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(trap_class, trap_id, 0);
|
||||
|
||||
/* Enter safe state */
|
||||
while(1) {
|
||||
/* Safe state - wait for watchdog */
|
||||
IfxScuWdt_clearSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
|
||||
}
|
||||
}
|
||||
|
||||
/* Enter Critical Section */
|
||||
void port_disable_interrupts(void) {
|
||||
IfxCpu_disableInterrupts();
|
||||
}
|
||||
|
||||
/* Exit Critical Section */
|
||||
void port_enable_interrupts(void) {
|
||||
IfxCpu_enableInterrupts();
|
||||
}
|
||||
|
||||
/* Get Current Exception Number */
|
||||
uint32_t port_get_current_exception(void) {
|
||||
uint32_t icr;
|
||||
__asm volatile (
|
||||
"mfcr %0, $ICR\n" /* Get Interrupt Control Register */
|
||||
: "=r" (icr)
|
||||
);
|
||||
return (icr >> 16) & 0xFF; /* Extract current CPU priority */
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @file port_asm.s
|
||||
* @brief Infineon TriCore TC3xx assembly routines
|
||||
*/
|
||||
|
||||
.section .text
|
||||
.align 2
|
||||
|
||||
/* Global symbols */
|
||||
.global current_task_sp
|
||||
.global next_task_sp
|
||||
.global port_context_switch
|
||||
.global port_start_first_task
|
||||
.global port_disable_interrupts
|
||||
.global port_enable_interrupts
|
||||
|
||||
/* Variables */
|
||||
.section .bss
|
||||
.align 2
|
||||
current_task_sp: .word 0
|
||||
next_task_sp: .word 0
|
||||
|
||||
.section .text
|
||||
|
||||
/* Start First Task */
|
||||
port_start_first_task:
|
||||
/* Load task context */
|
||||
mov.a a15, [next_task_sp]
|
||||
ld.a a14, [a15] /* Load CSA pointer */
|
||||
|
||||
/* Set up context */
|
||||
mtsv pcxi, a14 /* Set previous context */
|
||||
ld.a sp, [a14+8] /* Load stack pointer */
|
||||
ld.a a11, [a14+4] /* Load return address */
|
||||
|
||||
/* Restore context and start */
|
||||
rslcx /* Restore lower context */
|
||||
rfe /* Return from exception */
|
||||
|
||||
/* Context Switch */
|
||||
port_context_switch:
|
||||
/* Save current context */
|
||||
svlcx /* Save lower context */
|
||||
|
||||
/* Store current stack pointer */
|
||||
mov.a a15, sp
|
||||
mov.aa [current_task_sp], a15
|
||||
|
||||
/* Load next stack pointer */
|
||||
mov.aa a15, [next_task_sp]
|
||||
mov.a sp, a15
|
||||
|
||||
/* Restore next context */
|
||||
rslcx /* Restore lower context */
|
||||
rfe /* Return from exception */
|
||||
|
||||
/* Disable Interrupts */
|
||||
port_disable_interrupts:
|
||||
disable /* Disable interrupts */
|
||||
ret
|
||||
|
||||
/* Enable Interrupts */
|
||||
port_enable_interrupts:
|
||||
enable /* Enable interrupts */
|
||||
ret
|
||||
|
||||
/* System Timer Interrupt */
|
||||
stm_compare_match_isr:
|
||||
/* Save context */
|
||||
svlcx
|
||||
|
||||
/* Clear interrupt */
|
||||
movh.a a15, 0xF000 /* STM base address */
|
||||
lea a15, [a15]0x0010 /* STM interrupt clear register */
|
||||
st.w [a15], 0x1 /* Clear compare match flag */
|
||||
|
||||
/* Update compare value */
|
||||
movh.a a15, 0xF000
|
||||
lea a15, [a15]0x0020 /* STM compare register */
|
||||
ld.w d15, [a15]
|
||||
movh.a a14, 0xF000
|
||||
lea a14, [a14]0x0024 /* STM compare update register */
|
||||
add d15, d15, 1000 /* Add 1ms */
|
||||
st.w [a14], d15
|
||||
|
||||
/* Call kernel tick handler */
|
||||
call kernel_tick_handler
|
||||
|
||||
/* Restore context */
|
||||
rslcx
|
||||
rfe
|
||||
|
||||
/* Trap Handler */
|
||||
trap_handler:
|
||||
/* Save trap information */
|
||||
mov d15, d15 /* Trap class */
|
||||
mov d14, d14 /* Trap ID */
|
||||
|
||||
/* Call fault handler */
|
||||
call fault_handler_process
|
||||
|
||||
/* Enter safe state */
|
||||
j .
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file portmacro.h
|
||||
* @brief Infineon TriCore TC3xx specific macros
|
||||
*/
|
||||
|
||||
#ifndef PORTMACRO_H
|
||||
#define PORTMACRO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Ifx_Types.h"
|
||||
#include "IfxCpu.h"
|
||||
|
||||
/* Data Types */
|
||||
typedef uint32_t port_stack_type_t;
|
||||
typedef uint32_t port_base_type_t;
|
||||
|
||||
/* Architecture Constants */
|
||||
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||
#define PORT_BYTE_ALIGNMENT 64
|
||||
|
||||
/* Critical Section Macros */
|
||||
#define portENTER_CRITICAL() IfxCpu_disableInterrupts()
|
||||
#define portEXIT_CRITICAL() IfxCpu_enableInterrupts()
|
||||
|
||||
/* Interrupt Control */
|
||||
#define portENABLE_INTERRUPTS() IfxCpu_enableInterrupts()
|
||||
#define portDISABLE_INTERRUPTS() IfxCpu_disableInterrupts()
|
||||
|
||||
/* Context Switch */
|
||||
#define portYIELD() __asm volatile("syscall 0")
|
||||
#define portYIELD_FROM_ISR() __asm volatile("syscall 0")
|
||||
|
||||
/* Memory Barriers */
|
||||
#define portMEMORY_BARRIER() __asm volatile("dsync")
|
||||
#define portSYNC_BARRIER() __asm volatile("dsync")
|
||||
#define portINSTRUCTION_BARRIER() __asm volatile("isync")
|
||||
|
||||
/* NOP */
|
||||
#define portNOP() __asm volatile("nop")
|
||||
|
||||
/* CSA Management */
|
||||
#define portALLOCATE_CSA() __port_allocate_csa()
|
||||
#define portFREE_CSA(csa) __port_free_csa(csa)
|
||||
|
||||
/* Safety Features */
|
||||
#define portENABLE_SAFETY_WATCHDOG(timeout) \
|
||||
IfxScuWdt_enableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword(), timeout, timeout*2)
|
||||
|
||||
#define portSERVICE_WATCHDOG() \
|
||||
IfxScuWdt_clearSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword())
|
||||
|
||||
#endif /* PORTMACRO_H */
|
||||
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* @file port.c
|
||||
* @brief NXP S32K architecture specific port
|
||||
* @note S32K is designed for automotive body and safety applications
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include "scheduler.h"
|
||||
#include "portmacro.h"
|
||||
#include "S32K144.h"
|
||||
#include "interrupt_manager.h"
|
||||
|
||||
/* Context storage */
|
||||
uint32_t* current_task_sp = NULL;
|
||||
uint32_t* next_task_sp = NULL;
|
||||
|
||||
/* LPIT configuration */
|
||||
#define LPIT_CHANNEL 0
|
||||
#define LPIT_TICK_PERIOD 1000 /* 1ms at 1MHz */
|
||||
|
||||
/* Interrupt priorities */
|
||||
#define SYSTICK_PRIORITY 15
|
||||
#define PENDSV_PRIORITY 15
|
||||
#define SVC_PRIORITY 0
|
||||
|
||||
/* Initialize Architecture Port */
|
||||
void port_init(void) {
|
||||
/* Disable global interrupts */
|
||||
__disable_irq();
|
||||
|
||||
/* Configure system clock */
|
||||
/* Assuming 8MHz external crystal, PLL to 160MHz */
|
||||
SCG->SPLLCSR = SCG_SPLLCSR_SPLLEN_MASK;
|
||||
SCG->SPLLDIV = SCG_SPLLDIV_SPLLDIV1(1) | SCG_SPLLDIV_SPLLDIV2(1);
|
||||
SCG->SPLLCFG = SCG_SPLLCFG_MULT(20); /* 8MHz * 20 = 160MHz */
|
||||
|
||||
/* Wait for PLL lock */
|
||||
while(!(SCG->SPLLCSR & SCG_SPLLCSR_SPLLVLD_MASK));
|
||||
|
||||
/* Switch to PLL */
|
||||
SCG->RCCR = SCG_RCCR_DIVCORE(1) | SCG_RCCR_DIVBUS(2) |
|
||||
SCG_RCCR_DIVSLOW(4) | SCG_RCCR_SCS(6);
|
||||
|
||||
/* Enable clock to LPIT */
|
||||
PCC->PCCn[PCC_LPIT0_INDEX] = PCC_PCCn_PCS(6) | PCC_PCCn_CGC_MASK;
|
||||
|
||||
/* Configure LPIT for system tick */
|
||||
LPIT0->MCR = LPIT_MCR_M_CEN_MASK; /* Enable module */
|
||||
|
||||
/* Configure channel 0 */
|
||||
LPIT0->TMR[LPIT_CHANNEL].TVAL = LPIT_TICK_PERIOD;
|
||||
LPIT0->TMR[LPIT_CHANNEL].TCTRL =
|
||||
LPIT_TMR_TCTRL_T_EN_MASK | /* Enable timer */
|
||||
LPIT_TMR_TCTRL_MODE_MASK; /* 32-bit counter mode */
|
||||
|
||||
/* Enable LPIT interrupt */
|
||||
LPIT0->MIER |= (1 << LPIT_CHANNEL);
|
||||
|
||||
/* Set interrupt priority */
|
||||
NVIC_SetPriority(LPIT0_IRQn, SYSTICK_PRIORITY);
|
||||
NVIC_EnableIRQ(LPIT0_IRQn);
|
||||
|
||||
/* Configure watchdog */
|
||||
WDOG->CNT = 0x1000; /* 4s timeout */
|
||||
WDOG->TOVAL = 0x1000;
|
||||
WDOG->CS = WDOG_CS_EN_MASK | WDOG_CS_CLK(1) |
|
||||
WDOG_CS_WIN_MASK | WDOG_CS_UPDATE_MASK;
|
||||
|
||||
/* Enable faults */
|
||||
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk |
|
||||
SCB_SHCSR_BUSFAULTENA_Msk |
|
||||
SCB_SHCSR_USGFAULTENA_Msk;
|
||||
|
||||
/* Enable global interrupts */
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
/* Start First Task */
|
||||
void port_start_first_task(void) {
|
||||
TaskHandle_t first_task = scheduler_get_current_task();
|
||||
|
||||
if (first_task == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set PSP */
|
||||
__set_PSP((uint32_t)first_task->stack_pointer);
|
||||
|
||||
/* Switch to PSP */
|
||||
__set_CONTROL(0x02);
|
||||
__ISB();
|
||||
|
||||
/* Restore context */
|
||||
__asm volatile (
|
||||
"LDMIA R0!, {R4-R11}\n"
|
||||
"MSR PSP, R0\n"
|
||||
"MOV LR, #0xFFFFFFFD\n"
|
||||
"BX LR\n"
|
||||
:
|
||||
: "r" (first_task->stack_pointer)
|
||||
);
|
||||
}
|
||||
|
||||
/* Context Switch */
|
||||
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||
current_task_sp = current_context;
|
||||
next_task_sp = next_context;
|
||||
|
||||
/* Trigger PendSV */
|
||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||
|
||||
/* Data synchronization barrier */
|
||||
__DSB();
|
||||
__ISB();
|
||||
}
|
||||
|
||||
/* Initialize Task Stack */
|
||||
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||
void* parameters,
|
||||
uint32_t* stack_top) {
|
||||
uint32_t* stack_ptr = stack_top;
|
||||
|
||||
/* 8-byte alignment */
|
||||
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0x7);
|
||||
|
||||
/* Exception frame */
|
||||
*(--stack_ptr) = 0x01000000; /* xPSR */
|
||||
*(--stack_ptr) = (uint32_t)task_function; /* PC */
|
||||
*(--stack_ptr) = 0xFFFFFFFD; /* LR */
|
||||
*(--stack_ptr) = 0x00000000; /* R12 */
|
||||
*(--stack_ptr) = 0x00000003; /* R3 */
|
||||
*(--stack_ptr) = 0x00000002; /* R2 */
|
||||
*(--stack_ptr) = 0x00000001; /* R1 */
|
||||
*(--stack_ptr) = (uint32_t)parameters; /* R0 */
|
||||
|
||||
/* Additional context */
|
||||
*(--stack_ptr) = 0x0000000B; /* R11 */
|
||||
*(--stack_ptr) = 0x0000000A; /* R10 */
|
||||
*(--stack_ptr) = 0x00000009; /* R9 */
|
||||
*(--stack_ptr) = 0x00000008; /* R8 */
|
||||
*(--stack_ptr) = 0x00000007; /* R7 */
|
||||
*(--stack_ptr) = 0x00000006; /* R6 */
|
||||
*(--stack_ptr) = 0x00000005; /* R5 */
|
||||
*(--stack_ptr) = 0x00000004; /* R4 */
|
||||
|
||||
return stack_ptr;
|
||||
}
|
||||
|
||||
/* LPIT Interrupt Handler */
|
||||
void LPIT0_IRQHandler(void) {
|
||||
/* Clear interrupt flag */
|
||||
LPIT0->MSR |= (1 << LPIT_CHANNEL);
|
||||
|
||||
/* Call kernel tick handler */
|
||||
extern void kernel_tick_handler(void);
|
||||
kernel_tick_handler();
|
||||
|
||||
/* Service watchdog */
|
||||
WDOG->CNT = 0xB480; /* Refresh sequence */
|
||||
WDOG->CNT = 0x4B80;
|
||||
}
|
||||
|
||||
/* PendSV Handler */
|
||||
__attribute__((naked)) void PendSV_Handler(void) {
|
||||
__asm volatile (
|
||||
"MRS R0, PSP\n"
|
||||
"STMDB R0!, {R4-R11}\n"
|
||||
"LDR R1, =current_task_sp\n"
|
||||
"STR R0, [R1]\n"
|
||||
"LDR R0, =next_task_sp\n"
|
||||
"LDR R1, [R0]\n"
|
||||
"LDMIA R1!, {R4-R11}\n"
|
||||
"MSR PSP, R1\n"
|
||||
"BX LR\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* SVC Handler */
|
||||
__attribute__((naked)) void SVC_Handler(void) {
|
||||
__asm volatile (
|
||||
"TST LR, #4\n"
|
||||
"ITE EQ\n"
|
||||
"MRSEQ R0, MSP\n"
|
||||
"MRSNE R0, PSP\n"
|
||||
"LDR R0, [R0, #24]\n"
|
||||
"LDRB R0, [R0, #-2]\n"
|
||||
"PUSH {LR}\n"
|
||||
"BL svc_handler\n"
|
||||
"POP {LR}\n"
|
||||
"BX LR\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* Fault Handlers */
|
||||
void MemManage_Handler(void) {
|
||||
uint32_t fault_address = SCB->MMFAR;
|
||||
uint32_t fault_status = SCB->CFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(2, fault_address, fault_status);
|
||||
|
||||
while(1) {
|
||||
/* Safe state */
|
||||
WDOG->CNT = 0xB480;
|
||||
WDOG->CNT = 0x4B80;
|
||||
}
|
||||
}
|
||||
|
||||
void BusFault_Handler(void) {
|
||||
uint32_t fault_address = SCB->BFAR;
|
||||
uint32_t fault_status = SCB->CFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(3, fault_address, fault_status);
|
||||
|
||||
while(1) {
|
||||
WDOG->CNT = 0xB480;
|
||||
WDOG->CNT = 0x4B80;
|
||||
}
|
||||
}
|
||||
|
||||
void UsageFault_Handler(void) {
|
||||
uint32_t fault_status = SCB->CFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(4, 0, fault_status);
|
||||
|
||||
while(1) {
|
||||
WDOG->CNT = 0xB480;
|
||||
WDOG->CNT = 0x4B80;
|
||||
}
|
||||
}
|
||||
|
||||
void HardFault_Handler(void) {
|
||||
uint32_t fault_status = SCB->HFSR;
|
||||
|
||||
extern void fault_handler_process(uint32_t type, uint32_t address, uint32_t status);
|
||||
fault_handler_process(1, 0, fault_status);
|
||||
|
||||
while(1) {
|
||||
WDOG->CNT = 0xB480;
|
||||
WDOG->CNT = 0x4B80;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enter Critical Section */
|
||||
void port_disable_interrupts(void) {
|
||||
__disable_irq();
|
||||
}
|
||||
|
||||
/* Exit Critical Section */
|
||||
void port_enable_interrupts(void) {
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
/* Get Current Exception Number */
|
||||
uint32_t port_get_current_exception(void) {
|
||||
return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) >> SCB_ICSR_VECTACTIVE_Pos;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @file port_asm.s
|
||||
* @brief NXP S32K assembly routines
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.thumb
|
||||
.arch armv7e-m
|
||||
|
||||
.section .text
|
||||
|
||||
/* Global symbols */
|
||||
.global current_task_sp
|
||||
.global next_task_sp
|
||||
.global port_context_switch
|
||||
.global port_start_first_task
|
||||
.global port_disable_interrupts
|
||||
.global port_enable_interrupts
|
||||
|
||||
/* Variables */
|
||||
.section .bss
|
||||
.align 3
|
||||
current_task_sp: .word 0
|
||||
next_task_sp: .word 0
|
||||
|
||||
.section .text
|
||||
.thumb_func
|
||||
|
||||
/* Start First Task */
|
||||
port_start_first_task:
|
||||
/* Load task stack pointer */
|
||||
ldr r0, =next_task_sp
|
||||
ldr r1, [r0]
|
||||
|
||||
/* Set PSP */
|
||||
msr PSP, r1
|
||||
|
||||
/* Switch to PSP */
|
||||
mov r0, #2
|
||||
msr CONTROL, r0
|
||||
isb
|
||||
|
||||
/* Restore context */
|
||||
ldmia r1!, {r4-r11}
|
||||
msr PSP, r1
|
||||
mov lr, #0xFFFFFFFD
|
||||
bx lr
|
||||
|
||||
/* PendSV Handler */
|
||||
PendSV_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
ldr r1, =current_task_sp
|
||||
str r0, [r1]
|
||||
|
||||
/* Load next context */
|
||||
ldr r0, =next_task_sp
|
||||
ldr r1, [r0]
|
||||
ldmia r1!, {r4-r11}
|
||||
msr PSP, r1
|
||||
bx lr
|
||||
|
||||
/* SVC Handler */
|
||||
SVC_Handler:
|
||||
tst lr, #4
|
||||
ite eq
|
||||
mrseq r0, MSP
|
||||
mrsne r0, PSP
|
||||
ldr r0, [r0, #24]
|
||||
ldrb r0, [r0, #-2]
|
||||
push {lr}
|
||||
bl svc_handler
|
||||
pop {lr}
|
||||
bx lr
|
||||
|
||||
/* Disable Interrupts */
|
||||
port_disable_interrupts:
|
||||
cpsid i
|
||||
bx lr
|
||||
|
||||
/* Enable Interrupts */
|
||||
port_enable_interrupts:
|
||||
cpsie i
|
||||
bx lr
|
||||
|
||||
/* LPIT Interrupt Handler */
|
||||
LPIT0_IRQHandler:
|
||||
/* Save context */
|
||||
push {r4-r11, lr}
|
||||
|
||||
/* Clear interrupt flag */
|
||||
ldr r0, =0x40037008 /* LPIT0->MSR */
|
||||
movs r1, #1
|
||||
str r1, [r0]
|
||||
|
||||
/* Call kernel tick handler */
|
||||
bl kernel_tick_handler
|
||||
|
||||
/* Service watchdog */
|
||||
ldr r0, =0x40052000 /* WDOG->CNT */
|
||||
ldr r1, =0xB480
|
||||
str r1, [r0]
|
||||
ldr r1, =0x4B80
|
||||
str r1, [r0]
|
||||
|
||||
/* Restore context */
|
||||
pop {r4-r11, lr}
|
||||
bx lr
|
||||
|
||||
/* Hard Fault Handler */
|
||||
HardFault_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault status */
|
||||
ldr r2, =0xE000ED2C /* HFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #1
|
||||
movs r1, #0
|
||||
bl fault_handler_process
|
||||
|
||||
/* Safe state */
|
||||
b .
|
||||
|
||||
/* Memory Management Fault */
|
||||
MemManage_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault information */
|
||||
ldr r1, =0xE000ED34 /* MMFAR */
|
||||
ldr r1, [r1]
|
||||
ldr r2, =0xE000ED28 /* CFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #2
|
||||
bl fault_handler_process
|
||||
|
||||
/* Safe state */
|
||||
b .
|
||||
|
||||
/* Bus Fault Handler */
|
||||
BusFault_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault information */
|
||||
ldr r1, =0xE000ED38 /* BFAR */
|
||||
ldr r1, [r1]
|
||||
ldr r2, =0xE000ED28 /* CFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #3
|
||||
bl fault_handler_process
|
||||
|
||||
/* Safe state */
|
||||
b .
|
||||
|
||||
/* Usage Fault Handler */
|
||||
UsageFault_Handler:
|
||||
/* Save context */
|
||||
mrs r0, PSP
|
||||
stmdb r0!, {r4-r11}
|
||||
|
||||
/* Get fault status */
|
||||
ldr r2, =0xE000ED28 /* CFSR */
|
||||
ldr r2, [r2]
|
||||
|
||||
/* Call fault handler */
|
||||
movs r0, #4
|
||||
movs r1, #0
|
||||
bl fault_handler_process
|
||||
|
||||
/* Safe state */
|
||||
b .
|
||||
|
||||
.end
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @file portmacro.h
|
||||
* @brief NXP S32K specific macros
|
||||
*/
|
||||
|
||||
#ifndef PORTMACRO_H
|
||||
#define PORTMACRO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "S32K144.h"
|
||||
|
||||
/* Data Types */
|
||||
typedef uint32_t port_stack_type_t;
|
||||
typedef uint32_t port_base_type_t;
|
||||
|
||||
/* Architecture Constants */
|
||||
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||
#define PORT_BYTE_ALIGNMENT 8
|
||||
#define PORT_MAX_SYSCALL_INTERRUPT_PRIORITY 0x50
|
||||
|
||||
/* Critical Section Macros */
|
||||
#define portENTER_CRITICAL() __asm volatile("CPSID I" ::: "memory")
|
||||
#define portEXIT_CRITICAL() __asm volatile("CPSIE I" ::: "memory")
|
||||
|
||||
/* Interrupt Control */
|
||||
#define portENABLE_INTERRUPTS() __asm volatile("CPSIE I" ::: "memory")
|
||||
#define portDISABLE_INTERRUPTS() __asm volatile("CPSID I" ::: "memory")
|
||||
|
||||
/* Context Switch */
|
||||
#define portYIELD() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||
#define portYIELD_FROM_ISR() SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk
|
||||
|
||||
/* Memory Barriers */
|
||||
#define portMEMORY_BARRIER() __asm volatile("DMB" ::: "memory")
|
||||
#define portSYNC_BARRIER() __asm volatile("DSB" ::: "memory")
|
||||
#define portINSTRUCTION_BARRIER() __asm volatile("ISB" ::: "memory")
|
||||
|
||||
/* NOP */
|
||||
#define portNOP() __asm volatile("NOP")
|
||||
|
||||
/* Watchdog Control */
|
||||
#define portSERVICE_WATCHDOG() \
|
||||
do { \
|
||||
WDOG->CNT = 0xB480; \
|
||||
WDOG->CNT = 0x4B80; \
|
||||
} while(0)
|
||||
|
||||
/* Low Power Modes */
|
||||
#define portENTER_SLEEP() __asm volatile("WFI")
|
||||
#define portENTER_DEEP_SLEEP() __asm volatile("WFE")
|
||||
|
||||
/* Endian Definition */
|
||||
#define portBYTE_ORDER LITTLE_ENDIAN
|
||||
|
||||
#endif /* PORTMACRO_H */
|
||||
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* @file port.c
|
||||
* @brief RISC-V RV32 architecture specific port
|
||||
* @note RISC-V is emerging in automotive applications
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include "scheduler.h"
|
||||
#include "portmacro.h"
|
||||
|
||||
/* Context storage */
|
||||
uint32_t* current_task_sp = NULL;
|
||||
uint32_t* next_task_sp = NULL;
|
||||
|
||||
/* Machine Timer registers */
|
||||
#define MTIME_ADDR 0x0200BFF8
|
||||
#define MTIMECMP_ADDR 0x02004000
|
||||
|
||||
/* Initialize Architecture Port */
|
||||
void port_init(void) {
|
||||
/* Configure machine timer for 1ms interrupts */
|
||||
volatile uint32_t* mtime = (uint32_t*)MTIME_ADDR;
|
||||
volatile uint32_t* mtimecmp = (uint32_t*)MTIMECMP_ADDR;
|
||||
|
||||
uint32_t current_time = *mtime;
|
||||
*mtimecmp = current_time + (SystemCoreClock / 1000);
|
||||
|
||||
/* Enable machine timer interrupt */
|
||||
__asm volatile (
|
||||
"li t0, 0x80\n" /* Machine Timer Interrupt Enable */
|
||||
"csrs mie, t0\n"
|
||||
);
|
||||
|
||||
/* Enable global interrupts */
|
||||
__asm volatile (
|
||||
"csrsi mstatus, 0x8\n" /* Machine Interrupt Enable */
|
||||
);
|
||||
}
|
||||
|
||||
/* Start First Task */
|
||||
void port_start_first_task(void) {
|
||||
TaskHandle_t first_task = scheduler_get_current_task();
|
||||
|
||||
if (first_task == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Load task stack pointer */
|
||||
__asm volatile (
|
||||
"mv sp, %0\n"
|
||||
"ret\n"
|
||||
:
|
||||
: "r" (first_task->stack_pointer)
|
||||
);
|
||||
}
|
||||
|
||||
/* Context Switch */
|
||||
void port_context_switch(uint32_t* current_context, uint32_t* next_context) {
|
||||
current_task_sp = current_context;
|
||||
next_task_sp = next_context;
|
||||
|
||||
/* Trigger software interrupt */
|
||||
__asm volatile (
|
||||
"li t0, 0x8\n" /* Machine Software Interrupt */
|
||||
"csrs mip, t0\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* Initialize Task Stack */
|
||||
uint32_t* port_initialize_task_stack(TaskFunction_t task_function,
|
||||
void* parameters,
|
||||
uint32_t* stack_top) {
|
||||
uint32_t* stack_ptr = stack_top;
|
||||
|
||||
/* Align to 16 bytes */
|
||||
stack_ptr = (uint32_t*)((uint32_t)stack_ptr & ~0xF);
|
||||
|
||||
/* Initial stack frame */
|
||||
*(--stack_ptr) = (uint32_t)parameters; /* a0 */
|
||||
*(--stack_ptr) = 0; /* a1 */
|
||||
*(--stack_ptr) = 0; /* a2 */
|
||||
*(--stack_ptr) = 0; /* a3 */
|
||||
*(--stack_ptr) = 0; /* a4 */
|
||||
*(--stack_ptr) = 0; /* a5 */
|
||||
*(--stack_ptr) = 0; /* a6 */
|
||||
*(--stack_ptr) = 0; /* a7 */
|
||||
|
||||
*(--stack_ptr) = (uint32_t)task_function; /* ra */
|
||||
*(--stack_ptr) = 0; /* t0 */
|
||||
*(--stack_ptr) = 0; /* t1 */
|
||||
*(--stack_ptr) = 0; /* t2 */
|
||||
*(--stack_ptr) = 0; /* t3 */
|
||||
*(--stack_ptr) = 0; /* t4 */
|
||||
*(--stack_ptr) = 0; /* t5 */
|
||||
*(--stack_ptr) = 0; /* t6 */
|
||||
|
||||
*(--stack_ptr) = 0; /* s0 */
|
||||
*(--stack_ptr) = 0; /* s1 */
|
||||
*(--stack_ptr) = 0; /* s2 */
|
||||
*(--stack_ptr) = 0; /* s3 */
|
||||
*(--stack_ptr) = 0; /* s4 */
|
||||
*(--stack_ptr) = 0; /* s5 */
|
||||
*(--stack_ptr) = 0; /* s6 */
|
||||
*(--stack_ptr) = 0; /* s7 */
|
||||
*(--stack_ptr) = 0; /* s8 */
|
||||
*(--stack_ptr) = 0; /* s9 */
|
||||
*(--stack_ptr) = 0; /* s10 */
|
||||
*(--stack_ptr) = 0; /* s11 */
|
||||
|
||||
*(--stack_ptr) = 0; /* gp */
|
||||
*(--stack_ptr) = 0; /* tp */
|
||||
|
||||
/* mstatus */
|
||||
*(--stack_ptr) = 0x00001880; /* MPP = Machine mode */
|
||||
|
||||
return stack_ptr;
|
||||
}
|
||||
|
||||
/* Machine Timer Interrupt Handler */
|
||||
void machine_timer_interrupt_handler(void) {
|
||||
/* Clear interrupt */
|
||||
volatile uint32_t* mtimecmp = (uint32_t*)MTIMECMP_ADDR;
|
||||
volatile uint32_t* mtime = (uint32_t*)MTIME_ADDR;
|
||||
*mtimecmp = *mtime + (SystemCoreClock / 1000);
|
||||
|
||||
/* Call kernel tick handler */
|
||||
extern void kernel_tick_handler(void);
|
||||
kernel_tick_handler();
|
||||
}
|
||||
|
||||
/* Machine Software Interrupt Handler */
|
||||
void machine_software_interrupt_handler(void) {
|
||||
/* Clear interrupt */
|
||||
__asm volatile (
|
||||
"li t0, 0x8\n"
|
||||
"csrc mip, t0\n"
|
||||
);
|
||||
|
||||
/* Perform context switch */
|
||||
__asm volatile (
|
||||
/* Save current context */
|
||||
"addi sp, sp, -128\n"
|
||||
"sw ra, 0(sp)\n"
|
||||
"sw t0, 4(sp)\n"
|
||||
"sw t1, 8(sp)\n"
|
||||
"sw t2, 12(sp)\n"
|
||||
"sw s0, 16(sp)\n"
|
||||
"sw s1, 20(sp)\n"
|
||||
"sw a0, 24(sp)\n"
|
||||
"sw a1, 28(sp)\n"
|
||||
"sw a2, 32(sp)\n"
|
||||
"sw a3, 36(sp)\n"
|
||||
"sw a4, 40(sp)\n"
|
||||
"sw a5, 44(sp)\n"
|
||||
"sw a6, 48(sp)\n"
|
||||
"sw a7, 52(sp)\n"
|
||||
"sw s2, 56(sp)\n"
|
||||
"sw s3, 60(sp)\n"
|
||||
"sw s4, 64(sp)\n"
|
||||
"sw s5, 68(sp)\n"
|
||||
"sw s6, 72(sp)\n"
|
||||
"sw s7, 76(sp)\n"
|
||||
"sw s8, 80(sp)\n"
|
||||
"sw s9, 84(sp)\n"
|
||||
"sw s10, 88(sp)\n"
|
||||
"sw s11, 92(sp)\n"
|
||||
"sw t3, 96(sp)\n"
|
||||
"sw t4, 100(sp)\n"
|
||||
"sw t5, 104(sp)\n"
|
||||
"sw t6, 108(sp)\n"
|
||||
"csrr t0, mstatus\n"
|
||||
"sw t0, 112(sp)\n"
|
||||
"csrr t0, mepc\n"
|
||||
"sw t0, 116(sp)\n"
|
||||
|
||||
/* Save current stack pointer */
|
||||
"la t0, current_task_sp\n"
|
||||
"sw sp, 0(t0)\n"
|
||||
|
||||
/* Load next stack pointer */
|
||||
"la t0, next_task_sp\n"
|
||||
"lw sp, 0(t0)\n"
|
||||
|
||||
/* Restore next context */
|
||||
"lw ra, 0(sp)\n"
|
||||
"lw t0, 4(sp)\n"
|
||||
"lw t1, 8(sp)\n"
|
||||
"lw t2, 12(sp)\n"
|
||||
"lw s0, 16(sp)\n"
|
||||
"lw s1, 20(sp)\n"
|
||||
"lw a0, 24(sp)\n"
|
||||
"lw a1, 28(sp)\n"
|
||||
"lw a2, 32(sp)\n"
|
||||
"lw a3, 36(sp)\n"
|
||||
"lw a4, 40(sp)\n"
|
||||
"lw a5, 44(sp)\n"
|
||||
"lw a6, 48(sp)\n"
|
||||
"lw a7, 52(sp)\n"
|
||||
"lw s2, 56(sp)\n"
|
||||
"lw s3, 60(sp)\n"
|
||||
"lw s4, 64(sp)\n"
|
||||
"lw s5, 68(sp)\n"
|
||||
"lw s6, 72(sp)\n"
|
||||
"lw s7, 76(sp)\n"
|
||||
"lw s8, 80(sp)\n"
|
||||
"lw s9, 84(sp)\n"
|
||||
"lw s10, 88(sp)\n"
|
||||
"lw s11, 92(sp)\n"
|
||||
"lw t3, 96(sp)\n"
|
||||
"lw t4, 100(sp)\n"
|
||||
"lw t5, 104(sp)\n"
|
||||
"lw t6, 108(sp)\n"
|
||||
"lw t0, 112(sp)\n"
|
||||
"csrw mstatus, t0\n"
|
||||
"lw t0, 116(sp)\n"
|
||||
"csrw mepc, t0\n"
|
||||
"addi sp, sp, 128\n"
|
||||
|
||||
/* Return from interrupt */
|
||||
"mret\n"
|
||||
);
|
||||
}
|
||||
|
||||
/* Enter Critical Section */
|
||||
void port_disable_interrupts(void) {
|
||||
__asm volatile (
|
||||
"csrci mstatus, 0x8\n" /* Clear Machine Interrupt Enable */
|
||||
);
|
||||
}
|
||||
|
||||
/* Exit Critical Section */
|
||||
void port_enable_interrupts(void) {
|
||||
__asm volatile (
|
||||
"csrsi mstatus, 0x8\n" /* Set Machine Interrupt Enable */
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* @file portmacro.h
|
||||
* @brief RISC-V RV32 specific macros
|
||||
*/
|
||||
|
||||
#ifndef PORTMACRO_H
|
||||
#define PORTMACRO_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Data Types */
|
||||
typedef uint32_t port_stack_type_t;
|
||||
typedef uint32_t port_base_type_t;
|
||||
|
||||
/* Architecture Constants */
|
||||
#define PORT_STACK_GROWTH_DIRECTION (-1)
|
||||
#define PORT_BYTE_ALIGNMENT 16
|
||||
|
||||
/* Critical Section Macros */
|
||||
#define portENTER_CRITICAL() __asm volatile("csrci mstatus, 0x8")
|
||||
#define portEXIT_CRITICAL() __asm volatile("csrsi mstatus, 0x8")
|
||||
|
||||
/* Interrupt Control */
|
||||
#define portENABLE_INTERRUPTS() __asm volatile("csrsi mstatus, 0x8")
|
||||
#define portDISABLE_INTERRUPTS() __asm volatile("csrci mstatus, 0x8")
|
||||
|
||||
/* Context Switch */
|
||||
#define portYIELD() __asm volatile("li t0, 0x8\ncsrs mip, t0")
|
||||
#define portYIELD_FROM_ISR() __asm volatile("li t0, 0x8\ncsrs mip, t0")
|
||||
|
||||
/* Memory Barriers */
|
||||
#define portMEMORY_BARRIER() __asm volatile("fence")
|
||||
#define portSYNC_BARRIER() __asm volatile("fence")
|
||||
#define portINSTRUCTION_BARRIER() __asm volatile("fence.i")
|
||||
|
||||
/* NOP */
|
||||
#define portNOP() __asm volatile("nop")
|
||||
|
||||
#endif /* PORTMACRO_H */
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @file isr.h
|
||||
* @brief Interrupt Service Routine management
|
||||
*/
|
||||
|
||||
#ifndef ISR_H
|
||||
#define ISR_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/* ISR Types */
|
||||
typedef void (*ISRHandler_t)(void);
|
||||
|
||||
/* ISR Configuration */
|
||||
typedef struct {
|
||||
uint32_t irq_number;
|
||||
ISRHandler_t handler;
|
||||
uint8_t priority;
|
||||
} ISRConfig_t;
|
||||
|
||||
/* ISR Functions */
|
||||
KernelStatus_t isr_register(uint32_t irq_number, ISRHandler_t handler,
|
||||
uint8_t priority);
|
||||
KernelStatus_t isr_unregister(uint32_t irq_number);
|
||||
KernelStatus_t isr_enable(uint32_t irq_number);
|
||||
KernelStatus_t isr_disable(uint32_t irq_number);
|
||||
KernelStatus_t isr_set_priority(uint32_t irq_number, uint8_t priority);
|
||||
void isr_enter(void);
|
||||
void isr_exit(void);
|
||||
bool isr_is_in_context(void);
|
||||
|
||||
/* Critical Section Management */
|
||||
void critical_section_enter(void);
|
||||
void critical_section_exit(void);
|
||||
|
||||
#endif /* ISR_H */
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @file kernel.h
|
||||
* @brief Core kernel definitions and types for Automotive RTOS
|
||||
* @author Automotive RTOS Team
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
#ifndef KERNEL_H
|
||||
#define KERNEL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Kernel Version */
|
||||
#define KERNEL_VERSION_MAJOR 1
|
||||
#define KERNEL_VERSION_MINOR 0
|
||||
#define KERNEL_VERSION_PATCH 0
|
||||
|
||||
/* Configuration Constants */
|
||||
#define MAX_TASKS 32
|
||||
#define MAX_PRIORITY_LEVELS 16
|
||||
#define MAX_TASK_NAME_LENGTH 16
|
||||
#define IDLE_TASK_PRIORITY (MAX_PRIORITY_LEVELS - 1)
|
||||
|
||||
/* Task States */
|
||||
typedef enum {
|
||||
TASK_SUSPENDED = 0,
|
||||
TASK_READY = 1,
|
||||
TASK_RUNNING = 2,
|
||||
TASK_BLOCKED = 3,
|
||||
TASK_TERMINATED = 4
|
||||
} TaskState_t;
|
||||
|
||||
/* Task Priority Type */
|
||||
typedef uint8_t TaskPriority_t;
|
||||
|
||||
/* Task Handle */
|
||||
typedef struct TaskControlBlock* TaskHandle_t;
|
||||
|
||||
/* Task Function */
|
||||
typedef void (*TaskFunction_t)(void* parameters);
|
||||
|
||||
/* Time Types */
|
||||
typedef uint32_t TickType_t;
|
||||
typedef uint32_t TimeOut_t;
|
||||
|
||||
/* Error Codes */
|
||||
typedef enum {
|
||||
KERNEL_OK = 0,
|
||||
KERNEL_ERROR = -1,
|
||||
KERNEL_INVALID_PARAMETER = -2,
|
||||
KERNEL_OUT_OF_MEMORY = -3,
|
||||
KERNEL_TASK_NOT_FOUND = -4,
|
||||
KERNEL_PRIORITY_INVALID = -5,
|
||||
KERNEL_TIMEOUT = -6,
|
||||
KERNEL_RESOURCE_BUSY = -7
|
||||
} KernelStatus_t;
|
||||
|
||||
/* Task Creation Parameters */
|
||||
typedef struct {
|
||||
const char* name;
|
||||
TaskFunction_t function;
|
||||
void* parameters;
|
||||
uint32_t stack_size;
|
||||
TaskPriority_t priority;
|
||||
TickType_t period_ticks; /* 0 for aperiodic tasks */
|
||||
} TaskConfig_t;
|
||||
|
||||
/* Task Statistics */
|
||||
typedef struct {
|
||||
uint32_t execution_count;
|
||||
TickType_t last_execution_time;
|
||||
TickType_t max_execution_time;
|
||||
TickType_t total_execution_time;
|
||||
uint32_t stack_high_water_mark;
|
||||
uint32_t deadline_misses;
|
||||
} TaskStatistics_t;
|
||||
|
||||
/* Kernel Core Functions */
|
||||
KernelStatus_t kernel_init(void);
|
||||
KernelStatus_t kernel_start(void);
|
||||
void kernel_stop(void);
|
||||
TickType_t kernel_get_tick_count(void);
|
||||
KernelStatus_t kernel_delay(TickType_t ticks);
|
||||
|
||||
/* Task Management Functions */
|
||||
TaskHandle_t task_create(const TaskConfig_t* config);
|
||||
KernelStatus_t task_delete(TaskHandle_t task);
|
||||
KernelStatus_t task_suspend(TaskHandle_t task);
|
||||
KernelStatus_t task_resume(TaskHandle_t task);
|
||||
KernelStatus_t task_set_priority(TaskHandle_t task, TaskPriority_t new_priority);
|
||||
TaskPriority_t task_get_priority(TaskHandle_t task);
|
||||
TaskState_t task_get_state(TaskHandle_t task);
|
||||
KernelStatus_t task_get_statistics(TaskHandle_t task, TaskStatistics_t* stats);
|
||||
|
||||
/* Scheduler Control */
|
||||
void scheduler_yield(void);
|
||||
void scheduler_lock(void);
|
||||
void scheduler_unlock(void);
|
||||
TaskHandle_t scheduler_get_current_task(void);
|
||||
|
||||
#endif /* KERNEL_H */
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @file mutex.h
|
||||
* @brief Mutex with priority inheritance
|
||||
*/
|
||||
|
||||
#ifndef MUTEX_H
|
||||
#define MUTEX_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/* Mutex Control Block */
|
||||
typedef struct {
|
||||
TaskHandle_t owner;
|
||||
TaskPriority_t original_priority;
|
||||
uint32_t lock_count;
|
||||
TaskHandle_t* waiting_tasks;
|
||||
uint32_t waiting_count;
|
||||
bool recursive;
|
||||
} Mutex_t;
|
||||
|
||||
/* Mutex Functions */
|
||||
KernelStatus_t mutex_create(Mutex_t* mutex, bool recursive);
|
||||
KernelStatus_t mutex_lock(Mutex_t* mutex, TimeOut_t timeout);
|
||||
KernelStatus_t mutex_unlock(Mutex_t* mutex);
|
||||
KernelStatus_t mutex_delete(Mutex_t* mutex);
|
||||
TaskHandle_t mutex_get_owner(Mutex_t* mutex);
|
||||
|
||||
#endif /* MUTEX_H */
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @file queue.h
|
||||
* @brief Message queue for inter-task communication
|
||||
*/
|
||||
|
||||
#ifndef QUEUE_H
|
||||
#define QUEUE_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/* Queue Control Block */
|
||||
typedef struct {
|
||||
void* buffer;
|
||||
uint32_t item_size;
|
||||
uint32_t max_items;
|
||||
uint32_t current_items;
|
||||
uint32_t head;
|
||||
uint32_t tail;
|
||||
TaskHandle_t* waiting_senders;
|
||||
TaskHandle_t* waiting_receivers;
|
||||
uint32_t waiting_sender_count;
|
||||
uint32_t waiting_receiver_count;
|
||||
} Queue_t;
|
||||
|
||||
/* Queue Functions */
|
||||
KernelStatus_t queue_create(Queue_t* queue, void* buffer, uint32_t item_size,
|
||||
uint32_t max_items);
|
||||
KernelStatus_t queue_send(Queue_t* queue, const void* item, TimeOut_t timeout);
|
||||
KernelStatus_t queue_receive(Queue_t* queue, void* item, TimeOut_t timeout);
|
||||
KernelStatus_t queue_send_from_isr(Queue_t* queue, const void* item);
|
||||
KernelStatus_t queue_receive_from_isr(Queue_t* queue, void* item);
|
||||
uint32_t queue_get_count(Queue_t* queue);
|
||||
KernelStatus_t queue_delete(Queue_t* queue);
|
||||
|
||||
#endif /* QUEUE_H */
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @file scheduler.h
|
||||
* @brief Priority-based preemptive scheduler
|
||||
*/
|
||||
|
||||
#ifndef SCHEDULER_H
|
||||
#define SCHEDULER_H
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
|
||||
/* Scheduler Types */
|
||||
typedef enum {
|
||||
SCHEDULER_PRIORITY_PREEMPTIVE = 0,
|
||||
SCHEDULER_ROUND_ROBIN = 1,
|
||||
SCHEDULER_EDF = 2, /* Earliest Deadline First */
|
||||
SCHEDULER_RATE_MONOTONIC = 3
|
||||
} SchedulerType_t;
|
||||
|
||||
/* Scheduler Configuration */
|
||||
typedef struct {
|
||||
SchedulerType_t type;
|
||||
TickType_t time_slice_ticks; /* For round-robin */
|
||||
bool enable_deadline_monitoring;
|
||||
} SchedulerConfig_t;
|
||||
|
||||
/* Scheduler Interface */
|
||||
void scheduler_init(const SchedulerConfig_t* config);
|
||||
void scheduler_start(void);
|
||||
void scheduler_tick(void);
|
||||
void scheduler_add_task(TaskHandle_t task);
|
||||
void scheduler_remove_task(TaskHandle_t task);
|
||||
void scheduler_update_task_state(TaskHandle_t task, TaskState_t new_state);
|
||||
TaskHandle_t scheduler_select_next_task(void);
|
||||
void scheduler_context_switch(TaskHandle_t next_task);
|
||||
|
||||
/* Scheduler Statistics */
|
||||
typedef struct {
|
||||
uint32_t context_switches;
|
||||
uint32_t preemptions;
|
||||
TickType_t max_scheduling_latency;
|
||||
TickType_t total_idle_time;
|
||||
} SchedulerStatistics_t;
|
||||
|
||||
void scheduler_get_statistics(SchedulerStatistics_t* stats);
|
||||
|
||||
#endif /* SCHEDULER_H */
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @file semaphore.h
|
||||
* @brief Semaphore synchronization primitive
|
||||
*/
|
||||
|
||||
#ifndef SEMAPHORE_H
|
||||
#define SEMAPHORE_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/* Semaphore Types */
|
||||
typedef enum {
|
||||
SEMAPHORE_BINARY = 0,
|
||||
SEMAPHORE_COUNTING = 1,
|
||||
SEMAPHORE_MUTEX = 2
|
||||
} SemaphoreType_t;
|
||||
|
||||
/* Semaphore Control Block */
|
||||
typedef struct {
|
||||
SemaphoreType_t type;
|
||||
uint32_t count;
|
||||
uint32_t max_count;
|
||||
TaskHandle_t owner; /* For mutex */
|
||||
uint8_t priority_ceiling;
|
||||
TaskHandle_t* waiting_tasks;
|
||||
uint32_t waiting_count;
|
||||
} Semaphore_t;
|
||||
|
||||
/* Semaphore Functions */
|
||||
KernelStatus_t semaphore_create(Semaphore_t* sem, SemaphoreType_t type,
|
||||
uint32_t initial_count, uint32_t max_count);
|
||||
KernelStatus_t semaphore_take(Semaphore_t* sem, TimeOut_t timeout);
|
||||
KernelStatus_t semaphore_give(Semaphore_t* sem);
|
||||
KernelStatus_t semaphore_delete(Semaphore_t* sem);
|
||||
uint32_t semaphore_get_count(Semaphore_t* sem);
|
||||
|
||||
#endif /* SEMAPHORE_H */
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @file task.h
|
||||
* @brief Task management interface
|
||||
*/
|
||||
|
||||
#ifndef TASK_H
|
||||
#define TASK_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/* Internal Task Control Block */
|
||||
struct TaskControlBlock {
|
||||
/* Task Identification */
|
||||
char name[MAX_TASK_NAME_LENGTH];
|
||||
TaskHandle_t self;
|
||||
uint32_t task_id;
|
||||
|
||||
/* Task Function */
|
||||
TaskFunction_t function;
|
||||
void* parameters;
|
||||
|
||||
/* Stack Management */
|
||||
uint32_t* stack_pointer;
|
||||
uint32_t* stack_base;
|
||||
uint32_t stack_size;
|
||||
uint32_t stack_high_water_mark;
|
||||
|
||||
/* Scheduling Information */
|
||||
TaskPriority_t priority;
|
||||
TaskState_t state;
|
||||
TickType_t period_ticks;
|
||||
TickType_t last_wake_time;
|
||||
TickType_t deadline_ticks;
|
||||
|
||||
/* Blocking Information */
|
||||
TickType_t block_timeout;
|
||||
void* blocked_on;
|
||||
|
||||
/* Statistics */
|
||||
TaskStatistics_t statistics;
|
||||
|
||||
/* List Management */
|
||||
struct TaskControlBlock* next;
|
||||
struct TaskControlBlock* prev;
|
||||
|
||||
/* Architecture Specific */
|
||||
uint32_t context[32]; /* CPU register context */
|
||||
};
|
||||
|
||||
/* Internal Task Management Functions */
|
||||
void task_init(void);
|
||||
void task_switch_context(TaskHandle_t next_task);
|
||||
TaskHandle_t task_get_idle_task(void);
|
||||
void task_update_statistics(void);
|
||||
void task_check_stack_overflow(void);
|
||||
bool task_is_ready(TaskHandle_t task);
|
||||
|
||||
#endif /* TASK_H */
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file timer.h
|
||||
* @brief Software timer management
|
||||
*/
|
||||
|
||||
#ifndef TIMER_H
|
||||
#define TIMER_H
|
||||
|
||||
#include "kernel.h"
|
||||
|
||||
/* Timer Types */
|
||||
typedef enum {
|
||||
TIMER_ONE_SHOT = 0,
|
||||
TIMER_PERIODIC = 1
|
||||
} TimerType_t;
|
||||
|
||||
/* Timer Callback */
|
||||
typedef void (*TimerCallback_t)(void* parameters);
|
||||
|
||||
/* Timer Control Block */
|
||||
typedef struct {
|
||||
char name[16];
|
||||
TimerType_t type;
|
||||
TickType_t period_ticks;
|
||||
TickType_t expiry_time;
|
||||
TimerCallback_t callback;
|
||||
void* parameters;
|
||||
bool is_active;
|
||||
struct Timer* next;
|
||||
} Timer_t;
|
||||
|
||||
/* Timer Functions */
|
||||
KernelStatus_t timer_create(Timer_t* timer, const char* name, TimerType_t type,
|
||||
TickType_t period, TimerCallback_t callback,
|
||||
void* parameters);
|
||||
KernelStatus_t timer_start(Timer_t* timer);
|
||||
KernelStatus_t timer_stop(Timer_t* timer);
|
||||
KernelStatus_t timer_reset(Timer_t* timer);
|
||||
KernelStatus_t timer_delete(Timer_t* timer);
|
||||
bool timer_is_active(Timer_t* timer);
|
||||
void timer_process_expired(void);
|
||||
|
||||
#endif /* TIMER_H */
|
||||
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* @file fault_handler.c
|
||||
* @brief Fault handling and error management
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include "scheduler.h"
|
||||
#include "isr.h"
|
||||
|
||||
/* Fault Types */
|
||||
typedef enum {
|
||||
FAULT_NONE = 0,
|
||||
FAULT_HARD_FAULT = 1,
|
||||
FAULT_BUS_FAULT = 2,
|
||||
FAULT_USAGE_FAULT = 3,
|
||||
FAULT_STACK_OVERFLOW = 4,
|
||||
FAULT_ASSERTION = 5,
|
||||
FAULT_WATCHDOG = 6
|
||||
} FaultType_t;
|
||||
|
||||
/* Fault Information */
|
||||
typedef struct {
|
||||
FaultType_t type;
|
||||
uint32_t fault_address;
|
||||
uint32_t fault_status;
|
||||
TaskHandle_t faulting_task;
|
||||
uint32_t timestamp;
|
||||
} FaultInfo_t;
|
||||
|
||||
/* Fault Statistics */
|
||||
static struct {
|
||||
uint32_t total_faults;
|
||||
uint32_t fault_counts[7];
|
||||
FaultInfo_t last_fault;
|
||||
} fault_statistics;
|
||||
|
||||
/* Fault Callback */
|
||||
typedef void (*FaultCallback_t)(const FaultInfo_t* fault_info);
|
||||
static FaultCallback_t fault_callback = NULL;
|
||||
|
||||
/* Initialize Fault Handler */
|
||||
void fault_handler_init(void) {
|
||||
memset(&fault_statistics, 0, sizeof(fault_statistics));
|
||||
fault_callback = NULL;
|
||||
}
|
||||
|
||||
/* Register Fault Callback */
|
||||
void fault_handler_register_callback(FaultCallback_t callback) {
|
||||
fault_callback = callback;
|
||||
}
|
||||
|
||||
/* Handle Fault */
|
||||
void fault_handler_process(FaultType_t type, uint32_t address, uint32_t status) {
|
||||
/* Save fault information */
|
||||
fault_statistics.last_fault.type = type;
|
||||
fault_statistics.last_fault.fault_address = address;
|
||||
fault_statistics.last_fault.fault_status = status;
|
||||
fault_statistics.last_fault.faulting_task = scheduler_get_current_task();
|
||||
fault_statistics.last_fault.timestamp = kernel_get_tick_count();
|
||||
|
||||
/* Update statistics */
|
||||
fault_statistics.total_faults++;
|
||||
if (type < 7) {
|
||||
fault_statistics.fault_counts[type]++;
|
||||
}
|
||||
|
||||
/* Call user callback if registered */
|
||||
if (fault_callback != NULL) {
|
||||
fault_callback(&fault_statistics.last_fault);
|
||||
}
|
||||
|
||||
/* Handle specific faults */
|
||||
switch (type) {
|
||||
case FAULT_STACK_OVERFLOW:
|
||||
/* Terminate faulting task */
|
||||
if (fault_statistics.last_fault.faulting_task != NULL) {
|
||||
task_suspend(fault_statistics.last_fault.faulting_task);
|
||||
}
|
||||
break;
|
||||
|
||||
case FAULT_HARD_FAULT:
|
||||
case FAULT_BUS_FAULT:
|
||||
case FAULT_USAGE_FAULT:
|
||||
/* Stop kernel for safety-critical faults */
|
||||
kernel_stop();
|
||||
break;
|
||||
|
||||
case FAULT_WATCHDOG:
|
||||
/* Reset system */
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stack Overflow Fault Handler */
|
||||
void fault_handler_stack_overflow(TaskHandle_t task) {
|
||||
fault_handler_process(FAULT_STACK_OVERFLOW, (uint32_t)task->stack_base,
|
||||
task->stack_size);
|
||||
}
|
||||
|
||||
/* Hard Fault Handler (ARM Cortex-M) */
|
||||
void HardFault_Handler(void) {
|
||||
uint32_t fault_address;
|
||||
uint32_t fault_status;
|
||||
|
||||
/* Extract fault information from hardware registers */
|
||||
__asm volatile (
|
||||
"MRS %0, BFAR\n"
|
||||
"MRS %1, BFSR\n"
|
||||
: "=r" (fault_address), "=r" (fault_status)
|
||||
);
|
||||
|
||||
fault_handler_process(FAULT_HARD_FAULT, fault_address, fault_status);
|
||||
|
||||
/* Infinite loop - safety-critical */
|
||||
while (1) {
|
||||
/* Wait for watchdog reset */
|
||||
}
|
||||
}
|
||||
|
||||
/* Bus Fault Handler */
|
||||
void BusFault_Handler(void) {
|
||||
uint32_t fault_address;
|
||||
uint32_t fault_status;
|
||||
|
||||
__asm volatile (
|
||||
"MRS %0, BFAR\n"
|
||||
"MRS %1, BFSR\n"
|
||||
: "=r" (fault_address), "=r" (fault_status)
|
||||
);
|
||||
|
||||
fault_handler_process(FAULT_BUS_FAULT, fault_address, fault_status);
|
||||
|
||||
while (1) {
|
||||
/* Wait for watchdog reset */
|
||||
}
|
||||
}
|
||||
|
||||
/* Usage Fault Handler */
|
||||
void UsageFault_Handler(void) {
|
||||
uint32_t fault_status;
|
||||
|
||||
__asm volatile (
|
||||
"MRS %0, CFSR\n"
|
||||
: "=r" (fault_status)
|
||||
);
|
||||
|
||||
fault_handler_process(FAULT_USAGE_FAULT, 0, fault_status);
|
||||
|
||||
while (1) {
|
||||
/* Wait for watchdog reset */
|
||||
}
|
||||
}
|
||||
|
||||
/* Assertion Handler */
|
||||
void assert_failed(const char* file, uint32_t line) {
|
||||
/* Log assertion failure */
|
||||
(void)file;
|
||||
(void)line;
|
||||
|
||||
fault_handler_process(FAULT_ASSERTION, (uint32_t)file, line);
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* @file isr.c
|
||||
* @brief Interrupt Service Routine management
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "isr.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
/* ISR Context State */
|
||||
static struct {
|
||||
uint32_t nested_count;
|
||||
bool in_isr;
|
||||
uint32_t critical_nesting;
|
||||
uint32_t primask_backup;
|
||||
} isr_state;
|
||||
|
||||
/* ISR Table */
|
||||
#define MAX_ISR_HANDLERS 128
|
||||
static ISRHandler_t isr_handlers[MAX_ISR_HANDLERS];
|
||||
|
||||
/* Initialize ISR Management */
|
||||
void isr_init(void) {
|
||||
isr_state.nested_count = 0;
|
||||
isr_state.in_isr = false;
|
||||
isr_state.critical_nesting = 0;
|
||||
isr_state.primask_backup = 0;
|
||||
|
||||
/* Clear ISR handler table */
|
||||
memset(isr_handlers, 0, sizeof(isr_handlers));
|
||||
}
|
||||
|
||||
/* Register ISR Handler */
|
||||
KernelStatus_t isr_register(uint32_t irq_number, ISRHandler_t handler,
|
||||
uint8_t priority) {
|
||||
if (irq_number >= MAX_ISR_HANDLERS || handler == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
isr_handlers[irq_number] = handler;
|
||||
NVIC_SetPriority((IRQn_Type)irq_number, priority);
|
||||
NVIC_EnableIRQ((IRQn_Type)irq_number);
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Unregister ISR Handler */
|
||||
KernelStatus_t isr_unregister(uint32_t irq_number) {
|
||||
if (irq_number >= MAX_ISR_HANDLERS) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
NVIC_DisableIRQ((IRQn_Type)irq_number);
|
||||
isr_handlers[irq_number] = NULL;
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Enable IRQ */
|
||||
KernelStatus_t isr_enable(uint32_t irq_number) {
|
||||
if (irq_number >= MAX_ISR_HANDLERS) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
NVIC_EnableIRQ((IRQn_Type)irq_number);
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Disable IRQ */
|
||||
KernelStatus_t isr_disable(uint32_t irq_number) {
|
||||
if (irq_number >= MAX_ISR_HANDLERS) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
NVIC_DisableIRQ((IRQn_Type)irq_number);
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set IRQ Priority */
|
||||
KernelStatus_t isr_set_priority(uint32_t irq_number, uint8_t priority) {
|
||||
if (irq_number >= MAX_ISR_HANDLERS) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
NVIC_SetPriority((IRQn_Type)irq_number, priority);
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Enter ISR */
|
||||
void isr_enter(void) {
|
||||
isr_state.nested_count++;
|
||||
isr_state.in_isr = true;
|
||||
}
|
||||
|
||||
/* Exit ISR */
|
||||
void isr_exit(void) {
|
||||
if (isr_state.nested_count > 0) {
|
||||
isr_state.nested_count--;
|
||||
}
|
||||
|
||||
if (isr_state.nested_count == 0) {
|
||||
isr_state.in_isr = false;
|
||||
|
||||
/* Check for context switch */
|
||||
if (scheduler_get_current_task() != NULL) {
|
||||
/* Trigger PendSV for context switch if needed */
|
||||
SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if in ISR Context */
|
||||
bool isr_is_in_context(void) {
|
||||
return isr_state.in_isr;
|
||||
}
|
||||
|
||||
/* Enter Critical Section */
|
||||
void critical_section_enter(void) {
|
||||
/* Save current PRIMASK and disable interrupts */
|
||||
__asm volatile (
|
||||
"MRS %0, PRIMASK\n"
|
||||
"CPSID I\n"
|
||||
: "=r" (isr_state.primask_backup)
|
||||
);
|
||||
isr_state.critical_nesting++;
|
||||
}
|
||||
|
||||
/* Exit Critical Section */
|
||||
void critical_section_exit(void) {
|
||||
if (isr_state.critical_nesting > 0) {
|
||||
isr_state.critical_nesting--;
|
||||
}
|
||||
|
||||
if (isr_state.critical_nesting == 0) {
|
||||
/* Restore PRIMASK */
|
||||
__asm volatile (
|
||||
"MSR PRIMASK, %0\n"
|
||||
:
|
||||
: "r" (isr_state.primask_backup)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* Generic IRQ Handler */
|
||||
void IRQ_Handler(uint32_t irq_number) {
|
||||
isr_enter();
|
||||
|
||||
if (irq_number < MAX_ISR_HANDLERS && isr_handlers[irq_number] != NULL) {
|
||||
isr_handlers[irq_number]();
|
||||
}
|
||||
|
||||
isr_exit();
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* @file kernel_init.c
|
||||
* @brief Kernel initialization and startup
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include "scheduler.h"
|
||||
#include "timer.h"
|
||||
#include "isr.h"
|
||||
|
||||
/* Global Kernel State */
|
||||
static struct {
|
||||
bool initialized;
|
||||
bool running;
|
||||
TickType_t tick_count;
|
||||
TaskHandle_t idle_task;
|
||||
TaskHandle_t current_task;
|
||||
uint32_t task_count;
|
||||
} kernel_state = {0};
|
||||
|
||||
/* Idle Task Stack */
|
||||
static uint32_t idle_task_stack[1024] __attribute__((aligned(8)));
|
||||
|
||||
/* Idle Task Function */
|
||||
static void idle_task_function(void* parameters) {
|
||||
(void)parameters;
|
||||
|
||||
while (1) {
|
||||
/* Idle processing - could include power management */
|
||||
__WFI(); /* Wait for interrupt (ARM) */
|
||||
scheduler_yield();
|
||||
}
|
||||
}
|
||||
|
||||
/* System Tick Handler - Called from hardware timer ISR */
|
||||
void SysTick_Handler(void) {
|
||||
kernel_state.tick_count++;
|
||||
scheduler_tick();
|
||||
timer_process_expired();
|
||||
}
|
||||
|
||||
/* Kernel Initialization */
|
||||
KernelStatus_t kernel_init(void) {
|
||||
if (kernel_state.initialized) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Initialize kernel state */
|
||||
kernel_state.initialized = false;
|
||||
kernel_state.running = false;
|
||||
kernel_state.tick_count = 0;
|
||||
kernel_state.task_count = 0;
|
||||
|
||||
/* Configure scheduler */
|
||||
SchedulerConfig_t sched_config = {
|
||||
.type = SCHEDULER_PRIORITY_PREEMPTIVE,
|
||||
.time_slice_ticks = 10,
|
||||
.enable_deadline_monitoring = true
|
||||
};
|
||||
scheduler_init(&sched_config);
|
||||
|
||||
/* Initialize task management */
|
||||
task_init();
|
||||
|
||||
/* Create idle task */
|
||||
TaskConfig_t idle_config = {
|
||||
.name = "idle",
|
||||
.function = idle_task_function,
|
||||
.parameters = NULL,
|
||||
.stack_size = sizeof(idle_task_stack),
|
||||
.priority = IDLE_TASK_PRIORITY,
|
||||
.period_ticks = 0
|
||||
};
|
||||
|
||||
kernel_state.idle_task = task_create(&idle_config);
|
||||
if (kernel_state.idle_task == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Configure system tick timer */
|
||||
SysTick_Config(SystemCoreClock / 1000); /* 1ms tick */
|
||||
|
||||
kernel_state.initialized = true;
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Start Kernel */
|
||||
KernelStatus_t kernel_start(void) {
|
||||
if (!kernel_state.initialized || kernel_state.running) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
kernel_state.running = true;
|
||||
|
||||
/* Start scheduler */
|
||||
scheduler_start();
|
||||
|
||||
/* Should never return */
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Stop Kernel */
|
||||
void kernel_stop(void) {
|
||||
critical_section_enter();
|
||||
kernel_state.running = false;
|
||||
critical_section_exit();
|
||||
}
|
||||
|
||||
/* Get Current Tick Count */
|
||||
TickType_t kernel_get_tick_count(void) {
|
||||
return kernel_state.tick_count;
|
||||
}
|
||||
|
||||
/* Delay Function */
|
||||
KernelStatus_t kernel_delay(TickType_t ticks) {
|
||||
if (ticks == 0) {
|
||||
scheduler_yield();
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
TaskHandle_t current = scheduler_get_current_task();
|
||||
if (current == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
current->block_timeout = kernel_state.tick_count + ticks;
|
||||
scheduler_update_task_state(current, TASK_BLOCKED);
|
||||
critical_section_exit();
|
||||
|
||||
scheduler_yield();
|
||||
return KERNEL_OK;
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* @file mutex.c
|
||||
* @brief Mutex with priority inheritance
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "mutex.h"
|
||||
#include "scheduler.h"
|
||||
#include "isr.h"
|
||||
|
||||
/* Create Mutex */
|
||||
KernelStatus_t mutex_create(Mutex_t* mutex, bool recursive) {
|
||||
if (mutex == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
mutex->owner = NULL;
|
||||
mutex->original_priority = 0;
|
||||
mutex->lock_count = 0;
|
||||
mutex->waiting_tasks = NULL;
|
||||
mutex->waiting_count = 0;
|
||||
mutex->recursive = recursive;
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Lock Mutex */
|
||||
KernelStatus_t mutex_lock(Mutex_t* mutex, TimeOut_t timeout) {
|
||||
if (mutex == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
TaskHandle_t current = scheduler_get_current_task();
|
||||
if (current == NULL) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
/* Check if already owned by current task */
|
||||
if (mutex->owner == current) {
|
||||
if (mutex->recursive) {
|
||||
mutex->lock_count++;
|
||||
critical_section_exit();
|
||||
return KERNEL_OK;
|
||||
} else {
|
||||
critical_section_exit();
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if mutex is available */
|
||||
if (mutex->owner == NULL) {
|
||||
mutex->owner = current;
|
||||
mutex->lock_count = 1;
|
||||
mutex->original_priority = current->priority;
|
||||
critical_section_exit();
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Mutex is locked by another task */
|
||||
if (timeout == 0) {
|
||||
critical_section_exit();
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Priority inheritance */
|
||||
if (current->priority < mutex->owner->priority) {
|
||||
/* Current task has higher priority (lower number) */
|
||||
mutex->owner->priority = current->priority;
|
||||
}
|
||||
|
||||
/* Add to waiting list */
|
||||
mutex->waiting_tasks = (TaskHandle_t*)realloc(mutex->waiting_tasks,
|
||||
(mutex->waiting_count + 1) * sizeof(TaskHandle_t));
|
||||
if (mutex->waiting_tasks == NULL) {
|
||||
critical_section_exit();
|
||||
return KERNEL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mutex->waiting_tasks[mutex->waiting_count] = current;
|
||||
mutex->waiting_count++;
|
||||
|
||||
/* Block current task */
|
||||
scheduler_update_task_state(current, TASK_BLOCKED);
|
||||
|
||||
if (timeout != (TimeOut_t)-1) {
|
||||
current->block_timeout = kernel_get_tick_count() + timeout;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
/* Yield to other tasks */
|
||||
scheduler_yield();
|
||||
|
||||
/* Task has been unblocked */
|
||||
critical_section_enter();
|
||||
|
||||
if (mutex->owner == current) {
|
||||
mutex->lock_count = 1;
|
||||
critical_section_exit();
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Unlock Mutex */
|
||||
KernelStatus_t mutex_unlock(Mutex_t* mutex) {
|
||||
if (mutex == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
TaskHandle_t current = scheduler_get_current_task();
|
||||
if (current == NULL || mutex->owner != current) {
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
/* Decrement lock count */
|
||||
if (mutex->lock_count > 0) {
|
||||
mutex->lock_count--;
|
||||
}
|
||||
|
||||
if (mutex->lock_count > 0) {
|
||||
/* Still locked by current task */
|
||||
critical_section_exit();
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Restore original priority */
|
||||
current->priority = mutex->original_priority;
|
||||
|
||||
/* Check for waiting tasks */
|
||||
if (mutex->waiting_count > 0) {
|
||||
/* Find highest priority waiting task */
|
||||
TaskHandle_t next_owner = mutex->waiting_tasks[0];
|
||||
uint32_t next_owner_index = 0;
|
||||
|
||||
for (uint32_t i = 1; i < mutex->waiting_count; i++) {
|
||||
if (mutex->waiting_tasks[i]->priority < next_owner->priority) {
|
||||
next_owner = mutex->waiting_tasks[i];
|
||||
next_owner_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove from waiting list */
|
||||
for (uint32_t i = next_owner_index + 1; i < mutex->waiting_count; i++) {
|
||||
mutex->waiting_tasks[i-1] = mutex->waiting_tasks[i];
|
||||
}
|
||||
mutex->waiting_count--;
|
||||
|
||||
/* Transfer ownership */
|
||||
mutex->owner = next_owner;
|
||||
mutex->lock_count = 1;
|
||||
mutex->original_priority = next_owner->priority;
|
||||
|
||||
/* Unblock next owner */
|
||||
scheduler_update_task_state(next_owner, TASK_READY);
|
||||
} else {
|
||||
mutex->owner = NULL;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
/* Reschedule if needed */
|
||||
scheduler_yield();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Delete Mutex */
|
||||
KernelStatus_t mutex_delete(Mutex_t* mutex) {
|
||||
if (mutex == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
/* Wake up all waiting tasks */
|
||||
for (uint32_t i = 0; i < mutex->waiting_count; i++) {
|
||||
scheduler_update_task_state(mutex->waiting_tasks[i], TASK_READY);
|
||||
}
|
||||
|
||||
if (mutex->waiting_tasks != NULL) {
|
||||
free(mutex->waiting_tasks);
|
||||
}
|
||||
|
||||
memset(mutex, 0, sizeof(Mutex_t));
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
scheduler_yield();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get Mutex Owner */
|
||||
TaskHandle_t mutex_get_owner(Mutex_t* mutex) {
|
||||
if (mutex == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
return mutex->owner;
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
/**
|
||||
* @file queue.c
|
||||
* @brief Message queue implementation
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "queue.h"
|
||||
#include "scheduler.h"
|
||||
#include "isr.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Create Queue */
|
||||
KernelStatus_t queue_create(Queue_t* queue, void* buffer, uint32_t item_size,
|
||||
uint32_t max_items) {
|
||||
if (queue == NULL || buffer == NULL || item_size == 0 || max_items == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
queue->buffer = buffer;
|
||||
queue->item_size = item_size;
|
||||
queue->max_items = max_items;
|
||||
queue->current_items = 0;
|
||||
queue->head = 0;
|
||||
queue->tail = 0;
|
||||
queue->waiting_senders = NULL;
|
||||
queue->waiting_receivers = NULL;
|
||||
queue->waiting_sender_count = 0;
|
||||
queue->waiting_receiver_count = 0;
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Send to Queue */
|
||||
KernelStatus_t queue_send(Queue_t* queue, const void* item, TimeOut_t timeout) {
|
||||
if (queue == NULL || item == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
/* Check if queue is full */
|
||||
if (queue->current_items == queue->max_items) {
|
||||
if (timeout == 0) {
|
||||
critical_section_exit();
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Block current task */
|
||||
TaskHandle_t current = scheduler_get_current_task();
|
||||
|
||||
queue->waiting_senders = (TaskHandle_t*)realloc(queue->waiting_senders,
|
||||
(queue->waiting_sender_count + 1) * sizeof(TaskHandle_t));
|
||||
if (queue->waiting_senders == NULL) {
|
||||
critical_section_exit();
|
||||
return KERNEL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
queue->waiting_senders[queue->waiting_sender_count] = current;
|
||||
queue->waiting_sender_count++;
|
||||
|
||||
scheduler_update_task_state(current, TASK_BLOCKED);
|
||||
|
||||
if (timeout != (TimeOut_t)-1) {
|
||||
current->block_timeout = kernel_get_tick_count() + timeout;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
scheduler_yield();
|
||||
|
||||
/* Check if we were unblocked by receiver */
|
||||
critical_section_enter();
|
||||
if (queue->current_items < queue->max_items) {
|
||||
/* Copy item to queue */
|
||||
void* dest = (uint8_t*)queue->buffer + (queue->tail * queue->item_size);
|
||||
memcpy(dest, item, queue->item_size);
|
||||
queue->tail = (queue->tail + 1) % queue->max_items;
|
||||
queue->current_items++;
|
||||
critical_section_exit();
|
||||
return KERNEL_OK;
|
||||
}
|
||||
critical_section_exit();
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Queue has space */
|
||||
void* dest = (uint8_t*)queue->buffer + (queue->tail * queue->item_size);
|
||||
memcpy(dest, item, queue->item_size);
|
||||
queue->tail = (queue->tail + 1) % queue->max_items;
|
||||
queue->current_items++;
|
||||
|
||||
/* Wake up waiting receiver */
|
||||
if (queue->waiting_receiver_count > 0) {
|
||||
TaskHandle_t receiver = queue->waiting_receivers[0];
|
||||
|
||||
/* Remove from waiting list */
|
||||
for (uint32_t i = 1; i < queue->waiting_receiver_count; i++) {
|
||||
queue->waiting_receivers[i-1] = queue->waiting_receivers[i];
|
||||
}
|
||||
queue->waiting_receiver_count--;
|
||||
|
||||
scheduler_update_task_state(receiver, TASK_READY);
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
/* Reschedule if higher priority task was unblocked */
|
||||
scheduler_yield();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Receive from Queue */
|
||||
KernelStatus_t queue_receive(Queue_t* queue, void* item, TimeOut_t timeout) {
|
||||
if (queue == NULL || item == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
/* Check if queue is empty */
|
||||
if (queue->current_items == 0) {
|
||||
if (timeout == 0) {
|
||||
critical_section_exit();
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Block current task */
|
||||
TaskHandle_t current = scheduler_get_current_task();
|
||||
|
||||
queue->waiting_receivers = (TaskHandle_t*)realloc(queue->waiting_receivers,
|
||||
(queue->waiting_receiver_count + 1) * sizeof(TaskHandle_t));
|
||||
if (queue->waiting_receivers == NULL) {
|
||||
critical_section_exit();
|
||||
return KERNEL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
queue->waiting_receivers[queue->waiting_receiver_count] = current;
|
||||
queue->waiting_receiver_count++;
|
||||
|
||||
scheduler_update_task_state(current, TASK_BLOCKED);
|
||||
|
||||
if (timeout != (TimeOut_t)-1) {
|
||||
current->block_timeout = kernel_get_tick_count() + timeout;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
scheduler_yield();
|
||||
|
||||
/* Check if we were unblocked by sender */
|
||||
critical_section_enter();
|
||||
if (queue->current_items > 0) {
|
||||
/* Copy item from queue */
|
||||
void* src = (uint8_t*)queue->buffer + (queue->head * queue->item_size);
|
||||
memcpy(item, src, queue->item_size);
|
||||
queue->head = (queue->head + 1) % queue->max_items;
|
||||
queue->current_items--;
|
||||
critical_section_exit();
|
||||
return KERNEL_OK;
|
||||
}
|
||||
critical_section_exit();
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Queue has items */
|
||||
void* src = (uint8_t*)queue->buffer + (queue->head * queue->item_size);
|
||||
memcpy(item, src, queue->item_size);
|
||||
queue->head = (queue->head + 1) % queue->max_items;
|
||||
queue->current_items--;
|
||||
|
||||
/* Wake up waiting sender */
|
||||
if (queue->waiting_sender_count > 0) {
|
||||
TaskHandle_t sender = queue->waiting_senders[0];
|
||||
|
||||
/* Remove from waiting list */
|
||||
for (uint32_t i = 1; i < queue->waiting_sender_count; i++) {
|
||||
queue->waiting_senders[i-1] = queue->waiting_senders[i];
|
||||
}
|
||||
queue->waiting_sender_count--;
|
||||
|
||||
scheduler_update_task_state(sender, TASK_READY);
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
/* Reschedule if higher priority task was unblocked */
|
||||
scheduler_yield();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Send to Queue from ISR */
|
||||
KernelStatus_t queue_send_from_isr(Queue_t* queue, const void* item) {
|
||||
if (queue == NULL || item == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Check if queue is full */
|
||||
if (queue->current_items == queue->max_items) {
|
||||
return KERNEL_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
/* Copy item to queue */
|
||||
void* dest = (uint8_t*)queue->buffer + (queue->tail * queue->item_size);
|
||||
memcpy(dest, item, queue->item_size);
|
||||
queue->tail = (queue->tail + 1) % queue->max_items;
|
||||
queue->current_items++;
|
||||
|
||||
/* Wake up waiting receiver */
|
||||
if (queue->waiting_receiver_count > 0) {
|
||||
TaskHandle_t receiver = queue->waiting_receivers[0];
|
||||
|
||||
/* Remove from waiting list */
|
||||
for (uint32_t i = 1; i < queue->waiting_receiver_count; i++) {
|
||||
queue->waiting_receivers[i-1] = queue->waiting_receivers[i];
|
||||
}
|
||||
queue->waiting_receiver_count--;
|
||||
|
||||
scheduler_update_task_state(receiver, TASK_READY);
|
||||
}
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Receive from Queue from ISR */
|
||||
KernelStatus_t queue_receive_from_isr(Queue_t* queue, void* item) {
|
||||
if (queue == NULL || item == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* Check if queue is empty */
|
||||
if (queue->current_items == 0) {
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Copy item from queue */
|
||||
void* src = (uint8_t*)queue->buffer + (queue->head * queue->item_size);
|
||||
memcpy(item, src, queue->item_size);
|
||||
queue->head = (queue->head + 1) % queue->max_items;
|
||||
queue->current_items--;
|
||||
|
||||
/* Wake up waiting sender */
|
||||
if (queue->waiting_sender_count > 0) {
|
||||
TaskHandle_t sender = queue->waiting_senders[0];
|
||||
|
||||
/* Remove from waiting list */
|
||||
for (uint32_t i = 1; i < queue->waiting_sender_count; i++) {
|
||||
queue->waiting_senders[i-1] = queue->waiting_senders[i];
|
||||
}
|
||||
queue->waiting_sender_count--;
|
||||
|
||||
scheduler_update_task_state(sender, TASK_READY);
|
||||
}
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get Queue Count */
|
||||
uint32_t queue_get_count(Queue_t* queue) {
|
||||
if (queue == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return queue->current_items;
|
||||
}
|
||||
|
||||
/* Delete Queue */
|
||||
KernelStatus_t queue_delete(Queue_t* queue) {
|
||||
if (queue == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
/* Wake up all waiting tasks */
|
||||
for (uint32_t i = 0; i < queue->waiting_sender_count; i++) {
|
||||
scheduler_update_task_state(queue->waiting_senders[i], TASK_READY);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < queue->waiting_receiver_count; i++) {
|
||||
scheduler_update_task_state(queue->waiting_receivers[i], TASK_READY);
|
||||
}
|
||||
|
||||
if (queue->waiting_senders != NULL) {
|
||||
free(queue->waiting_senders);
|
||||
}
|
||||
|
||||
if (queue->waiting_receivers != NULL) {
|
||||
free(queue->waiting_receivers);
|
||||
}
|
||||
|
||||
memset(queue, 0, sizeof(Queue_t));
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
scheduler_yield();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* @file scheduler.c
|
||||
* @brief Priority-based preemptive scheduler implementation
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "scheduler.h"
|
||||
#include "task.h"
|
||||
#include "isr.h"
|
||||
|
||||
/* Scheduler State */
|
||||
static struct {
|
||||
SchedulerConfig_t config;
|
||||
bool running;
|
||||
TaskHandle_t current_task;
|
||||
TaskHandle_t ready_list[MAX_PRIORITY_LEVELS];
|
||||
uint32_t ready_bitmap;
|
||||
SchedulerStatistics_t statistics;
|
||||
uint32_t scheduling_locked;
|
||||
} scheduler_state;
|
||||
|
||||
/* Ready List Operations */
|
||||
static void ready_list_insert(TaskHandle_t task);
|
||||
static void ready_list_remove(TaskHandle_t task);
|
||||
static TaskHandle_t ready_list_get_highest(void);
|
||||
|
||||
/* Initialize Scheduler */
|
||||
void scheduler_init(const SchedulerConfig_t* config) {
|
||||
if (config != NULL) {
|
||||
scheduler_state.config = *config;
|
||||
} else {
|
||||
/* Default configuration */
|
||||
scheduler_state.config.type = SCHEDULER_PRIORITY_PREEMPTIVE;
|
||||
scheduler_state.config.time_slice_ticks = 10;
|
||||
scheduler_state.config.enable_deadline_monitoring = true;
|
||||
}
|
||||
|
||||
scheduler_state.running = false;
|
||||
scheduler_state.current_task = NULL;
|
||||
scheduler_state.ready_bitmap = 0;
|
||||
scheduler_state.scheduling_locked = 0;
|
||||
|
||||
/* Initialize ready lists */
|
||||
for (int i = 0; i < MAX_PRIORITY_LEVELS; i++) {
|
||||
scheduler_state.ready_list[i] = NULL;
|
||||
}
|
||||
|
||||
/* Initialize statistics */
|
||||
memset(&scheduler_state.statistics, 0, sizeof(SchedulerStatistics_t));
|
||||
}
|
||||
|
||||
/* Start Scheduler */
|
||||
void scheduler_start(void) {
|
||||
scheduler_state.running = true;
|
||||
|
||||
/* Select and run first task */
|
||||
TaskHandle_t first_task = ready_list_get_highest();
|
||||
if (first_task != NULL) {
|
||||
task_switch_context(first_task);
|
||||
}
|
||||
|
||||
/* Should never reach here */
|
||||
while (1) {
|
||||
/* Fallback loop */
|
||||
}
|
||||
}
|
||||
|
||||
/* Scheduler Tick Handler */
|
||||
void scheduler_tick(void) {
|
||||
if (!scheduler_state.running || scheduler_state.scheduling_locked > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update current task statistics */
|
||||
if (scheduler_state.current_task != NULL) {
|
||||
task_update_statistics();
|
||||
}
|
||||
|
||||
/* Handle periodic tasks */
|
||||
TaskHandle_t task = NULL;
|
||||
for (int i = 0; i < MAX_PRIORITY_LEVELS; i++) {
|
||||
task = scheduler_state.ready_list[i];
|
||||
while (task != NULL) {
|
||||
if (task->period_ticks > 0) {
|
||||
TickType_t current_time = kernel_get_tick_count();
|
||||
if ((current_time - task->last_wake_time) >= task->period_ticks) {
|
||||
task->state = TASK_READY;
|
||||
task->last_wake_time = current_time;
|
||||
if (scheduler_state.config.enable_deadline_monitoring) {
|
||||
task->deadline_ticks = current_time + task->period_ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
task = task->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for task preemption */
|
||||
TaskHandle_t highest_ready = ready_list_get_highest();
|
||||
if (highest_ready != NULL &&
|
||||
highest_ready != scheduler_state.current_task &&
|
||||
highest_ready->priority < scheduler_state.current_task->priority) {
|
||||
|
||||
/* Preempt current task */
|
||||
scheduler_state.statistics.preemptions++;
|
||||
scheduler_context_switch(highest_ready);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add Task to Scheduler */
|
||||
void scheduler_add_task(TaskHandle_t task) {
|
||||
if (task == NULL || task->priority >= MAX_PRIORITY_LEVELS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (task->state == TASK_READY) {
|
||||
ready_list_insert(task);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove Task from Scheduler */
|
||||
void scheduler_remove_task(TaskHandle_t task) {
|
||||
if (task == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
ready_list_remove(task);
|
||||
}
|
||||
|
||||
/* Update Task State in Scheduler */
|
||||
void scheduler_update_task_state(TaskHandle_t task, TaskState_t new_state) {
|
||||
if (task == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
TaskState_t old_state = task->state;
|
||||
task->state = new_state;
|
||||
|
||||
if (old_state == TASK_READY && new_state != TASK_READY) {
|
||||
/* Remove from ready list */
|
||||
ready_list_remove(task);
|
||||
} else if (old_state != TASK_READY && new_state == TASK_READY) {
|
||||
/* Add to ready list */
|
||||
ready_list_insert(task);
|
||||
}
|
||||
}
|
||||
|
||||
/* Select Next Task to Run */
|
||||
TaskHandle_t scheduler_select_next_task(void) {
|
||||
return ready_list_get_highest();
|
||||
}
|
||||
|
||||
/* Context Switch */
|
||||
void scheduler_context_switch(TaskHandle_t next_task) {
|
||||
if (next_task == NULL || next_task == scheduler_state.current_task) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update statistics */
|
||||
scheduler_state.statistics.context_switches++;
|
||||
scheduler_state.statistics.max_scheduling_latency =
|
||||
(scheduler_state.statistics.max_scheduling_latency >
|
||||
kernel_get_tick_count()) ?
|
||||
scheduler_state.statistics.max_scheduling_latency :
|
||||
kernel_get_tick_count();
|
||||
|
||||
/* Perform context switch */
|
||||
task_switch_context(next_task);
|
||||
}
|
||||
|
||||
/* Yield CPU */
|
||||
void scheduler_yield(void) {
|
||||
if (scheduler_state.scheduling_locked > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
TaskHandle_t next_task = ready_list_get_highest();
|
||||
if (next_task != NULL) {
|
||||
scheduler_context_switch(next_task);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lock Scheduler */
|
||||
void scheduler_lock(void) {
|
||||
critical_section_enter();
|
||||
scheduler_state.scheduling_locked++;
|
||||
critical_section_exit();
|
||||
}
|
||||
|
||||
/* Unlock Scheduler */
|
||||
void scheduler_unlock(void) {
|
||||
critical_section_enter();
|
||||
if (scheduler_state.scheduling_locked > 0) {
|
||||
scheduler_state.scheduling_locked--;
|
||||
if (scheduler_state.scheduling_locked == 0) {
|
||||
/* Check if rescheduling is needed */
|
||||
TaskHandle_t highest_ready = ready_list_get_highest();
|
||||
if (highest_ready != NULL &&
|
||||
highest_ready->priority < scheduler_state.current_task->priority) {
|
||||
scheduler_yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
critical_section_exit();
|
||||
}
|
||||
|
||||
/* Get Current Task */
|
||||
TaskHandle_t scheduler_get_current_task(void) {
|
||||
return scheduler_state.current_task;
|
||||
}
|
||||
|
||||
/* Get Scheduler Statistics */
|
||||
void scheduler_get_statistics(SchedulerStatistics_t* stats) {
|
||||
if (stats != NULL) {
|
||||
memcpy(stats, &scheduler_state.statistics, sizeof(SchedulerStatistics_t));
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert Task into Ready List */
|
||||
static void ready_list_insert(TaskHandle_t task) {
|
||||
if (task == NULL || task->priority >= MAX_PRIORITY_LEVELS) {
|
||||
return;
|
||||
}
|
||||
|
||||
TaskPriority_t priority = task->priority;
|
||||
|
||||
/* Insert at head of priority list (simple FIFO within priority) */
|
||||
task->next = scheduler_state.ready_list[priority];
|
||||
task->prev = NULL;
|
||||
|
||||
if (scheduler_state.ready_list[priority] != NULL) {
|
||||
scheduler_state.ready_list[priority]->prev = task;
|
||||
}
|
||||
|
||||
scheduler_state.ready_list[priority] = task;
|
||||
|
||||
/* Set ready bitmap */
|
||||
scheduler_state.ready_bitmap |= (1 << priority);
|
||||
}
|
||||
|
||||
/* Remove Task from Ready List */
|
||||
static void ready_list_remove(TaskHandle_t task) {
|
||||
if (task == NULL || task->priority >= MAX_PRIORITY_LEVELS) {
|
||||
return;
|
||||
}
|
||||
|
||||
TaskPriority_t priority = task->priority;
|
||||
|
||||
/* Remove from priority list */
|
||||
if (task->prev != NULL) {
|
||||
task->prev->next = task->next;
|
||||
} else {
|
||||
scheduler_state.ready_list[priority] = task->next;
|
||||
}
|
||||
|
||||
if (task->next != NULL) {
|
||||
task->next->prev = task->prev;
|
||||
}
|
||||
|
||||
task->next = NULL;
|
||||
task->prev = NULL;
|
||||
|
||||
/* Clear ready bitmap if list is empty */
|
||||
if (scheduler_state.ready_list[priority] == NULL) {
|
||||
scheduler_state.ready_bitmap &= ~(1 << priority);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get Highest Priority Ready Task */
|
||||
static TaskHandle_t ready_list_get_highest(void) {
|
||||
if (scheduler_state.ready_bitmap == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find highest priority (lowest number) with ready task */
|
||||
uint32_t bitmap = scheduler_state.ready_bitmap;
|
||||
int highest_priority = 0;
|
||||
|
||||
while ((bitmap & 1) == 0) {
|
||||
bitmap >>= 1;
|
||||
highest_priority++;
|
||||
}
|
||||
|
||||
if (highest_priority >= MAX_PRIORITY_LEVELS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return scheduler_state.ready_list[highest_priority];
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @file semaphore.c
|
||||
* @brief Semaphore implementation
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "semaphore.h"
|
||||
#include "scheduler.h"
|
||||
#include "isr.h"
|
||||
|
||||
/* Create Semaphore */
|
||||
KernelStatus_t semaphore_create(Semaphore_t* sem, SemaphoreType_t type,
|
||||
uint32_t initial_count, uint32_t max_count) {
|
||||
if (sem == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (type == SEMAPHORE_BINARY && max_count > 1) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (initial_count > max_count) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
sem->type = type;
|
||||
sem->count = initial_count;
|
||||
sem->max_count = max_count;
|
||||
sem->owner = NULL;
|
||||
sem->waiting_tasks = NULL;
|
||||
sem->waiting_count = 0;
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Take Semaphore */
|
||||
KernelStatus_t semaphore_take(Semaphore_t* sem, TimeOut_t timeout) {
|
||||
if (sem == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
if (sem->count > 0) {
|
||||
/* Semaphore available */
|
||||
sem->count--;
|
||||
if (sem->type == SEMAPHORE_MUTEX) {
|
||||
sem->owner = scheduler_get_current_task();
|
||||
}
|
||||
critical_section_exit();
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Semaphore not available */
|
||||
if (timeout == 0) {
|
||||
critical_section_exit();
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Block current task */
|
||||
TaskHandle_t current = scheduler_get_current_task();
|
||||
if (current == NULL) {
|
||||
critical_section_exit();
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
|
||||
/* Add to waiting list */
|
||||
sem->waiting_tasks = (TaskHandle_t*)realloc(sem->waiting_tasks,
|
||||
(sem->waiting_count + 1) * sizeof(TaskHandle_t));
|
||||
if (sem->waiting_tasks == NULL) {
|
||||
critical_section_exit();
|
||||
return KERNEL_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
sem->waiting_tasks[sem->waiting_count] = current;
|
||||
sem->waiting_count++;
|
||||
|
||||
/* Block task */
|
||||
scheduler_update_task_state(current, TASK_BLOCKED);
|
||||
|
||||
if (timeout != (TimeOut_t)-1) { /* Not infinite timeout */
|
||||
current->block_timeout = kernel_get_tick_count() + timeout;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
/* Yield to other tasks */
|
||||
scheduler_yield();
|
||||
|
||||
/* Task has been unblocked */
|
||||
critical_section_enter();
|
||||
|
||||
if (sem->count > 0 && sem->owner == current) {
|
||||
sem->count--;
|
||||
critical_section_exit();
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
return KERNEL_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Give Semaphore */
|
||||
KernelStatus_t semaphore_give(Semaphore_t* sem) {
|
||||
if (sem == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
if (sem->type == SEMAPHORE_MUTEX) {
|
||||
/* Check ownership */
|
||||
if (sem->owner != scheduler_get_current_task()) {
|
||||
critical_section_exit();
|
||||
return KERNEL_ERROR;
|
||||
}
|
||||
sem->owner = NULL;
|
||||
}
|
||||
|
||||
if (sem->count < sem->max_count) {
|
||||
sem->count++;
|
||||
}
|
||||
|
||||
/* Wake up waiting tasks */
|
||||
if (sem->waiting_count > 0) {
|
||||
TaskHandle_t waiting_task = sem->waiting_tasks[0];
|
||||
|
||||
/* Remove from waiting list */
|
||||
for (uint32_t i = 1; i < sem->waiting_count; i++) {
|
||||
sem->waiting_tasks[i-1] = sem->waiting_tasks[i];
|
||||
}
|
||||
sem->waiting_count--;
|
||||
|
||||
/* Unblock task */
|
||||
if (sem->type == SEMAPHORE_MUTEX) {
|
||||
sem->owner = waiting_task;
|
||||
}
|
||||
scheduler_update_task_state(waiting_task, TASK_READY);
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
/* Reschedule if needed */
|
||||
scheduler_yield();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Delete Semaphore */
|
||||
KernelStatus_t semaphore_delete(Semaphore_t* sem) {
|
||||
if (sem == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
/* Wake up all waiting tasks */
|
||||
for (uint32_t i = 0; i < sem->waiting_count; i++) {
|
||||
scheduler_update_task_state(sem->waiting_tasks[i], TASK_READY);
|
||||
}
|
||||
|
||||
if (sem->waiting_tasks != NULL) {
|
||||
free(sem->waiting_tasks);
|
||||
}
|
||||
|
||||
memset(sem, 0, sizeof(Semaphore_t));
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
scheduler_yield();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get Semaphore Count */
|
||||
uint32_t semaphore_get_count(Semaphore_t* sem) {
|
||||
if (sem == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return sem->count;
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* @file task.c
|
||||
* @brief Task management implementation
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "task.h"
|
||||
#include "scheduler.h"
|
||||
#include "isr.h"
|
||||
|
||||
/* Task List Head */
|
||||
static TaskHandle_t task_list_head = NULL;
|
||||
static TaskHandle_t task_list_tail = NULL;
|
||||
static uint32_t next_task_id = 0;
|
||||
static TaskHandle_t current_task = NULL;
|
||||
|
||||
/* Stack Overflow Pattern */
|
||||
#define STACK_FILL_PATTERN 0xA5A5A5A5
|
||||
#define STACK_CHECK_PATTERN 0xDEADBEEF
|
||||
|
||||
/* Initialize Task Management */
|
||||
void task_init(void) {
|
||||
task_list_head = NULL;
|
||||
task_list_tail = NULL;
|
||||
next_task_id = 1;
|
||||
current_task = NULL;
|
||||
}
|
||||
|
||||
/* Create Task */
|
||||
TaskHandle_t task_create(const TaskConfig_t* config) {
|
||||
if (config == NULL || config->function == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (config->priority >= MAX_PRIORITY_LEVELS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate task control block */
|
||||
TaskHandle_t task = (TaskHandle_t)malloc(sizeof(struct TaskControlBlock));
|
||||
if (task == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize task control block */
|
||||
memset(task, 0, sizeof(struct TaskControlBlock));
|
||||
|
||||
/* Set task name */
|
||||
if (config->name != NULL) {
|
||||
strncpy(task->name, config->name, MAX_TASK_NAME_LENGTH - 1);
|
||||
} else {
|
||||
snprintf(task->name, MAX_TASK_NAME_LENGTH, "task_%u", next_task_id);
|
||||
}
|
||||
|
||||
/* Initialize task fields */
|
||||
task->task_id = next_task_id++;
|
||||
task->function = config->function;
|
||||
task->parameters = config->parameters;
|
||||
task->priority = config->priority;
|
||||
task->state = TASK_SUSPENDED;
|
||||
task->period_ticks = config->period_ticks;
|
||||
task->last_wake_time = 0;
|
||||
|
||||
/* Allocate stack */
|
||||
task->stack_size = config->stack_size;
|
||||
task->stack_base = (uint32_t*)malloc(task->stack_size);
|
||||
if (task->stack_base == NULL) {
|
||||
free(task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize stack with pattern for overflow detection */
|
||||
memset(task->stack_base, STACK_FILL_PATTERN, task->stack_size);
|
||||
|
||||
/* Set initial stack pointer (grows downward on ARM) */
|
||||
task->stack_pointer = task->stack_base + (task->stack_size / sizeof(uint32_t)) - 16;
|
||||
|
||||
/* Initialize task context for first run */
|
||||
task->context[0] = (uint32_t)task->function; /* PC */
|
||||
task->context[1] = 0x01000000; /* xPSR */
|
||||
task->context[2] = (uint32_t)task->parameters; /* R0 */
|
||||
task->context[3] = 0; /* R1 */
|
||||
task->context[4] = 0; /* R2 */
|
||||
task->context[5] = 0; /* R3 */
|
||||
task->context[6] = 0; /* R12 */
|
||||
task->context[7] = 0; /* LR */
|
||||
task->context[8] = (uint32_t)task->stack_pointer; /* PSP */
|
||||
|
||||
/* Add to task list */
|
||||
critical_section_enter();
|
||||
|
||||
if (task_list_head == NULL) {
|
||||
task_list_head = task;
|
||||
task_list_tail = task;
|
||||
} else {
|
||||
task_list_tail->next = task;
|
||||
task->prev = task_list_tail;
|
||||
task_list_tail = task;
|
||||
}
|
||||
|
||||
/* Make task ready */
|
||||
task->state = TASK_READY;
|
||||
scheduler_add_task(task);
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/* Delete Task */
|
||||
KernelStatus_t task_delete(TaskHandle_t task) {
|
||||
if (task == NULL || task == current_task) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
/* Remove from scheduler */
|
||||
scheduler_remove_task(task);
|
||||
|
||||
/* Remove from task list */
|
||||
if (task->prev != NULL) {
|
||||
task->prev->next = task->next;
|
||||
} else {
|
||||
task_list_head = task->next;
|
||||
}
|
||||
|
||||
if (task->next != NULL) {
|
||||
task->next->prev = task->prev;
|
||||
} else {
|
||||
task_list_tail = task->prev;
|
||||
}
|
||||
|
||||
/* Free stack and task control block */
|
||||
free(task->stack_base);
|
||||
free(task);
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Suspend Task */
|
||||
KernelStatus_t task_suspend(TaskHandle_t task) {
|
||||
if (task == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
if (task->state != TASK_SUSPENDED) {
|
||||
task->state = TASK_SUSPENDED;
|
||||
scheduler_update_task_state(task, TASK_SUSPENDED);
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
if (task == current_task) {
|
||||
scheduler_yield();
|
||||
}
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Resume Task */
|
||||
KernelStatus_t task_resume(TaskHandle_t task) {
|
||||
if (task == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
if (task->state == TASK_SUSPENDED) {
|
||||
task->state = TASK_READY;
|
||||
scheduler_update_task_state(task, TASK_READY);
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Set Task Priority */
|
||||
KernelStatus_t task_set_priority(TaskHandle_t task, TaskPriority_t new_priority) {
|
||||
if (task == NULL || new_priority >= MAX_PRIORITY_LEVELS) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
task->priority = new_priority;
|
||||
critical_section_exit();
|
||||
|
||||
/* Reschedule if needed */
|
||||
scheduler_yield();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Get Task Priority */
|
||||
TaskPriority_t task_get_priority(TaskHandle_t task) {
|
||||
if (task == NULL) {
|
||||
return MAX_PRIORITY_LEVELS;
|
||||
}
|
||||
return task->priority;
|
||||
}
|
||||
|
||||
/* Get Task State */
|
||||
TaskState_t task_get_state(TaskHandle_t task) {
|
||||
if (task == NULL) {
|
||||
return TASK_TERMINATED;
|
||||
}
|
||||
return task->state;
|
||||
}
|
||||
|
||||
/* Get Task Statistics */
|
||||
KernelStatus_t task_get_statistics(TaskHandle_t task, TaskStatistics_t* stats) {
|
||||
if (task == NULL || stats == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
memcpy(stats, &task->statistics, sizeof(TaskStatistics_t));
|
||||
|
||||
/* Update stack high water mark */
|
||||
task_check_stack_overflow();
|
||||
stats->stack_high_water_mark = task->stack_high_water_mark;
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Switch Context to Next Task */
|
||||
void task_switch_context(TaskHandle_t next_task) {
|
||||
if (next_task == NULL || next_task == current_task) {
|
||||
return;
|
||||
}
|
||||
|
||||
TaskHandle_t previous_task = current_task;
|
||||
current_task = next_task;
|
||||
|
||||
/* Update task states */
|
||||
if (previous_task != NULL && previous_task->state == TASK_RUNNING) {
|
||||
previous_task->state = TASK_READY;
|
||||
}
|
||||
next_task->state = TASK_RUNNING;
|
||||
|
||||
/* Perform architecture-specific context switch */
|
||||
// This is implemented in port_asm.s
|
||||
port_context_switch(&previous_task->context, &next_task->context);
|
||||
}
|
||||
|
||||
/* Get Idle Task */
|
||||
TaskHandle_t task_get_idle_task(void) {
|
||||
/* Return the idle task (stored in kernel state) */
|
||||
extern TaskHandle_t kernel_get_idle_task(void);
|
||||
return kernel_get_idle_task();
|
||||
}
|
||||
|
||||
/* Update Task Statistics */
|
||||
void task_update_statistics(void) {
|
||||
if (current_task == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
TickType_t current_time = kernel_get_tick_count();
|
||||
|
||||
current_task->statistics.execution_count++;
|
||||
current_task->statistics.last_execution_time = current_time;
|
||||
|
||||
/* Check for deadline miss */
|
||||
if (current_task->deadline_ticks > 0 &&
|
||||
current_time > current_task->deadline_ticks) {
|
||||
current_task->statistics.deadline_misses++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check Stack Overflow */
|
||||
void task_check_stack_overflow(void) {
|
||||
if (current_task == NULL || current_task->stack_base == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check stack guard pattern */
|
||||
uint32_t* stack_bottom = current_task->stack_base;
|
||||
uint32_t guard_size = 16; /* Number of guard words */
|
||||
|
||||
for (uint32_t i = 0; i < guard_size; i++) {
|
||||
if (stack_bottom[i] != STACK_FILL_PATTERN) {
|
||||
/* Stack overflow detected! */
|
||||
fault_handler_stack_overflow(current_task);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate stack usage */
|
||||
uint32_t* stack_ptr = current_task->stack_base;
|
||||
uint32_t* stack_top = current_task->stack_base +
|
||||
(current_task->stack_size / sizeof(uint32_t));
|
||||
uint32_t used_words = 0;
|
||||
|
||||
while (stack_ptr < stack_top && *stack_ptr != STACK_FILL_PATTERN) {
|
||||
used_words++;
|
||||
stack_ptr++;
|
||||
}
|
||||
|
||||
current_task->stack_high_water_mark = used_words * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
/* Check if Task is Ready */
|
||||
bool task_is_ready(TaskHandle_t task) {
|
||||
return (task != NULL && task->state == TASK_READY);
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* @file timer.c
|
||||
* @brief Software timer implementation
|
||||
*/
|
||||
|
||||
#include "kernel.h"
|
||||
#include "timer.h"
|
||||
#include "scheduler.h"
|
||||
#include "isr.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Timer List */
|
||||
static Timer_t* timer_list = NULL;
|
||||
|
||||
/* Create Timer */
|
||||
KernelStatus_t timer_create(Timer_t* timer, const char* name, TimerType_t type,
|
||||
TickType_t period, TimerCallback_t callback,
|
||||
void* parameters) {
|
||||
if (timer == NULL || callback == NULL || period == 0) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
if (name != NULL) {
|
||||
strncpy(timer->name, name, sizeof(timer->name) - 1);
|
||||
timer->name[sizeof(timer->name) - 1] = '\0';
|
||||
} else {
|
||||
timer->name[0] = '\0';
|
||||
}
|
||||
|
||||
timer->type = type;
|
||||
timer->period_ticks = period;
|
||||
timer->expiry_time = 0;
|
||||
timer->callback = callback;
|
||||
timer->parameters = parameters;
|
||||
timer->is_active = false;
|
||||
timer->next = NULL;
|
||||
|
||||
/* Add to timer list */
|
||||
timer->next = timer_list;
|
||||
timer_list = timer;
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Start Timer */
|
||||
KernelStatus_t timer_start(Timer_t* timer) {
|
||||
if (timer == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
timer->expiry_time = kernel_get_tick_count() + timer->period_ticks;
|
||||
timer->is_active = true;
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Stop Timer */
|
||||
KernelStatus_t timer_stop(Timer_t* timer) {
|
||||
if (timer == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
timer->is_active = false;
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Reset Timer */
|
||||
KernelStatus_t timer_reset(Timer_t* timer) {
|
||||
if (timer == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
if (timer->is_active) {
|
||||
timer->expiry_time = kernel_get_tick_count() + timer->period_ticks;
|
||||
}
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Delete Timer */
|
||||
KernelStatus_t timer_delete(Timer_t* timer) {
|
||||
if (timer == NULL) {
|
||||
return KERNEL_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
critical_section_enter();
|
||||
|
||||
/* Remove from timer list */
|
||||
if (timer_list == timer) {
|
||||
timer_list = timer->next;
|
||||
} else {
|
||||
Timer_t* current = timer_list;
|
||||
while (current != NULL && current->next != timer) {
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
if (current != NULL) {
|
||||
current->next = timer->next;
|
||||
}
|
||||
}
|
||||
|
||||
memset(timer, 0, sizeof(Timer_t));
|
||||
|
||||
critical_section_exit();
|
||||
|
||||
return KERNEL_OK;
|
||||
}
|
||||
|
||||
/* Check if Timer is Active */
|
||||
bool timer_is_active(Timer_t* timer) {
|
||||
if (timer == NULL) {
|
||||
return false;
|
||||
}
|
||||
return timer->is_active;
|
||||
}
|
||||
|
||||
/* Process Expired Timers - Called from tick handler */
|
||||
void timer_process_expired(void) {
|
||||
TickType_t current_time = kernel_get_tick_count();
|
||||
Timer_t* timer = timer_list;
|
||||
|
||||
while (timer != NULL) {
|
||||
if (timer->is_active && timer->expiry_time <= current_time) {
|
||||
/* Timer expired */
|
||||
timer->is_active = false;
|
||||
|
||||
/* Call callback */
|
||||
if (timer->callback != NULL) {
|
||||
timer->callback(timer->parameters);
|
||||
}
|
||||
|
||||
/* Restart if periodic */
|
||||
if (timer->type == TIMER_PERIODIC) {
|
||||
timer->expiry_time = current_time + timer->period_ticks;
|
||||
timer->is_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
timer = timer->next;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user