pub mod cp;
pub mod disasm;
mod exec;
pub mod isa;
pub mod thumb;
#[cfg(test)]
mod tests;
#[cfg(all(test, feature = "std"))]
mod conformance;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt;
use crate::core::device::{
Device, DeviceClass, Initiator, PropertySpec, RealizeCtx, ResetKind, SinkPin,
};
use crate::core::error::{Error, Result};
use crate::core::props::{Props, ValueKind};
use crate::core::registry::Registry;
use crate::core::sched::{Budget, Consumed};
use crate::core::space::{AddressSpace, MemAttrs, RequesterId};
use crate::core::state::{ChunkReader, ChunkWriter, Sink, Source};
use crate::core::sync::{self, AtomicBool, AtomicU32, LockRank, Ordering};
use crate::core::value::Endian;
use crate::core::wire::{FanIn, Level, Resolve, WireId, WireSink};
use cp::{Coprocessor, FlatMmu, Mmu};
use exec::{Exec, State};
pub use exec::Exception;
pub mod psr {
pub const N: u32 = 1 << 31;
pub const Z: u32 = 1 << 30;
pub const C: u32 = 1 << 29;
pub const V: u32 = 1 << 28;
pub const Q: u32 = 1 << 27;
pub const I: u32 = 1 << 7;
pub const F: u32 = 1 << 6;
pub const T: u32 = 1 << 5;
pub const MODE: u32 = 0x1f;
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Mode(pub u8);
impl Mode {
pub const USER: Mode = Mode(0b1_0000);
pub const FIQ: Mode = Mode(0b1_0001);
pub const IRQ: Mode = Mode(0b1_0010);
pub const SUPERVISOR: Mode = Mode(0b1_0011);
pub const ABORT: Mode = Mode(0b1_0111);
pub const UNDEFINED: Mode = Mode(0b1_1011);
pub const SYSTEM: Mode = Mode(0b1_1111);
pub const ALL: &'static [Mode] = &[
Mode::USER,
Mode::FIQ,
Mode::IRQ,
Mode::SUPERVISOR,
Mode::ABORT,
Mode::UNDEFINED,
Mode::SYSTEM,
];
#[must_use]
pub const fn bank(self) -> usize {
match self.0 & 0x1f {
0b1_0001 => 1,
0b1_0010 => 2,
0b1_0011 => 3,
0b1_0111 => 4,
0b1_1011 => 5,
_ => 0,
}
}
#[must_use]
pub const fn spsr_index(self) -> Option<usize> {
match self.bank() {
0 => None,
n => Some(n - 1),
}
}
#[must_use]
pub const fn is_privileged(self) -> bool {
self.0 & 0x1f != Mode::USER.0
}
#[must_use]
pub const fn is_defined(self) -> bool {
matches!(
self.0 & 0x1f,
0b1_0000 | 0b1_0001 | 0b1_0010 | 0b1_0011 | 0b1_0111 | 0b1_1011 | 0b1_1111
)
}
#[must_use]
pub const fn name(self) -> &'static str {
match self.0 & 0x1f {
0b1_0000 => "usr",
0b1_0001 => "fiq",
0b1_0010 => "irq",
0b1_0011 => "svc",
0b1_0111 => "abt",
0b1_1011 => "und",
0b1_1111 => "sys",
_ => "???",
}
}
}
impl fmt::Display for Mode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Regs {
pub r: [u32; 16],
pub cpsr: u32,
pub banked_sp_lr: [[u32; 2]; 6],
pub banked_r8_r12: [[u32; 5]; 2],
pub spsr: [u32; 5],
}
impl Regs {
#[must_use]
pub const fn new() -> Regs {
Regs {
r: [0; 16],
cpsr: Mode::SUPERVISOR.0 as u32 | psr::I | psr::F,
banked_sp_lr: [[0; 2]; 6],
banked_r8_r12: [[0; 5]; 2],
spsr: [0; 5],
}
}
#[must_use]
pub const fn mode(&self) -> Mode {
Mode((self.cpsr & psr::MODE) as u8)
}
#[must_use]
pub const fn is_thumb(&self) -> bool {
self.cpsr & psr::T != 0
}
#[must_use]
pub const fn pc(&self) -> u32 {
self.r[15]
}
#[must_use]
pub const fn spsr(&self) -> Option<u32> {
match self.mode().spsr_index() {
Some(i) => Some(self.spsr[i]),
None => None,
}
}
pub const fn set_spsr(&mut self, value: u32) {
if let Some(i) = self.mode().spsr_index() {
self.spsr[i] = value;
}
}
pub const fn set_mode(&mut self, to: Mode) {
let from = self.mode();
if from.0 & 0x1f == to.0 & 0x1f {
return;
}
let (old_bank, new_bank) = (from.bank(), to.bank());
if old_bank != new_bank {
self.banked_sp_lr[old_bank][0] = self.r[13];
self.banked_sp_lr[old_bank][1] = self.r[14];
self.r[13] = self.banked_sp_lr[new_bank][0];
self.r[14] = self.banked_sp_lr[new_bank][1];
}
let old_fiq = old_bank == 1;
let new_fiq = new_bank == 1;
if old_fiq != new_fiq {
let (out, into) = if old_fiq { (1, 0) } else { (0, 1) };
let mut i = 0;
while i < 5 {
self.banked_r8_r12[out][i] = self.r[8 + i];
self.r[8 + i] = self.banked_r8_r12[into][i];
i += 1;
}
}
self.cpsr = (self.cpsr & !psr::MODE) | ((to.0 as u32) & psr::MODE);
}
pub const fn write_cpsr(&mut self, value: u32) {
let value = value | 0x10;
self.set_mode(Mode((value & psr::MODE) as u8));
self.cpsr = value;
}
#[must_use]
pub const fn reg_in_mode(&self, mode: Mode, index: u8) -> u32 {
let index = (index & 0xf) as usize;
let current = self.mode();
if mode.0 & 0x1f == current.0 & 0x1f {
return self.r[index];
}
match index {
8..=12 => {
let want_fiq = mode.bank() == 1;
if want_fiq == (current.bank() == 1) {
self.r[index]
} else {
self.banked_r8_r12[if want_fiq { 1 } else { 0 }][index - 8]
}
}
13 | 14 => {
if mode.bank() == current.bank() {
self.r[index]
} else {
self.banked_sp_lr[mode.bank()][index - 13]
}
}
_ => self.r[index],
}
}
pub const fn set_reg_in_mode(&mut self, mode: Mode, index: u8, value: u32) {
let index = (index & 0xf) as usize;
let current = self.mode();
if mode.0 & 0x1f == current.0 & 0x1f {
self.r[index] = value;
return;
}
match index {
8..=12 => {
let want_fiq = mode.bank() == 1;
if want_fiq == (current.bank() == 1) {
self.r[index] = value;
} else {
self.banked_r8_r12[if want_fiq { 1 } else { 0 }][index - 8] = value;
}
}
13 | 14 => {
if mode.bank() == current.bank() {
self.r[index] = value;
} else {
self.banked_sp_lr[mode.bank()][index - 13] = value;
}
}
_ => self.r[index] = value,
}
}
}
impl Default for Regs {
fn default() -> Regs {
Regs::new()
}
}
impl fmt::Display for Regs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, value) in self.r.iter().enumerate() {
write!(f, "r{i}:{value:08x} ")?;
}
write!(
f,
"cpsr:{:08x} [{}{}{}{}{}{}{}{} {}]",
self.cpsr,
if self.cpsr & psr::N != 0 { 'N' } else { 'n' },
if self.cpsr & psr::Z != 0 { 'Z' } else { 'z' },
if self.cpsr & psr::C != 0 { 'C' } else { 'c' },
if self.cpsr & psr::V != 0 { 'V' } else { 'v' },
if self.cpsr & psr::Q != 0 { 'Q' } else { 'q' },
if self.cpsr & psr::I != 0 { 'I' } else { 'i' },
if self.cpsr & psr::F != 0 { 'F' } else { 'f' },
if self.cpsr & psr::T != 0 { 'T' } else { 't' },
self.mode()
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Config {
pub requester: RequesterId,
pub endian: Endian,
pub high_vectors: bool,
pub alignment_faults: bool,
pub store_pc_offset: u8,
}
impl Config {
pub const ARM926EJS: Config = Config {
requester: RequesterId::ANONYMOUS,
endian: Endian::Little,
high_vectors: false,
alignment_faults: false,
store_pc_offset: 8,
};
pub const ARM7TDMI: Config = Config {
store_pc_offset: 12,
..Config::ARM926EJS
};
#[must_use]
pub const fn with_requester(mut self, id: RequesterId) -> Config {
self.requester = id;
self
}
#[must_use]
pub const fn with_endian(mut self, endian: Endian) -> Config {
self.endian = endian;
self
}
#[must_use]
pub const fn with_high_vectors(mut self, high: bool) -> Config {
self.high_vectors = high;
self
}
#[must_use]
pub const fn with_alignment_faults(mut self, on: bool) -> Config {
self.alignment_faults = on;
self
}
}
impl Default for Config {
fn default() -> Config {
Config::ARM926EJS
}
}
#[derive(Debug, Default)]
pub(crate) struct Lines {
irq: AtomicBool,
fiq: AtomicBool,
reset: AtomicBool,
}
impl Lines {
fn snapshot(&self) -> (bool, bool) {
(
self.irq.load(Ordering::Acquire),
self.fiq.load(Ordering::Acquire),
)
}
fn restore(&self, (irq, fiq): (bool, bool)) {
self.irq.store(irq, Ordering::Release);
self.fiq.store(fiq, Ordering::Release);
}
fn request_reset(&self) {
self.reset.store(true, Ordering::Release);
}
fn take_reset_request(&self) -> bool {
self.reset.swap(false, Ordering::AcqRel)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Interrupt {
Irq,
Fiq,
}
struct Session {
state: State,
space: Option<Arc<AddressSpace>>,
mmu: Arc<dyn Mmu>,
coprocessors: [Option<Arc<dyn Coprocessor>>; 16],
}
impl fmt::Debug for Session {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Session")
.field("state", &self.state)
.field("space", &self.space.as_ref().map(|s| s.name()))
.field("mmu", &self.mmu)
.field(
"coprocessors",
&self.coprocessors.iter().filter(|c| c.is_some()).count(),
)
.finish()
}
}
#[derive(Debug)]
pub struct Arm {
cfg: Config,
lines: Arc<Lines>,
requester: AtomicU32,
session: sync::Mutex<Session>,
pins: sync::Mutex<Pins>,
}
#[derive(Debug, Default)]
struct Pins {
irq: Option<Arc<InterruptPin>>,
fiq: Option<Arc<InterruptPin>>,
reset: Option<Arc<ResetPin>>,
}
impl Arm {
#[must_use]
pub fn new(cfg: Config) -> Arm {
Arm {
cfg,
lines: Arc::new(Lines::default()),
requester: AtomicU32::new(cfg.requester.0),
session: sync::Mutex::with_rank(
LockRank::BUS,
Session {
state: State::new(),
space: None,
mmu: Arc::new(FlatMmu),
coprocessors: [const { None }; 16],
},
),
pins: sync::Mutex::new(Pins::default()),
}
}
pub fn from_props(props: &Props) -> Result<Arm> {
let mut r = props.reader();
let big_endian = r.or("big-endian", false)?;
let high_vectors = r.or("high-vectors", false)?;
let alignment_faults = r.or("alignment-faults", false)?;
let store_pc_offset = r.or_range("store-pc-offset", 8u64, 8..=12)?;
let _engine = r.or_enum("engine", "interp", &["interp"])?;
r.finish()?;
if store_pc_offset != 8 && store_pc_offset != 12 {
return Err(Error::Property(
"store-pc-offset must be 8 (ARM926EJ-S) or 12 (ARM7TDMI)".into(),
));
}
Ok(Arm::new(Config {
requester: RequesterId::ANONYMOUS,
endian: if big_endian {
Endian::Big
} else {
Endian::Little
},
high_vectors,
alignment_faults,
store_pc_offset: store_pc_offset as u8,
}))
}
#[must_use]
pub fn config(&self) -> Config {
Config {
requester: RequesterId(self.requester.load(Ordering::Relaxed)),
..self.cfg
}
}
pub fn set_requester(&self, id: RequesterId) {
self.requester.store(id.0, Ordering::Relaxed);
}
pub fn attach_space(&self, space: Arc<AddressSpace>) {
self.session.lock().space = Some(space);
}
#[must_use]
pub fn space(&self) -> Option<Arc<AddressSpace>> {
self.session.lock().space.clone()
}
pub fn attach_mmu(&self, mmu: Arc<dyn Mmu>) {
self.session.lock().mmu = mmu;
}
pub fn attach_coprocessor(&self, cp: u8, coprocessor: Arc<dyn Coprocessor>) {
self.session.lock().coprocessors[(cp & 0xf) as usize] = Some(coprocessor);
}
pub fn detach_coprocessor(&self, cp: u8) {
self.session.lock().coprocessors[(cp & 0xf) as usize] = None;
}
#[must_use]
pub fn regs(&self) -> Regs {
self.session.lock().state.regs
}
pub fn set_regs(&self, regs: Regs) {
self.session.lock().state.regs = regs;
}
#[must_use]
pub fn reg(&self, index: u8) -> u32 {
self.session.lock().state.regs.r[(index & 0xf) as usize]
}
pub fn set_reg(&self, index: u8, value: u32) {
self.session.lock().state.regs.r[(index & 0xf) as usize] = value;
}
#[must_use]
pub fn pc(&self) -> u32 {
self.session.lock().state.regs.r[15]
}
pub fn set_pc(&self, value: u32) {
self.session.lock().state.regs.r[15] = value;
}
#[must_use]
pub fn cpsr(&self) -> u32 {
self.session.lock().state.regs.cpsr
}
pub fn set_cpsr(&self, value: u32) {
self.session.lock().state.regs.write_cpsr(value);
}
#[must_use]
pub fn mode(&self) -> Mode {
self.session.lock().state.regs.mode()
}
#[must_use]
pub fn is_thumb(&self) -> bool {
self.session.lock().state.regs.is_thumb()
}
#[must_use]
pub fn cycles(&self) -> u64 {
self.session.lock().state.cycles
}
#[must_use]
pub fn is_halted(&self) -> bool {
self.session.lock().state.halted
}
#[must_use]
pub fn reset_pending(&self) -> bool {
self.session.lock().state.reset_pending
}
#[must_use]
pub fn bus_faults(&self) -> (u64, u32) {
let s = self.session.lock();
(s.state.faults, s.state.last_fault)
}
#[must_use]
pub fn last_swi(&self) -> u32 {
self.session.lock().state.last_swi
}
#[must_use]
pub fn last_bkpt(&self) -> u16 {
self.session.lock().state.last_bkpt
}
pub fn set_irq(&self, asserted: bool) {
self.lines.irq.store(asserted, Ordering::Release);
}
#[must_use]
pub fn irq_asserted(&self) -> bool {
self.lines.irq.load(Ordering::Acquire)
}
pub fn set_fiq(&self, asserted: bool) {
self.lines.fiq.store(asserted, Ordering::Release);
}
#[must_use]
pub fn fiq_asserted(&self) -> bool {
self.lines.fiq.load(Ordering::Acquire)
}
pub fn request_reset(&self) {
self.session.lock().state.reset_pending = true;
}
pub fn step(&self) -> u64 {
let (irq, fiq) = self.lines.snapshot();
let reset = self.lines.take_reset_request();
let cfg = self.config();
let mut session = self.session.lock();
let Session {
state,
space,
mmu,
coprocessors,
} = &mut *session;
state.reset_pending |= reset;
let Some(space) = space.clone() else {
return 0;
};
let mmu = Arc::clone(mmu);
Exec::new(state, &space, mmu.as_ref(), coprocessors, &cfg).step(irq, fiq)
}
pub fn run(&self, budget: u64) -> u64 {
let mut used = 0;
while used < budget {
let n = self.step();
if n == 0 {
break;
}
used += n;
}
used
}
pub fn run_budget(&self, ticks: u64) -> u64 {
let owed = self.session.lock().state.debt;
if owed >= ticks {
self.session.lock().state.debt = owed - ticks;
return ticks;
}
let allowance = ticks - owed;
let mut used = 0u64;
while used < allowance {
let n = self.step();
if n == 0 {
break;
}
used += n;
}
if used >= allowance {
self.session.lock().state.debt = used - allowance;
ticks
} else {
self.session.lock().state.debt = 0;
owed + used
}
}
#[must_use]
pub fn cycle_debt(&self) -> u64 {
self.session.lock().state.debt
}
#[must_use]
pub fn disassemble(&self, addr: u32, count: usize, thumb: bool) -> Vec<disasm::Listed> {
let Some(space) = self.space() else {
return Vec::new();
};
disasm::disassemble_run(addr, count, thumb, |a| {
space
.read(u64::from(a), crate::core::value::Width::U8, MemAttrs::DEBUG)
.ok()
.map(|v| v as u8)
})
}
}
pub static CLASS: DeviceClass = DeviceClass {
name: "cpu.arm",
version: 2,
summary: "ARMv5TE (ARM926EJ-S class) 32-bit CPU core with Thumb and the DSP extensions",
properties: &[
PropertySpec {
name: "big-endian",
kind: ValueKind::Bool,
required: false,
summary: "use big-endian byte order for data accesses",
},
PropertySpec {
name: "high-vectors",
kind: ValueKind::Bool,
required: false,
summary: "put the exception vectors at 0xffff0000 from reset (VINITHI)",
},
PropertySpec {
name: "alignment-faults",
kind: ValueKind::Bool,
required: false,
summary: "take a data abort on an unaligned access instead of rotating",
},
PropertySpec {
name: "store-pc-offset",
kind: ValueKind::Uint,
required: false,
summary: "what a store of R15 writes: the instruction plus 8 or plus 12",
},
PropertySpec {
name: "engine",
kind: ValueKind::Str,
required: false,
summary: "which execution engine; only `interp` exists until phase 5",
},
],
construct: |props| Ok(Box::new(Arm::from_props(props)?)),
};
pub fn register(reg: &mut Registry) -> Result<()> {
reg.add(&CLASS)
}
impl Device for Arm {
fn class(&self) -> &'static DeviceClass {
&CLASS
}
fn realize(&self, _ctx: &mut RealizeCtx<'_>) -> Result<()> {
Ok(())
}
fn reset(&self, kind: ResetKind) {
{
let mut session = self.session.lock();
if kind == ResetKind::Cold {
session.state = State::new();
} else {
session.state.reset_pending = true;
session.state.halted = false;
}
}
if kind == ResetKind::Cold {
self.lines.restore((false, false));
}
self.lines.take_reset_request();
}
fn save(&self, w: &mut ChunkWriter<'_>) -> Result<()> {
let reset = self.lines.take_reset_request();
let state = {
let mut session = self.session.lock();
session.state.reset_pending |= reset;
session.state
};
for value in state.regs.r {
w.write_u32(value)?;
}
w.write_u32(state.regs.cpsr)?;
for bank in state.regs.banked_sp_lr {
w.write_u32(bank[0])?;
w.write_u32(bank[1])?;
}
for bank in state.regs.banked_r8_r12 {
for value in bank {
w.write_u32(value)?;
}
}
for value in state.regs.spsr {
w.write_u32(value)?;
}
w.write_u64(state.cycles)?;
w.write_bool(state.halted)?;
w.write_bool(state.reset_pending)?;
w.write_u64(state.faults)?;
w.write_u32(state.last_fault)?;
w.write_u32(state.last_swi)?;
w.write_u16(state.last_bkpt)?;
w.write_u64(state.debt)?;
let (irq, fiq) = self.lines.snapshot();
w.write_bool(irq)?;
w.write_bool(fiq)?;
Ok(())
}
fn load(&self, r: &mut ChunkReader<'_>) -> Result<()> {
let mut state = State::new();
for value in &mut state.regs.r {
*value = r.read_u32()?;
}
state.regs.cpsr = r.read_u32()?;
for bank in &mut state.regs.banked_sp_lr {
bank[0] = r.read_u32()?;
bank[1] = r.read_u32()?;
}
for bank in &mut state.regs.banked_r8_r12 {
for value in bank {
*value = r.read_u32()?;
}
}
for value in &mut state.regs.spsr {
*value = r.read_u32()?;
}
state.cycles = r.read_u64()?;
state.halted = r.read_bool()?;
state.reset_pending = r.read_bool()?;
state.faults = r.read_u64()?;
state.last_fault = r.read_u32()?;
state.last_swi = r.read_u32()?;
state.last_bkpt = r.read_u16()?;
state.debt = r.read_u64()?;
let irq = r.read_bool()?;
let fiq = r.read_bool()?;
self.session.lock().state = state;
self.lines.restore((irq, fiq));
Ok(())
}
fn sink(&self, port: &str, sources: &[WireId]) -> Option<SinkPin> {
let mut pins = self.pins.lock();
let sink: Arc<dyn WireSink> = match port {
"irq" => {
let pin = Arc::new(InterruptPin::from_lines(
Arc::clone(&self.lines),
Interrupt::Irq,
sources,
));
pins.irq = Some(Arc::clone(&pin));
pin
}
"fiq" => {
let pin = Arc::new(InterruptPin::from_lines(
Arc::clone(&self.lines),
Interrupt::Fiq,
sources,
));
pins.fiq = Some(Arc::clone(&pin));
pin
}
"reset" => {
let pin = Arc::new(ResetPin::new(Arc::clone(&self.lines), sources));
pins.reset = Some(Arc::clone(&pin));
pin
}
_ => return None,
};
Some(SinkPin { sink, line: 0 })
}
fn is_runnable(&self) -> bool {
true
}
fn run(&self, budget: Budget) -> Consumed {
Consumed::new(self.run_budget(budget.ticks))
}
}
impl Initiator for Arm {
fn requester(&self) -> RequesterId {
RequesterId(self.requester.load(Ordering::Relaxed))
}
}
impl crate::machine::Instance for Arm {
fn bind(&self, ctx: &crate::machine::BindCtx<'_>) -> Result<()> {
let space = ctx.space().ok_or_else(|| Error::Config {
at: ctx.path().to_string(),
message: String::from(
"an ARM core needs an address space to fetch from (`space = mem`)",
),
})?;
self.attach_space(Arc::clone(space));
self.set_requester(ctx.requester());
Ok(())
}
}
pub fn bind(bindings: &mut crate::machine::Bindings) -> Result<()> {
bindings.bind(CLASS.name, |props| Ok(Arc::new(Arm::from_props(props)?)))
}
#[must_use]
pub fn schema() -> crate::machine::validate::ClassSchema {
use crate::machine::validate::{ClassSchema, PortDir, PropSchema};
ClassSchema::new(CLASS.name)
.prop(PropSchema::new("big-endian", ValueKind::Bool))
.prop(PropSchema::new("high-vectors", ValueKind::Bool))
.prop(PropSchema::new("alignment-faults", ValueKind::Bool))
.prop(PropSchema::new("store-pc-offset", ValueKind::Uint).range(8, 12))
.prop(PropSchema::new("engine", ValueKind::Str).values(&["interp"]))
.port("irq", PortDir::In)
.port("fiq", PortDir::In)
.port("reset", PortDir::In)
}
#[derive(Debug)]
pub struct InterruptPin {
lines: Arc<Lines>,
which: Interrupt,
inputs: FanIn,
resolve: Resolve,
}
impl InterruptPin {
#[must_use]
pub fn new(cpu: Arc<Arm>, which: Interrupt, sources: &[WireId]) -> InterruptPin {
InterruptPin::from_lines(Arc::clone(&cpu.lines), which, sources)
}
fn from_lines(lines: Arc<Lines>, which: Interrupt, sources: &[WireId]) -> InterruptPin {
InterruptPin {
lines,
which,
inputs: FanIn::new(sources),
resolve: Resolve::Or,
}
}
#[must_use]
pub fn with_resolve(mut self, resolve: Resolve) -> InterruptPin {
self.resolve = resolve;
self
}
#[must_use]
pub fn which(&self) -> Interrupt {
self.which
}
#[must_use]
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
}
impl WireSink for InterruptPin {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
self.inputs.set(src, level);
let asserted = self.inputs.resolve(self.resolve).is_high();
match self.which {
Interrupt::Irq => self.lines.irq.store(asserted, Ordering::Release),
Interrupt::Fiq => self.lines.fiq.store(asserted, Ordering::Release),
}
}
}
#[derive(Debug)]
pub struct ResetPin {
lines: Arc<Lines>,
inputs: FanIn,
resolve: Resolve,
}
impl ResetPin {
#[must_use]
pub fn new_for(cpu: Arc<Arm>, sources: &[WireId]) -> ResetPin {
ResetPin::new(Arc::clone(&cpu.lines), sources)
}
fn new(lines: Arc<Lines>, sources: &[WireId]) -> ResetPin {
ResetPin {
lines,
inputs: FanIn::new(sources),
resolve: Resolve::Or,
}
}
#[must_use]
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
}
impl WireSink for ResetPin {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
self.inputs.set(src, level);
if self.inputs.resolve(self.resolve).is_high() {
self.lines.request_reset();
}
}
}