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
|
||||
/.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
|
||||
//!
|
||||
//! Crate du point d'entrée de burritos
|
||||
//!
|
||||
//! ## Liste des crate:
|
||||
//!
|
||||
//! - kernel
|
||||
//! - machine
|
||||
//! - A remplir
|
||||
use simulator::machine::Machine;
|
||||
use simulator::mem_cmp;
|
||||
|
||||
/// 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() {
|
||||
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