use std::any::Any;
use crate::soc::{Access, Layout, Peripheral, RegisterSpec, WriteModify};
use crate::time::{EventId, EventQueue, VirtualNanos};
pub trait Device {
type Register: Layout;
fn read(
&mut self,
_register: Self::Register,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) -> Option<u64> {
None
}
fn write(
&mut self,
_register: Self::Register,
_value: u64,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
}
fn on_event(
&mut self,
_event: EventId,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
}
}
#[derive(Debug)]
pub struct DeviceContext<'a> {
now: VirtualNanos,
owner: usize,
instance: &'static str,
interrupts: &'static [u16],
events: &'a mut EventQueue,
changes: &'a mut Vec<InterruptChange>,
}
impl<'a> DeviceContext<'a> {
pub(crate) fn new(
now: VirtualNanos,
owner: usize,
peripheral: Peripheral,
events: &'a mut EventQueue,
changes: &'a mut Vec<InterruptChange>,
) -> Self {
Self {
now,
owner,
instance: peripheral.name,
interrupts: peripheral.interrupts,
events,
changes,
}
}
#[must_use]
pub fn now(&self) -> VirtualNanos {
self.now
}
pub fn schedule(&mut self, at: VirtualNanos) -> EventId {
self.events.schedule(at.max(self.now), self.owner)
}
pub fn raise(&mut self, interrupt: usize) {
self.change(interrupt, Level::High);
}
pub fn lower(&mut self, interrupt: usize) {
self.change(interrupt, Level::Low);
}
fn change(&mut self, position: usize, level: Level) {
let Some(&interrupt) = self.interrupts.get(position) else {
panic!(
"{} lists {} interrupts; the model asked for position {position}",
self.instance,
self.interrupts.len()
);
};
self.changes.push(InterruptChange {
instance: self.instance,
interrupt,
level,
});
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InterruptChange {
pub instance: &'static str,
pub interrupt: u16,
pub level: Level,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Level {
High,
#[default]
Low,
}
impl From<bool> for Level {
fn from(high: bool) -> Self {
if high { Self::High } else { Self::Low }
}
}
impl std::ops::Not for Level {
type Output = Self;
fn not(self) -> Self {
match self {
Self::High => Self::Low,
Self::Low => Self::High,
}
}
}
pub(crate) trait ErasedDevice {
fn as_any_mut(&mut self) -> &mut dyn Any;
fn read(
&mut self,
row: usize,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64>;
fn write(
&mut self,
row: usize,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
);
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
);
}
impl<D: Device + 'static> ErasedDevice for D {
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn read(
&mut self,
row: usize,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64> {
D::Register::from_index(row)
.and_then(|register| Device::read(self, register, registers, context))
}
fn write(
&mut self,
row: usize,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
if let Some(register) = D::Register::from_index(row) {
Device::write(self, register, value, registers, context);
}
}
fn on_event(
&mut self,
event: EventId,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
Device::on_event(self, event, registers, context);
}
}
#[derive(Debug, Clone)]
pub struct RegisterFile {
table: &'static [RegisterSpec],
slot_of_row: Vec<usize>,
reset_values: Vec<u64>,
values: Vec<u64>,
written_once: Vec<bool>,
corrected_rules: Vec<Option<WriteModify>>,
}
impl RegisterFile {
pub(crate) fn new(table: &'static [RegisterSpec]) -> Self {
let slot_of_row: Vec<usize> = table
.iter()
.map(|register| {
table
.iter()
.position(|earlier| earlier.offset == register.offset)
.unwrap_or_default()
})
.collect();
let reset_values: Vec<u64> = table.iter().map(|register| register.reset).collect();
Self {
table,
slot_of_row,
values: reset_values.clone(),
reset_values,
written_once: vec![false; table.len()],
corrected_rules: vec![None; table.len()],
}
}
pub(crate) fn correct_write(&mut self, row: usize, rule: WriteModify) {
self.corrected_rules[row] = Some(rule);
}
pub(crate) fn write_rule(&self, row: usize) -> WriteModify {
self.corrected_rules[row].unwrap_or(self.table[row].write)
}
pub(crate) fn correct_reset(&mut self, row: usize, value: u64) {
let slot = self.slot_of_row[row];
self.reset_values[slot] = value;
self.values[slot] = value;
}
pub fn reset(&mut self) {
self.values.clone_from(&self.reset_values);
self.written_once.fill(false);
}
pub fn get<L: Layout>(&self, register: L) -> u64 {
self.get_row(register.index())
}
pub fn set<L: Layout>(&mut self, register: L, value: u64) {
self.values[self.slot_of_row[register.index()]] = value & register.spec().width.mask();
}
pub fn write_over_bus<L: Layout>(&mut self, register: L, written: u64) {
self.bus_write(register.index(), written);
}
pub(crate) fn get_row(&self, row: usize) -> u64 {
self.values[self.slot_of_row[row]]
}
pub(crate) fn row_covering(&self, offset: u32, allows: fn(Access) -> bool) -> Option<usize> {
let first_past = self
.table
.partition_point(|register| register.offset <= offset);
let covering = (0..first_past)
.rev()
.take_while(|&row| self.table[row].end() > offset);
let mut candidates = covering.collect::<Vec<usize>>();
candidates.reverse();
candidates
.iter()
.copied()
.find(|&row| allows(self.table[row].access))
.or_else(|| candidates.first().copied())
}
pub(crate) fn bus_read(&mut self, row: usize) -> u64 {
let register = &self.table[row];
if !register.access.is_readable() {
return 0;
}
let slot = self.slot_of_row[row];
let value = self.values[slot];
let mut after = register.read.apply(value, register.width.mask());
for field in register
.fields
.iter()
.filter(|field| field.read != register.read)
{
after =
(after & !field.mask()) | (field.read.apply(value, field.mask()) & field.mask());
}
self.values[slot] = after;
value
}
pub(crate) fn bus_write(&mut self, row: usize, written: u64) {
let register = &self.table[row];
if !register.access.is_writable()
|| (register.access.is_write_once() && self.written_once[row])
{
return;
}
self.written_once[row] = true;
let slot = self.slot_of_row[row];
let old = self.values[slot];
let corrected = self.corrected_rules[row];
let mut landed = corrected.unwrap_or(register.write).apply(old, written);
for field in register.fields {
let field_bits = if field.access.is_writable() {
corrected.unwrap_or(field.write).apply(old, written)
} else {
old
};
landed = (landed & !field.mask()) | (field_bits & field.mask());
}
self.values[slot] = landed & register.width.mask();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::soc::{FieldSpec, ReadAction, Width, WriteModify};
const fn register(
name: &'static str,
offset: u32,
access: Access,
read: ReadAction,
) -> RegisterSpec {
RegisterSpec {
name,
offset,
width: Width::W16,
reset: 0xBEEF,
access,
read,
write: WriteModify::None,
fields: &[],
}
}
const FLAG: FieldSpec = FieldSpec {
name: "FLAG",
bit: 0,
width: 1,
access: Access::ReadOnly,
read: ReadAction::Clear,
write: WriteModify::None,
};
static REGISTERS: [RegisterSpec; 4] = [
register("PLAIN", 0x0, Access::ReadWrite, ReadAction::None),
register("ONCE", 0x2, Access::ReadWriteOnce, ReadAction::None),
register("POP", 0x4, Access::ReadOnly, ReadAction::Clear),
RegisterSpec {
fields: &[FLAG],
..register("STATUS", 0x6, Access::ReadWrite, ReadAction::None)
},
];
#[derive(Debug, Clone, Copy)]
enum Register {
Plain,
Once,
Pop,
Status,
}
impl Layout for Register {
const REGISTERS: &'static [RegisterSpec] = ®ISTERS;
fn from_index(index: usize) -> Option<Self> {
[Self::Plain, Self::Once, Self::Pop, Self::Status]
.get(index)
.copied()
}
fn index(self) -> usize {
self as usize
}
}
#[test]
fn write_modify_arithmetic_and_its_neutral_value() {
assert_eq!(WriteModify::OneToClear.apply(0b1111, 0b0101), 0b1010);
assert_eq!(WriteModify::OneToSet.apply(0b1000, 0b0001), 0b1001);
assert_eq!(WriteModify::OneToToggle.apply(0b1100, 0b0110), 0b1010);
assert_eq!(WriteModify::ZeroToClear.apply(0b1111, 0b0101), 0b0101);
assert_eq!(WriteModify::ZeroToSet.apply(0, 0b1110) & 0xF, 0b0001);
assert_eq!(
WriteModify::ZeroToToggle.apply(0b1111, 0b1110) & 0xF,
0b1110
);
assert_eq!(WriteModify::Clear.apply(0b1111, 0b0101), 0);
assert_eq!(WriteModify::Modify.apply(0b1111, 0b0101), 0b1111);
for modify in [
WriteModify::None,
WriteModify::OneToClear,
WriteModify::OneToSet,
WriteModify::ZeroToClear,
WriteModify::ZeroToSet,
] {
assert_eq!(
modify.apply(0xA5, modify.neutral(0xA5)) & 0xFF,
0xA5,
"{modify:?}"
);
}
}
#[test]
fn set_masks_to_the_register_width() {
let mut registers = RegisterFile::new(®ISTERS);
registers.set(Register::Plain, 0x1_2345);
assert_eq!(registers.get(Register::Plain), 0x2345);
}
#[test]
fn reset_restores_values_and_forgets_first_writes() {
let mut registers = RegisterFile::new(®ISTERS);
registers.bus_write(Register::Once.index(), 1);
registers.bus_write(Register::Once.index(), 2);
assert_eq!(registers.get(Register::Once), 1);
registers.reset();
assert_eq!(registers.get(Register::Once), 0xBEEF);
registers.bus_write(Register::Once.index(), 3);
assert_eq!(
registers.get(Register::Once),
3,
"the first write after reset lands again"
);
}
#[test]
fn a_read_only_register_ignores_writes_and_clears_on_read() {
let mut registers = RegisterFile::new(®ISTERS);
registers.bus_write(Register::Pop.index(), 0);
assert_eq!(registers.bus_read(Register::Pop.index()), 0xBEEF);
assert_eq!(registers.bus_read(Register::Pop.index()), 0);
}
#[test]
fn a_field_may_clear_on_read_while_its_register_does_not() {
let mut registers = RegisterFile::new(®ISTERS);
assert_eq!(registers.bus_read(Register::Status.index()), 0xBEEF);
assert_eq!(registers.get(Register::Status), 0xBEEE, "only FLAG cleared");
}
#[test]
fn rows_are_found_by_the_offset_they_cover() {
let registers = RegisterFile::new(®ISTERS);
assert_eq!(registers.row_covering(0x2, Access::is_readable), Some(1));
assert_eq!(
registers.row_covering(0x3, Access::is_readable),
Some(1),
"the second byte of ONCE"
);
assert_eq!(registers.row_covering(0x8, Access::is_readable), None);
}
}