Skip to content

Commit

Permalink
Refactor bootstrap sequence.
Browse files Browse the repository at this point in the history
  • Loading branch information
zyma98 committed Aug 16, 2024
1 parent 7eae4cc commit 5a8438f
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 72 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ tag = "v0.1.3"

[dependencies.hopter_conf_params]
git = "https://github.com/ZhiyaoMa98/hopter-conf-params.git"
tag = "v0.1.5"
tag = "v0.1.7"

[dependencies.cortex-m]
version = "0.7"
Expand Down
97 changes: 59 additions & 38 deletions src/boot/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,78 @@ use alloc::boxed::Box;
use core::sync::atomic::AtomicPtr;
use cortex_m::peripheral::scb::SystemHandler;

#[no_mangle]
pub extern "C" fn entry() -> ! {
pub(super) extern "C" fn system_start() -> ! {
allocator::init_allocator();

let mut cp = cortex_m::Peripherals::take().unwrap();

// Configure system call and context switch exception priority.
unsafe {
cp.SCB.set_priority(
cortex_m::peripheral::scb::SystemHandler::SVCall,
config::SVC_RAISED_PRIORITY,
);
cp.SCB.set_priority(
cortex_m::peripheral::scb::SystemHandler::PendSV,
config::PENDSV_PRIORITY,
);

cp.SCB
.set_priority(SystemHandler::SysTick, config::SYSTICK_PRIORITY);

// Circumvent compiler bug.
// use cortex_m::peripheral::syst::SystClkSource;
// cp.SYST.set_clock_source(SystClkSource::Core);
let val = cp.SYST.csr.read();
let val = val | (1 << 2);
cp.SYST.csr.write(val);

cp.SYST.set_reload(168_000);
cp.SYST.clear_current();
cp.SYST.enable_counter();
cp.SYST.enable_interrupt();

cortex_m::register::basepri::write(config::IRQ_DISABLE_BASEPRI_PRIORITY);
.set_priority(SystemHandler::SVCall, config::SVC_NORMAL_PRIORITY);
cp.SCB
.set_priority(SystemHandler::PendSV, config::PENDSV_PRIORITY);
cortex_m::register::basepri::write(config::IRQ_ENABLE_BASEPRI_PRIORITY);
}

cp.SCB.enable_fpu();

let boxed_cp = Box::new(cp);
let raw_cp = AtomicPtr::new(Box::into_raw(boxed_cp) as *mut u8);
// Spawn the main task. The task will not be executed until we start the
// scheduler.
task::build()
.set_id(config::MAIN_TASK_ID)
.set_entry(move || main_task(cp))
.set_stack_size(config::MAIN_TASK_INITIAL_STACK_SIZE)
.set_priority(config::MAIN_TASK_PRIORITY)
.spawn()
.unwrap_or_die();

extern "C" {
fn __main_trampoline(arg: AtomicPtr<u8>);
unsafe {
scheduler::start();
}
}

fn enable_systick(cp: &mut cortex_m::Peripherals) {
// Manually set the config register to circumvent compiler bug, otherwise
// there will be a compile error related to debug info generation.
// The code is equivalenet to the following:
// ```
// use cortex_m::peripheral::syst::SystClkSource;
// cp.SYST.set_clock_source(SystClkSource::Core);
// ```
let val = cp.SYST.csr.read();
let val = val | (1 << 2);
unsafe {
task::build()
.set_id(1)
.set_entry(move || __main_trampoline(raw_cp))
.set_stack_size(config::MAIN_TASK_INITIAL_STACK_SIZE)
.set_priority(config::MAIN_TASK_PRIORITY)
.spawn()
.unwrap_or_die();
scheduler::start();
cp.SYST.csr.write(val);
}

// Assume that the clock is 168 MHz. Trigger an interrupt for every 1
// millisecond.
cp.SYST.set_reload(168_000);
cp.SYST.clear_current();
cp.SYST.enable_counter();

// Enable interrupt.
unsafe {
cp.SCB
.set_priority(SystemHandler::SysTick, config::SYSTICK_PRIORITY);
}
cp.SYST.enable_interrupt();
}

extern "C" {
/// A glue function that calls to the user defined main function with
/// the [`#[main]`](super::main) attribute.
fn __main_trampoline(arg: AtomicPtr<u8>);
}

/// The first non-idle task run by the scheduler. It enables SysTick and then
/// calls the user defined main function with the [`#[main]`](super::main)
/// attribute.
fn main_task(mut cp: cortex_m::Peripherals) {
enable_systick(&mut cp);

let boxed_cp = Box::new(cp);
let raw_cp = AtomicPtr::new(Box::into_raw(boxed_cp) as *mut u8);
unsafe { __main_trampoline(raw_cp) }
}
4 changes: 2 additions & 2 deletions src/boot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ unsafe extern "C" fn Reset() -> ! {
"ldr r1, ={deferred_unwind}",
"str r1, [r0, #12]",
"mov lr, #0",
"b {entry}",
"b {system_start}",
sbss = sym __sbss,
ebss = sym __ebss,
sdata = sym __sdata,
Expand All @@ -127,7 +127,7 @@ unsafe extern "C" fn Reset() -> ! {
memclr = sym memclr,
memcpy = sym memcpy,
memset = sym memset,
entry = sym entry::entry,
system_start = sym entry::system_start,
options(noreturn)
);
}
Expand Down
5 changes: 5 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,11 @@ const_assert!(UNWIND_PRIORITY < IDLE_TASK_PRIORITY);
pub use hopter_conf_params::IDLE_TASK_ID;
assert_value_type!(IDLE_TASK_ID, u8);

/// The ID for the idle task. A task ID does not have functional purpose. It
/// might be helpful for diagnosing bugs.
pub use hopter_conf_params::MAIN_TASK_ID;
assert_value_type!(MAIN_TASK_ID, u8);

/// The ID for a task when the ID is not explicitly set during task creation.
/// Tasks can have the same ID.
pub use hopter_conf_params::DEFAULT_TASK_ID;
Expand Down
19 changes: 7 additions & 12 deletions src/schedule/idle.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::{
config,
interrupt::svc,
sync::{SpinSchedSafe, SpinSchedSafeGuard},
};
Expand All @@ -26,17 +25,13 @@ pub(super) fn lock_idle_callbacks() -> SpinSchedSafeGuard<'static, Vec<Arc<dyn I
/// The idle task. Just endlessly yield itself so that whenever a task becomes
/// ready, that task will be chosen by the scheduler to run.
pub(super) unsafe extern "C" fn idle_task() -> ! {
super::scheduler::set_started();
// The idle task is always executed first when the scheduler is just
// started. A main task should always be present awaiting to run. Perform
// a context switch to let the main task run.
svc::svc_yield_current_task();

// Enable interrupt.
unsafe {
cortex_m::register::basepri::write(config::IRQ_ENABLE_BASEPRI_PRIORITY);
cortex_m::Peripherals::steal().SCB.set_priority(
cortex_m::peripheral::scb::SystemHandler::SVCall,
config::SVC_NORMAL_PRIORITY,
);
// If nothing to do, enter low power state.
loop {
cortex_m::asm::wfe();
}

svc::svc_yield_current_task();
loop {}
}
35 changes: 19 additions & 16 deletions src/schedule/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ static CUR_TASK_IDLE: AtomicBool = AtomicBool::new(false);
/// assembly instruction sequence in [`crate::interrupt::context_switch`].
pub(crate) fn pick_next() {
// Sanity check that the scheduler is not being suspended.
if SCHEDULER_SUSPEND_CNT.load(Ordering::SeqCst) > 0 {
if SUSPEND_CNT.load(Ordering::SeqCst) > 0 {
unrecoverable::die();
}

Expand Down Expand Up @@ -197,6 +197,8 @@ pub(crate) unsafe fn start() -> ! {

CUR_TASK_IDLE.store(true, Ordering::SeqCst);

STARTED.store(true, Ordering::SeqCst);

unsafe {
// Run the idle task.
asm!(
Expand All @@ -223,7 +225,8 @@ pub(crate) unsafe fn start() -> ! {
// registers. Just enabling FPU is NOT enough for the CPU to push
// floating point registers upon exception.
"vmov.f32 s0, s0",
// Jump to the idle function.
// With the stack pointer and boundary updated, now the code runs
// in the idle task's context. Jump to the idle task entry.
"b {idle_task}",
idle_task = sym idle::idle_task,
tls_mem_addr = const config::TLS_MEM_ADDR,
Expand Down Expand Up @@ -252,24 +255,26 @@ pub(crate) fn destroy_current_task_and_schedule() {
cortex_m::peripheral::SCB::set_pendsv()
}

static SCHEDULER_STARTED: AtomicBool = AtomicBool::new(false);
/// A boolean flag set to true after the scheduler has been started.
static STARTED: AtomicBool = AtomicBool::new(false);

/// Return if the scheduler has been started.
pub(crate) fn has_started() -> bool {
SCHEDULER_STARTED.load(Ordering::SeqCst)
}

pub(super) fn set_started() {
SCHEDULER_STARTED.store(true, Ordering::SeqCst)
STARTED.load(Ordering::SeqCst)
}

static SCHEDULER_SUSPEND_CNT: AtomicUsize = AtomicUsize::new(0);
/// When the counter is positive the scheduler will be suspended and no context
/// switch will occur.
static SUSPEND_CNT: AtomicUsize = AtomicUsize::new(0);

pub(crate) fn increment_suspend_count() {
SCHEDULER_SUSPEND_CNT.fetch_add(1, Ordering::SeqCst);
/// Increment scheduler suspend count by 1.
pub(crate) fn incr_suspend_cnt() {
SUSPEND_CNT.fetch_add(1, Ordering::SeqCst);
}

pub(crate) fn decrement_suspend_count() {
SCHEDULER_SUSPEND_CNT.fetch_sub(1, Ordering::SeqCst);
/// Decrement scheduler suspend count by 1.
pub(crate) fn decr_suspend_cnt() {
SUSPEND_CNT.fetch_sub(1, Ordering::SeqCst);
}

pub(crate) fn yield_cur_task_from_isr() {
Expand All @@ -283,9 +288,7 @@ fn request_context_switch() {
}

pub(crate) fn yield_for_preemption() {
if !CONTEXT_SWITCH_REQUESTED.load(Ordering::SeqCst)
|| SCHEDULER_SUSPEND_CNT.load(Ordering::SeqCst) > 0
{
if !CONTEXT_SWITCH_REQUESTED.load(Ordering::SeqCst) || SUSPEND_CNT.load(Ordering::SeqCst) > 0 {
return;
}

Expand Down
6 changes: 3 additions & 3 deletions src/sync/suspend_scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ pub struct Scheduler {}
pub struct SchedulerSuspendGuard {}

pub fn suspend_scheduler() -> SchedulerSuspendGuard {
scheduler::increment_suspend_count();
scheduler::incr_suspend_cnt();
SchedulerSuspendGuard {}
}

impl Drop for SchedulerSuspendGuard {
fn drop(&mut self) {
scheduler::decrement_suspend_count();
scheduler::decr_suspend_cnt();
scheduler::yield_for_preemption();
}
}
Expand All @@ -28,7 +28,7 @@ impl Holdable for Scheduler {
}

unsafe fn force_unhold() {
scheduler::decrement_suspend_count();
scheduler::decr_suspend_cnt();
scheduler::yield_for_preemption();
}
}
Expand Down

0 comments on commit 5a8438f

Please sign in to comment.