pub mod debug;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use crate::common::{HartId, PhysAddr, SimError};
use crate::config::Config;
use crate::isa::op::MemWidth;
use crate::isa::privileged::Trap;
use crate::sim::events::EventQueue;
use crate::sim::memory::GlobalMemory;
use crate::sim::memory::write_log::Writer;
use crate::sim::stats::Stats;
use crate::sim::stats::paths::HartPaths;
use crate::soc::bus::Bus;
use crate::soc::cache::Cache;
use crate::soc::coherence::CoherenceFabric;
use crate::soc::devices::Htif;
use crate::soc::memory::controller::MemoryController;
use crate::soc::topology::Topology;
use debug::HartDebug;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TraceControl {
pub armed: bool,
pub harts: Vec<HartId>,
pub cycle_from: Option<u64>,
pub cycle_to: Option<u64>,
pub trap_causes: Vec<u64>,
}
impl TraceControl {
#[must_use]
pub fn applies(&self, hart: Option<HartId>, cycle: u64) -> bool {
self.armed
&& self.cycle_from.is_none_or(|from| cycle >= from)
&& self.cycle_to.is_none_or(|to| cycle <= to)
&& hart.is_none_or(|h| self.harts.is_empty() || self.harts.contains(&h))
}
#[must_use]
pub fn trap_visible(&self, code: u64, routine: bool) -> bool {
if self.trap_causes.is_empty() { !routine } else { self.trap_causes.contains(&code) }
}
}
#[derive(Debug)]
pub struct Uncore {
pub trace: TraceControl,
pub topology: Topology,
pub cycle: u64,
pub exited_at: Option<u64>,
pub bus: Bus,
pub mem_controller: Box<dyn MemoryController + Send + Sync>,
pub l3_cache: Cache,
pub coherence: Option<CoherenceFabric>,
pub memory: GlobalMemory,
pub config: Config,
pub per_hart_debug: Vec<HartDebug>,
pub panic_detected_at_cycle: Option<u64>,
#[cfg(feature = "commit-log")]
pub commit_log: Option<std::io::BufWriter<std::fs::File>>,
pub exit_signal: Arc<AtomicU64>,
pub direct_mode: bool,
pub event_queue: EventQueue,
pub stats: Stats,
pub stats_epoch: StatsEpoch,
pub stats_dumps: Vec<StatsDump>,
pub pending_break: Option<u64>,
pub hart_stat_paths: Vec<HartPaths>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct StatsEpoch {
pub cycle: u64,
pub instructions_retired: u64,
}
#[derive(Clone, Debug)]
pub struct StatsDump {
pub label: u64,
pub epoch: StatsEpoch,
pub stats: Stats,
pub cycles: u64,
pub instructions_retired: u64,
}
impl Uncore {
pub fn publish_write(&mut self, writer: Writer, paddr: PhysAddr, data: u64, width: MemWidth) {
let width_bytes = width.bytes();
if width_bytes == 0 || !self.bus.is_ram(paddr, width_bytes) {
return;
}
self.memory.write(writer, paddr, data, width_bytes as usize);
}
#[must_use]
pub fn trace_trap_enabled(&self, trap: &Trap) -> bool {
self.config.general.trace_instructions
&& self.trace.trap_visible(trap.mcause_code(), trap.is_routine())
}
#[must_use]
pub const fn reported_cycle(&self) -> u64 {
match self.exited_at {
Some(cycle) => cycle,
None => self.cycle,
}
}
pub fn take_exit(&self) -> Option<u64> {
let val = self.exit_signal.swap(u64::MAX, Ordering::Relaxed);
if val == u64::MAX { None } else { Some(val) }
}
pub fn check_exit(&self) -> Option<u64> {
let val = self.exit_signal.load(Ordering::Relaxed);
if val == u64::MAX { None } else { Some(val) }
}
pub fn signal_exit(&self, code: u64) {
self.exit_signal.store(code, Ordering::Relaxed);
}
pub fn add_htif(&mut self, tohost_addr: u64, exit_signal: &Arc<AtomicU64>) {
let htif = Htif::new(tohost_addr, exit_signal.clone());
self.bus.add_device(Box::new(htif));
}
pub fn load_binary_at(&mut self, image: &[u8], addr: PhysAddr) {
self.memory.load(addr, image);
}
#[cfg(feature = "commit-log")]
pub fn open_commit_log(&mut self, path: &str) -> Result<(), crate::common::SimError> {
use std::fs::File;
use std::io::BufWriter;
let file = File::create(path).map_err(|source| crate::common::SimError::FileRead {
path: path.to_owned(),
source,
})?;
self.commit_log = Some(BufWriter::with_capacity(1 << 20, file));
Ok(())
}
pub fn pre_cycle(&mut self) -> Result<bool, SimError> {
if self.check_exit().is_some() {
return Ok(false);
}
if self.bus.check_kernel_panic() {
let detected_at = *self.panic_detected_at_cycle.get_or_insert(self.cycle);
if self.cycle.saturating_sub(detected_at) >= 10_000 {
return Err(SimError::KernelPanic { cycle: detected_at });
}
}
self.bus.tick();
Ok(true)
}
pub const fn advance_cycle(&mut self) {
self.cycle += 1;
}
}