avr-oxide 0.4.0

An extremely simple Rusty operating system for AVR microcontrollers
/* cpu.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! CPU control flags/registers.
//!
//!

// Imports ===================================================================
use core::arch::asm;
use avr_oxide::concurrency::interrupt;
use avr_oxide::concurrency::util::ThreadId;
use avr_oxide::util::datatypes::{BitField, BitFieldAccess, BitIndex};
use avr_oxide::hardware::cpu::cpuregs::{IOREG_CONTEXT_FLAGS,IOREG_SREG};

// Declarations ==============================================================
pub const CONTEXT_FLAG_PREEMPTION: u8 = 4;
pub const CONTEXT_FLAG_INISR:      u8 = 3;
pub const CONTEXT_FLAG_ENABLEINTS: u8 = 2;
pub const CONTEXT_FLAG_RETI:       u8 = 1;
pub const CONTEXT_FLAG_RESTORED:   u8 = 0;

/**
 * Types of protected configuration change
 */
pub enum ConfigurationChange {
  /// Self programming, i.e. writing to the nonvolatile (Flash) memory
  /// controller
  SelfProgramming,

  /// Protected register access
  ProtectedRegister
}

/**
 * Thread execution context structure for this CPU.  As used by the
 * save_thread_context and load_thread_context extern functions.
 */
#[repr(C)]
#[derive(Clone)]
pub(crate) struct ProcessorContext {
  pub(crate) sreg: BitField,          // +0
  pub(crate) gpregs: [u8; 32],           // +1 to +32
  pub(crate) pc: u16,                    // +33 (pcl) +34 (pch)
  pub(crate) sp: u16,                    // +35 (spl) +36 (sph)

  pub(crate) tid: ThreadId,              // +37 Thread ID

  #[cfg(feature="extended_addressing")]
  pub(crate) rampx: u8,
  #[cfg(feature="extended_addressing")]
  pub(crate) rampy: u8,
  #[cfg(feature="extended_addressing")]
  pub(crate) rampz: u8,
  #[cfg(feature="extended_addressing")]
  pub(crate) eind: u8
}

/**
 * Handy methods for a CPU implementing savable/restorable context.
 *
 * In practice that means `save_thread_context` and `restore_thread_context`
 * methods implemented in our assembly code section (the somewhat misnamed
 * `boot.S` that is increasingly about more than booting.)
 */
pub(crate) trait ContextSaveRestore {
  /// Retrieve the current ThreadContext structure as referenced by the
  /// thread context register.
  unsafe fn get_processor_context(&self, isotoken: interrupt::token::Isolated) -> &mut ProcessorContext;

  /// Return true if this thread was just restored (as opposed to just being
  /// saved).
  fn was_thread_restored(&self) -> bool;
}
#[cfg(target_arch="avr")]
pub trait Cpu {
  /**
   * Write to a protected register.
   */
  unsafe fn write_protected(&mut self, change: ConfigurationChange,
                            register: &mut u8, value: u8 );

  /**
   * Read the current stack pointer.
   *
   * Note that it is critical this function is inlined if it is to read the
   * SP value you expect :-).
   */
  #[inline(always)]
  fn read_sp(&self) -> u16 {
    let sphval: u8;
    let splval: u8;

    unsafe {
      asm!(
        "in {reg_sphval},{sphregio}",
        "in {reg_splval},{splregio}",
        reg_sphval = out(reg) sphval,
        reg_splval = out(reg) splval,
        sphregio = const(avr_oxide::hardware::cpu::cpuregs::IOADR_SPH),
        splregio = const(avr_oxide::hardware::cpu::cpuregs::IOADR_SPL)
      );
    }

    ((sphval as u16) << 8) | (splval as u16)
  }

