Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign up
Find file
Copy path
Fetching contributors…
| // Zinc, the bare metal stack for rust. | |
| // Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com> | |
| // | |
| // 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<T> where T: Copy { | |
| value: T, | |
| } | |
| impl<T: Copy> VolatileCell<T> { | |
| /// Create a cell with initial value. | |
| pub fn new(value: T) -> VolatileCell<T> { | |
| 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<u32> { | |
| 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<u16> { | |
| 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<u8> { | |
| 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<ReplayRecord>, | |
| 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<VolatileCellReplayer> = 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: 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<E> { | |
| expected: E, | |
| context: String, | |
| } | |
| #[cfg(feature="replayer")] | |
| fn be_equal_to_with_context<E>(expected: E, context: String) -> BeEqualToWithContext<E> { | |
| BeEqualToWithContext { | |
| expected: expected, | |
| context: context, | |
| } | |
| } | |
| #[cfg(feature="replayer")] | |
| impl<A, E> Matcher<A, E> for BeEqualToWithContext<E> | |
| where | |
| A: PartialEq<E> + 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()); | |
| ); | |
| } |