pub mod csr;
pub mod disasm;
pub mod elf;
mod exec;
pub mod isa;
#[cfg(feature = "cpu-riscv-lift")]
#[cfg_attr(docsrs, doc(cfg(feature = "cpu-riscv-lift")))]
pub mod differential;
#[cfg(feature = "cpu-riscv-lift")]
#[cfg_attr(docsrs, doc(cfg(feature = "cpu-riscv-lift")))]
pub mod lift;
pub mod mmu;
#[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 crate::core::device::{
DebugTranslation, Device, DeviceClass, ExportId, Initiator, PropertySpec, RealizeCtx,
ResetKind, SinkPin,
};
use crate::core::error::{Error, Result};
use crate::core::exec::{Exit, ExitMask, ExitingCore, Run};
use crate::core::props::{Props, ValueKind};
use crate::core::registry::Registry;
use crate::core::sched::{Budget, Consumed, ExitFlag, TickCursor};
use crate::core::space::{AddressSpace, MemAttrs, RequesterId};
use crate::core::state::{ChunkReader, ChunkWriter, Sink, Source};
use crate::core::sync::{self, AtomicU32, AtomicU64, LockRank, Ordering};
use crate::core::value::Width;
use crate::core::wire::{FanIn, Level, Resolve, WireId, WireSink};
use csr::{Csrs, Extensions, Lines, Priv, irq};
use exec::{Exec, State};
use isa::Xlen;
use mmu::Tlb;
pub(crate) const PAGE_MASK: u64 = mmu::PAGE_SIZE - 1;
pub const X_NAMES: [&str; 32] = [
"zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4",
"a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4",
"t5", "t6",
];
pub const F_NAMES: [&str; 32] = [
"ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", "fs0", "fs1", "fa0", "fa1", "fa2",
"fa3", "fa4", "fa5", "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9",
"fs10", "fs11", "ft8", "ft9", "ft10", "ft11",
];
#[must_use]
pub fn x_by_name(name: &str) -> Option<u32> {
if let Some(rest) = name.strip_prefix('x')
&& let Ok(n) = rest.parse::<u32>()
&& n < 32
{
return Some(n);
}
if name == "fp" {
return Some(8);
}
X_NAMES.iter().position(|n| *n == name).map(|i| i as u32)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Config {
pub xlen: Xlen,
pub ext: Extensions,
pub hartid: u64,
pub reset_vector: u64,
pub pmp_count: usize,
pub misaligned: bool,
pub requester: RequesterId,
}
impl Config {
#[must_use]
pub const fn rv64gc() -> Config {
Config {
xlen: Xlen::Rv64,
ext: Extensions::GC,
hartid: 0,
reset_vector: 0x8000_0000,
pmp_count: csr::PMP_ENTRIES,
misaligned: true,
requester: RequesterId::ANONYMOUS,
}
}
#[must_use]
pub const fn rv32gc() -> Config {
Config {
xlen: Xlen::Rv32,
..Config::rv64gc()
}
}
#[must_use]
pub const fn rv64i() -> Config {
Config {
xlen: Xlen::Rv64,
ext: Extensions::I,
pmp_count: 0,
..Config::rv64gc()
}
}
#[must_use]
pub const fn with_reset_vector(mut self, pc: u64) -> Self {
self.reset_vector = pc;
self
}
#[must_use]
pub const fn with_hartid(mut self, id: u64) -> Self {
self.hartid = id;
self
}
#[must_use]
pub const fn with_requester(mut self, id: RequesterId) -> Self {
self.requester = id;
self
}
#[must_use]
pub const fn with_ext(mut self, ext: Extensions) -> Self {
self.ext = ext;
self
}
#[must_use]
pub fn isa_string(&self) -> String {
let mut s = String::from(self.xlen.name());
s.push('i');
for (present, letter) in [
(self.ext.m, 'm'),
(self.ext.a, 'a'),
(self.ext.f, 'f'),
(self.ext.d, 'd'),
(self.ext.c, 'c'),
] {
if present {
s.push(letter);
}
}
s
}
}
impl Default for Config {
fn default() -> Self {
Config::rv64gc()
}
}
#[derive(Debug)]
struct Session {
state: State,
tlb: Tlb,
space: Option<Arc<AddressSpace>>,
time_src: Option<Arc<AtomicU64>>,
}
#[derive(Debug)]
pub struct Hart {
cfg: Config,
timer: Option<String>,
lines: Arc<Lines>,
session: sync::Mutex<Session>,
exits: AtomicU32,
requester: AtomicU32,
pins: sync::Mutex<Pins>,
exit: sync::Mutex<Option<ExitFlag>>,
}
#[derive(Debug, Default)]
struct Pins {
interrupts: Vec<(u64, Arc<InterruptPin>)>,
reset: Option<Arc<ResetPin>>,
}
impl Hart {
#[must_use]
pub fn new(cfg: Config) -> Hart {
let mut cfg = cfg;
if cfg.ext.d {
cfg.ext.f = true;
}
Hart {
timer: None,
lines: Arc::new(Lines::default()),
session: sync::Mutex::with_rank(
LockRank::BUS,
Session {
state: State::new(&cfg),
tlb: Tlb::new(),
space: None,
time_src: None,
},
),
exits: AtomicU32::new(ExitMask::NONE.bits()),
requester: AtomicU32::new(cfg.requester.0),
pins: sync::Mutex::new(Pins::default()),
exit: sync::Mutex::new(None),
cfg,
}
}
pub fn from_props(props: &Props) -> Result<Hart> {
let mut r = props.reader();
let xlen = match r.or_enum("xlen", "rv64", &["rv32", "rv64"])? {
"rv32" => Xlen::Rv32,
_ => Xlen::Rv64,
};
let isa = r.or("isa", String::from("imafdc"))?;
let hartid = r.or("hartid", 0u64)?;
let reset_vector = r.or("reset", 0x8000_0000u64)?;
let pmp_count = r.or_range("pmp", csr::PMP_ENTRIES as u64, 0..=csr::PMP_ENTRIES as u64)?;
let misaligned = r.or("misaligned", true)?;
let supervisor = r.or("supervisor", true)?;
let user = r.or("user", true)?;
let _ = r.or_enum("engine", "interp", &["interp"])?;
let timer = r.optional_link("timer")?.map(|l| String::from(l.as_str()));
r.finish()?;
let mut ext = Extensions {
m: false,
a: false,
f: false,
d: false,
c: false,
s: supervisor,
u: user,
};
for letter in isa.chars() {
match letter {
'i' => {}
'm' => ext.m = true,
'a' => ext.a = true,
'f' => ext.f = true,
'd' => ext.d = true,
'c' => ext.c = true,
'g' => {
ext.m = true;
ext.a = true;
ext.f = true;
ext.d = true;
}
other => {
return Err(Error::Property(alloc::format!(
"`isa` names extension `{other}`, which this core does not \
implement; it understands i, m, a, f, d, c and g"
)));
}
}
}
let mut hart = Hart::new(Config {
xlen,
ext,
hartid,
reset_vector,
pmp_count: pmp_count as usize,
misaligned,
requester: RequesterId::ANONYMOUS,
});
hart.timer = timer;
Ok(hart)
}
#[must_use]
pub fn config(&self) -> Config {
self.cfg
}
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 set_requester(&self, id: RequesterId) {
self.requester.store(id.0, Ordering::Relaxed);
}
fn effective_config(&self) -> Config {
Config {
requester: RequesterId(self.requester.load(Ordering::Relaxed)),
..self.cfg
}
}
#[must_use]
pub fn x(&self, index: u32) -> u64 {
self.session.lock().state.x[(index & 31) as usize]
}
pub fn set_x(&self, index: u32, value: u64) {
if index & 31 != 0 {
let value = self.cfg.xlen.sext(value);
self.session.lock().state.x[(index & 31) as usize] = value;
}
}
#[must_use]
pub fn f(&self, index: u32) -> u64 {
self.session.lock().state.f[(index & 31) as usize]
}
pub fn set_f(&self, index: u32, value: u64) {
self.session.lock().state.f[(index & 31) as usize] = value;
}
#[must_use]
pub fn pc(&self) -> u64 {
self.session.lock().state.pc
}
pub fn set_pc(&self, pc: u64) {
let pc = self.cfg.xlen.trunc(pc);
self.session.lock().state.pc = pc;
}
#[must_use]
pub fn priv_mode(&self) -> Priv {
self.session.lock().state.csrs.priv_mode
}
#[must_use]
pub fn csrs(&self) -> Csrs {
self.session.lock().state.csrs.clone()
}
pub fn set_csrs(&self, csrs: Csrs) {
let mut session = self.session.lock();
session.state.csrs = csrs;
session.tlb.flush();
}
#[must_use]
pub fn cycles(&self) -> u64 {
self.session.lock().state.cycles
}
#[must_use]
pub fn instret(&self) -> u64 {
self.session.lock().state.csrs.minstret
}
#[must_use]
pub fn is_waiting(&self) -> bool {
self.session.lock().state.wfi
}
#[must_use]
pub fn bus_faults(&self) -> u64 {
self.session.lock().state.faults
}
#[must_use]
pub fn tlb_stats(&self) -> (u64, u64) {
self.session.lock().tlb.stats()
}
pub fn set_interrupt(&self, mask: u64, asserted: bool) {
self.lines.set_pending(mask, asserted);
}
#[must_use]
pub fn interrupts(&self) -> u64 {
self.lines.pending()
}
pub fn request_reset(&self) {
self.lines.request_reset();
}
pub fn set_time(&self, now: u64) {
self.session.lock().state.csrs.mtime = now;
}
pub fn attach_time(&self, timer: Arc<AtomicU64>) {
self.session.lock().time_src = Some(timer);
}
pub fn step(&self) -> u64 {
self.step_to_exit().0
}
pub fn step_to_exit(&self) -> (u64, Option<Exit>) {
let cfg = self.effective_config();
let exits = self.exit_mask();
let mut session = self.session.lock();
if self.lines.take_reset_request() {
session.state = State::new(&cfg);
session.tlb.flush();
}
let Session {
state,
tlb,
space,
time_src,
} = &mut *session;
if let Some(timer) = time_src {
state.csrs.mtime = timer.load(Ordering::Relaxed);
}
let Some(space) = space.clone() else {
return (0, None);
};
let mut exec = Exec::new(state, tlb, &space, &cfg, &self.lines, exits);
let used = exec.step();
(used, exec.take_exit())
}
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 exit = self.exit.lock().clone();
let allowance = ticks - owed;
let mut used = 0u64;
while used < allowance {
let n = self.step();
if n == 0 {
break;
}
used += n;
if exit.as_ref().is_some_and(ExitFlag::raised) {
break;
}
}
if used >= allowance {
self.session.lock().state.debt = used - allowance;
ticks
} else {
self.session.lock().state.debt = 0;
owed + used
}
}
pub fn attach_cursor(&self, cursor: &TickCursor) {
*self.exit.lock() = Some(cursor.exit_flag());
}
#[must_use]
pub fn cycle_debt(&self) -> u64 {
self.session.lock().state.debt
}
pub fn run_to_exit_ticks(&self, ticks: u64) -> Run {
let owed = self.session.lock().state.debt;
if owed >= ticks {
self.session.lock().state.debt = owed - ticks;
return Run::completed(Consumed::new(ticks));
}
let allowance = ticks - owed;
let mut used = 0u64;
while used < allowance {
let (n, exit) = self.step_to_exit();
if n == 0 {
break;
}
used += n;
if let Some(exit) = exit {
let total = owed + used;
self.session.lock().state.debt = total.saturating_sub(ticks);
return Run::exited(Consumed::new(total.min(ticks)), exit);
}
}
if used >= allowance {
self.session.lock().state.debt = used - allowance;
Run::completed(Consumed::new(ticks))
} else {
self.session.lock().state.debt = 0;
Run::completed(Consumed::new(owed + used))
}
}
#[must_use]
pub fn translate_debug(&self, va: u64) -> Option<u64> {
let cfg = self.effective_config();
let session = self.session.lock();
let space = session.space.as_ref()?;
exec::debug_translate(&session.state, space, &cfg, va)
}
#[must_use]
pub fn disassemble_virtual(&self, pc: u64, count: usize) -> Vec<disasm::Disassembled> {
let Some(space) = self.space() else {
return Vec::new();
};
let xlen = self.cfg.xlen;
let attrs = MemAttrs::DEBUG.with_requester(self.effective_config().requester);
disasm::disassemble_run(pc, count, xlen, |addr| {
let phys = self
.translate_debug(addr)
.ok_or(disasm::Missing::Untranslated)?;
space
.read(phys, Width::U16, attrs)
.map(|v| v as u16)
.map_err(|_| disasm::Missing::Unmapped)
})
}
#[must_use]
pub fn disassemble_physical(&self, addr: u64, count: usize) -> Vec<disasm::Disassembled> {
let Some(space) = self.space() else {
return Vec::new();
};
let xlen = self.cfg.xlen;
let attrs = MemAttrs::DEBUG.with_requester(self.effective_config().requester);
disasm::disassemble_run(addr, count, xlen, |at| {
space
.read(at, Width::U16, attrs)
.map(|v| v as u16)
.map_err(|_| disasm::Missing::Unmapped)
})
}
}
pub static CLASS: DeviceClass = DeviceClass {
name: "cpu.riscv",
version: 1,
summary: "RISC-V RV64GC / RV32 hart with M/S/U modes, Sv39 paging and software IEEE-754",
properties: &[
PropertySpec {
name: "xlen",
kind: ValueKind::Str,
required: false,
summary: "register width: `rv32` or `rv64` (default rv64)",
},
PropertySpec {
name: "isa",
kind: ValueKind::Str,
required: false,
summary: "extension letters beyond I: any of `mafdc`, or `g` for `imafd`",
},
PropertySpec {
name: "hartid",
kind: ValueKind::Uint,
required: false,
summary: "the value `mhartid` reports",
},
PropertySpec {
name: "reset",
kind: ValueKind::Uint,
required: false,
summary: "the address the program counter starts at (default 0x80000000)",
},
PropertySpec {
name: "pmp",
kind: ValueKind::Uint,
required: false,
summary: "how many PMP entries are implemented; 0 means PMP is absent",
},
PropertySpec {
name: "misaligned",
kind: ValueKind::Bool,
required: false,
summary: "whether misaligned loads and stores are performed rather than trapped",
},
PropertySpec {
name: "supervisor",
kind: ValueKind::Bool,
required: false,
summary: "whether supervisor mode is implemented",
},
PropertySpec {
name: "user",
kind: ValueKind::Bool,
required: false,
summary: "whether user mode is implemented",
},
PropertySpec {
name: "engine",
kind: ValueKind::Str,
required: false,
summary: "which execution engine; only `interp` exists until phase 5",
},
PropertySpec {
name: "timer",
kind: ValueKind::Link,
required: false,
summary: "the object whose platform timer the `time` CSR reads (`timer = clint`)",
},
],
construct: |props| Ok(Box::new(Hart::from_props(props)?)),
};
pub fn register(reg: &mut Registry) -> Result<()> {
reg.add(&CLASS)
}
fn pin_mask(port: &str) -> Option<u64> {
match port {
"meip" => Some(irq::MEI),
"mtip" => Some(irq::MTI),
"msip" => Some(irq::MSI),
"seip" => Some(irq::SEI),
"stip" => Some(irq::STI),
_ => None,
}
}
impl Device for Hart {
fn class(&self) -> &'static DeviceClass {
&CLASS
}
fn debug_translate(&self, va: u64) -> DebugTranslation {
match self.translate_debug(va) {
Some(pa) => DebugTranslation::Mapped(pa),
None => DebugTranslation::Unmapped,
}
}
fn realize(&self, _ctx: &mut RealizeCtx<'_>) -> Result<()> {
Ok(())
}
fn sink(&self, port: &str, sources: &[WireId]) -> Option<SinkPin> {
let mut pins = self.pins.lock();
if port == "reset" {
let pin = Arc::new(ResetPin::new(Arc::clone(&self.lines), sources));
pins.reset = Some(Arc::clone(&pin));
return Some(SinkPin { sink: pin, line: 0 });
}
let mask = pin_mask(port)?;
let pin = Arc::new(InterruptPin::new(Arc::clone(&self.lines), mask, sources));
pins.interrupts.push((mask, Arc::clone(&pin)));
Some(SinkPin {
sink: pin,
line: mask.trailing_zeros(),
})
}
fn is_runnable(&self) -> bool {
true
}
fn run(&self, budget: Budget) -> Consumed {
Consumed::new(self.run_budget(budget.ticks))
}
fn attach_cursor(&self, cursor: TickCursor) {
Hart::attach_cursor(self, &cursor);
}
fn reset(&self, kind: ResetKind) {
let cfg = self.effective_config();
let mut session = self.session.lock();
session.state = State::new(&cfg);
session.tlb.flush();
drop(session);
if kind == ResetKind::Cold {
self.lines.set_all_pending(0);
}
}
fn save(&self, w: &mut ChunkWriter<'_>) -> Result<()> {
let session = self.session.lock();
let s = &session.state;
for r in s.x {
w.write_u64(r)?;
}
for r in s.f {
w.write_u64(r)?;
}
w.write_u64(s.pc)?;
w.write_u64(s.cycles)?;
w.write_u64(s.debt)?;
w.write_u64(s.faults)?;
w.write_bool(s.wfi)?;
match s.reservation {
None => w.write_bool(false)?,
Some(addr) => {
w.write_bool(true)?;
w.write_u64(addr)?;
}
}
let c = &s.csrs;
w.write_u8(c.priv_mode.bits() as u8)?;
for v in [
c.mstatus,
c.medeleg,
c.mideleg,
c.mie,
c.mtvec,
c.mcounteren,
c.mcountinhibit,
c.mscratch,
c.mepc,
c.mcause,
c.mtval,
c.menvcfg,
c.stvec,
c.scounteren,
c.sscratch,
c.sepc,
c.scause,
c.stval,
c.satp,
c.senvcfg,
c.fcsr,
c.minstret,
c.mcycle,
c.mtime,
] {
w.write_u64(v)?;
}
w.write_u64(c.pmp_count as u64)?;
for byte in c.pmpcfg {
w.write_u8(byte)?;
}
for addr in c.pmpaddr {
w.write_u64(addr)?;
}
w.write_u64(self.lines.pending())?;
Ok(())
}
fn load(&self, r: &mut ChunkReader<'_>) -> Result<()> {
let cfg = self.effective_config();
let mut s = State::new(&cfg);
for slot in &mut s.x {
*slot = r.read_u64()?;
}
s.x[0] = 0;
for slot in &mut s.f {
*slot = r.read_u64()?;
}
s.pc = r.read_u64()?;
s.cycles = r.read_u64()?;
s.debt = r.read_u64()?;
s.faults = r.read_u64()?;
s.wfi = r.read_bool()?;
s.reservation = if r.read_bool()? {
Some(r.read_u64()?)
} else {
None
};
let mode = r.read_u8()?;
s.csrs.priv_mode = Priv::from_bits(u64::from(mode))
.ok_or_else(|| Error::State(alloc::format!("unknown privilege mode {mode}")))?;
let c = &mut s.csrs;
for slot in [
&mut c.mstatus,
&mut c.medeleg,
&mut c.mideleg,
&mut c.mie,
&mut c.mtvec,
&mut c.mcounteren,
&mut c.mcountinhibit,
&mut c.mscratch,
&mut c.mepc,
&mut c.mcause,
&mut c.mtval,
&mut c.menvcfg,
&mut c.stvec,
&mut c.scounteren,
&mut c.sscratch,
&mut c.sepc,
&mut c.scause,
&mut c.stval,
&mut c.satp,
&mut c.senvcfg,
&mut c.fcsr,
&mut c.minstret,
&mut c.mcycle,
&mut c.mtime,
] {
*slot = r.read_u64()?;
}
c.pmp_count = (r.read_u64()? as usize).min(csr::PMP_ENTRIES);
for slot in &mut c.pmpcfg {
*slot = r.read_u8()?;
}
for slot in &mut c.pmpaddr {
*slot = r.read_u64()?;
}
let pending = r.read_u64()?;
let mut session = self.session.lock();
session.state = s;
session.tlb.flush();
drop(session);
self.lines.set_all_pending(pending);
Ok(())
}
}
impl ExitingCore for Hart {
fn exit_mask(&self) -> ExitMask {
ExitMask::from_bits(self.exits.load(Ordering::Relaxed))
}
fn set_exit_mask(&self, mask: ExitMask) {
self.exits.store(mask.bits(), Ordering::Relaxed);
}
fn run_to_exit(&self, budget: Budget) -> Run {
self.run_to_exit_ticks(budget.ticks)
}
fn pc(&self) -> u64 {
Hart::pc(self)
}
fn set_pc(&self, pc: u64) {
Hart::set_pc(self, pc);
}
fn sp(&self) -> u64 {
self.x(2)
}
fn set_sp(&self, sp: u64) {
self.set_x(2, sp);
}
}
impl Initiator for Hart {
fn requester(&self) -> RequesterId {
RequesterId(self.requester.load(Ordering::Relaxed))
}
}
impl crate::machine::Instance for Hart {
fn bind(&self, ctx: &crate::machine::BindCtx<'_>) -> Result<()> {
let space = ctx.space().ok_or_else(|| Error::Config {
at: ctx.path().to_string(),
message: "a RISC-V hart needs an address space to fetch from (`space = mem`)"
.to_string(),
})?;
self.attach_space(Arc::clone(space));
self.set_requester(ctx.requester());
if let Some(path) = &self.timer {
self.attach_time(ctx.export_cell(path, ExportId::TIMEBASE)?);
}
Ok(())
}
}
pub fn bind(bindings: &mut crate::machine::Bindings) -> Result<()> {
bindings.bind(CLASS.name, |props| Ok(Arc::new(Hart::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("xlen", ValueKind::Str).values(&["rv32", "rv64"]))
.prop(PropSchema::new("isa", ValueKind::Str))
.prop(PropSchema::new("hartid", ValueKind::Uint))
.prop(PropSchema::new("reset", ValueKind::Uint))
.prop(PropSchema::new("pmp", ValueKind::Uint).range(0, csr::PMP_ENTRIES as u64))
.prop(PropSchema::new("misaligned", ValueKind::Bool))
.prop(PropSchema::new("supervisor", ValueKind::Bool))
.prop(PropSchema::new("user", ValueKind::Bool))
.prop(PropSchema::new("engine", ValueKind::Str).values(&["interp"]))
.prop(PropSchema::new("timer", ValueKind::Link))
.port("meip", PortDir::In)
.port("mtip", PortDir::In)
.port("msip", PortDir::In)
.port("seip", PortDir::In)
.port("stip", PortDir::In)
.port("reset", PortDir::In)
}
#[derive(Debug)]
pub struct InterruptPin {
lines: Arc<Lines>,
mask: u64,
inputs: FanIn,
resolve: Resolve,
}
impl InterruptPin {
#[must_use]
pub fn new(lines: Arc<Lines>, mask: u64, sources: &[WireId]) -> InterruptPin {
InterruptPin {
lines,
mask,
inputs: FanIn::new(sources),
resolve: Resolve::Or,
}
}
#[must_use]
pub fn with_resolve(mut self, resolve: Resolve) -> Self {
self.resolve = resolve;
self
}
#[must_use]
pub fn mask(&self) -> u64 {
self.mask
}
#[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();
self.lines.set_pending(self.mask, asserted);
}
}
#[derive(Debug)]
pub struct ResetPin {
lines: Arc<Lines>,
inputs: FanIn,
resolve: Resolve,
}
impl ResetPin {
#[must_use]
pub 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();
}
}
}
#[must_use]
pub fn describe_isa() -> String {
use core::fmt::Write as _;
let mut out = String::new();
for insn in isa::TABLE {
let _ = writeln!(
out,
"{:08x}/{:08x} {:<10} {:<8} {}",
insn.bits,
insn.mask,
insn.op.mnemonic(),
insn.ext.name(),
insn.op.summary()
);
}
for insn in isa::CTABLE {
let _ = writeln!(
out,
" {:04x}/{:04x} {:<10} {:<8} {}",
insn.bits,
insn.mask,
insn.op.mnemonic(),
"c",
insn.op.summary()
);
}
out
}