Skip to content

Commit

Permalink
Refactor context switch test.
Browse files Browse the repository at this point in the history
  • Loading branch information
zyma98 committed Aug 21, 2024
1 parent 86d8b16 commit 65f561e
Show file tree
Hide file tree
Showing 10 changed files with 449 additions and 234 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/actions/build/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,22 @@ runs:
sub-category: segmented_stack
test-name: return_values

# *** Tests for task - context switch ***

- name: Build test test-task-context_switch-gp_registers
uses: ./.github/workflows/actions/build-test
with:
category: task
sub-category: context_switch
test-name: gp_registers

- name: Build test test-task-context_switch-fp_registers
uses: ./.github/workflows/actions/build-test
with:
category: task
sub-category: context_switch
test-name: fp_registers

# *** Tests for sync - channel ***

- name: Build test test-sync-channel-produce_consume_single_task
Expand Down
39 changes: 39 additions & 0 deletions .github/workflows/context_switch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Run Tests for Task Context Switch

on:
workflow_call:
secrets:
cookie:
required: true

env:
CARGO_TERM_COLOR: always

jobs:
gp_registers:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run test gp_registers
uses: ./.github/workflows/actions/run-test
with:
cookie: ${{ secrets.cookie }}
category: task
sub-category: context_switch
test-name: gp_registers

fp_registers:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run test fp_registers
uses: ./.github/workflows/actions/run-test
with:
cookie: ${{ secrets.cookie }}
category: task
sub-category: context_switch
test-name: fp_registers
5 changes: 5 additions & 0 deletions .github/workflows/task.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ jobs:
uses: ./.github/workflows/segmented_stack.yaml
secrets:
cookie: ${{ secrets.cookie }}

context_switch:
uses: ./.github/workflows/context_switch.yaml
secrets:
cookie: ${{ secrets.cookie }}
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,16 @@ path = "examples/tests/task/segmented_stack/function_arguments.rs"
name = "test-task-segmented_stack-return_values"
path = "examples/tests/task/segmented_stack/return_values.rs"

# *** Tests for task - context switch ***

[[example]]
name = "test-task-context_switch-gp_registers"
path = "examples/tests/task/context_switch/gp_registers.rs"

[[example]]
name = "test-task-context_switch-fp_registers"
path = "examples/tests/task/context_switch/fp_registers.rs"

# *** Tests for interrupt - unwind ***

[[example]]
Expand Down
200 changes: 200 additions & 0 deletions examples/tests/task/context_switch/fp_registers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
//! Tests that a context switch maintains all floating point registers.
#![no_std]
#![no_main]
#![feature(naked_functions)]

extern crate alloc;
use core::{arch::asm, sync::atomic::AtomicBool};
use hopter::{boot::main, config, debug::semihosting, hprintln, task};

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

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

static mut KNOWN_VALUE: [f32; 32] = [
1f32, 2f32, 3f32, 4f32, 5f32, 6f32, 7f32, 8f32, 9f32, 10f32, 11f32, 12f32, 13f32, 14f32, 15f32,
16f32, 17f32, 18f32, 19f32, 20f32, 21f32, 22f32, 23f32, 24f32, 25f32, 26f32, 27f32, 28f32,
29f32, 30f32, 31f32, 32f32,
];

static mut CLOBBERED_VALUE: [f32; 32] = [0f32; 32];

#[main]
fn main(_: cortex_m::Peripherals) {
// Two tasks have the same priority so will be scheduled round-robin.
task::build()
.set_entry(|| verify_registers())
.set_priority(config::DEFAULT_TASK_PRIORITY)
.spawn()
.unwrap();
task::build()
.set_entry(|| clobber_all_fp_regs())
.set_priority(config::DEFAULT_TASK_PRIORITY)
.spawn()
.unwrap();
}

/// Write known values to registers and check that they remain the same value
/// after context switch.
#[naked]
extern "C" fn verify_registers() -> ! {
unsafe {
asm!(
// Set `TEST_STARTED` to true.
"ldr r0, ={test_started}",
"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",
// 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}",
// 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}",
// 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,
known_value = sym KNOWN_VALUE,
compare_fp_regs = sym compare_fp_regs,
clobber = sym clobber_all_fp_regs,
success = sym success,
options(noreturn)
)
}
}

/// After the verifier task has started, write `0xffffffff` to all general
/// purpose registers.
#[naked]
extern "C" fn clobber_all_fp_regs() -> ! {
unsafe {
asm!(
"ldr r0, ={test_started}",
"0:",
// Load the current value of `TEST_STARTED`.
"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",
"b 0b",
// The verify task is running now. Clobber all registers. This
// should not affect the registers in the verify task's context.
"1:",
// Set `CLOBERRED` to true.
"ldr r0, ={cloberred}",
"mov r1, #1",
"strb r1, [r0]",
// Clobber registers.
"ldr r0, ={clobbered_value}",
"vldmia r0, {{s0-s31}}",
// Perform context switch so that the verifier task can perform
// the check.
"2:",
"svc #1",
"b 2b",
test_started = sym TEST_STARTED,
clobbered_value = sym CLOBBERED_VALUE,
cloberred = sym CLOBBERED,
options(noreturn)
)
}
}

/// Verify that floating point register `s{x}` contains the same value as in
/// `s{x+16}` for x in 0..16.
#[naked]
extern "C" fn compare_fp_regs() {
unsafe {
asm!(
"vcmp.f32 s0, s16",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s1, s17",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s2, s18",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s3, s19",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s4, s20",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s5, s21",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s6, s22",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s7, s23",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s8, s24",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s9, s25",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s10, s26",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s11, s27",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s12, s28",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s13, s29",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s14, s30",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"vcmp.f32 s15, s31",
"vmrs apsr_nzcv, fpscr",
"bne {error}",
"bx lr",
error = sym error,
options(noreturn)
)
}
}

extern "C" fn error() -> ! {
hprintln!("Test Failed");
semihosting::terminate(false);
}

extern "C" fn success() -> ! {
hprintln!("Test Succeeded");
semihosting::terminate(true);
}
1 change: 1 addition & 0 deletions examples/tests/task/context_switch/fp_registers.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test Succeeded
Loading

0 comments on commit 65f561e

Please sign in to comment.