use crate::isa::csr::CsrAddr;
use crate::isa::encoding::rv64f::opcodes as fp_opcodes;
use crate::isa::encoding::rv64i::opcodes;
use crate::isa::encoding::rvc;
use crate::isa::reg::RegIdx;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum InstSize {
Compressed,
#[default]
Standard,
}
impl InstSize {
#[must_use]
pub const fn as_u64(self) -> u64 {
match self {
Self::Compressed => 2,
Self::Standard => 4,
}
}
}
#[must_use]
pub const fn is_compressed(low_half: u16) -> bool {
low_half & rvc::QUADRANT_MASK != rvc::NOT_COMPRESSED
}
pub const OPCODE_MASK: u32 = 0x7F;
pub const RD_MASK: u32 = 0x1F;
pub const RS1_MASK: u32 = 0x1F;
pub const RS2_MASK: u32 = 0x1F;
pub const FUNCT3_MASK: u32 = 0x7;
pub const FUNCT7_MASK: u32 = 0x7F;
pub const CSR_MASK: u32 = 0xFFF;
pub trait InstructionBits {
fn opcode(&self) -> u32;
fn rd(&self) -> RegIdx;
fn rs1(&self) -> RegIdx;
fn rs2(&self) -> RegIdx;
fn funct3(&self) -> u32;
fn funct7(&self) -> u32;
fn csr(&self) -> CsrAddr;
fn rs3(&self) -> RegIdx;
}
impl InstructionBits for u32 {
#[inline(always)]
fn opcode(&self) -> u32 {
self & OPCODE_MASK
}
#[inline(always)]
fn rd(&self) -> RegIdx {
RegIdx::new(((self >> 7) & RD_MASK) as u8)
}
#[inline(always)]
fn rs1(&self) -> RegIdx {
RegIdx::new(((self >> 15) & RS1_MASK) as u8)
}
#[inline(always)]
fn rs2(&self) -> RegIdx {
RegIdx::new(((self >> 20) & RS2_MASK) as u8)
}
#[inline(always)]
fn rs3(&self) -> RegIdx {
RegIdx::new(((self >> 27) & RS1_MASK) as u8)
}
#[inline(always)]
fn funct3(&self) -> u32 {
(self >> 12) & FUNCT3_MASK
}
#[inline(always)]
fn funct7(&self) -> u32 {
(self >> 25) & FUNCT7_MASK
}
#[inline(always)]
fn csr(&self) -> CsrAddr {
CsrAddr::from_u32((self >> 20) & CSR_MASK)
}
}
#[derive(Clone, Debug, Default)]
pub struct Decoded {
pub raw: u32,
pub opcode: u32,
pub rd: RegIdx,
pub rs1: RegIdx,
pub rs2: RegIdx,
pub funct3: u32,
pub funct7: u32,
pub imm: i64,
}
const INSTRUCTION_WIDTH: u32 = 32;
const I_IMM_SHIFT: u32 = 20;
const S_IMM_LOW_SHIFT: u32 = 7;
const S_IMM_LOW_MASK: u32 = 0x1F;
const S_IMM_HIGH_SHIFT: u32 = 25;
const S_IMM_HIGH_MASK: u32 = 0x7F;
const S_IMM_COMBINED_SHIFT: u32 = 5;
const S_IMM_BITS: u32 = 12;
const B_IMM_11_SHIFT: u32 = 7;
const B_IMM_11_MASK: u32 = 1;
const B_IMM_4_1_SHIFT: u32 = 8;
const B_IMM_4_1_MASK: u32 = 0xF;
const B_IMM_10_5_SHIFT: u32 = 25;
const B_IMM_10_5_MASK: u32 = 0x3F;
const B_IMM_12_SHIFT: u32 = 31;
const B_IMM_12_MASK: u32 = 1;
const B_IMM_BITS: u32 = 13;
const B_IMM_12_POS: u32 = 12;
const B_IMM_11_POS: u32 = 11;
const B_IMM_10_5_POS: u32 = 5;
const B_IMM_4_1_POS: u32 = 1;
const U_IMM_MASK: u32 = 0xFFFFF000;
const J_IMM_19_12_SHIFT: u32 = 12;
const J_IMM_19_12_MASK: u32 = 0xFF;
const J_IMM_11_SHIFT: u32 = 20;
const J_IMM_11_MASK: u32 = 1;
const J_IMM_10_1_SHIFT: u32 = 21;
const J_IMM_10_1_MASK: u32 = 0x3FF;
const J_IMM_20_SHIFT: u32 = 31;
const J_IMM_20_MASK: u32 = 1;
const J_IMM_BITS: u32 = 21;
const J_IMM_20_POS: u32 = 20;
const J_IMM_19_12_POS: u32 = 12;
const J_IMM_11_POS: u32 = 11;
const J_IMM_10_1_POS: u32 = 1;
pub fn decode(inst: u32) -> Decoded {
let opcode = inst.opcode();
let imm = match opcode {
opcodes::OP_IMM
| opcodes::OP_LOAD
| opcodes::OP_JALR
| opcodes::OP_IMM_32
| opcodes::OP_MISC_MEM
| fp_opcodes::OP_LOAD_FP => decode_i_type_imm(inst),
opcodes::OP_STORE | fp_opcodes::OP_STORE_FP => decode_s_type_imm(inst),
opcodes::OP_BRANCH => decode_b_type_imm(inst),
opcodes::OP_LUI | opcodes::OP_AUIPC => decode_u_type_imm(inst),
opcodes::OP_JAL => decode_j_type_imm(inst),
_ => 0,
};
Decoded {
raw: inst,
opcode,
rd: InstructionBits::rd(&inst),
rs1: InstructionBits::rs1(&inst),
rs2: InstructionBits::rs2(&inst),
funct3: InstructionBits::funct3(&inst),
funct7: InstructionBits::funct7(&inst),
imm,
}
}
const fn decode_i_type_imm(inst: u32) -> i64 {
((inst as i32) >> I_IMM_SHIFT) as i64
}
const fn decode_s_type_imm(inst: u32) -> i64 {
let low = (inst >> S_IMM_LOW_SHIFT) & S_IMM_LOW_MASK;
let high = (inst >> S_IMM_HIGH_SHIFT) & S_IMM_HIGH_MASK;
let combined = (high << S_IMM_COMBINED_SHIFT) | low;
sign_extend(combined, S_IMM_BITS)
}
pub(crate) const fn decode_b_type_imm(inst: u32) -> i64 {
let bit_11 = (inst >> B_IMM_11_SHIFT) & B_IMM_11_MASK;
let bits_4_1 = (inst >> B_IMM_4_1_SHIFT) & B_IMM_4_1_MASK;
let bits_10_5 = (inst >> B_IMM_10_5_SHIFT) & B_IMM_10_5_MASK;
let bit_12 = (inst >> B_IMM_12_SHIFT) & B_IMM_12_MASK;
let combined = (bit_12 << B_IMM_12_POS)
| (bit_11 << B_IMM_11_POS)
| (bits_10_5 << B_IMM_10_5_POS)
| (bits_4_1 << B_IMM_4_1_POS);
sign_extend(combined, B_IMM_BITS)
}
const fn decode_u_type_imm(inst: u32) -> i64 {
((inst & U_IMM_MASK) as i32) as i64
}
pub(crate) const fn decode_j_type_imm(inst: u32) -> i64 {
let bits_19_12 = (inst >> J_IMM_19_12_SHIFT) & J_IMM_19_12_MASK;
let bit_11 = (inst >> J_IMM_11_SHIFT) & J_IMM_11_MASK;
let bits_10_1 = (inst >> J_IMM_10_1_SHIFT) & J_IMM_10_1_MASK;
let bit_20 = (inst >> J_IMM_20_SHIFT) & J_IMM_20_MASK;
let combined = (bit_20 << J_IMM_20_POS)
| (bits_19_12 << J_IMM_19_12_POS)
| (bit_11 << J_IMM_11_POS)
| (bits_10_1 << J_IMM_10_1_POS);
sign_extend(combined, J_IMM_BITS)
}
const fn sign_extend(val: u32, bits: u32) -> i64 {
let shift = INSTRUCTION_WIDTH - bits;
((val as i32) << shift >> shift) as i64
}