Merge branch 'decode_print' into 'main'

Simulator done

See merge request simpleos/burritos!6
This commit is contained in:
François Autin 2023-03-10 15:25:20 +00:00
commit b44b9d2b67
35 changed files with 4136 additions and 14 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
/target /target
/.idea
*.iml
*.txt

27
.gitlab-ci.yml Normal file
View 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

View File

@ -1,18 +1,14 @@
#![warn(missing_docs)] mod simulator;
//! # Crate burritos use simulator::machine::Machine;
//! use simulator::mem_cmp;
//! Crate du point d'entrée de burritos
//!
//! ## Liste des crate:
//!
//! - kernel
//! - machine
//! - A remplir
/// Cette fonction est le point d'entrée de burritos
///
/// Elle se contente de parser les arguments de lancement et les transmet ensuite
fn main() { fn main() {
println!("Hello, world!"); let mut m = Machine::_init_machine();
let path = "memoryComp.txt".to_string();
let checker = mem_cmp::MemChecker::from(&path);
mem_cmp::MemChecker::fill_memory_from_mem_checker(&checker, &mut m);
//mem_cmp::Mem_Checker::print_Mem_Checker(&checker);
//Machine::print_memory(&mut m, 0x400000, 0x405000);
Machine::run(&mut m);
} }

95
src/simulator/decode.rs Normal file
View 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
View 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
View 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
View 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(&section.addr);
let len: usize = string_hex_to_usize(&section.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(&current_line[0..next_word_index]);
tmp_len_str = String::from(&current_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(&section_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!("{}", &current);
}
}
#[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(&section_format);
let expected_vec: Vec<u8> = vec![0u8, 255u8, 10u8, 160u8, 165u8];
//println!("Vec from created section {:?}", &section.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
View 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
View 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
View File

@ -0,0 +1,4 @@
# Ignoring dump files
*.dump
*.o
target

21
test_programs/Makefile Normal file
View 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

View 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

View 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
View 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.

View 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/

View 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

View 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;
}
}
}

View 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;
}
}

View File

@ -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;
}
}
}

View File

@ -0,0 +1,7 @@
int main() {
int x = 0;
switch(x) {
case 1: x = 1; break;
default: return 0;
}
}

View File

@ -0,0 +1,6 @@
TOPDIR = ../..
include $(TOPDIR)/Makefile.tests
dumps: jump.dump ret.dump
tests: jump.guac ret.guac

View File

@ -0,0 +1,8 @@
int test() {
return 0;
}
int main() {
int x = test();
return x;
}

View File

@ -0,0 +1,3 @@
int main() {
return 1;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -0,0 +1,4 @@
TOPDIR = ../
include $(TOPDIR)/Makefile.tests
default: sys.o libnachos.o

View 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 = .;
}

View 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);
}

View 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
View 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

View 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