88#include < rtos/rtos.hpp>
99
1010#include " task.hpp"
11+ #include " syscall.hpp"
1112
1213namespace klib ::rtos {
13- template <uint32_t CpuId, typename Irq>
1414 class scheduler {
15+ public:
16+ /* *
17+ * @brief All the available syscalls
18+ *
19+ */
20+ enum class syscalls {
21+ create_task = 0 ,
22+ delete_task = 1 ,
23+ yield = 2 ,
24+ sleep = 3 ,
25+ malloc = 4 ,
26+ free = 5
27+ };
28+
1529 protected:
1630 // make sure we can register our callback with the systick handler
1731 static_assert (SYSTICK_CALLBACK_ENABLED, " Systick callback needs to be enabled to switch tasks" );
@@ -46,6 +60,13 @@ namespace klib::rtos {
4660 *
4761 */
4862 static void schedule_irq () {
63+ // check if we have any tasks when we are called
64+ // by the systick interrupt
65+ if (tasks.size () == 0 ) {
66+ // no tasks to schedule
67+ return ;
68+ }
69+
4970 // TODO: implement tick count for tasks
5071 // decrement sleep time for all tasks
5172 for (size_t i = 0 ; i < tasks.size (); i++) {
@@ -54,18 +75,6 @@ namespace klib::rtos {
5475 }
5576 }
5677
57- // remove tasks that are marked for deletion. We move backwards
58- // to not mess up the indexing when erasing. Note this is something
59- // that is specific to our dynamic array implementation.
60- // TODO: update this when changing away from the dynamic array
61- if (tasks.size () > 0 ) {
62- for (auto it = tasks.end () - 1 ; it != tasks.begin (); it--) {
63- if ((*it)->marked_for_deletion ) {
64- tasks.erase (it);
65- }
66- }
67- }
68-
6978 // call the scheduler to pick the next task
7079 schedule ();
7180 }
@@ -76,14 +85,23 @@ namespace klib::rtos {
7685 *
7786 */
7887 static void schedule () {
79- if (!tasks.size ()) {
80- return ;
81- }
88+ // schedule the next task. We use the priority to pick
89+ // the next task to run. We always pick the first task
90+ // with the highest priority that is not sleeping. If no
91+ // task is ready we run the idle task.
92+ next_task = (current_task == nullptr ? &idle_task : (
93+ current_task->time_to_sleep .value == 0 ? current_task : &idle_task
94+ ));
8295
8396 // TODO: implement a better scheduling algorithm
84- // check if we have a task that is not sleeping
8597 for (size_t i = 0 ; i < tasks.size (); i++) {
86- if (tasks[i]->time_to_sleep .value == 0 ) {
98+ if (tasks[i]->time_to_sleep .value > 0 ) {
99+ // task is sleeping
100+ continue ;
101+ }
102+
103+ // task is not sleeping, check priority
104+ if (tasks[i]->current_priority > next_task->current_priority ) {
87105 next_task = tasks[i];
88106 break ;
89107 }
@@ -112,26 +130,22 @@ namespace klib::rtos {
112130 * @brief Start the scheduler
113131 *
114132 */
133+ template <uint32_t CpuId, typename Irq>
115134 static void start () {
116135 // update the callback to our scheduler
117136 io::systick<CpuId, SYSTICK_CALLBACK_ENABLED>::set_callback (schedule_irq);
118137
119138 // register our interrupt to the pendsv interrupt
120- // Irq::template register_irq<Irq::arm_vector::pendsv>(pendsv_handler);
121139 Irq::template register_irq<Irq::arm_vector::pendsv>(pendsv);
122140
141+ // register the syscall handler
142+ Irq::template register_irq<Irq::arm_vector::svcall>(klib::target::rtos::detail::sycall_handler<scheduler>);
143+
123144 // add the idle task to the scheduler. This task is always ready to run
124145 create_task (&idle_task);
125146
126147 // start the scheduler from the target
127148 klib::target::rtos::detail::scheduler_start ();
128-
129- // endless loop to wait on the start of the scheduler
130- while (true ) {
131- // wait for a interrupt. The systick callback
132- // should schedule what task to run
133- asm volatile (" wfi" );
134- }
135149 }
136150
137151 /* *
@@ -144,25 +158,70 @@ namespace klib::rtos {
144158 // add the task to the queue
145159 tasks.push_back (task);
146160 }
147-
161+
162+ public:
148163 /* *
149- * @brief Sleep the current task for the given time
164+ * @brief Syscall handler, called when a syscall is invoked
165+ * by the target implementation. Parameters need to be passed
166+ * in registers r1-r3. These are converted to the correct
167+ * types based on the syscall.
150168 *
169+ * @param number
170+ * @param arg0
171+ * @param arg1
172+ * @param arg2
173+ * @return uint32_t
151174 */
152- static void sleep (klib::time::ms time) {
153- // TODO: change this to use syscalls. This should stop
154- // the current task from running and switch to another task
155- // if available. As we are in unprivileged mode we cannot
156- // trigger a context switch ourselves.
157-
158- // set the time to sleep for the current task
159- current_task->time_to_sleep .value = time.value ;
160-
161- // wait until the time to sleep is over
162- while (current_task->time_to_sleep .value > 0 ) {
163- // wait for the systick to wake us up
164- asm volatile (" wfi" );
175+ static uint32_t syscall_handler (syscalls number, uint32_t arg0, uint32_t arg1, uint32_t arg2) {
176+ switch (number) {
177+ case syscalls::create_task:
178+ // add the task to the scheduler
179+ tasks.push_back (reinterpret_cast <detail::base_task*>(arg0));
180+ break ;
181+
182+ case syscalls::delete_task:
183+ // find and remove the task from the scheduler. We move backwards
184+ // to not mess up the indexing when erasing. Note this is something
185+ // that is specific to our dynamic array implementation.
186+ // TODO: update this when changing away from the dynamic array
187+ for (size_t i = 0 ; i < tasks.size (); i++) {
188+ if (tasks[i] == reinterpret_cast <detail::base_task*>(arg0)) {
189+ // erase the provided task
190+ tasks.erase (tasks.begin () + i);
191+
192+ // check if we are running the task we are deleting
193+ if (current_task == reinterpret_cast <detail::base_task*>(arg0)) {
194+ // clear the current task and switch to the
195+ // next one
196+ current_task = nullptr ;
197+
198+ // switch to the next task
199+ schedule ();
200+ }
201+
202+ // task found and deleted
203+ return true ;
204+ }
205+ }
206+
207+ // task not found
208+ return false ;
209+
210+ case syscalls::yield:
211+ // yield the cpu to the next task
212+ schedule ();
213+ break ;
214+
215+ case syscalls::sleep:
216+ // set the time to sleep for the current task
217+ current_task->time_to_sleep .value = arg0;
218+
219+ // switch to the next task
220+ schedule ();
221+ break ;
165222 }
223+
224+ return true ;
166225 }
167226 };
168227}
0 commit comments