cleaning up simulation -- more tests pass
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::{Arc, RwLock, mpsc::Receiver};
|
||||
|
||||
pub use super::mmu::Memory;
|
||||
use super::mmu::{AddressingMode, Mmu};
|
||||
@ -54,6 +54,13 @@ pub const MIP_SEIP: u64 = 0x200;
|
||||
const MIP_STIP: u64 = 0x020;
|
||||
const MIP_SSIP: u64 = 0x002;
|
||||
|
||||
pub enum TickResult {
|
||||
Ok,
|
||||
ExitThread(u64),
|
||||
PauseEmulation(Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>),
|
||||
}
|
||||
|
||||
|
||||
/// Emulates a RISC-V CPU core
|
||||
pub struct Cpu {
|
||||
clock: u64,
|
||||
@ -120,6 +127,7 @@ pub enum TrapType {
|
||||
UserExternalInterrupt,
|
||||
SupervisorExternalInterrupt,
|
||||
MachineExternalInterrupt,
|
||||
PauseEmulation(std::sync::mpsc::Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>),
|
||||
}
|
||||
|
||||
fn _get_privilege_mode_name(mode: &PrivilegeMode) -> &'static str {
|
||||
@ -176,6 +184,7 @@ fn _get_trap_type_name(trap_type: &TrapType) -> &'static str {
|
||||
TrapType::UserExternalInterrupt => "UserExternalInterrupt",
|
||||
TrapType::SupervisorExternalInterrupt => "SupervisorExternalInterrupt",
|
||||
TrapType::MachineExternalInterrupt => "MachineExternalInterrupt",
|
||||
TrapType::PauseEmulation(_) => "PauseEmulation",
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,6 +208,7 @@ fn get_trap_cause(trap: &Trap, xlen: &Xlen) -> u64 {
|
||||
TrapType::InstructionPageFault => 12,
|
||||
TrapType::LoadPageFault => 13,
|
||||
TrapType::StorePageFault => 15,
|
||||
TrapType::PauseEmulation(_) => 16,
|
||||
TrapType::UserSoftwareInterrupt => interrupt_bit,
|
||||
TrapType::SupervisorSoftwareInterrupt => interrupt_bit + 1,
|
||||
TrapType::MachineSoftwareInterrupt => interrupt_bit + 3,
|
||||
@ -213,6 +223,8 @@ fn get_trap_cause(trap: &Trap, xlen: &Xlen) -> u64 {
|
||||
|
||||
pub struct CpuBuilder {
|
||||
xlen: Xlen,
|
||||
pc: u64,
|
||||
sp: u64,
|
||||
memory: Arc<RwLock<dyn Memory + Send + Sync>>,
|
||||
}
|
||||
|
||||
@ -221,6 +233,8 @@ impl CpuBuilder {
|
||||
CpuBuilder {
|
||||
xlen: Xlen::Bit64,
|
||||
memory,
|
||||
pc: 0,
|
||||
sp: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,9 +243,20 @@ impl CpuBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pc(mut self, pc: u64) -> Self {
|
||||
self.pc = pc;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn sp(mut self, sp: u64) -> Self {
|
||||
self.sp = sp;
|
||||
self
|
||||
}
|
||||
pub fn build(self) -> Cpu {
|
||||
let mut cpu = Cpu::new(self.memory);
|
||||
cpu.update_xlen(self.xlen.clone());
|
||||
cpu.update_pc(self.pc);
|
||||
cpu.write_register(2, self.sp as i64);
|
||||
cpu
|
||||
}
|
||||
}
|
||||
@ -317,10 +342,22 @@ impl Cpu {
|
||||
}
|
||||
|
||||
/// Runs program one cycle. Fetch, decode, and execution are completed in a cycle so far.
|
||||
pub fn tick(&mut self) {
|
||||
pub fn tick(&mut self) -> TickResult {
|
||||
let instruction_address = self.pc;
|
||||
match self.tick_operate() {
|
||||
Ok(()) => {}
|
||||
Err(Trap {
|
||||
trap_type: TrapType::PauseEmulation(rx),
|
||||
..
|
||||
}) => {
|
||||
return TickResult::PauseEmulation(rx);
|
||||
}
|
||||
Err(Trap {
|
||||
trap_type: TrapType::InstructionPageFault,
|
||||
value: 0xff803000,
|
||||
}) => {
|
||||
return TickResult::ExitThread(self.read_register(10) as u64);
|
||||
}
|
||||
Err(e) => self.handle_exception(e, instruction_address),
|
||||
}
|
||||
self.mmu.tick(&mut self.csr[CSR_MIP_ADDRESS as usize]);
|
||||
@ -331,6 +368,8 @@ impl Cpu {
|
||||
// just an arbiraty ratio.
|
||||
// @TODO: Implement more properly
|
||||
self.write_csr_raw(CSR_CYCLE_ADDRESS, self.clock * 8);
|
||||
|
||||
TickResult::Ok
|
||||
}
|
||||
|
||||
// @TODO: Rename?
|
||||
@ -742,7 +781,7 @@ impl Cpu {
|
||||
// println!("Fetching word from {:08x}...", self.pc);
|
||||
self.mmu.fetch_word(self.pc).map_err(|e| {
|
||||
self.pc = self.pc.wrapping_add(4); // @TODO: What if instruction is compressed?
|
||||
println!("Fetch error: {:x?}", e);
|
||||
// println!("Fetch error: {:x?}", e);
|
||||
e
|
||||
})
|
||||
}
|
||||
@ -1465,14 +1504,6 @@ impl Cpu {
|
||||
&mut self.mmu
|
||||
}
|
||||
|
||||
// pub fn memory_base(&self) -> u64 {
|
||||
// self.memory_base
|
||||
// }
|
||||
|
||||
// pub fn memory_size(&self) -> u64 {
|
||||
// self.mmu.memory_size()
|
||||
// }
|
||||
|
||||
pub fn phys_read_u32(&self, address: u64) -> u32 {
|
||||
self.mmu.load_word_raw(address)
|
||||
}
|
||||
@ -2413,16 +2444,23 @@ const INSTRUCTIONS: [Instruction; INSTRUCTION_NUM] = [
|
||||
mask: 0xffffffff,
|
||||
data: 0x00000073,
|
||||
name: "ECALL",
|
||||
operation: |cpu, _word, _address| {
|
||||
operation: |cpu, _word, address| {
|
||||
let mut args = [0i64; 8];
|
||||
for (src, dest) in cpu.x[10..].iter().zip(args.iter_mut()) {
|
||||
*dest = *src;
|
||||
}
|
||||
let result = cpu.memory.write().unwrap().syscall(args);
|
||||
for (src, dest) in result.iter().zip(cpu.x[10..].iter_mut()) {
|
||||
*dest = *src;
|
||||
match cpu.memory.write().unwrap().syscall(args) {
|
||||
super::mmu::SyscallResult::Ok(result) => {
|
||||
for (src, dest) in result.iter().zip(cpu.x[10..].iter_mut()) {
|
||||
*dest = *src;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
super::mmu::SyscallResult::Defer(receiver) => Err(Trap {
|
||||
trap_type: TrapType::PauseEmulation(receiver),
|
||||
value: address,
|
||||
}),
|
||||
}
|
||||
Ok(())
|
||||
|
||||
// let exception_type = match cpu.privilege_mode {
|
||||
// PrivilegeMode::User => TrapType::EnvironmentCallFromUMode,
|
||||
|
@ -5,6 +5,23 @@ use std::{
|
||||
|
||||
use crate::cpu::{decode_privilege_mode, PrivilegeMode, Trap, TrapType, Xlen};
|
||||
|
||||
pub enum SyscallResult {
|
||||
Ok([i64; 8]),
|
||||
Defer(std::sync::mpsc::Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>),
|
||||
}
|
||||
|
||||
impl From<[i64; 8]> for SyscallResult {
|
||||
fn from(args: [i64; 8]) -> Self {
|
||||
SyscallResult::Ok(args)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::sync::mpsc::Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>> for SyscallResult {
|
||||
fn from(receiver: std::sync::mpsc::Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>) -> Self {
|
||||
SyscallResult::Defer(receiver)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Memory {
|
||||
fn read_u8(&self, p_address: u64) -> u8;
|
||||
fn read_u16(&self, p_address: u64) -> u16;
|
||||
@ -15,7 +32,7 @@ pub trait Memory {
|
||||
fn write_u32(&mut self, p_address: u64, value: u32);
|
||||
fn write_u64(&mut self, p_address: u64, value: u64);
|
||||
fn validate_address(&self, address: u64) -> bool;
|
||||
fn syscall(&mut self, args: [i64; 8]) -> [i64; 8];
|
||||
fn syscall(&mut self, args: [i64; 8]) -> SyscallResult;
|
||||
}
|
||||
|
||||
/// Emulates Memory Management Unit. It holds the Main memory and peripheral
|
||||
|
Reference in New Issue
Block a user