raki 1.3.2

RISC-V instruction decoder written in Rust.
Documentation
use super::{
    a_extension, base_i, m_extension, priv_extension, zicboz_extension, zicfiss_extension,
    zicntr_extension, zicsr_extension, zifencei_extension,
};
use super::{Decode, DecodeUtil, DecodingError};
use crate::instruction::{InstFormat, Instruction, OpcodeKind};
use crate::{Extensions, Isa};

#[allow(non_snake_case)]
impl Decode for u32 {
    fn decode(&self, isa: Isa) -> Result<Instruction, DecodingError> {
        let new_opc: OpcodeKind = self.parse_opcode(isa)?;
        let new_rd: Option<usize> = self.parse_rd(&new_opc)?;
        let new_rs1: Option<usize> = self.parse_rs1(&new_opc)?;
        let new_rs2: Option<usize> = self.parse_rs2(&new_opc)?;
        let new_imm: Option<i32> = self.parse_imm(&new_opc, isa)?;
        let new_fmt: InstFormat = new_opc.get_format();

        Ok(Instruction {
            opc: new_opc,
            rd: new_rd,
            rs1: new_rs1,
            rs2: new_rs2,
            imm: new_imm,
            inst_format: new_fmt,
            is_compressed: false,
        })
    }

    fn parse_opcode(self, isa: Isa) -> Result<OpcodeKind, DecodingError> {
        let extension = self.parse_extension();

        match extension {
            Ok(Extensions::BaseI) => {
                Ok(OpcodeKind::BaseI(base_i::bit_32::parse_opcode(self, isa)?))
            }
            Ok(Extensions::M) => Ok(OpcodeKind::M(m_extension::bit_32::parse_opcode(self, isa)?)),
            Ok(Extensions::A) => Ok(OpcodeKind::A(a_extension::bit_32::parse_opcode(self, isa)?)),
            Ok(Extensions::Zifencei) => Ok(OpcodeKind::Zifencei(
                zifencei_extension::bit_32::parse_opcode(self)?,
            )),
            Ok(Extensions::Zicsr) => Ok(OpcodeKind::Zicsr(zicsr_extension::bit_32::parse_opcode(
                self,
            )?)),
            Ok(Extensions::Zicfiss) => Ok(OpcodeKind::Zicfiss(
                zicfiss_extension::bit_32::parse_opcode(self)?,
            )),
            Ok(Extensions::Zicntr) => Ok(OpcodeKind::Zicntr(
                zicntr_extension::bit_32::parse_opcode(self)?,
            )),
            Ok(Extensions::Zicboz) => Ok(OpcodeKind::Zicboz(
                zicboz_extension::bit_32::parse_opcode(self)?,
            )),
            Ok(Extensions::Priv) => Ok(OpcodeKind::Priv(priv_extension::bit_32::parse_opcode(
                self,
            )?)),
            Ok(Extensions::C) => Err(DecodingError::Not32BitInst),
            Err(decoding_err) => Err(decoding_err),
        }
    }

    fn parse_rd(self, opkind: &OpcodeKind) -> Result<Option<usize>, DecodingError> {
        match opkind {
            OpcodeKind::BaseI(opc) => Ok(base_i::bit_32::parse_rd(self, opc)),
            OpcodeKind::M(opc) => Ok(m_extension::bit_32::parse_rd(self, opc)),
            OpcodeKind::A(opc) => Ok(a_extension::bit_32::parse_rd(self, opc)),
            OpcodeKind::Zifencei(opc) => Ok(zifencei_extension::bit_32::parse_rd(self, opc)),
            OpcodeKind::Zicsr(opc) => Ok(zicsr_extension::bit_32::parse_rd(self, opc)),
            OpcodeKind::Zicfiss(opc) => Ok(zicfiss_extension::bit_32::parse_rd(self, opc)),
            OpcodeKind::Zicntr(opc) => Ok(zicntr_extension::bit_32::parse_rd(self, opc)),
            OpcodeKind::Zicboz(opc) => Ok(zicboz_extension::bit_32::parse_rd(self, opc)),
            OpcodeKind::Priv(opc) => Ok(priv_extension::bit_32::parse_rd(self, opc)),
            OpcodeKind::C(_) => Err(DecodingError::Not32BitInst),
        }
    }

