OpenRTX/rtos/uC-OS3/Ports/POSIX/os_cpu_c.c

731 lines
24 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
*********************************************************************************************************
* uC/OS-III
* The Real-Time Kernel
*
* Copyright 2009-2020 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* POSIX GNU Port
*
* File : os_cpu_c.c
* Version : V3.08.00
*********************************************************************************************************
* For : POSIX
* Toolchain : GNU
*********************************************************************************************************
*/
#define OS_CPU_GLOBALS
#define _GNU_SOURCE
#ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
const CPU_CHAR *os_cpu_c__c = "$Id: $";
#endif
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#include "../../Source/os.h"
#include <os_cfg_app.h>
#include <stdio.h>
#include <pthread.h>
#include <stdint.h>
#include <signal.h>
#include <semaphore.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <errno.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
#define THREAD_CREATE_PRIO 50u /* Tasks underlying posix threads prio. */
/* Err handling convenience macro. */
#define ERR_CHK(func) do {int res = func; \
if (res != 0u) { \
printf("Error in call '%s' from %s(): %s­\r\n", #func, __FUNCTION__, strerror(res)); \
perror(" \\->'errno' indicates (might not be relevant if function doesn't use 'errno')"); \
raise(SIGABRT); \
} \
} while(0)
/*
*********************************************************************************************************
* LOCAL DATA TYPES
*********************************************************************************************************
*/
typedef struct os_tcb_ext_posix {
pthread_t Thread;
pid_t ProcessId;
sem_t InitSem;
sem_t Sem;
} OS_TCB_EXT_POSIX;
/*
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void *OSTaskPosix (void *p_arg);
static void OSTaskTerminate (OS_TCB *p_tcb);
static void OSThreadCreate (pthread_t *p_thread,
void *p_task,
void *p_arg,
int prio);
static void OSTimeTickHandler (void);
/*
*********************************************************************************************************
* LOCAL VARIABLES
*********************************************************************************************************
*/
/* Tick timer cfg. */
static CPU_TMR_INTERRUPT OSTickTmrInterrupt = { .Interrupt.NamePtr = "Tick tmr interrupt",
.Interrupt.Prio = 10u,
.Interrupt.TraceEn = 0u,
.Interrupt.ISR_Fnct = OSTimeTickHandler,
.Interrupt.En = 1u,
.OneShot = 0u,
.PeriodSec = 0u,
.PeriodMuSec = (1000000u / OS_CFG_TICK_RATE_HZ)
};
/*
*********************************************************************************************************
* LOCAL CONFIGURATION ERRORS
*********************************************************************************************************
*/
#if (OS_CFG_TICK_RATE_HZ > 100u)
#warning "Time accuracy cannot be maintained with OS_CFG_TICK_RATE_HZ > 100u.\n\n",
#endif
/*
*********************************************************************************************************
* IDLE TASK HOOK
*
* Description: This function is called by the idle task. This hook has been added to allow you to do
* such things as STOP the CPU to conserve power.
*
* Arguments : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
void OSIdleTaskHook (void)
{
#if OS_CFG_APP_HOOKS_EN > 0u
if (OS_AppIdleTaskHookPtr != (OS_APP_HOOK_VOID)0) {
(*OS_AppIdleTaskHookPtr)();
}
#endif
sleep(1u); /* Reduce CPU utilization. */
}
/*
*********************************************************************************************************
* OS INITIALIZATION HOOK
*
* Description: This function is called by OSInit() at the beginning of OSInit().
*
* Arguments : None.
*
* Note(s) : 1) Interrupts should be disabled during this call.
*********************************************************************************************************
*/
void OSInitHook (void)
{
struct rlimit rtprio_limits;
ERR_CHK(getrlimit(RLIMIT_RTPRIO, &rtprio_limits));
if (rtprio_limits.rlim_cur != RLIM_INFINITY) {
printf("Error: RTPRIO limit is too low. Set to 'unlimited' via 'ulimit -r' or /etc/security/limits.conf\r\n");
exit(-1);
}
CPU_IntInit(); /* Initialize critical section objects. */
}
/*
*********************************************************************************************************
* STATISTIC TASK HOOK
*
* Description: This function is called every second by uC/OS-III's statistics task. This allows your
* application to add functionality to the statistics task.
*
* Arguments : None.
*
* Note(s) : None.
*********************************************************************************************************
*/
void OSStatTaskHook (void)
{
#if OS_CFG_APP_HOOKS_EN > 0u
if (OS_AppStatTaskHookPtr != (OS_APP_HOOK_VOID)0) {
(*OS_AppStatTaskHookPtr)();
}
#endif
}
/*
*********************************************************************************************************
* TASK CREATION HOOK
*
* Description: This function is called when a task is created.
*
* Arguments : p_tcb Pointer to the task control block of the task being created.
*
* Note(s) : 1) Interrupts are disabled during this call.
*********************************************************************************************************
*/
void OSTaskCreateHook (OS_TCB *p_tcb)
{
OS_TCB_EXT_POSIX *p_tcb_ext;
int ret;
#if OS_CFG_APP_HOOKS_EN > 0u
if (OS_AppTaskCreateHookPtr != (OS_APP_HOOK_TCB)0) {
(*OS_AppTaskCreateHookPtr)(p_tcb);
}
#endif
p_tcb_ext = malloc(sizeof(OS_TCB_EXT_POSIX));
p_tcb->ExtPtr = p_tcb_ext;
ERR_CHK(sem_init(&p_tcb_ext->InitSem, 0u, 0u));
ERR_CHK(sem_init(&p_tcb_ext->Sem, 0u, 0u));
OSThreadCreate(&p_tcb_ext->Thread, OSTaskPosix, p_tcb, THREAD_CREATE_PRIO);
do {
ret = sem_wait(&p_tcb_ext->InitSem); /* Wait for init. */
if (ret != 0 && errno != EINTR) {
raise(SIGABRT);
}
} while (ret != 0);
}
/*
*********************************************************************************************************
* TASK DELETION HOOK
*
* Description: This function is called when a task is deleted.
*
* Arguments : p_tcb Pointer to the task control block of the task being deleted.
*
* Note(s) : 1) Interrupts are disabled during this call.
*********************************************************************************************************
*/
void OSTaskDelHook (OS_TCB *p_tcb)
{
OS_TCB_EXT_POSIX *p_tcb_ext = (OS_TCB_EXT_POSIX *)p_tcb->ExtPtr;
pthread_t self;
CPU_BOOLEAN same;
#if OS_CFG_APP_HOOKS_EN > 0u
if (OS_AppTaskDelHookPtr != (OS_APP_HOOK_TCB)0) {
(*OS_AppTaskDelHookPtr)(p_tcb);
}
#endif
self = pthread_self();
same = (pthread_equal(self, p_tcb_ext->Thread) != 0u);
if (same != 1u) {
ERR_CHK(pthread_cancel(p_tcb_ext->Thread));
}
OSTaskTerminate(p_tcb);
}
/*
*********************************************************************************************************
* TASK RETURN HOOK
*
* Description: This function is called if a task accidentally returns. In other words, a task should
* either be an infinite loop or delete itself when done.
*
* Arguments : p_tcb Pointer to the task control block of the task that is returning.
*
* Note(s) : None.
*********************************************************************************************************
*/
void OSTaskReturnHook (OS_TCB *p_tcb)
{
#if OS_CFG_APP_HOOKS_EN > 0u
if (OS_AppTaskReturnHookPtr != (OS_APP_HOOK_TCB)0) {
(*OS_AppTaskReturnHookPtr)(p_tcb);
}
#else
(void)p_tcb; /* Prevent compiler warning */
#endif
}
/*
*********************************************************************************************************
* INITIALIZE A TASK'S STACK
*
* Description: This function is called by OS_Task_Create() or OSTaskCreateExt() to initialize the stack
* frame of the task being created. This function is highly processor specific.
*
* Arguments : p_task Pointer to the task entry point address.
*
* p_arg Pointer to a user supplied data area that will be passed to the task
* when the task first executes.
*
* p_stk_base Pointer to the base address of the stack.
*
* stk_size Size of the stack, in number of CPU_STK elements.
*
* opt Options used to alter the behavior of OS_Task_StkInit().
* (see OS.H for OS_TASK_OPT_xxx).
*
* Returns : Always returns the location of the new top-of-stack' once the processor registers have
* been placed on the stack in the proper order.
*********************************************************************************************************
*/
CPU_STK *OSTaskStkInit (OS_TASK_PTR p_task,
void *p_arg,
CPU_STK *p_stk_base,
CPU_STK *p_stk_limit,
CPU_STK_SIZE stk_size,
OS_OPT opt)
{
return (p_stk_base);
}
/*
*********************************************************************************************************
* TASK SWITCH HOOK
*
* Description: This function is called when a task switch is performed. This allows you to perform other
* operations during a context switch.
*
* Arguments : None.
*
* Note(s) : 1) Interrupts are disabled during this call.
* 2) It is assumed that the global pointer 'OSTCBHighRdyPtr' points to the TCB of the task
* that will be 'switched in' (i.e. the highest priority task) and, 'OSTCBCurPtr' points
* to the task being switched out (i.e. the preempted task).
*********************************************************************************************************
*/
void OSTaskSwHook (void)
{
#if OS_CFG_TASK_PROFILE_EN > 0u
CPU_TS ts;
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
CPU_TS int_dis_time;
#endif
#if OS_CFG_APP_HOOKS_EN > 0u
if (OS_AppTaskSwHookPtr != (OS_APP_HOOK_VOID)0) {
(*OS_AppTaskSwHookPtr)();
}
#endif
#if OS_CFG_TASK_PROFILE_EN > 0u
ts = OS_TS_GET();
if (OSTCBCurPtr != OSTCBHighRdyPtr) {
OSTCBCurPtr->CyclesDelta = ts - OSTCBCurPtr->CyclesStart;
OSTCBCurPtr->CyclesTotal += (OS_CYCLES)OSTCBCurPtr->CyclesDelta;
}
OSTCBHighRdyPtr->CyclesStart = ts;
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
int_dis_time = CPU_IntDisMeasMaxCurReset(); /* Keep track of per-task interrupt disable time */
if (OSTCBCurPtr->IntDisTimeMax < int_dis_time) {
OSTCBCurPtr->IntDisTimeMax = int_dis_time;
}
#endif
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0u
/* Keep track of per-task scheduler lock time */
if (OSTCBCurPtr->SchedLockTimeMax < (CPU_TS)OSSchedLockTimeMaxCur) {
OSTCBCurPtr->SchedLockTimeMax = (CPU_TS)OSSchedLockTimeMaxCur;
}
OSSchedLockTimeMaxCur = (CPU_TS)0; /* Reset the per-task value */
#endif
}
/*
*********************************************************************************************************
* TICK HOOK
*
* Description: This function is called every tick.
*
* Arguments : None.
*
* Note(s) : 1) This function is assumed to be called from the Tick ISR.
*********************************************************************************************************
*/
void OSTimeTickHook (void)
{
#if OS_CFG_APP_HOOKS_EN > 0u
if (OS_AppTimeTickHookPtr != (OS_APP_HOOK_VOID)0) {
(*OS_AppTimeTickHookPtr)();
}
#endif
}
/*
*********************************************************************************************************
* START HIGHEST PRIORITY TASK READY-TO-RUN
*
* Description: This function is called by OSStart() to start the highest priority task that was created
* by your application before calling OSStart().
*
* Arguments : None.
*
* Note(s) : 1) OSStartHighRdy() MUST:
* a) Call OSTaskSwHook() then,
* b) Switch to the highest priority task.
*********************************************************************************************************
*/
void OSStartHighRdy (void)
{
OS_TCB_EXT_POSIX *p_tcb_ext;
sigset_t sig_set;
int signo;
OSTaskSwHook();
p_tcb_ext = (OS_TCB_EXT_POSIX *)OSTCBCurPtr->ExtPtr;
CPU_INT_DIS();
ERR_CHK(sem_post(&p_tcb_ext->Sem));
ERR_CHK(sigemptyset(&sig_set));
ERR_CHK(sigaddset(&sig_set, SIGTERM));
ERR_CHK(sigwait(&sig_set, &signo));
}
/*
*********************************************************************************************************
* TASK LEVEL CONTEXT SWITCH
*
* Description: This function is called when a task makes a higher priority task ready-to-run.
*
* Arguments : None.
*
* Note(s) : 1) Upon entry,
* OSTCBCur points to the OS_TCB of the task to suspend
* OSTCBHighRdy points to the OS_TCB of the task to resume
*
* 2) OSCtxSw() MUST:
* a) Save processor registers then,
* b) Save current task's stack pointer into the current task's OS_TCB,
* c) Call OSTaskSwHook(),
* d) Set OSTCBCur = OSTCBHighRdy,
* e) Set OSPrioCur = OSPrioHighRdy,
* f) Switch to the highest priority task.
*
* pseudo-code:
* void OSCtxSw (void)
* {
* Save processor registers;
*
* OSTCBCur->OSTCBStkPtr = SP;
*
* OSTaskSwHook();
*
* OSTCBCur = OSTCBHighRdy;
* OSPrioCur = OSPrioHighRdy;
*
* Restore processor registers from (OSTCBHighRdy->OSTCBStkPtr);
* }
*********************************************************************************************************
*/
void OSCtxSw (void)
{
OS_TCB_EXT_POSIX *p_tcb_ext_old;
OS_TCB_EXT_POSIX *p_tcb_ext_new;
int ret;
CPU_BOOLEAN detach = 0u;
OSTaskSwHook();
p_tcb_ext_new = (OS_TCB_EXT_POSIX *)OSTCBHighRdyPtr->ExtPtr;
p_tcb_ext_old = (OS_TCB_EXT_POSIX *)OSTCBCurPtr->ExtPtr;
if (OSTCBCurPtr->TaskState == OS_TASK_STATE_DEL) {
detach = 1u;
}
OSTCBCurPtr = OSTCBHighRdyPtr;
OSPrioCur = OSPrioHighRdy;
ERR_CHK(sem_post(&p_tcb_ext_new->Sem));
if (detach == 0u) {
do {
ret = sem_wait(&p_tcb_ext_old->Sem);
if (ret != 0 && errno != EINTR) {
raise(SIGABRT);
}
} while (ret != 0);
}
}
/*
*********************************************************************************************************
* INTERRUPT LEVEL CONTEXT SWITCH
*
* Description: This function is called by OSIntExit() to perform a context switch from an ISR.
*
* Arguments : None.
*
* Note(s) : 1) OSIntCtxSw() MUST:
* a) Call OSTaskSwHook() then,
* b) Set OSTCBCurPtr = OSTCBHighRdyPtr,
* c) Set OSPrioCur = OSPrioHighRdy,
* d) Switch to the highest priority task.
*
* 2) OSIntCurTaskSuspend() MUST be called prior to OSIntEnter().
*
* 3) OSIntCurTaskResume() MUST be called after OSIntExit() to switch to the highest
* priority task.
*********************************************************************************************************
*/
void OSIntCtxSw (void)
{
if (OSTCBCurPtr != OSTCBHighRdyPtr) {
OSCtxSw();
}
}
/*
*********************************************************************************************************
* INITIALIZE SYS TICK
*
* Description: Initialize the SysTick.
*
* Arguments : none.
*
* Note(s) : 1) This function MUST be called after OSStart() & after processor initialization.
*********************************************************************************************************
*/
void OS_CPU_SysTickInit (void)
{
CPU_TmrInterruptCreate(&OSTickTmrInterrupt);
}
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
static void OSTimeTickHandler (void)
{
OSIntEnter();
OSTimeTick();
CPU_ISR_End();
OSIntExit();
}
/*
*********************************************************************************************************
* OSTaskPosix()
*
* Description: This function is a generic POSIX task wrapper for uC/OS-III tasks.
*
* Arguments : p_arg Pointer to argument of the task's TCB.
*
* Note(s) : 1) Priorities of these tasks are very important.
*********************************************************************************************************
*/
static void *OSTaskPosix (void *p_arg)
{
OS_TCB_EXT_POSIX *p_tcb_ext;
OS_TCB *p_tcb;
OS_ERR err;
p_tcb = (OS_TCB *)p_arg;
p_tcb_ext = (OS_TCB_EXT_POSIX *)p_tcb->ExtPtr;
p_tcb_ext->ProcessId = syscall(SYS_gettid);
ERR_CHK(sem_post(&p_tcb_ext->InitSem));
#ifdef OS_CFG_MSG_TRACE_EN
if (p_tcb->NamePtr != (CPU_CHAR *)0) {
printf("Task[%3.1d] '%-32s' running\n", p_tcb->Prio, p_tcb->NamePtr);
}
#endif
CPU_INT_DIS();
{
int ret = -1u;
while (ret != 0u) {
ret = sem_wait(&p_tcb_ext->Sem); /* Wait until first CTX SW. */
if ((ret != 0) && (ret != -EINTR)) {
ERR_CHK(ret);
}
}
}
CPU_INT_EN();
((void (*)(void *))p_tcb->TaskEntryAddr)(p_tcb->TaskEntryArg);
OSTaskDel(p_tcb, &err); /* Thread may exit at OSCtxSw(). */
return (0u);
}
/*
*********************************************************************************************************
* OSTaskTerminate()
*
* Description: This function handles task termination control signals.
*
* Arguments : p_task Pointer to the task information structure of the task to clear its control
* signals.
*********************************************************************************************************
*/
static void OSTaskTerminate (OS_TCB *p_tcb)
{
#ifdef OS_CFG_MSG_TRACE_EN
if (p_tcb->NamePtr != (CPU_CHAR *)0) {
printf("Task[%3.1d] '%-32s' deleted\n", p_tcb->Prio, p_tcb->NamePtr);
}
#endif
free(p_tcb->ExtPtr);
}
/*
*********************************************************************************************************
* OSThreadCreate()
*
* Description : Create new posix thread.
*
* Argument(s) : p_thread Pointer to preallocated thread variable.
*
* p_task Pointer to associated function.
*
* p_arg Pointer to associated function's argument.
*
* prio Thread priority.
*
* Return(s) : Thread's corresponding LWP pid.
*
* Note(s) : none.
*
*********************************************************************************************************
*/
static void OSThreadCreate (pthread_t *p_thread,
void *p_task,
void *p_arg,
int prio)
{
pthread_attr_t attr;
struct sched_param param;
if (prio < sched_get_priority_min(SCHED_RR) ||
prio > sched_get_priority_max(SCHED_RR)) {
#ifdef OS_CFG_MSG_TRACE_EN
printf("ThreadCreate(): Invalid prio arg.\n");
#endif
raise(SIGABRT);
}
ERR_CHK(pthread_attr_init(&attr));
ERR_CHK(pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED));
param.__sched_priority = prio;
ERR_CHK(pthread_attr_setschedpolicy(&attr, SCHED_RR));
ERR_CHK(pthread_attr_setschedparam(&attr, &param));
ERR_CHK(pthread_create(p_thread, &attr, p_task, p_arg));
}
#ifdef __cplusplus
}
#endif