  /**
   * Write the stack pointer.
   *
   * Note that it is critical this function is inlined if it is to read the
   * SP value you expect :-).
   */
  #[inline(always)]
  unsafe fn write_sp(&mut self, sp: u16) {
    let sphval:u8 = ((sp & 0xff00) >> 8) as u8;
    let splval:u8 = (sp & 0x00ff) as u8;

    asm!(
      "out {sphregio},{reg_sphval}",
      "out {splregio},{reg_splval}",
      reg_sphval = in(reg) sphval,
      reg_splval = in(reg) splval,
      sphregio = const(avr_oxide::hardware::cpu::cpuregs::IOADR_SPH),
      splregio = const(avr_oxide::hardware::cpu::cpuregs::IOADR_SPL)
    );
  }

  /**
   * Read the current status register
   */
  #[inline(always)]
  fn read_sreg(&self) -> u8 {
    unsafe {
      IOREG_SREG.read_byte()
    }
  }

  /**
   * Write the status register
   */
  #[inline(always)]
  fn write_sreg(&mut self, sregval: u8) {
    unsafe {
      IOREG_SREG.write_byte(sregval);
    }
  }

  /**
   * Return true iff interrupts are enabled
   */
  #[inline(always)]
  fn interrupts_enabled(&self) -> bool {
    unsafe {
      IOREG_SREG.is_set(BitIndex::bit_c(7))
    }
  }

  /**
   * Return true iff we are inside an interrupt service routine
   */
  #[inline(never)]
  fn in_isr(&self) -> bool {
    unsafe {
      IOREG_CONTEXT_FLAGS.is_set_c::<{avr_oxide::hal::generic::cpu::CONTEXT_FLAG_INISR}>()
    }
  }
}

#[cfg(not(target_arch="avr"))]
pub trait Cpu {
  /**
   * Write to a protected register.
   */
  unsafe fn write_protected(&mut self, change: ConfigurationChange,
                            register: &mut u8, value: u8 );

  /**
   * Read the current stack pointer.
   *
   * Note that it is critical this function is inlined if it is to read the
   * SP value you expect :-).
   */
  #[inline(always)]
  fn read_sp(&self) -> u16 {
    unimplemented!()
  }

  /**
   * Write the stack pointer.
   *
   * Note that it is critical this function is inlined if it is to read the
   * SP value you expect :-).
   */
  #[inline(always)]
  unsafe fn write_sp(&mut self, sp: u16) {
    unimplemented!()
  }

  /**
   * Read the current status register
   */
  #[inline(always)]
  fn read_sreg(&self) -> u8 {
    unimplemented!()
  }

  /**
   * Write the status register
   */
  #[inline(always)]
  unsafe fn write_sreg(&mut self, sregval: u8) {
    unimplemented!()
  }

  /**
   * Return true iff interrupts are enabled
   */
  #[inline(always)]
  fn interrupts_enabled(&self) -> bool {
    unimplemented!()
  }

  /**
   * Return true iff we are inside an interrupt service routine
   */
  #[inline(always)]
  fn in_isr(&self) -> bool {
    false
  }

}



pub trait ClockControl {
  /**
   * Configure the peripheral clock prescaler
   */
  unsafe fn clk_per_prescaler(&mut self, scaler: u8);
}

pub(crate) mod private {
  /// Token returned by [`SleepControl::inhibit_standby()`] that entitles
  /// the caller to call [`SleepControl::permit_standby()`].
  pub struct PermitStandbyToken;

  /// Token returned by [`SleepControl::inhibit_idle()`] that entitles
  /// the caller to call [`SleepControl::permit_idle()`].
  pub struct PermitIdleToken;
}


pub trait SleepControl {
  /**
   * Reset the sleep mode to default state.  Both Idle and Standby mode will
   * be uninhibited.
   */
  unsafe fn reset(&mut self);

