aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2016-01-05 22:01:51 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2016-01-05 22:01:51 +0100
commitef4a7bbe3a818e5166548d1ba5edee7713c66214 (patch)
treeb0ba8576af43c1a391f36c8b3ce12229d1399112
parentec96951943921b57ef9c1e9dacb63e34716fe5b7 (diff)
downloadstm32f103-playground-ef4a7bbe3a818e5166548d1ba5edee7713c66214.tar.gz
stm32f103-playground-ef4a7bbe3a818e5166548d1ba5edee7713c66214.tar.bz2
stm32f103-playground-ef4a7bbe3a818e5166548d1ba5edee7713c66214.tar.xz
stm32f103-playground-ef4a7bbe3a818e5166548d1ba5edee7713c66214.zip
o Seemingly working Mutexes.
o Dropping the privileged/unprivileged split for now.
-rw-r--r--apps/os2/os2.cpp197
-rw-r--r--apps/os2/os2_cm3.s20
-rw-r--r--cmake/stm32.toolchain.cmake4
-rw-r--r--playground/include/playground.h1
-rw-r--r--playground/src/init_high.cpp7
5 files changed, 191 insertions, 38 deletions
diff --git a/apps/os2/os2.cpp b/apps/os2/os2.cpp
index 66c5b4d..b7e4edc 100644
--- a/apps/os2/os2.cpp
+++ b/apps/os2/os2.cpp
@@ -4,7 +4,6 @@
#include <stm32f10x.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_gpio.h>
-#include <inttypes.h>
#include "debug.h"
#include "tinyprintf.h"
@@ -13,12 +12,86 @@
namespace trygvis {
namespace os2 {
+namespace generic {
+
+template<typename _index_t, unsigned bits>
+class bitmap {
+ static_assert(std::is_signed<_index_t>::value, "_index_t has to be a signed type");
+
+public:
+ typedef _index_t index_t;
+
+ virtual void assign(index_t bit, bool value) = 0;
+
+ virtual void set(index_t bit) = 0;
+
+ virtual bool get(index_t bit) = 0;
+
+ virtual bool is_empty() = 0;
+
+ virtual index_t find_first() = 0;
+};
+
+// TODO: these operations has to be made atomic
+class bitmap_32 : public bitmap<int8_t, 32> {
+public:
+ bitmap_32() : map_(0) {
+ }
+
+ void assign(index_t bit, bool value) {
+ if (value) {
+ map_ |= 1 << bit;
+ } else {
+ map_ &= ~(1 << bit);
+ }
+ }
+
+ void set(index_t bit) {
+ map_ |= 1 << bit;
+ }
+
+ void clear(index_t bit) {
+ map_ &= ~(1 << bit);
+ }
+
+ bool get(index_t bit) {
+ return (map_ & 1 << bit) > 0;
+ }
+
+ bool is_empty() {
+ return map_ == 0;
+ }
+
+ index_t find_first() {
+ for (index_t i = 0; i < 32; i++) {
+ if (get(i)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+private:
+ uint32_t map_;
+};
+
+} // generic
+
+namespace os {
+
+using namespace trygvis::os2::generic;
+
+
/*
* System configuration
*/
constexpr bool enable_stack_smashing_check = false;
-namespace os {
+typedef bitmap_32 process_map;
+
+typedef int8_t pid_t;
+#define PRIpid_t "d"
enum class exc_return_t : uint32_t {
RETURN_TO_HANDLER_MODE_USE_MSP = 0xFFFFFFF1,
@@ -38,10 +111,22 @@ struct task_t {
this->current_stack = current_stack;
this->stack_start = stack_start;
this->stack_end = stack_end;
- flags = 0x01;
+ set_active();
set_ready();
}
+ void fini() {
+ this->flags = 0;
+ }
+
+ void set_active() {
+ flags |= 0x01;
+ }
+
+ bool is_active() {
+ return (flags & 0x01) > 0;
+ }
+
bool is_ready() {
return (flags & 0x02) > 0;
}
@@ -68,9 +153,9 @@ static_assert(stack_size % 4 == 0, "stack_size must be word-aligned.");
task_t tasks[max_task_count];
uint8_t stacks[SizeOfArray(tasks)][stack_size];
uint8_t task_count = 0;
-uint32_t current_task;
+pid_t current_task;
-const unsigned int SYSTICK_FREQUENCY_HZ = 10;
+const unsigned int SYSTICK_FREQUENCY_HZ = 50;
struct hardware_frame_t {
uint32_t r0;
@@ -107,7 +192,7 @@ void HardFault_Handler_C(uint32_t *stack) {
dbg_printf("psr = 0x%08lx (%lu)\n", stack[7], stack[7]);
dbg_printf("\n");
- dbg_printf("current_task = %" PRIu32 "\n", current_task);
+ dbg_printf("current_task = %" PRIpid_t "\n", current_task);
dbg_printf("\n");
Default_Handler();
@@ -139,12 +224,22 @@ void SVC_Handler() {
/**
* Implemented in assembly, simply executes an SVC instruction.
*/
-__attribute__((used))
-extern void reschedule();
+//__attribute__((used))
+//extern void reschedule();
+
+void reschedule() {
+// dbg_printf("rescheduling %" PRIpid_t ", ready=%d\n", current_task, tasks[current_task].is_ready());
+ SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk;
+}
-// This doesn't quite work.
+// TODO: test properly
+__attribute__((noreturn))
void thread_end() {
- dbg_printf("thread_end(). current_task=%" PRIu32 "\n", current_task);
+ dbg_printf("thread_end(): current_task=%" PRIpid_t "\n", current_task);
+ tasks[current_task].fini();
+ reschedule();
+ while (1) {
+ }
}
extern "C" void asm_idle_task(const void *const);
@@ -227,19 +322,28 @@ void os_create_thread(void (task)(void const *const), bool create_sw = true) {
}
static
-uint32_t find_first_ready_task() {
+pid_t find_first_ready_task() {
+// dbg_printf("find_first_ready_task: current_task=%" PRIpid_t ", max_task_count=%d\n", current_task, max_task_count);
+// for(int i = 0; i < max_task_count; i++) {
+// task_t *t = &tasks[i];
+// dbg_printf("task #%d, active=%d, ready=%d\n", i, t->is_active(), t->is_ready());
+// }
+
task_t *t;
- // Start from the task after the current one
- uint32_t idx = current_task;
+
+ pid_t end_task = current_task == 0 ? pid_t(1) : current_task;
+ pid_t idx = end_task;
do {
idx++;
+// dbg_printf("idx=%" PRIpid_t "\n", idx);
// If we have checked the entire array, start from the first non-idle task
if (idx == max_task_count) {
idx = 1;
}
t = &tasks[idx];
- if (idx == current_task) {
+ if (idx == end_task) {
+// dbg_printf("wrap\n");
// If we have checked all other tasks, use the current one if it is still ready. if not, use the idle task
if (!t->is_ready()) {
idx = 0;
@@ -249,6 +353,7 @@ uint32_t find_first_ready_task() {
} while (!t->is_ready());
+// dbg_printf("new task: %d\n", idx);
return idx;
}
@@ -263,7 +368,7 @@ void check_stack_smashing<false>(task_t *) {
template<>
void check_stack_smashing<true>(task_t *t) {
if (t->current_stack < t->stack_end) {
- dbg_printf("STACK SMASHED: task #%" PRIu32 ", end=%p, current=%p\n", current_task,
+ dbg_printf("STACK SMASHED: task #%" PRIpid_t ", end=%p, current=%p\n", current_task,
static_cast<void *>(t->current_stack), static_cast<void *>(t->current_stack));
}
}
@@ -276,11 +381,14 @@ task_t *select_next_task(uint8_t *current_stack) {
check_stack_smashing<enable_stack_smashing_check>(t);
- uint32_t new_task = find_first_ready_task();
+ pid_t new_task = find_first_ready_task();
if (new_task != current_task) {
+// dbg_printf("switch: %d -> %d\n", current_task, new_task);
t = &tasks[new_task];
current_task = new_task;
+ } else {
+// dbg_printf("no switch: %d\n", current_task);
}
return t;
@@ -294,7 +402,10 @@ void os_init() {
NVIC_SetPriority(SysTick_IRQn, 0xff);
NVIC_SetPriority(PendSV_IRQn, 0xff);
- SysTick_Config(SystemCoreClock / SYSTICK_FREQUENCY_HZ);
+ uint32_t err = SysTick_Config(SystemCoreClock / SYSTICK_FREQUENCY_HZ);
+ if (err) {
+ dbg_printf("Failed init of system timer.");
+ }
GPIO_InitTypeDef init;
GPIO_StructInit(&init);
@@ -337,23 +448,53 @@ void os_start() {
//};
class Mutex final {
+private:
+ process_map sleepers;
+ static const uint32_t UNLOCKED = 0x80000000;
+
public:
- Mutex() : owner(UINT32_MAX) {
+ Mutex() : owner(UNLOCKED) {
}
void lock() {
- uint32_t old;
+ static_assert(sizeof(process_map::index_t) <= 31,
+ "The process map's index type has to fit within 31 bits.");
- do {
- // read the semaphore value
- old = __LDREXW(&owner);
- // loop again if it is locked and we are blocking
- // or setting it with strex failed
- } while ((old == current_task) || __STREXW(current_task, &owner) != 0);
+ pid_t old = pid_t(__LDREXW(&owner));
+
+ if (old == current_task) {
+ return;
+ }
+
+ if (__STREXW(uint32_t(current_task), &owner) == 0) {
+ return;
+ }
+
+ tasks[current_task].set_blocked();
+ sleepers.set(current_task);
+ reschedule();
}
+// void lock() {
+// uint32_t old;
+//
+// do {
+// // read the semaphore value
+// old = __LDREXW(&owner);
+// // loop again if it is locked and we are blocking
+// // or setting it with strex failed
+// } while ((old == current_task) || __STREXW(current_task, &owner) != 0);
+// }
+
void unlock() {
- owner = UINT32_MAX;
+ owner = UNLOCKED;
+ pid_t lucky_process = sleepers.find_first();
+
+ if (lucky_process >= 0) {
+// dbg_printf("marking %" PRIpid_t " as ready\n", lucky_process);
+ tasks[lucky_process].set_ready();
+ reschedule();
+ }
}
private:
@@ -384,6 +525,8 @@ volatile bool run1 = true;
os::Mutex mutex;
+volatile int shared_variable;
+
void job1(void const *const) {
GPIO_InitTypeDef init;
GPIO_StructInit(&init);
@@ -431,6 +574,8 @@ int main(void) {
RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOB, DISABLE);
+ shared_variable = 0;
+
os::os_init();
os::os_create_thread(job1);
os::os_create_thread(job2);
diff --git a/apps/os2/os2_cm3.s b/apps/os2/os2_cm3.s
index aa18703..f0eb4b7 100644
--- a/apps/os2/os2_cm3.s
+++ b/apps/os2/os2_cm3.s
@@ -32,11 +32,11 @@ do_first_context_switch:
msr control, r0
isb
- // Set CONTROL.nPRIV=1 so that we run with in unprivileged mode
- mrs r0, control
- orr r0, #1
- msr control, r0
- isb
+// // Set CONTROL.nPRIV=1 so that we run with in unprivileged mode
+// mrs r0, control
+// orr r0, #1
+// msr control, r0
+// isb
// Restore the data from hardware_frame_t.
pop {r0 - r3, r12, lr}
@@ -44,11 +44,11 @@ do_first_context_switch:
pop {r4, r5}
bx r4
-.thumb_func
-.global _ZN7trygvis3os22os10rescheduleEv
-_ZN7trygvis3os22os10rescheduleEv:
- svc #0
- bx lr
+// .thumb_func
+// .global _ZN7trygvis3os22os10rescheduleEv
+// _ZN7trygvis3os22os10rescheduleEv:
+// svc #0
+// bx lr
// A very simple idle task, just to know exactly which registers that are used and what their values are supposed to be.
// Toggles GPIO B, pin #6 on a STM32F103
diff --git a/cmake/stm32.toolchain.cmake b/cmake/stm32.toolchain.cmake
index e595ec9..8e380fe 100644
--- a/cmake/stm32.toolchain.cmake
+++ b/cmake/stm32.toolchain.cmake
@@ -22,12 +22,12 @@ set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_CROSSCOMPILING 1)
set(TARGET_FLAGS "-mcpu=cortex-m3 -mthumb")
-set(BASE_FLAGS "-O3 -ffreestanding -nostdlib -Wall -Wextra -g -ffunction-sections -fdata-sections ${TARGET_FLAGS}")
+set(BASE_FLAGS "-O3 -ffreestanding -Wall -Wextra -g -ffunction-sections -fdata-sections ${TARGET_FLAGS}")
set(CMAKE_C_FLAGS "${BASE_FLAGS}" CACHE STRING "c flags")
set(CMAKE_CXX_FLAGS "${BASE_FLAGS} -fno-exceptions -fno-rtti -felide-constructors -std=c++14" CACHE STRING "c++ flags")
-set(LINKER_FLAGS "-O3 -Wl,--gc-sections ${TARGET_FLAGS}")
+set(LINKER_FLAGS "-O3 -nostdlib -Wl,--gc-sections ${TARGET_FLAGS}")
#set(LINKER_LIBS "-larm_cortexM4l_math -lm")
set(CMAKE_EXE_LINKER_FLAGS "${LINKER_FLAGS}" CACHE STRING "linker flags" FORCE)
diff --git a/playground/include/playground.h b/playground/include/playground.h
index 3ccfeeb..168c5c5 100644
--- a/playground/include/playground.h
+++ b/playground/include/playground.h
@@ -13,6 +13,7 @@ int main();
extern "C"
void Default_Handler();
+// TODO: replace with std::extent<>; http://en.cppreference.com/w/cpp/types/extent
template<typename T, size_t N>
static inline constexpr
size_t SizeOfArray(const T(&)[N]) {
diff --git a/playground/src/init_high.cpp b/playground/src/init_high.cpp
index b877b7d..85e7d41 100644
--- a/playground/src/init_high.cpp
+++ b/playground/src/init_high.cpp
@@ -18,6 +18,13 @@ extern constructor_t _init_array_start[], _init_array_end[];
}
extern "C"
+__attribute__((noreturn))
+void __cxa_pure_virtual() {
+ while (1) {
+ }
+}
+
+extern "C"
void *memset(void *dst, int i, size_t n) {
if (n) {
char *d = (char *) dst;