Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/isa/riscv/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5497,7 +5497,8 @@ mod tests {
);
assert_eq!(c.csr_read(0x104).unwrap(), ssip | stip);

c.csr_write(0x344, msip | mtip | ssip | stip).unwrap(); // mip
c.csr_write(0x344, msip | mtip).unwrap(); // mip (WARL: M-mode bits only)
c.set_interrupt_pending(ssip | stip, true); // supervisor bits are externally pending
assert_eq!(c.csr_read(0x144).unwrap(), ssip | stip);
c.csr_write(0x144, 0).unwrap();
assert_eq!(c.csr_read(0x344).unwrap() & ssip, 0);
Expand Down
87 changes: 83 additions & 4 deletions src/isa/riscv/cpu/csr_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,27 @@ impl RiscVCpu {
// Jump-table mode zero is the only currently defined/implemented
// WARL mode; BASE is consequently always 64-byte aligned.
Csr::Jvt => self.jvt = value & !0x3f & self.xmask(),
Csr::Mstatus => self.mstatus = value,
Csr::Mstatus => {
// norm:mstatus: MPP is WARL; the 0b10 encoding is reserved
// and must canonicalize to a legal value (0b00 here).
let mut v = value;
if (v >> 11) & 0b11 == 0b10 {
v &= !(0b11 << 11);
}
self.mstatus = v;
}
Csr::Sstatus => {
let mask = self.sstatus_mask();
self.mstatus = (self.mstatus & !mask) | (value & mask);
}
Csr::Medeleg => self.medeleg = value,
Csr::Mideleg => self.mideleg = value,
Csr::Medeleg => {
// norm:medeleg: only the delegable exception causes are
// writable (bits 1,3,4,6,7,8,9); reserved bits are zero.
let delegable =
(1 << 1) | (1 << 3) | (1 << 4) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9);
self.medeleg = value & delegable & self.xmask();
}
Csr::Mideleg => self.mideleg = value & S_INTERRUPT_MASK & self.xmask(),
Csr::Mie => self.mie = value,
Csr::Sie => {
let mask = self.supervisor_interrupt_mask();
Expand All @@ -135,7 +149,7 @@ impl RiscVCpu {
Csr::Mepc => self.mepc = value & self.mepc_alignment_mask() & self.xmask(),
Csr::Mcause => self.mcause = value,
Csr::Mtval => self.mtval = value,
Csr::Mip => self.mip = value,
Csr::Mip => self.mip = value & M_INTERRUPT_MASK & self.xmask(),
Csr::Sip => {
let mask = self.supervisor_software_interrupt_mask();
self.mip = (self.mip & !mask) | (value & mask);
Expand Down Expand Up @@ -295,6 +309,71 @@ mod tests {
}
}

#[test]
fn mstatus_mpp_reserved_encoding_is_warl_normalized() {
let mut cpu = cpu(Isa::rv_i());
// 0b10 is reserved: write must canonicalize to a supported encoding.
cpu.csr_write(0x300, 0b10 << 11).unwrap();
assert_eq!(
(cpu.csr_read(0x300).unwrap() >> 11) & 0b11,
0b00,
"reserved MPP=0b10 must read back as 0b00"
);
// Legal encodings are stored verbatim.
for mpp in [0b00, 0b01, 0b11] {
cpu.csr_write(0x300, mpp << 11).unwrap();
assert_eq!(
(cpu.csr_read(0x300).unwrap() >> 11) & 0b11,
mpp,
"legal MPP={mpp:#04b} must be preserved"
);
}
}

#[test]
fn mip_warl_masks_reserved_bit0() {
let mut cpu = cpu(Isa::rv_i());
// mip[0] is reserved and must stay zero.
cpu.csr_write(0x344, 1).unwrap();
assert_eq!(cpu.csr_read(0x344).unwrap() & 1, 0, "mip bit0 must stay 0");
// All implemented interrupt-pending bits remain writable.
let legal = (1 << 3) | (1 << 7) | (1 << 11); // MSIP, MTIP, MEIP
cpu.csr_write(0x344, legal).unwrap();
assert_eq!(cpu.csr_read(0x344).unwrap() & legal, legal);
}

#[test]
fn mideleg_warl_masks_reserved_bit0() {
let mut cpu = cpu(Isa::rv_i());
// mideleg[0] is reserved and must stay zero.
cpu.csr_write(0x303, 1).unwrap();
assert_eq!(
cpu.csr_read(0x303).unwrap() & 1,
0,
"mideleg bit0 must stay 0"
);
// The delegable supervisor interrupts remain writable.
let legal = (1 << 1) | (1 << 5) | (1 << 9); // SSIP, STIP, SEIP
cpu.csr_write(0x303, legal).unwrap();
assert_eq!(cpu.csr_read(0x303).unwrap() & legal, legal);
}

#[test]
fn medeleg_warl_masks_reserved_bit10() {
let mut cpu = cpu(Isa::rv_i());
// Exception cause 10 is reserved and must stay zero.
cpu.csr_write(0x302, 1 << 10).unwrap();
assert_eq!(
cpu.csr_read(0x302).unwrap() & (1 << 10),
0,
"medeleg bit10 must stay 0"
);
// The delegable exception causes remain writable.
let legal = (1 << 1) | (1 << 3) | (1 << 4) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9);
cpu.csr_write(0x302, legal).unwrap();
assert_eq!(cpu.csr_read(0x302).unwrap() & legal, legal);
}

#[test]
fn csr_availability_tracks_xlen_and_declaring_extensions() {
let mut unavailable = cpu(Isa::rv_i());
Expand Down
Loading