  /**
   * Prevent the CPU going into Standby mode.  The CPU will not be allowed
   * to Standby until a corresponding call to `permit_standby()` is made.
   *
   * Calls may be nested, but up to a hard limit of 256.
   *
   * # Panics
   * Will panic if more than 256 inhibit_sleep() calls are made without
   * corresponding permit_standby().
   */
  fn inhibit_standby(&mut self) -> private::PermitStandbyToken;

  /**
   * Permit the CPU to enter Standby mode, if it was previously inhibited
   * by `inhibit_standby()`.
   *
   * If stanndby was already permitted, this call does nothing.
   */
  fn permit_standby(&mut self, token: private::PermitStandbyToken);

  /**
   * Return true iff the CPU is currently permitted to Standby.
   */
  fn standby_permitted(&self) -> bool;

  /**
   * Prevent the CPU going into Idle mode.  The CPU will not be allowed
   * to Idle until a corresponding call to `permit_idle()` is made.
   *
   * Calls may be nested, but up to a hard limit of 256.
   *
   * # Panics
   * Will panic if more than 256 inhibit_idle() calls are made without
   * corresponding permit_idle().
   */
  fn inhibit_idle(&mut self) -> private::PermitIdleToken;

  /**
   * Permit the CPU to enter Idle mode, if it was previously inhibited
   * by `inhibit_idle()`.
   *
   * If sleep was already permitted, this call does nothing.
   */
  fn permit_idle(&mut self, token: private::PermitIdleToken);

  /**
   * Return true iff the CPU is currently permitted to Idle.
   */
  fn idle_permitted(&self) -> bool;

}

// Code ======================================================================
#[cfg(target_arch="avr")]
pub mod base {
  use avr_oxide::hal::generic::cpu::{ConfigurationChange, Cpu, ClockControl, SleepControl, private::PermitIdleToken, private::PermitStandbyToken};
  use avr_oxide::util::datatypes::Volatile;

  #[repr(C)]
  pub struct AvrCpuControlBlock {
    reserved_0: [u8; 4],
    pub(crate) ccp: u8,
    reserved_1: [u8; 8],
    pub(crate) sp: u16,
    pub(crate) sreg: u8
  }

  #[repr(C)]
  pub struct AvrClockControlBlock {
    pub(crate) mclkctrla: u8,
    pub(crate) mclkctrlb: u8,
    pub(crate) mclklock: u8,
    pub(crate) mclkstatus: u8,
    reserved_0: [u8; 12],
    pub(crate) osc20mctrla: u8,
    pub(crate) osc20mcaliba: u8,
    pub(crate) osc20mcalibb: u8,
    reserved_1: [u8; 5],
    pub(crate) osc32kctrla: u8,
    reserved_2: [u8; 3],
    pub(crate) xosc32kctrla: u8
  }

  #[repr(C)]
  pub struct AvrSleepControlBlock {
    pub(crate) ctrla: Volatile<u8>
  }



  pub struct AvrSleepController {
    pub(crate) scb: &'static mut AvrSleepControlBlock,
    pub(crate) idle_inhibits: Volatile<u8>,
    pub(crate) sleep_inhibits: Volatile<u8>
  }

  // Provided by `boot.S`
  extern "C" {
    //fn ccp_write_io(ioaddr: *mut u8, value: u8);
    fn ccp_io_write(ioaddr: *mut u8, value: u8);
    fn ccp_spm_write(ioaddr: *mut u8, value: u8);
  }

  impl Cpu for AvrCpuControlBlock {
    unsafe fn write_protected(&mut self, change: ConfigurationChange, register: &mut u8, value: u8) {
      match change {
        ConfigurationChange::SelfProgramming =>
          ccp_spm_write(register as *mut u8, value),
        ConfigurationChange::ProtectedRegister =>
          ccp_io_write(register as *mut u8, value),
      }
    }
  }

