📄 rtos_services.c
字号:
/*
** Copyright (C) 2006 Tamir Michael
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* TODO:
* fix performance measurement
* measure interrupt execution time (optional, MACRO?)
* optional stack status checks (MACRO ?). cound be interesting for ISRs
* allow tasks to be signalled (unblocked) when waiting
* write a "blocking" post thread message
*/
#include <XC167.h>
#include <stdio.h>
#include "system_messages.h"
#include "rtos_services.h"
#include "synchronization.h"
#include "general_definitions.h"
#include "trace_buffer.h"
// used to map a priority (the index of the array) to a time slice (the contents of the array)
static int16s s_task_time_slices[] = {TIME_SLICE_0, TIME_SLICE_1, TIME_SLICE_2, TIME_SLICE_3, TIME_SLICE_4, TIME_SLICE_5, TIME_SLICE_6, TIME_SLICE_7} ;
// this table returns the index of the task group with the highest priority.
// each task administration has a 'priority_bitmap' member of type int8u (8 bit). fr example, if the administration's
// 'priority_bitmap' value is 0x34 = 00110100 (meaning: there are tasks with priorities 2, 4 and 5 ready to run)
// this table returns 2, because the lower the value of the priority, the higher it is considered.
static int8u s_priority_resolution_table[] =
{
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x00 to 0x0F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x10 to 0x1F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x20 to 0x2F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x30 to 0x3F */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x40 to 0x4F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x50 to 0x5F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x60 to 0x6F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x70 to 0x7F */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x80 to 0x8F */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0x90 to 0x9F */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xA0 to 0xAF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xB0 to 0xBF */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xC0 to 0xCF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xD0 to 0xDF */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 0xE0 to 0xEF */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 0xF0 to 0xFF */
} ;
task_info g_tcb[MAX_TASKS] ; // TCB - task control block. this is a global.
int16s g_running_task = 0 ;
static int16s s_number_of_tasks = 0 ;
static int16s s_number_of_timers = 0 ;
static schedule_policy s_schedule_policy = ePriorityBased ;
static timer_info s_timers[MAX_TIMERS] ; // system timers (of all types)
int32u volatile g_tick_count = 0;
static stacktype s_idle_stack[DEFUALT_STACK_SIZE] ;
static int32u s_schedule_session = 0 ; // used to efficPSW_IENtly reload the time-slices of tasks
static queue_info s_suspended_tasks_queue ; // tasks that are explicitly suspended
static queue_info s_waiting_tasks_queue ; // tasks blocked on a synchronization primitive
static queue_info s_waiting_for_messages_tasks_queue ; // tasks waiting for incoming messages
// performance measurements
static int32u s_max_performance_task_counter = 0 ;
static int32u s_last_performance_task_counter = 0 ;
static int16s s_performance_mutex ;
static int8u s_cpu_load = 0 ;
static stacktype s_performance_task_stack[DEFUALT_STACK_SIZE] ; // note: user stack + processor stack (as system stack defined in A66 file * 2)
static bit s_scheduler_started = 0 ;
// the scheduler shall switch to this task id if an interrupt occurs and a task is waiting for it.
// this variable is set in the respective ISR.
int16s g_force_switch_to_task_interrupt_occurred = -1 ;
// if the scheduler is suspended, the system repeatedly switches from the task that suspended the scheduler to
// the idle task and back. the id of the task that suspended the scheduler is save in
// "s_scheduler_disabled_idle_task_forced_context_switch_to_task".
static int16s s_scheduler_disabled_idle_task_forced_context_switch_to_task = -1 ;
// user defined callback in case of system failure
callback_ptr g_system_failure_hook = 0 ;
int16s g_vector_to_task_index[NUM_INTERRUPTS] ;
// primary and secondary task administrations. switched once the active task is empty. tasks that consume their
// time slice are transfered to the secondary administration pending the switch. the usage of pointers here is
// intended to speed up administration switch by reducing it to a simple pointer address assigmnet operation (see
// 'scheduler_select_next_stack').
static prio_array s_main_priority_array ;
static prio_array s_secondary_priority_array ;
prio_array *s_primary_ready_to_run_tasks = &s_main_priority_array ;
static prio_array *s_secondary_ready_to_run_tasks = &s_secondary_priority_array ;
// idle task hook
static callback_ptr s_idle_task_hook ;
static int32s s_idle_task_hook_parameter ;
// task stack snapshot
static stacktype s_task_snapshot_image[DEFUALT_STACK_SIZE] ; // the snapshot buffer
static stacktype *s_task_snapshot_sp = (stacktype *)SYSTEM_STACK_TOP ; // offset in the snapshot buffer - actually stack top
static int16s s_snapshot_task_id ;
// WAR STORY
// these variables are only used in 'task_switch', but it is not desirable to allocate them on the processor stack (that
// contains the state of the task that is left) because they will be copied into the stack-copy of the task.
// so they are allocated at compile time instead.
static stacktype *s_lp_source, *s_lp_target, *s_lp_sourceSP, *s_lp_next_stack ;
static stacktype **s_lp_R0 ; // todo: static here is used to prevent the compiler from optimizing s_lp_R0 assignment after #pragma and other measures failed. find a way to solve this.
static int16u s_active_task ;
// allowed from interrupt context; does not change anything
int8u rtos_get_cpu_load()
{
int8u l_load ;
rtos_mutex_lock(s_performance_mutex) ;
l_load = s_cpu_load ;
rtos_mutex_unlock(s_performance_mutex) ;
return l_load ;
}
// returns the index of the next free timer
static int16s s_find_next_available_timer()
{
int16s l_index ;
timer_info *lp_timer ;
for (l_index = 0; l_index < MAX_TIMERS; l_index++)
{
lp_timer = s_timers + l_index ;
if (lp_timer->state == eTimerNotAllocated)
{
return l_index ;
}
}
return ERR_TIMER_NOT_FOUND ;
}
// allows invocation of this functionality without meddling with interrupts (from 'scheduler_task_wait').
// calling 'rtos_save_and_disable_interrupts' twice in a row with interrupts disabled will not allow them to
// be enabled again without calling 'rtos_enable_interrupts' which is less desired than restoring
// the previous state with 'rtos_restore_interrupts'.
static int16s s_internal_allocate_timer()
{
int16s l_index ;
timer_info *lp_timer ;
if ( (l_index = s_find_next_available_timer() ) != ERR_TIMER_NOT_FOUND)
{
lp_timer = s_timers + l_index ;
lp_timer->owner = ERR_TIMER_NOT_FOUND ; // an owner will be set once this Mutex is locked
lp_timer->state = eTimerAllocated ;
lp_timer->parameter = 0 ;
lp_timer->id = l_index ;
s_number_of_timers++ ;
}
return l_index ;
}
// invoked once a second, to calculate CPU load. 'init_performance_meter' ran for one second
// to measure how many CPU cycles elapse at 100% CPU time. depending on how well 'performance_task' does
// (how often is it being scheduled?) this interrupt handler will calculate CPU load
/*void s_performanceTask()
{
// allow the idle task count one second before determining how far it managed to get; that is a reflection of "0%" CPU usage (only the idle task runs).
scheduler_task_wait(SECONDS(1)) ;
// save the results
rtos_mutex_lock(s_performance_mutex) ;
s_max_performance_task_counter = s_last_performance_task_counter ;
s_last_performance_task_counter = 0 ;
rtos_mutex_unlock(s_performance_mutex) ;
// continue measuring periodically
while (1)
{
scheduler_task_wait(SECONDS(1)) ;
rtos_mutex_lock(s_performance_mutex) ;
s_cpu_load = 100 - ( (float32)s_last_performance_task_counter / (float32)s_max_performance_task_counter * 100) ;
s_last_performance_task_counter = 0 ;
rtos_mutex_unlock(s_performance_mutex) ;
}
} */
// idle system task. always executed.
static void s_idle_task()
{
while (1)
{
if (s_idle_task_hook)
{
int8u l_interrupts_status ;
// the kernel is not reentrant; protect static/global data from ISRs
rtos_save_and_disable_interrupts(&l_interrupts_status) ;
s_idle_task_hook(s_idle_task_hook_parameter) ;
rtos_restore_interrupts(l_interrupts_status) ;
scheduler_reschedule() ;
}
}
}
int32s rtos_system_time(int16u *hours, int16u *minutes, int32u *seconds, int32u *milliseconds)
{
int32u l_tick_count_copy ;
if ( (!hours) || (!minutes) || (!seconds) || (!milliseconds) )
{
software_warning("%s %s %d", resolve_system_message(ERR_INVALID_PARAMETER), __FILE__, __LINE__ ) ;
return ERR_INVALID_PARAMETER ;
}
_atomic_(0) ;
l_tick_count_copy = g_tick_count ;
_endatomic_() ;
*milliseconds = l_tick_count_copy / 10L ;
*seconds = *milliseconds / 1000L ;
*minutes = (int16u)(*seconds / 60L) ;
*hours = (int16u)(*minutes / 60L) ;
return 0 ;
}
static int8u s_updatable_timer(int16s timer)
{
switch (s_timers[timer].state)
{
case eTimerTicking:
return 1 ;
case eTimerNotAllocated:
case eTimerAllocated:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -