Merge branch 'decode_print' into 'main'
Simulator done See merge request simpleos/burritos!6
This commit is contained in:
commit
b44b9d2b67
3
.gitignore
vendored
3
.gitignore
vendored
@ -1 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
|
/.idea
|
||||||
|
*.iml
|
||||||
|
*.txt
|
27
.gitlab-ci.yml
Normal file
27
.gitlab-ci.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
default:
|
||||||
|
image: rust:latest
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
|
||||||
|
build-job:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- echo "Compiling the code..."
|
||||||
|
- cargo build
|
||||||
|
- echo "Compile complete."
|
||||||
|
|
||||||
|
unit-test-job:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- echo "Running unit tests..."
|
||||||
|
- cargo test
|
||||||
|
|
||||||
|
lint-test-job:
|
||||||
|
only:
|
||||||
|
- merge_requests
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- echo "Linting code..."
|
||||||
|
- cargo clippy
|
24
src/main.rs
24
src/main.rs
@ -1,18 +1,14 @@
|
|||||||
#![warn(missing_docs)]
|
mod simulator;
|
||||||
|
|
||||||
//! # Crate burritos
|
use simulator::machine::Machine;
|
||||||
//!
|
use simulator::mem_cmp;
|
||||||
//! Crate du point d'entrée de burritos
|
|
||||||
//!
|
|
||||||
//! ## Liste des crate:
|
|
||||||
//!
|
|
||||||
//! - kernel
|
|
||||||
//! - machine
|
|
||||||
//! - A remplir
|
|
||||||
|
|
||||||
/// Cette fonction est le point d'entrée de burritos
|
|
||||||
///
|
|
||||||
/// Elle se contente de parser les arguments de lancement et les transmet ensuite
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
let mut m = Machine::_init_machine();
|
||||||
|
let path = "memoryComp.txt".to_string();
|
||||||
|
let checker = mem_cmp::MemChecker::from(&path);
|
||||||
|
mem_cmp::MemChecker::fill_memory_from_mem_checker(&checker, &mut m);
|
||||||
|
//mem_cmp::Mem_Checker::print_Mem_Checker(&checker);
|
||||||
|
//Machine::print_memory(&mut m, 0x400000, 0x405000);
|
||||||
|
Machine::run(&mut m);
|
||||||
}
|
}
|
||||||
|
95
src/simulator/decode.rs
Normal file
95
src/simulator/decode.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
use core::num::Wrapping; // Permet d'autoriser les overflow pour les opérations voulues
|
||||||
|
|
||||||
|
#[allow(non_snake_case)] // supprimer le warning snake case (quand les noms de variables ont des majuscules)
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Instruction {
|
||||||
|
pub value : u64,
|
||||||
|
|
||||||
|
pub opcode : u8,
|
||||||
|
pub rs1 : u8,
|
||||||
|
pub rs2 : u8,
|
||||||
|
pub rs3 : u8,
|
||||||
|
pub rd : u8,
|
||||||
|
pub funct7 : u8,
|
||||||
|
pub funct7_smaller : u8,
|
||||||
|
pub funct3 : u8,
|
||||||
|
pub shamt : u8, // shamt = imm[5:0] or imm[4:0] (depend of opcode)
|
||||||
|
|
||||||
|
pub imm12_I : u16,
|
||||||
|
pub imm12_S : u16,
|
||||||
|
|
||||||
|
pub imm12_I_signed : i16,
|
||||||
|
pub imm12_S_signed : i16,
|
||||||
|
pub imm13 : i16,
|
||||||
|
pub imm13_signed : i16,
|
||||||
|
|
||||||
|
pub imm31_12 : u32,
|
||||||
|
pub imm21_1 : u32,
|
||||||
|
|
||||||
|
pub imm31_12_signed : i32,
|
||||||
|
pub imm21_1_signed : i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub fn decode(val : u64) -> Instruction {
|
||||||
|
|
||||||
|
let value = val;
|
||||||
|
|
||||||
|
let opcode = (val & 0x7f) as u8;
|
||||||
|
let rs1 = ((val >> 15) & 0x1f) as u8;
|
||||||
|
let rs2 = ((val >> 20) & 0x1f) as u8;
|
||||||
|
let rs3 = ((val >> 27) & 0x1f) as u8;
|
||||||
|
let rd = ((val >> 7) & 0x1f) as u8;
|
||||||
|
let funct7 = ((val >> 25) & 0x7f) as u8;
|
||||||
|
let funct7_smaller = funct7 & 0x3e;
|
||||||
|
|
||||||
|
let funct3 = ((val >> 12) & 0x7) as u8;
|
||||||
|
let imm12_I = ((val >> 20) & 0xfff) as u16;
|
||||||
|
let imm12_S = (((val >> 20) & 0xfe0) + ((val >> 7) & 0x1f)) as u16;
|
||||||
|
|
||||||
|
let imm12_I_signed = if imm12_I >= 2048 { (Wrapping(imm12_I) - Wrapping(4096)).0 } else { imm12_I } as i16;
|
||||||
|
let imm12_S_signed = if imm12_S >= 2048 { (Wrapping(imm12_S) - Wrapping(4096)).0 } else { imm12_S } as i16;
|
||||||
|
|
||||||
|
let imm13 = (((val >> 19) & 0x1000) + ((val >> 20) & 0x7e0) +
|
||||||
|
((val >> 7) & 0x1e) + ((val << 4) & 0x800)) as i16;
|
||||||
|
let imm13_signed = if imm13 >= 4096 { imm13 - 8192 } else { imm13 };
|
||||||
|
|
||||||
|
let imm31_12 = (val & 0xfffff000) as u32;
|
||||||
|
let imm31_12_signed = imm31_12 as i32;
|
||||||
|
|
||||||
|
let imm21_1 = ((val & 0xff000) + ((val >> 9) & 0x800) +
|
||||||
|
((val >> 20) & 0x7fe) + ((val >> 11) & 0x100000)) as u32;
|
||||||
|
let imm21_1_signed = if imm21_1 >= 1048576 { (Wrapping(imm21_1) - Wrapping(2097152)).0 } else { imm21_1 } as i32;
|
||||||
|
|
||||||
|
let shamt = ((val >> 20) & 0x3f) as u8;
|
||||||
|
|
||||||
|
Instruction {
|
||||||
|
value,
|
||||||
|
|
||||||
|
opcode,
|
||||||
|
rs1,
|
||||||
|
rs2,
|
||||||
|
rs3,
|
||||||
|
rd,
|
||||||
|
funct7,
|
||||||
|
funct7_smaller,
|
||||||
|
|
||||||
|
funct3,
|
||||||
|
imm12_I,
|
||||||
|
imm12_S,
|
||||||
|
|
||||||
|
imm12_I_signed,
|
||||||
|
imm12_S_signed,
|
||||||
|
|
||||||
|
imm13,
|
||||||
|
imm13_signed,
|
||||||
|
|
||||||
|
imm31_12,
|
||||||
|
imm31_12_signed,
|
||||||
|
|
||||||
|
imm21_1,
|
||||||
|
imm21_1_signed,
|
||||||
|
|
||||||
|
shamt
|
||||||
|
}
|
||||||
|
}
|
34
src/simulator/loader.rs
Normal file
34
src/simulator/loader.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use crate::Machine;
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::io::BufRead;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Load a file into a new machine
|
||||||
|
///
|
||||||
|
/// `panic!` when size is not 1, 2, 4 or 8
|
||||||
|
/// `panic!` when the text does not represents instructions in hexadecimal
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **path** the path of the file to load
|
||||||
|
/// - **size** the number of bytes to write (1, 2, 4 or 8)
|
||||||
|
pub fn _load(path : &str, instruction_size: i32) -> Machine {
|
||||||
|
let file = fs::File::open(path).expect("Wrong filename");
|
||||||
|
let reader = io::BufReader::new(file);
|
||||||
|
let mut machine = Machine::_init_machine();
|
||||||
|
|
||||||
|
for (i,line) in reader.lines().enumerate() {
|
||||||
|
let res = u64::from_str_radix(&line.unwrap(), 16);
|
||||||
|
match res {
|
||||||
|
Ok(value) => {
|
||||||
|
Machine::write_memory(&mut machine, instruction_size, i*instruction_size as usize, value);
|
||||||
|
},
|
||||||
|
_ => panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("{:x}", Machine::read_memory(& mut machine, 4, 0));
|
||||||
|
machine
|
||||||
|
}
|
836
src/simulator/machine.rs
Normal file
836
src/simulator/machine.rs
Normal file
@ -0,0 +1,836 @@
|
|||||||
|
use std::{ops::{Add, Sub}, io::Write};
|
||||||
|
|
||||||
|
use crate::simulator::print;
|
||||||
|
|
||||||
|
use super::{decode::{Instruction, decode}};
|
||||||
|
use super::global::*;
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
/// doit disparaitre
|
||||||
|
const MEM_SIZE : usize = 0x500000;
|
||||||
|
|
||||||
|
pub trait RegisterNum: Add<Output=Self> + Sub<Output=Self> + PartialEq + Copy {}
|
||||||
|
|
||||||
|
impl RegisterNum for i64 {}
|
||||||
|
|
||||||
|
impl RegisterNum for f32 {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct Register<U: RegisterNum> {
|
||||||
|
register: [U; 32]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: RegisterNum> Register<U> {
|
||||||
|
|
||||||
|
pub fn get_reg(&self, position: usize) -> U {
|
||||||
|
self.register[position]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register<i64> {
|
||||||
|
|
||||||
|
pub fn init() -> Register<i64> {
|
||||||
|
Register {
|
||||||
|
register: [0i64; 32]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_reg(&mut self, position: usize, value: i64) {
|
||||||
|
if position != 0 {
|
||||||
|
self.register[position] = value;
|
||||||
|
} else {
|
||||||
|
// Panic ou rien ? (dans le doute pour le moment panic)
|
||||||
|
// unreachable!("You can't write to zero register")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register<f32> {
|
||||||
|
|
||||||
|
pub fn init() -> Register<f32> {
|
||||||
|
Register {
|
||||||
|
register: [0f32; 32]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_reg(&mut self, position: usize, value: f32) {
|
||||||
|
self.register[position] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Machine {
|
||||||
|
pub pc : u64,
|
||||||
|
pub sp: usize,
|
||||||
|
pub int_reg : Register<i64>,
|
||||||
|
pub fp_reg : Register<f32>,
|
||||||
|
pub main_memory : Vec<u8>,
|
||||||
|
pub shiftmask : [u64 ; 64],
|
||||||
|
pub registers_trace : String // for tests
|
||||||
|
// futur taille à calculer int memSize = g_cfg->NumPhysPages * g_cfg->PageSize;
|
||||||
|
//creer une struct cfg(configuration) qui s'initialise avec valeur dans un fichier cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Machine {
|
||||||
|
|
||||||
|
pub fn _init_machine() -> Machine {
|
||||||
|
let mut shiftmask : [u64 ; 64] = [0 ; 64];
|
||||||
|
let mut value : u64 = 0xffffffff;
|
||||||
|
|
||||||
|
value = (value << 32) + value;
|
||||||
|
for item in &mut shiftmask {
|
||||||
|
*item = value;
|
||||||
|
value >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = Machine {
|
||||||
|
pc : 0,
|
||||||
|
sp: 0,
|
||||||
|
int_reg : Register::<i64>::init(),
|
||||||
|
fp_reg : Register::<f32>::init(),
|
||||||
|
main_memory : vec![0; MEM_SIZE],
|
||||||
|
shiftmask,
|
||||||
|
registers_trace : String::from("")
|
||||||
|
};
|
||||||
|
|
||||||
|
ret.int_reg.set_reg(10, -1);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from main memory of the machine
|
||||||
|
///
|
||||||
|
/// `panic!` when size is not 1, 2, 4 or 8
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **machine** which contains the main memory
|
||||||
|
/// - **size** the number of bytes to read (1, 2, 4, 8)
|
||||||
|
/// - **address** in the memory to read
|
||||||
|
pub fn read_memory(machine : &mut Machine, size : i32, address : usize) -> u64 {
|
||||||
|
if ![1, 2, 4, 8].contains(&size) {
|
||||||
|
panic!("ERROR read_memory : wrong size parameter {size}, must be (1, 2, 4 or 8)");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret: u64 = 0;
|
||||||
|
for i in 0..size {
|
||||||
|
ret <<= 8;
|
||||||
|
ret += machine.main_memory[address + i as usize] as u64;
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write to the main memory of the machine
|
||||||
|
///
|
||||||
|
/// `panic!` when size is not 1, 2, 4 or 8
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **machine** contains the memory
|
||||||
|
/// - **size** the number of bytes to write (1, 2, 4 or 8)
|
||||||
|
/// - **address** the address to write to
|
||||||
|
/// - **value** data to be written
|
||||||
|
pub fn write_memory(machine: &mut Machine, size: i32, address: usize, value: u64) {
|
||||||
|
if ![1, 2, 4, 8].contains(&size) {
|
||||||
|
panic!("ERROR write_memory: WRONG `size` PARAMETER ({size}), must be 1, 2, 4 or 8")
|
||||||
|
}
|
||||||
|
for i in 0..size as usize {
|
||||||
|
let inv_i = size as usize - i - 1;
|
||||||
|
machine.main_memory[address + i] = ((value & 0xff << (8 * inv_i)) >> (inv_i * 8)) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the contains of the main memory of the machine
|
||||||
|
/// in a file called burritos_memory.txt
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **machine** contains the memory
|
||||||
|
pub fn _extract_memory(machine: &mut Machine){
|
||||||
|
let file_path = "burritos_memory.txt";
|
||||||
|
let write_to_file = |path| -> std::io::Result<File> {
|
||||||
|
let mut file = File::create(path)?;
|
||||||
|
file.write_all(&machine.main_memory)?;
|
||||||
|
Ok(file)
|
||||||
|
};
|
||||||
|
match write_to_file(file_path) {
|
||||||
|
Err(e) => eprintln!("Failed to write memory to file: {}", e),
|
||||||
|
Ok(_) => println!("Memory extracted to {}", file_path)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_machine_status(machine: &mut Machine) {
|
||||||
|
println!("######### Machine status #########");
|
||||||
|
for i in (0..32).step_by(3) {
|
||||||
|
print!(">{0: <4} : {1:<16x} ", print::REG_X[i], machine.int_reg.get_reg(i));
|
||||||
|
print!(">{0: <4} : {1:<16x} ", print::REG_X[i+1], machine.int_reg.get_reg(i+1));
|
||||||
|
if i+2 < 32 {
|
||||||
|
print!(">{0: <4} : {1:<16x} ", print::REG_X[i+2], machine.int_reg.get_reg(i+2));
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
println!("________________SP________________");
|
||||||
|
let sp_index = machine.int_reg.get_reg(2);
|
||||||
|
for i in 0..5 {
|
||||||
|
println!("SP+{:<2} : {:16x}", i*8, Self::read_memory(machine, 8, (sp_index + i*8) as usize));
|
||||||
|
}
|
||||||
|
println!("##################################");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string_registers(machine: &mut Machine) -> String {
|
||||||
|
let mut s = String::from("");
|
||||||
|
for i in 0..32 {
|
||||||
|
s.push_str(format!("{} ", machine.int_reg.get_reg(i)).as_str());
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute the instructions table of a machine putted in param
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **machine** which contains a table of instructions
|
||||||
|
pub fn run(machine : &mut Machine){
|
||||||
|
while Machine::one_instruction(machine) == 0 {}
|
||||||
|
println!("trace : \n{}", machine.registers_trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// execute the current instruction
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **machine** which contains a table of instructions and a pc to the actual instruction
|
||||||
|
pub fn one_instruction(machine :&mut Machine) -> i32 {
|
||||||
|
|
||||||
|
let unsigned_reg1 : u64;
|
||||||
|
let unsigned_reg2 : u64;
|
||||||
|
let long_result : i128;
|
||||||
|
|
||||||
|
/*__int128 longResult;
|
||||||
|
int32_t local_data_a, local_data_b;
|
||||||
|
int64_t localLongResult;
|
||||||
|
uint32_t local_data_aUnsigned, local_data_bUnsigned;
|
||||||
|
int32_t localResult;
|
||||||
|
float localFloat;
|
||||||
|
uint64_t value;*/
|
||||||
|
|
||||||
|
if machine.main_memory.len() <= machine.pc as usize {
|
||||||
|
panic!("ERROR : number max of instructions rushed");
|
||||||
|
}
|
||||||
|
let mut val: [u8; 4] = [0; 4];
|
||||||
|
for (i, mut _item) in val.iter_mut().enumerate() {
|
||||||
|
_item = &mut machine.main_memory[machine.pc as usize + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
let val = u32::from_be_bytes(val) as u64;
|
||||||
|
let inst : Instruction = decode(val);
|
||||||
|
Self::print_machine_status(machine);
|
||||||
|
println!("executing instruction : {:016x} at pc {:x}", val, machine.pc);
|
||||||
|
println!("{}", print::print(decode(val), machine.pc as i32));
|
||||||
|
let trace = Self::string_registers(machine);
|
||||||
|
machine.registers_trace.push_str(format!("{}\n", trace).as_str());
|
||||||
|
|
||||||
|
machine.pc += 4;
|
||||||
|
|
||||||
|
match inst.opcode {
|
||||||
|
RISCV_LUI => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, inst.imm31_12 as i64);
|
||||||
|
},
|
||||||
|
RISCV_AUIPC => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize,machine.pc as i64 - 4 + inst.imm31_12 as i64);
|
||||||
|
},
|
||||||
|
RISCV_JAL => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.pc as i64);
|
||||||
|
machine.pc = (machine.pc as i64 + inst.imm21_1_signed as i64 - 4) as u64;
|
||||||
|
},
|
||||||
|
RISCV_JALR => {
|
||||||
|
let tmp = machine.pc;
|
||||||
|
machine.pc = (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as u64 & 0xfffffffe;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, tmp as i64);
|
||||||
|
},
|
||||||
|
|
||||||
|
//******************************************************************************************
|
||||||
|
// Treatment for: BRANCH INSTRUCTIONS
|
||||||
|
RISCV_BR => {
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_BR_BEQ => {
|
||||||
|
if machine.int_reg.get_reg(inst.rs1 as usize) == machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||||
|
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_BR_BNE => {
|
||||||
|
if machine.int_reg.get_reg(inst.rs1 as usize) != machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||||
|
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_BR_BLT => {
|
||||||
|
if machine.int_reg.get_reg(inst.rs1 as usize) < machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||||
|
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_BR_BGE => {
|
||||||
|
if machine.int_reg.get_reg(inst.rs1 as usize) >= machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||||
|
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_BR_BLTU => {
|
||||||
|
if machine.int_reg.get_reg(inst.rs1 as usize) < machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||||
|
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_BR_BGEU => {
|
||||||
|
if machine.int_reg.get_reg(inst.rs1 as usize) >= machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||||
|
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("In BR switch case, this should never happen... Instr was {}", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
//******************************************************************************************
|
||||||
|
// Treatment for: LOAD INSTRUCTIONS
|
||||||
|
RISCV_LD => {
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_LD_LB | RISCV_LD_LBU => {
|
||||||
|
let tmp = Self::read_memory(machine, 1, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||||
|
},
|
||||||
|
RISCV_LD_LH | RISCV_LD_LHU => {
|
||||||
|
let tmp = Self::read_memory(machine, 2, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||||
|
},
|
||||||
|
RISCV_LD_LW | RISCV_LD_LWU => {
|
||||||
|
let tmp = Self::read_memory(machine, 4, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||||
|
},
|
||||||
|
RISCV_LD_LD => {
|
||||||
|
let tmp = Self::read_memory(machine, 8, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("In LD switch case, this should never happen... Instr was {}", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// store instructions
|
||||||
|
RISCV_ST => {
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_ST_STB => {
|
||||||
|
Self::write_memory(machine, 1, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_S_signed as i64) as usize, machine.int_reg.get_reg(inst.rs2 as usize) as u64);
|
||||||
|
},
|
||||||
|
RISCV_ST_STH => {
|
||||||
|
Self::write_memory(machine, 2, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_S_signed as i64) as usize, machine.int_reg.get_reg(inst.rs2 as usize) as u64);
|
||||||
|
},
|
||||||
|
RISCV_ST_STW => {
|
||||||
|
Self::write_memory(machine, 4, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_S_signed as i64) as usize, machine.int_reg.get_reg(inst.rs2 as usize) as u64);
|
||||||
|
},
|
||||||
|
RISCV_ST_STD => {
|
||||||
|
Self::write_memory(machine, 8, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_S_signed as i64) as usize, machine.int_reg.get_reg(inst.rs2 as usize) as u64);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("In ST switch case, this should never happen... Instr was {}", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//******************************************************************************************
|
||||||
|
// Treatment for: OPI INSTRUCTIONS
|
||||||
|
RISCV_OPI => {
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_OPI_ADDI => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64);
|
||||||
|
},
|
||||||
|
RISCV_OPI_SLTI => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, (machine.int_reg.get_reg(inst.rs1 as usize) < inst.imm12_I_signed as i64) as i64);
|
||||||
|
},
|
||||||
|
RISCV_OPI_XORI => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) ^ inst.imm12_I_signed as i64);
|
||||||
|
},
|
||||||
|
RISCV_OPI_ORI => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) | inst.imm12_I_signed as i64);
|
||||||
|
},
|
||||||
|
RISCV_OPI_ANDI => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) & inst.imm12_I_signed as i64);
|
||||||
|
},
|
||||||
|
RISCV_OPI_SLLI => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) << inst.shamt);
|
||||||
|
},
|
||||||
|
RISCV_OPI_SRI => {
|
||||||
|
if inst.funct7_smaller == RISCV_OPI_SRI_SRLI {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, (machine.int_reg.get_reg(inst.rs1 as usize) >> inst.shamt) & machine.shiftmask[inst.shamt as usize] as i64);
|
||||||
|
} else { // SRAI
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) >> inst.shamt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => { panic!("In OPI switch case, this should never happen... Instr was %x\n {}", inst.value); }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
RISCV_OP => {
|
||||||
|
if inst.funct7 == 1 {
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_OP_M_MUL => {
|
||||||
|
long_result = (machine.int_reg.get_reg(inst.rs1 as usize) * machine.int_reg.get_reg(inst.rs2 as usize)) as i128;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, (long_result & 0xffffffffffffffff) as i64);
|
||||||
|
},
|
||||||
|
RISCV_OP_M_MULH => {
|
||||||
|
long_result = (machine.int_reg.get_reg(inst.rs1 as usize) * machine.int_reg.get_reg(inst.rs2 as usize)) as i128;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, ((long_result >> 64) & 0xffffffffffffffff) as i64);
|
||||||
|
},
|
||||||
|
RISCV_OP_M_MULHSU => {
|
||||||
|
unsigned_reg2 = machine.int_reg.get_reg(inst.rs2 as usize) as u64;
|
||||||
|
long_result = (machine.int_reg.get_reg(inst.rs1 as usize) as u64 * unsigned_reg2) as i128;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, ((long_result >> 64) & 0xffffffffffffffff) as i64);
|
||||||
|
},
|
||||||
|
// VOIR CE QUE FAIT EXACTEMENT CE TRUC , PK on converve
|
||||||
|
/*
|
||||||
|
* VOIR SI LES CAST machine.int_reg[....] = i128*u64 as u32 FAUSSE RESULTAT (suit pas la logique du code c++)
|
||||||
|
* WHAT DA HECK
|
||||||
|
*/
|
||||||
|
RISCV_OP_M_MULHU => {
|
||||||
|
unsigned_reg1 = machine.int_reg.get_reg(inst.rs1 as usize) as u64;
|
||||||
|
unsigned_reg2 = machine.int_reg.get_reg(inst.rs2 as usize) as u64;
|
||||||
|
long_result = (unsigned_reg1 * unsigned_reg2) as i128;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, ((long_result >> 64) & 0xffffffffffffffff) as i64);
|
||||||
|
},
|
||||||
|
RISCV_OP_M_DIV => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) / machine.int_reg.get_reg(inst.rs2 as usize));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("RISCV_OP : funct7 = 1 (Multiplication) :: Error\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_OP_ADD => {
|
||||||
|
if inst.funct7 == RISCV_OP_ADD_ADD {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) + machine.int_reg.get_reg(inst.rs2 as usize));
|
||||||
|
} else {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) - machine.int_reg.get_reg(inst.rs2 as usize));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_OP_SLL => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) << (machine.int_reg.get_reg(inst.rs2 as usize) & 0x3f));
|
||||||
|
},
|
||||||
|
RISCV_OP_SLT => {
|
||||||
|
if machine.int_reg.get_reg(inst.rs1 as usize) < machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, 1);
|
||||||
|
} else {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_OP_SLTU => {
|
||||||
|
unsigned_reg1 = machine.int_reg.get_reg(inst.rs1 as usize) as u64;
|
||||||
|
unsigned_reg2 = machine.int_reg.get_reg(inst.rs2 as usize) as u64;
|
||||||
|
if unsigned_reg1 < unsigned_reg2 {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, 1);
|
||||||
|
} else {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_OP_XOR => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) ^ machine.int_reg.get_reg(inst.rs2 as usize));
|
||||||
|
},
|
||||||
|
RISCV_OP_SR => {
|
||||||
|
// RISCV_OP_SR_SRL inaccessible
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) >> machine.int_reg.get_reg(inst.rs2 as usize));
|
||||||
|
},
|
||||||
|
RISCV_OP_OR => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) | machine.int_reg.get_reg(inst.rs2 as usize));
|
||||||
|
},
|
||||||
|
RISCV_OP_AND => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) & machine.int_reg.get_reg(inst.rs2 as usize));
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("RISCV_OP undefined case\n");
|
||||||
|
}
|
||||||
|
}//LA
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//******************************************************************************************
|
||||||
|
// Treatment for OPIW INSTRUCTIONS
|
||||||
|
RISCV_OPIW => {
|
||||||
|
let local_data = machine.int_reg.get_reg(inst.rs1 as usize);
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_OPIW_ADDIW => {
|
||||||
|
let result = local_data + inst.imm12_I_signed as i64;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, result);
|
||||||
|
},
|
||||||
|
RISCV_OPIW_SLLIW => {
|
||||||
|
let result = local_data << inst.shamt;
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, result);
|
||||||
|
},
|
||||||
|
RISCV_OPIW_SRW => {
|
||||||
|
let result = if inst.funct7 == RISCV_OPIW_SRW_SRLIW {
|
||||||
|
(local_data >> inst.shamt) & machine.shiftmask[32 + inst.shamt as usize] as i64
|
||||||
|
} else { // SRAIW
|
||||||
|
local_data >> inst.shamt
|
||||||
|
};
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, result);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("In OPI switch case, this should never happen... Instr was {}\n", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//******************************************************************************************
|
||||||
|
// Treatment for: OPW INSTRUCTIONS
|
||||||
|
RISCV_OPW => {
|
||||||
|
if inst.funct7 == 1 { // rv64m
|
||||||
|
let local_data_a = machine.int_reg.get_reg(inst.rs1 as usize) & 0xffffffff;
|
||||||
|
let local_data_b = machine.int_reg.get_reg(inst.rs2 as usize) & 0xffffffff;
|
||||||
|
let local_data_a_unsigned = machine.int_reg.get_reg(inst.rs1 as usize) & 0xffffffff;
|
||||||
|
let local_data_b_unsigned = machine.int_reg.get_reg(inst.rs2 as usize) & 0xffffffff;
|
||||||
|
|
||||||
|
// Match case for multiplication operations (in standard extension RV32M)
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_OPW_M_MULW => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_data_a * local_data_b);
|
||||||
|
},
|
||||||
|
RISCV_OPW_M_DIVW => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_data_a / local_data_b);
|
||||||
|
},
|
||||||
|
RISCV_OPW_M_DIVUW => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_data_a_unsigned / local_data_b_unsigned);
|
||||||
|
},
|
||||||
|
RISCV_OPW_M_REMW => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_data_a % local_data_b);
|
||||||
|
},
|
||||||
|
RISCV_OPW_M_REMUW => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_data_a_unsigned % local_data_b_unsigned);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // others rv64 OPW operations
|
||||||
|
let local_dataa = machine.int_reg.get_reg(inst.rs1 as usize) & 0xffffffff;
|
||||||
|
let local_datab = machine.int_reg.get_reg(inst.rs2 as usize) & 0xffffffff;
|
||||||
|
|
||||||
|
// Match case for base OP operation
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_OPW_ADDSUBW => {
|
||||||
|
if inst.funct7 == RISCV_OPW_ADDSUBW_ADDW {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_dataa + local_datab);
|
||||||
|
} else { // SUBW
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_dataa - local_datab);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_OPW_SLLW => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_dataa << (local_datab & 0x1f));
|
||||||
|
},
|
||||||
|
RISCV_OPW_SRW => {
|
||||||
|
if inst.funct7 == RISCV_OPW_SRW_SRLW {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_dataa >> (local_datab & 0x1f) & machine.shiftmask[32 + local_datab as usize] as i64);
|
||||||
|
} else { // SRAW
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, local_dataa >> (local_datab & 0x1f));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//******************************************************************************************
|
||||||
|
// Treatment for: Simple floating point extension
|
||||||
|
RISCV_FP => {
|
||||||
|
match inst.funct7 {
|
||||||
|
RISCV_FP_ADD => {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) + machine.fp_reg.get_reg(inst.rs2 as usize));
|
||||||
|
},
|
||||||
|
RISCV_FP_SUB => {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) - machine.fp_reg.get_reg(inst.rs2 as usize));
|
||||||
|
},
|
||||||
|
RISCV_FP_MUL => {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) * machine.fp_reg.get_reg(inst.rs2 as usize));
|
||||||
|
},
|
||||||
|
RISCV_FP_DIV => {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) / machine.fp_reg.get_reg(inst.rs2 as usize));
|
||||||
|
},
|
||||||
|
RISCV_FP_SQRT => {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize).sqrt());
|
||||||
|
},
|
||||||
|
RISCV_FP_FSGN => {
|
||||||
|
let local_float = machine.fp_reg.get_reg(inst.rs1 as usize);
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_FP_FSGN_J => {
|
||||||
|
if machine.fp_reg.get_reg(inst.rs2 as usize) < 0f32 {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, -local_float);
|
||||||
|
} else {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, local_float);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RISCV_FP_FSGN_JN => {
|
||||||
|
if machine.fp_reg.get_reg(inst.rs2 as usize) < 0f32 {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, local_float);
|
||||||
|
} else {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, -local_float);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RISCV_FP_FSGN_JX => {
|
||||||
|
if (machine.fp_reg.get_reg(inst.rs2 as usize) < 0.0 && machine.fp_reg.get_reg(inst.rs1 as usize) >= 0.0) || (machine.fp_reg.get_reg(inst.rs2 as usize) >= 0.0 && machine.fp_reg.get_reg(inst.rs1 as usize) < 0.0) {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, -local_float);
|
||||||
|
} else {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, local_float);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_MINMAX => {
|
||||||
|
let r1 = machine.fp_reg.get_reg(inst.rs1 as usize);
|
||||||
|
let r2 = machine.fp_reg.get_reg(inst.rs2 as usize);
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_FP_MINMAX_MIN => {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, if r1 < r2 {r1} else {r2});
|
||||||
|
},
|
||||||
|
RISCV_FP_MINMAX_MAX => {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, if r1 > r2 {r1} else {r2});
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FCVTW => {
|
||||||
|
if inst.rs2 == RISCV_FP_FCVTW_W {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) as i64);
|
||||||
|
} else {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) as u64) as i64);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FCVTS => {
|
||||||
|
if inst.rs2 == RISCV_FP_FCVTS_W {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) as f32);
|
||||||
|
} else {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, (machine.int_reg.get_reg(inst.rs1 as usize) as u32) as f32);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FMVW => {
|
||||||
|
machine.fp_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) as f32);
|
||||||
|
},
|
||||||
|
RISCV_FP_FMVXFCLASS => {
|
||||||
|
if inst.funct3 == RISCV_FP_FMVXFCLASS_FMVX {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) as i64);
|
||||||
|
} else {
|
||||||
|
panic!("Fclass instruction is not handled in riscv simulator");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FCMP => {
|
||||||
|
match inst.funct3 {
|
||||||
|
RISCV_FP_FCMP_FEQ => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) == machine.fp_reg.get_reg(inst.rs2 as usize)) as i64);
|
||||||
|
},
|
||||||
|
RISCV_FP_FCMP_FLT => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) < machine.fp_reg.get_reg(inst.rs2 as usize)) as i64);
|
||||||
|
},
|
||||||
|
RISCV_FP_FCMP_FLE => {
|
||||||
|
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) <= machine.fp_reg.get_reg(inst.rs2 as usize)) as i64);
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RISCV_SYSTEM => {
|
||||||
|
// temporary return value to stop the loop of run
|
||||||
|
// before we can use system call
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
_ => { panic!("{:x} opcode non géré pc : {:x}", inst.opcode, machine.pc)},
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// print memory FOR DEBUG
|
||||||
|
///
|
||||||
|
/// "@"adresse [16 bytes]
|
||||||
|
pub fn _print_memory(machine : &mut Machine, from: usize, to: usize) {
|
||||||
|
for i in from..to {
|
||||||
|
if i%16 == 0 {
|
||||||
|
print!("\n@{:04x} ", i);
|
||||||
|
}
|
||||||
|
print!("{:02x}", machine.main_memory[i]);
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use crate::simulator::{machine::Machine, mem_cmp};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_read_memory() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
m.main_memory[4] = 43;
|
||||||
|
m.main_memory[5] = 150;
|
||||||
|
assert_eq!((43 << 8) + 150, Machine::read_memory(&mut m, 2, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_memory() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
Machine::write_memory(&mut m, 2, 6, (43 << 8) + 150);
|
||||||
|
assert_eq!(43, m.main_memory[6]);
|
||||||
|
assert_eq!(150, m.main_memory[7]);
|
||||||
|
Machine::write_memory(&mut m, 4, 8, (52 << 24) + (20 << 16) + (43 << 8) + 150);
|
||||||
|
assert_eq!(52, m.main_memory[8]);
|
||||||
|
assert_eq!(20, m.main_memory[9]);
|
||||||
|
assert_eq!(43, m.main_memory[10]);
|
||||||
|
assert_eq!(150, m.main_memory[11]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_comp() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
let path_before = "memoryComp.txt".to_string();
|
||||||
|
let path_after = "memoryCompEnd.txt".to_string();
|
||||||
|
let memory_before = mem_cmp::MemChecker::from(&path_before);
|
||||||
|
let memory_after = mem_cmp::MemChecker::from(&path_after);
|
||||||
|
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||||
|
Machine::run(&mut m);
|
||||||
|
|
||||||
|
let path_trace = "memoryCompTrace.txt".to_string();
|
||||||
|
let expected_trace = fs::read_to_string(path_trace).unwrap();
|
||||||
|
|
||||||
|
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||||
|
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_div() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
let path_before = "memoryDiv.txt".to_string();
|
||||||
|
let path_after = "memoryDivEnd.txt".to_string();
|
||||||
|
let memory_before = mem_cmp::MemChecker::from(&path_before);
|
||||||
|
let memory_after = mem_cmp::MemChecker::from(&path_after);
|
||||||
|
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||||
|
Machine::run(&mut m);
|
||||||
|
|
||||||
|
let path_trace = "memoryDivTrace.txt".to_string();
|
||||||
|
let expected_trace = fs::read_to_string(path_trace).unwrap();
|
||||||
|
|
||||||
|
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||||
|
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
let path_before = "memoryIf.txt".to_string();
|
||||||
|
let path_after = "memoryIfEnd.txt".to_string();
|
||||||
|
let memory_before = mem_cmp::MemChecker::from(&path_before);
|
||||||
|
let memory_after = mem_cmp::MemChecker::from(&path_after);
|
||||||
|
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||||
|
Machine::run(&mut m);
|
||||||
|
|
||||||
|
let path_trace = "memoryIfTrace.txt".to_string();
|
||||||
|
let expected_trace = fs::read_to_string(path_trace).unwrap();
|
||||||
|
|
||||||
|
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||||
|
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_jump() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
let path_before = "memoryJump.txt".to_string();
|
||||||
|
let path_after = "memoryJumpEnd.txt".to_string();
|
||||||
|
let memory_before = mem_cmp::MemChecker::from(&path_before);
|
||||||
|
let memory_after = mem_cmp::MemChecker::from(&path_after);
|
||||||
|
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||||
|
Machine::run(&mut m);
|
||||||
|
|
||||||
|
let path_trace = "memoryJumpTrace.txt".to_string();
|
||||||
|
let expected_trace = fs::read_to_string(path_trace).unwrap();
|
||||||
|
|
||||||
|
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||||
|
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mul() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
let path_before = "memoryMul.txt".to_string();
|
||||||
|
let path_after = "memoryMulEnd.txt".to_string();
|
||||||
|
let memory_before = mem_cmp::MemChecker::from(&path_before);
|
||||||
|
let memory_after = mem_cmp::MemChecker::from(&path_after);
|
||||||
|
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||||
|
Machine::run(&mut m);
|
||||||
|
|
||||||
|
let path_trace = "memoryMulTrace.txt".to_string();
|
||||||
|
let expected_trace = fs::read_to_string(path_trace).unwrap();
|
||||||
|
|
||||||
|
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||||
|
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ret() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
let path_before = "memoryRet.txt".to_string();
|
||||||
|
let path_after = "memoryRetEnd.txt".to_string();
|
||||||
|
let memory_before = mem_cmp::MemChecker::from(&path_before);
|
||||||
|
let memory_after = mem_cmp::MemChecker::from(&path_after);
|
||||||
|
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||||
|
Machine::run(&mut m);
|
||||||
|
|
||||||
|
let path_trace = "memoryRetTrace.txt".to_string();
|
||||||
|
let expected_trace = fs::read_to_string(path_trace).unwrap();
|
||||||
|
|
||||||
|
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||||
|
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sub() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
let path_before = "memorySub.txt".to_string();
|
||||||
|
let path_after = "memorySubEnd.txt".to_string();
|
||||||
|
let memory_before = mem_cmp::MemChecker::from(&path_before);
|
||||||
|
let memory_after = mem_cmp::MemChecker::from(&path_after);
|
||||||
|
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||||
|
Machine::run(&mut m);
|
||||||
|
|
||||||
|
let path_trace = "memorySubTrace.txt".to_string();
|
||||||
|
let expected_trace = fs::read_to_string(path_trace).unwrap();
|
||||||
|
|
||||||
|
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||||
|
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_switch() {
|
||||||
|
let mut m = Machine::_init_machine();
|
||||||
|
let path_before = "memorySwitch.txt".to_string();
|
||||||
|
let path_after = "memorySwitchEnd.txt".to_string();
|
||||||
|
let memory_before = mem_cmp::MemChecker::from(&path_before);
|
||||||
|
let memory_after = mem_cmp::MemChecker::from(&path_after);
|
||||||
|
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||||
|
Machine::run(&mut m);
|
||||||
|
|
||||||
|
let path_trace = "memorySwitchTrace.txt".to_string();
|
||||||
|
let expected_trace = fs::read_to_string(path_trace).unwrap();
|
||||||
|
|
||||||
|
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||||
|
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||||
|
}
|
||||||
|
}
|
404
src/simulator/mem_cmp.rs
Normal file
404
src/simulator/mem_cmp.rs
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::io::BufRead;
|
||||||
|
use std::io::BufReader;
|
||||||
|
use std::io::Lines;
|
||||||
|
use crate::Machine;
|
||||||
|
|
||||||
|
const MEM_SIZE : usize = 4096;
|
||||||
|
|
||||||
|
|
||||||
|
/* TRUCS MANQUANTS
|
||||||
|
* Verifier qu'il y a un nombre pair de caractere hexa dans la ligne correspondante d'une section du fichier source
|
||||||
|
* Sinon on ne peut pas automatiquement remplir chaque octect car 2 hexa = 1 octet
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* FORMAT FICHIER.TXT Représentant la mémoire apres éxecution d'un prog
|
||||||
|
* PC
|
||||||
|
* SP
|
||||||
|
* Section_1
|
||||||
|
* Section_2
|
||||||
|
* ...
|
||||||
|
* ...
|
||||||
|
* Section_n
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Chaque section se divise en 3 parties, sur 2 lignes de texte
|
||||||
|
* addr ESPACE len
|
||||||
|
* content
|
||||||
|
*/
|
||||||
|
|
||||||
|
//content est une suite hexadécimale
|
||||||
|
|
||||||
|
//Section dans le fichier, champ String car informations proviennent d'un fichier txt
|
||||||
|
pub struct SectionFormat{
|
||||||
|
addr: String,
|
||||||
|
len: String,
|
||||||
|
content: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
//Section dans le programme
|
||||||
|
pub struct Section{
|
||||||
|
addr: usize, // adresse dans la mémoire
|
||||||
|
len: usize, // nombre d'octets de la donnée à addr
|
||||||
|
content: Vec<u8>, // la donnée en question
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Voir si instanciation d'une structure deplace les valeurs "locales" à la méthode from, je sais plus ....
|
||||||
|
*/
|
||||||
|
impl Section{
|
||||||
|
|
||||||
|
fn from(section: &SectionFormat) -> Section {
|
||||||
|
|
||||||
|
let mut content: Vec<u8> = Vec::new();
|
||||||
|
let addr: usize = string_hex_to_usize(§ion.addr);
|
||||||
|
let len: usize = string_hex_to_usize(§ion.len);
|
||||||
|
|
||||||
|
let mut tmp_a: char = ' ';
|
||||||
|
|
||||||
|
for (i, c) in section.content.chars().enumerate(){
|
||||||
|
|
||||||
|
if i%2 == 0 {
|
||||||
|
tmp_a = c;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
content.push(two_hex_to_u8(tmp_a,c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section{addr, len, content}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn print_section(s: &Section){
|
||||||
|
println!("ADDR :: {:x}", s.addr);
|
||||||
|
println!("LEN :: {:x}", s.len);
|
||||||
|
println!("CONTENT :: {:?}", s.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Representation de l'etat de la mémoire (apres execution.... a confirmer), sous forme de sections
|
||||||
|
*/
|
||||||
|
pub struct MemChecker{
|
||||||
|
pc: usize,
|
||||||
|
sp: usize,
|
||||||
|
sections: Vec<Section>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl MemChecker{
|
||||||
|
|
||||||
|
///Translate lines of a file in e Vector of String
|
||||||
|
///We need this method to parse the memory we received
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **Lines** The file to parse
|
||||||
|
///
|
||||||
|
/// ### Return
|
||||||
|
/// - A vector of String where each line of the file os an element of the vector
|
||||||
|
fn vect_from_lines(lines: &mut Lines<BufReader<fs::File>>, pc: &mut usize, sp: &mut usize) -> Vec<String>{
|
||||||
|
let mut vector = Vec::new();
|
||||||
|
for (_,line) in lines.enumerate() {
|
||||||
|
vector.push(line.unwrap());
|
||||||
|
}
|
||||||
|
let size = vector.len();
|
||||||
|
*pc = string_hex_to_usize(vector.get(size - 2).expect("0"));
|
||||||
|
*sp = string_hex_to_usize(vector.get(size - 1).expect("0"));
|
||||||
|
vector
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill a mem checker from a file (here the mock memory)
|
||||||
|
/// Extract the values of pc, sp and sections
|
||||||
|
///
|
||||||
|
/// ### Parameter
|
||||||
|
/// -**path** addr to the file
|
||||||
|
///
|
||||||
|
/// ### Return
|
||||||
|
/// Mem-checker filled
|
||||||
|
pub fn from(path: &String) -> MemChecker {
|
||||||
|
|
||||||
|
let file = fs::File::open(path).expect("Wrong filename");
|
||||||
|
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
let mut lines = reader.lines();
|
||||||
|
|
||||||
|
let mut pc: usize = 0;
|
||||||
|
let mut sp: usize = 0;
|
||||||
|
let vector = MemChecker::vect_from_lines(&mut lines, &mut pc, &mut sp);
|
||||||
|
|
||||||
|
let mut sections: Vec<Section> = Vec::new();
|
||||||
|
let mut tmp_addr_str: String = String::new();
|
||||||
|
let mut tmp_len_str: String = String::new();
|
||||||
|
|
||||||
|
let default = String::new();
|
||||||
|
for i in 0..vector.len()-2 {
|
||||||
|
let current_line = vector.get(i).unwrap_or(&default);
|
||||||
|
|
||||||
|
//Lecture des sections
|
||||||
|
if i % 2 == 0 {
|
||||||
|
//lecture ligne ADDR LEN
|
||||||
|
let next_word_index = current_line.find(' ').unwrap();
|
||||||
|
tmp_addr_str = String::from(¤t_line[0..next_word_index]);
|
||||||
|
tmp_len_str = String::from(¤t_line[next_word_index+1..]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//lecture ligne CONTENT
|
||||||
|
let section_f = SectionFormat{
|
||||||
|
addr: tmp_addr_str.clone(),
|
||||||
|
len: tmp_len_str.clone(),
|
||||||
|
content: current_line.clone().replace(' ', ""),
|
||||||
|
};
|
||||||
|
sections.push(Section::from(§ion_f));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MemChecker{pc, sp, sections}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Print the content of a Mem_Checker
|
||||||
|
///
|
||||||
|
/// ### Parameter
|
||||||
|
///
|
||||||
|
/// - **m_c** Contains the data we want to print
|
||||||
|
pub fn print_mem_checker(m_c: &MemChecker){
|
||||||
|
println!("PC :: {:x}", m_c.pc);
|
||||||
|
println!("SP :: {:x}", m_c.sp);
|
||||||
|
|
||||||
|
for(i,s) in m_c.sections.iter().enumerate() {
|
||||||
|
println!("\nSection {}\n", i);
|
||||||
|
Section::print_section(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill a machine's memory from a Mem Chacker
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **m_c** contains the data
|
||||||
|
/// - **machine** contains the memry to fill
|
||||||
|
pub fn fill_memory_from_mem_checker(m_c: &MemChecker, machine: &mut Machine){
|
||||||
|
|
||||||
|
machine.sp = m_c.sp;
|
||||||
|
machine.int_reg.set_reg(2, m_c.sp as i64);
|
||||||
|
machine.pc = m_c.pc as u64;
|
||||||
|
|
||||||
|
|
||||||
|
for section in m_c.sections.iter() {
|
||||||
|
|
||||||
|
for (i,b) in section.content.iter().enumerate() {
|
||||||
|
machine.main_memory[section.addr + i] = *b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FOR DEBUG
|
||||||
|
*/
|
||||||
|
fn compare_print_m_c_machine(m_c: &MemChecker, machine: &mut Machine){
|
||||||
|
|
||||||
|
MemChecker::print_mem_checker(m_c);
|
||||||
|
|
||||||
|
for section in m_c.sections.iter() {
|
||||||
|
|
||||||
|
print!("\n\n");
|
||||||
|
|
||||||
|
println!("Content addr : {}", section.addr);
|
||||||
|
println!("Content len (number of bytes) : {}", section.len);
|
||||||
|
|
||||||
|
for i in 0..section.len {
|
||||||
|
println!("mem[{}] = {}", section.addr + i, machine.main_memory[section.addr + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare sections of a memChecker and a machine memory
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **m_c** contains section of the memory checker
|
||||||
|
/// - **machine** contains the main memory
|
||||||
|
pub fn compare_machine_memory(m_c: &MemChecker, machine: &Machine) -> bool {
|
||||||
|
m_c.sections.iter().map(|section| {
|
||||||
|
(0..section.len).into_iter().all(|i| machine.main_memory[section.addr + i] == section.content[i])
|
||||||
|
}).all(|e| e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn string_hex_to_usize(s: &String) -> usize {
|
||||||
|
|
||||||
|
if s.is_empty() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_pow = (s.len()-1) as u32;
|
||||||
|
let mut ret_value: usize = 0;
|
||||||
|
let base: usize = 16;
|
||||||
|
|
||||||
|
for (i,c )in s.chars().enumerate(){
|
||||||
|
//println!("Current char :: {} :: Current pow :: {} ::", c, max_pow - (i as u32));
|
||||||
|
let tmp: usize = one_hex_to_dec(c) as usize;
|
||||||
|
ret_value += base.pow(max_pow - (i as u32))*tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret_value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* c doit etre un caractère hexadécimale
|
||||||
|
*/
|
||||||
|
fn one_hex_to_dec(c: char) -> u8 {
|
||||||
|
|
||||||
|
match c {
|
||||||
|
'A' | 'a' => 10,
|
||||||
|
'B' | 'b' => 11,
|
||||||
|
'C' | 'c' => 12,
|
||||||
|
'D' | 'd' => 13,
|
||||||
|
'E' | 'e' => 14,
|
||||||
|
'F' | 'f' => 15,
|
||||||
|
_ => {
|
||||||
|
c.to_digit(10).unwrap() as u8
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn two_hex_to_u8(c1: char, c2: char) -> u8 {
|
||||||
|
let a = one_hex_to_dec(c1);
|
||||||
|
let b = one_hex_to_dec(c2);
|
||||||
|
|
||||||
|
16*a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Juste pour voir si via BufReader les \n sont présent, apres test il s'avère que non
|
||||||
|
* De toute facon on limitera d'une section la lecture par len
|
||||||
|
*/
|
||||||
|
fn test_show_sections_file(){
|
||||||
|
let file = fs::File::open("test_file_section.txt").expect("Wrong filename");
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
|
for line in reader.lines() {
|
||||||
|
//println!("Tailles de la ligne : {}",
|
||||||
|
let current = line.unwrap();
|
||||||
|
//println!("Taille de la ligne : {}", current.len()); // En effet pas de \n dans chaque line, parfait
|
||||||
|
println!("{}", ¤t);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fill_memory(){
|
||||||
|
let path = "osef".to_string();
|
||||||
|
let m_c = MemChecker::from(&path);
|
||||||
|
let mut machine = Machine::_init_machine();
|
||||||
|
|
||||||
|
MemChecker::fill_memory_from_mem_checker(&m_c, &mut machine);
|
||||||
|
|
||||||
|
print!("\n Comparing memory from loaded context\n\n");
|
||||||
|
|
||||||
|
MemChecker::compare_print_m_c_machine(&m_c, &mut machine);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_enum_start_at_zero(){
|
||||||
|
let v = vec![1,2,3];
|
||||||
|
|
||||||
|
for (i,val) in v.iter().enumerate() {
|
||||||
|
println!("i = {} :: v[i] = {}", i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_mem_checker(){
|
||||||
|
let path: String = "osef".to_string();
|
||||||
|
let m_c = MemChecker::from(&path);
|
||||||
|
MemChecker::print_mem_checker(&m_c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string_hex_to_usize(){
|
||||||
|
let s = String::from("AE1F20");
|
||||||
|
//println!("taille de string : {}", s.len());
|
||||||
|
let expected: usize = 11411232;
|
||||||
|
let result = string_hex_to_usize(&s);
|
||||||
|
|
||||||
|
assert_eq!(expected,result);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tmp_fct_read_file(){
|
||||||
|
println!("Reading A file \n");
|
||||||
|
test_show_sections_file();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_section_content(){
|
||||||
|
let section_format = SectionFormat{
|
||||||
|
addr: "0".to_string(),
|
||||||
|
len: "0".to_string(),
|
||||||
|
content: "00FF0AA0A5".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let section = Section::from(§ion_format);
|
||||||
|
let expected_vec: Vec<u8> = vec![0u8, 255u8, 10u8, 160u8, 165u8];
|
||||||
|
|
||||||
|
//println!("Vec from created section {:?}", §ion.content);
|
||||||
|
//println!("Expected vec {:?}", &expected_vec);
|
||||||
|
|
||||||
|
assert_eq!(section.content, expected_vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hex_1(){
|
||||||
|
let b = two_hex_to_u8('0', '0');
|
||||||
|
assert_eq!(0u8, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hex_2(){
|
||||||
|
let b = two_hex_to_u8('F', 'F');
|
||||||
|
assert_eq!(255u8, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hex_3(){
|
||||||
|
let b = two_hex_to_u8('0', 'A');
|
||||||
|
assert_eq!(10u8, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hex_4(){
|
||||||
|
let b = two_hex_to_u8('A', '0');
|
||||||
|
assert_eq!(160u8, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hex_5(){
|
||||||
|
let b = two_hex_to_u8('A', '5');
|
||||||
|
assert_eq!(165u8, b);
|
||||||
|
}
|
||||||
|
}
|
659
src/simulator/mod.rs
Normal file
659
src/simulator/mod.rs
Normal file
@ -0,0 +1,659 @@
|
|||||||
|
pub mod machine;
|
||||||
|
pub mod decode;
|
||||||
|
pub mod print;
|
||||||
|
pub mod mem_cmp;
|
||||||
|
pub mod loader;
|
||||||
|
|
||||||
|
pub mod global {
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
// Instructions type:
|
||||||
|
// - R: Register / register
|
||||||
|
// - I: Immediate
|
||||||
|
// - U: Upper-Immediate
|
||||||
|
// - S: Store
|
||||||
|
// - B: Branch (conditional branches...)
|
||||||
|
// - J: Jump
|
||||||
|
|
||||||
|
// xlen = i64::MAX_VALUE on rv64 and i32::MAX_VALUE on rv32
|
||||||
|
|
||||||
|
/// Type: U
|
||||||
|
///
|
||||||
|
/// Load upper immediate
|
||||||
|
///
|
||||||
|
/// `LUI rd, imm31_12` => `rd <- imm31_12 << 12`
|
||||||
|
///
|
||||||
|
pub const RISCV_LUI: u8 = 0x37;
|
||||||
|
/// Type: U
|
||||||
|
///
|
||||||
|
/// Add upper immediate to PC
|
||||||
|
///
|
||||||
|
/// `AUIP rd, imm31_12` => `rd <- PC + imm31_12 << 12`
|
||||||
|
pub const RISCV_AUIPC: u8 = 0x17;
|
||||||
|
/// Type: J
|
||||||
|
///
|
||||||
|
/// Jump and link
|
||||||
|
///
|
||||||
|
/// `JAL rd, imm20(rs1)` => `rd <- pc + 4; pc <- rs1 + imm12`
|
||||||
|
pub const RISCV_JAL: u8 = 0x6f;
|
||||||
|
/// type: J
|
||||||
|
///
|
||||||
|
/// Jump and link register
|
||||||
|
///
|
||||||
|
/// `JALR rd, imm12(rs1)` => `rd <- pc + 4; pc <- rs1 + imm12`
|
||||||
|
pub const RISCV_JALR: u8 = 0x67;
|
||||||
|
pub const RISCV_BR: u8 = 0x63;
|
||||||
|
|
||||||
|
/// Load instructions
|
||||||
|
///
|
||||||
|
/// See func3 to know the type of instruction (LD, LW, LH, LB, LWU, LHU, LBU)
|
||||||
|
pub const RISCV_LD: u8 = 0x3;
|
||||||
|
// Store instructions
|
||||||
|
pub const RISCV_ST: u8 = 0x23;
|
||||||
|
// immediate Arithmetic operations
|
||||||
|
pub const RISCV_OPI: u8 = 0x13;
|
||||||
|
// Arithmetic operations
|
||||||
|
pub const RISCV_OP: u8 = 0x33;
|
||||||
|
/// Immediate arithmetic operations for rv64i
|
||||||
|
pub const RISCV_OPIW: u8 = 0x1b;
|
||||||
|
// Arithmetic operations for rv64i
|
||||||
|
pub const RISCV_OPW: u8 = 0x3b;
|
||||||
|
|
||||||
|
/// Type: B
|
||||||
|
///
|
||||||
|
/// Branch equal
|
||||||
|
///
|
||||||
|
/// `BEQ rs1, rs2, imm12` => `if rs1 = rs2 then pc <- pc + imm12`
|
||||||
|
pub const RISCV_BR_BEQ: u8 = 0x0;
|
||||||
|
/// Type: B
|
||||||
|
///
|
||||||
|
/// Branch not equal
|
||||||
|
///
|
||||||
|
/// `BNE rs1, rs2, imm12` => `if rs1 != rs2 then pc <- pc + imm12`
|
||||||
|
pub const RISCV_BR_BNE: u8 = 0x1;
|
||||||
|
/// Type: B
|
||||||
|
///
|
||||||
|
/// Branch less than
|
||||||
|
///
|
||||||
|
/// `BLT rs1, rs2, imm12` => `if rs1 < rs2 then pc <- pc + imm12`
|
||||||
|
pub const RISCV_BR_BLT: u8 = 0x4;
|
||||||
|
/// Type: B
|
||||||
|
///
|
||||||
|
/// Branch greater than or equal
|
||||||
|
///
|
||||||
|
/// `BGE rs1, rs2, imm12` => `if rs1 >= rs2 then pc <- pc + imm12`
|
||||||
|
pub const RISCV_BR_BGE: u8 = 0x5;
|
||||||
|
/// Type: B
|
||||||
|
///
|
||||||
|
/// Branch less than unsigned
|
||||||
|
///
|
||||||
|
/// Same as BLT but for unsigned values
|
||||||
|
pub const RISCV_BR_BLTU: u8 = 0x6;
|
||||||
|
/// Type: B
|
||||||
|
///
|
||||||
|
/// Greater than or equal unsigned
|
||||||
|
///
|
||||||
|
/// Same as BGE but for unsigned values
|
||||||
|
pub const RISCV_BR_BGEU: u8 = 0x7;
|
||||||
|
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Load byte (8 bits word)
|
||||||
|
///
|
||||||
|
/// `LB rd, imm12(rs1)` => `rd <- mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_LD_LB: u8 = 0x0;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Load halfword (16 bits word)
|
||||||
|
///
|
||||||
|
/// `LH rd, imm12(rs1)` => `rd <- mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_LD_LH: u8 = 0x1;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Load word (32 bits word)
|
||||||
|
///
|
||||||
|
/// `LW rd, imm12(rs1)` => `rd <- mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_LD_LW: u8 = 0x2;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Load doubleword (64-bits word)
|
||||||
|
///
|
||||||
|
/// `LD rd, imm12(rs1)` => `rd <- mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_LD_LD: u8 = 0x3;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Load byte unsigned
|
||||||
|
///
|
||||||
|
/// `LBU rd, imm12(rs1)` => `rd <- mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_LD_LBU: u8 = 0x4;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Load halfword unsigned
|
||||||
|
///
|
||||||
|
/// `LHU rd, imm12(rs1)` => `rd <- mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_LD_LHU: u8 = 0x5;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Load word unsigned (64 bits word)
|
||||||
|
///
|
||||||
|
/// `LW rd, imm12(rs1)` => `rd <- mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_LD_LWU: u8 = 0x6;
|
||||||
|
|
||||||
|
/// Type: S
|
||||||
|
///
|
||||||
|
/// Store halfword (SH) (16 bits)
|
||||||
|
///
|
||||||
|
/// In case of overflow (rs2 is a 64 bits reg), only the first 16 bits values are stored
|
||||||
|
///
|
||||||
|
/// `SH rs2, imm12(rs1)` => `rs2 -> mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_ST_STH: u8 = 0x1;
|
||||||
|
/// Type: S
|
||||||
|
///
|
||||||
|
/// Store word (SW) (32 bits)
|
||||||
|
///
|
||||||
|
/// In case of overflow (rs2 is a 64 bits reg), only the first 32 bits values are stored
|
||||||
|
///
|
||||||
|
/// `SW rs2, imm12(rs1)` => `rs2 -> mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_ST_STW: u8 = 0x2;
|
||||||
|
/// Type: S
|
||||||
|
///
|
||||||
|
/// Store byte (SB) (8 bits)
|
||||||
|
///
|
||||||
|
/// In case of overflow (rs2 is a 64 bits reg), only the first 8 bits values are stored
|
||||||
|
///
|
||||||
|
/// `SB rs2, imm12(rs1)` => `rs2 -> mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_ST_STB: u8 = 0x0;
|
||||||
|
/// Type: S
|
||||||
|
///
|
||||||
|
/// Store doubleword (SD) (64 bits)
|
||||||
|
///
|
||||||
|
/// `SD rs2, imm12(rs1)` => `rs2 -> mem[rs1 + imm12]`
|
||||||
|
pub const RISCV_ST_STD: u8 = 0x3;
|
||||||
|
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Add immediate
|
||||||
|
///
|
||||||
|
/// `addi rd, rs1, imm12` => `rd <- rs1 + imm12`
|
||||||
|
pub const RISCV_OPI_ADDI: u8 = 0x0;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Set less than immediate: set rd to 1 if rs1 < imm12, 0 otherwise
|
||||||
|
///
|
||||||
|
/// `SLT rd, rs1, imm12` => `rd <- rs1 < imm12 ? 1 : 0`
|
||||||
|
pub const RISCV_OPI_SLTI: u8 = 0x2;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Set less than immediate unsigned : same than SLTI but for unsigned values
|
||||||
|
pub const RISCV_OPI_SLTIU: u8 = 0x3;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// XOR immediate instruction
|
||||||
|
///
|
||||||
|
/// `XORI rd, rs1, imm12` => `rd <- rs1 ^ imm12`
|
||||||
|
pub const RISCV_OPI_XORI: u8 = 0x4;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// OR immediate instruction
|
||||||
|
///
|
||||||
|
/// `ORI rd, rs1, imm12` => `rd <- rs1 | imm12`
|
||||||
|
pub const RISCV_OPI_ORI: u8 = 0x6;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// AND immediate instruction
|
||||||
|
///
|
||||||
|
/// `ANDI rd, rs1, imm12` => `rd <- rs1 & imm12`
|
||||||
|
pub const RISCV_OPI_ANDI: u8 = 0x7;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Shift left logical immediate
|
||||||
|
///
|
||||||
|
/// `SLLI rd, rs1, shamt` => `rd <- rs1 >> shamt`
|
||||||
|
pub const RISCV_OPI_SLLI: u8 = 0x1;
|
||||||
|
/// Shift right immediate, may be SRAI or SRLI
|
||||||
|
pub const RISCV_OPI_SRI: u8 = 0x5;
|
||||||
|
/// type: I
|
||||||
|
///
|
||||||
|
/// Shift right arithmetic immediate
|
||||||
|
///
|
||||||
|
/// `SRAI rd, rs1, shamt` => `rd <- rs1 >> shamt`
|
||||||
|
pub const RISCV_OPI_SRI_SRAI: u8 = 0x20;
|
||||||
|
/// type: I
|
||||||
|
///
|
||||||
|
/// Shift right logical immediate
|
||||||
|
///
|
||||||
|
/// `SRLI rd, rs1, shamt` => `rd <- rs1 >> shamt`
|
||||||
|
pub const RISCV_OPI_SRI_SRLI: u8 = 0x0;
|
||||||
|
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Add or sub (see RISCV_OP_ADD_ADD or RISCV_OP_ADD_SUB) depending of func7 value
|
||||||
|
pub const RISCV_OP_ADD: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Shift left logical, add a 0 on right of the word
|
||||||
|
///
|
||||||
|
/// `SLL rd, rs1, rs2` => `rs <- rs1 << rs2`
|
||||||
|
pub const RISCV_OP_SLL: u8 = 0x1;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Set less than : set rd to 1 if rs1 < rs2, 0 otherwise
|
||||||
|
///
|
||||||
|
/// `SLT rd, rs1, rs2` => `rd <- rs1 < rs2 ? 1 : 0`
|
||||||
|
pub const RISCV_OP_SLT: u8 = 0x2;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Set less than unsigned : same than SLT but for unsigned values
|
||||||
|
pub const RISCV_OP_SLTU: u8 = 0x3;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// XOR instruction
|
||||||
|
///
|
||||||
|
/// `XOR rd, rs1, rs2` => `rd <- rs1 ^ rs2`
|
||||||
|
pub const RISCV_OP_XOR: u8 = 0x4;
|
||||||
|
pub const RISCV_OP_SR: u8 = 0x5;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// OR instruction
|
||||||
|
///
|
||||||
|
/// `OR rd, rs1, rs2` => `rd <- rs1 | rs2`
|
||||||
|
pub const RISCV_OP_OR: u8 = 0x6;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// AND instruction
|
||||||
|
///
|
||||||
|
/// `AND rd, rs1, rs2` => `rd <- rs1 & rs2`
|
||||||
|
pub const RISCV_OP_AND: u8 = 0x7;
|
||||||
|
/// Type : R
|
||||||
|
///
|
||||||
|
/// Addition
|
||||||
|
///
|
||||||
|
/// `ADD rd, rs1, rs2` => `rd <- rs1 + rs2`
|
||||||
|
pub const RISCV_OP_ADD_ADD: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Substract
|
||||||
|
///
|
||||||
|
/// `SUB rd, rs1, rs2` => `rd <- rs1 - rs2`
|
||||||
|
pub const RISCV_OP_ADD_SUB: u8 = 0x20;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Shift right logical, add a 0 at the left of the word (should not used for signed values in most cases)
|
||||||
|
///
|
||||||
|
/// `SRL rd, rs1, rs2` => `rd <- rs1 >> rs2`
|
||||||
|
pub const RISCV_OP_SR_SRL: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Shift right arithmetic, add a 1 at the left of the word(useful for signed values bacause it keep signed value)
|
||||||
|
///
|
||||||
|
/// `SRA rd, rs1, rs2` => `rd <- rs1 >> rs2`
|
||||||
|
pub const RISCV_OP_SR_SRA: u8 = 0x20;
|
||||||
|
|
||||||
|
// ECALL or EBREAK from base instructions
|
||||||
|
/// or instructions from Ricsr extension
|
||||||
|
pub const RISCV_SYSTEM: u8 = 0x73;
|
||||||
|
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Add immediate word (RV64I only)
|
||||||
|
///
|
||||||
|
/// `ADDIW rd, rs1, imm12` => `rd <- rs1 + imm12`
|
||||||
|
pub const RISCV_OPIW_ADDIW: u8 = 0x0;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Shift right logical immediate word (RV64I only)
|
||||||
|
///
|
||||||
|
/// `SLLIW rd, rs1, imm12` => `rd <- rs1 >> shamt`
|
||||||
|
pub const RISCV_OPIW_SLLIW: u8 = 0x1;
|
||||||
|
|
||||||
|
/// Shift right immediate instructions (logical or arithmetic depend of func7)
|
||||||
|
pub const RISCV_OPIW_SRW: u8 = 0x5;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Shift right logical immediate word (RV64I only)
|
||||||
|
///
|
||||||
|
/// `SRLIW rd, rs1, imm12` => `rd <- rs1 >> shamt`
|
||||||
|
///
|
||||||
|
/// Complete left bits by a zero, should be used with an unsigned value in most case
|
||||||
|
pub const RISCV_OPIW_SRW_SRLIW: u8 = 0x0;
|
||||||
|
/// Type: I
|
||||||
|
///
|
||||||
|
/// Shift right arithmetic immediate word (RV64I only)
|
||||||
|
///
|
||||||
|
/// `SRAIW rd, rs1, imm12` => `rd <- rs1 >> shamt`
|
||||||
|
///
|
||||||
|
/// Keep sign bit
|
||||||
|
pub const RISCV_OPIW_SRW_SRAIW: u8 = 0x20;
|
||||||
|
|
||||||
|
// ADD or SUB immediate instructions, depend of func7 value
|
||||||
|
pub const RISCV_OPW_ADDSUBW: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Shift left logical word (RV64I only)
|
||||||
|
///
|
||||||
|
/// `SLLW rd, rs1, rs2` => `rd <- rs1 << rs2`
|
||||||
|
pub const RISCV_OPW_SLLW: u8 = 0x1;
|
||||||
|
/// Shift right word instructions (logical or arithmetic depend of func3)
|
||||||
|
pub const RISCV_OPW_SRW: u8 = 0x5;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Add word (rv64I only)
|
||||||
|
///
|
||||||
|
/// `ADDW rd, rs1, rs2` => `rd <- rs1 + rs2`
|
||||||
|
pub const RISCV_OPW_ADDSUBW_ADDW: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Subtract word (rv64I only)
|
||||||
|
///
|
||||||
|
/// `SUBW rd, rs1, rs2` => `rd <- rs1 - rs2`
|
||||||
|
pub const RISCV_OPW_ADDSUBW_SUBW: u8 = 0x20;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Shift right logical word (rv64I only)
|
||||||
|
///
|
||||||
|
/// rd <- rs1 >> rs2
|
||||||
|
///
|
||||||
|
/// Complete left bits by a 0, should be used with an unsigned value
|
||||||
|
pub const RISCV_OPW_SRW_SRLW: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Shift right arithmetic word (rv64I only)
|
||||||
|
///
|
||||||
|
/// `SRAW rd, rs1, rs2` => `rd <- rs1 >> rs2`
|
||||||
|
///
|
||||||
|
/// Keep sign bit
|
||||||
|
pub const RISCV_OPW_SRW_SRAW: u8 = 0x20;
|
||||||
|
|
||||||
|
pub const RISCV_SYSTEM_ENV: u8 = 0x0;
|
||||||
|
pub const RISCV_SYSTEM_ENV_ECALL: u8 = 0x0;
|
||||||
|
pub const RISCV_SYSTEM_ENV_EBREAK: u8 = 0x1;
|
||||||
|
pub const RISCV_SYSTEM_CSRRS: u8 = 0x2;
|
||||||
|
pub const RISCV_SYSTEM_CSRRW: u8 = 0x1;
|
||||||
|
pub const RISCV_SYSTEM_CSRRC: u8 = 0x3;
|
||||||
|
pub const RISCV_SYSTEM_CSRRWI: u8 = 0x5;
|
||||||
|
pub const RISCV_SYSTEM_CSRRSI: u8 = 0x6;
|
||||||
|
pub const RISCV_SYSTEM_CSRRCI: u8 = 0x7;
|
||||||
|
|
||||||
|
pub const RISCV_FLW: u8 = 0x07;
|
||||||
|
pub const RISCV_FSW: u8 = 0x27;
|
||||||
|
pub const RISCV_FMADD: u8 = 0x43;
|
||||||
|
pub const RISCV_FMSUB: u8 = 0x47;
|
||||||
|
pub const RISCV_FNMSUB: u8 = 0x4b;
|
||||||
|
pub const RISCV_FNMADD: u8 = 0x4f;
|
||||||
|
|
||||||
|
/// Simple floating point extension
|
||||||
|
pub const RISCV_FP: u8 = 0x53;
|
||||||
|
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Simple precision floating point addition
|
||||||
|
///
|
||||||
|
/// `FADD.S rd, rs1, rs2` => `rd <- rs1 + rs2`
|
||||||
|
pub const RISCV_FP_ADD: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Simple precision floating point substraction
|
||||||
|
///
|
||||||
|
/// `FSUB.S rd, rs1, rs2` => `rd <- rs1 - rs2`
|
||||||
|
pub const RISCV_FP_SUB: u8 = 0x4;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Simple precision floating point multiplication
|
||||||
|
///
|
||||||
|
/// `fmul.s rd, rs1, rs2` => `rd <- rs1 * rs2`
|
||||||
|
pub const RISCV_FP_MUL: u8 = 0x8;
|
||||||
|
/// Type : R
|
||||||
|
///
|
||||||
|
/// Simple precision floating point division
|
||||||
|
///
|
||||||
|
/// `fdiv.s rd, rs1, rs2` => `rd <- rs1 / rs2`
|
||||||
|
pub const RISCV_FP_DIV: u8 = 0xc;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Simple precision square root
|
||||||
|
///
|
||||||
|
/// `fsqrt.s rd, rs1` => `rd <- sqrt(rs1)`
|
||||||
|
pub const RISCV_FP_SQRT: u8 = 0x2c;
|
||||||
|
/// FSGN instructions
|
||||||
|
pub const RISCV_FP_FSGN: u8 = 0x10;
|
||||||
|
// fmin or fmax instructions
|
||||||
|
pub const RISCV_FP_MINMAX: u8 = 0x14;
|
||||||
|
/// fcvt.w instructions
|
||||||
|
///
|
||||||
|
/// convert fp to integer
|
||||||
|
pub const RISCV_FP_FCVTW: u8 = 0x60;
|
||||||
|
/// fmv.x.w or fclass.s instruction
|
||||||
|
pub const RISCV_FP_FMVXFCLASS: u8 = 0x70;
|
||||||
|
/// floating points comparaison instructions
|
||||||
|
pub const RISCV_FP_FCMP: u8 = 0x50;
|
||||||
|
pub const RISCV_FP_FEQS: u8 = 0x53;
|
||||||
|
/// fcvt.s instructions
|
||||||
|
///
|
||||||
|
/// Convert integer to fp
|
||||||
|
pub const RISCV_FP_FCVTS: u8 = 0x68;
|
||||||
|
pub const RISCV_FP_FCVTDS: u8 = 0x21;
|
||||||
|
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Take all bits except sign bit from rs1. sign is rs2's sign bit
|
||||||
|
///
|
||||||
|
/// `fsgnj.s rd, rs1, rs2` => `rd <- {rs2[31], rs1[30:0]}`
|
||||||
|
pub const RISCV_FP_FSGN_J: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Take all bits except sign bit from rs1, sign is opposite of rs2's sign bit
|
||||||
|
///
|
||||||
|
/// `fsgnjs.s rd, rs1, rs2` => `rd <- {rs2[31], rs[30:0]}`
|
||||||
|
pub const RISCV_FP_FSGN_JN: u8 = 0x1;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Take all bits except sign bit from rs1, sign is XOR of sign bit of rs1 and rs2
|
||||||
|
///
|
||||||
|
/// `fsgnjx.s rd, rs1, rs2` => `rd <- {rs1[31] ^ rs2[31], rs1[30:0]}`
|
||||||
|
pub const RISCV_FP_FSGN_JX: u8 = 0x2;
|
||||||
|
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// write the smaller number between rs1 and rs2 to rd
|
||||||
|
///
|
||||||
|
/// `fmin.s rd, rs1, rs2` => `rd <- min(rs1, rs2)`
|
||||||
|
pub const RISCV_FP_MINMAX_MIN: u8 = 0x0;
|
||||||
|
/// type: R
|
||||||
|
///
|
||||||
|
/// Write the larger number between rs1 and rs2 to rd
|
||||||
|
///
|
||||||
|
/// `fmax.s rd, rs1, rs2` => `rd <- max(rs1, rs2)`
|
||||||
|
pub const RISCV_FP_MINMAX_MAX: u8 = 0x1;
|
||||||
|
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Convert a floating point number in register to a signed 32-bit integer and write it in integer register
|
||||||
|
///
|
||||||
|
/// `fcvt.w.s rd, rs1` => `rd <- rs1_f32 as i32`
|
||||||
|
///
|
||||||
|
/// rd is integer register and rs1 is floating point register
|
||||||
|
pub const RISCV_FP_FCVTW_W: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Convert a floating point number in register to a unsigned 32 bit integer and write it in integer register
|
||||||
|
///
|
||||||
|
/// `fcvt.wu.s rd, rs1` => `rd <- rs1_f32 as u32`
|
||||||
|
pub const RISCV_FP_FCVTW_WU: u8 = 0x1;
|
||||||
|
|
||||||
|
/// Type : R
|
||||||
|
///
|
||||||
|
/// Convert signed 32 bit integer in register to a floating point number and write it in fp register
|
||||||
|
///
|
||||||
|
/// `fcvt.s.w rd, rs1` => `rd <- rs1_s32 as f32`
|
||||||
|
pub const RISCV_FP_FCVTS_W: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Convert unsigned 32 bit integer in register to a floating point number and write it in fp register
|
||||||
|
///
|
||||||
|
/// `fcvt.s.wu rd, rs1` => `rd <- rs1_u32 as f32`
|
||||||
|
pub const RISCV_FP_FCVTS_WU: u8 = 0x1;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Move floating point value in register to integer register, bits value aren't modified during the process
|
||||||
|
///
|
||||||
|
/// On rv64, the lower 32 bits of the integer register are transfered, for the upper 32 bits, values are filles with copies of the floating point number's sign bit
|
||||||
|
///
|
||||||
|
/// `fmv.x.w rd ,rs1` => `rd[31,0] <- rs1; rd[63:32] <- rs[31]`
|
||||||
|
pub const RISCV_FP_FMVXFCLASS_FMVX: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// examine the value given in fp register rs1 and writes to integer register rd a 10 bit mask that indicates the class of the fp number.
|
||||||
|
/// Format is described here:
|
||||||
|
///
|
||||||
|
/// | rd bit | meaning |
|
||||||
|
/// |--------|------------------------------------|
|
||||||
|
/// | 0 | rs1 is -infinite |
|
||||||
|
/// | 1 | rs1 is a negative normal number |
|
||||||
|
/// | 2 | rs1 is a negative subnormal number |
|
||||||
|
/// | 3 | rs1 is -0 |
|
||||||
|
/// | 4 | rs1 is +0 |
|
||||||
|
/// | 5 | rs1 is a positive subnormal number |
|
||||||
|
/// | 6 | rs1 is a positive normal number |
|
||||||
|
/// | 7 | rs1 is +infinite |
|
||||||
|
/// | 8 | rs1 is a signaling NaN |
|
||||||
|
/// | 9 | rs1 is a quiet NaN |
|
||||||
|
///
|
||||||
|
/// All others bit in rd are cleared
|
||||||
|
pub const RISCV_FP_FMVXFCLASS_FCLASS: u8 = 0x1;
|
||||||
|
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Quiet equal comparaison, NaN cause an invalid operation exception
|
||||||
|
///
|
||||||
|
/// `feq.s rd, rs1, rs2` => `rd <- rs1 == rs2`
|
||||||
|
pub const RISCV_FP_FCMP_FEQ: u8 = 2;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Quiet less comparaison, NaN cause an invalid operation exception
|
||||||
|
///
|
||||||
|
/// `flt.s rd, rs1, rs2` => `rdf <- rs1 < rs2`
|
||||||
|
pub const RISCV_FP_FCMP_FLT: u8 = 1;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Quiet less or equal comparaison, NaN cause an invalid operation exception
|
||||||
|
///
|
||||||
|
/// `fle.s rd, rs1, rs2` => `rd <- rs1 <= rs2`
|
||||||
|
pub const RISCV_FP_FCMP_FLE: u8 = 0;
|
||||||
|
|
||||||
|
/// Type : R
|
||||||
|
///
|
||||||
|
/// Move floating point value in integer register to the fp register. Bits aren't modified in the transfer
|
||||||
|
///
|
||||||
|
/// On rv64, only the lower 32 bits in the integer register are transfered.
|
||||||
|
///
|
||||||
|
/// `fmv.w.x rd, rs1` => `rd <- rs1[31:0]`
|
||||||
|
pub const RISCV_FP_FMVW: u8 = 0x78;
|
||||||
|
|
||||||
|
/// Integer, multiplication and division extension
|
||||||
|
pub const RISCV_OP_M: u8 = 0x1;
|
||||||
|
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Multiply
|
||||||
|
///
|
||||||
|
/// `MUL rd, rs1, rs2` => `rd <- rs1 * rs2`
|
||||||
|
pub const RISCV_OP_M_MUL: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Multiply high signed signed
|
||||||
|
///
|
||||||
|
/// `MULH rd, rs1, rs2` => `rd <- (rs1 * rs2) >> xlen`
|
||||||
|
///
|
||||||
|
/// rs1 and rs2 signed
|
||||||
|
pub const RISCV_OP_M_MULH: u8 = 0x1;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Multiply high signed unsigned
|
||||||
|
///
|
||||||
|
/// `MULHSU rd, rs1, rs2` => `rd <- (rs1 x rs2) >> xlen`
|
||||||
|
///
|
||||||
|
/// rs1 is signed and rs2 is unsigned
|
||||||
|
pub const RISCV_OP_M_MULHSU: u8 = 0x2;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Multiply high unsigned unsigned
|
||||||
|
///
|
||||||
|
/// `MULHU rd, rs1, rs2` => `rd <- (rs1 × rs2) >> xlen`
|
||||||
|
///
|
||||||
|
/// rs1 and rs2 unsigned
|
||||||
|
pub const RISCV_OP_M_MULHU: u8 = 0x3;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Divide signed
|
||||||
|
///
|
||||||
|
/// `DIV rd, rs1, rs2` => `rd <- r1 / rs2`
|
||||||
|
pub const RISCV_OP_M_DIV: u8 = 0x4;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Divide unsigned
|
||||||
|
///
|
||||||
|
/// `DIVU rd, rs1, rs2` => `rd <- rs1 / rs2`
|
||||||
|
pub const RISCV_OP_M_DIVU: u8 = 0x5;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Remainder signed
|
||||||
|
///
|
||||||
|
/// `REM rd, rs1, rs2` => `rd <- rs1 % rs2`
|
||||||
|
pub const RISCV_OP_M_REM: u8 = 0x6;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Remaindder unsigned
|
||||||
|
///
|
||||||
|
/// `REMU rd, rs1, rs2` => `rd <- rs1 % rs2`
|
||||||
|
pub const RISCV_OP_M_REMU: u8 = 0x7;
|
||||||
|
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Multiply Word (rv64M only)
|
||||||
|
///
|
||||||
|
/// `MULW rd, rs1, rs2` => `rd <- rs1 * rs2`
|
||||||
|
pub const RISCV_OPW_M_MULW: u8 = 0x0;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Divide signed word (RV64M only)
|
||||||
|
///
|
||||||
|
/// `DIVW rd, rs1, rs2` => `rd <- rs1 / rs2`
|
||||||
|
pub const RISCV_OPW_M_DIVW: u8 = 0x4;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Divide unsigned word
|
||||||
|
///
|
||||||
|
/// `DIVUW rd, rs1, rs2` => `red <- rs1 / rs2`
|
||||||
|
pub const RISCV_OPW_M_DIVUW: u8 = 0x5;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Remainder signed word (RV64M only)
|
||||||
|
///
|
||||||
|
/// `REMW rd, rs1, rs2` => `rd <- rs1 % rs2`
|
||||||
|
pub const RISCV_OPW_M_REMW: u8 = 0x6;
|
||||||
|
/// Type: R
|
||||||
|
///
|
||||||
|
/// Remainder unsigned word (RV64M only)
|
||||||
|
///
|
||||||
|
/// `REMUW rd, rs1, rs2` => `rd <- rs1 % rs2`
|
||||||
|
pub const RISCV_OPW_M_REMUW: u8 = 0x7;
|
||||||
|
|
||||||
|
/// Instruction from Zifencei extension
|
||||||
|
pub const RISCV_FENCE: u8 = 0x0f;
|
||||||
|
|
||||||
|
/// Atomic instructions extension
|
||||||
|
pub const RISCV_ATOM: u8 = 0x2f;
|
||||||
|
pub const RISCV_ATOM_LR: u8 = 0x2;
|
||||||
|
pub const RISCV_ATOM_SC: u8 = 0x3;
|
||||||
|
pub const RISCV_ATOM_SWAP: u8 = 0x1;
|
||||||
|
pub const RISCV_ATOM_ADD: u8 = 0;
|
||||||
|
pub const RISCV_ATOM_XOR: u8 = 0x4;
|
||||||
|
pub const RISCV_ATOM_AND: u8 = 0xc;
|
||||||
|
pub const RISCV_ATOM_OR: u8 = 0x8;
|
||||||
|
pub const RISCV_ATOM_MIN: u8 = 0x10;
|
||||||
|
pub const RISCV_ATOM_MAX: u8 = 0x14;
|
||||||
|
pub const RISCV_ATOM_MINU: u8 = 0x18;
|
||||||
|
pub const RISCV_ATOM_MAXU: u8 = 0x1c;
|
||||||
|
|
||||||
|
}
|
414
src/simulator/print.rs
Normal file
414
src/simulator/print.rs
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
use super::decode::{Instruction};
|
||||||
|
use super::global::*;
|
||||||
|
|
||||||
|
const NAMES_OP: [&str; 8] = ["add", "sll", "slt", "sltu", "xor", "sr", "or", "and"];
|
||||||
|
const NAMES_OPI: [&str; 8] = ["addi", "slli", "slti", "sltiu", "xori", "slri", "ori", "andi"];
|
||||||
|
const NAMES_MUL: [&str; 8] = ["mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu"];
|
||||||
|
const NAMES_BR: [&str; 8] = ["beq", "bne", "", "", "blt", "bge", "bltu", "bgeu"];
|
||||||
|
const NAMES_ST: [&str; 4] = ["sb", "sh", "sw", "sd"];
|
||||||
|
const NAMES_LD: [&str; 7] = ["lb", "lh", "lw", "ld", "lbu", "lhu", "lwu"];
|
||||||
|
const NAMES_OPW: [&str; 8] = ["addw", "sllw", "", "", "", "srw", "", ""];
|
||||||
|
const NAMES_OPIW: [&str; 8] = ["addiw", "slliw", "", "", "", "sri", "", ""];
|
||||||
|
|
||||||
|
|
||||||
|
// Register name mapping
|
||||||
|
pub const REG_X: [&str; 32] = ["zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1",
|
||||||
|
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
|
||||||
|
"s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
|
||||||
|
"t3", "t4", "t5", "t6"];
|
||||||
|
|
||||||
|
const REG_F: [&str; 32] = ["ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", "fs0", "fs1",
|
||||||
|
"fa0", "fa1", "fa2", "fa3", "fa4", "fa5", "fa6", "fa7",
|
||||||
|
"fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", "fs10", "fs11",
|
||||||
|
"ft8", "ft9", "ft10", "ft11"];
|
||||||
|
|
||||||
|
|
||||||
|
pub fn print(ins: Instruction, pc: i32) -> String { //TODO pc should be u64
|
||||||
|
let rd = ins.rd as usize;
|
||||||
|
let rs1 = ins.rs1 as usize;
|
||||||
|
let rs2 = ins.rs2 as usize;
|
||||||
|
let rs3 = ins.rs3 as usize;
|
||||||
|
|
||||||
|
match ins.opcode {
|
||||||
|
RISCV_OP => {
|
||||||
|
let name: &str;
|
||||||
|
if ins.funct7 == 1 { // Use mul array
|
||||||
|
name = NAMES_MUL[ins.funct3 as usize]
|
||||||
|
} else if ins.funct3 == RISCV_OP_ADD {
|
||||||
|
// Add or Sub
|
||||||
|
if ins.funct7 == RISCV_OP_ADD_ADD {
|
||||||
|
name = "add";
|
||||||
|
} else {
|
||||||
|
name = "sub";
|
||||||
|
}
|
||||||
|
} else if ins.funct3 == RISCV_OP_SR {
|
||||||
|
// Srl or Sra
|
||||||
|
if ins.funct7 == RISCV_OP_SR_SRL {
|
||||||
|
name = "srl";
|
||||||
|
} else {
|
||||||
|
name = "sra";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
name = NAMES_OP[ins.funct3 as usize];
|
||||||
|
}
|
||||||
|
format!("{}\t{},{},{}", name, REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
},
|
||||||
|
RISCV_OPI => {
|
||||||
|
// SHAMT OR IMM
|
||||||
|
if ins.funct3 == RISCV_OPI_SRI {
|
||||||
|
if ins.funct7 == RISCV_OPI_SRI_SRLI {
|
||||||
|
format!("srli\t{},{},{}", REG_X[rd], REG_X[rs1], ins.shamt)
|
||||||
|
} else {
|
||||||
|
format!("srai\t{},{},{}", REG_X[rd], REG_X[rs1], ins.shamt)
|
||||||
|
}
|
||||||
|
} else if ins.funct3 == RISCV_OPI_SLLI {
|
||||||
|
format!("{}\t{},{},{}", NAMES_OPI[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.shamt)
|
||||||
|
} else {
|
||||||
|
format!("{}\t{},{},{}", NAMES_OPI[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.imm12_I_signed)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_LUI => {
|
||||||
|
format!("lui\t{},{:x}", REG_X[rd], ins.imm31_12)
|
||||||
|
},
|
||||||
|
RISCV_AUIPC => {
|
||||||
|
format!("auipc\t{},{:x}", REG_X[rd], ins.imm31_12)
|
||||||
|
},
|
||||||
|
RISCV_JAL => {
|
||||||
|
format!("jal\t{},{:x}", REG_X[rd], (pc + ins.imm21_1_signed))
|
||||||
|
},
|
||||||
|
RISCV_JALR => {
|
||||||
|
format!("jalr\t{},{:x}({})", REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||||
|
},
|
||||||
|
RISCV_BR => {
|
||||||
|
format!("{}\t{},{},{:x}", NAMES_BR[ins.funct3 as usize], REG_X[rs1], REG_X[rs2], pc + (ins.imm13_signed as i32))
|
||||||
|
},
|
||||||
|
RISCV_LD => {
|
||||||
|
format!("{}\t{},{}({})", NAMES_LD[ins.funct3 as usize], REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||||
|
},
|
||||||
|
RISCV_ST => {
|
||||||
|
format!("{}\t{},{}({})", NAMES_ST[ins.funct3 as usize], REG_X[rs2], ins.imm12_S_signed, REG_X[rs1])
|
||||||
|
},
|
||||||
|
RISCV_OPIW => {
|
||||||
|
if ins.funct3 == RISCV_OPIW_SRW {
|
||||||
|
if ins.funct7 == RISCV_OPIW_SRW_SRLIW {
|
||||||
|
format!("srliw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
} else {
|
||||||
|
format!("sraiw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
format!("{}\t{},{},0x{:x}", NAMES_OPIW[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.imm12_I_signed)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_OPW => {
|
||||||
|
if ins.funct7 == 1 {
|
||||||
|
format!("{}w\t{},{},{}", NAMES_MUL[ins.funct3 as usize], REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
} else if ins.funct3 == RISCV_OP_ADD {
|
||||||
|
if ins.funct7 == RISCV_OPW_ADDSUBW_ADDW {
|
||||||
|
format!("addw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
} else {
|
||||||
|
format!("subw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
}
|
||||||
|
} else if ins.funct3 == RISCV_OPW_SRW {
|
||||||
|
if ins.funct7 == RISCV_OPW_SRW_SRLW {
|
||||||
|
format!("srlw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
} else {
|
||||||
|
format!("sraw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
format!("{}\t{},{},{}", NAMES_OPW[ins.funct3 as usize], REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// RV32F Standard Extension
|
||||||
|
RISCV_FLW => {
|
||||||
|
format!("flw\t{},{},({})", REG_F[rd], ins.imm12_I_signed, REG_F[rs1])
|
||||||
|
},
|
||||||
|
RISCV_FSW => {
|
||||||
|
format!("fsw\t{},{},({})", REG_F[rs2], "OFFSET TODO", REG_F[rs1]) // TODO Offset in decode
|
||||||
|
},
|
||||||
|
RISCV_FMADD => {
|
||||||
|
format!("fmadd\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||||
|
},
|
||||||
|
RISCV_FMSUB => {
|
||||||
|
format!("fmsub\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||||
|
},
|
||||||
|
RISCV_FNMSUB => {
|
||||||
|
format!("fnmsub\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||||
|
},
|
||||||
|
RISCV_FNMADD => {
|
||||||
|
format!("fnmadd\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||||
|
},
|
||||||
|
RISCV_FP => {
|
||||||
|
match ins.funct7 {
|
||||||
|
RISCV_FP_ADD => {
|
||||||
|
format!("{}\t{}{}{}", "fadd", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_SUB => {
|
||||||
|
format!("{}\t{}{}{}", "fsub.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_MUL => {
|
||||||
|
format!("{}\t{}{}{}", "fmul.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_DIV => {
|
||||||
|
format!("{}\t{}{}{}", "fdiv.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_SQRT => {
|
||||||
|
format!("{}\t{}{}", "fsqrt.s", REG_F[rd], REG_F[rs1])
|
||||||
|
},
|
||||||
|
RISCV_FP_FSGN => {
|
||||||
|
match ins.funct3 {
|
||||||
|
RISCV_FP_FSGN_J => {
|
||||||
|
format!("{}\t{}{}{}", "fsgnj.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_FSGN_JN => {
|
||||||
|
format!("{}\t{}{}{}", "fsgnn.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_FSGN_JX => {
|
||||||
|
format!("{}\t{}{}{}", "fsgnx.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
_ => todo!("Unknown code")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_MINMAX => {
|
||||||
|
if ins.funct3 == 0 {
|
||||||
|
format!("{}\t{}{}{}", "fmin.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}{}", "fmax.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FCVTW => {
|
||||||
|
if rs2 == 0 {
|
||||||
|
format!("{}\t{}{}", "fcvt.w.s", REG_F[rd], REG_F[rs1])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}", "fcvt.wu.s", REG_F[rd], REG_F[rs1])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FMVXFCLASS => {
|
||||||
|
if ins.funct3 == 0 {
|
||||||
|
format!("{}\t{}{}", "fmv.x.w", REG_F[rd], REG_F[rs1])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}", "fclass.s", REG_F[rd], REG_F[rs1])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FCMP => {
|
||||||
|
if ins.funct3 == 0 {
|
||||||
|
format!("{}\t{}{}{}", "fle.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
} else if ins.funct3 == 1 {
|
||||||
|
format!("{}\t{}{}{}", "flt.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}{}", "feq.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FCVTS => {
|
||||||
|
if rs2 == 0 {
|
||||||
|
format!("{}\t{}{}", "fcvt.s.w", REG_F[rd], REG_F[rs1])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}", "fcvt.s.wu", REG_F[rd], REG_F[rs1])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FMVW => {
|
||||||
|
format!("{}\t{}{}", "fmv.w.x", REG_F[rd], REG_F[rs1])
|
||||||
|
},
|
||||||
|
_ => todo!("Unknown code")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
RISCV_SYSTEM => {
|
||||||
|
"ecall".to_string()
|
||||||
|
},
|
||||||
|
_ => todo!("{:x} opcode non géré pc : {:x}, value : {:x}", ins.opcode, pc, ins.value) // Change todo! to panic! in the future, I put todo! because there's a lot of opcode currently not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
#![allow(clippy::unusual_byte_groupings)]
|
||||||
|
|
||||||
|
use crate::simulator::{decode, print};
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_op() {
|
||||||
|
let sub = decode::decode(0b0100000_10000_10001_000_11100_0110011);
|
||||||
|
let add = decode::decode(0b0000000_10000_10001_000_11100_0110011);
|
||||||
|
let xor = decode::decode(0b0000000_10000_10001_100_11100_0110011);
|
||||||
|
let slr = decode::decode(0b0000000_10000_10001_101_11100_0110011);
|
||||||
|
let sra = decode::decode(0b0100000_10000_10001_101_11100_0110011);
|
||||||
|
|
||||||
|
assert_eq!("sub\tt3,a7,a6", print::print(sub, 0));
|
||||||
|
assert_eq!("xor\tt3,a7,a6", print::print(xor, 0));
|
||||||
|
assert_eq!("srl\tt3,a7,a6", print::print(slr, 0));
|
||||||
|
assert_eq!("sra\tt3,a7,a6", print::print(sra, 0));
|
||||||
|
assert_eq!("add\tt3,a7,a6", print::print(add, 0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_opi() {
|
||||||
|
let addi = decode::decode(0b0000000000_10001_000_11100_0010011);
|
||||||
|
let slli = decode::decode(0b0000000000_10001_001_11100_0010011);
|
||||||
|
let slti = decode::decode(0b0000000000_10001_010_11100_0010011);
|
||||||
|
let sltiu = decode::decode(0b0000000000_10001_011_11100_0010011);
|
||||||
|
let xori = decode::decode(0b_0000000000010001_100_11100_0010011);
|
||||||
|
let ori = decode::decode(0b00000000000_10001_110_11100_0010011);
|
||||||
|
let andi = decode::decode(0b000000000000_10001_111_11100_0010011);
|
||||||
|
assert_eq!("andi\tt3,a7,0", print::print(andi, 0));
|
||||||
|
assert_eq!("addi\tt3,a7,0", print::print(addi, 0));
|
||||||
|
assert_eq!("slli\tt3,a7,0", print::print(slli, 0));
|
||||||
|
assert_eq!("slti\tt3,a7,0", print::print(slti, 0));
|
||||||
|
assert_eq!("sltiu\tt3,a7,0", print::print(sltiu, 0));
|
||||||
|
assert_eq!("xori\tt3,a7,0", print::print(xori, 0));
|
||||||
|
assert_eq!("ori\tt3,a7,0", print::print(ori, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lui() {
|
||||||
|
let lui = decode::decode(0b01110001000011111000_11100_0110111);
|
||||||
|
let lui_negatif = decode::decode(0b11110001000011111000_11100_0110111);
|
||||||
|
assert_eq!("lui\tt3,710f8000", print::print(lui, 0));
|
||||||
|
assert_eq!("lui\tt3,f10f8000", print::print(lui_negatif, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ld() {
|
||||||
|
// imm rs1 f3 rd opcode
|
||||||
|
let lb = decode::decode(0b010111110000_10001_000_11100_0000011);
|
||||||
|
let lh = decode::decode(0b010111110000_10001_001_11100_0000011);
|
||||||
|
let lw = decode::decode(0b010111110000_10001_010_11100_0000011);
|
||||||
|
let lbu = decode::decode(0b010111110000_10001_100_11100_0000011);
|
||||||
|
let lhu = decode::decode(0b010111110000_10001_101_11100_0000011);
|
||||||
|
let ld = decode::decode(0b010111110000_10001_011_11100_0000011);
|
||||||
|
let lwu = decode::decode(0b010111110000_10001_110_11100_0000011);
|
||||||
|
|
||||||
|
assert_eq!("lb\tt3,1520(a7)", print::print(lb, 0));
|
||||||
|
assert_eq!("lh\tt3,1520(a7)", print::print(lh, 0));
|
||||||
|
assert_eq!("lw\tt3,1520(a7)", print::print(lw, 0));
|
||||||
|
assert_eq!("lbu\tt3,1520(a7)", print::print(lbu, 0));
|
||||||
|
assert_eq!("lhu\tt3,1520(a7)", print::print(lhu, 0));
|
||||||
|
assert_eq!("ld\tt3,1520(a7)", print::print(ld, 0));
|
||||||
|
assert_eq!("lwu\tt3,1520(a7)", print::print(lwu, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_opw() {
|
||||||
|
let addw: decode::Instruction = decode::decode(0b0000000_10000_10001_000_11100_0111011);
|
||||||
|
let sllw: decode::Instruction = decode::decode(0b0000000_10000_10001_001_11100_0111011);
|
||||||
|
let srlw: decode::Instruction = decode::decode(0b0000000_10000_10001_101_11100_0111011);
|
||||||
|
let sraw: decode::Instruction = decode::decode(0b0100000_10000_10001_101_11100_0111011);
|
||||||
|
|
||||||
|
assert_eq!("addw\tt3,a7,a6", print::print(addw, 0));
|
||||||
|
assert_eq!("sllw\tt3,a7,a6", print::print(sllw, 0));
|
||||||
|
assert_eq!("srlw\tt3,a7,a6", print::print(srlw, 0));
|
||||||
|
assert_eq!("sraw\tt3,a7,a6", print::print(sraw, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_opwi() {
|
||||||
|
let addiw: decode::Instruction =decode::decode(0b000000000000_10001_000_11100_0011011);
|
||||||
|
let slliw: decode::Instruction = decode::decode(0b0000000_10000_10001_001_11100_0011011);
|
||||||
|
let srai: decode::Instruction = decode::decode(0b010000010001_10001_101_11100_0010011);
|
||||||
|
assert_eq!("addiw\tt3,a7,0x0", print::print(addiw, 0));
|
||||||
|
assert_eq!("slliw\tt3,a7,0x10", print::print(slliw, 0));
|
||||||
|
assert_eq!("srai\tt3,a7,17", print::print(srai, 0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_br() {
|
||||||
|
let beq: decode::Instruction = decode::decode(0b0000000_10000_10001_000_00000_1100011);
|
||||||
|
let bne: decode::Instruction = decode::decode(0b0000000_10000_10001_001_00000_1100011);
|
||||||
|
let blt: decode::Instruction = decode::decode(0b0000000_10000_10001_100_00000_1100011);
|
||||||
|
let bge: decode::Instruction = decode::decode(0b0000000_10000_10001_101_00000_1100011);
|
||||||
|
let bge2: decode::Instruction = decode::decode(0x00f75863);
|
||||||
|
let bltu: decode::Instruction = decode::decode(0b0000000_10000_10001_110_00000_1100011);
|
||||||
|
let bgeu: decode::Instruction = decode::decode(0b0000000_10000_10001_111_00000_1100011);
|
||||||
|
assert_eq!("blt\ta7,a6,0", print::print(blt, 0));
|
||||||
|
assert_eq!("bge\ta7,a6,0", print::print(bge, 0));
|
||||||
|
assert_eq!("bge\ta4,a5,104d4", print::print(bge2, 0x104c4));
|
||||||
|
assert_eq!("bltu\ta7,a6,0", print::print(bltu, 0));
|
||||||
|
assert_eq!("bgeu\ta7,a6,0", print::print(bgeu, 0));
|
||||||
|
assert_eq!("bne\ta7,a6,0", print::print(bne, 0));
|
||||||
|
assert_eq!("beq\ta7,a6,0", print::print(beq, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_small_program() {
|
||||||
|
/* Code for :
|
||||||
|
int a = 0;
|
||||||
|
int b = 5;
|
||||||
|
a = b;
|
||||||
|
a = a * b;
|
||||||
|
a = a + b;
|
||||||
|
b = a - b;
|
||||||
|
*/
|
||||||
|
assert_eq!("addi sp,sp,-32", print::print(decode::decode(0xfe010113), 0));
|
||||||
|
assert_eq!("sd s0,24(sp)", print::print(decode::decode(0x00813c23), 0));
|
||||||
|
assert_eq!("addi s0,sp,32", print::print(decode::decode(0x02010413), 0));
|
||||||
|
assert_eq!("sw zero,-20(s0)", print::print(decode::decode(0xfe042623), 0));
|
||||||
|
assert_eq!("addi a5,zero,5", print::print(decode::decode(0x00500793), 0));
|
||||||
|
assert_eq!("sw a5,-24(s0)", print::print(decode::decode(0xfef42423), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||||
|
assert_eq!("mulw a5,a4,a5", print::print(decode::decode(0x02f707bb), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||||
|
assert_eq!("addw a5,a4,a5", print::print(decode::decode(0x00f707bb), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||||
|
assert_eq!("subw a5,a4,a5", print::print(decode::decode(0x40f707bb), 0));
|
||||||
|
assert_eq!("sw a5,-24(s0)", print::print(decode::decode(0xfef42423), 0));
|
||||||
|
assert_eq!("addi a5,zero,0", print::print(decode::decode(0x00000793), 0));
|
||||||
|
assert_eq!("addi a0,a5,0", print::print(decode::decode(0x00078513), 0));
|
||||||
|
assert_eq!("ld s0,24(sp)", print::print(decode::decode(0x01813403), 0));
|
||||||
|
assert_eq!("addi sp,sp,32", print::print(decode::decode(0x02010113), 0));
|
||||||
|
assert_eq!("jalr zero,0(ra)", print::print(decode::decode(0x00008067), 0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fibo() {
|
||||||
|
assert_eq!("jal zero,10504", print::print(decode::decode(0x0500006f), 0x104b4));
|
||||||
|
assert_eq!("blt a4,a5,104b8", print::print(decode::decode(0xfaf740e3), 0x10518));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mul_prog() {
|
||||||
|
assert_eq!("addi sp,sp,-32", print::print(decode::decode(0xfe010113), 0));
|
||||||
|
assert_eq!("sd s0,24(sp)", print::print(decode::decode(0x00813c23), 0));
|
||||||
|
assert_eq!("addi s0,sp,32", print::print(decode::decode(0x02010413), 0));
|
||||||
|
assert_eq!("addi a5,zero,5", print::print(decode::decode(0x00500793), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||||
|
assert_eq!("addi a5,a4,0", print::print(decode::decode(0x00070793), 0));
|
||||||
|
assert_eq!("slliw a5,a5,0x2", print::print(decode::decode(0x0027979b), 0));
|
||||||
|
assert_eq!("addw a5,a5,a4", print::print(decode::decode(0x00e787bb), 0));
|
||||||
|
assert_eq!("sw a5,-24(s0)", print::print(decode::decode(0xfef42423), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||||
|
assert_eq!("mulw a5,a4,a5", print::print(decode::decode(0x02f707bb), 0));
|
||||||
|
assert_eq!("sw a5,-28(s0)", print::print(decode::decode(0xfef42223), 0));
|
||||||
|
assert_eq!("lw a5,-28(s0)", print::print(decode::decode(0xfe442783), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||||
|
assert_eq!("divw a5,a4,a5", print::print(decode::decode(0x02f747bb), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||||
|
assert_eq!("addi a5,zero,0", print::print(decode::decode(0x00000793), 0));
|
||||||
|
assert_eq!("addi a0,a5,0", print::print(decode::decode(0x00078513), 0));
|
||||||
|
assert_eq!("ld s0,24(sp)", print::print(decode::decode(0x01813403), 0));
|
||||||
|
assert_eq!("addi sp,sp,32", print::print(decode::decode(0x02010113), 0));
|
||||||
|
assert_eq!("jalr zero,0(ra)", print::print(decode::decode(0x00008067), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
4
test_programs/.gitignore
vendored
Normal file
4
test_programs/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Ignoring dump files
|
||||||
|
*.dump
|
||||||
|
*.o
|
||||||
|
target
|
21
test_programs/Makefile
Normal file
21
test_programs/Makefile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
TOPDIR=.
|
||||||
|
include $(TOPDIR)/Makefile.config
|
||||||
|
|
||||||
|
#
|
||||||
|
# Main targets
|
||||||
|
#
|
||||||
|
dumps:
|
||||||
|
$(MAKE) dumps -C riscv_instructions/
|
||||||
|
mkdir -p ${TOPDIR}/target/dumps/
|
||||||
|
find . -name '*.dump' -exec mv {} ${TOPDIR}/target/dumps/ \;
|
||||||
|
|
||||||
|
user_lib:
|
||||||
|
$(MAKE) -C userlib/
|
||||||
|
|
||||||
|
tests: user_lib
|
||||||
|
$(MAKE) tests -C riscv_instructions/
|
||||||
|
mkdir -p ${TOPDIR}/target/guac/
|
||||||
|
find . -name '*.guac' -exec mv {} ${TOPDIR}/target/guac/ \;
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(TOPDIR)/target
|
19
test_programs/Makefile.config
Normal file
19
test_programs/Makefile.config
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# This is part of a GNU -*- Makefile -*-, to specify system-dependent
|
||||||
|
# parts of the Makefile enviroment.
|
||||||
|
#
|
||||||
|
# This gets included as part of the GNU-Makefile used in each of
|
||||||
|
# the subdirectories.
|
||||||
|
#
|
||||||
|
# Depending on your platform, you need to select the correct definition.
|
||||||
|
|
||||||
|
## RISCV target compilation toolchain
|
||||||
|
RISCV_PREFIX=/opt/riscv/bin/
|
||||||
|
RISCV_AS = $(RISCV_PREFIX)riscv64-unknown-elf-gcc -x assembler-with-cpp -march=rv64imfd
|
||||||
|
RISCV_GCC = $(RISCV_PREFIX)riscv64-unknown-elf-gcc
|
||||||
|
RISCV_LD = $(RISCV_PREFIX)riscv64-unknown-elf-ld
|
||||||
|
RISCV_OBJCOPY = $(RISCV_PREFIX)riscv64-unknown-elf-objcopy
|
||||||
|
DUMP_FORMAT = ihex
|
||||||
|
RISCV_ASFLAGS = $(RISCV_CPPFLAGS)
|
||||||
|
RISCV_CPPFLAGS = #nil
|
||||||
|
RISCV_CFLAGS = -Wall $(RISCV_CPPFLAGS) -march=rv64imfd
|
||||||
|
RISCV_LDFLAGS = #nil
|
38
test_programs/Makefile.tests
Normal file
38
test_programs/Makefile.tests
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
include $(TOPDIR)/Makefile.config
|
||||||
|
USERLIB = $(TOPDIR)/userlib
|
||||||
|
INCPATH += -I$(TOPDIR) -I$(USERLIB)
|
||||||
|
LDFLAGS = $(RISCV_LDFLAGS) -T $(USERLIB)/ldscript.lds
|
||||||
|
ASFLAGS = $(RISCV_ASFLAGS) $(INCPATH)
|
||||||
|
CFLAGS = $(RISCV_CFLAGS) $(INCPATH)
|
||||||
|
|
||||||
|
# Rules
|
||||||
|
%.o: %.s
|
||||||
|
$(RISCV_AS) $(ASFLAGS) -c $<
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(RISCV_GCC) $(CFLAGS) -c $<
|
||||||
|
|
||||||
|
%.dump: %.o
|
||||||
|
$(RISCV_OBJCOPY) -j .text -O $(DUMP_FORMAT) $< $@
|
||||||
|
|
||||||
|
%.guac: %.o
|
||||||
|
$(RISCV_LD) $(LDFLAGS) $+ -o $@
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
.%.d: %.s
|
||||||
|
@echo Generating dependencies for $<
|
||||||
|
@$(SHELL) -ec '$(GCC) -x assembler-with-cpp -M $(ASFLAGS) $< \
|
||||||
|
| sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
|
||||||
|
[ -s $@ ] || rm -f $@'
|
||||||
|
|
||||||
|
.%.d: %.c
|
||||||
|
@echo Generating dependencies for $<
|
||||||
|
@$(SHELL) -ec '$(GCC) -M $(CFLAGS) $< \
|
||||||
|
| sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
|
||||||
|
[ -s $@ ] || rm -f $@'
|
||||||
|
|
||||||
|
# Targets
|
||||||
|
#clean:
|
||||||
|
# rm -rf *.o 2> /dev/null
|
||||||
|
# rm -rf *.dump 2> /dev/null
|
||||||
|
# rm -rf *.guac 2> /dev/null
|
28
test_programs/README.md
Normal file
28
test_programs/README.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# BurritOS Test programs
|
||||||
|
This folder contains small C programs pertaining to the assessment of the correctness of BurritOS' behavior.
|
||||||
|
|
||||||
|
## Folder Structure
|
||||||
|
- **riscv_instructions**: contains small programs for testing simulator instruction coverage.
|
||||||
|
|
||||||
|
## How to build
|
||||||
|
### In the Makefile.config file
|
||||||
|
Set the variables to the correct paths for the [RISCV Newlib compilation toolchain](https://github.com/riscv-collab/riscv-gnu-toolchain). Simply changing the `RISCV_PREFIX` variable should do the trick. **Do not forget to add a trailing slash**.
|
||||||
|
|
||||||
|
### Exporting objdumps
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make dumps
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiling programs
|
||||||
|
```
|
||||||
|
$ make programs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cleaning
|
||||||
|
```
|
||||||
|
$ make clean
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output folder
|
||||||
|
Compilation results are located in the target/objdumps and target/programs directories.
|
9
test_programs/riscv_instructions/Makefile
Normal file
9
test_programs/riscv_instructions/Makefile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
dumps:
|
||||||
|
make dumps -C boolean_logic/
|
||||||
|
make dumps -C jump_instructions/
|
||||||
|
make dumps -C simple_arithmetics/
|
||||||
|
|
||||||
|
tests:
|
||||||
|
make tests -C boolean_logic/
|
||||||
|
make tests -C jump_instructions/
|
||||||
|
make tests -C simple_arithmetics/
|
9
test_programs/riscv_instructions/boolean_logic/Makefile
Normal file
9
test_programs/riscv_instructions/boolean_logic/Makefile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
TOPDIR = ../..
|
||||||
|
include $(TOPDIR)/Makefile.tests
|
||||||
|
|
||||||
|
dumps: comparisons.dump if.dump switch.dump
|
||||||
|
|
||||||
|
tests: comparisons.guac if.guac switch.guac
|
||||||
|
|
||||||
|
# Dependances
|
||||||
|
$(PROGRAMS): % : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
15
test_programs/riscv_instructions/boolean_logic/comparisons.c
Normal file
15
test_programs/riscv_instructions/boolean_logic/comparisons.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
int main() {
|
||||||
|
int x = 0;
|
||||||
|
int y = 1;
|
||||||
|
while (x <= y) {
|
||||||
|
if (x > y) {
|
||||||
|
x += 1;
|
||||||
|
} else if (x == y) {
|
||||||
|
x += y;
|
||||||
|
} else if (x < y) {
|
||||||
|
y += 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
test_programs/riscv_instructions/boolean_logic/if.c
Normal file
10
test_programs/riscv_instructions/boolean_logic/if.c
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
int main() {
|
||||||
|
int x = 1;
|
||||||
|
if (x == 1 && x > 0) {
|
||||||
|
x = 2;
|
||||||
|
} else if (x || x == 0 ) {
|
||||||
|
x = 3;
|
||||||
|
} else {
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
int main() {
|
||||||
|
int x = 0;
|
||||||
|
int y = 1;
|
||||||
|
while (x <= y) {
|
||||||
|
if (x > y) {
|
||||||
|
y += 1;
|
||||||
|
}
|
||||||
|
if (x == y) {
|
||||||
|
x += y;
|
||||||
|
}
|
||||||
|
if (x < y) {
|
||||||
|
x += 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
test_programs/riscv_instructions/boolean_logic/switch.c
Normal file
7
test_programs/riscv_instructions/boolean_logic/switch.c
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
int main() {
|
||||||
|
int x = 0;
|
||||||
|
switch(x) {
|
||||||
|
case 1: x = 1; break;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
TOPDIR = ../..
|
||||||
|
include $(TOPDIR)/Makefile.tests
|
||||||
|
|
||||||
|
dumps: jump.dump ret.dump
|
||||||
|
|
||||||
|
tests: jump.guac ret.guac
|
@ -0,0 +1,8 @@
|
|||||||
|
int test() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int x = test();
|
||||||
|
return x;
|
||||||
|
}
|
3
test_programs/riscv_instructions/jump_instructions/ret.c
Normal file
3
test_programs/riscv_instructions/jump_instructions/ret.c
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
int main() {
|
||||||
|
return 1;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
TOPDIR = ../..
|
||||||
|
include $(TOPDIR)/Makefile.tests
|
||||||
|
|
||||||
|
dumps: unsigned_addition.dump unsigned_division.dump unsigned_multiplication.dump unsigned_substraction.dump
|
||||||
|
|
||||||
|
tests: unsigned_addition.guac unsigned_division.guac unsigned_multiplication.guac unsigned_substraction.guac
|
@ -0,0 +1,15 @@
|
|||||||
|
# Simple arithmetics program
|
||||||
|
|
||||||
|
These allow to check whether the following instructions are correctly implemented.
|
||||||
|
|
||||||
|
- addi
|
||||||
|
- sd
|
||||||
|
- sw
|
||||||
|
- li
|
||||||
|
- sw
|
||||||
|
- lw
|
||||||
|
- mv
|
||||||
|
- addw
|
||||||
|
- nop
|
||||||
|
- ld
|
||||||
|
- ret
|
@ -0,0 +1,9 @@
|
|||||||
|
#include "userlib/syscall.h"
|
||||||
|
#include "userlib/libnachos.h"
|
||||||
|
|
||||||
|
// EXPECTS TWO VARIABLES WITH A VALUE OF UNSIGNED 1
|
||||||
|
int main() {
|
||||||
|
unsigned int x = 0;
|
||||||
|
unsigned int y = 1;
|
||||||
|
x = x + y;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
// Expecting two variables with a value of two
|
||||||
|
int main() {
|
||||||
|
unsigned int x = 4;
|
||||||
|
unsigned int y = 2;
|
||||||
|
x = x / y;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
// EXPECTS TWO VARIABLES WITH A VALUE OF UNSIGNED 2
|
||||||
|
int main() {
|
||||||
|
unsigned int x = 1;
|
||||||
|
unsigned int y = 2;
|
||||||
|
x = x * y;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
// EXPECTS TWO VARIABLES WITH A VALUE OF UNSIGNED 1
|
||||||
|
int main() {
|
||||||
|
unsigned int x = 1;
|
||||||
|
unsigned int y = 1;
|
||||||
|
x = x - y;
|
||||||
|
}
|
4
test_programs/userlib/Makefile
Normal file
4
test_programs/userlib/Makefile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
TOPDIR = ../
|
||||||
|
include $(TOPDIR)/Makefile.tests
|
||||||
|
|
||||||
|
default: sys.o libnachos.o
|
61
test_programs/userlib/ldscript.lds
Normal file
61
test_programs/userlib/ldscript.lds
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
ldscript for running user programs under Nachos
|
||||||
|
|
||||||
|
Sections should be aligned on page boundaries. Here an alignement of
|
||||||
|
at least 0x2000 is selected, thus supporting pages up to 8KB
|
||||||
|
large. See addrspace.cc for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ENTRY(__start)
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Skip an area of about 8k, so that NULL pointer dereferences can
|
||||||
|
be detected */
|
||||||
|
. += 0x2000;
|
||||||
|
|
||||||
|
.sys ALIGN(0x4000) : {
|
||||||
|
*(.init)
|
||||||
|
*(.sys)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code is aligned on a 16k boundary
|
||||||
|
Due to the size of the .sys section, the code start address will
|
||||||
|
presumably be at address 0x4000 */
|
||||||
|
.text ALIGN(0x400000) : {
|
||||||
|
_ftext = .;
|
||||||
|
eprol = .;
|
||||||
|
*(.text)
|
||||||
|
*(.fini)
|
||||||
|
}
|
||||||
|
etext = .;
|
||||||
|
_etext = .;
|
||||||
|
|
||||||
|
/* Initialized data is aligned on a 16k boundary */
|
||||||
|
.data ALIGN(0x4000) : {
|
||||||
|
_fdata = .;
|
||||||
|
*(.data)
|
||||||
|
*(.sdata)
|
||||||
|
}
|
||||||
|
.rodata ALIGN(0x4000) :
|
||||||
|
{
|
||||||
|
*(.rdata)
|
||||||
|
*(.srodata)
|
||||||
|
*(.rodata)
|
||||||
|
}
|
||||||
|
edata = .;
|
||||||
|
_edata = .;
|
||||||
|
|
||||||
|
/* Non-initialized data is aligned on a 16k boundary */
|
||||||
|
/* Bss = Block Started by Symbol */
|
||||||
|
.bss ALIGN(0x4000) : {
|
||||||
|
*(.bss)
|
||||||
|
*(.sbss)
|
||||||
|
*(.scommon)
|
||||||
|
*(COMMON)
|
||||||
|
}
|
||||||
|
|
||||||
|
end = .;
|
||||||
|
_end = .;
|
||||||
|
|
||||||
|
}
|
630
test_programs/userlib/libnachos.c
Normal file
630
test_programs/userlib/libnachos.c
Normal file
@ -0,0 +1,630 @@
|
|||||||
|
/*! \file libnachos.c
|
||||||
|
* \brief Functions of our library, for user programs.
|
||||||
|
*
|
||||||
|
* This library only provides some usefull functions for
|
||||||
|
* programming.
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* This file is part of the Nachos-RiscV distribution
|
||||||
|
* Copyright (c) 2022 University of Rennes 1.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details
|
||||||
|
* (see see <http://www.gnu.org/licenses/>).
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libnachos.h"
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// threadStart()
|
||||||
|
/*! Makes a thread execute a function or program. This function
|
||||||
|
// is static, it is called internally by library function threadCreate
|
||||||
|
// and should not be called directly by user programs. The interest
|
||||||
|
// of this function is to be able to terminate threads correctly,
|
||||||
|
// even when the thread to be terminated does not explicitly call
|
||||||
|
// Exit. threadStart provides the mechanism by which Exit
|
||||||
|
// is called automatically
|
||||||
|
//
|
||||||
|
// \param func is the identificator of the function to execute.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static void threadStart(uint64_t func)
|
||||||
|
{
|
||||||
|
VoidNoArgFunctionPtr func2;
|
||||||
|
func2=(VoidNoArgFunctionPtr)func;
|
||||||
|
// Call the function that actually contains the thread code
|
||||||
|
(*func2)();
|
||||||
|
// Call exit, such that there is no return using an empty stack
|
||||||
|
Exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// threadCreate()
|
||||||
|
/*! Creates a thread and makes it execute a function.
|
||||||
|
//
|
||||||
|
// NB : instead of directly executing the required function,
|
||||||
|
// function threadStart is called so as to ensure
|
||||||
|
// that the thread will properly exit
|
||||||
|
// This function must be called instead of calling directly
|
||||||
|
// the system call newThread
|
||||||
|
//
|
||||||
|
// \param name the name of the thread (for debugging purpose)
|
||||||
|
// \param func is the address of the function to execute.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
ThreadId threadCreate(char *debug_name, VoidNoArgFunctionPtr func)
|
||||||
|
{
|
||||||
|
return newThread(debug_name, (uint64_t)threadStart,(uint64_t)func);
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_strcmp()
|
||||||
|
/*! String comparison
|
||||||
|
//
|
||||||
|
// \param s1 is the first string,
|
||||||
|
// \param s2 is the second one.
|
||||||
|
// \return an integer greater than, equal to, or less than 0,
|
||||||
|
// if the first string is greater than, equal to, or less than
|
||||||
|
// the the second string.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
int n_strcmp(const char *s1, const char *s2)
|
||||||
|
{
|
||||||
|
int comparaison;
|
||||||
|
int fini=0;
|
||||||
|
int i=0;
|
||||||
|
while(!fini) {
|
||||||
|
if ((s1[i]==0)&&(s2[i]==0)) {
|
||||||
|
fini=1;
|
||||||
|
comparaison=0;
|
||||||
|
}
|
||||||
|
if (s1[i]<s2[i]) {
|
||||||
|
fini=1;
|
||||||
|
comparaison=-1;
|
||||||
|
}
|
||||||
|
if(s1[i]>s2[i]) {
|
||||||
|
fini=1;
|
||||||
|
comparaison=1;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return comparaison;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_strcpy()
|
||||||
|
/*! String copy
|
||||||
|
//
|
||||||
|
// \param dst is where the string is to be copied,
|
||||||
|
// \param src is where the string to copy is.
|
||||||
|
// \return dst, if the copy successes, 0 otherwise
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
char *n_strcpy(char *dst, const char *src)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
int fini=0;
|
||||||
|
if ((dst!=0)&&(src!=0)) {
|
||||||
|
while(fini==0) {
|
||||||
|
if(src[i]=='\0') fini=1;
|
||||||
|
dst[i]=src[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_strlen()
|
||||||
|
/*! Gives the number of bytes in a string, not including the
|
||||||
|
// terminating null character.
|
||||||
|
//
|
||||||
|
// \param c is a pointer onto a string.
|
||||||
|
// \return the length of the string.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
size_t n_strlen(const char *s)
|
||||||
|
{
|
||||||
|
size_t i=0;
|
||||||
|
while (s[i] != 0) i++;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_strcat()
|
||||||
|
/*! Appends a copy of a string, including null character, to the end
|
||||||
|
// of another string. Enough memory has to be available in the
|
||||||
|
// destination string.
|
||||||
|
//
|
||||||
|
// \param dst is a pointer onto the string where the other string
|
||||||
|
// will be appended.
|
||||||
|
// \param src is the string to append.
|
||||||
|
// \return the pointer string dst.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
char *n_strcat(char *dst, const char *src)
|
||||||
|
{
|
||||||
|
int i,j,k;
|
||||||
|
i=(int)n_strlen(dst);
|
||||||
|
j=(int)n_strlen(src);
|
||||||
|
for(k=i;k<=j+i;k++) {
|
||||||
|
dst[k]=src[k-i];
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_toupper()
|
||||||
|
/*! Gives the upper-case letter corresponding to the lower-case
|
||||||
|
// letter passed as parameter.
|
||||||
|
//
|
||||||
|
// \param c is the ASCII code of the letter to transform.
|
||||||
|
// \return the corresponding upper-case letter
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
int n_toupper(int c)
|
||||||
|
{
|
||||||
|
if((c>='a')&&(c<='z'))
|
||||||
|
return c+('A'-'a');
|
||||||
|
else return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_tolower()
|
||||||
|
/*! Gives the lower-case letter corresponding to the upper-case
|
||||||
|
// letter passed as parameter
|
||||||
|
//
|
||||||
|
// \param c is the ASCII code of the letter to transform.
|
||||||
|
// \return the corresponding lower-case letter
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
int n_tolower(int c)
|
||||||
|
{
|
||||||
|
if((c<='Z')&&(c>='A'))
|
||||||
|
return c+('a'-'A');
|
||||||
|
else return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_atoi()
|
||||||
|
/*! String to integer conversion.
|
||||||
|
//
|
||||||
|
// \param c is a pointer onto a string.
|
||||||
|
// \return the corresponding value
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
int n_atoi(const char *str)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
int fini=0;
|
||||||
|
int val=0;
|
||||||
|
int negative = 0;
|
||||||
|
if (str[i] == '-') {
|
||||||
|
negative = 1; i=1;
|
||||||
|
}
|
||||||
|
while(!fini)
|
||||||
|
{
|
||||||
|
if(str[i]==0 || str[i]<'0' || str[i]>'9')
|
||||||
|
fini=1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val*=10;
|
||||||
|
val+=str[i]-'0';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (negative) return(-val); else return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_memcmp()
|
||||||
|
/*! Memory comparison.
|
||||||
|
//
|
||||||
|
// \param s1 is the first memory area,
|
||||||
|
// \param s2 is the second memory area.
|
||||||
|
// \param n size in bytes of the area to be compared.
|
||||||
|
// \return an integer less than, equal to, or greater than 0,
|
||||||
|
// according as s1 is lexicographically less than, equal to,
|
||||||
|
// or greater than s2 when taken to be unsigned characters.
|
||||||
|
//
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
int n_memcmp(const void *s1, const void *s2, size_t n)
|
||||||
|
{
|
||||||
|
unsigned char* c1=(unsigned char*)s1;
|
||||||
|
unsigned char* c2=(unsigned char*)s2;
|
||||||
|
|
||||||
|
int comparaison=0;
|
||||||
|
int fini=0;
|
||||||
|
int i=0;
|
||||||
|
while ((!fini)&&(i<n)) {
|
||||||
|
if (c1[i]<c2[i]) {
|
||||||
|
fini=1;
|
||||||
|
comparaison=-1;
|
||||||
|
}
|
||||||
|
if (c1[i]>c2[i]) {
|
||||||
|
fini=1;
|
||||||
|
comparaison=1;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return comparaison;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_memcpy()
|
||||||
|
/*! Memory copy.
|
||||||
|
//
|
||||||
|
// \param s1 is where the elements are to be copied,
|
||||||
|
// \param s2 is the memory area to copy.
|
||||||
|
// \param n size in bytes of the area to be copied.
|
||||||
|
// \return the memory area where the copy has been done.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void *n_memcpy(void *s1, const void *s2, size_t n)
|
||||||
|
{
|
||||||
|
|
||||||
|
unsigned char* c1=(unsigned char*)s1;
|
||||||
|
unsigned char* c2=(unsigned char*)s2;
|
||||||
|
|
||||||
|
int i=0;
|
||||||
|
if ((c1!=0)&&(c2!=0)) {
|
||||||
|
while(i<n) {
|
||||||
|
c1[i]=c2[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return (void *)c1;
|
||||||
|
}
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_memset()
|
||||||
|
/*! Sets the first n bytes of a memory area to a value (converted to
|
||||||
|
// an unsigned char).
|
||||||
|
//
|
||||||
|
// \param s is the memory area to transform,
|
||||||
|
// \param c is the value wanted,
|
||||||
|
// \param n is the number of bytes to put at c.
|
||||||
|
// \return s.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void *n_memset(void *s, int c, size_t n)
|
||||||
|
{
|
||||||
|
unsigned char* c1=(unsigned char*)s;
|
||||||
|
int i;
|
||||||
|
for (i=0;i<n;i++) {
|
||||||
|
c1[i]=c;
|
||||||
|
}
|
||||||
|
return (void *)c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_dumpmem()
|
||||||
|
/*! Dumps on the string the n first bytes of a memory area
|
||||||
|
// (used for debugging)
|
||||||
|
//
|
||||||
|
// \param addr address of the memory area
|
||||||
|
// \param len number of bytes to be dumped
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void n_dumpmem(char *addr, int len)
|
||||||
|
{
|
||||||
|
#define TOHEX(x) \
|
||||||
|
({ char __x = (x); if(__x < 10) __x+='0'; else __x='a'+(__x-10) ; __x; })
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0 ; i < len ; i++) {
|
||||||
|
char s[3];
|
||||||
|
if ((i%16) == 0)
|
||||||
|
n_printf("%x\t", (unsigned long)&addr[i]);
|
||||||
|
else if ((i%8) == 0)
|
||||||
|
n_printf(" ");
|
||||||
|
s[0] = TOHEX((addr[i] >> 4) & 0xf);
|
||||||
|
s[1] = TOHEX(addr[i] & 0xf);
|
||||||
|
s[2] = '\0';
|
||||||
|
n_printf("%s ", s);
|
||||||
|
if ((((i+1)%16) == 0) || (i == len-1))
|
||||||
|
n_printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PUTCHAR(carac) \
|
||||||
|
do { \
|
||||||
|
if (result < len-1) *buff++ = carac;\
|
||||||
|
result++; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_vsnprintf()
|
||||||
|
/*! Build a string according to a specified format (internal function)
|
||||||
|
//
|
||||||
|
// Nachos vsnprintf accepts:
|
||||||
|
// %c to print a character,
|
||||||
|
// %s, to print a string,
|
||||||
|
// %d, to print an integer,
|
||||||
|
// %x, to print an integer in hexa
|
||||||
|
// %lx %ld same for 64-bit values
|
||||||
|
// %f, to print a floating point value
|
||||||
|
//
|
||||||
|
// \param buff the destination buffer to generate the string to
|
||||||
|
// \param len the size of buff, determines the number max of
|
||||||
|
// characters copied to buff (taking the final \0 into account)
|
||||||
|
// \param format the string to parse
|
||||||
|
// \param ap parameters to print
|
||||||
|
//
|
||||||
|
// \return the number of characters formatted (NOT including \0),
|
||||||
|
// that is, the number of characters that would have been written
|
||||||
|
// to the buffer if it were large enough. -1 on error.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
static int n_vsnprintf(char *buff, int len, const char *format, va_list ap)
|
||||||
|
{
|
||||||
|
int i, result;
|
||||||
|
|
||||||
|
if (!buff || !format || (len < 0)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
result = 0;
|
||||||
|
|
||||||
|
for (i=0 ; format[i] != '\0' ; i++) {
|
||||||
|
switch (format[i]) {
|
||||||
|
case '%':
|
||||||
|
i++;
|
||||||
|
switch(format[i]) {
|
||||||
|
case '%': {
|
||||||
|
PUTCHAR('%');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'i':
|
||||||
|
case'd': {
|
||||||
|
int integer = (int) va_arg(ap,int);
|
||||||
|
int cpt2 = 0;
|
||||||
|
char buff_int[11];
|
||||||
|
if (integer<0) {PUTCHAR('-');
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
int m10 = integer%10;
|
||||||
|
m10 = (m10 < 0)? -m10:m10;
|
||||||
|
buff_int[cpt2++]=(char)('0'+ m10);
|
||||||
|
integer=integer/10;
|
||||||
|
} while(integer!=0);
|
||||||
|
for (cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--) {
|
||||||
|
PUTCHAR(buff_int[cpt2]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'l': {
|
||||||
|
i++;
|
||||||
|
switch(format[i]) {
|
||||||
|
case 'd': {
|
||||||
|
long longer = va_arg(ap,long);
|
||||||
|
int cpt2 = 0;
|
||||||
|
char buff_long[20];
|
||||||
|
if (longer<0) {
|
||||||
|
PUTCHAR('-');
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
int m10 = longer%10;
|
||||||
|
m10 = (m10 < 0)? -m10:m10;
|
||||||
|
buff_long[cpt2++]=(char)('0'+ m10);
|
||||||
|
longer=longer/10;
|
||||||
|
} while(longer!=0);
|
||||||
|
for (cpt2 = cpt2 - 1 ; cpt2 >= 0 ; cpt2--) {
|
||||||
|
PUTCHAR(buff_long[cpt2]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x': {
|
||||||
|
uint64_t hexa = va_arg(ap,long);
|
||||||
|
uint64_t nb;
|
||||||
|
uint32_t i, had_nonzero = 0;
|
||||||
|
for (i=0 ; i < 16 ; i++) {
|
||||||
|
nb = (hexa << (i*4));
|
||||||
|
nb = (nb >> 60);
|
||||||
|
nb = nb & 0x000000000000000f;
|
||||||
|
// Skip the leading zeros
|
||||||
|
if (nb == 0) {
|
||||||
|
if (had_nonzero) {
|
||||||
|
PUTCHAR((uint8_t)'0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
had_nonzero = 1;
|
||||||
|
if (nb < 10)
|
||||||
|
PUTCHAR((uint8_t)'0'+(uint8_t)nb);
|
||||||
|
else
|
||||||
|
PUTCHAR((uint8_t)'a'+(uint8_t)(nb-10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! had_nonzero)
|
||||||
|
PUTCHAR((uint8_t)'0');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
PUTCHAR('%');
|
||||||
|
PUTCHAR('l');
|
||||||
|
PUTCHAR(format[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c': {
|
||||||
|
int value = va_arg(ap,int);
|
||||||
|
PUTCHAR((char)value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 's': {
|
||||||
|
char *string = va_arg(ap,char *);
|
||||||
|
if (! string)
|
||||||
|
string = "(null)";
|
||||||
|
for( ; *string != '\0' ; string++)
|
||||||
|
PUTCHAR(*string);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x': {
|
||||||
|
uint32_t hexa = va_arg(ap,int);
|
||||||
|
uint32_t nb;
|
||||||
|
uint32_t i, had_nonzero = 0;
|
||||||
|
for (i=0 ; i < 8 ; i++) {
|
||||||
|
nb = (hexa << (i*4));
|
||||||
|
nb = (nb >> 28);
|
||||||
|
nb = nb & 0x0000000f;
|
||||||
|
// Skip the leading zeros
|
||||||
|
if (nb == 0) {
|
||||||
|
if (had_nonzero)
|
||||||
|
PUTCHAR((uint8_t)'0');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
had_nonzero = 1;
|
||||||
|
if (nb < 10)
|
||||||
|
PUTCHAR((uint8_t)'0'+(uint8_t)nb);
|
||||||
|
else
|
||||||
|
PUTCHAR((uint8_t)'a'+(uint8_t)(nb-10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! had_nonzero)
|
||||||
|
PUTCHAR((uint8_t)'0');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/*case 'f': {
|
||||||
|
// Very simple routine to print floats as xxxx.yyyyy
|
||||||
|
// Not very good (unable to print large numbers)
|
||||||
|
// If anyone wants to re-write it, feel free ...
|
||||||
|
double f = (double) va_arg(ap,double);
|
||||||
|
int cpt2, j;
|
||||||
|
char buff_float[200];
|
||||||
|
long ient,idec;
|
||||||
|
if (f<0) {
|
||||||
|
PUTCHAR('-');
|
||||||
|
f = -f;
|
||||||
|
}
|
||||||
|
ient = (int)f;
|
||||||
|
// 100000 = print 5 digits max
|
||||||
|
idec = (int)((f - ((double)ient))*100000);
|
||||||
|
// Round up
|
||||||
|
if ( f - ((double)ient) - ((double)idec)/100000.0 >= 0.5E-5)
|
||||||
|
idec ++;
|
||||||
|
cpt2 = 0;
|
||||||
|
// Print digits after the '.'
|
||||||
|
for (j=0 ; j<5 ; j++) {
|
||||||
|
buff_float[cpt2++]=(char)('0'+(idec%10));
|
||||||
|
idec=idec/10;
|
||||||
|
}
|
||||||
|
buff_float[cpt2++] = '.';
|
||||||
|
// Print digits before the '.'
|
||||||
|
do {
|
||||||
|
buff_float[cpt2++]=(char)('0'+ (ient%10));
|
||||||
|
ient=ient/10;
|
||||||
|
} while (ient!=0);
|
||||||
|
for(j = cpt2 - 1 ; j >= 0 ; j--)
|
||||||
|
PUTCHAR(buff_float[j]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
default:
|
||||||
|
PUTCHAR('%');
|
||||||
|
PUTCHAR(format[i]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PUTCHAR(format[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*buff = '\0';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_snprintf()
|
||||||
|
/*! Build a string according to a specified format
|
||||||
|
//
|
||||||
|
// Nachos snprintf accepts:
|
||||||
|
// %c to print a character,
|
||||||
|
// %s, to print a string,
|
||||||
|
// %d, to print an integer,
|
||||||
|
// %x, to print a string in hexa
|
||||||
|
// %f, to print a floating point value
|
||||||
|
//
|
||||||
|
// \param buff the destination buffer to generate the string to
|
||||||
|
// \param len the size of buff, determines the number max of
|
||||||
|
// characters copied to buff (taking the final \0 into account)
|
||||||
|
// \param format the string to parse
|
||||||
|
// \param ... the (variable number of) arguments
|
||||||
|
//
|
||||||
|
// \return the number of characters formatted (NOT including \0),
|
||||||
|
// that is, the number of characters that would have been written
|
||||||
|
// to the buffer if it were large enough. -1 on error.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
int n_snprintf(char * buff, int len, const char *format, ...){
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, format);
|
||||||
|
len = n_vsnprintf(buff, len, format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_printf()
|
||||||
|
/*! Print to the standard output parameters.
|
||||||
|
//
|
||||||
|
// Nachos printf accepts:
|
||||||
|
// %c to print a character,
|
||||||
|
// %s, to print a string,
|
||||||
|
// %d, to print an integer,
|
||||||
|
// %x, to print a string in hexa
|
||||||
|
// %ld, %lx, same for 64-bit values
|
||||||
|
// %f, to print a floating point value
|
||||||
|
//
|
||||||
|
// \param parameters to print,
|
||||||
|
// \param type of print.
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
void n_printf(const char *format, ...){
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
char buff[200];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
len = n_vsnprintf(buff, sizeof(buff), format, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (len >= sizeof(buff)) {
|
||||||
|
len = sizeof(buff) - 1;
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
|
Write(buff,len,CONSOLE_OUTPUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
// n_read_int()
|
||||||
|
/*!
|
||||||
|
// Very basic minimalist read integer function, no error
|
||||||
|
// checking...
|
||||||
|
*/
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
int n_read_int(void) {
|
||||||
|
char buff[200];
|
||||||
|
Read(buff,200,CONSOLE_INPUT);
|
||||||
|
return n_atoi(buff);
|
||||||
|
}
|
87
test_programs/userlib/libnachos.h
Normal file
87
test_programs/userlib/libnachos.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*! \file libnachos.h
|
||||||
|
\brief Function structures for programs
|
||||||
|
|
||||||
|
Libnachos proposes several 'libc-like' functions
|
||||||
|
for:
|
||||||
|
Input-Output operations,
|
||||||
|
String operations,
|
||||||
|
Memory operations,
|
||||||
|
System calls are defined in kernel/syscalls.h
|
||||||
|
|
||||||
|
Nachos-libc functions are prefixed by 'n' to avoid
|
||||||
|
any confusion with standard libc functions.
|
||||||
|
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* This file is part of the Nachos-RiscV distribution
|
||||||
|
* Copyright (c) 2022 University of Rennes 1.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details
|
||||||
|
* (see see <http://www.gnu.org/licenses/>).
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "userlib/syscall.h"
|
||||||
|
|
||||||
|
typedef void (*VoidNoArgFunctionPtr)();
|
||||||
|
typedef unsigned int size_t;
|
||||||
|
|
||||||
|
// Thread management
|
||||||
|
// ----------------------------
|
||||||
|
ThreadId threadCreate(char * debug_name, VoidNoArgFunctionPtr func);
|
||||||
|
|
||||||
|
// Input/Output operations :
|
||||||
|
// ------------------------------------
|
||||||
|
|
||||||
|
// Print on the standard output specified parameters.
|
||||||
|
void n_printf(const char *format, ...);
|
||||||
|
|
||||||
|
// Format <buff> (of max length <len>) according to the format <format>
|
||||||
|
int n_snprintf(char * buff, int len, const char *format, ...);
|
||||||
|
|
||||||
|
// Read an integer on the standard input
|
||||||
|
int n_read_int(void);
|
||||||
|
|
||||||
|
// String operations :
|
||||||
|
// -------------------
|
||||||
|
|
||||||
|
// Compare two strings byte by byte.
|
||||||
|
int n_strcmp(const char *s1, const char *s2);
|
||||||
|
|
||||||
|
// Copy a string.
|
||||||
|
char* n_strcpy(char *dst, const char *src);
|
||||||
|
|
||||||
|
// Return the number of bytes in a string.
|
||||||
|
size_t n_strlen(const char *s);
|
||||||
|
|
||||||
|
// appends a copy of a string, to the end of another string.
|
||||||
|
char* n_strcat(char *dst, const char *src);
|
||||||
|
|
||||||
|
// Return a upper-case letter,
|
||||||
|
// equivalent to the lower-case letter given.
|
||||||
|
int n_toupper(int c);
|
||||||
|
|
||||||
|
// Return a lower-case letter,
|
||||||
|
// equivalent to the upper-case letter given.
|
||||||
|
int n_tolower(int c);
|
||||||
|
|
||||||
|
// Convert a string in integer.
|
||||||
|
int n_atoi(const char *str);
|
||||||
|
|
||||||
|
// Concerning memory area operations :
|
||||||
|
// -----------------------------------
|
||||||
|
|
||||||
|
// Compare two memory area, looking at the first n bytes .
|
||||||
|
int n_memcmp(const void *s1, const void *s2, size_t n);
|
||||||
|
|
||||||
|
// Copy n byte from an memory area to another.
|
||||||
|
void* n_memcpy(void *s1, const void *s2, size_t n);
|
||||||
|
|
||||||
|
// Set the first n bytes in a memory area to a specified value.
|
||||||
|
void* n_memset(void *s, int c, size_t n);
|
343
test_programs/userlib/sys.s
Normal file
343
test_programs/userlib/sys.s
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
/* Start.s
|
||||||
|
* Assembly language assist for user programs running on top of Nachos.
|
||||||
|
*
|
||||||
|
* Since we don't want to pull in the entire C library, we define
|
||||||
|
* what we need for a user program here, namely Start and the system
|
||||||
|
* calls.
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* This file is part of the BurritOS distribution
|
||||||
|
* Copyright (c) 2022 University of Rennes 1.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details
|
||||||
|
* (see see <http://www.gnu.org/licenses/>).
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define IN_ASM
|
||||||
|
#include "userlib/syscall.h"
|
||||||
|
|
||||||
|
// Equivalent to ".text", but with a different name, in order
|
||||||
|
// to be correctly handled by the ldscript
|
||||||
|
.section .sys,"ax",@progbits
|
||||||
|
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------
|
||||||
|
* __start
|
||||||
|
* Initialize running a C program, by calling "main".
|
||||||
|
*
|
||||||
|
* NOTE: This has to be first, so that it gets loaded at location 0.
|
||||||
|
* The Nachos kernel always starts a program by jumping to location 0.
|
||||||
|
* -------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
.globl __start
|
||||||
|
.type __start, @function
|
||||||
|
__start:
|
||||||
|
|
||||||
|
/* Call the program entry point */
|
||||||
|
call main
|
||||||
|
li a0, 0
|
||||||
|
call Exit
|
||||||
|
jr ra /* if we return from main, exit(0) */
|
||||||
|
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------
|
||||||
|
* System call stubs:
|
||||||
|
* Assembly language assist to make system calls to the Nachos kernel.
|
||||||
|
* There is one stub per system call, that places the code for the
|
||||||
|
* system call into register r10, and leaves the arguments to the
|
||||||
|
* system call alone (in other words, arg1 is in r12, arg2 is
|
||||||
|
* in r13, arg3 is in r14, arg4 is in r15)
|
||||||
|
*
|
||||||
|
* The return value is in r10. This follows the standard C calling
|
||||||
|
* convention on the RISC-V.
|
||||||
|
* -------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
.globl Halt
|
||||||
|
.type __Halt, @function
|
||||||
|
Halt:
|
||||||
|
addi a7,zero,SC_HALT
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Exit
|
||||||
|
.type __Exit, @function
|
||||||
|
Exit:
|
||||||
|
addi a7,zero,SC_EXIT
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Exec
|
||||||
|
.type __Exec, @function
|
||||||
|
Exec:
|
||||||
|
addi a7,zero,SC_EXEC
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Join
|
||||||
|
.type __Join, @function
|
||||||
|
Join:
|
||||||
|
addi a7,zero,SC_JOIN
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Create
|
||||||
|
.type __Create, @function
|
||||||
|
Create:
|
||||||
|
addi a7,zero,SC_CREATE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Open
|
||||||
|
.type __Open, @function
|
||||||
|
Open:
|
||||||
|
addi a7,zero,SC_OPEN
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Read
|
||||||
|
.type __Read, @function
|
||||||
|
Read:
|
||||||
|
addi a7,zero,SC_READ
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.globl Write
|
||||||
|
.type __Write, @function
|
||||||
|
Write:
|
||||||
|
addi a7,zero,SC_WRITE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Seek
|
||||||
|
.type __Seek, @function
|
||||||
|
Seek:
|
||||||
|
addi a7,zero,SC_SEEK
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Close
|
||||||
|
.type __Close, @function
|
||||||
|
Close:
|
||||||
|
addi a7,zero,SC_CLOSE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl FSList
|
||||||
|
.type __FSList, @function
|
||||||
|
FSList:
|
||||||
|
addi a7,zero,SC_FSLIST
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl newThread
|
||||||
|
.type __newThread, @function
|
||||||
|
newThread:
|
||||||
|
addi a7,zero,SC_NEW_THREAD
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Remove
|
||||||
|
.type __Remove, @function
|
||||||
|
Remove:
|
||||||
|
addi a7,zero,SC_REMOVE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Yield
|
||||||
|
.type __Yield, @function
|
||||||
|
Yield:
|
||||||
|
addi a7,zero,SC_YIELD
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl PError
|
||||||
|
.type __PError, @function
|
||||||
|
PError:
|
||||||
|
addi a7,zero,SC_PERROR
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl P
|
||||||
|
.type __P, @function
|
||||||
|
P:
|
||||||
|
addi a7,zero,SC_P
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl V
|
||||||
|
.type __V, @function
|
||||||
|
V:
|
||||||
|
addi a7,zero,SC_V
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
.globl SemCreate
|
||||||
|
.type __SemCreate, @function
|
||||||
|
SemCreate:
|
||||||
|
addi a7,zero,SC_SEM_CREATE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl SemDestroy
|
||||||
|
.type __SemDestroy, @function
|
||||||
|
SemDestroy:
|
||||||
|
addi a7,zero,SC_SEM_DESTROY
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl SysTime
|
||||||
|
.type __SysTime, @function
|
||||||
|
SysTime:
|
||||||
|
addi a7,zero,SC_SYS_TIME
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl LockCreate
|
||||||
|
.type __LockCreate, @function
|
||||||
|
LockCreate:
|
||||||
|
addi a7,zero,SC_LOCK_CREATE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
.globl LockDestroy
|
||||||
|
.type __LockDestroy, @function
|
||||||
|
LockDestroy:
|
||||||
|
addi a7,zero,SC_LOCK_DESTROY
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl LockAcquire
|
||||||
|
.type __LockAquire, @function
|
||||||
|
LockAcquire:
|
||||||
|
addi a7,zero,SC_LOCK_ACQUIRE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl LockRelease
|
||||||
|
.type __LockRelease, @function
|
||||||
|
LockRelease:
|
||||||
|
addi a7,zero,SC_LOCK_RELEASE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl CondCreate
|
||||||
|
.type __CondCreate, @function
|
||||||
|
CondCreate:
|
||||||
|
addi a7,zero,SC_COND_CREATE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl CondDestroy
|
||||||
|
.type __CondDestroy, @function
|
||||||
|
CondDestroy:
|
||||||
|
addi a7,zero,SC_COND_DESTROY
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl CondWait
|
||||||
|
.type __CondWait, @function
|
||||||
|
CondWait:
|
||||||
|
addi a7,zero,SC_COND_WAIT
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl CondSignal
|
||||||
|
.type __CondSignal, @function
|
||||||
|
CondSignal:
|
||||||
|
addi a7,zero,SC_COND_SIGNAL
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl CondBroadcast
|
||||||
|
.type __CondBroadcast, @function
|
||||||
|
CondBroadcast:
|
||||||
|
addi a7,zero,SC_COND_BROADCAST
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl TtySend
|
||||||
|
.type __TtySend, @function
|
||||||
|
TtySend:
|
||||||
|
addi a7,zero,SC_TTY_SEND
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl TtyReceive
|
||||||
|
.type __TtyReceive, @function
|
||||||
|
TtyReceive:
|
||||||
|
addi a7,zero,SC_TTY_RECEIVE
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Mkdir
|
||||||
|
.type __Mkdir, @function
|
||||||
|
Mkdir:
|
||||||
|
addi a7,zero,SC_MKDIR
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Rmdir
|
||||||
|
.type __Rmdir, @function
|
||||||
|
Rmdir:
|
||||||
|
addi a7,zero,SC_RMDIR
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
||||||
|
.globl Mmap
|
||||||
|
.type __Mmap, @function
|
||||||
|
Mmap:
|
||||||
|
addi a7,zero,SC_MMAP
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
.globl Debug
|
||||||
|
.type __Debug, @function
|
||||||
|
Debug:
|
||||||
|
addi a7,zero,SC_DEBUG
|
||||||
|
ecall
|
||||||
|
jr ra
|
||||||
|
|
||||||
|
|
287
test_programs/userlib/syscall.h
Normal file
287
test_programs/userlib/syscall.h
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
/*! \file syscall.h
|
||||||
|
\brief Nachos system call interface.
|
||||||
|
|
||||||
|
These are Nachos kernel operations
|
||||||
|
that can be invoked from user programs, by trapping to the kernel
|
||||||
|
via the "syscall" instruction.
|
||||||
|
|
||||||
|
This file is included by user programs and by the Nachos kernel.
|
||||||
|
|
||||||
|
Each of these is invoked by a user program by simply calling the
|
||||||
|
procedure; an assembly language stub stuffs the system call code
|
||||||
|
into a register, and traps to the kernel. The kernel procedures
|
||||||
|
are then invoked in the Nachos kernel, after appropriate error checking,
|
||||||
|
from the system call entry point in exception.cc.
|
||||||
|
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* This file is part of the Nachos-RiscV distribution
|
||||||
|
* Copyright (c) 2022 University of Rennes 1.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details
|
||||||
|
* (see see <http://www.gnu.org/licenses/>).
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef SYSCALLS_H
|
||||||
|
#define SYSCALLS_H
|
||||||
|
|
||||||
|
//#include "kernel/copyright.h"
|
||||||
|
|
||||||
|
/* system call codes -- used by the stubs to tell the kernel which system call
|
||||||
|
* is being asked for
|
||||||
|
*/
|
||||||
|
#define SC_HALT 0
|
||||||
|
#define SC_EXIT 1
|
||||||
|
#define SC_EXEC 2
|
||||||
|
#define SC_JOIN 3
|
||||||
|
#define SC_CREATE 4
|
||||||
|
#define SC_OPEN 5
|
||||||
|
#define SC_READ 6
|
||||||
|
#define SC_WRITE 7
|
||||||
|
#define SC_SEEK 8
|
||||||
|
#define SC_CLOSE 9
|
||||||
|
#define SC_NEW_THREAD 10
|
||||||
|
#define SC_YIELD 11
|
||||||
|
#define SC_PERROR 12
|
||||||
|
#define SC_P 13
|
||||||
|
#define SC_V 14
|
||||||
|
#define SC_SEM_CREATE 15
|
||||||
|
#define SC_SEM_DESTROY 16
|
||||||
|
#define SC_LOCK_CREATE 17
|
||||||
|
#define SC_LOCK_DESTROY 18
|
||||||
|
#define SC_LOCK_ACQUIRE 19
|
||||||
|
#define SC_LOCK_RELEASE 20
|
||||||
|
#define SC_COND_CREATE 21
|
||||||
|
#define SC_COND_DESTROY 22
|
||||||
|
#define SC_COND_WAIT 23
|
||||||
|
#define SC_COND_SIGNAL 24
|
||||||
|
#define SC_COND_BROADCAST 25
|
||||||
|
#define SC_TTY_SEND 26
|
||||||
|
#define SC_TTY_RECEIVE 27
|
||||||
|
#define SC_MKDIR 28
|
||||||
|
#define SC_RMDIR 29
|
||||||
|
#define SC_REMOVE 30
|
||||||
|
#define SC_FSLIST 31
|
||||||
|
#define SC_SYS_TIME 32
|
||||||
|
#define SC_MMAP 33
|
||||||
|
#define SC_DEBUG 34
|
||||||
|
|
||||||
|
#ifndef IN_ASM
|
||||||
|
|
||||||
|
/* The system call interface. These are the operations the Nachos
|
||||||
|
* kernel needs to support, to be able to run user programs.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef int t_error;
|
||||||
|
|
||||||
|
/* Stop Nachos, and print out performance stats */
|
||||||
|
void Halt();
|
||||||
|
|
||||||
|
|
||||||
|
/* Return the time spent running Nachos */
|
||||||
|
|
||||||
|
/*! \brief Defines the Nachos basic time unit */
|
||||||
|
typedef struct {
|
||||||
|
long seconds;
|
||||||
|
long nanos;
|
||||||
|
} Nachos_Time;
|
||||||
|
void SysTime(Nachos_Time *t);
|
||||||
|
|
||||||
|
/* Address space control operations: Exit, Exec, and Join */
|
||||||
|
|
||||||
|
/* This user program is done (status = 0 means exited normally). */
|
||||||
|
void Exit(int status);
|
||||||
|
|
||||||
|
/* A unique identifier for a thread executed within a user program */
|
||||||
|
typedef unsigned long ThreadId;
|
||||||
|
|
||||||
|
/* Run the executable, stored in the Nachos file "name", and return the
|
||||||
|
* master thread identifier
|
||||||
|
*/
|
||||||
|
ThreadId Exec(char *name);
|
||||||
|
|
||||||
|
/* Create a new thread in the current process
|
||||||
|
* Return thread identifier
|
||||||
|
*/
|
||||||
|
ThreadId newThread(char * debug_name, int func, int arg);
|
||||||
|
|
||||||
|
/* Only return once the the thread "id" has finished.
|
||||||
|
*/
|
||||||
|
t_error Join(ThreadId id);
|
||||||
|
|
||||||
|
/* Yield the CPU to another runnable thread, whether in this address space
|
||||||
|
* or not.
|
||||||
|
*/
|
||||||
|
void Yield();
|
||||||
|
|
||||||
|
/*! Print the last error message with the personalized one "mess" */
|
||||||
|
void PError(char *mess);
|
||||||
|
|
||||||
|
/* File system operations: Create, Open, Read, Write, Seek, Close
|
||||||
|
* These functions are patterned after UNIX -- files represent
|
||||||
|
* both files *and* hardware I/O devices.
|
||||||
|
*
|
||||||
|
* If this assignment is done before doing the file system assignment,
|
||||||
|
* note that the Nachos file system has a stub implementation, which
|
||||||
|
* will work for the purposes of testing out these routines.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* A unique identifier for an open Nachos file. */
|
||||||
|
typedef unsigned long OpenFileId;
|
||||||
|
|
||||||
|
/* when an address space starts up, it has two open files, representing
|
||||||
|
* keyboard input and display output (in UNIX terms, stdin and stdout).
|
||||||
|
* Read and Write can be used directly on these, without first opening
|
||||||
|
* the console device.
|
||||||
|
*/
|
||||||
|
#define CONSOLE_INPUT 0
|
||||||
|
#define CONSOLE_OUTPUT 1
|
||||||
|
|
||||||
|
/* Create a Nachos file, with "name" */
|
||||||
|
t_error Create(char *name,int size);
|
||||||
|
|
||||||
|
/* Open the Nachos file "name", and return an "OpenFileId" that can
|
||||||
|
* be used to read and write to the file.
|
||||||
|
*/
|
||||||
|
OpenFileId Open(char *name);
|
||||||
|
|
||||||
|
/* Write "size" bytes from "buffer" to the open file. */
|
||||||
|
t_error Write(char *buffer, int size, OpenFileId id);
|
||||||
|
|
||||||
|
/* Read "size" bytes from the open file into "buffer".
|
||||||
|
* Return the number of bytes actually read -- if the open file isn't
|
||||||
|
* long enough, or if it is an I/O device, and there aren't enough
|
||||||
|
* characters to read, return whatever is available (for I/O devices,
|
||||||
|
* you should always wait until you can return at least one character).
|
||||||
|
*/
|
||||||
|
t_error Read(char *buffer, int size, OpenFileId id);
|
||||||
|
|
||||||
|
/* Seek to a specified offset into an opened file */
|
||||||
|
t_error Seek(int offset, OpenFileId id);
|
||||||
|
|
||||||
|
#ifndef SYSDEP_H
|
||||||
|
/* Close the file, we're done reading and writing to it. */
|
||||||
|
t_error Close(OpenFileId id);
|
||||||
|
#endif // SYSDEP_H
|
||||||
|
|
||||||
|
/* Remove the file */
|
||||||
|
t_error Remove(char* name);
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
/* system calls concerning directory management */
|
||||||
|
|
||||||
|
/* Create a new repertory
|
||||||
|
Return a negative number if an error ocurred.
|
||||||
|
*/
|
||||||
|
t_error Mkdir(char* name);
|
||||||
|
|
||||||
|
/* Destroy a repertory, which must be empty.
|
||||||
|
Return a negative number if an error ocurred.
|
||||||
|
*/
|
||||||
|
t_error Rmdir(char* name);
|
||||||
|
|
||||||
|
/* List the content of NachOS FileSystem */
|
||||||
|
t_error FSList();
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
/* User-level synchronization operations : */
|
||||||
|
|
||||||
|
/* System calls concerning semaphores management */
|
||||||
|
|
||||||
|
typedef unsigned long SemId;
|
||||||
|
|
||||||
|
/* Create a semaphore, initialising it at count.
|
||||||
|
Return a Semid, which will enable to do operations on this
|
||||||
|
semaphore */
|
||||||
|
SemId SemCreate(char * debug_name, int count);
|
||||||
|
|
||||||
|
/* Destroy a semaphore identified by sema.
|
||||||
|
Return a negative number if an error occured during the destruction */
|
||||||
|
t_error SemDestroy(SemId sema);
|
||||||
|
|
||||||
|
/* Do the operation P() on the semaphore sema */
|
||||||
|
t_error P(SemId sema);
|
||||||
|
|
||||||
|
/* Do the operation V() on the semaphore sema */
|
||||||
|
t_error V(SemId sema);
|
||||||
|
|
||||||
|
/* System calls concerning locks management */
|
||||||
|
typedef unsigned long LockId;
|
||||||
|
|
||||||
|
/* Create a lock.
|
||||||
|
Return an identifier */
|
||||||
|
LockId LockCreate(char * debug_name);
|
||||||
|
|
||||||
|
/* Destroy a lock.
|
||||||
|
Return a negative number if an error ocurred
|
||||||
|
during the destruction. */
|
||||||
|
t_error LockDestroy(LockId id);
|
||||||
|
|
||||||
|
/* Do the operation Acquire on the lock id.
|
||||||
|
Return a negative number if an error ocurred. */
|
||||||
|
t_error LockAcquire(LockId id);
|
||||||
|
|
||||||
|
/* Do the operation Release on the lock id.
|
||||||
|
Return a negative number if an error ocurred.
|
||||||
|
*/
|
||||||
|
t_error LockRelease(LockId id);
|
||||||
|
|
||||||
|
/* System calls concerning conditions variables. */
|
||||||
|
typedef unsigned long CondId;
|
||||||
|
|
||||||
|
/* Create a new condition variable */
|
||||||
|
CondId CondCreate(char * debug_name);
|
||||||
|
|
||||||
|
/* Destroy a condition variable.
|
||||||
|
Return a negative number if an error ocurred.
|
||||||
|
*/
|
||||||
|
t_error CondDestroy(CondId id);
|
||||||
|
|
||||||
|
/* Do the operation Wait on a condition variable.
|
||||||
|
Returns a negative number if an error ocurred.
|
||||||
|
*/
|
||||||
|
t_error CondWait(CondId cond);
|
||||||
|
|
||||||
|
/* Do the operation Signal on a condition variable (wake up only one thread).
|
||||||
|
Return a negative number if an error ocurred.
|
||||||
|
*/
|
||||||
|
t_error CondSignal(CondId cond);
|
||||||
|
|
||||||
|
/* Do the operation Signal on a condition variable (wake up all threads).
|
||||||
|
Return a negative number if an error ocurred.
|
||||||
|
*/
|
||||||
|
t_error CondBroadcast(CondId cond);
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
/* System calls concerning serial port and console */
|
||||||
|
|
||||||
|
/* Send the message on the serial communication link.
|
||||||
|
Returns the number of bytes successfully sent.
|
||||||
|
*/
|
||||||
|
int TtySend(char *mess);
|
||||||
|
|
||||||
|
/* Wait for a message comming from the serial communication link.
|
||||||
|
The length of the buffer where the bytes will be copied is given as a parameter.
|
||||||
|
Returns the number of characters actually received.
|
||||||
|
*/
|
||||||
|
int TtyReceive(char *mess,int length);
|
||||||
|
|
||||||
|
/* Map an opened file in memory. Size is the size to be mapped in bytes.
|
||||||
|
*/
|
||||||
|
void *Mmap(OpenFileId f, int size);
|
||||||
|
|
||||||
|
/* For debug purpose
|
||||||
|
*/
|
||||||
|
void Debug(int param);
|
||||||
|
|
||||||
|
#endif // IN_ASM
|
||||||
|
#endif // SYSCALL_H
|
Loading…
Reference in New Issue
Block a user