wup getting ecall
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
141cb2e9dc
commit
fc18060bd9
79
Cargo.lock
generated
79
Cargo.lock
generated
@ -2,6 +2,47 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "goblin"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f27c1b4369c2cd341b5de549380158b105a04c331be5db9110eef7b6d2742134"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"plain",
|
||||||
|
"scroll",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plain"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.71"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "riscv-cpu"
|
name = "riscv-cpu"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -10,5 +51,43 @@ version = "0.1.0"
|
|||||||
name = "rouns"
|
name = "rouns"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"goblin",
|
||||||
"riscv-cpu",
|
"riscv-cpu",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scroll"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
|
||||||
|
dependencies = [
|
||||||
|
"scroll_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scroll_derive"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
@ -9,3 +9,4 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
riscv-cpu = { path = "crates/riscv-cpu" }
|
riscv-cpu = { path = "crates/riscv-cpu" }
|
||||||
|
goblin = { version = "0.7.1", features = [ "std", "elf32", "alloc" ]}
|
||||||
|
@ -23,12 +23,12 @@ const CSR_SIDELEG_ADDRESS: u16 = 0x103;
|
|||||||
const CSR_SIE_ADDRESS: u16 = 0x104;
|
const CSR_SIE_ADDRESS: u16 = 0x104;
|
||||||
const CSR_STVEC_ADDRESS: u16 = 0x105;
|
const CSR_STVEC_ADDRESS: u16 = 0x105;
|
||||||
const _CSR_SSCRATCH_ADDRESS: u16 = 0x140;
|
const _CSR_SSCRATCH_ADDRESS: u16 = 0x140;
|
||||||
const CSR_SEPC_ADDRESS: u16 = 0x141;
|
pub const CSR_SEPC_ADDRESS: u16 = 0x141;
|
||||||
const CSR_SCAUSE_ADDRESS: u16 = 0x142;
|
const CSR_SCAUSE_ADDRESS: u16 = 0x142;
|
||||||
const CSR_STVAL_ADDRESS: u16 = 0x143;
|
const CSR_STVAL_ADDRESS: u16 = 0x143;
|
||||||
const CSR_SIP_ADDRESS: u16 = 0x144;
|
const CSR_SIP_ADDRESS: u16 = 0x144;
|
||||||
const CSR_SATP_ADDRESS: u16 = 0x180;
|
pub const CSR_SATP_ADDRESS: u16 = 0x180;
|
||||||
const CSR_MSTATUS_ADDRESS: u16 = 0x300;
|
pub const CSR_MSTATUS_ADDRESS: u16 = 0x300;
|
||||||
// const CSR_MISA_ADDRESS: u16 = 0x301;
|
// const CSR_MISA_ADDRESS: u16 = 0x301;
|
||||||
const CSR_MEDELEG_ADDRESS: u16 = 0x302;
|
const CSR_MEDELEG_ADDRESS: u16 = 0x302;
|
||||||
const CSR_MIDELEG_ADDRESS: u16 = 0x303;
|
const CSR_MIDELEG_ADDRESS: u16 = 0x303;
|
||||||
@ -82,8 +82,7 @@ pub enum Xlen {
|
|||||||
Bit64, // @TODO: Support Bit128
|
Bit64, // @TODO: Support Bit128
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
#[allow(dead_code)]
|
|
||||||
pub enum PrivilegeMode {
|
pub enum PrivilegeMode {
|
||||||
User,
|
User,
|
||||||
Supervisor,
|
Supervisor,
|
||||||
@ -91,12 +90,13 @@ pub enum PrivilegeMode {
|
|||||||
Machine,
|
Machine,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Trap {
|
pub struct Trap {
|
||||||
pub trap_type: TrapType,
|
pub trap_type: TrapType,
|
||||||
pub value: u64, // Trap type specific value
|
pub value: u64, // Trap type specific value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[derive(Debug)]
|
||||||
pub enum TrapType {
|
pub enum TrapType {
|
||||||
InstructionAddressMisaligned,
|
InstructionAddressMisaligned,
|
||||||
InstructionAccessFault,
|
InstructionAccessFault,
|
||||||
@ -319,6 +319,19 @@ impl Cpu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Writes integer register content
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `reg` Register number. Must be 0-31
|
||||||
|
/// * `val` 64-bit value
|
||||||
|
pub fn write_register(&mut self, reg: u8, val: i64) {
|
||||||
|
debug_assert!(reg <= 31, "reg must be 0-31. {}", reg);
|
||||||
|
if reg == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.x[reg as usize] = val;
|
||||||
|
}
|
||||||
|
|
||||||
/// Reads Program counter content
|
/// Reads Program counter content
|
||||||
pub fn read_pc(&self) -> u64 {
|
pub fn read_pc(&self) -> u64 {
|
||||||
self.pc
|
self.pc
|
||||||
@ -350,35 +363,37 @@ impl Cpu {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let original_word = match self.fetch() {
|
let original_word = self.fetch()?;
|
||||||
Ok(word) => word,
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
};
|
|
||||||
let instruction_address = self.pc;
|
let instruction_address = self.pc;
|
||||||
let word = match (original_word & 0x3) == 0x3 {
|
let word = if (original_word & 0x3) == 0x3 {
|
||||||
true => {
|
self.pc = self.pc.wrapping_add(4); // 32-bit length non-compressed instruction
|
||||||
self.pc = self.pc.wrapping_add(4); // 32-bit length non-compressed instruction
|
original_word
|
||||||
original_word
|
} else {
|
||||||
}
|
self.pc = self.pc.wrapping_add(2); // 16-bit length compressed instruction
|
||||||
false => {
|
self.uncompress(original_word & 0xffff)
|
||||||
self.pc = self.pc.wrapping_add(2); // 16-bit length compressed instruction
|
|
||||||
self.uncompress(original_word & 0xffff)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.decode(word) {
|
let Ok(inst) = self.decode_raw(word) else {
|
||||||
Ok(inst) => {
|
panic!(
|
||||||
let result = (inst.operation)(self, word, instruction_address);
|
"Unknown instruction PC:{:x} WORD:{:x}",
|
||||||
self.x[0] = 0; // hardwired zero
|
instruction_address, original_word
|
||||||
result
|
);
|
||||||
}
|
};
|
||||||
Err(()) => {
|
|
||||||
panic!(
|
println!(
|
||||||
"Unknown instruction PC:{:x} WORD:{:x}",
|
"pc @ 0x{:08x}: {:08x} {} {}",
|
||||||
instruction_address, original_word
|
instruction_address,
|
||||||
);
|
original_word,
|
||||||
}
|
inst.name,
|
||||||
}
|
(inst.disassemble)(self, original_word, self.pc, true)
|
||||||
|
);
|
||||||
|
let result = (inst.operation)(self, word, instruction_address);
|
||||||
|
self.x[0] = 0; // hardwired zero
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute_opcode(&mut self, op: u32) -> Result<(), Trap> {
|
||||||
|
(self.decode_raw(op)?.operation)(self, op, self.pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes a word instruction data and returns a reference to
|
/// Decodes a word instruction data and returns a reference to
|
||||||
@ -386,27 +401,27 @@ impl Cpu {
|
|||||||
/// so if cache hits this method returns the result very quickly.
|
/// so if cache hits this method returns the result very quickly.
|
||||||
/// The result will be stored to cache.
|
/// The result will be stored to cache.
|
||||||
fn decode(&mut self, word: u32) -> Result<&Instruction, ()> {
|
fn decode(&mut self, word: u32) -> Result<&Instruction, ()> {
|
||||||
match self.decode_cache.get(word) {
|
if let Some(index) = self.decode_cache.get(word) {
|
||||||
Some(index) => Ok(&INSTRUCTIONS[index]),
|
return Ok(&INSTRUCTIONS[index]);
|
||||||
None => match self.decode_and_get_instruction_index(word) {
|
|
||||||
Ok(index) => {
|
|
||||||
self.decode_cache.insert(word, index);
|
|
||||||
Ok(&INSTRUCTIONS[index])
|
|
||||||
}
|
|
||||||
Err(()) => Err(()),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
let Ok(index) = self.decode_and_get_instruction_index(word) else {
|
||||||
|
return Err(());
|
||||||
|
};
|
||||||
|
self.decode_cache.insert(word, index);
|
||||||
|
Ok(&INSTRUCTIONS[index])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes a word instruction data and returns a reference to
|
/// Decodes a word instruction data and returns a reference to
|
||||||
/// [`Instruction`](struct.Instruction.html). Not Using [`DecodeCache`](struct.DecodeCache.html)
|
/// [`Instruction`](struct.Instruction.html). Not Using [`DecodeCache`](struct.DecodeCache.html)
|
||||||
/// so if you don't want to pollute the cache you should use this method
|
/// so if you don't want to pollute the cache you should use this method
|
||||||
/// instead of `decode`.
|
/// instead of `decode`.
|
||||||
fn decode_raw(&self, word: u32) -> Result<&Instruction, ()> {
|
fn decode_raw(&self, word: u32) -> Result<&Instruction, Trap> {
|
||||||
match self.decode_and_get_instruction_index(word) {
|
self.decode_and_get_instruction_index(word)
|
||||||
Ok(index) => Ok(&INSTRUCTIONS[index]),
|
.map(|index| &INSTRUCTIONS[index])
|
||||||
Err(()) => Err(()),
|
.map_err(|_| Trap {
|
||||||
}
|
value: self.pc.wrapping_sub(4),
|
||||||
|
trap_type: TrapType::IllegalInstruction,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes a word instruction data and returns an index of
|
/// Decodes a word instruction data and returns an index of
|
||||||
@ -744,14 +759,12 @@ impl Cpu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self) -> Result<u32, Trap> {
|
fn fetch(&mut self) -> Result<u32, Trap> {
|
||||||
let word = match self.mmu.fetch_word(self.pc) {
|
// println!("Fetching word from {:08x}...", self.pc);
|
||||||
Ok(word) => word,
|
self.mmu.fetch_word(self.pc).map_err(|e| {
|
||||||
Err(e) => {
|
self.pc = self.pc.wrapping_add(4); // @TODO: What if instruction is compressed?
|
||||||
self.pc = self.pc.wrapping_add(4); // @TODO: What if instruction is compressed?
|
println!("Fetch error: {:x?}", e);
|
||||||
return Err(e);
|
e
|
||||||
}
|
})
|
||||||
};
|
|
||||||
Ok(word)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_csr_access_privilege(&self, address: u16) -> bool {
|
fn has_csr_access_privilege(&self, address: u16) -> bool {
|
||||||
@ -769,7 +782,7 @@ impl Cpu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_csr(&mut self, address: u16, value: u64) -> Result<(), Trap> {
|
pub fn write_csr(&mut self, address: u16, value: u64) -> Result<(), Trap> {
|
||||||
match self.has_csr_access_privilege(address) {
|
match self.has_csr_access_privilege(address) {
|
||||||
true => {
|
true => {
|
||||||
/*
|
/*
|
||||||
@ -1442,31 +1455,22 @@ impl Cpu {
|
|||||||
// for example updating page table entry or update peripheral hardware registers.
|
// for example updating page table entry or update peripheral hardware registers.
|
||||||
// But ideally disassembling doesn't want to cause any side effect.
|
// But ideally disassembling doesn't want to cause any side effect.
|
||||||
// How can we avoid side effect?
|
// How can we avoid side effect?
|
||||||
let mut original_word = match self.mmu.fetch_word(self.pc) {
|
let Ok(mut original_word) = self.mmu.fetch_word(self.pc) else {
|
||||||
Ok(data) => data,
|
return format!("PC:{:016x}, InstructionPageFault Trap!\n", self.pc);
|
||||||
Err(_e) => {
|
|
||||||
return format!("PC:{:016x}, InstructionPageFault Trap!\n", self.pc);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let word = match (original_word & 0x3) == 0x3 {
|
let word = if (original_word & 0x3) == 0x3 {
|
||||||
true => original_word,
|
original_word
|
||||||
false => {
|
} else {
|
||||||
original_word &= 0xffff;
|
original_word &= 0xffff;
|
||||||
self.uncompress(original_word)
|
self.uncompress(original_word)
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let inst = {
|
let Ok(inst) = self.decode_raw(word) else {
|
||||||
match self.decode_raw(word) {
|
return format!(
|
||||||
Ok(inst) => inst,
|
"Unknown instruction PC:{:x} WORD:{:x}",
|
||||||
Err(()) => {
|
self.pc, original_word
|
||||||
return format!(
|
);
|
||||||
"Unknown instruction PC:{:x} WORD:{:x}",
|
|
||||||
self.pc, original_word
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut s = format!("PC:{:016x} ", self.unsigned_data(self.pc as i64));
|
let mut s = format!("PC:{:016x} ", self.unsigned_data(self.pc as i64));
|
||||||
@ -1484,6 +1488,18 @@ impl Cpu {
|
|||||||
pub fn memory_base(&self) -> u64 {
|
pub fn memory_base(&self) -> u64 {
|
||||||
self.memory_base
|
self.memory_base
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn memory_size(&self) -> u64 {
|
||||||
|
self.mmu.memory_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn phys_read_u8(&mut self, address: u64) -> u8 {
|
||||||
|
self.mmu.load_raw(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn phys_write_u8(&mut self, address: u64, value: u8) {
|
||||||
|
self.mmu.store_raw(address, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Instruction {
|
struct Instruction {
|
||||||
@ -1491,7 +1507,7 @@ struct Instruction {
|
|||||||
data: u32, // @TODO: rename
|
data: u32, // @TODO: rename
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
operation: fn(cpu: &mut Cpu, word: u32, address: u64) -> Result<(), Trap>,
|
operation: fn(cpu: &mut Cpu, word: u32, address: u64) -> Result<(), Trap>,
|
||||||
disassemble: fn(cpu: &mut Cpu, word: u32, address: u64, evaluate: bool) -> String,
|
disassemble: fn(cpu: &Cpu, word: u32, address: u64, evaluate: bool) -> String,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FormatB {
|
struct FormatB {
|
||||||
@ -1517,7 +1533,7 @@ fn parse_format_b(word: u32) -> FormatB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_format_b(cpu: &mut Cpu, word: u32, address: u64, evaluate: bool) -> String {
|
fn dump_format_b(cpu: &Cpu, word: u32, address: u64, evaluate: bool) -> String {
|
||||||
let f = parse_format_b(word);
|
let f = parse_format_b(word);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += get_register_name(f.rs1);
|
s += get_register_name(f.rs1);
|
||||||
@ -1546,7 +1562,7 @@ fn parse_format_csr(word: u32) -> FormatCSR {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_format_csr(cpu: &mut Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
fn dump_format_csr(cpu: &Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
||||||
let f = parse_format_csr(word);
|
let f = parse_format_csr(word);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += get_register_name(f.rd);
|
s += get_register_name(f.rd);
|
||||||
@ -1586,7 +1602,7 @@ fn parse_format_i(word: u32) -> FormatI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_format_i(cpu: &mut Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
fn dump_format_i(cpu: &Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
||||||
let f = parse_format_i(word);
|
let f = parse_format_i(word);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += get_register_name(f.rd);
|
s += get_register_name(f.rd);
|
||||||
@ -1601,7 +1617,7 @@ fn dump_format_i(cpu: &mut Cpu, word: u32, _address: u64, evaluate: bool) -> Str
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_format_i_mem(cpu: &mut Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
fn dump_format_i_mem(cpu: &Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
||||||
let f = parse_format_i(word);
|
let f = parse_format_i(word);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += get_register_name(f.rd);
|
s += get_register_name(f.rd);
|
||||||
@ -1637,7 +1653,7 @@ fn parse_format_j(word: u32) -> FormatJ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_format_j(cpu: &mut Cpu, word: u32, address: u64, evaluate: bool) -> String {
|
fn dump_format_j(cpu: &Cpu, word: u32, address: u64, evaluate: bool) -> String {
|
||||||
let f = parse_format_j(word);
|
let f = parse_format_j(word);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += get_register_name(f.rd);
|
s += get_register_name(f.rd);
|
||||||
@ -1662,7 +1678,7 @@ fn parse_format_r(word: u32) -> FormatR {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_format_r(cpu: &mut Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
fn dump_format_r(cpu: &Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
||||||
let f = parse_format_r(word);
|
let f = parse_format_r(word);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += get_register_name(f.rd);
|
s += get_register_name(f.rd);
|
||||||
@ -1697,7 +1713,7 @@ fn parse_format_r2(word: u32) -> FormatR2 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_format_r2(cpu: &mut Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
fn dump_format_r2(cpu: &Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
||||||
let f = parse_format_r2(word);
|
let f = parse_format_r2(word);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += get_register_name(f.rd);
|
s += get_register_name(f.rd);
|
||||||
@ -1741,7 +1757,7 @@ fn parse_format_s(word: u32) -> FormatS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_format_s(cpu: &mut Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
fn dump_format_s(cpu: &Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
||||||
let f = parse_format_s(word);
|
let f = parse_format_s(word);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += get_register_name(f.rs2);
|
s += get_register_name(f.rs2);
|
||||||
@ -1775,7 +1791,7 @@ fn parse_format_u(word: u32) -> FormatU {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_format_u(cpu: &mut Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
fn dump_format_u(cpu: &Cpu, word: u32, _address: u64, evaluate: bool) -> String {
|
||||||
let f = parse_format_u(word);
|
let f = parse_format_u(word);
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
s += get_register_name(f.rd);
|
s += get_register_name(f.rd);
|
||||||
@ -1786,7 +1802,7 @@ fn dump_format_u(cpu: &mut Cpu, word: u32, _address: u64, evaluate: bool) -> Str
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump_empty(_cpu: &mut Cpu, _word: u32, _address: u64, _evaluate: bool) -> String {
|
fn dump_empty(_cpu: &Cpu, _word: u32, _address: u64, _evaluate: bool) -> String {
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3424,6 +3440,7 @@ const INSTRUCTIONS: [Instruction; INSTRUCTION_NUM] = [
|
|||||||
1 => PrivilegeMode::Supervisor,
|
1 => PrivilegeMode::Supervisor,
|
||||||
_ => panic!(), // Shouldn't happen
|
_ => panic!(), // Shouldn't happen
|
||||||
};
|
};
|
||||||
|
println!("Updating privilege mode to {:?}", cpu.privilege_mode);
|
||||||
cpu.mmu.update_privilege_mode(cpu.privilege_mode.clone());
|
cpu.mmu.update_privilege_mode(cpu.privilege_mode.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, num::NonZeroU64};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cpu::{get_privilege_mode, PrivilegeMode, Trap, TrapType, Xlen},
|
cpu::{get_privilege_mode, PrivilegeMode, Trap, TrapType, Xlen},
|
||||||
@ -18,6 +18,9 @@ pub struct Mmu {
|
|||||||
privilege_mode: PrivilegeMode,
|
privilege_mode: PrivilegeMode,
|
||||||
memory: MemoryWrapper,
|
memory: MemoryWrapper,
|
||||||
|
|
||||||
|
/// The size of main memory (if initialized)
|
||||||
|
memory_length: Option<NonZeroU64>,
|
||||||
|
|
||||||
/// Address translation can be affected `mstatus` (MPRV, MPP in machine mode)
|
/// Address translation can be affected `mstatus` (MPRV, MPP in machine mode)
|
||||||
/// then `Mmu` has copy of it.
|
/// then `Mmu` has copy of it.
|
||||||
mstatus: u64,
|
mstatus: u64,
|
||||||
@ -39,6 +42,7 @@ pub struct Mmu {
|
|||||||
store_page_cache: HashMap<u64, u64>,
|
store_page_cache: HashMap<u64, u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum AddressingMode {
|
pub enum AddressingMode {
|
||||||
None,
|
None,
|
||||||
SV32,
|
SV32,
|
||||||
@ -87,6 +91,7 @@ impl Mmu {
|
|||||||
fetch_page_cache: HashMap::default(),
|
fetch_page_cache: HashMap::default(),
|
||||||
load_page_cache: HashMap::default(),
|
load_page_cache: HashMap::default(),
|
||||||
store_page_cache: HashMap::default(),
|
store_page_cache: HashMap::default(),
|
||||||
|
memory_length: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,9 +109,15 @@ impl Mmu {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `capacity`
|
/// * `capacity`
|
||||||
pub fn init_memory(&mut self, capacity: u64) {
|
pub fn init_memory(&mut self, capacity: u64) {
|
||||||
|
assert!(self.memory_length.is_none());
|
||||||
|
self.memory_length = Some(NonZeroU64::new(capacity).unwrap());
|
||||||
self.memory.init(capacity);
|
self.memory.init(capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn memory_size(&self) -> u64 {
|
||||||
|
self.memory_length.unwrap().get()
|
||||||
|
}
|
||||||
|
|
||||||
/// Enables or disables page cache optimization.
|
/// Enables or disables page cache optimization.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -190,29 +201,25 @@ impl Mmu {
|
|||||||
/// * `v_address` Virtual address
|
/// * `v_address` Virtual address
|
||||||
pub fn fetch_word(&mut self, v_address: u64) -> Result<u32, Trap> {
|
pub fn fetch_word(&mut self, v_address: u64) -> Result<u32, Trap> {
|
||||||
let width = 4;
|
let width = 4;
|
||||||
match (v_address & 0xfff) <= (0x1000 - width) {
|
if (v_address & 0xfff) <= (0x1000 - width) {
|
||||||
true => {
|
// Fast path. All bytes fetched are in the same page so
|
||||||
// Fast path. All bytes fetched are in the same page so
|
// translating an address only once.
|
||||||
// translating an address only once.
|
let effective_address = self.get_effective_address(v_address);
|
||||||
let effective_address = self.get_effective_address(v_address);
|
self.translate_address(effective_address, &MemoryAccessType::Execute)
|
||||||
match self.translate_address(effective_address, &MemoryAccessType::Execute) {
|
.map(|p_address| self.load_word_raw(p_address))
|
||||||
Ok(p_address) => Ok(self.load_word_raw(p_address)),
|
.map_err(|()| Trap {
|
||||||
Err(()) => Err(Trap {
|
trap_type: TrapType::InstructionPageFault,
|
||||||
trap_type: TrapType::InstructionPageFault,
|
value: effective_address,
|
||||||
value: effective_address,
|
})
|
||||||
}),
|
} else {
|
||||||
}
|
let mut data = 0;
|
||||||
}
|
for i in 0..width {
|
||||||
false => {
|
match self.fetch(v_address.wrapping_add(i)) {
|
||||||
let mut data = 0;
|
Ok(byte) => data |= (byte as u32) << (i * 8),
|
||||||
for i in 0..width {
|
Err(e) => return Err(e),
|
||||||
match self.fetch(v_address.wrapping_add(i)) {
|
};
|
||||||
Ok(byte) => data |= (byte as u32) << (i * 8),
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(data)
|
|
||||||
}
|
}
|
||||||
|
Ok(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +403,7 @@ impl Mmu {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `p_address` Physical address
|
/// * `p_address` Physical address
|
||||||
fn load_raw(&mut self, p_address: u64) -> u8 {
|
pub(crate) fn load_raw(&mut self, p_address: u64) -> u8 {
|
||||||
self.memory.read_byte(self.get_effective_address(p_address))
|
self.memory.read_byte(self.get_effective_address(p_address))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +423,9 @@ impl Mmu {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `p_address` Physical address
|
/// * `p_address` Physical address
|
||||||
pub fn load_word_raw(&mut self, p_address: u64) -> u32 {
|
pub fn load_word_raw(&mut self, p_address: u64) -> u32 {
|
||||||
self.memory.read_word(self.get_effective_address(p_address))
|
let val = self.memory.read_word(self.get_effective_address(p_address));
|
||||||
|
// println!("Read value from {:08x}: {:08x}", p_address, val);
|
||||||
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads eight bytes from main memory or peripheral devices depending on
|
/// Loads eight bytes from main memory or peripheral devices depending on
|
||||||
@ -435,7 +444,7 @@ impl Mmu {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `p_address` Physical address
|
/// * `p_address` Physical address
|
||||||
/// * `value` data written
|
/// * `value` data written
|
||||||
pub fn store_raw(&mut self, p_address: u64, value: u8) {
|
pub(crate) fn store_raw(&mut self, p_address: u64, value: u8) {
|
||||||
self.memory
|
self.memory
|
||||||
.write_byte(self.get_effective_address(p_address), value)
|
.write_byte(self.get_effective_address(p_address), value)
|
||||||
}
|
}
|
||||||
@ -496,7 +505,7 @@ impl Mmu {
|
|||||||
) -> Result<u64, ()> {
|
) -> Result<u64, ()> {
|
||||||
let address = self.get_effective_address(v_address);
|
let address = self.get_effective_address(v_address);
|
||||||
let v_page = address & !0xfff;
|
let v_page = address & !0xfff;
|
||||||
let cache = match self.page_cache_enabled {
|
if let Some(p_page) = match self.page_cache_enabled {
|
||||||
true => match access_type {
|
true => match access_type {
|
||||||
MemoryAccessType::Execute => self.fetch_page_cache.get(&v_page),
|
MemoryAccessType::Execute => self.fetch_page_cache.get(&v_page),
|
||||||
MemoryAccessType::Read => self.load_page_cache.get(&v_page),
|
MemoryAccessType::Read => self.load_page_cache.get(&v_page),
|
||||||
@ -504,106 +513,94 @@ impl Mmu {
|
|||||||
MemoryAccessType::DontCare => None,
|
MemoryAccessType::DontCare => None,
|
||||||
},
|
},
|
||||||
false => None,
|
false => None,
|
||||||
};
|
} {
|
||||||
match cache {
|
return Ok(p_page | (address & 0xfff));
|
||||||
Some(p_page) => Ok(p_page | (address & 0xfff)),
|
}
|
||||||
None => {
|
|
||||||
let p_address = match self.addressing_mode {
|
let p_address = match self.addressing_mode {
|
||||||
AddressingMode::None => Ok(address),
|
AddressingMode::None => Ok(address),
|
||||||
AddressingMode::SV32 => match self.privilege_mode {
|
AddressingMode::SV32 => match self.privilege_mode {
|
||||||
// @TODO: Optimize
|
// @TODO: Optimize
|
||||||
PrivilegeMode::Machine => match access_type {
|
PrivilegeMode::Machine => match access_type {
|
||||||
MemoryAccessType::Execute => Ok(address),
|
MemoryAccessType::Execute => Ok(address),
|
||||||
// @TODO: Remove magic number
|
// @TODO: Remove magic number
|
||||||
_ => match (self.mstatus >> 17) & 1 {
|
_ => match (self.mstatus >> 17) & 1 {
|
||||||
0 => Ok(address),
|
0 => Ok(address),
|
||||||
|
_ => {
|
||||||
|
let privilege_mode = get_privilege_mode((self.mstatus >> 9) & 3);
|
||||||
|
match privilege_mode {
|
||||||
|
PrivilegeMode::Machine => Ok(address),
|
||||||
_ => {
|
_ => {
|
||||||
let privilege_mode =
|
let current_privilege_mode = self.privilege_mode.clone();
|
||||||
get_privilege_mode((self.mstatus >> 9) & 3);
|
self.update_privilege_mode(privilege_mode);
|
||||||
match privilege_mode {
|
let result = self.translate_address(v_address, access_type);
|
||||||
PrivilegeMode::Machine => Ok(address),
|
self.update_privilege_mode(current_privilege_mode);
|
||||||
_ => {
|
result
|
||||||
let current_privilege_mode =
|
|
||||||
self.privilege_mode.clone();
|
|
||||||
self.update_privilege_mode(privilege_mode);
|
|
||||||
let result =
|
|
||||||
self.translate_address(v_address, access_type);
|
|
||||||
self.update_privilege_mode(current_privilege_mode);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
},
|
|
||||||
PrivilegeMode::User | PrivilegeMode::Supervisor => {
|
|
||||||
let vpns = [(address >> 12) & 0x3ff, (address >> 22) & 0x3ff];
|
|
||||||
self.traverse_page(address, 2 - 1, self.ppn, &vpns, access_type)
|
|
||||||
}
|
}
|
||||||
_ => Ok(address),
|
|
||||||
},
|
},
|
||||||
AddressingMode::SV39 => match self.privilege_mode {
|
},
|
||||||
// @TODO: Optimize
|
PrivilegeMode::User | PrivilegeMode::Supervisor => {
|
||||||
// @TODO: Remove duplicated code with SV32
|
let vpns = [(address >> 12) & 0x3ff, (address >> 22) & 0x3ff];
|
||||||
PrivilegeMode::Machine => match access_type {
|
self.traverse_page(address, 2 - 1, self.ppn, &vpns, access_type)
|
||||||
MemoryAccessType::Execute => Ok(address),
|
|
||||||
// @TODO: Remove magic number
|
|
||||||
_ => match (self.mstatus >> 17) & 1 {
|
|
||||||
0 => Ok(address),
|
|
||||||
_ => {
|
|
||||||
let privilege_mode =
|
|
||||||
get_privilege_mode((self.mstatus >> 9) & 3);
|
|
||||||
match privilege_mode {
|
|
||||||
PrivilegeMode::Machine => Ok(address),
|
|
||||||
_ => {
|
|
||||||
let current_privilege_mode =
|
|
||||||
self.privilege_mode.clone();
|
|
||||||
self.update_privilege_mode(privilege_mode);
|
|
||||||
let result =
|
|
||||||
self.translate_address(v_address, access_type);
|
|
||||||
self.update_privilege_mode(current_privilege_mode);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PrivilegeMode::User | PrivilegeMode::Supervisor => {
|
|
||||||
let vpns = [
|
|
||||||
(address >> 12) & 0x1ff,
|
|
||||||
(address >> 21) & 0x1ff,
|
|
||||||
(address >> 30) & 0x1ff,
|
|
||||||
];
|
|
||||||
self.traverse_page(address, 3 - 1, self.ppn, &vpns, access_type)
|
|
||||||
}
|
|
||||||
_ => Ok(address),
|
|
||||||
},
|
|
||||||
AddressingMode::SV48 => {
|
|
||||||
panic!("AddressingMode SV48 is not supported yet.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match self.page_cache_enabled {
|
|
||||||
true => match p_address {
|
|
||||||
Ok(p_address) => {
|
|
||||||
let p_page = p_address & !0xfff;
|
|
||||||
match access_type {
|
|
||||||
MemoryAccessType::Execute => {
|
|
||||||
self.fetch_page_cache.insert(v_page, p_page)
|
|
||||||
}
|
|
||||||
MemoryAccessType::Read => {
|
|
||||||
self.load_page_cache.insert(v_page, p_page)
|
|
||||||
}
|
|
||||||
MemoryAccessType::Write => {
|
|
||||||
self.store_page_cache.insert(v_page, p_page)
|
|
||||||
}
|
|
||||||
MemoryAccessType::DontCare => None,
|
|
||||||
};
|
|
||||||
Ok(p_address)
|
|
||||||
}
|
|
||||||
Err(()) => Err(()),
|
|
||||||
},
|
|
||||||
false => p_address,
|
|
||||||
}
|
}
|
||||||
|
_ => Ok(address),
|
||||||
|
},
|
||||||
|
AddressingMode::SV39 => match self.privilege_mode {
|
||||||
|
// @TODO: Optimize
|
||||||
|
// @TODO: Remove duplicated code with SV32
|
||||||
|
PrivilegeMode::Machine => match access_type {
|
||||||
|
MemoryAccessType::Execute => Ok(address),
|
||||||
|
// @TODO: Remove magic number
|
||||||
|
_ => match (self.mstatus >> 17) & 1 {
|
||||||
|
0 => Ok(address),
|
||||||
|
_ => {
|
||||||
|
let privilege_mode = get_privilege_mode((self.mstatus >> 9) & 3);
|
||||||
|
match privilege_mode {
|
||||||
|
PrivilegeMode::Machine => Ok(address),
|
||||||
|
_ => {
|
||||||
|
let current_privilege_mode = self.privilege_mode.clone();
|
||||||
|
self.update_privilege_mode(privilege_mode);
|
||||||
|
let result = self.translate_address(v_address, access_type);
|
||||||
|
self.update_privilege_mode(current_privilege_mode);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PrivilegeMode::User | PrivilegeMode::Supervisor => {
|
||||||
|
let vpns = [
|
||||||
|
(address >> 12) & 0x1ff,
|
||||||
|
(address >> 21) & 0x1ff,
|
||||||
|
(address >> 30) & 0x1ff,
|
||||||
|
];
|
||||||
|
self.traverse_page(address, 3 - 1, self.ppn, &vpns, access_type)
|
||||||
|
}
|
||||||
|
_ => Ok(address),
|
||||||
|
},
|
||||||
|
AddressingMode::SV48 => {
|
||||||
|
panic!("AddressingMode SV48 is not supported yet.");
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.page_cache_enabled {
|
||||||
|
match p_address {
|
||||||
|
Ok(p_address) => {
|
||||||
|
let p_page = p_address & !0xfff;
|
||||||
|
match access_type {
|
||||||
|
MemoryAccessType::Execute => self.fetch_page_cache.insert(v_page, p_page),
|
||||||
|
MemoryAccessType::Read => self.load_page_cache.insert(v_page, p_page),
|
||||||
|
MemoryAccessType::Write => self.store_page_cache.insert(v_page, p_page),
|
||||||
|
MemoryAccessType::DontCare => None,
|
||||||
|
};
|
||||||
|
Ok(p_address)
|
||||||
|
}
|
||||||
|
Err(()) => Err(()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p_address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user