use crate::arch::Hart;
use crate::arch::trap::irq_to_trap;
use crate::exec::compute::vector::shadow::VectorWrites;
use crate::isa::csr;
use crate::isa::privileged::{PrivilegeMode, Trap};
use crate::isa::reg::RegIdx;
use crate::isa::rvv::VectorConfig;
const INTERRUPT_PRIORITY: [(u64, u64); 6] = [
(csr::MIP_MEIP, csr::MIE_MEIP),
(csr::MIP_MSIP, csr::MIE_MSIP),
(csr::MIP_MTIP, csr::MIE_MTIE),
(csr::MIP_SEIP, csr::MIE_SEIP),
(csr::MIP_SSIP, csr::MIE_SSIP),
(csr::MIP_STIP, csr::MIE_STIE),
];
#[must_use]
pub fn pending_interrupt(hart: &Hart) -> Option<Trap> {
let csrs = &hart.csrs;
let m_global_ie = (csrs.mstatus & csr::MSTATUS_MIE) != 0;
let s_global_ie = (csrs.mstatus & csr::MSTATUS_SIE) != 0;
let privilege = hart.privilege;
let check = |bit: u64, enable_bit: u64| -> Option<Trap> {
if (csrs.mip & bit) == 0 || (csrs.mie & enable_bit) == 0 {
return None;
}
let delegated = (csrs.mideleg & bit) != 0;
let target = if delegated { PrivilegeMode::Supervisor } else { PrivilegeMode::Machine };
let below = privilege.to_u8() < target.to_u8();
let enabled_here = privilege == target
&& match target {
PrivilegeMode::Machine => m_global_ie,
PrivilegeMode::Supervisor => s_global_ie,
PrivilegeMode::User => false,
};
(below || enabled_here).then(|| irq_to_trap(bit))
};
[false, true].into_iter().find_map(|to_supervisor| {
INTERRUPT_PRIORITY
.iter()
.filter(|&&(bit, _)| (csrs.mideleg & bit != 0) == to_supervisor)
.find_map(|&(bit, enable_bit)| check(bit, enable_bit))
})
}
pub const fn write_int(hart: &mut Hart, rd: RegIdx, value: u64) {
if !rd.is_zero() {
hart.regs.write(rd, value);
}
}
pub const fn write_fp(hart: &mut Hart, rd: RegIdx, value: u64) {
hart.regs.write_f(rd, value);
mark_fp_dirty(hart);
}
pub fn apply_vector_writes(hart: &mut Hart, writes: &VectorWrites) {
writes.apply(hart.regs.vpr_mut());
mark_vector_retired(hart);
}
pub const fn mark_vector_retired(hart: &mut Hart) {
hart.csrs.mstatus = (hart.csrs.mstatus & !csr::MSTATUS_VS) | csr::MSTATUS_VS_DIRTY;
hart.csrs.vstart = 0;
}
pub const fn accrue_fp_flags(hart: &mut Hart, flags: u8) {
if flags != 0 {
hart.csrs.fflags |= flags as u64;
mark_fp_dirty(hart);
}
}
pub const fn apply_vector_config(hart: &mut Hart, config: VectorConfig) {
hart.csrs.vtype = config.vtype;
hart.csrs.vl = config.vl;
mark_vector_retired(hart);
}
pub const fn wfi(hart: &mut Hart) -> bool {
let waits = hart.csrs.mie != 0 || hart.csrs.mip != 0;
if waits {
hart.wfi_waiting = true;
}
waits
}
const fn mark_fp_dirty(hart: &mut Hart) {
hart.csrs.mstatus = (hart.csrs.mstatus & !csr::MSTATUS_FS) | csr::MSTATUS_FS_DIRTY;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::Config;
#[test]
fn no_interrupt_is_taken_when_none_is_pending() {
let config = Config::default();
let mut sys = crate::system::SystemState::build(&config, "");
let state = sys.core_ctx(0);
assert!(pending_interrupt(state.hart).is_none());
}
#[test]
fn a_pending_enabled_machine_interrupt_is_taken_in_machine_mode() {
let config = Config::default();
let mut sys = crate::system::SystemState::build(&config, "");
let state = sys.core_ctx(0);
state.hart.csrs.mip = csr::MIP_MEIP;
state.hart.csrs.mie = csr::MIE_MEIP;
state.hart.csrs.mstatus |= csr::MSTATUS_MIE;
state.hart.privilege = PrivilegeMode::Machine;
assert_eq!(pending_interrupt(state.hart), Some(Trap::MachineExternalInterrupt));
}
#[test]
fn a_delegated_interrupt_is_taken_in_supervisor_mode() {
let config = Config::default();
let mut sys = crate::system::SystemState::build(&config, "");
let state = sys.core_ctx(0);
state.hart.csrs.mip = csr::MIP_SEIP;
state.hart.csrs.mie = csr::MIE_SEIP;
state.hart.csrs.mstatus |= csr::MSTATUS_SIE;
state.hart.csrs.mideleg |= csr::MIP_SEIP;
state.hart.privilege = PrivilegeMode::Supervisor;
assert_eq!(pending_interrupt(state.hart), Some(Trap::SupervisorExternalInterrupt));
}
}