FP instruction implementations

This commit is contained in:
Lumi Kalt 2024-05-01 13:15:03 +01:00
parent 29a7dfa277
commit ac07031631
4 changed files with 295 additions and 22 deletions

View file

@ -1,5 +1,17 @@
use std::mem;
use crate::{env::Env, instructions::kind::Kind};
/// Always "safe" because f32 and i32 have the same size.
fn u32_to_f32(i: u32) -> f32 {
unsafe { mem::transmute(i) }
}
/// Always "safe" because f32 and i32 have the same size.
fn _f32_to_u32(f: f32) -> u32 {
unsafe { mem::transmute(f) }
}
/// lui rd, imm
fn lui(env: &mut Env, rd: usize, imm: u32) {
env.set_register(rd, imm);
@ -25,6 +37,30 @@ fn mul(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(rd, env.get_register(ra).wrapping_mul(env.get_register(rb)));
}
/// mulh rd, ra, rb (SxS)
fn mulh(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(
rd,
(env.get_register(ra) as i64 * env.get_register(rb) as i64 >> 32) as u32,
);
}
/// mulhsu rd, ra, rb (SxU)
fn mulhsu(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(
rd,
(env.get_register(ra) as i64 * env.get_register(rb) as i64 >> 32) as u32,
);
}
/// mulhu rd, ra, rb (UxU)
fn mulu(env: &mut Env, rd: usize, ra: usize, rb: usize) {
env.set_register(
rd,
(env.get_register(ra) as u64 * env.get_register(rb) as u64 >> 32) as u32,
);
}
/// beq ra, rb, imm
fn beq(env: &mut Env, ra: usize, rb: usize, imm: u32) -> bool {
if env.get_register(ra) == env.get_register(rb) {
@ -40,6 +76,16 @@ fn jal(env: &mut Env, rd: usize, imm: u32) {
env.pc = env.pc.wrapping_add(imm);
}
/// fadd.s fd, fa, fb
fn fadd_s(env: &mut Env, fd: usize, fa: usize, fb: usize) {
env.set_fregister(fd, env.get_fregister(fa) + env.get_fregister(fb));
}
/// fdiv.s fd, fa, fb
fn fdiv_s(env: &mut Env, fd: usize, fa: usize, fb: usize) {
env.set_fregister(fd, env.get_fregister(fa) / env.get_fregister(fb));
}
/// fmadd.s fd, fa, fb, fc
fn fmadd_s(env: &mut Env, fd: usize, fa: usize, fb: usize, fc: usize) {
env.set_fregister(
@ -48,6 +94,19 @@ fn fmadd_s(env: &mut Env, fd: usize, fa: usize, fb: usize, fc: usize) {
);
}
/// fcvt.s.w fd, ra
fn fcvt_s_w(env: &mut Env, fd: usize, ra: usize) {
env.set_fregister(fd, env.get_register(ra) as i32 as f32);
}
/// fmv.w.x fd, ra
fn fmv_w_x(env: &mut Env, fd: usize, ra: usize) {
env.set_fregister(fd, u32_to_f32(env.get_register(ra)));
}
/// Executes the instruction.
///
/// Returns true if the instruction is a jump.
pub fn run_instruction(env: &mut Env, instruction: u32) -> bool {
let (kind, _) = Kind::to_op(instruction);
let mut regs = kind.get_regs().unwrap();
@ -81,15 +140,43 @@ pub fn run_instruction(env: &mut Env, instruction: u32) -> bool {
mul(env, rd, ra, rb);
false
}
0b0110011 if funct3 == 0b001 && funct7 == 0b0000001 => {
mulh(env, rd, ra, rb);
false
}
0b0110011 if funct3 == 0b010 && funct7 == 0b0000001 => {
mulhsu(env, rd, ra, rb);
false
}
0b0110011 if funct3 == 0b011 && funct7 == 0b0000001 => {
mulu(env, rd, ra, rb);
false
}
0b1100011 => beq(env, ra, rb, imm.unwrap()),
0b1101111 => {
jal(env, rd, imm.unwrap());
true
}
0b1000011 => {
0b1010011 if funct7 == 0x00 => {
fadd_s(env, fd, fa, fb);
false
}
0b1010011 if funct7 == 0x0c => {
fdiv_s(env, fd, fa, fb);
false
}
0b1000011 if fb == 0 => {
fmadd_s(env, fd, fa, fb, fc);
false
}
_ => todo!(),
0b1010011 if funct7 == 0x68 => {
fcvt_s_w(env, fd, ra);
false
}
0b1010011 if funct7 == 0x78 => {
fmv_w_x(env, fd, ra);
false
}
_ => todo!("op: {:032b}", instruction),
}
}

View file

@ -2,29 +2,161 @@ use colored::Colorize;
use crate::env::Env;
pub fn info(_env: &Env, op: &str, args: Vec<String>) -> Vec<String> {
match op {
"nop" => vec!["Do nothing - wait 1 cycle".to_string()],
"li" => vec![format!("load {} into the register {}", args[1], args[0])],
/// Display a helpful message about an instruction.
///
/// Returns the message and, if needed, tags to highlight specific registers ([x regs...], [fregs...]).
pub fn info(
env: &Env,
op: &str,
args: Vec<String>,
display_mode: char,
) -> (String, (Vec<usize>, Vec<usize>)) {
let args: Vec<_> = args
.into_iter()
.map(|a| {
if let Ok(num) = a.parse::<u32>() {
match display_mode {
'd' => num.to_string(),
's' => (num as i32).to_string(),
'b' => format!("{:032b}", num),
'h' => format!("{:08x}", num),
_ => unreachable!(),
}
} else {
a
}
})
.collect();
let mut tag = (vec![], vec![]);
let msg = match op {
"nop" => vec!["do nothing".to_string()],
"li" => vec![format!(
"load {} into {}",
args[1].italic().yellow(),
args[0].red()
)],
"lui" => {
let imm = format!("{:032b}", args[1].parse::<i32>().unwrap() as u32)
.chars()
.rev()
.collect::<String>();
vec![
format!(
"load the upper 20 bits of {} into {}",
args[1], args[0]
),
format!("load the upper 20 bits of {} into {}", args[1], args[0]),
format!(
"{} = {}{}",
args[1],
imm[12..32].to_string().red(),
imm[0..12].to_string()
args[1].italic().yellow(),
imm[12..32].to_string().green(),
imm[0..12].to_string().strikethrough().black()
),
format!(
"{:>1$} ← {2} << 12",
args[0].blue(),
args[1].to_string().len(),
imm[12..32].to_string(),
),
format!("{:>1$} <- {2}{3:0>12}", args[0], args[1].to_string().len() - 1, imm[12..32].to_string(), "0".to_string().bold()),
]
}
_ => todo!(),
"add" => {
tag = (
vec![
env.str_to_register(&args[1]).unwrap(),
env.str_to_register(&args[2]).unwrap(),
],
vec![],
);
vec![format!(
"add the values of {0} and {1} and store the result in {2}\n{2} ← {0} + {1}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)]
}
"addi" => {
tag = (
vec![
env.str_to_register(&args[1]).unwrap(),
env.str_to_register(&args[2]).unwrap(),
],
vec![],
);
vec![format!(
"add the values of {0} and {1} and store the result in {2}\n{2} ← {0} + {1}",
args[2].blue(),
args[1].italic().yellow(),
args[0].blue()
)]
}
"sub" => vec![format!(
"subtract the value of {} from the value of {} and store the result in {}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)],
"mul" => {
tag = (
vec![],
vec![
env.str_to_fregister(&args[1]).unwrap(),
env.str_to_fregister(&args[2]).unwrap(),
],
);
vec![format!(
"multiply the values of {0} and {1} and store the result in {2}\n{2} ← {0} ✕ {1}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)]
}
"fadd.s" => {
tag = (
vec![],
vec![
env.str_to_fregister(&args[1]).unwrap(),
env.str_to_fregister(&args[2]).unwrap(),
],
);
vec![format!(
"add the values of {0} and {1} and store the result in {2}\n{2} ← {0} + {1}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)]
}
"fdiv.s" => {
tag = (
vec![],
vec![
env.str_to_fregister(&args[1]).unwrap(),
env.str_to_fregister(&args[2]).unwrap(),
],
);
vec![format!(
"divide the value of {0} by the value of {1} and store the result in {2}\n{2} ← {0} ÷ {1}",
args[1].blue(),
args[2].blue(),
args[0].blue()
)]
}
"fcvt.s.w" => {
tag = (vec![env.str_to_register(&args[1]).unwrap()], vec![]);
vec![format!(
"convert the value of {} to a float and store it in {}",
args[1].blue(),
args[0].blue()
)]
}
"fmv.w.x" => {
tag = (vec![env.str_to_register(&args[1]).unwrap()], vec![]);
vec![format!(
"interpret the value of {0} as a float and store it in {1}\n{1} ← {0}",
args[1].blue(),
args[0].blue()
)]
}
op => todo!("{}", op),
}
.join("\n");
(msg, tag)
}

View file

@ -3,6 +3,7 @@ pub mod kind {
use bitfield::bitfield;
/// A pseudo instruction is an instruction that
/// will be converted by the engine to real instructions
#[derive(Debug)]
pub struct Pseudo(pub &'static str);
@ -218,8 +219,20 @@ pub mod kind {
}
0b1100011 => (Kind::B(B(instruction)), "beq".into()),
0b1101111 => (Kind::J(J(instruction)), "jal".into()),
0b1010011 if funct7 == 0x00 => {
(Kind::R(R(instruction)), "fadd.s".into())
}
0b1010011 if funct7 == 0x0c => {
(Kind::R(R(instruction)), "fdiv.s".into())
}
0b1010011 if funct7 == 0x68 => {
(Kind::R(R(instruction)), "fcvt.s.w".into())
}
0b1010011 if funct7 == 0x78 => {
(Kind::R(R(instruction)), "fmv.w.x".into())
}
other => {
println!("{:07b}", other);
println!("todo: opcode={:07b}", other);
todo!()
}
}
@ -509,6 +522,52 @@ pub fn instruction(op: &str) -> Option<(Kind, Vec<Arg>)> {
}),
vec![Arg::Register(0), Arg::Symbol],
),
// F Extension - assune rm is 0b000
// Arithmetic
"fadd.s" => (
Kind::R({
let mut r = R(0);
r.set_funct7(0x00);
r.set_funct3(0b000);
r.set_opcode(0b1010011);
r
}),
vec![Arg::Register(0), Arg::Register(1), Arg::Register(2)],
),
"fdiv.s" => (
Kind::R({
let mut r = R(0);
r.set_funct7(0x0c);
r.set_funct3(0b000);
r.set_opcode(0b1010011);
r
}),
vec![Arg::Register(0), Arg::Register(1), Arg::Register(2)],
),
// Move / Convert
"fcvt.s.w" => (
Kind::R({
let mut r = R(0);
r.set_funct7(0x68);
r.set_funct3(0b000);
r.set_opcode(0b1010011);
r
}),
vec![Arg::Register(0), Arg::Register(1)], // rb is not used
),
"fmv.w.x" => (
Kind::R({
let mut r = R(0);
r.set_funct7(0x78);
r.set_funct3(0b000);
r.set_opcode(0b1010011);
r
}),
vec![Arg::Register(0), Arg::Register(1)], // rb is not used
),
op => unimplemented!("{}", op),
})
}
@ -612,7 +671,7 @@ pub fn handle_pseudo(
// lui rd, imm
with(
get_instruction("lui"),
(imm & 0xFFFF000) | ((imm >> 11) & 0x1) << 12,
(imm & 0xfffff000) ^ ((imm >> 11) & 0x1) << 12,
regs.clone(),
),
// addi rd, rd, imm

View file

@ -67,8 +67,6 @@ fn lui() {
// U-Type
// | imm20 | rd | opcode
// 00000011010100101001 01010 0110111
// 11111111111101111011 01010 0110111
// 00001111111101111011 01010 0110111
};
// lui a0 13609
assert_eq!(
@ -146,7 +144,6 @@ fn addi() {
// I-Type
// | imm12 | ra |f3 | rd | opcode
// 000000000001 01010 000 01010 0010011
// 000000000001 01010 000 01010 0010011 (Ripes)
};
// addi a0 a0 1
assert_eq!(
@ -173,8 +170,6 @@ fn beq() {
// B-Type
// | imm7 | rb | ra |f3 |imm5 | opcode
// 0000000 01011 01010 000 00100 1100011
// 0000000 01011 01010 000 00100 1100011 (Ripes)
// 0000000 01011 01010 000 01000 1100011
};
// beq a0 a1 4
assert_eq!(