  impl ClockControl for AvrClockControlBlock {
    unsafe fn clk_per_prescaler(&mut self, scaler: u8) {
      if scaler == 0 {
        ccp_io_write(&mut self.mclkctrlb as *mut u8, 0x00);
      } else {
        let pdiv_pen_val = match scaler {
          1  => 0x00,
          2  => (0x00 << 1) | 0x01,
          4  => (0x01 << 1) | 0x01,
          8  => (0x02 << 1) | 0x01,
          16 => (0x03 << 1) | 0x01,
          32 => (0x04 << 1) | 0x01,
          64 => (0x05 << 1) | 0x01,
          6  => (0x08 << 1) | 0x01,
          10 => (0x09 << 1) | 0x01,
          12 => (0x0A << 1) | 0x01,
          24 => (0x0B << 1) | 0x01,
          48 => (0x0C << 1) | 0x01,
          _ => avr_oxide::oserror::halt(avr_oxide::oserror::OsError::BadParams)
        };
        ccp_io_write(&mut self.mclkctrlb as *mut u8, pdiv_pen_val);
      }
    }
  }

  impl AvrSleepController {
    /// Set the hardware `SLPCTRL.ctrla` field according to what is
    /// currently permitted by our inhibit flags
    fn set_sleep_state(&mut self) {
      self.scb.ctrla.write(match (self.standby_permitted(), self.idle_permitted()) {
        (true, true)  => 0b00000011, // Standby permitted
        (false, true) => 0b00000001, // Idle permitted
        _             => 0b00000000  // No sleep permitted
      });
      // Worth noting, if by request Standby is permitted but Idle is
      // inhibited, we inhibit all sleep (since Standby is Idle++)
    }
  }

  impl SleepControl for AvrSleepController {
    unsafe fn reset(&mut self) {
      self.sleep_inhibits = 0.into();
      self.idle_inhibits = 0.into();
      self.set_sleep_state();
    }

    fn inhibit_standby(&mut self) -> PermitStandbyToken {
      if self.sleep_inhibits == u8::MAX {
        avr_oxide::oserror::halt(avr_oxide::oserror::OsError::NestedInhibitTooDeep);
      }
      self.sleep_inhibits += 1;
      self.set_sleep_state();

      PermitStandbyToken
    }

    fn permit_standby(&mut self, _token: PermitStandbyToken) {
      if self.sleep_inhibits != 0 {
        self.sleep_inhibits -= 1;
      }
      self.set_sleep_state();
    }

    fn standby_permitted(&self) -> bool {
      self.sleep_inhibits == 0
    }

    fn inhibit_idle(&mut self) -> PermitIdleToken {
      if self.idle_inhibits == u8::MAX {
        avr_oxide::oserror::halt(avr_oxide::oserror::OsError::NestedInhibitTooDeep);
      }
      self.idle_inhibits += 1;
      self.set_sleep_state();

      PermitIdleToken
    }

    fn permit_idle(&mut self, _token: PermitIdleToken) {
      if self.idle_inhibits != 0 {
        self.idle_inhibits -= 1;
      }
      self.set_sleep_state();
    }

    fn idle_permitted(&self) -> bool {
      self.idle_inhibits == 0
    }
  }

  #[doc(hidden)]
  #[macro_export]
  macro_rules! atmel_cpu_tpl {
    ($cputype:ty, $cpuref:expr,$clkref:expr,$slpref:expr) => {
      use avr_oxide::hal::generic::cpu::base::{AvrClockControlBlock,AvrSleepController};
      use avr_oxide::mut_singleton;
      pub type CpuImpl = $cputype;
      pub type ClockImpl = AvrClockControlBlock;
      pub type SleepImpl = AvrSleepController;



      pub fn instance() -> &'static mut CpuImpl {
        unsafe {
          core::mem::transmute($cpuref)
        }
      }
      pub fn clock() -> &'static mut ClockImpl {
        unsafe {
          core::mem::transmute($clkref)
        }
      }

