avr-oxide 0.3.1

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
 */
//! ATmega328P-specific CPU control block implementation

// Imports ===================================================================

use avr_oxide::hal::generic::cpu::private::{PermitIdleToken, PermitStandbyToken};
use avr_oxide::hal::generic::cpu::{ClockControl, ConfigurationChange, ContextSaveRestore, Cpu, SleepControl, ProcessorContext};
use avr_oxide::mut_singleton;
use avr_oxide::concurrency::interrupt;
use avr_oxide::hal::atmega328p::{ADDR_CLKPR, ADDR_CPU};
use avr_oxide::util::datatypes::{BitField, BitFieldAccess, BitIndex, Volatile};

// Declarations ==============================================================
pub mod cpuregs {
  use avr_oxide::hal::generic::datatypes::IOSpaceU8;

  pub const MAX_BIT_ADDRESSABLE: u8 = 0x1F;

  pub const IOADR_SPL:  u8  = 0x3D;
  pub const IOADR_SPH:  u8  = 0x3E;
  pub const IOADR_SREG: u8 = 0x3F;
  pub const RAMTOP: u16 = 0x08FF;

  pub const ADDR_CURRENT_CONTEXT_LO:  usize = 0x004A;
  pub const ADDR_CURRENT_CONTEXT_HI:  usize = 0x004B;
  pub const IOADR_CONTEXT_FLAGS:      u8 = 0x1E;

  pub(crate) static mut IOREG_SREG          : IOSpaceU8<IOADR_SREG> = IOSpaceU8::get();
  pub(crate) static mut IOREG_CONTEXT_FLAGS : IOSpaceU8<IOADR_CONTEXT_FLAGS> = IOSpaceU8::get();
}

#[repr(C)]
pub struct Atmega328PControlRegisters {
  pub(crate) sp: u16,
  pub(crate) sreg: u8
}

#[repr(C)]
pub struct Atmega328PClockPrescaleRegister {
  pub(crate) clkpr: u8
}

pub struct NullSleepController;


// Code ======================================================================
impl Cpu for Atmega328PControlRegisters {
  unsafe fn write_protected(&mut self, change: ConfigurationChange, register: &mut u8, value: u8) {
    // The 328P doesn't have a register protection mechanism, so we JFDI
    *register = value;
  }
}

impl ContextSaveRestore for Atmega328PControlRegisters {
  unsafe fn get_processor_context(&self, _isotoken: interrupt::token::Isolated) -> &mut ProcessorContext {
    let acc_lo : *mut u8 = core::mem::transmute(cpuregs::ADDR_CURRENT_CONTEXT_LO);
    let acc_hi : *mut u8 = core::mem::transmute(cpuregs::ADDR_CURRENT_CONTEXT_HI);

    let context_addr =
      ((core::ptr::read_volatile(acc_hi) as usize) << 8) |
        (core::ptr::read_volatile(acc_lo) as usize);

    &mut *(context_addr as *mut ProcessorContext)
  }

  #[inline(always)]
  fn was_thread_restored(&self) -> bool {
    unsafe {
      cpuregs::IOREG_CONTEXT_FLAGS.is_set_c::<0>()
    }
  }
}

#[inline(always)]
pub fn instance() -> &'static mut Atmega328PControlRegisters {
  unsafe {
    core::mem::transmute(ADDR_CPU)
  }
}

pub fn clock() -> &'static mut Atmega328PClockPrescaleRegister {
  unsafe {
    core::mem::transmute(ADDR_CLKPR)
  }
}

impl ClockControl for Atmega328PClockPrescaleRegister {
  unsafe fn clk_per_prescaler(&mut self, scaler: u8) {
    extern "C" {
      fn ccp_clkper_write(ioaddr: *mut u8, value: u8);
    }

    let clkps_val = match scaler {
      1 => 0x00,
      2 => 0x01,
      4 => 0x02,
      8 => 0x03,
      16 => 0x04,
      32 => 0x05,
      64 => 0x06,
      128 => 0x07,
      _ => panic!()
    };
    ccp_clkper_write(&mut self.clkpr as *mut u8, clkps_val);
  }
}


#[macro_export]
macro_rules! cpu {
  () => {
    avr_oxide::hal::atmega328p::cpu::instance()
  }
}


impl SleepControl for NullSleepController {
  unsafe fn reset(&mut self) {
  }

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

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

  fn standby_permitted(&self) -> bool {
    false
  }

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

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

  fn idle_permitted(&self) -> bool {
    false
  }
}

mut_singleton!(
  NullSleepController,
  __SLEEPCTRLINSTANCE,
  sleepctrl, sleepctrl_isolated,
  NullSleepController {
  });

/**
 * Obtain a references to the Sleep controller instance
 */
#[macro_export]
macro_rules! sleepctrl {
  ($isotoken:expr) => {
    avr_oxide::hal::atmega328p::cpu::sleepctrl_isolated($isotoken)
  };
  () => {
    avr_oxide::hal::atmega328p::cpu::sleepctrl()
  };
}