Skip to content

Commit

Permalink
bootloader: move timeout into idle function
Browse files Browse the repository at this point in the history
  • Loading branch information
surban committed Dec 23, 2023
1 parent 2f67f30 commit 39843b5
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 45 deletions.
19 changes: 11 additions & 8 deletions openemc-bootloader/src/board.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Board.
use core::num::NonZeroU32;

use crate::{i2c_reg_slave::I2CRegTransaction, util::enter_standby, BoardInitResult};
use crate::{
bootloader::BootloaderResult, i2c_reg_slave::I2CRegTransaction, util::enter_standby, BoardInitResult,
};
use openemc_shared::boot::{BootInfo, ResetStatus};

/// Board-specific functionality.
Expand Down Expand Up @@ -58,11 +58,14 @@ pub trait Board {
fn bootloader_request(&mut self, _t: I2CRegTransaction) {}

/// Bootloader idle function.
fn idle(&mut self) {}

/// Ticks before bootloader times out.
fn timeout_ticks(&mut self) -> Option<NonZeroU32> {
Some(defmt::unwrap!(NonZeroU32::new(4_200_000_000)))
///
/// If it returns a value, the bootloader exits with the specified result.
fn idle(&mut self, _total_s: u16, idle_s: u16, timeout_enabled: bool) -> Option<BootloaderResult> {
if idle_s >= 600 && timeout_enabled {
Some(BootloaderResult::Timeout)
} else {
None
}
}

/// Shutdown the system and go to sleep.
Expand Down
68 changes: 36 additions & 32 deletions openemc-bootloader/src/bootloader.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Bootloader with an I2C interface.
use core::{mem::size_of, num::NonZeroU32, ptr};
use core::{mem::size_of, num::Saturating, ptr};
use cortex_m::peripheral::{scb::Exception, SCB};
use defmt::Format;

Expand Down Expand Up @@ -116,8 +116,8 @@ pub struct BootloaderInfo<'a, Ext, Idle> {
pub user_flash_start: usize,
/// User flash end address (non-inclusive).
pub user_flash_end: usize,
/// Bootloader timeout value in clock ticks.
pub timeout_ticks: Option<NonZeroU32>,
/// Processor clock speed in Hz.
pub cpu_clock: u32,
/// Boot reason.
pub boot_reason: u16,
/// Reset status.
Expand All @@ -132,7 +132,7 @@ pub struct BootloaderInfo<'a, Ext, Idle> {

/// Result of running the bootloader.
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq, Format)]
pub enum BootloaderResult {
/// Start the user program.
Start = 0x01,
Expand All @@ -148,30 +148,37 @@ pub enum BootloaderResult {
pub fn run<Ext, Idle>(mut info: BootloaderInfo<Ext, Idle>, i2c_slave: I2CSlave) -> BootloaderResult
where
Ext: FnMut(I2CRegTransaction),
Idle: FnMut(),
Idle: FnMut(u16, u16, bool) -> Option<BootloaderResult>,
{
let mut i2c = I2CRegSlave::new(i2c_slave);
let mut flash_addr = info.user_flash_start;
let mut writer = None;
let mut flash_fail = false;
let mut verify_result = None;

let mut timeout_enabled = true;
let mut timer = match info.timeout_ticks {
Some(ticks) => {
let mut timer = Timer4::new();
timer.set_auto_reload(ticks.get() as u16);
timer.set_prescaler((ticks.get() >> 16) as u16);
timer.set_one_pulse_mode(true);
timer.generate_update();
timer.set_enabled(true);
Some(timer)
}
None => None,
};
let mut total_seconds = Saturating(0);
let mut idle_seconds = Saturating(0);

// Program timer tick for one second.
let mut timer = Timer4::new();
timer.set_prescaler(u16::MAX);
timer.set_auto_reload((info.cpu_clock / u16::MAX as u32) as _);
timer.set_one_pulse_mode(true);
timer.generate_update();
timer.set_enabled(true);

loop {
watchdog::pet();
(info.idle_fn)();
if let Some(res) = (info.idle_fn)(total_seconds.0, idle_seconds.0, timeout_enabled) {
defmt::info!(
"bootloader exit with result {:?} after {} seconds with {} seconds idle",
res,
total_seconds.0,
idle_seconds.0
);
return res;
}

watchdog::pet();
let mut transacted = true;
Expand Down Expand Up @@ -314,7 +321,7 @@ where
return BootloaderResult::Start;
}
Some(I2CRegTransaction::Read(mut tx)) if tx.reg() == REG_TIMEOUT_ENABLED => {
tx.send_u8(u8::from(timer.is_some() && timeout_enabled));
tx.send_u8(timeout_enabled as _);
}
Some(I2CRegTransaction::Write(mut rx)) if rx.reg() == REG_TIMEOUT_ENABLED => {
timeout_enabled = rx.recv_u8() != 0;
Expand All @@ -340,19 +347,16 @@ where
}
}

// Check for timeout.
if let Some(timer) = &mut timer {
// defmt::info!("counter: {} {:?}", timer.counter(), timer.is_enabled());

if transacted {
timer.generate_update();
timer.set_enabled(true);
}

if timeout_enabled && !timer.is_enabled() {
defmt::info!("bootloader: timeout");
return BootloaderResult::Timeout;
}
// Update times.
if !timer.is_enabled() {
idle_seconds += 1;
total_seconds += 1;
timer.generate_update();
timer.set_enabled(true);
defmt::debug!("bootloader total time: {} s idle time: {} s", total_seconds.0, idle_seconds.0);
}
if transacted {
idle_seconds.0 = 0;
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions openemc-bootloader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,19 +283,20 @@ fn main() -> ! {
loop {
defmt::info!("bootloader start");

let timeout_ticks = board.timeout_ticks();
let board_cell = RefCell::new(board);
let info = BootloaderInfo {
emc_model: emc_model(),
board_model: board_cell.borrow().model(),
user_flash_start: user_flash_start(),
user_flash_end: flash::end(),
timeout_ticks,
cpu_clock: ThisBoard::CPU_CLOCK,
boot_reason,
reset_status,
id: program.map(|p| p.id).unwrap_or_default(),
extend_fn: |req: I2CRegTransaction| board_cell.borrow_mut().bootloader_request(req),
idle_fn: || board_cell.borrow_mut().idle(),
idle_fn: |total, idle, timeout_enabled| {
board_cell.borrow_mut().idle(total, idle, timeout_enabled)
},
};

let i2c_slave = I2CSlave::new(
Expand Down
8 changes: 6 additions & 2 deletions openemc-bootloader/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ pub fn delay_ms(ms: u32) {
/// Enters standby mode.
#[allow(dead_code)]
pub fn enter_standby() -> ! {
defmt::warn!("entering standby mode");

let mut cp = unsafe { cortex_m::Peripherals::steal() };
let dp = unsafe { Peripherals::steal() };

// Enable GPIO A.
dp.RCC.apb2enr.modify(|_, w| w.afioen().enabled().iopaen().enabled());

// Check if WKUP pin is low.
dp.GPIOA.crl.modify(|_, w| w.cnf0().open_drain().mode0().input());
delay_ms(10);
let wkup = dp.GPIOA.idr.read().idr0();
if wkup.is_high() {
// Device should stay awake, convert shutdown into reset.
Expand Down Expand Up @@ -49,8 +55,6 @@ pub fn enter_standby() -> ! {
cp.SCB.set_sleepdeep();

// Enter standby mode.
defmt::warn!("entering standby mode");
delay_ms(10);
loop {
wfi()
}
Expand Down

0 comments on commit 39843b5

Please sign in to comment.