Skip to content
This repository has been archived by the owner on Jul 6, 2019. It is now read-only.

Refactoring scheduler to PT #131

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/zinc/hal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ pub mod spi;
pub mod stack;
pub mod timer;
pub mod uart;
pub mod systick;
28 changes: 28 additions & 0 deletions src/zinc/hal/systick.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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);
}
2 changes: 2 additions & 0 deletions src/zinc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/zinc/os/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
20 changes: 20 additions & 0 deletions src/zinc/os/sched/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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;
218 changes: 218 additions & 0 deletions src/zinc/os/sched/scheduler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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)));
}
)
}
26 changes: 26 additions & 0 deletions src/zinc/os/sched/stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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);
}
50 changes: 50 additions & 0 deletions src/zinc/os/sched/task.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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,
}