Commit d38e73b9 authored by Michael Zehrer's avatar Michael Zehrer
Browse files

tidy up code and simplify the porting interface

parent 3c97a078
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/src")
# Headers
target_sources(${UNIT} PUBLIC
"${CMAKE_CURRENT_LIST_DIR}/inc/arctos/porting/all.hpp"
"${CMAKE_CURRENT_LIST_DIR}/inc/arctos/porting/bare-metal.hpp"
)
# Internal include directory
target_include_directories(${UNIT} PUBLIC
"${CMAKE_CURRENT_LIST_DIR}/inc/"
)
/**
* Copyright (c) 2020, ARCTOS
* All rights reserved.
*
* @licence BSD
* @brief Porting interface functions
* @author Michael Zehrer
*/
#include <arctos/porting/independent.hpp>
#include <arctos/porting/bare-metal.hpp>
/**
* Copyright (c) 2020, ARCTOS
* All rights reserved.
*
* @licence BSD
* @brief Porting interface functions
* @author Michael Zehrer
*/
#ifndef ARCTOS_PORTING_BARE_METAL_HPP
#define ARCTOS_PORTING_BARE_METAL_HPP
#include <arctos/task.hpp>
namespace arctos {
namespace porting {
/**
* @brief Atomic compare and exchange.
*
* @param[in/out] v volatile memory
* @param[in] o old value
* @param[in] n new value
*
* @return old value; test if successful by comparing old with return value
*/
int32_t atomicCompareAndExchange(int32_t *v, int32_t o, int32_t n);
/**
* @brief Atomic compare and exchange.
*
* @param[in/out] v volatile memory
* @param[in] o old value
* @param[in] n new value
*
* @return old value; test if successful by comparing old with return value
*/
int64_t atomicCompareAndExchange(int64_t *v, int64_t o, int64_t n);
void saveContextAndCallScheduler(void) asm("arctos_saveContextAndCallScheduler");
void activateContext(void *context) asm("arctos_activateContext");
void runActiveContext(void) asm("arctos_runActiveContext");
/**
* @brief Creates a context on the stack and return a pointer to it
*
* This context has to look like it had been created by a normal
* context switch. Don't forget to align, if necessary
* @note EACH PORTATION MUST PROVIDE THIS FUNCTION!
*
* @param[in] stack Pointer to the top of the stack (stack grows downwards)
* @param[in] task Pointer to the underlying task
*/
uint32_t *createContext(uint32_t *stack, Task *task);
} /* namespace porting */
} /* namespace arctos */
#endif /* ARCTOS_PORTING_BARE_METAL_HPP */
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/porting")
target_sources(${UNIT} PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/cxxabi.cpp"
"${CMAKE_CURRENT_LIST_DIR}/hw_interface.cpp"
"${CMAKE_CURRENT_LIST_DIR}/scheduler.cpp"
"${CMAKE_CURRENT_LIST_DIR}/task.cpp"
)
/**
* Copyright (c) 2018, Michael Zehrer
* Copyright (c) 2018-2020, ARCTOS
* All rights reserved.
*
* @licence BSD
......@@ -24,7 +24,7 @@
#include <stdint.h>
#include <arctos/log/logging.hpp>
#include <arctos/hw_interface.h>
#include <arctos/porting/all.hpp>
#ifdef __cplusplus
namespace __cxxabiv1 {
......@@ -112,8 +112,9 @@ int __cxa_guard_acquire (__guard *g) {
if (g->detail.state == 1)
return 0;
int32_t *guard_state = reinterpret_cast<int32_t *>(&g->detail.state);
// if possible, we are ready to initialize, set state to 2 (PENDING) and return 1
if (atomic_cmpxchg(reinterpret_cast<int32_t *>(&g->detail.state), 0, 2) == 0)
if (::arctos::porting::atomicCompareAndExchange(guard_state, 0, 2) == 0)
return 1;
else {
// if not possible, wait for completion of the other thread
......
target_sources(${UNIT} PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/bare-metal.cpp"
)
/**
* Copyright (c) 2018, Michael Zehrer
* Copyright (c) 2018-2020, ARCTOS
* All rights reserved.
*
* @licence BSD
* @brief Some of the functions from the HW-Interface can be implemented
* @brief Some of the functions from the Porting-Interface can be implemented
* independently with other well known functions and/or definitions.
* Implementations suitable for bare-metal targets come in here.
* @author Michael Zehrer
*/
#include <stdint.h>
#include <arctos/hw_interface.hpp>
#include <arctos/porting/all.hpp>
extern "C" {
uint8_t _dynamic_memory_start_;
......@@ -18,9 +17,14 @@ extern "C" {
}
namespace arctos {
namespace porting {
__attribute__((weak))
uint32_t *_hwAllocateMemory(uint32_t &size) {
/**
* @note This interface function is optional for bare-metal targets.
* If you don't implement it, your linker script must provide
* the symbols '_dynamic_memory_start_' and '_dynamic_memory_end_'
*/
__attribute__((weak)) uint32_t *allocateMemory(uint32_t &size) {
// dyn_start = ALIGN(4Byte, _dynamic_memory_start_)
static uintptr_t dyn_start_addr = 0;
if (dyn_start_addr == 0)
......@@ -41,4 +45,5 @@ uint32_t *_hwAllocateMemory(uint32_t &size) {
return reinterpret_cast<uint32_t*>(lastdyn_start_addr);
}
} /* namespace porting */
} /* namespace arctos */
/**
* Copyright (c) 2018-2019, Michael Zehrer
* Copyright (c) 2018-2020, ARCTOS
* All rights reserved.
*
* @licence BSD
......@@ -9,12 +9,12 @@
#include <stdbool.h>
#include <stdint.h>
#include <arctos/porting/all.hpp>
#include <arctos/log/logging.hpp>
#include <arctos/task.hpp>
#include <arctos/time.hpp>
#include <arctos/scheduler.hpp>
#include <arctos/hw_interface.hpp>
namespace arctos {
......@@ -22,8 +22,8 @@ namespace arctos {
* Idle-Task
***********************************************************************/
/**
* @class IdleTask
* @brief This task will be executed if no other task wants to run
* @class IdleTask
* @brief This task will be executed if no other task wants to run
*/
class IdleTask : public Task {
public:
......@@ -64,51 +64,51 @@ void Scheduler::idle() {
//idle_task.suspendedUntil = 0;
this->schedule();
_hwRunActiveContext();
porting::runActiveContext();
}
void Scheduler::schedule() {
if (this->p_preselected_task == nullptr)
if (this->preselected_task_ == nullptr)
this->findNextToRun(NOW());
Task *p_next_to_run = this->p_preselected_task;
int64_t now = this->preselected_time;
Task *next_to_run = this->preselected_task_;
int64_t now = this->preselected_time_;
this->clearCachedSelection();
p_next_to_run->last_activation = now;
p_next_to_run->activate();
next_to_run->last_activation_ = now;
next_to_run->activate();
}
void Scheduler::onSystemTick(void *p_ctx) {
void Scheduler::onSystemTick(void *ctx) {
// TODO
Task::p_current->p_context = p_ctx;
Task::current->context_ = ctx;
this->schedule();
}
void Scheduler::onTimeEvent(void *p_ctx) {
void Scheduler::onTimeEvent(void *ctx) {
// TODO
Task::p_current->p_context = p_ctx;
Task::current->context_ = ctx;
this->schedule();
}
Task *Scheduler::findNextToRun(int64_t now) {
this->preselected_time = now;
this->p_preselected_task = &idle_task;
this->preselected_time_ = now;
this->preselected_task_ = &idle_task;
ITERATE_LIST(Task, Task::tasks) {
Task *t = item->getContent();
// TODO
this->p_preselected_task = t;
this->preselected_task_ = t;
break;
}
return this->p_preselected_task;
return this->preselected_task_;
}
void Scheduler::wrapper(void *p_ctx) {
Task::p_current->p_context = p_ctx;
void Scheduler::wrapper(void *ctx) {
Task::current->context_ = ctx;
Scheduler::unique.schedule();
}
......
/**
* Copyright (c) 2018-2019, Michael Zehrer
* Copyright (c) 2018-2020, ARCTOS
* All rights reserved.
*
* @licence BSD
......@@ -10,9 +10,8 @@
#include <arctos/log/logger.hpp>
#include <arctos/task.hpp>
#include <arctos/time.hpp>
#include <arctos/hw_interface.hpp>
#include <arctos/scheduler.hpp>
#include <arctos/porting/all.hpp>
#define EMPTY_MEMORY_MARKER 0xDEADBEEF
......@@ -20,26 +19,26 @@ namespace arctos {
/***********************************************************************
* Task - Non-Static stuff
***********************************************************************/
Task::Task(const char *p_name, const uint32_t priority, uint32_t stack_size)
: self(ListItem<Task>(this, priority)), p_name(p_name),
last_activation(0), suspended_until(0) {
Task::Task(const char *name, const uint32_t priority, uint32_t stack_size)
: self_(ListItem<Task>(this, priority)), name_(name),
last_activation_(0), suspended_until_(0) {
this->p_stack_low = _hwAllocateMemory(stack_size);
if (this->p_stack_low == nullptr)
log::critical("Not enough memory for task '%s'. The user requested %lu bytes", p_name, stack_size);
this->p_stack_high = reinterpret_cast<uint32_t*>(
reinterpret_cast<uintptr_t>(this->p_stack_low) + stack_size);
this->stack_low_ = porting::allocateMemory(stack_size);
if (this->stack_low_ == nullptr)
log::critical("Not enough memory for task '%s'. The user requested %lu bytes", name, stack_size);
this->stack_high_ = reinterpret_cast<uint32_t*>(
reinterpret_cast<uintptr_t>(this->stack_low_) + stack_size);
// paint the stack space
for (uint32_t *stk = this->p_stack_high; stk >= this->p_stack_low; --stk)
for (uint32_t *stk = this->stack_high_; stk >= this->stack_low_; --stk)
*stk = EMPTY_MEMORY_MARKER;
this->p_context = _hwInitContext(this->p_stack_high, this);
this->context_ = porting::createContext(this->stack_high_, this);
Task::tasks.insert(&this->self);
Task::tasks.insert(&this->self_);
}
Task::~Task() {
Task::tasks.remove(&this->self);
Task::tasks.remove(&this->self_);
// TODO
}
void Task::activate() {
......@@ -50,15 +49,15 @@ void Task::activate() {
Timer::start();
__asmSwitchToContext((long*)context);
*/
Task::p_current = this;
_hwActivateContext(this->p_context);
Task::current = this;
porting::activateContext(this->context_);
}
uint32_t Task::getMaxStackUsage() const {
uint32_t *stk = this->p_stack_low;
uint32_t *stk = this->stack_low_;
while (*stk == EMPTY_MEMORY_MARKER)
++stk;
uint32_t free = (stk - this->p_stack_low) * 4;
uint32_t free = (stk - this->stack_low_) * 4;
return this->getStackSize() - free;
}
......@@ -70,7 +69,7 @@ uint32_t Task::getMaxStackUsage() const {
__attribute__((init_priority(200)))
LinkedList<Task> Task::tasks;
Task *Task::p_current = nullptr;
Task *Task::current = nullptr;
void Task::initAll() {
log::system("Task's in System:");
......@@ -93,13 +92,13 @@ void Task::initAll() {
__attribute__(( naked ))
void Task::startupWrapper(Task *task) {
Task::p_current = task;
Task::current = task;
//task->suspended_until = 0;
task->run();
// TODO
Task::tasks.remove(&task->self);
Task::tasks.remove(&task->self_);
while (1) {
//task->suspended_until = END_OF_TIME;
......@@ -108,14 +107,14 @@ void Task::startupWrapper(Task *task) {
}
void Task::yield() {
Scheduler *p_scheduler = Scheduler::getInstance();
Task *p_preselection = p_scheduler->findNextToRun(NOW());
Scheduler *scheduler = Scheduler::getInstance();
Task *preselection = scheduler->findNextToRun(NOW());
if (p_preselection == Task::p_current) {
p_scheduler->clearCachedSelection();
if (preselection == Task::current) {
scheduler->clearCachedSelection();
return;
}
_hwSaveContextAndCallScheduler();
porting::saveContextAndCallScheduler();
}
} /* namespace arctos */
......@@ -6,7 +6,6 @@ target_sources(${UNIT} PUBLIC
"${CMAKE_CURRENT_LIST_DIR}/api/arctos/memory/partitioning.hpp"
"${CMAKE_CURRENT_LIST_DIR}/api/arctos/fifo.hpp"
"${CMAKE_CURRENT_LIST_DIR}/api/arctos/list.hpp"
"${CMAKE_CURRENT_LIST_DIR}/api/arctos/mutex.hpp"
"${CMAKE_CURRENT_LIST_DIR}/api/arctos/printable.hpp"
"${CMAKE_CURRENT_LIST_DIR}/api/arctos/scheduler.hpp"
"${CMAKE_CURRENT_LIST_DIR}/api/arctos/task.hpp"
......@@ -15,8 +14,8 @@ target_sources(${UNIT} PUBLIC
"${CMAKE_CURRENT_LIST_DIR}/api/new.hpp"
"${CMAKE_CURRENT_LIST_DIR}/inc/arctos/log/logger.hpp"
"${CMAKE_CURRENT_LIST_DIR}/inc/arctos/hw_interface.h"
"${CMAKE_CURRENT_LIST_DIR}/inc/arctos/hw_interface.hpp"
"${CMAKE_CURRENT_LIST_DIR}/inc/arctos/porting/all.hpp"
"${CMAKE_CURRENT_LIST_DIR}/inc/arctos/porting/independent.hpp"
)
# Internal include directory
......
/**
* Copyright (c) 2018, Michael Zehrer
* All rights reserved.
*
* @licence BSD
* @brief Simple Mutex-Implementation
* @author Michael Zehrer
*/
#ifndef ARCTOS_MUTEX_HPP
#define ARCTOS_MUTEX_HPP
#include <stdint.h>
#include <stdbool.h>
namespace arctos {
// TODO comment and implement
class Mutex {
protected:
uint32_t holder_priority;
public:
Mutex() : holder_priority(0) { }
void lock(void);
void unlock(void);
bool tryLock(void);
};
} /* namespace arctos */
#endif /* ARCTOS_MUTEX_HPP */
/**
* Copyright (c) 2018-2019, Michael Zehrer
* Copyright (c) 2018-2020, ARCTOS
* All rights reserved.
*
* @licence BSD
......@@ -11,7 +11,6 @@
#include <stdint.h>
#include <arctos/task.hpp>
#include <arctos/hw_interface.hpp>
namespace arctos {
......@@ -23,10 +22,8 @@ namespace arctos {
*/
class Scheduler {
private:
int64_t preselected_time = 0;
Task *p_preselected_task = nullptr;
OSTimer timer;
int64_t preselected_time_ = 0;
Task *preselected_task_ = nullptr;
Scheduler(void) {};
......@@ -35,17 +32,17 @@ public:
void idle(void);
void schedule(void);
void onSystemTick(void *p_ctx);
void onTimeEvent(void *p_ctx);
void onSystemTick(void *ctx);
void onTimeEvent(void *ctx);
Task *findNextToRun(int64_t now);
void clearCachedSelection() {
this->preselected_time = 0;
this->p_preselected_task = nullptr;
this->preselected_time_ = 0;
this->preselected_task_ = nullptr;
}
static Scheduler *getInstance() { return &Scheduler::unique; }
static void wrapper(void *p_ctx) asm("_arctosSchedulerWrapper");
static void wrapper(void *ctx) asm("arctos_SchedulerWrapper");
};
} /* namespace arctos */
......
/**
* Copyright (c) 2018, Michael Zehrer
* Copyright (c) 2018-2020, ARCTOS
* All rights reserved.
*
* @licence BSD
......@@ -20,30 +20,31 @@ namespace arctos {
class Task {
friend class Scheduler;
friend void _initSystem(void);
friend void initSystem(void);
private:
ListItem<Task> self;
void *volatile p_context;
const char *p_name;
uint32_t *p_stack_low;
uint32_t *p_stack_high;
volatile int64_t last_activation;
volatile int64_t suspended_until;
ListItem<Task> self_;
void *volatile context_;
const char *name_;
uint32_t *stack_low_;
uint32_t *stack_high_;
volatile int64_t last_activation_;
volatile int64_t suspended_until_;
/**
* @brief Activates this task. (This function is called by the scheduler)
* It restores the context of this task and continues the execution.
* @brief Activates this task.
*
* This function is called by the scheduler.
* It restores the context of this task and continues the execution.
*/
void activate();
static LinkedList<Task> tasks;
static Task *p_current;
static Task *current;
/**
* @brief Initializes all tasks in the system
* (call there init()-function)
* @brief Initializes all tasks in the system (call there init()-function)
*/
static void initAll();
public:
......@@ -51,15 +52,16 @@ public:
const uint32_t priority = ARCTOS_DEFAULT_TASK_PRIORITY,
uint32_t stack_size = ARCTOS_DEFAULT_STACKSIZE);
virtual ~Task();
/**
* @brief Entry point for user code.
* The tasks activities shall be implemented by overloading this method.
* @brief Entry point for user code.
*
* The tasks activities shall be implemented by overloading this method.
*/
virtual void run() = 0;
/**
* @brief Perform some initialization stuff before the task is been started
* @brief Perform some initialization stuff before the task is been started
*
* This method is called at startup BEFORE the tasks are started.
*
......@@ -69,22 +71,22 @@ public:
* @see run
*/
virtual void init() { }
/**
* @brief Get the priority of this task
* @brief Get the priority of this task
* @return priority
*/
uint32_t getPriority() const { return this->self.getRank(); }
const char *getName() const { return this->p_name; }
uint32_t getStackSize() const { return (this->p_stack_high - this->p_stack_low) * 4; }
uint32_t getPriority() const { return this->self_.getRank(); }
const char *getName() const { return this->name_; }
uint32_t getStackSize() const { return (this->stack_high_ - this->stack_low_) * 4; }
uint32_t getMaxStackUsage() const;
static Task * getCurrent() { return p_current; }
static void startupWrapper(Task *task) asm("_arctosTaskStartupWrapper");
static Task *getCurrent() { return current; }
static void startupWrapper(Task *task) asm("arctos_TaskStartupWrapper");
/**
* @brief Cause the currently running task to pause and perform a reschedule.
* @brief Cause the currently running task to pause and perform a reschedule.
*/
static void yield();
};
......
/**
* Copyright (c) 2018, Michael Zehrer
* Copyright (c) 2018-2020, ARCTOS
* All rights reserved.
*
* @licence BSD
......
/**
* Copyright (c) 2018-2019, Michael Zehrer
* All rights reserved.
*
* @licence BSD
* @brief ARCTOS hardware interface functions (C)
* @author Michael Zehrer
*/