// Zinc, the bare metal stack for rust. // Copyright 2014 Vladimir "farcaller" Pouzanov // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! A cell that with volatile setter and getter. #![feature(core_intrinsics)] #![no_std] #[cfg(feature="replayer")] #[macro_use(expect)] extern crate expectest; #[cfg(feature="replayer")] #[macro_use] extern crate std; #[cfg(feature="replayer")] use std::vec::Vec; #[cfg(feature="replayer")] use expectest::prelude::*; #[cfg(feature="replayer")] use expectest::core::Matcher; #[cfg(feature="replayer")] use std::string::String; #[cfg(feature="replayer")] use std::fmt; #[cfg(feature="replayer")] use core::cmp::PartialEq; #[cfg(feature="replayer")] use core::clone::Clone; #[cfg(feature="replayer")] use core::cell::RefCell; #[cfg(not(feature="replayer"))] use core::intrinsics::{volatile_load, volatile_store}; #[cfg(feature="replayer")] use core::intrinsics::transmute; // TODO(farcaller): why this needs copy/clone? /// This structure is used to represent a hardware register. /// It is mostly used by the ioreg family of macros. #[derive(Copy, Clone)] #[repr(C)] pub struct VolatileCell { value: T, } impl VolatileCell { /// Create a cell with initial value. pub fn new(value: T) -> VolatileCell { VolatileCell { value: value, } } /// Get register value. #[cfg(not(feature="replayer"))] #[inline] pub fn get(&self) -> T { unsafe { volatile_load(&self.value) } } /// Set register value. #[cfg(not(feature="replayer"))] #[inline] pub fn set(&self, value: T) { unsafe { volatile_store(&self.value as *const T as *mut T, value) } } } #[cfg(feature="replayer")] impl VolatileCell { pub fn get(&self) -> u32 { unsafe { GLOBAL_REPLAYER.with(|gr| { gr.borrow_mut().get_cell(transmute(&self.value)) }) } } pub fn set(&self, value: u32) { unsafe { GLOBAL_REPLAYER.with(|gr| { gr.borrow_mut().set_cell(transmute(&self.value), value) }) } } } #[cfg(feature="replayer")] impl VolatileCell { pub fn get(&self) -> u16 { unsafe { GLOBAL_REPLAYER.with(|gr| { gr.borrow_mut().get_cell(transmute(&self.value)) }) as u16 } } pub fn set(&self, value: u16) { unsafe { GLOBAL_REPLAYER.with(|gr| { gr.borrow_mut().set_cell(transmute(&self.value), value as u32) }) } } } #[cfg(feature="replayer")] impl VolatileCell { pub fn get(&self) -> u8 { unsafe { GLOBAL_REPLAYER.with(|gr| { gr.borrow_mut().get_cell(transmute(&self.value)) }) as u8 } } pub fn set(&self, value: u8) { unsafe { GLOBAL_REPLAYER.with(|gr| { gr.borrow_mut().set_cell(transmute(&self.value), value as u32) }) } } } #[cfg(feature="replayer")] struct ReplayRecord { is_read: bool, address: usize, value: u32, replayed: bool, did_read: bool, actual_address: usize, actual_value: u32, loc: expectest::core::SourceLocation, } #[cfg(feature="replayer")] impl core::fmt::Display for ReplayRecord { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self.is_read { true => write!(f, "read 0x{:x} from 0x{:x}", self.value, self.address), false => write!(f, "write 0x{:x} to 0x{:x}", self.value, self.address), } } } #[cfg(feature="replayer")] pub struct VolatileCellReplayer { replays: Vec, current_replay: usize, } #[cfg(feature="replayer")] impl VolatileCellReplayer { pub fn new() -> VolatileCellReplayer { VolatileCellReplayer { replays: Vec::new(), current_replay: 0, } } pub fn expect_read(&mut self, address: usize, value: u32, loc: expectest::core::SourceLocation) { self.replays.push(ReplayRecord { is_read: true, address: address, value: value, replayed: false, did_read: false, actual_address: 0, actual_value: 0, loc: loc, }); } pub fn expect_write(&mut self, address: usize, value: u32, loc: expectest::core::SourceLocation) { self.replays.push(ReplayRecord { is_read: false, address: address, value: value, replayed: false, did_read: false, actual_address: 0, actual_value: 0, loc: loc, }); } pub fn verify(&self, loc: expectest::core::SourceLocation) { expect(self.current_replay).location(loc).to( be_equal_to_with_context( self.replays.len(), format!("expected {} replays, performed {}", self.replays.len(), self.current_replay))); for ref replay in &*self.replays { expect(replay.replayed).location(replay.loc).to(be_equal_to_with_context(true, format!("expected replay {} to be performed, was not", replay))); expect(replay.is_read).location(replay.loc).to(be_equal_to_with_context(replay.did_read, format!("expected replay to be {} replay, was {} replay", if replay.is_read {"read"} else {"write"}, if replay.is_read {"write"} else {"read"}))); expect(replay.address).location(replay.loc).to(be_equal_to_with_context(replay.actual_address, format!("expected replay address 0x{:x}, was 0x{:x}", replay.address, replay.actual_address))); if !replay.is_read { expect(replay.value).location(replay.loc).to(be_equal_to_with_context(replay.actual_value, format!("expected replay to write 0x{:x}, written 0x{:x}", replay.value, replay.actual_value))); } } } pub fn get_cell(&mut self, address: usize) -> u32 { if self.current_replay >= self.replays.len() { panic!("get_cell(0x{:x}) faled, current replay: {}, total replays: {}", address, self.current_replay+1, self.replays.len()); } let replay: &mut ReplayRecord = &mut self.replays[self.current_replay]; replay.replayed = true; replay.did_read = true; replay.actual_address = address; self.current_replay += 1; replay.value } pub fn set_cell(&mut self, address: usize, value: u32) { if self.current_replay >= self.replays.len() { panic!("set_cell(0x{:x}, 0x{:x}) faled, current replay: {}, total replays: {}", address, value, self.current_replay+1, self.replays.len()); } let replay: &mut ReplayRecord = &mut self.replays[self.current_replay]; replay.replayed = true; replay.did_read = false; replay.actual_address = address; replay.actual_value = value; self.current_replay += 1; } } #[cfg(feature="replayer")] thread_local!(static GLOBAL_REPLAYER: RefCell = RefCell::new(VolatileCellReplayer::new())); #[cfg(feature="replayer")] pub fn set_replayer(replayer: VolatileCellReplayer) { GLOBAL_REPLAYER.with(|gr| { let mut bm = gr.borrow_mut(); *bm = replayer; }); } #[cfg(feature="replayer")] pub fn with_mut_replayer(f: F) where F: core::ops::FnOnce(&mut VolatileCellReplayer) { GLOBAL_REPLAYER.with(|gr| { let mut bm = gr.borrow_mut(); f(&mut *bm); }); } #[cfg(feature="replayer")] struct BeEqualToWithContext { expected: E, context: String, } #[cfg(feature="replayer")] fn be_equal_to_with_context(expected: E, context: String) -> BeEqualToWithContext { BeEqualToWithContext { expected: expected, context: context, } } #[cfg(feature="replayer")] impl Matcher for BeEqualToWithContext where A: PartialEq + fmt::Debug, E: fmt::Debug { fn failure_message(&self, _: expectest::core::Join, _: &A) -> String { self.context.clone() } fn matches(&self, actual: &A) -> bool { *actual == self.expected } } #[macro_export] macro_rules! expect_volatile_read { ($addr: expr, $val: expr) => ( $crate::with_mut_replayer(|r| { r.expect_read($addr, $val, expectest::core::SourceLocation::new(file!(), line!())); }) ); } #[macro_export] macro_rules! expect_volatile_write { ($addr: expr, $val: expr) => ( $crate::with_mut_replayer(|r| { r.expect_write($addr, $val, expectest::core::SourceLocation::new(file!(), line!())); }) ); } #[macro_export] macro_rules! expect_replayer_valid { () => ( $crate::with_mut_replayer(|r| { r.verify(expectest::core::SourceLocation::new(file!(), line!())); }) ); } #[macro_export] macro_rules! init_replayer { () => ( set_replayer(VolatileCellReplayer::new()); ); }