Skip to content

Commit

Permalink
Merge pull request #368 from SWIM-ucf/riscv-fpu
Browse files Browse the repository at this point in the history
Riscv-fpu Support!
  • Loading branch information
rharding8 authored Mar 29, 2024
2 parents fe5ec47 + ecf76fd commit ded6ffd
Show file tree
Hide file tree
Showing 12 changed files with 1,178 additions and 32 deletions.
5 changes: 5 additions & 0 deletions src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ pub async fn emulation_core_agent(scope: ReactorScope<Command, DatapathUpdate>)
state.updates.changed_stack,
RiscStateUpdate::UpdateStack(datapath.stack.clone())
);
send_update_riscv!(
state.scope,
state.updates.changed_coprocessor_registers,
RiscStateUpdate::UpdateCoprocessorRegisters(datapath.coprocessor.registers)
);
}
}
state.updates = Default::default();
Expand Down
18 changes: 17 additions & 1 deletion src/agent/datapath_reducer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use crate::emulation_core::mips::gp_registers::{GpRegisterType, GpRegisters};
use crate::emulation_core::mips::memory::Memory;
use crate::emulation_core::register::{RegisterType, Registers};
use crate::emulation_core::riscv::datapath::{RiscDatapathState, RiscStage};
use crate::emulation_core::riscv::registers::{RiscGpRegisterType, RiscGpRegisters};
use crate::emulation_core::riscv::registers::{
RiscFpRegisters, RiscGpRegisterType, RiscGpRegisters,
};
use crate::emulation_core::stack::Stack;
use gloo_console::log;
use std::rc::Rc;
Expand Down Expand Up @@ -40,6 +42,7 @@ pub struct MipsCoreState {
pub struct RiscCoreState {
pub state: RiscDatapathState,
pub registers: RiscGpRegisters,
pub coprocessor_registers: RiscFpRegisters,
pub memory: Memory,
pub current_stage: RiscStage,
pub stack: Stack,
Expand Down Expand Up @@ -151,6 +154,12 @@ impl Reducible for DatapathReducer {
stack,
..self.riscv.clone()
},
RiscStateUpdate::UpdateCoprocessorRegisters(coprocessor_registers) => {
RiscCoreState {
coprocessor_registers,
..self.riscv.clone()
}
}
},
..(*self).clone()
},
Expand Down Expand Up @@ -180,6 +189,13 @@ impl DatapathReducer {
}
}

pub fn get_dyn_fp_registers(&self) -> Vec<(Rc<dyn RegisterType>, u64)> {
match self.current_architecture {
MIPS => self.mips.coprocessor_registers.get_dyn_register_list(),
RISCV => self.riscv.coprocessor_registers.get_dyn_register_list(),
}
}

