Skip to content

Commit

Permalink
Optimize context switch implementation for better performance.
Browse files Browse the repository at this point in the history
  • Loading branch information
zyma98 committed Oct 4, 2024
1 parent fd53ca0 commit dde8b33
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 120 deletions.
92 changes: 70 additions & 22 deletions examples/tests/task/context_switch/fp_registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ use hopter::{
task::main,
};

/// Whether the verifier task is running.
static TEST_STARTED: AtomicBool = AtomicBool::new(false);
/// Whether the clobbering task should run.
static RUN_CLOBBER: AtomicBool = AtomicBool::new(false);

/// Whether the cloberring task has executed.
/// Whether the cloberring task has run.
static CLOBBERED: AtomicBool = AtomicBool::new(false);

static mut KNOWN_VALUE: [f32; 32] = [
Expand Down Expand Up @@ -48,44 +48,75 @@ fn main(_: cortex_m::Peripherals) {
extern "C" fn verify_registers() -> ! {
unsafe {
asm!(
// Set `TEST_STARTED` to true.
"ldr r0, ={test_started}",
// Set `RUN_CLOBBER` to true.
"0:",
"ldr r0, ={run_clobber}",
"mov r1, #1",
"strb r1, [r0]",
"0:",
// Set register `s0-s15` to known values.
"ldr r0, ={known_value}",
"vldmia r0, {{s0-s15}}",
// Trigger context switch.
"svc #1",
"mov r0, #0xe0",
"msr basepri, r0",
"mov r1, #0x10000000",
"movw r0, #0xed04",
"movt r0, #0xe000",
"str r1, [r0]",
"mov r0, #0",
"msr basepri, r0",
// See if the clobbering task has run.
"ldr r0, ={clobber}",
"ldrb r0, [r0]",
// If the clobbering task has not run yet, we loop back and do
// everything another time.
"cmp r0, #0",
"beq 0b",
// Examine the values of registers `s0-s15`. They should remain the
// same as before the context switch.
"ldr r0, ={known_value}",
"vldmia r0, {{s16-s31}}",
"bl {compare_fp_regs}",
"1:",
// Set `CLOBERRED` to false.
"ldr r0, ={cloberred}",
"mov r1, #1",
"strb r1, [r0]",
// Set `RUN_CLOBBER` to true.
"ldr r0, ={run_clobber}",
"mov r1, #1",
"strb r1, [r0]",
// Set register `s16-s31` to known values.
"ldr r0, ={known_value} + 64",
"vldmia r0, {{s16-s31}}",
// Trigger context switch.
"svc #1",
// Examine the values of registers `s16-s31`. They should remain the
// same as before the context switch.
"ldr r0, ={known_value} + 64",
"vldmia r0, {{s0-s15}}",
"bl {compare_fp_regs}",
"mov r0, #0xe0",
"msr basepri, r0",
"mov r1, #0x10000000",
"movw r0, #0xed04",
"movt r0, #0xe000",
"str r1, [r0]",
"mov r0, #0",
"msr basepri, r0",
// See if the clobbering task has run.
"ldr r0, ={clobber}",
"ldrb r0, [r0]",
// If the clobbering task has not run yet, we loop back and do
// everything another time.
"cmp r0, #0",
"beq 0b",
"beq 1b",
// Examine the values of registers `s16-s31`. They should remain the
// same as before the context switch.
"ldr r0, ={known_value} + 64",
"vldmia r0, {{s0-s15}}",
"bl {compare_fp_regs}",
// If the clobbering task has run, then we have verified that the
// registers in this task's context were not affected. Declare
// success.
"b {success}",
test_started = sym TEST_STARTED,
run_clobber = sym RUN_CLOBBER,
known_value = sym KNOWN_VALUE,
cloberred = sym CLOBBERED,
compare_fp_regs = sym compare_fp_regs,
clobber = sym clobber_all_fp_regs,
success = sym success,
Expand All @@ -100,15 +131,22 @@ extern "C" fn verify_registers() -> ! {
extern "C" fn clobber_all_fp_regs() -> ! {
unsafe {
asm!(
"ldr r0, ={test_started}",
"0:",
// Load the current value of `TEST_STARTED`.
"ldr r0, ={run_clobber}",
// Load the current value of `RUN_CLOBBER`.
"ldrb r1, [r0]",
"cmp r1, #0",
// Goto cloberring the register if has started.
"bne 1f",
// Otherwise, perform a context switch and try again.
"svc #1",
"mov r0, #0xe0",
"msr basepri, r0",
"mov r1, #0x10000000",
"movw r0, #0xed04",
"movt r0, #0xe000",
"str r1, [r0]",
"mov r0, #0",
"msr basepri, r0",
"b 0b",
// The verify task is running now. Clobber all registers. This
// should not affect the registers in the verify task's context.
Expand All @@ -120,12 +158,22 @@ extern "C" fn clobber_all_fp_regs() -> ! {
// Clobber registers.
"ldr r0, ={clobbered_value}",
"vldmia r0, {{s0-s31}}",
// Set `RUN_CLOBBER` to false.
"ldr r0, ={run_clobber}",
"mov r1, #1",
"strb r1, [r0]",
// Perform context switch so that the verifier task can perform
// the check.
"2:",
"svc #1",
"b 2b",
test_started = sym TEST_STARTED,
"mov r0, #0xe0",
"msr basepri, r0",
"mov r1, #0x10000000",
"movw r0, #0xed04",
"movt r0, #0xe000",
"str r1, [r0]",
"mov r0, #0",
"msr basepri, r0",
"b 0b",
run_clobber = sym RUN_CLOBBER,
clobbered_value = sym CLOBBERED_VALUE,
cloberred = sym CLOBBERED,
options(noreturn)
Expand Down
121 changes: 87 additions & 34 deletions examples/tests/task/context_switch/gp_registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ use hopter::{
task::main,
};

/// Whether the verifier task is running.
static TEST_STARTED: AtomicBool = AtomicBool::new(false);
/// Whether the clobbering task should run.
static RUN_CLOBBER: AtomicBool = AtomicBool::new(false);

/// Whether the cloberring task has executed.
/// Whether the cloberring task has run.
static CLOBBERED: AtomicBool = AtomicBool::new(false);

#[main]
Expand All @@ -41,15 +41,12 @@ fn main(_: cortex_m::Peripherals) {
extern "C" fn verify_registers() -> ! {
unsafe {
asm!(
// Set `TEST_STARTED` to true.
"ldr r0, ={test_started}",
// Set `RUN_CLOBBER` to true.
"0:",
"ldr r0, ={run_clobber}",
"mov r1, #1",
"strb r1, [r0]",
"0:",
// Preserve the current stack pointer value in the stack.
"mov r0, sp",
"push {{r0}}",
// Write some known values to the registers.
// Write some known values to the low registers.
"mov r0, #1",
"mov r1, #2",
"mov r2, #3",
Expand All @@ -58,16 +55,24 @@ extern "C" fn verify_registers() -> ! {
"mov r5, #6",
"mov r6, #7",
"mov r7, #8",
"mov r8, #9",
"mov r9, #10",
"mov r10, #11",
"mov r11, #12",
"mov r12, #13",
"mov lr, #14",
// Trigger context switch.
"svc #1",
// Examine the values of registers. They should remain the same as
// before the context switch.
"mov r8, #0xe0",
"msr basepri, r8",
"mov r9, #0x10000000",
"movw r8, #0xed04",
"movt r8, #0xe000",
"str r9, [r8]",
"mov r8, #0",
"msr basepri, r8",
// See if the clobbering task has run.
"ldr r8, ={clobber}",
"ldrb r8, [r8]",
// If the clobbering task has not run yet, we loop back and do
// everything another time.
"cmp r8, #0",
"beq 0b",
// Examine the values of low registers. They should remain the same
// as before the context switch.
"cmp r0, #1",
"bne {error}",
"cmp r1, #2",
Expand All @@ -84,6 +89,43 @@ extern "C" fn verify_registers() -> ! {
"bne {error}",
"cmp r7, #8",
"bne {error}",
"1:",
// Set `CLOBERRED` to false.
"ldr r0, ={cloberred}",
"mov r1, #1",
"strb r1, [r0]",
// Set `RUN_CLOBBER` to true.
"ldr r0, ={run_clobber}",
"mov r1, #1",
"strb r1, [r0]",
// Preserve the current stack pointer value in the stack.
"mov r0, sp",
"push {{r0}}",
// Write some known values to the high registers.
"mov r8, #9",
"mov r9, #10",
"mov r10, #11",
"mov r11, #12",
"mov r12, #13",
"mov lr, #14",
// Trigger context switch.
"mov r0, #0xe0",
"msr basepri, r0",
"mov r1, #0x10000000",
"movw r0, #0xed04",
"movt r0, #0xe000",
"str r1, [r0]",
"mov r0, #0",
"msr basepri, r0",
// See if the clobbering task has run.
"ldr r0, ={clobber}",
"ldrb r0, [r0]",
// If the clobbering task has not run yet, we loop back and do
// everything another time.
"cmp r0, #0",
"beq 1b",
// Examine the values of high registers. They should remain the same
// as before the context switch.
"cmp r8, #9",
"bne {error}",
"cmp r9, #10",
Expand All @@ -101,18 +143,12 @@ extern "C" fn verify_registers() -> ! {
"pop {{r0}}",
"cmp r0, sp",
"bne {error}",
// See if the clobbering task has run.
"ldr r0, ={clobber}",
"ldrb r0, [r0]",
// If the clobbering task has not run yet, we loop back and do
// everything another time.
"cmp r0, #0",
"beq 0b",
// If the clobbering task has run, then we have verified that the
// registers in this task's context were not affected. Declare
// success.
"b {success}",
test_started = sym TEST_STARTED,
run_clobber = sym RUN_CLOBBER,
cloberred = sym CLOBBERED,
clobber = sym clobber_all_gp_regs,
error = sym error,
success = sym success,
Expand All @@ -127,15 +163,22 @@ extern "C" fn verify_registers() -> ! {
extern "C" fn clobber_all_gp_regs() -> ! {
unsafe {
asm!(
"ldr r0, ={test_started}",
"0:",
// Load the current value of `TEST_STARTED`.
"ldr r0, ={run_clobber}",
// Load the current value of `RUN_CLOBBER`.
"ldrb r1, [r0]",
"cmp r1, #0",
// Goto cloberring the register if has started.
"bne 1f",
// Otherwise, perform a context switch and try again.
"svc #1",
"mov r0, #0xe0",
"msr basepri, r0",
"mov r1, #0x10000000",
"movw r0, #0xed04",
"movt r0, #0xe000",
"str r1, [r0]",
"mov r0, #0",
"msr basepri, r0",
"b 0b",
// The verify task is running now. Clobber all registers. This
// should not affect the registers in the verify task's context.
Expand All @@ -159,12 +202,22 @@ extern "C" fn clobber_all_gp_regs() -> ! {
"mov r11, #0xffffffff",
"mov r12, #0xffffffff",
"mov lr, #0xffffffff",
// Set `RUN_CLOBBER` to false.
"ldr r0, ={run_clobber}",
"mov r1, #1",
"strb r1, [r0]",
// Perform context switch so that the verifier task can perform
// the check.
"2:",
"svc #1",
"b 2b",
test_started = sym TEST_STARTED,
"mov r0, #0xe0",
"msr basepri, r0",
"mov r1, #0x10000000",
"movw r0, #0xed04",
"movt r0, #0xe000",
"str r1, [r0]",
"mov r0, #0",
"msr basepri, r0",
"b 0b",
run_clobber = sym RUN_CLOBBER,
cloberred = sym CLOBBERED,
options(noreturn)
)
Expand Down
15 changes: 15 additions & 0 deletions src/interrupt/context_switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,18 @@ extern "C" fn pendsv_handler(ex_ret_lr: u32) {
// chosen task to run.
Scheduler::pick_next();
}

/// Invoke the scheduler to choose a new task to run.
///
/// A task may voluntarily yield the CPU or it may be forced to yield, e.g.,
/// when becoming blocked on a synchronization primitive.
///
/// This function should be called in a task's context and never in an ISR's
/// context.
pub(crate) fn yield_current_task() {
unsafe {
cortex_m::register::basepri::write(config::PENDSV_PRIORITY);
cortex_m::peripheral::SCB::set_pendsv();
cortex_m::register::basepri::write(config::IRQ_ENABLE_BASEPRI_PRIORITY);
}
}
15 changes: 0 additions & 15 deletions src/interrupt/svc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,6 @@ pub(crate) unsafe extern "C" fn svc_free(ptr: *mut u8) {
)
}

/// Yield the current task. Let the scheduler choose the next task to run.
/// A task may voluntarily yield the CPU or it may be forced to yield when
/// becoming blocked on a synchronization primitive.
#[naked]
pub(crate) extern "C" fn svc_yield_current_task() {
unsafe {
asm!(
"svc {task_yield}",
"bx lr",
task_yield = const(SVCNum::TaskYield as u8),
options(noreturn)
)
}
}

/// Terminate the current task and free its task struct.
#[naked]
pub(crate) unsafe extern "C" fn svc_destroy_current_task() {
Expand Down
Loading

0 comments on commit dde8b33

Please sign in to comment.