Skip to content

Commit

Permalink
Merge pull request #30 from SteelPh0enix/timers-integration-tests
Browse files Browse the repository at this point in the history
ROS#119 - Timer integration tests
  • Loading branch information
SteelPh0enix authored Aug 16, 2023
2 parents 7890f69 + f35aa15 commit 520d8e5
Show file tree
Hide file tree
Showing 16 changed files with 427 additions and 21 deletions.
3 changes: 3 additions & 0 deletions arch/cortex-m/samv71-hal/src/hal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ impl Hal {
/// guarantee safety if multiple instances of peripherals are used in the system.
fn create_peripherals() -> (UserPeripherals, SystemPeripherals) {
let mcu_peripherals = unsafe { pac::Peripherals::steal() };
let core_peripherals = unsafe { pac::CorePeripherals::steal() };

let system_peripherals = SystemPeripherals {
watchdog: Watchdog::new(mcu_peripherals.WDT),
Expand All @@ -72,6 +73,8 @@ impl Hal {
timer_counter1: Some(mcu_peripherals.TC1),
timer_counter2: Some(mcu_peripherals.TC2),
timer_counter3: Some(mcu_peripherals.TC3),
pmc: Some(mcu_peripherals.PMC),
nvic: Some(core_peripherals.NVIC),
};

(user_peripherals, system_peripherals)
Expand Down
1 change: 1 addition & 0 deletions arch/cortex-m/samv71-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod user_peripherals;

pub use self::hal::Hal;
pub use embedded_hal;
pub use pac::{NVIC, PMC};

#[cfg(feature = "rt")]
pub use pac::interrupt;
4 changes: 4 additions & 0 deletions arch/cortex-m/samv71-hal/src/user_peripherals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ pub struct UserPeripherals {
pub timer_counter2: Option<pac::TC2>,
/// Timer Counter 3.
pub timer_counter3: Option<pac::TC3>,
/// PMC
pub pmc: Option<pac::PMC>,
/// NVIC
pub nvic: Option<pac::NVIC>,
}
4 changes: 4 additions & 0 deletions calldwell/gdb_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ def _handle_async_events(self, responses: GDBResponsesList):
self._program_state.last_stop_reason = stop_reason
self._program_state.program_frame = current_program_frame

if stop_reason == "signal-received":
# temporary fix for not detecting watchdog reset
self.program_state.was_reset = True

self._logger.info(
f"Program has stopped at {current_program_frame}, reason: {stop_reason}"
)
Expand Down
10 changes: 9 additions & 1 deletion examples/samv71-hal-timer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use core::cell::RefCell;
use aerugo::hal::drivers::timer::{
channel_config::ChannelClock, waveform_config::WaveformModeConfig, Ch0, Channel, Waveform, TC1,
};
use aerugo::hal::PMC;
use cortex_m::interrupt::free as irq_free;
use cortex_m::interrupt::Mutex;
use panic_semihosting as _;
Expand Down Expand Up @@ -48,6 +49,11 @@ fn dummy_task(_: (), context: &mut DummyTaskContext, _: &'static dyn RuntimeApi)

static DUMMY_TASK_STORAGE: TaskletStorage<(), DummyTaskContext, 0> = TaskletStorage::new();

fn init_pmc(pmc: PMC) {
// Enable TC1 CH0 clock
pmc.pcer0.write(|w| w.pid26().set_bit());
}

fn init_timer(timer: &mut Timer<TC1>) {
let ch0 = timer
.channel_0
Expand Down Expand Up @@ -109,7 +115,9 @@ fn main() -> ! {
.expect("peripherals already taken");

let mut timer = Timer::new(peripherals.timer_counter1.expect("Timer 1 already used"));
// TODO when PMC driver is done: actually enable timer clock so it starts correctly.
// TODO: Change this to use proper PMC driver when it's done
let pmc = peripherals.pmc.expect("PMC already used");
init_pmc(pmc);
init_timer(&mut timer);

init_tasks();
Expand Down
11 changes: 11 additions & 0 deletions testbins/test-hal-timer/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[build]
target = "thumbv7em-none-eabihf"

[env]
AERUGO_TASKLET_COUNT = { value = "2" }

[target.thumbv7em-none-eabihf]
rustflags = [
"-C", "link-arg=--nmagic", # Disable page alignment of sections (to prevent issues with binary size)
"-C", "link-arg=-Tlink.x", # Use cortex-m-rt's linker script
]
23 changes: 23 additions & 0 deletions testbins/test-hal-timer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "test-hal-timer"
authors = ["Wojciech Olech <wojciech_olech@hotmail.com>"]
edition = "2021"
version = "0.1.0"

[dependencies]
aerugo = { version = "0.1.0", path = "../..", features = [
"use-aerugo-cortex-m",
"rt",
] }
calldwell = { version = "0.1.0", path = "../../calldwell/calldwell-rs" }
cortex-m = { version = "0.7.7", features = ["critical-section-single-core"] }
cortex-m-rt = { version = "0.7.3", features = ["device"] }
panic-rtt-target = { version = "0.1.2", features = ["cortex-m"] }

[features]
rt = ["aerugo/rt"]

[profile.release]
codegen-units = 1
lto = true
debug = true
14 changes: 14 additions & 0 deletions testbins/test-hal-timer/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
}
6 changes: 6 additions & 0 deletions testbins/test-hal-timer/memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* Linker script for SAMV71Q21 */
MEMORY
{
FLASH (rx) : ORIGIN = 0x00400000, LENGTH = 0x00200000
RAM (rwx) : ORIGIN = 0x20400000, LENGTH = 0x00060000
}
202 changes: 202 additions & 0 deletions testbins/test-hal-timer/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#![no_std]
#![no_main]

extern crate aerugo;
extern crate calldwell;
extern crate cortex_m;
extern crate cortex_m_rt as rt;
extern crate panic_rtt_target;

use aerugo::{
hal::drivers::timer::{
channel_config::ChannelClock, waveform_config::WaveformModeConfig, Ch0, Channel, Timer,
Waveform, TC1,
},
hal::{drivers::timer::channel_config::ChannelInterrupts, interrupt, NVIC, PMC},
time::MillisDurationU32,
InitApi, RuntimeApi, SystemHardwareConfig, TaskletConfig, TaskletStorage, AERUGO,
};
use calldwell::{with_rtt_in, with_rtt_out};
use core::{cell::RefCell, fmt::Write, ops::AddAssign};
use cortex_m::interrupt::{free as irq_free, Mutex};
use rt::entry;

static TIMER_CHANNEL: Mutex<RefCell<Option<Channel<TC1, Ch0, Waveform>>>> =
Mutex::new(RefCell::new(None));
static TIMER_IRQ_COUNT: Mutex<RefCell<u32>> = Mutex::new(RefCell::new(0));

#[derive(Default)]
struct TimerTestTaskContext {
acc: u32,
}

fn timer_test_task(_: (), context: &mut TimerTestTaskContext, _: &dyn RuntimeApi) {
context.acc = context.acc.wrapping_add(1);

if context.acc % 100 == 0 {
let irq_count = get_irq_count();
with_rtt_out(|w, _| write!(w.writer(), "{}", irq_count).unwrap());

if context.acc == 1000 {
disable_channel();
}

if context.acc == 2000 {
change_channels_clock_source();
enable_and_trigger_channel();
}
}
}

static TIMER_TEST_TASK_STORAGE: TaskletStorage<(), TimerTestTaskContext, 0> = TaskletStorage::new();

fn initialize_tasks() {
let timer_test_task_config = TaskletConfig {
name: "TimerTestTask",
..Default::default()
};

let timer_test_task_context = TimerTestTaskContext::default();

AERUGO
.create_tasklet_with_context(
timer_test_task_config,
timer_test_task,
timer_test_task_context,
&TIMER_TEST_TASK_STORAGE,
)
.expect("Unable to create timer test task!");

let timer_test_task_handle = TIMER_TEST_TASK_STORAGE
.create_handle()
.expect("Unable to create timer test task handle!");

AERUGO
.subscribe_tasklet_to_cyclic(&timer_test_task_handle, None)
.expect("Unable to subscribe timer test task to cyclic execution!");
}

fn initialize_nvic() {
unsafe {
// Enable TC0 CH0 interrupt
NVIC::unmask(interrupt::TC3);
}
}

fn initialize_pmc(pmc: PMC) {
// Enable TC1 CH0 clock
pmc.pcer0.write(|w| w.pid26().set_bit());
}

fn initialize_timer(mut timer: Timer<TC1>) {
// Enable waveform mode
let channel = timer
.channel_0
.take()
.expect("TC1 Ch0 already taken")
.into_waveform_channel(WaveformModeConfig::default());

// Use non-default clock source
channel.set_clock_source(ChannelClock::MckDividedBy8);

// Enable overflow interrupt
channel.enable_interrupts(ChannelInterrupts {
counter_overflow: true,
..Default::default()
});

// Put channel's instance in global context
irq_free(|cs| {
TIMER_CHANNEL.borrow(cs).replace(Some(channel));
});
}

fn enable_and_trigger_channel() {
irq_free(|cs| {
let channel_ref = TIMER_CHANNEL.borrow(cs).borrow();
let channel = channel_ref.as_ref().unwrap();
channel.enable();
channel.trigger();
});
}

fn disable_channel() {
irq_free(|cs| {
let channel_ref = TIMER_CHANNEL.borrow(cs).borrow();
let channel = channel_ref.as_ref().unwrap();
channel.disable();
});
}

fn change_channels_clock_source() {
irq_free(|cs| {
let channel_ref = TIMER_CHANNEL.borrow(cs).borrow();
let channel = channel_ref.as_ref().unwrap();
channel.set_clock_source(ChannelClock::MckDividedBy32);
});
}

fn clear_channel_irq_flags() {
irq_free(|cs| {
let channel_ref = TIMER_CHANNEL.borrow(cs).borrow();
let channel = channel_ref.as_ref().unwrap();
channel.read_and_clear_status();
});
}

fn get_irq_count() -> u32 {
irq_free(|cs| *TIMER_IRQ_COUNT.borrow(cs).borrow())
}

#[entry]
fn main() -> ! {
calldwell::initialize();
wait_for_host();

AERUGO.initialize(SystemHardwareConfig {
watchdog_timeout: MillisDurationU32::secs(3),
});

let peripherals = AERUGO
.peripherals()
.expect("HAL was not initialized!")
.expect("Peripherals already taken!");

let timer = Timer::new(peripherals.timer_counter1.expect("TC1 already taken!"));

initialize_nvic();
initialize_pmc(peripherals.pmc.expect("PMC already taken!"));
initialize_timer(timer);

initialize_tasks();
enable_and_trigger_channel();

AERUGO.start();
}

fn wait_for_host() {
let mut input_buffer: [u8; 128] = [0; 128];

with_rtt_out(|w, _| w.write_str("mcu ready"));
let response_length = with_rtt_in(|r, _| r.read(&mut input_buffer));

if let Err(e) = response_length {
with_rtt_out(|w, _| {
write!(
w.writer(),
"an error occurred while receiving response from host: {:?}",
e
)
.expect("Unable to send data via RTT")
});
}
}

#[interrupt]
fn TC3() {
clear_channel_irq_flags();

irq_free(|cs| {
TIMER_IRQ_COUNT.borrow(cs).borrow_mut().add_assign(1);
});
}
6 changes: 0 additions & 6 deletions testbins/test-hal-watchdog/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@ use core::fmt::Write;
use cortex_m::asm;
use rt::entry;

// Test scenario:
// - Configure Aerugo with watchdog that will reset the MCU after 3 seconds
// - Execute a task that will run shorter than 3 seconds and send a message to host
// - Execute a task that will run longer than 3 seconds
// - Validate that MCU has rebooted

#[derive(Default)]
struct ShortTaskContext {
acc: u16,
Expand Down
1 change: 1 addition & 0 deletions tests/requirements/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod test_boolean_condition;
mod test_event_cancellation;
mod test_event_clear_queue;
mod test_events;
mod test_hal_timer;
mod test_hal_watchdog;
mod test_message_queue;
mod test_tasklet_priority;
Expand Down
Loading

0 comments on commit 520d8e5

Please sign in to comment.