#[cfg(not(emulator_absent))]
mod exception;
#[cfg(not(emulator_absent))]
pub(crate) mod family;
#[cfg(not(emulator_absent))]
mod fp;
#[cfg(not(emulator_absent))]
mod itm;
#[cfg(not(emulator_absent))]
mod mpu;
#[cfg_attr(
emulator_absent,
expect(
dead_code,
reason = "only the core uses most of it, and Miri builds the crate without one"
)
)]
pub(crate) mod registers;
#[cfg_attr(
all(emulator_absent, not(test)),
expect(
dead_code,
reason = "only the core uses it, and Miri builds the crate without one"
)
)]
pub(crate) mod scs;
#[cfg(not(emulator_absent))]
mod semihosting;
#[cfg(not(emulator_absent))]
mod state;
#[cfg(not(emulator_absent))]
mod translate;
#[cfg(fuzzing)]
pub(crate) use translate::testing;
use crate::device::{Device, DeviceContext, RegisterFile};
use crate::emulator::{Error, Processor};
use crate::machine::armv7m::Config;
use crate::soc::{
Access, Endian, FieldSpec, Layout, Peripheral, ReadAction, RegisterSpec, Width, WriteModify,
};
use crate::time::VirtualNanos;
#[cfg(not(emulator_absent))]
pub fn processor(config: &Config, endian: Endian) -> Result<Box<dyn Processor>, Error> {
Ok(family::Family::new(config, endian)?)
}
#[cfg(emulator_absent)]
pub fn processor(config: &Config, endian: Endian) -> Result<Box<dyn Processor>, Error> {
let _ = (config, endian);
Err(Error::NoEmulator)
}
#[must_use]
pub fn code_address(value: u64) -> u64 {
value & !1
}
pub const DWT: &str = "DWT";
pub(crate) const PPB_BASE: u64 = 0xE000_0000;
pub(crate) const PPB_SIZE: u64 = 0x0010_0000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Window {
pub base: u64,
pub size: u64,
}
#[must_use]
pub fn windows(bus: &crate::soc::Bus) -> Vec<Window> {
let mut windows: Vec<Window> = bus
.peripherals()
.filter(|peripheral| peripheral.base >= PPB_BASE && peripheral.base < PPB_BASE + PPB_SIZE)
.map(|peripheral| Window {
base: peripheral.base,
size: peripheral.size,
})
.collect();
windows.sort();
windows
}
pub const DWT_BASE: u64 = 0xE000_1000;
pub const SCS_BASE: u64 = scs::SCS_BASE;
pub const FIRST_INTERRUPT: u16 = scs::exception::FIRST_INTERRUPT;
pub const PERIPHERAL: Peripheral = Peripheral {
name: DWT,
base: DWT_BASE,
size: 0x8,
interrupts: &[],
registers: ®ISTERS,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Register {
Ctrl = 0,
Cyccnt = 1,
}
impl Register {
pub const ALL: [Self; 2] = [Self::Ctrl, Self::Cyccnt];
}
impl Layout for Register {
const REGISTERS: &'static [RegisterSpec] = ®ISTERS;
fn from_index(index: usize) -> Option<Self> {
Self::ALL.get(index).copied()
}
fn index(self) -> usize {
self as usize
}
}
pub mod dwt_ctrl {
use super::FieldSpec;
pub const CYCCNTENA: &FieldSpec = &super::REGISTERS[0].fields[0];
pub const NOCYCCNT: &FieldSpec = &super::REGISTERS[0].fields[1];
}
pub mod dwt_cyccnt {
use super::FieldSpec;
pub const CYCCNT: &FieldSpec = &super::REGISTERS[1].fields[0];
}
pub static REGISTERS: [RegisterSpec; 2] = [
RegisterSpec {
name: "DWT_CTRL",
offset: 0x0,
width: Width::W32,
reset: 0x4000_0000,
access: Access::ReadWrite,
read: ReadAction::None,
write: WriteModify::None,
fields: &[
FieldSpec {
name: "CYCCNTENA",
bit: 0,
width: 1,
access: Access::ReadWrite,
read: ReadAction::None,
write: WriteModify::None,
},
FieldSpec {
name: "NOCYCCNT",
bit: 25,
width: 1,
access: Access::ReadOnly,
read: ReadAction::None,
write: WriteModify::None,
},
],
},
RegisterSpec {
name: "DWT_CYCCNT",
offset: 0x4,
width: Width::W32,
reset: 0x0000_0000,
access: Access::ReadWrite,
read: ReadAction::None,
write: WriteModify::None,
fields: &[FieldSpec {
name: "CYCCNT",
bit: 0,
width: 32,
access: Access::ReadWrite,
read: ReadAction::None,
write: WriteModify::None,
}],
},
];
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Dwt {
clock_hz: u64,
held: u64,
since: Option<VirtualNanos>,
}
impl Dwt {
#[must_use]
pub fn with_clock(clock_hz: u64) -> Self {
Self {
clock_hz,
held: 0,
since: None,
}
}
fn cycles(&self, span: VirtualNanos) -> u64 {
u128::from(span.0)
.saturating_mul(u128::from(self.clock_hz))
.checked_div(1_000_000_000)
.and_then(|cycles| u64::try_from(cycles).ok())
.unwrap_or(u64::MAX)
}
fn counter(&self, now: VirtualNanos) -> u64 {
let running = match self.since {
Some(started) => self.cycles(VirtualNanos(now.0.saturating_sub(started.0))),
None => 0,
};
self.held.wrapping_add(running) & dwt_cyccnt::CYCCNT.mask()
}
fn settle(&mut self, now: VirtualNanos) {
self.held = self.counter(now);
self.since = None;
}
}
impl Device for Dwt {
type Register = Register;
fn on_clock(
&mut self,
hz: u64,
_registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let now = context.now();
if self.since.is_some() {
self.settle(now);
self.since = Some(now);
}
self.clock_hz = hz;
}
fn read(
&mut self,
register: Register,
_registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64> {
match register {
Register::Cyccnt => Some(self.counter(context.now())),
Register::Ctrl => None,
}
}
fn peek(
&self,
register: Register,
_registers: &RegisterFile,
now: VirtualNanos,
) -> Option<u64> {
(register == Register::Cyccnt).then(|| self.counter(now))
}
fn write(
&mut self,
register: Register,
_value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let now = context.now();
match register {
Register::Ctrl => {
let enabled = dwt_ctrl::CYCCNTENA.is_set(registers.get(Register::Ctrl));
match (enabled, self.since) {
(true, None) => self.since = Some(now),
(false, Some(_)) => self.settle(now),
_ => {}
}
}
Register::Cyccnt => {
self.held = registers.get(Register::Cyccnt) & dwt_cyccnt::CYCCNT.mask();
if self.since.is_some() {
self.since = Some(now);
}
}
}
}
}