dremu/src/core/isa/rv32i/store.rs
Victor Mignot a22e0a7738
Add first basic implementation of a RISC-V hart.
This commit add the main components to run a simple RISC-V hart.
This hart implement all the RV32I instructions (excepted FENCE and
System instruction ones).
Multicore is not implemented as it would need first to implement the
RISC-V Weak Memory Ordering model into our harts.
2024-05-10 15:22:44 +02:00

115 lines
4.6 KiB
Rust

//! The RV32I Store instructions.
//! These instructions allow to write data on the system bus.
//! All these instructions use the same opcode and use the S-type instruction format.
use crate::core::isa::{Instruction, RVException, RVHart};
/// The Store instructions opcode.
pub const STORE_OPCODE: u32 = 0x23;
/// The start bit for the `rs1` field on the 32-bits encoded instruction.
const RS1_START_BIT: usize = 15;
/// The start bit for the `rs2` field on the 32-bits encoded instruction.
const RS2_START_BIT: usize = 20;
/// The start bit for the `funct3` field on the 32-bits encoded instruction.
const FUNCT3_START_BIT: usize = 12;
/// The start bit for the `imm[4:0]` field on the 32-bits encoded instruction.
const IMM_4_0_START_BIT: usize = 7;
/// The start bit for the `imm[11:5]` field on the 32-bits encoded instruction.
const IMM_11_5_START_BIT: usize = 25;
/// The mask to apply on top of the encoded instruction shifted to `FUNCT3_START_BIT` to extract the `funct3` value.
const FUNCT3_MASK: u32 = 0b111;
/// The mask to apply on top of the encoded instruction shifted to a register field to extract the register index value.
const REGISTER_INDEX_MASK: u32 = 0x1f;
/// The mask to apply on top of the encoded instruction shifted to `IMM_4_0_START_BIT` to extract the `imm[4:0]` value.
const IMM_4_0_MASK: u32 = 0x1f;
/// The `funct3` field code for the SB operation.
const FUNCT3_SB_CODE: u32 = 0b000;
/// The `funct3` field code for the SH operation.
const FUNCT3_SH_CODE: u32 = 0b001;
/// The `funct3` field code for the SW operation.
const FUNCT3_SW_CODE: u32 = 0b010;
/// All the available Store instruction.
enum StoreOperation {
/// Store Byte
/// Store the `rs2` 8 least significant bits at the address resulting from the addition of the `rs1` register and the signed-extended 12-bits immediate.
SB,
/// Store Halfword
/// Store the `rs2` 16 least significant bits at the address resulting from the addition of the `rs1` register and the signed-extended 12-bits immediate.
SH,
/// Store Word
/// Store the `rs2` register value at the address resulting from the addition of the `rs1` register and the signed-extended 12-bits immediate.
SW,
/// Unknown operation.
/// This is the result of having a `funct3` instruction field that does not match any of the previous operations.
Unknown,
}
/// A decoded Store instruction, ready to be executed.
pub struct StoreInstruction {
/// The hart's X register index holding the base address to which should be added the signed immediate to get the effective address where data should be written.
rs1: usize,
/// The hart's X register index holding the data to be stored at the given address.
rs2: usize,
/// The store operation to be applied.
funct3: StoreOperation,
/// The sign-extended 12-bits immediate that should be added to the `rs1` register value to get the store effective address.
imm: u32,
}
impl StoreInstruction {
/// Decode the given raw `instruction` into a new `StoreInstruction` ready to be executed.
pub fn new(instruction: u32) -> Self {
let rs1 = ((instruction >> RS1_START_BIT) & REGISTER_INDEX_MASK) as usize;
let rs2 = ((instruction >> RS2_START_BIT) & REGISTER_INDEX_MASK) as usize;
let funct3 = match (instruction >> FUNCT3_START_BIT) & FUNCT3_MASK {
FUNCT3_SB_CODE => StoreOperation::SB,
FUNCT3_SH_CODE => StoreOperation::SH,
FUNCT3_SW_CODE => StoreOperation::SW,
_ => StoreOperation::Unknown,
};
let imm_4_0 = (instruction >> IMM_4_0_START_BIT) & IMM_4_0_MASK;
let imm_11_5 = (instruction as i32 >> IMM_11_5_START_BIT) as u32;
let imm = (imm_11_5 << 5) | imm_4_0;
StoreInstruction {
rs1,
rs2,
funct3,
imm,
}
}
}
impl Instruction for StoreInstruction {
fn execute(&self, core: &mut RVHart) -> Result<(), RVException> {
let dest = (core.x[self.rs1] as i32).wrapping_add(self.imm as i32) as u32;
match self.funct3 {
StoreOperation::SB => {
core.store_byte(dest, core.x[self.rs2] as u8);
Ok(())
}
StoreOperation::SH => {
core.store_halfword(dest, core.x[self.rs2] as u16);
Ok(())
}
StoreOperation::SW => {
core.store_word(dest, core.x[self.rs2]);
Ok(())
}
StoreOperation::Unknown => Err(RVException::InstructionDecodeError),
}
}
}
#[cfg(test)]
mod test {
// TODO: Add test when proper bus and memory unit implementation.
}