use std::num::{NonZeroU32, NonZeroU64};
pub use irv_traits::*;
mod instruction;
mod memory;
pub use memory::Memory;
#[derive(Debug)]
pub enum Exception {
InstructionAddressMisaligned { address: Option<NonZeroU64> },
InstructionAccessFault { address: Option<NonZeroU64> },
IllegalInstruction { instruction: Option<NonZeroU32> },
Breakpoint { address: Option<NonZeroU64> },
LoadAddressMisaligned { address: Option<NonZeroU64> },
LoadAccessFault { address: Option<NonZeroU64> },
StoreAmoAddressMisaligned { address: Option<NonZeroU64> },
StoreAmoAccessFault { address: Option<NonZeroU64> },
EnvironmentCall,
}
pub struct BaseHart<B, C> {
pub bus: B,
pub pc: u64,
pub next: u64,
pub gpr: [u64; 32],
pub csr: C,
result: Result<(), Exception>,
}
impl<B, C> BaseHart<B, C> {
pub fn new(bus: B, csr: C) -> BaseHart<B, C> {
BaseHart {
bus,
pc: 0,
next: 0,
gpr: [0; 32],
csr,
result: Ok(()),
}
}
pub fn execute(&mut self) -> Result<(), Exception>
where
B: Bus<u64, u64> + Bus<u64, u32> + Bus<u64, u16> + Bus<u64, u8>,
C: Csr,
{
let raw: u32 = match self.bus.load(self.pc) {
Ok(raw) => raw,
Err(BusError::AccessFault) => {
return Err(Exception::InstructionAccessFault {
address: NonZeroU64::new(self.pc),
})
}
Err(BusError::AddressMisaligned) => {
return Err(Exception::InstructionAddressMisaligned {
address: NonZeroU64::new(self.pc),
})
}
};
let funct3_opcode = raw & 0b1111111 | raw >> 5 & 0b111 << 7;
self.gpr[0] = 0;
self.next = self.pc.wrapping_add(4);
let instruction = match funct3_opcode {
0b000_0110111 | 0b001_0110111 | 0b010_0110111 | 0b011_0110111 | 0b100_0110111
| 0b101_0110111 | 0b110_0110111 | 0b111_0110111 => instruction::lui,
0b000_0010111 | 0b001_0010111 | 0b010_0010111 | 0b011_0010111 | 0b100_0010111
| 0b101_0010111 | 0b110_0010111 | 0b111_0010111 => instruction::auipc,
0b000_1101111 | 0b001_1101111 | 0b010_1101111 | 0b011_1101111 | 0b100_1101111
| 0b101_1101111 | 0b110_1101111 | 0b111_1101111 => instruction::jal,
0b000_1100111 => instruction::jalr,
0b000_1100011 => instruction::beq,
0b001_1100011 => instruction::bne,
0b100_1100011 => instruction::blt,
0b101_1100011 => instruction::bge,
0b110_1100011 => instruction::bltu,
0b111_1100011 => instruction::bgeu,
0b000_0000011 => instruction::lb,
0b001_0000011 => instruction::lh,
0b010_0000011 => instruction::lw,
0b011_0000011 => instruction::ld,
0b100_0000011 => instruction::lbu,
0b101_0000011 => instruction::lhu,
0b110_0000011 => instruction::lwu,
0b000_0100011 => instruction::sb,
0b001_0100011 => instruction::sh,
0b010_0100011 => instruction::sw,
0b011_0100011 => instruction::sd,
0b000_0010011 => instruction::addi,
0b000_0011011 => instruction::addiw,
0b010_0010011 => instruction::slti,
0b011_0010011 => instruction::sltiu,
0b100_0010011 => instruction::xori,
0b110_0010011 => instruction::ori,
0b111_0010011 => instruction::andi,
0b001_0010011 => instruction::slli,
0b101_0010011 => instruction::srxi,
0b001_0011011 => instruction::slliw,
0b101_0011011 => instruction::srxiw,
0b000_0110011 => instruction::add_sub,
0b000_0111011 => instruction::addw_subw,
0b001_0110011 => instruction::sll,
0b010_0110011 => instruction::slt,
0b011_0110011 => instruction::sltu,
0b100_0110011 => instruction::xor,
0b101_0110011 => instruction::srx,
0b110_0110011 => instruction::or,
0b111_0110011 => instruction::and,
0b001_0111011 => instruction::sllw,
0b101_0111011 => instruction::srxw,
0b000_0001111 => instruction::fence,
0b001_0001111 => instruction::fence_i,
0b000_1110011 => instruction::ecall_ebreak,
0b001_1110011 => instruction::csrrw,
0b010_1110011 => instruction::csrrs,
0b011_1110011 => instruction::csrrc,
0b101_1110011 => instruction::csrrwi,
0b110_1110011 => instruction::csrrsi,
0b111_1110011 => instruction::csrrci,
_ => |hart: &mut BaseHart<B, C>, raw| {
hart.raise(Exception::IllegalInstruction {
instruction: NonZeroU32::new(raw),
})
},
};
instruction(self, raw);
self.pc = self.next;
let mut result = Ok(());
std::mem::swap(&mut self.result, &mut result);
result
}
fn raise(&mut self, exception: Exception) {
self.result = Err(exception);
}
}