use std::collections::VecDeque;
use crate::emulator::{AccessKind, InterruptLine};
use crate::snapshot::store;
use crate::soc::{Access, FieldSpec, Layout, Peripheral, RegisterSpec, Width, WriteModify};
use crate::time::{EventId, EventQueue, VirtualNanos};
pub trait Device {
type Register: Layout;
fn layout(&self) -> &'static [RegisterSpec] {
<Self::Register as Layout>::REGISTERS
}
fn read(
&mut self,
_register: Self::Register,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) -> Option<u64> {
None
}
fn peek(
&self,
_register: Self::Register,
_registers: &RegisterFile,
_now: VirtualNanos,
) -> 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<'_>,
) {
}
fn on_request(
&mut self,
_request: RequestChange,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
}
fn on_interrupt(
&mut self,
_change: InterruptChange,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
}
fn on_clock(
&mut self,
_hz: u64,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
}
fn on_access(
&mut self,
_token: u64,
_value: u64,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
}
fn on_refused(
&mut self,
_token: u64,
_registers: &mut RegisterFile,
_context: &mut DeviceContext<'_>,
) {
}
fn poll(&mut self, _registers: &mut RegisterFile, _context: &mut DeviceContext<'_>) {}
fn reset(&mut self) {}
}
#[derive(Debug)]
pub struct DeviceContext<'a> {
now: VirtualNanos,
owner: usize,
instance: &'static str,
interrupts: &'static [u16],
effects: &'a mut Effects,
}
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
#[serde(bound(deserialize = "'de: 'static"))]
pub(crate) struct Effects {
pub(crate) events: EventQueue,
pub(crate) interrupts: VecDeque<(VirtualNanos, InterruptChange)>,
#[serde(skip)]
pub(crate) lines: Vec<LineChange>,
pub(crate) requests: VecDeque<(VirtualNanos, RequestChange)>,
pub(crate) clocks: VecDeque<(VirtualNanos, ClockChange)>,
pub(crate) accesses: VecDeque<PendingAccess>,
pub(crate) answers: VecDeque<Answer>,
pub(crate) reset: Option<ResetCause>,
pub(crate) told: Vec<Told>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
pub enum ResetCause {
Core,
Model(&'static str),
Pin,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub(crate) struct Answer {
pub(crate) at: VirtualNanos,
pub(crate) owner: usize,
pub(crate) token: u64,
pub(crate) found: Option<u64>,
}
impl<'a> DeviceContext<'a> {
pub(crate) fn new(
now: VirtualNanos,
owner: usize,
peripheral: Peripheral,
effects: &'a mut Effects,
) -> Self {
Self {
now,
owner,
instance: peripheral.name,
interrupts: peripheral.interrupts,
effects,
}
}
#[must_use]
pub fn now(&self) -> VirtualNanos {
self.now
}
pub fn schedule(&mut self, at: VirtualNanos) -> EventId {
self.effects.events.schedule(at.max(self.now), self.owner)
}
pub fn interrupt(&mut self, position: usize, level: Level) {
let Some(&interrupt) = self.interrupts.get(position) else {
panic!(
"{} lists {} interrupts, and the model asked for position {position}",
self.instance,
self.interrupts.len()
);
};
self.effects.interrupts.push_back((
self.now,
InterruptChange {
instance: self.instance,
interrupt,
level,
},
));
}
pub fn drive_line(&mut self, line: InterruptLine, level: Level) {
self.effects.lines.push(LineChange { line, level });
}
pub fn raise(&mut self, position: usize) {
self.interrupt(position, Level::High);
}
pub fn lower(&mut self, position: usize) {
self.interrupt(position, Level::Low);
}
pub fn request(&mut self, request: usize, level: Level) {
self.effects.requests.push_back((
self.now,
RequestChange {
instance: self.instance,
request,
level,
},
));
}
pub fn clock(&mut self, output: usize, hz: u64) {
self.effects.clocks.push_back((
self.now,
ClockChange {
instance: self.instance,
output,
hz,
},
));
}
pub fn request_reset(&mut self) {
self.effects
.reset
.get_or_insert(ResetCause::Model(self.instance));
}
pub fn read_from(&mut self, address: u64, width: Width, token: u64) {
self.effects.accesses.push_back(PendingAccess {
at: self.now,
owner: self.owner,
token,
kind: AccessKind::Read,
address,
width,
value: 0,
});
}
pub fn write_to(&mut self, address: u64, width: Width, value: u64, token: u64) {
self.effects.accesses.push_back(PendingAccess {
at: self.now,
owner: self.owner,
token,
kind: AccessKind::Write,
address,
width,
value,
});
}
pub fn tell(&mut self, register: &'static str, reason: &'static str) {
self.effects.told.push(Told {
instance: self.instance,
register,
reason,
at: self.now,
});
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Told {
pub instance: &'static str,
pub register: &'static str,
pub reason: &'static str,
pub at: VirtualNanos,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct RequestChange {
pub instance: &'static str,
pub request: usize,
pub level: Level,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ClockChange {
pub instance: &'static str,
pub output: usize,
pub hz: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub(crate) struct PendingAccess {
pub(crate) at: VirtualNanos,
pub(crate) owner: usize,
pub(crate) token: u64,
pub(crate) kind: AccessKind,
pub(crate) address: u64,
pub(crate) width: Width,
pub(crate) value: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct InterruptChange {
pub instance: &'static str,
pub interrupt: u16,
pub level: Level,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LineChange {
pub line: InterruptLine,
pub level: Level,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
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 trait ErasedDevice {
fn layout(&self) -> &'static [RegisterSpec];
fn read(
&mut self,
row: usize,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) -> Option<u64>;
fn peek(&self, _row: usize, _registers: &RegisterFile, _now: VirtualNanos) -> Option<u64> {
None
}
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<'_>,
);
fn on_request(
&mut self,
request: RequestChange,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
);
fn on_interrupt(
&mut self,
change: InterruptChange,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
let _ = (change, registers, context);
}
fn on_clock(&mut self, hz: u64, registers: &mut RegisterFile, context: &mut DeviceContext<'_>);
fn poll(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>);
fn reset(&mut self);
fn on_access(
&mut self,
token: u64,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
);
fn on_refused(
&mut self,
token: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
);
fn read_window(&mut self, offset: u64, width: Width, context: &mut DeviceContext<'_>) -> u64 {
let _ = (offset, width, context);
0
}
fn write_window(
&mut self,
offset: u64,
width: Width,
value: u64,
context: &mut DeviceContext<'_>,
) {
let _ = (offset, width, value, context);
}
fn peek_window(&self, offset: u64, width: Width, now: VirtualNanos) -> Option<u64> {
let _ = (offset, width, now);
None
}
}
impl<D: Device + 'static> ErasedDevice for D {
fn layout(&self) -> &'static [RegisterSpec] {
Device::layout(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 peek(&self, row: usize, registers: &RegisterFile, now: VirtualNanos) -> Option<u64> {
D::Register::from_index(row)
.and_then(|register| Device::peek(self, register, registers, now))
}
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);
}
fn on_request(
&mut self,
request: RequestChange,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
Device::on_request(self, request, registers, context);
}
fn on_interrupt(
&mut self,
change: InterruptChange,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
Device::on_interrupt(self, change, registers, context);
}
fn on_clock(&mut self, hz: u64, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
Device::on_clock(self, hz, registers, context);
}
fn on_access(
&mut self,
token: u64,
value: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
Device::on_access(self, token, value, registers, context);
}
fn on_refused(
&mut self,
token: u64,
registers: &mut RegisterFile,
context: &mut DeviceContext<'_>,
) {
Device::on_refused(self, token, registers, context);
}
fn poll(&mut self, registers: &mut RegisterFile, context: &mut DeviceContext<'_>) {
Device::poll(self, registers, context);
}
fn reset(&mut self) {
Device::reset(self);
}
}
#[derive(Debug, Clone)]
pub struct RegisterFile {
table: &'static [RegisterSpec],
by_offset: Box<[usize]>,
slot_of_row: Vec<usize>,
reset_values: Vec<u64>,
values: Vec<u64>,
written_once: Vec<bool>,
corrected_rules: Vec<Option<WriteModify>>,
corrected_fields: Vec<Option<Box<[FieldSpec]>>>,
}
store!(RegisterFile {
values: [values, written_once],
parts: [],
built: [
table,
by_offset,
slot_of_row,
reset_values,
corrected_rules,
corrected_fields
],
});
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();
let mut by_offset: Box<[usize]> = (0..table.len()).collect();
by_offset.sort_by_key(|&row| table[row].offset);
Self {
table,
by_offset,
slot_of_row,
values: reset_values.clone(),
reset_values,
written_once: vec![false; table.len()],
corrected_rules: vec![None; table.len()],
corrected_fields: 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_field(&mut self, row: usize, name: &'static str, corrected: FieldSpec) {
let mut fields = self.fields(row).to_vec();
for field in &mut fields {
if field.name == name {
*field = corrected;
}
}
self.corrected_fields[row] = Some(fields.into_boxed_slice());
}
pub(crate) fn declare_field(&mut self, row: usize, field: &'static FieldSpec) {
let mut fields = self.fields(row).to_vec();
fields.push(*field);
self.corrected_fields[row] = Some(fields.into_boxed_slice());
}
#[must_use]
pub fn fields(&self, row: usize) -> &[FieldSpec] {
self.corrected_fields[row]
.as_deref()
.unwrap_or(self.table[row].fields)
}
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 restore<L: Layout>(&mut self, register: L, mask: u64, kept: u64) {
let value = self.get(register);
self.set(register, (value & !mask) | (kept & 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]]
}
#[inline]
pub(crate) fn row_covering(&self, offset: u32, allows: fn(Access) -> bool) -> Option<usize> {
let first_past = self
.by_offset
.partition_point(|&row| self.table[row].offset <= offset);
let mut first = first_past;
while first > 0 && self.table[self.by_offset[first - 1]].end() > offset {
first -= 1;
}
let covering = &self.by_offset[first..first_past];
covering
.iter()
.copied()
.find(|&row| allows(self.table[row].access))
.or_else(|| covering.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 register_read = register.read;
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 self
.fields(row)
.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_peek(&self, row: usize) -> u64 {
if self.table[row].access.is_readable() {
self.get_row(row)
} else {
0
}
}
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;
}
let register_write = register.write;
let width_mask = register.width.mask();
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 self.fields(row) {
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 & 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 a_peek_answers_what_a_read_would_and_takes_no_read_action() {
static HIDDEN: [RegisterSpec; 1] =
[register("KEY", 0x0, Access::WriteOnly, ReadAction::None)];
let mut registers = RegisterFile::new(®ISTERS);
for register in [Register::Pop, Register::Status] {
assert_eq!(registers.bus_peek(register.index()), 0xBEEF);
assert_eq!(registers.bus_peek(register.index()), 0xBEEF, "still");
assert_eq!(registers.bus_read(register.index()), 0xBEEF);
}
assert_eq!(
registers.bus_peek(Register::Pop.index()),
0,
"the read cleared it"
);
let mut hidden = RegisterFile::new(&HIDDEN);
assert_eq!(
(hidden.bus_peek(0), hidden.bus_read(0)),
(0, 0),
"a register the bus can't read"
);
}
#[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);
}
#[test]
fn rows_are_found_by_offset_in_any_order() {
static BACKWARDS: [RegisterSpec; 3] = [
register("HIGH", 0x4, Access::ReadWrite, ReadAction::None),
register("MIDDLE", 0x2, Access::ReadWrite, ReadAction::None),
register("LOW", 0x0, Access::ReadWrite, ReadAction::None),
];
let registers = RegisterFile::new(&BACKWARDS);
for (offset, row) in [(0x0, 2), (0x1, 2), (0x2, 1), (0x4, 0), (0x5, 0)] {
assert_eq!(
registers.row_covering(offset, Access::is_readable),
Some(row),
"{offset:#x}"
);
}
assert_eq!(registers.row_covering(0x6, Access::is_readable), None);
}
}