      mut_singleton!(
        SleepImpl,
        __SLEEPCTRLINSTANCE,
        sleepctrl, sleepctrl_isolated,
        AvrSleepController {
          scb: core::mem::transmute($slpref),
          idle_inhibits: 0.into(),
          sleep_inhibits: 0.into()
        });
    }
  }
}

// Dummy Implementations =====================================================
#[cfg(not(target_arch="avr"))]
pub mod base {
  //! Dummy implementation of the CPU interface for running unit tests
  //! of Oxide applications on the developer-environment architecture rather
  //! than AVR.  Unlike the 'true' implementations these use std:: functions
  //! like println!() to print to the terminal.
  use avr_oxide::hal::generic::cpu::{ConfigurationChange, Cpu, ClockControl, SleepControl};
  use avr_oxide::hal::generic::cpu::private::{PermitIdleToken, PermitStandbyToken};

  pub struct AvrCpuControlBlock {
    pub(crate) sreg: u8
  }

  pub struct DummyClockControl {}



  impl Cpu for AvrCpuControlBlock {
    unsafe fn write_protected(&mut self, _change: ConfigurationChange, register: &mut u8, value: u8) {
      println!("*** CPU: Protected register write: @{} <- {}", register, value);
    }

    fn read_sp(&self) -> u16 {
      unimplemented!()
    }

    unsafe fn write_sp(&mut self, _sp: u16) {
      unimplemented!()
    }

    fn read_sreg(&self) -> u8 {
      println!("*** CPU: Read SREG: {}", self.sreg);
      self.sreg
    }

    unsafe fn write_sreg(&mut self, sreg: u8) {
      println!("*** CPU: Write SREG: {}", sreg);
      self.sreg = sreg;
    }

    fn interrupts_enabled(&self) -> bool {
      true
    }
  }

  impl ClockControl for DummyClockControl {
    unsafe fn clk_per_prescaler(&mut self, scaler: u8) {
      println!("*** CPU: Set clock prescaler to: {}", scaler);
    }
  }

  pub struct DummySleepControl {}

  impl SleepControl for DummySleepControl {
    unsafe fn reset(&mut self) {
      unimplemented!()
    }

    fn inhibit_standby(&mut self) -> PermitStandbyToken {
      unimplemented!()
    }

    fn permit_standby(&mut self, token: PermitStandbyToken) {
      unimplemented!()
    }

    fn standby_permitted(&self) -> bool {
      unimplemented!()
    }

    fn inhibit_idle(&mut self) -> PermitIdleToken {
      unimplemented!()
    }

    fn permit_idle(&mut self, token: PermitIdleToken) {
      unimplemented!()
    }

    fn idle_permitted(&self) -> bool {
      unimplemented!()
    }
  }


  #[doc(hidden)]
  #[macro_export]
  macro_rules! atmel_cpu_tpl {
    ($cputype:ty, $cpuref:expr,$clkref:expr,$sleepref:expr) => {
      use avr_oxide::hal::generic::cpu::base::{AvrCpuControlBlock,DummyClockControl,DummySleepControl};
      use avr_oxide::mut_singleton;

      pub type CpuImpl   = AvrCpuControlBlock;
      pub type ClockImpl = DummyClockControl;
      pub type SleepImpl = DummySleepControl;

      static mut DUMMY_CPU: AvrCpuControlBlock = AvrCpuControlBlock {
        sreg: 0
      };
      static mut DUMMY_CLOCK: DummyClockControl = DummyClockControl {};

      #[inline(always)]
      pub fn instance() -> &'static mut CpuImpl {
        unsafe {
          &mut DUMMY_CPU
        }
      }
      #[inline(always)]
      pub fn clock() -> &'static mut DummyClockControl {
        unsafe {
          &mut DUMMY_CLOCK
        }
      }

      mut_singleton!(
        SleepImpl,
        __SLEEPCTRLINSTANCE,
        sleepctrl, sleepctrl_isolated,
        DummySleepControl {
        });

    }
  }
}
// Tests =====================================================================