pub fn get_memory(&self) -> &Memory {
match self.current_architecture {
MIPS => &self.mips.memory,
Expand Down
3 changes: 2 additions & 1 deletion src/agent/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::emulation_core::mips::fp_registers::FpRegisters;
use crate::emulation_core::mips::gp_registers::GpRegisters;
use crate::emulation_core::mips::memory::Memory;
use crate::emulation_core::riscv::datapath::{RiscDatapathState, RiscStage};
use crate::emulation_core::riscv::registers::RiscGpRegisters;
use crate::emulation_core::riscv::registers::{RiscFpRegisters, RiscGpRegisters};
use crate::emulation_core::stack::Stack;
use crate::emulation_core::{architectures::AvailableDatapaths, mips::datapath::Stage};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -44,6 +44,7 @@ pub enum MipsStateUpdate {
pub enum RiscStateUpdate {
UpdateState(RiscDatapathState),
UpdateRegisters(RiscGpRegisters),
UpdateCoprocessorRegisters(RiscFpRegisters),
UpdateMemory(Memory),
UpdateStage(RiscStage),
UpdateStack(Stack),
Expand Down
3 changes: 1 addition & 2 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ use web_sys::HtmlInputElement;
use yew::prelude::*;
use yew::{html, Html, Properties};

use swim::emulation_core::register::Registers;
use swim::emulation_core::riscv::datapath::RiscStage;
use yew_agent::Spawnable;

Expand Down Expand Up @@ -657,7 +656,7 @@ fn app(props: &AppProps) -> Html {
</div>

// Right column
<Regview gp={datapath_state.get_dyn_gp_registers()} fp={datapath_state.mips.coprocessor_registers.get_dyn_register_list()} pc_limit={*pc_limit} communicator={props.communicator}/>
<Regview gp={datapath_state.get_dyn_gp_registers()} fp={datapath_state.get_dyn_fp_registers()} pc_limit={*pc_limit} communicator={props.communicator}/>
</div>
</>
}
Expand Down
1 change: 1 addition & 0 deletions src/emulation_core/riscv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

pub mod constants;
pub mod control_signals;
pub mod coprocessor;
pub mod datapath;
pub mod datapath_signals;
pub mod instruction;
Expand Down
16 changes: 16 additions & 0 deletions src/emulation_core/riscv/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub const FUNCT_SOP37: u8 = 0b011111;
/// Used for R-type instructions.
pub const OPCODE_OP: u8 = 0b0110011;
pub const OPCODE_OP_32: u8 = 0b0111011;
pub const OPCODE_OP_FP: u8 = 0b1010011;

/// Used for I-type instructions.
pub const OPCODE_IMM: u8 = 0b0010011;
Expand All @@ -52,11 +53,13 @@ pub const OPCODE_IMM_32: u8 = 0b0011011;
pub const OPCODE_JALR: u8 = 0b1100111;
// LOAD
pub const OPCODE_LOAD: u8 = 0b0000011;
pub const OPCODE_LOAD_FP: u8 = 0b0000111;
// SYSTEM
pub const OPCODE_SYSTEM: u8 = 0b1110011;

/// Used for S-type instructions.
pub const OPCODE_STORE: u8 = 0b0100011;
pub const OPCODE_STORE_FP: u8 = 0b0100111;

/// Used for B-type instructions.
pub const OPCODE_BRANCH: u8 = 0b1100011;
Expand All @@ -70,6 +73,19 @@ pub const OPCODE_AUIPC: u8 = 0b0010111;
/// Used for J-type instructions.
pub const OPCODE_JAL: u8 = 0b1101111;

/// Used for R4-type instructions.
// FMADD.S
pub const OPCODE_MADD: u8 = 0b1000011;
// FMSUB.S
pub const OPCODE_MSUB: u8 = 0b1000111;
// FNMSUB.S
pub const OPCODE_NMSUB: u8 = 0b1001011;
// FNMADD.S
pub const OPCODE_NMADD: u8 = 0b1001111;

/// Not a Number
pub const RISC_NAN: u32 = 0x7fc00000;

// "ENC" is short for encoding. There is no formal name for this field
// in the MIPS64 specification, other than the "shamt"/"sa" field that it
// replaces, so this was chosen as the mnemonic for this project.
Expand Down
220 changes: 220 additions & 0 deletions src/emulation_core/riscv/control_signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,223 @@ pub enum RegWriteEn {
NoWrite = 0,
YesWrite = 1,
}

pub mod floating_point {

use super::super::constants::*;
use serde::{Deserialize, Serialize};

#[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)]
pub struct FpuControlSignals {
pub round_mode: RoundingMode,
pub data_src: DataSrc,
pub data_write: DataWrite,
pub fpu_alu_op: FpuAluOp,
pub fpu_mem_to_reg: FpuMemToReg,
pub fpu_reg_dst: FpuRegDst,
pub fpu_reg_write: FpuRegWrite,
}

/// Determines the source of the `Data` register in the floating-point unit.
///
/// This is a special intermediary register that facilitates passing data between
/// the main processing unit and the floating-point unit.
#[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)]
pub enum DataSrc {
/// Use data from the main processing unit. Specifically, the data from register
/// `rs1` from a given instruction. This value can additionally be used in the cases
/// where this register is not written to.
MainProcessorUnit = 0,

/// Use data from the floating-point unit. Specifically, the data from register `rs1`
/// from a given instruction.
#[default]
FloatingPointUnitRS1 = 1,

/// Use data from the floating-point unit. Specifically, the data from the comparator.
FloatingPointUnitComp = 2,

/// Use data from the floating-point unit. Specifically, the Classify Mask.
FloatingPointUnitMask = 3,

/// Use the un-altered bits from the floating-point unit.
FloatingPointBits = 4,

/// Use the un-altered bits from the main unit.
MainProcessorBits = 5,
}

/// Determines whether to write to the `Data` register in the floating-point unit.
///
/// This acts as a toggle for the source of data to the main processing unit register
/// file. Additionally, it acts as a toggle for a source to the floating-point unit
/// register file (this could be overridden by the [`FpuMemToReg`] control signal).
/// For the latter two functions, it is imperative to unset the [`RegWriteEn`](super::RegWriteEn) and
/// [`FpuRegWrite`] control signals in cases where registers should not be modified
/// with unintended data.
#[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)]
pub enum DataWrite {
/// - Do not write to the data register.
/// - Source data to write to the main processing unit register file from the main
/// processing unit. This implies either the ALU result or the data read from memory
/// - Source data to write to the floating-point register file from the floating-point
/// ALU.
#[default]
NoWrite = 0,

/// - Write to the data register.
/// - Source data to write to the main processing unit register file from the
/// floating-point unit. Specifically, this is the data stored in the `Data` register
/// in the FPU, likely from register `rs1` from a given instruction. This data source
/// overrides the decision given by the [`MemToReg`](super::MemToReg) control signal.
/// - Source data to write to the floating-point register file from the `Data` register
/// in the FPU, likely from register `rs1` from a given instruction.
YesWrite = 1,
}

/// This doubly determines the operations sent to the floating-point ALU and the
/// floating-point comparator.
///
/// Only one of these units are effectively utilized in any given instruction.
///
/// The fifth bit of the control signal represents either a single-precision
/// floating-point operation (0), or a double-precision floating-point operation (1).
/// This fifth bit is determined by [`FpuRegWidth`].
///
/// *Implementation note:* The bits set for the comparator are intended to match
/// the bits used in the `cond` field of a `c.cond.fmt` instruction.
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub enum FpuAluOp {
#[default]
/// `_00000` (0):
/// - ALU: Perform an addition.
Addition = 0,

/// `_00001` (1):
/// - ALU: Perform a subtraction.
Subtraction = 1,

/// `_00010` (2):
/// - ALU: Perform a multiplication.
/// - Comparator: Set if equal.
MultiplicationOrEqual = 2,

/// `_00011` (3):
/// - ALU: Perform a division.
Division = 3,

/// `_00100` (4):
/// - ALU: Perform a Square Root.
Sqrt = 4,

/// `_00101` (5):
/// - ALU: Take the Minimum value.
Min = 5,

/// `_00110` (6):
/// - ALU: Take the Maximum value.
Max = 6,

/// `_00111` (7):
/// - ALU: Sign-Injection.
SGNJ = 7,

/// `_01000` (8):
/// - ALU: Negative Sign-Injection.
SGNJN = 8,

/// `_01001` (9):
/// - ALU: Xor Sign-Injection.
SGNJX = 9,

/// `_01010` (10):
/// - ALU: Classification Mask.
Class = 10,

/// `_01011` (11):
/// - ALU: Fused Multiplication-Addition.
MAdd = 11,

/// `_01100` (12):
/// - ALU: Fused Multiplication-Subtraction.
MSub = 12,

/// `_01101` (13):
/// - ALU: Fused Negated Multiplication-Subtraction.
NMSub = 13,

/// `_01110` (14):
/// - ALU: Fused Negated Multiplication-Addition.
NMAdd = 14,

/// `_10000` (16):
/// - Comparator: Set if less than.
Slt = 16,

/// `_10001` (17):
/// - Comparator: Set if less than or equal.
Sle = 17,
}

impl FpuAluOp {
/// Get the corresponding control signal given a function code.
pub fn from_function(function: u8) -> Result<Self, String> {
match function {
FUNCTION_C_EQ => Ok(Self::MultiplicationOrEqual),
FUNCTION_C_LT => Ok(Self::Slt),
FUNCTION_C_LE => Ok(Self::Sle),
_ => Err(format!("Unsupported function code `{function}`")),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub enum RoundingMode {
RNE = 0,
RTZ = 1,
RDN = 2,
RUP = 3,
RMM = 4,
#[default]
DRM = 7,
}

/// Determines, given that [`FpuRegWrite`] is set, what the source of a floating-point
/// register's new data will be.
///
/// This decision, if set, overrides the decision from the [`DataWrite`] control signal.
#[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)]
pub enum FpuMemToReg {
/// Do not use data from memory. Use the result of the [`DataWrite`] control signal.
#[default]
UseDataWrite = 0,

/// Use data from memory.
UseMemory = 1,
}

/// Determines, given that [`FpuRegWrite`] is set, which destination register to write
/// to, which largely depends on the instruction format.
#[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)]
pub enum FpuRegDst {
/// Use register `rs1`.
Reg1 = 0,

/// Use register `rs2`.
Reg2 = 1,

/// Use register `rd`.
#[default]
Reg3 = 2,
}

/// Determines if the floating-point register file should be written to.
#[derive(Clone, Default, PartialEq, Serialize, Deserialize, Debug)]
pub enum FpuRegWrite {
/// Do not write to the floating-point register file.
#[default]
NoWrite = 0,

/// Write to the floating-point register file.
YesWrite = 1,
}
}
Loading

0 comments on commit ded6ffd

Please sign in to comment.