From 45792535dc2c4351c56d14d92aece1536d0da925 Mon Sep 17 00:00:00 2001 From: Vladimir Pouzanov Date: Wed, 23 Jul 2014 08:56:19 +0100 Subject: [PATCH 1/5] First chunk of work on a new scheduler --- src/hal/systick.rs | 26 +++++ src/os/sched/mod.rs | 20 ++++ src/os/sched/scheduler.rs | 206 ++++++++++++++++++++++++++++++++++++++ src/os/sched/stack.rs | 26 +++++ src/os/sched/task.rs | 34 +++++++ src/zinc/hal/mod.rs | 1 + src/zinc/lib.rs | 2 + src/zinc/os/mod.rs | 2 +- 8 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 src/hal/systick.rs create mode 100644 src/os/sched/mod.rs create mode 100644 src/os/sched/scheduler.rs create mode 100644 src/os/sched/stack.rs create mode 100644 src/os/sched/task.rs diff --git a/src/hal/systick.rs b/src/hal/systick.rs new file mode 100644 index 00000000..a8c17f8e --- /dev/null +++ b/src/hal/systick.rs @@ -0,0 +1,26 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Vladimir "farcaller" Pouzanov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! +Interface to Systick timer. + +This might be merged into generic timer interface. Systick is a bit specific in +terms of interrupts, but PT should make the difference negligible. +*/ + +pub trait Systick { + /// Starts the systick timer. + fn start(&self); +} diff --git a/src/os/sched/mod.rs b/src/os/sched/mod.rs new file mode 100644 index 00000000..365bad08 --- /dev/null +++ b/src/os/sched/mod.rs @@ -0,0 +1,20 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Vladimir "farcaller" Pouzanov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Tasks scheduling and management. + +pub mod scheduler; +pub mod stack; +pub mod task; diff --git a/src/os/sched/scheduler.rs b/src/os/sched/scheduler.rs new file mode 100644 index 00000000..0b39641d --- /dev/null +++ b/src/os/sched/scheduler.rs @@ -0,0 +1,206 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Vladimir "farcaller" Pouzanov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/*! +Basic round-robin scheduler. + +TODO(farcaller): it's not round-robin, actually. A stricter time slice + accounting must be done. +*/ + +use core::collections::Collection; + +use super::task; +use super::stack::StackManager; +use hal::systick::Systick; + +/// Scheduler interface. +pub struct Scheduler<'a, T, S> { + index: task::TasksIndex<'a>, + context_switch: ||:'a, + systick: &'a T, + stack_manager: &'a S, +} + +impl<'a, T: Systick, S: StackManager> Scheduler<'a, T, S> { + /// Creates a new scheduler given a list of tasks, systick timer and + /// management routines. + /// + /// At least one task must be defined in task index. + pub fn new(ti: task::TasksIndex<'a>, systick: &'a T, + stack_manager: &'a S, ctx_switch: ||:'a) + -> Scheduler<'a, T, S> { + Scheduler { + index: ti, + context_switch: ctx_switch, + systick: systick, + stack_manager: stack_manager, + } + } + + /// Starts a scheduler and switches to first task. Never returns. + pub fn start(&mut self) { + self.stack_manager.set_task_stack_pointer(self.index.tasks[0].stack_start); + self.systick.start(); + (self.context_switch)(); + } + + /// Switches to next task. + /// + /// Intended to be run by systick ISR, not invoked directly. + pub fn switch(&mut self) { + self.index.tasks[self.index.current_task_index as uint].stack_start = + self.stack_manager.get_task_stack_pointer(); + + self.index.current_task_index += 1; + if (self.index.current_task_index as uint) == self.index.tasks.len() { + self.index.current_task_index = 0; + } + + self.stack_manager.set_task_stack_pointer( + self.index.tasks[self.index.current_task_index as uint].stack_start); + } + + fn current_task_index(&self) -> u8 { + self.index.current_task_index + } + + fn index(&self) -> &task::TasksIndex { + &self.index + } +} + +#[cfg(test)] +mod test { + use hamcrest::{assert_that, is, equal_to}; + use std::cell::Cell; + use std::kinds::marker; + + use hal::systick::Systick; + use os::sched::stack::StackManager; + use os::sched::task; + use super::Scheduler; + + struct FakeSystick { + pub started: Cell + } + + impl FakeSystick { + pub fn new() -> FakeSystick { FakeSystick { started: Cell::new(false) } } + } + impl Systick for FakeSystick { + fn start(&self) { self.started.set(true); } + } + + struct FakeStackManager { + pub sp: Cell + } + impl FakeStackManager { + pub fn new() -> FakeStackManager { FakeStackManager { sp: Cell::new(0) } } + } + impl StackManager for FakeStackManager { + fn get_task_stack_pointer(&self) -> u32 { + self.sp.get() + } + fn set_task_stack_pointer(&self, sp: u32) { + self.sp.set(sp); + } + } + + describe!( + before_each { + let tick = FakeSystick::new(); + let mut tasks = [task::Task { + state: task::Runnable, + stack_start: 100, + stack_end: 200, + }, + task::Task { + state: task::Runnable, + stack_start: 200, + stack_end: 300, + }]; + let ti = task::TasksIndex { + tasks: tasks, + current_task_index: 0, + no_copy: marker::NoCopy, + }; + let fsm = FakeStackManager::new(); + } + + it "calls a context switch with first task" { + let mut called = false; + + { + let mut scheduler = Scheduler::new(ti, &tick, &fsm, || { called = true }); + scheduler.start(); + } + + assert_that(called, is(equal_to(true))); + } + + it "schedules second task on timer interrupt" { + let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + scheduler.start(); + + scheduler.switch(); + + assert_that(scheduler.current_task_index(), is(equal_to(1u8))); + } + + it "wraps over to first task when all tasks are done" { + let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + scheduler.start(); + + scheduler.switch(); + scheduler.switch(); + + assert_that(scheduler.current_task_index(), is(equal_to(0u8))); + } + + it "enables systick timer on start" { + let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + scheduler.start(); + + assert_that(tick.started.get(), is(equal_to(true))); + } + + it "loads first task stack pointer" { + let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + scheduler.start(); + + assert_that(fsm.sp.get(), is(equal_to(100u32))); + } + + it "saves stack pointer to current task on switch" { + let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + scheduler.start(); + + fsm.sp.set(110); + scheduler.switch(); + + assert_that(scheduler.index().tasks[0].stack_start, is(equal_to(110u32))); + } + + it "loads stack pointer to next task on switch" { + let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + scheduler.start(); + + scheduler.switch(); + + assert_that(fsm.sp.get(), is(equal_to(200u32))); + } + ) +} diff --git a/src/os/sched/stack.rs b/src/os/sched/stack.rs new file mode 100644 index 00000000..2d2f1047 --- /dev/null +++ b/src/os/sched/stack.rs @@ -0,0 +1,26 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Vladimir "farcaller" Pouzanov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Tasks stack management. + +/// StackManager provides scheduler with interface to manage task-specific stack +/// pointer. +pub trait StackManager { + /// Returns stack pointer for currently scheduled task. + fn get_task_stack_pointer(&self) -> u32; + + /// Sets stack pointer for currently scheduled task. + fn set_task_stack_pointer(&self, sp: u32); +} diff --git a/src/os/sched/task.rs b/src/os/sched/task.rs new file mode 100644 index 00000000..6a4409e7 --- /dev/null +++ b/src/os/sched/task.rs @@ -0,0 +1,34 @@ +// Zinc, the bare metal stack for rust. +// Copyright 2014 Vladimir "farcaller" Pouzanov +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::kinds::marker; + +pub enum State { + Runnable +} + +pub struct Task { + pub state: State, + pub stack_start: u32, + pub stack_end: u32, +} + +#[packed] +pub struct TasksIndex<'a> { + pub tasks: &'a mut [Task], + pub current_task_index: u8, + + pub no_copy: marker::NoCopy, +} diff --git a/src/zinc/hal/mod.rs b/src/zinc/hal/mod.rs index 00802c46..28234425 100644 --- a/src/zinc/hal/mod.rs +++ b/src/zinc/hal/mod.rs @@ -37,3 +37,4 @@ pub mod spi; pub mod stack; pub mod timer; pub mod uart; +pub mod systick; diff --git a/src/zinc/lib.rs b/src/zinc/lib.rs index 51ab12b0..48ff0652 100644 --- a/src/zinc/lib.rs +++ b/src/zinc/lib.rs @@ -48,7 +48,9 @@ extern crate core; extern crate rlibc; #[cfg(test)] #[phase(plugin,link)] extern crate std; +#[cfg(test)] #[phase(plugin,link)] extern crate shiny; #[cfg(test)] extern crate native; +#[cfg(test)] extern crate hamcrest; #[phase(plugin)] extern crate macro_ioreg; pub mod drivers; diff --git a/src/zinc/os/mod.rs b/src/zinc/os/mod.rs index e4ae9bb8..660a50d1 100644 --- a/src/zinc/os/mod.rs +++ b/src/zinc/os/mod.rs @@ -20,9 +20,9 @@ This module is a higher level abstraction over hardware than hal. It might be incompatible direct hal usage in some cases. */ -// pub mod debug; pub mod syscall; #[cfg(cfg_multitasking)] pub mod task; pub mod mutex; pub mod cond_var; pub mod debug; +pub mod sched; From 5dadda6ae0d803721f8df40b634dcefcb8818014 Mon Sep 17 00:00:00 2001 From: Vladimir Pouzanov Date: Wed, 23 Jul 2014 19:09:41 +0100 Subject: [PATCH 2/5] Added docs to os::sched::task --- src/os/sched/task.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/os/sched/task.rs b/src/os/sched/task.rs index 6a4409e7..7b896200 100644 --- a/src/os/sched/task.rs +++ b/src/os/sched/task.rs @@ -13,21 +13,36 @@ // See the License for the specific language governing permissions and // limitations under the License. +/// Task structures. + use core::kinds::marker; +/// Task states. pub enum State { + /// This task can be scheduled. Runnable } +/// Task descriptor. Should be treated as opaque struct. pub struct Task { + /// Current task state. pub state: State, + + /// Pointer to top of the stack. pub stack_start: u32, + + /// Pointer to the lowest possible stack address. pub stack_end: u32, } +/// Tasks index provides a scheduler with a list of all registered tasks and +/// initial state. #[packed] pub struct TasksIndex<'a> { + /// A mutabler slice with all defined tasks. pub tasks: &'a mut [Task], + + /// Current running task index. pub current_task_index: u8, pub no_copy: marker::NoCopy, From 7baec6d2bf9b0b8b26251bee4bcbb4bbcd744880 Mon Sep 17 00:00:00 2001 From: Vladimir Pouzanov Date: Wed, 23 Jul 2014 19:17:25 +0100 Subject: [PATCH 3/5] Scheduler now moves in Systick and StackManager --- src/os/sched/scheduler.rs | 66 +++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/os/sched/scheduler.rs b/src/os/sched/scheduler.rs index 0b39641d..a17c87fe 100644 --- a/src/os/sched/scheduler.rs +++ b/src/os/sched/scheduler.rs @@ -30,8 +30,8 @@ use hal::systick::Systick; pub struct Scheduler<'a, T, S> { index: task::TasksIndex<'a>, context_switch: ||:'a, - systick: &'a T, - stack_manager: &'a S, + systick: T, + stack_manager: S, } impl<'a, T: Systick, S: StackManager> Scheduler<'a, T, S> { @@ -39,9 +39,8 @@ impl<'a, T: Systick, S: StackManager> Scheduler<'a, T, S> { /// management routines. /// /// At least one task must be defined in task index. - pub fn new(ti: task::TasksIndex<'a>, systick: &'a T, - stack_manager: &'a S, ctx_switch: ||:'a) - -> Scheduler<'a, T, S> { + pub fn new(ti: task::TasksIndex<'a>, systick: T, stack_manager: S, + ctx_switch: ||:'a) -> Scheduler<'a, T, S> { Scheduler { index: ti, context_switch: ctx_switch, @@ -52,7 +51,8 @@ impl<'a, T: Systick, S: StackManager> Scheduler<'a, T, S> { /// Starts a scheduler and switches to first task. Never returns. pub fn start(&mut self) { - self.stack_manager.set_task_stack_pointer(self.index.tasks[0].stack_start); + self.stack_manager.set_task_stack_pointer( + self.index.tasks[self.index.current_task_index as uint].stack_start); self.systick.start(); (self.context_switch)(); } @@ -85,7 +85,6 @@ impl<'a, T: Systick, S: StackManager> Scheduler<'a, T, S> { #[cfg(test)] mod test { use hamcrest::{assert_that, is, equal_to}; - use std::cell::Cell; use std::kinds::marker; use hal::systick::Systick; @@ -94,34 +93,45 @@ mod test { use super::Scheduler; struct FakeSystick { - pub started: Cell + started_ptr: *mut bool } impl FakeSystick { - pub fn new() -> FakeSystick { FakeSystick { started: Cell::new(false) } } + pub fn new(started: &mut bool) -> FakeSystick { + FakeSystick { + started_ptr: started as *mut bool + } + } } impl Systick for FakeSystick { - fn start(&self) { self.started.set(true); } + fn start(&self) { + unsafe { *self.started_ptr = true; } + } } struct FakeStackManager { - pub sp: Cell + pub sp_ptr: *mut u32 } impl FakeStackManager { - pub fn new() -> FakeStackManager { FakeStackManager { sp: Cell::new(0) } } + pub fn new(sp: &mut u32) -> FakeStackManager { + FakeStackManager { + sp_ptr: sp as *mut u32 + } + } } impl StackManager for FakeStackManager { fn get_task_stack_pointer(&self) -> u32 { - self.sp.get() + unsafe { *self.sp_ptr } } fn set_task_stack_pointer(&self, sp: u32) { - self.sp.set(sp); + unsafe { *self.sp_ptr = sp; } } } describe!( before_each { - let tick = FakeSystick::new(); + let mut systick_started = false; + let tick = FakeSystick::new(&mut systick_started); let mut tasks = [task::Task { state: task::Runnable, stack_start: 100, @@ -137,14 +147,15 @@ mod test { current_task_index: 0, no_copy: marker::NoCopy, }; - let fsm = FakeStackManager::new(); + let mut sp = 0u32; + let fsm = FakeStackManager::new(&mut sp); } it "calls a context switch with first task" { let mut called = false; { - let mut scheduler = Scheduler::new(ti, &tick, &fsm, || { called = true }); + let mut scheduler = Scheduler::new(ti, tick, fsm, || { called = true }); scheduler.start(); } @@ -152,7 +163,7 @@ mod test { } it "schedules second task on timer interrupt" { - let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + let mut scheduler = Scheduler::new(ti, tick, fsm, || {}); scheduler.start(); scheduler.switch(); @@ -161,7 +172,7 @@ mod test { } it "wraps over to first task when all tasks are done" { - let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + let mut scheduler = Scheduler::new(ti, tick, fsm, || {}); scheduler.start(); scheduler.switch(); @@ -171,36 +182,37 @@ mod test { } it "enables systick timer on start" { - let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + let mut scheduler = Scheduler::new(ti, tick, fsm, || {}); scheduler.start(); - assert_that(tick.started.get(), is(equal_to(true))); + assert_that(systick_started, is(equal_to(true))); } it "loads first task stack pointer" { - let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + let mut scheduler = Scheduler::new(ti, tick, fsm, || {}); scheduler.start(); - assert_that(fsm.sp.get(), is(equal_to(100u32))); + assert_that(sp, is(equal_to(100u32))); } it "saves stack pointer to current task on switch" { - let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + let mut scheduler = Scheduler::new(ti, tick, fsm, || {}); scheduler.start(); - fsm.sp.set(110); + sp = 110; scheduler.switch(); assert_that(scheduler.index().tasks[0].stack_start, is(equal_to(110u32))); + assert_that(sp, is(equal_to(200u32))); } it "loads stack pointer to next task on switch" { - let mut scheduler = Scheduler::new(ti, &tick, &fsm, || {}); + let mut scheduler = Scheduler::new(ti, tick, fsm, || {}); scheduler.start(); scheduler.switch(); - assert_that(fsm.sp.get(), is(equal_to(200u32))); + assert_that(sp, is(equal_to(200u32))); } ) } From e7b1bd84c2cc17ae5fc5e335ead6b8d2d84903b8 Mon Sep 17 00:00:00 2001 From: Vladimir Pouzanov Date: Sun, 31 Aug 2014 11:09:53 +0100 Subject: [PATCH 4/5] Moved source following the rebase --- src/{ => zinc}/hal/systick.rs | 0 src/{ => zinc}/os/sched/mod.rs | 0 src/{ => zinc}/os/sched/scheduler.rs | 0 src/{ => zinc}/os/sched/stack.rs | 0 src/{ => zinc}/os/sched/task.rs | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename src/{ => zinc}/hal/systick.rs (100%) rename src/{ => zinc}/os/sched/mod.rs (100%) rename src/{ => zinc}/os/sched/scheduler.rs (100%) rename src/{ => zinc}/os/sched/stack.rs (100%) rename src/{ => zinc}/os/sched/task.rs (100%) diff --git a/src/hal/systick.rs b/src/zinc/hal/systick.rs similarity index 100% rename from src/hal/systick.rs rename to src/zinc/hal/systick.rs diff --git a/src/os/sched/mod.rs b/src/zinc/os/sched/mod.rs similarity index 100% rename from src/os/sched/mod.rs rename to src/zinc/os/sched/mod.rs diff --git a/src/os/sched/scheduler.rs b/src/zinc/os/sched/scheduler.rs similarity index 100% rename from src/os/sched/scheduler.rs rename to src/zinc/os/sched/scheduler.rs diff --git a/src/os/sched/stack.rs b/src/zinc/os/sched/stack.rs similarity index 100% rename from src/os/sched/stack.rs rename to src/zinc/os/sched/stack.rs diff --git a/src/os/sched/task.rs b/src/zinc/os/sched/task.rs similarity index 100% rename from src/os/sched/task.rs rename to src/zinc/os/sched/task.rs From 2d35f629827d09f8dc825fcd354c1f740936d95a Mon Sep 17 00:00:00 2001 From: Vladimir Pouzanov Date: Sun, 31 Aug 2014 16:29:22 +0100 Subject: [PATCH 5/5] Fixed a few comments to make it compilable again --- src/zinc/hal/systick.rs | 2 ++ src/zinc/os/sched/mod.rs | 2 +- src/zinc/os/sched/stack.rs | 2 +- src/zinc/os/sched/task.rs | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/zinc/hal/systick.rs b/src/zinc/hal/systick.rs index a8c17f8e..035cc7bc 100644 --- a/src/zinc/hal/systick.rs +++ b/src/zinc/hal/systick.rs @@ -20,6 +20,8 @@ This might be merged into generic timer interface. Systick is a bit specific in terms of interrupts, but PT should make the difference negligible. */ +/// A simple systick interface. +#[experimental] pub trait Systick { /// Starts the systick timer. fn start(&self); diff --git a/src/zinc/os/sched/mod.rs b/src/zinc/os/sched/mod.rs index 365bad08..d3e39a0b 100644 --- a/src/zinc/os/sched/mod.rs +++ b/src/zinc/os/sched/mod.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Tasks scheduling and management. +//! Tasks scheduling and management. pub mod scheduler; pub mod stack; diff --git a/src/zinc/os/sched/stack.rs b/src/zinc/os/sched/stack.rs index 2d2f1047..327f71ab 100644 --- a/src/zinc/os/sched/stack.rs +++ b/src/zinc/os/sched/stack.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Tasks stack management. +//! Tasks stack management. /// StackManager provides scheduler with interface to manage task-specific stack /// pointer. diff --git a/src/zinc/os/sched/task.rs b/src/zinc/os/sched/task.rs index 7b896200..ce91a63b 100644 --- a/src/zinc/os/sched/task.rs +++ b/src/zinc/os/sched/task.rs @@ -13,7 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Task structures. +//! Task structures. use core::kinds::marker; @@ -45,5 +45,6 @@ pub struct TasksIndex<'a> { /// Current running task index. pub current_task_index: u8, + /// Tasks are not copyable. pub no_copy: marker::NoCopy, }