    fn parse_rs1(self, opkind: &OpcodeKind) -> Result<Option<usize>, DecodingError> {
        match opkind {
            OpcodeKind::BaseI(opc) => Ok(base_i::bit_32::parse_rs1(self, opc)),
            OpcodeKind::M(opc) => Ok(m_extension::bit_32::parse_rs1(self, opc)),
            OpcodeKind::A(opc) => Ok(a_extension::bit_32::parse_rs1(self, opc)),
            OpcodeKind::Zifencei(opc) => Ok(zifencei_extension::bit_32::parse_rs1(self, opc)),
            OpcodeKind::Zicsr(opc) => Ok(zicsr_extension::bit_32::parse_rs1(self, opc)),
            OpcodeKind::Zicfiss(opc) => Ok(zicfiss_extension::bit_32::parse_rs1(self, opc)),
            OpcodeKind::Zicntr(opc) => Ok(zicntr_extension::bit_32::parse_rs1(self, opc)),
            OpcodeKind::Zicboz(opc) => Ok(zicboz_extension::bit_32::parse_rs1(self, opc)),
            OpcodeKind::Priv(opc) => Ok(priv_extension::bit_32::parse_rs1(self, opc)),
            OpcodeKind::C(_) => Err(DecodingError::Not32BitInst),
        }
    }

    fn parse_rs2(self, opkind: &OpcodeKind) -> Result<Option<usize>, DecodingError> {
        match opkind {
            OpcodeKind::BaseI(opc) => Ok(base_i::bit_32::parse_rs2(self, opc)),
            OpcodeKind::M(opc) => Ok(m_extension::bit_32::parse_rs2(self, opc)),
            OpcodeKind::A(opc) => Ok(a_extension::bit_32::parse_rs2(self, opc)),
            OpcodeKind::Zifencei(opc) => Ok(zifencei_extension::bit_32::parse_rs2(self, opc)),
            OpcodeKind::Zicsr(opc) => Ok(zicsr_extension::bit_32::parse_rs2(self, opc)),
            OpcodeKind::Zicfiss(opc) => Ok(zicfiss_extension::bit_32::parse_rs2(self, opc)),
            OpcodeKind::Zicntr(opc) => Ok(zicntr_extension::bit_32::parse_rs2(self, opc)),
            OpcodeKind::Zicboz(opc) => Ok(zicboz_extension::bit_32::parse_rs2(self, opc)),
            OpcodeKind::Priv(opc) => Ok(priv_extension::bit_32::parse_rs2(self, opc)),
            OpcodeKind::C(_) => Err(DecodingError::Not32BitInst),
        }
    }

    fn parse_imm(self, opkind: &OpcodeKind, isa: Isa) -> Result<Option<i32>, DecodingError> {
        match opkind {
            OpcodeKind::BaseI(opc) => Ok(base_i::bit_32::parse_imm(self, opc, isa)),
            OpcodeKind::M(opc) => Ok(m_extension::bit_32::parse_imm(self, opc)),
            OpcodeKind::A(opc) => Ok(a_extension::bit_32::parse_imm(self, opc)),
            OpcodeKind::Zifencei(opc) => Ok(zifencei_extension::bit_32::parse_imm(self, opc)),
            OpcodeKind::Zicsr(opc) => Ok(zicsr_extension::bit_32::parse_imm(self, opc)),
            OpcodeKind::Zicfiss(opc) => Ok(zicfiss_extension::bit_32::parse_imm(self, opc)),
            OpcodeKind::Zicntr(opc) => Ok(zicntr_extension::bit_32::parse_imm(self, opc)),
            OpcodeKind::Zicboz(opc) => Ok(zicboz_extension::bit_32::parse_imm(self, opc)),
            OpcodeKind::Priv(opc) => Ok(priv_extension::bit_32::parse_imm(self, opc)),
            OpcodeKind::C(_) => Err(DecodingError::Not32BitInst),
        }
    }
}

impl DecodeUtil for u32 {
    fn slice(self, end: u32, start: u32) -> Self {
        assert!(end >= start);
        (self >> start) & (2_u32.pow(end - start + 1) - 1)
    }

