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};
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;
pub enum ConfigurationChange {
SelfProgramming,
ProtectedRegister
}
#[repr(C)]
#[derive(Clone)]
pub(crate) struct ProcessorContext {
pub(crate) sreg: BitField, pub(crate) gpregs: [u8; 32], pub(crate) pc: u16, pub(crate) sp: u16, pub(crate) tid: ThreadId, #[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
}
pub(crate) trait ContextSaveRestore {
unsafe fn get_processor_context(&self, isotoken: interrupt::token::Isolated) -> &mut ProcessorContext;
fn was_thread_restored(&self) -> bool;
}
#[cfg(target_arch="avr")]
pub trait Cpu {
unsafe fn write_protected(&mut self, change: ConfigurationChange,
register: &mut u8, value: u8 );
#[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)
}
#[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)
);
}
#[inline(always)]
fn read_sreg(&self) -> u8 {
unsafe {
IOREG_SREG.read_byte()
}
}
#[inline(always)]
fn write_sreg(&mut self, sregval: u8) {
unsafe {
IOREG_SREG.write_byte(sregval);
}
}
#[inline(always)]
fn interrupts_enabled(&self) -> bool {
unsafe {
IOREG_SREG.is_set(BitIndex::bit_c(7))
}
}
#[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 {
unsafe fn write_protected(&mut self, change: ConfigurationChange,
register: &mut u8, value: u8 );
#[inline(always)]
fn read_sp(&self) -> u16 {
unimplemented!()
}
#[inline(always)]
unsafe fn write_sp(&mut self, sp: u16) {
unimplemented!()
}
#[inline(always)]
fn read_sreg(&self) -> u8 {
unimplemented!()
}
#[inline(always)]
unsafe fn write_sreg(&mut self, sregval: u8) {
unimplemented!()
}
#[inline(always)]
fn interrupts_enabled(&self) -> bool {
unimplemented!()
}
#[inline(always)]
fn in_isr(&self) -> bool {
false
}
}
pub trait ClockControl {
unsafe fn clk_per_prescaler(&mut self, scaler: u8);
}
pub(crate) mod private {
pub struct PermitStandbyToken;
pub struct PermitIdleToken;
}
pub trait SleepControl {
unsafe fn reset(&mut self);
fn inhibit_standby(&mut self) -> private::PermitStandbyToken;
fn permit_standby(&mut self, token: private::PermitStandbyToken);
fn standby_permitted(&self) -> bool;
fn inhibit_idle(&mut self) -> private::PermitIdleToken;
fn permit_idle(&mut self, token: private::PermitIdleToken);
fn idle_permitted(&self) -> bool;
}
#[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>
}
extern "C" {
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 {
fn set_sleep_state(&mut self) {
self.scb.ctrla.write(match (self.standby_permitted(), self.idle_permitted()) {
(true, true) => 0b00000011, (false, true) => 0b00000001, _ => 0b00000000 });
}
}
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()
});
}
}
}
#[cfg(not(target_arch="avr"))]
pub mod base {
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 {
});
}
}
}