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/hal/systick.rs b/src/zinc/hal/systick.rs new file mode 100644 index 00000000..035cc7bc --- /dev/null +++ b/src/zinc/hal/systick.rs @@ -0,0 +1,28 @@ +// 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. +*/ + +/// A simple systick interface. +#[experimental] +pub trait Systick { + /// Starts the systick timer. + fn start(&self); +} 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; diff --git a/src/zinc/os/sched/mod.rs b/src/zinc/os/sched/mod.rs new file mode 100644 index 00000000..d3e39a0b --- /dev/null +++ b/src/zinc/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/zinc/os/sched/scheduler.rs b/src/zinc/os/sched/scheduler.rs new file mode 100644 index 00000000..a17c87fe --- /dev/null +++ b/src/zinc/os/sched/scheduler.rs @@ -0,0 +1,218 @@ +// 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: T, + stack_manager: 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: T, stack_manager: 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[self.index.current_task_index as uint].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::kinds::marker; + + use hal::systick::Systick; + use os::sched::stack::StackManager; + use os::sched::task; + use super::Scheduler; + + struct FakeSystick { + started_ptr: *mut bool + } + + impl FakeSystick { + pub fn new(started: &mut bool) -> FakeSystick { + FakeSystick { + started_ptr: started as *mut bool + } + } + } + impl Systick for FakeSystick { + fn start(&self) { + unsafe { *self.started_ptr = true; } + } + } + + struct FakeStackManager { + pub sp_ptr: *mut u32 + } + impl FakeStackManager { + 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 { + unsafe { *self.sp_ptr } + } + fn set_task_stack_pointer(&self, sp: u32) { + unsafe { *self.sp_ptr = sp; } + } + } + + describe!( + before_each { + let mut systick_started = false; + let tick = FakeSystick::new(&mut systick_started); + 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 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 }); + 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(systick_started, is(equal_to(true))); + } + + it "loads first task stack pointer" { + let mut scheduler = Scheduler::new(ti, tick, fsm, || {}); + scheduler.start(); + + assert_that(sp, is(equal_to(100u32))); + } + + it "saves stack pointer to current task on switch" { + let mut scheduler = Scheduler::new(ti, tick, fsm, || {}); + scheduler.start(); + + 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, || {}); + scheduler.start(); + + scheduler.switch(); + + assert_that(sp, is(equal_to(200u32))); + } + ) +} diff --git a/src/zinc/os/sched/stack.rs b/src/zinc/os/sched/stack.rs new file mode 100644 index 00000000..327f71ab --- /dev/null +++ b/src/zinc/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/zinc/os/sched/task.rs b/src/zinc/os/sched/task.rs new file mode 100644 index 00000000..ce91a63b --- /dev/null +++ b/src/zinc/os/sched/task.rs @@ -0,0 +1,50 @@ +// 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. + +//! 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, + + /// Tasks are not copyable. + pub no_copy: marker::NoCopy, +}