avr-oxide 0.3.1

An extremely simple Rusty operating system for AVR microcontrollers
/* interrupt.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Support for interrupt service routines/interrupt isolation for Oxide.

// Imports ===================================================================
use core::arch::asm;
use avr_oxide::hal::generic::cpu::Cpu;
use avr_oxide::cpu;
use avr_oxide::concurrency::Isolated;

// Declarations ==============================================================
pub mod token {
  /// Token obtained by either the isolated() or isr() methods that
  /// can be used to indicate we are isolated from interrupts
  #[derive(Clone, Copy)]
  pub struct Isolated {
    // This ensures only we can create it, nobody else
    pub(super) _private: ()
  }
}

// Code ======================================================================
/**
 * Execute the given closure without interruptions
 */
pub fn isolated<F,R>(f: F) -> R
where
  F: FnOnce(Isolated) -> R
{
  if cpu!().interrupts_enabled() && !cpu!().in_isr() {
    unsafe {
      disable_interrupts();
      let result = f(Isolated{_private:()});
      enable_interrupts();
      result
    }
  } else {
    f(Isolated{_private:()})
  }
}

/// Execute the given code within the context of an Interrupt Service Routine.
/// Actually, we let the `[oxide_macros::interrupt]` attribute macro generate
/// the preamble/postamble for us, so this closure is just responsible for
/// setting the "I'm in an interrupt" flag and passing an isotoken.
///
/// # Parameters
/// `reschedule`: If true, a thread reschedule will be triggered before returning
#[cfg(target_arch="avr")]
pub(crate) fn isr<F>(reschedule: bool, f: F) -> ()
where
  F: FnOnce(Isolated) -> ()
{
  let isotoken = Isolated{_private:()};

  unsafe {
    // Set the context flag that indicates we're inside an ISR
    core::arch::asm!(
      "sbi {context_flags_reg},{flag_inisr}",
      context_flags_reg = const(avr_oxide::hardware::cpu::cpuregs::IOADR_CONTEXT_FLAGS),
      flag_inisr = const(avr_oxide::hal::generic::cpu::CONTEXT_FLAG_INISR)
    );

    // Call our closure
    f(isotoken);

    // Reschedule if requested
    if reschedule {
      avr_oxide::concurrency::scheduler::schedule_next_thread(isotoken);
    }

    // Clear the 'in an interrupt' context flag
    core::arch::asm!(
      "cbi {context_flags_reg},{flag_inisr}",
      context_flags_reg = const(avr_oxide::hardware::cpu::cpuregs::IOADR_CONTEXT_FLAGS),
      flag_inisr = const(avr_oxide::hal::generic::cpu::CONTEXT_FLAG_INISR)
    );
  }
}

#[cfg(not(target_arch="avr"))]
pub fn isr<F>(_reschedule: bool, f: F) -> ()
  where
    F: FnOnce(Isolated) -> (),
{
  f(Isolated{_private:()});
}

#[cfg(not(target_arch="avr"))]
pub(crate) unsafe fn enable_interrupts() {
}
#[cfg(not(target_arch="avr"))]
pub(crate) unsafe fn disable_interrupts() {
}


#[cfg(target_arch="avr")]
#[inline(always)]
pub(crate) unsafe fn enable_interrupts(){
  asm!("sei")
}

#[cfg(target_arch="avr")]
#[inline(always)]
pub(crate) unsafe fn disable_interrupts() {
  asm!("cli")
}

#[cfg(not(target_arch="avr"))]
pub fn wait() {
  std::thread::sleep(std::time::Duration::from_millis(100));
}

#[cfg(target_arch="avr")]
#[inline(always)]
pub fn wait() {
  unsafe {
    asm!("sleep")
  }
}
// Tests =====================================================================