use crate::device::{Device, DeviceContext, RegisterFile};
use crate::soc::{
Access, FieldSpec, Layout, Peripheral, ReadAction, RegisterSpec, Width, WriteModify,
};
use crate::time::VirtualNanos;
pub const DWT: &str = "DWT";
const PPB_BASE: u64 = 0xE000_0000;
const PPB_SIZE: u64 = 0x0010_0000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Window {
pub base: u64,
pub size: u64,
}
impl std::fmt::Display for Window {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "{:#x}:{:#x}", self.base, self.size)
}
}
#[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 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)]
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 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);
}
}
}
}
}