    fn parse_extension(self) -> Result<Extensions, DecodingError> {
        let opmap: u8 = u8::try_from(self.slice(6, 0)).unwrap();
        let funct3: u8 = u8::try_from(self.slice(14, 12)).unwrap();
        let funct5: u8 = u8::try_from(self.slice(31, 27)).unwrap();
        let funct7: u8 = u8::try_from(self.slice(31, 25)).unwrap();
        let csr: u16 = u16::try_from(self.slice(31, 20)).unwrap();

        match opmap {
            0b000_1111 => match funct3 {
                0b000 => Ok(Extensions::Zifencei),
                0b010 => Ok(Extensions::Zicboz),
                _ => Err(DecodingError::UnknownExtension),
            },
            0b010_1111 => match funct5 {
                0b00000 | 0b00001 | 0b00010 | 0b00011 | 0b00100 | 0b01000 | 0b01100 | 0b10000
                | 0b10100 | 0b11000 | 0b11100 => Ok(Extensions::A),
                0b01001 => Ok(Extensions::Zicfiss),
                _ => Err(DecodingError::UnknownExtension),
            },
            0b011_0011 => match funct7 {
                0b000_0001 => Ok(Extensions::M),
                _ => Ok(Extensions::BaseI),
            },
            0b011_1011 => match funct7 {
                0b000_0000 | 0b010_0000 => Ok(Extensions::BaseI),
                0b000_0001 => Ok(Extensions::M),
                _ => Err(DecodingError::UnknownExtension),
            },
            0b111_0011 => match funct3 {
                0b000 => match funct7 {
                    0b000_0000 => Ok(Extensions::BaseI),
                    _ => Ok(Extensions::Priv),
                },
                0b010 => match csr {
                    // csrrs rd, (cycle|time|instret), zero
                    0xc00..=0xc02 | 0xc80..=0xc82 => Ok(Extensions::Zicntr),
                    _ => Ok(Extensions::Zicsr),
                },
                0b100 => Ok(Extensions::Zicfiss),
                _ => Ok(Extensions::Zicsr),
            },
            _ => Ok(Extensions::BaseI),
        }
    }

    fn set(self, mask: &[u32]) -> u32 {
        let mut inst: u32 = 0;
        for (i, m) in mask.iter().rev().enumerate() {
            inst |= ((self >> i) & 0x1) << m;
        }

        inst
    }
}

#[cfg(test)]
pub fn test_32(
    isa: Isa,
    location: &std::panic::Location,
    inst_32: u32,
    op: OpcodeKind,
    rd: Option<usize>,
    rs1: Option<usize>,
    rs2: Option<usize>,
    imm: Option<i32>,
) {
    let op_32 = inst_32.parse_opcode(isa).unwrap_or_else(|e| {
        panic!(
            "{e:?}: failed to decode {inst_32:032b} ({}: line {})",
            location.file(),
            location.line()
        );
    });
    assert_eq!(
        op_32,
        op,
        "Opecode does not match: {op_32} != {op} ({}: line {})",
        location.file(),
        location.line()
    );
    assert_eq!(
        inst_32.parse_rd(&op_32).unwrap_or_else(|e| {
            panic!(
                "{e:?}: failed to parse rd in {op}({inst_32:032b}) ({}: line {})",
                location.file(),
                location.line()
            );
        }),
        rd,
        "rd does not match: {:x?} != {rd:x?} ({}: line {})",
        inst_32.parse_rd(&op_32),
        location.file(),
        location.line()
    );
    assert_eq!(
        inst_32.parse_rs1(&op_32).unwrap_or_else(|e| {
            panic!(
                "{e:?}: failed to parse rs1 in {op}({inst_32:032b}) ({}: line {})",
                location.file(),
                location.line()
            );
        }),
        rs1,
        "rs1 does not match: {:x?} != {rs1:x?} ({}: line {})",
        inst_32.parse_rs1(&op_32),
        location.file(),
        location.line()
    );
    assert_eq!(
        inst_32.parse_rs2(&op_32).unwrap_or_else(|e| {
            panic!(
                "{e:?}: failed to parse rs2 in {op}({inst_32:032b}) ({}: line {})",
                location.file(),
                location.line()
            );
        }),
        rs2,
        "rs2 does not match: {:x?} != {rs2:x?} ({}: line {})",
        inst_32.parse_rs2(&op_32),
        location.file(),
        location.line()
    );
    assert_eq!(
        inst_32.parse_imm(&op_32, isa).unwrap_or_else(|e| {
            panic!(
                "{e:?}: failed to parse imm in {op}({inst_32:032b}) ({}: line {})",
                location.file(),
                location.line()
            );
        }),
        imm,
        "imm does not match: {:x?} != {imm:x?} ({}: line {})",
        inst_32.parse_imm(&op_32, isa),
        location.file(),
        location.line()
    );
}

#[cfg(test)]
#[track_caller]
#[allow(dead_code)]
pub fn test_32_in_rv32(
    inst_32: u32,
    op: OpcodeKind,
    rd: Option<usize>,
    rs1: Option<usize>,
    rs2: Option<usize>,
    imm: Option<i32>,
) {
    let location = std::panic::Location::caller();
    test_32(Isa::Rv32, location, inst_32, op, rd, rs1, rs2, imm);
}

#[cfg(test)]
#[track_caller]
pub fn test_32_in_rv64(
    inst_32: u32,
    op: OpcodeKind,
    rd: Option<usize>,
    rs1: Option<usize>,
    rs2: Option<usize>,
    imm: Option<i32>,
) {
    let location = std::panic::Location::caller();
    test_32(Isa::Rv64, location, inst_32, op, rd, rs1, rs2, imm);
}