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.
115 lines
4.6 KiB
Rust
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.
|
|
}
|