avr-oxide 0.2.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
 */
//! CPU control flags/registers.
//!
//!

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

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

  /// Protected register access
  ProtectedRegister
}


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
   */
  fn read_sp(&self) -> u16;

  /**
   * Write the stack pointer.
   */
  unsafe fn write_sp(&mut self, sp: u16);

  /**
   * Read the current status register
   */
  fn read_sreg(&self) -> u8;

  /**
   * Write the status register
   */
  unsafe fn write_sreg(&mut self, sp: u8);

  /**
   * Return true iff interrupts are enabled
   */
  fn interrupts_enabled(&self) -> bool;
}

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

// Code ======================================================================
#[cfg(target_arch="avr")]
pub mod base {
  use avr_oxide::{v_read,v_write};
  use avr_oxide::hal::generic::cpu::{ConfigurationChange, Cpu, ClockControl};

  #[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
  }

  #[allow(dead_code)]
  pub struct AvrClockControl {
    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
  }

  // 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),
      }
    }

    #[inline(always)]
    fn read_sp(&self) -> u16 {
      unsafe {
        v_read!(u16, self.sp)
      }
    }

    #[inline(always)]
    unsafe fn write_sp(&mut self, sp: u16) {
      v_write!(u16, self.sp, sp)
    }

    #[inline(always)]
    fn read_sreg(&self) -> u8 {
      unsafe {
        v_read!(u8, self.sreg)
      }
    }

    #[inline(always)]
    unsafe fn write_sreg(&mut self, sreg:u8) {
      v_write!(u8, self.sreg, sreg);
    }

    #[inline(always)]
    fn interrupts_enabled(&self) -> bool {
      unsafe {
        (v_read!(u8, self.sreg) & 0b10000000) > 0
      }
    }
  }

  impl ClockControl for AvrClockControl {
    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_val = match scaler {
          2  => 0x00,
          4  => 0x01,
          8  => 0x02,
          16 => 0x03,
          32 => 0x04,
          64 => 0x05,
          6  => 0x08,
          10 => 0x09,
          12 => 0x0A,
          24 => 0x0B,
          48 => 0x0C,
          _ => panic!()
        };
        ccp_io_write(&mut self.mclkctrlb as *mut u8, (pdiv_val << 1) | 0x01);
      }
    }
  }

  #[doc(hidden)]
  #[macro_export]
  macro_rules! atmel_cpu_tpl {
    ($cpuref:expr,$clkref:expr) => {
      use avr_oxide::hal::generic::cpu::base::{AvrCpuControlBlock,AvrClockControl};
      pub type CpuImpl = AvrCpuControlBlock;

      #[inline(always)]
      pub fn instance() -> &'static mut AvrCpuControlBlock {
        unsafe {
          core::mem::transmute($cpuref)
        }
      }
      #[inline(always)]
      pub fn clock() -> &'static mut AvrClockControl {
        unsafe {
          core::mem::transmute($clkref)
        }
      }
    }
  }
}

// Dummy Implementations =====================================================
#[cfg(not(target_arch="avr"))]
pub mod dummy {
  //! 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};

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

  pub struct DummyClockControl {}

  impl Cpu for DummyCpuControlBlock {
    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);
    }
  }

  #[doc(hidden)]
  #[macro_export]
  macro_rules! atmel_cpu_tpl {
    ($cpuref:expr,$clkref:expr) => {
      use avr_oxide::hal::generic::cpu::dummy::{DummyCpuControlBlock,DummyClockControl};
      pub type CpuImpl = DummyCpuControlBlock;

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

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