From 58181a4cca81e454d4f5ed6e09924816006af4bb Mon Sep 17 00:00:00 2001 From: Alingof Date: Mon, 12 Aug 2024 23:56:57 +0900 Subject: [PATCH 01/14] [refactor] add missing doc comment --- src/instruction.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/instruction.rs b/src/instruction.rs index a74abcf..2888093 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -219,6 +219,7 @@ impl Display for Instruction { } } +/// Convert register number to string. fn reg2str(rd_value: usize) -> &'static str { match rd_value { 0 => "zero", From cef76bee1b491368d93d41c92d00dfb8cb120844 Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 12:21:17 +0900 Subject: [PATCH 02/14] [add] Implement `PartialEq` trait to some enum and structs --- src/decode.rs | 2 +- src/instruction.rs | 6 +++--- src/instruction/a_extension.rs | 2 +- src/instruction/base_i.rs | 2 +- src/instruction/c_extension.rs | 2 +- src/instruction/m_extension.rs | 2 +- src/instruction/priv_extension.rs | 2 +- src/instruction/zicsr_extension.rs | 2 +- src/lib.rs | 27 +++++++++++++++++++++++++++ 9 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/decode.rs b/src/decode.rs index 4807fbc..5342691 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -34,7 +34,7 @@ fn only_rv64(opcode: T, isa: Isa) -> Result { /// assert!(matches!(error, DecodingError::OnlyRv64Inst)); /// } /// ``` -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum DecodingError { /// 32bit instructions are expected, but it is compressed instruction. Not16BitInst, diff --git a/src/instruction.rs b/src/instruction.rs index 2888093..d7532f4 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -17,7 +17,7 @@ use priv_extension::PrivOpcode; use zicsr_extension::ZicsrOpcode; /// Instruction -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Instruction { /// Opcode pub opc: OpcodeKind, @@ -277,7 +277,7 @@ pub enum Extensions { /// Instruction format #[allow(non_camel_case_types)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum InstFormat { /// Regular format /// ```ignore @@ -429,7 +429,7 @@ pub trait Opcode { } /// Extension type and Instruction name. -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum OpcodeKind { /// Base Integer Instruction Set BaseI(BaseIOpcode), diff --git a/src/instruction/a_extension.rs b/src/instruction/a_extension.rs index c58f739..02a23fa 100644 --- a/src/instruction/a_extension.rs +++ b/src/instruction/a_extension.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Display, Formatter}; /// Insturctions in A Extension. #[allow(non_camel_case_types)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum AOpcode { LR_W, SC_W, diff --git a/src/instruction/base_i.rs b/src/instruction/base_i.rs index 84b1894..14b8917 100644 --- a/src/instruction/base_i.rs +++ b/src/instruction/base_i.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Display, Formatter}; /// Insturctions in Base-I. #[allow(non_camel_case_types, clippy::module_name_repetitions)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum BaseIOpcode { LUI, AUIPC, diff --git a/src/instruction/c_extension.rs b/src/instruction/c_extension.rs index c943264..12cd1ad 100644 --- a/src/instruction/c_extension.rs +++ b/src/instruction/c_extension.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Display, Formatter}; /// Insturctions in C Extension. #[allow(non_camel_case_types)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum COpcode { ADDI4SPN, LW, diff --git a/src/instruction/m_extension.rs b/src/instruction/m_extension.rs index 090acf8..b1e5443 100644 --- a/src/instruction/m_extension.rs +++ b/src/instruction/m_extension.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Display, Formatter}; /// Insturctions in M Extension. #[allow(non_camel_case_types)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum MOpcode { MUL, MULH, diff --git a/src/instruction/priv_extension.rs b/src/instruction/priv_extension.rs index 96e8ede..c2dd234 100644 --- a/src/instruction/priv_extension.rs +++ b/src/instruction/priv_extension.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Display, Formatter}; /// Privileged Insturctions. #[allow(non_camel_case_types)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum PrivOpcode { SRET, MRET, diff --git a/src/instruction/zicsr_extension.rs b/src/instruction/zicsr_extension.rs index e14cc52..6240712 100644 --- a/src/instruction/zicsr_extension.rs +++ b/src/instruction/zicsr_extension.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Display, Formatter}; /// Insturctions in Zicsr Extension. #[allow(non_camel_case_types)] -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum ZicsrOpcode { CSRRW, CSRRS, diff --git a/src/lib.rs b/src/lib.rs index cfe71c8..9771b9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,4 +90,31 @@ mod tests { println!("{inst}"); } } + + #[test] + fn inst_eq_test() { + use crate::decode::Decode; + use crate::instruction::{base_i::BaseIOpcode, InstFormat, Instruction, OpcodeKind}; + use crate::Isa; + + assert_eq!( + 0b1111_1111_1001_1111_1111_0000_0110_1111_u32.decode(Isa::Rv64), + Ok(Instruction { + opc: OpcodeKind::BaseI(BaseIOpcode::JAL), + rd: Some(0), + rs1: None, + rs2: None, + imm: Some(-8), + inst_format: InstFormat::Jformat, + }) + ); + + assert_eq!( + 0b1111_1111_1001_1111_1111_0000_0110_1111_u32 + .decode(Isa::Rv64) + .unwrap() + .opc, + OpcodeKind::BaseI(BaseIOpcode::JAL), + ) + } } From 0de67747ed67292ab19644406b227f05172564d3 Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 13:04:24 +0900 Subject: [PATCH 03/14] [fix] fix example code --- src/lib.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9771b9c..af7a74d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,14 +11,19 @@ //! ``` //! use raki::Isa; //! use raki::decode::Decode; -//! use raki::instruction::Instruction; +//! use raki::instruction::base_i::BaseIOpcode; +//! use raki::instruction::{Instruction, OpcodeKind}; //! -//! let inst: u32 = 0b1110_1110_1100_0010_1000_0010_1001_0011; -//! let inst: Instruction = match inst.decode(Isa::Rv32) { -//! Ok(inst) => inst, -//! Err(e) => panic!("decoding failed due to {e:?}"), -//! }; -//! println!("{inst}"); +//! fn main() { +//! let inst_bytes: u32 = 0b1110_1110_1100_0010_1000_0010_1001_0011; +//! let inst: Instruction = match inst_bytes.decode(Isa::Rv32) { +//! Ok(inst) => inst, +//! Err(e) => panic!("decoding failed due to {e:?}"), +//! }; +//! +//! assert_eq!(inst.opc, OpcodeKind::BaseI(BaseIOpcode::ADDI)); +//! println!("{inst}"); +//! } //! // --output-- //! // addi t0, t0, -276 //! ``` From ee176be0c1655e0f432c249961af2934f12c9316 Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 13:58:49 +0900 Subject: [PATCH 04/14] [!][update] re-export struct and enum in lib.rs --- src/lib.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index af7a74d..92cb612 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,10 +9,7 @@ //! # Usage //! Call the `decode` as u16/u32 method. //! ``` -//! use raki::Isa; -//! use raki::decode::Decode; -//! use raki::instruction::base_i::BaseIOpcode; -//! use raki::instruction::{Instruction, OpcodeKind}; +//! use raki::{BaseIOpcode, Decode, Instruction, Isa, OpcodeKind}; //! //! fn main() { //! let inst_bytes: u32 = 0b1110_1110_1100_0010_1000_0010_1001_0011; @@ -28,8 +25,15 @@ //! // addi t0, t0, -276 //! ``` -pub mod decode; -pub mod instruction; +mod decode; +mod instruction; + +// re-export +pub use crate::decode::{Decode, DecodingError}; +pub use crate::instruction::{ + a_extension::AOpcode, base_i::BaseIOpcode, c_extension::COpcode, priv_extension::PrivOpcode, + zicsr_extension::ZicsrOpcode, InstFormat, Instruction, OpcodeKind, +}; /// Target isa. #[derive(Copy, Clone)] From 80ca55e5cdbccd05a641597eec50e20f049b4926 Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 14:05:22 +0900 Subject: [PATCH 05/14] [wip][update] move `Extensions` to root and make it private --- src/decode.rs | 4 ++-- src/decode/inst_16.rs | 4 ++-- src/decode/inst_32.rs | 4 ++-- src/instruction.rs | 17 ----------------- src/lib.rs | 17 +++++++++++++++++ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/decode.rs b/src/decode.rs index 5342691..f02b888 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -3,8 +3,8 @@ mod inst_16; mod inst_32; -use crate::instruction::{Extensions, Instruction, Opcode, OpcodeKind}; -use crate::Isa; +use crate::instruction::{Instruction, Opcode, OpcodeKind}; +use crate::{Extensions, Isa}; /// Return Err if given opcode is only available on Rv64. fn only_rv64(opcode: T, isa: Isa) -> Result { diff --git a/src/decode/inst_16.rs b/src/decode/inst_16.rs index 93e879a..2814984 100644 --- a/src/decode/inst_16.rs +++ b/src/decode/inst_16.rs @@ -2,8 +2,8 @@ mod c_extension; use super::{Decode, DecodeUtil, DecodingError}; -use crate::instruction::{Extensions, InstFormat, Instruction, OpcodeKind}; -use crate::Isa; +use crate::instruction::{InstFormat, Instruction, OpcodeKind}; +use crate::{Extensions, Isa}; impl Decode for u16 { fn decode(&self, isa: Isa) -> Result { diff --git a/src/decode/inst_32.rs b/src/decode/inst_32.rs index ac4dd75..1869b4b 100644 --- a/src/decode/inst_32.rs +++ b/src/decode/inst_32.rs @@ -5,8 +5,8 @@ mod priv_extension; mod zicsr_extension; use super::{Decode, DecodeUtil, DecodingError}; -use crate::instruction::{Extensions, InstFormat, Instruction, OpcodeKind}; -use crate::Isa; +use crate::instruction::{InstFormat, Instruction, OpcodeKind}; +use crate::{Extensions, Isa}; #[allow(non_snake_case)] impl Decode for u32 { diff --git a/src/instruction.rs b/src/instruction.rs index d7532f4..d5bacfa 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -258,23 +258,6 @@ fn reg2str(rd_value: usize) -> &'static str { } } -/// RISC-V extensions -#[derive(Debug)] -pub enum Extensions { - /// Base Integer Instruction Set - BaseI, - /// Integer Multiplication and Division - M, - /// Atomic Instructions - A, - /// Compressed Instructions - C, - /// Control and Status Register Instructions - Zicsr, - /// Privileged Instructions - Priv, -} - /// Instruction format #[allow(non_camel_case_types)] #[derive(Debug, PartialEq)] diff --git a/src/lib.rs b/src/lib.rs index 92cb612..9d5cd7d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,23 @@ pub enum Isa { Rv64, } +/// RISC-V extensions +#[derive(Debug)] +enum Extensions { + /// Base Integer Instruction Set + BaseI, + /// Integer Multiplication and Division + M, + /// Atomic Instructions + A, + /// Compressed Instructions + C, + /// Control and Status Register Instructions + Zicsr, + /// Privileged Instructions + Priv, +} + #[cfg(test)] mod tests { #[test] From 043850d4ac8f725ba09724a10d08b515eacf8b8e Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 14:08:32 +0900 Subject: [PATCH 06/14] [update] move `parse_extension` to `DecodeUtil` --- src/decode.rs | 18 +++++++++++---- src/decode/inst_16.rs | 8 +++---- src/decode/inst_32.rs | 54 +++++++++++++++++++++---------------------- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/decode.rs b/src/decode.rs index f02b888..0780168 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -59,6 +59,7 @@ pub enum DecodingError { } /// A trait to decode an instruction from u16/u32. +/// This trait provides public api. /// /// # Usage /// `decode` method is implemented for u16/u32. @@ -76,31 +77,31 @@ pub trait Decode { /// # Errors /// It will throws an error if target bytes is invalid for decoding. fn decode(&self, isa: Isa) -> Result; - /// Parse extension from a u16/u32 value. - /// - /// # Errors - /// It will throws `UnknownExtension` if the extension is unsupported. - fn parse_extension(self) -> Result; + /// Parse opcode. /// /// # Errors /// It will throws an error if opcode is unknown. fn parse_opcode(self, isa: Isa) -> Result; + /// Parse destination register. /// /// # Errors /// It will throws an error if rd is invalid. fn parse_rd(self, opkind: &OpcodeKind) -> Result, DecodingError>; + /// Parse source register 1. /// /// # Errors /// It will throws an error if rs1 is invalid. fn parse_rs1(self, opkind: &OpcodeKind) -> Result, DecodingError>; + /// Parse source register 2. /// /// # Errors /// It will throws an error if rs2 is invalid. fn parse_rs2(self, opkind: &OpcodeKind) -> Result, DecodingError>; + /// Parse immediate. /// /// # Errors @@ -109,6 +110,7 @@ pub trait Decode { } /// A trait to help decoding. +/// This trait provides private api. trait DecodeUtil { /// Obtains bits in a specified range. /// The range is `[end, start]`. @@ -134,6 +136,12 @@ trait DecodeUtil { /// * `mask` - It contain the bit order. fn set(self, mask: &[u32]) -> u32; + /// Parse extension from a u16/u32 value. + /// + /// # Errors + /// It will throws `UnknownExtension` if the extension is unsupported. + fn parse_extension(self) -> Result; + /// Convert i32 to a sign-extended any size number. /// # Arguments /// * `imm32` - The value to be converted. diff --git a/src/decode/inst_16.rs b/src/decode/inst_16.rs index 2814984..a10fd09 100644 --- a/src/decode/inst_16.rs +++ b/src/decode/inst_16.rs @@ -28,10 +28,6 @@ impl Decode for u16 { }) } - fn parse_extension(self) -> Result { - Ok(Extensions::C) - } - fn parse_opcode(self, isa: Isa) -> Result { let extension = self.parse_extension(); @@ -75,6 +71,10 @@ impl DecodeUtil for u16 { (self >> start) & (2_u16.pow(end - start + 1) - 1) } + fn parse_extension(self) -> Result { + Ok(Extensions::C) + } + fn set(self, mask: &[u32]) -> u32 { let mut inst: u32 = 0; for (i, m) in mask.iter().rev().enumerate() { diff --git a/src/decode/inst_32.rs b/src/decode/inst_32.rs index 1869b4b..050334e 100644 --- a/src/decode/inst_32.rs +++ b/src/decode/inst_32.rs @@ -28,33 +28,6 @@ impl Decode for u32 { }) } - fn parse_extension(self) -> Result { - let opmap: u8 = u8::try_from(self.slice(6, 0)).unwrap(); - let funct3: u8 = u8::try_from(self.slice(14, 12)).unwrap(); - let funct7: u8 = u8::try_from(self.slice(31, 25)).unwrap(); - - match opmap { - 0b010_1111 => Ok(Extensions::A), - 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), - }, - _ => Ok(Extensions::Zicsr), - }, - _ => Ok(Extensions::BaseI), - } - } - fn parse_opcode(self, isa: Isa) -> Result { let extension = self.parse_extension(); @@ -119,6 +92,33 @@ impl DecodeUtil for u32 { (self >> start) & (2_u32.pow(end - start + 1) - 1) } + fn parse_extension(self) -> Result { + let opmap: u8 = u8::try_from(self.slice(6, 0)).unwrap(); + let funct3: u8 = u8::try_from(self.slice(14, 12)).unwrap(); + let funct7: u8 = u8::try_from(self.slice(31, 25)).unwrap(); + + match opmap { + 0b010_1111 => Ok(Extensions::A), + 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), + }, + _ => 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() { From 62845e15d6e13a262e394ca77e561b70fc8858fa Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 14:26:32 +0900 Subject: [PATCH 07/14] [refactor] fix clippy warnings --- src/instruction/m_extension.rs | 2 +- src/instruction/priv_extension.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/instruction/m_extension.rs b/src/instruction/m_extension.rs index b1e5443..20ece8b 100644 --- a/src/instruction/m_extension.rs +++ b/src/instruction/m_extension.rs @@ -4,7 +4,7 @@ use super::{InstFormat, Opcode}; use core::fmt::{self, Display, Formatter}; /// Insturctions in M Extension. -#[allow(non_camel_case_types)] +#[allow(non_camel_case_types, clippy::upper_case_acronyms)] #[derive(Debug, PartialEq)] pub enum MOpcode { MUL, diff --git a/src/instruction/priv_extension.rs b/src/instruction/priv_extension.rs index c2dd234..37ac94c 100644 --- a/src/instruction/priv_extension.rs +++ b/src/instruction/priv_extension.rs @@ -7,8 +7,8 @@ use core::fmt::{self, Display, Formatter}; #[allow(non_camel_case_types)] #[derive(Debug, PartialEq)] pub enum PrivOpcode { - SRET, MRET, + SRET, WFI, SFENCE_VMA, } diff --git a/src/lib.rs b/src/lib.rs index 9d5cd7d..17e11e0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ //! ``` //! use raki::{BaseIOpcode, Decode, Instruction, Isa, OpcodeKind}; //! -//! fn main() { +//! fn test() { //! let inst_bytes: u32 = 0b1110_1110_1100_0010_1000_0010_1001_0011; //! let inst: Instruction = match inst_bytes.decode(Isa::Rv32) { //! Ok(inst) => inst, From 8186309bbed095c2bc53ad9df3552d5195a36138 Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 14:52:12 +0900 Subject: [PATCH 08/14] [!][add] support zifenci extension (and remove fence instruction from base_i) --- src/decode/inst_32.rs | 19 ++++++++++- src/decode/inst_32/base_i.rs | 1 - src/decode/inst_32/zifencei_extension.rs | 43 ++++++++++++++++++++++++ src/instruction.rs | 6 ++++ src/instruction/base_i.rs | 6 +--- src/instruction/zifencei_extension.rs | 27 +++++++++++++++ src/lib.rs | 7 ++-- 7 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 src/decode/inst_32/zifencei_extension.rs create mode 100644 src/instruction/zifencei_extension.rs diff --git a/src/decode/inst_32.rs b/src/decode/inst_32.rs index 050334e..bb86fac 100644 --- a/src/decode/inst_32.rs +++ b/src/decode/inst_32.rs @@ -3,6 +3,7 @@ mod base_i; mod m_extension; mod priv_extension; mod zicsr_extension; +mod zifencei_extension; use super::{Decode, DecodeUtil, DecodingError}; use crate::instruction::{InstFormat, Instruction, OpcodeKind}; @@ -35,6 +36,9 @@ impl Decode for u32 { Ok(Extensions::BaseI) => Ok(OpcodeKind::BaseI(base_i::parse_opcode(self, isa)?)), Ok(Extensions::M) => Ok(OpcodeKind::M(m_extension::parse_opcode(self, isa)?)), Ok(Extensions::A) => Ok(OpcodeKind::A(a_extension::parse_opcode(self, isa)?)), + Ok(Extensions::Zifencei) => Ok(OpcodeKind::Zifencei(zifencei_extension::parse_opcode( + self, + )?)), Ok(Extensions::Zicsr) => Ok(OpcodeKind::Zicsr(zicsr_extension::parse_opcode(self)?)), Ok(Extensions::Priv) => Ok(OpcodeKind::Priv(priv_extension::parse_opcode(self)?)), Ok(Extensions::C) => Err(DecodingError::Not32BitInst), @@ -47,6 +51,7 @@ impl Decode for u32 { OpcodeKind::BaseI(opc) => Ok(base_i::parse_rd(self, opc)), OpcodeKind::M(opc) => Ok(m_extension::parse_rd(self, opc)), OpcodeKind::A(opc) => Ok(a_extension::parse_rd(self, opc)), + OpcodeKind::Zifencei(opc) => Ok(zifencei_extension::parse_rd(self, opc)), OpcodeKind::Zicsr(opc) => Ok(zicsr_extension::parse_rd(self, opc)), OpcodeKind::Priv(opc) => Ok(priv_extension::parse_rd(self, opc)), OpcodeKind::C(_) => Err(DecodingError::Not32BitInst), @@ -58,6 +63,7 @@ impl Decode for u32 { OpcodeKind::BaseI(opc) => Ok(base_i::parse_rs1(self, opc)), OpcodeKind::M(opc) => Ok(m_extension::parse_rs1(self, opc)), OpcodeKind::A(opc) => Ok(a_extension::parse_rs1(self, opc)), + OpcodeKind::Zifencei(opc) => Ok(zifencei_extension::parse_rs1(self, opc)), OpcodeKind::Zicsr(opc) => Ok(zicsr_extension::parse_rs1(self, opc)), OpcodeKind::Priv(opc) => Ok(priv_extension::parse_rs1(self, opc)), OpcodeKind::C(_) => Err(DecodingError::Not32BitInst), @@ -69,6 +75,7 @@ impl Decode for u32 { OpcodeKind::BaseI(opc) => Ok(base_i::parse_rs2(self, opc)), OpcodeKind::M(opc) => Ok(m_extension::parse_rs2(self, opc)), OpcodeKind::A(opc) => Ok(a_extension::parse_rs2(self, opc)), + OpcodeKind::Zifencei(opc) => Ok(zifencei_extension::parse_rs2(self, opc)), OpcodeKind::Zicsr(opc) => Ok(zicsr_extension::parse_rs2(self, opc)), OpcodeKind::Priv(opc) => Ok(priv_extension::parse_rs2(self, opc)), OpcodeKind::C(_) => Err(DecodingError::Not32BitInst), @@ -80,6 +87,7 @@ impl Decode for u32 { OpcodeKind::BaseI(opc) => Ok(base_i::parse_imm(self, opc, isa)), OpcodeKind::M(opc) => Ok(m_extension::parse_imm(self, opc)), OpcodeKind::A(opc) => Ok(a_extension::parse_imm(self, opc)), + OpcodeKind::Zifencei(opc) => Ok(zifencei_extension::parse_imm(self, opc)), OpcodeKind::Zicsr(opc) => Ok(zicsr_extension::parse_imm(self, opc)), OpcodeKind::Priv(opc) => Ok(priv_extension::parse_imm(self, opc)), OpcodeKind::C(_) => Err(DecodingError::Not32BitInst), @@ -98,6 +106,7 @@ impl DecodeUtil for u32 { let funct7: u8 = u8::try_from(self.slice(31, 25)).unwrap(); match opmap { + 0b000_1111 => Ok(Extensions::Zifencei), 0b010_1111 => Ok(Extensions::A), 0b011_0011 => match funct7 { 0b000_0001 => Ok(Extensions::M), @@ -136,7 +145,7 @@ mod decode_32 { #[allow(overflowing_literals)] fn decoding_32bit_inst_test() { use super::*; - use crate::instruction::{a_extension::AOpcode, base_i::BaseIOpcode, m_extension::MOpcode}; + use crate::{AOpcode, BaseIOpcode, MOpcode, ZifenceiOpcode}; let test_32 = |inst_32: u32, op: OpcodeKind, @@ -288,5 +297,13 @@ mod decode_32 { Some(8), None, ); + test_32( + 0b0000_0011_0011_0000_0000_0000_0000_1111, + OpcodeKind::Zifencei(ZifenceiOpcode::FENCE), + Some(0), + Some(0), + None, + Some(0x033), + ) } } diff --git a/src/decode/inst_32/base_i.rs b/src/decode/inst_32/base_i.rs index 2f9d93e..6ed30ce 100644 --- a/src/decode/inst_32/base_i.rs +++ b/src/decode/inst_32/base_i.rs @@ -91,7 +91,6 @@ pub fn parse_opcode(inst: u32, isa: Isa) -> Result { 0b111 => Ok(BaseIOpcode::AND), _ => Err(DecodingError::InvalidFunct3), }, - 0b000_1111 => Ok(BaseIOpcode::FENCE), 0b111_0011 => match funct3 { 0b000 => match funct7 { 0b000_0000 => match funct5 { diff --git a/src/decode/inst_32/zifencei_extension.rs b/src/decode/inst_32/zifencei_extension.rs new file mode 100644 index 0000000..71611d7 --- /dev/null +++ b/src/decode/inst_32/zifencei_extension.rs @@ -0,0 +1,43 @@ +use super::super::{DecodeUtil, DecodingError}; +use crate::instruction::zifencei_extension::ZifenceiOpcode; + +pub fn parse_opcode(inst: u32) -> Result { + let opmap: u8 = u8::try_from(inst.slice(6, 0)).unwrap(); + + match opmap { + 0b000_1111 => Ok(ZifenceiOpcode::FENCE), + _ => Err(DecodingError::InvalidOpcode), + } +} + +#[allow(clippy::unnecessary_wraps)] +pub fn parse_rd(inst: u32, opkind: &ZifenceiOpcode) -> Option { + let rd: usize = inst.slice(11, 7) as usize; + + match opkind { + ZifenceiOpcode::FENCE => Some(rd), + } +} + +pub fn parse_rs1(inst: u32, opkind: &ZifenceiOpcode) -> Option { + let rs1: usize = inst.slice(19, 15) as usize; + + match opkind { + ZifenceiOpcode::FENCE => Some(rs1), + } +} + +#[allow(clippy::unnecessary_wraps)] +pub fn parse_rs2(_inst: u32, opkind: &ZifenceiOpcode) -> Option { + match opkind { + ZifenceiOpcode::FENCE => None, + } +} + +#[allow(clippy::cast_possible_wrap)] +pub fn parse_imm(inst: u32, opkind: &ZifenceiOpcode) -> Option { + let fm_pred_succ: u32 = inst.slice(31, 20); + match opkind { + ZifenceiOpcode::FENCE => Some(fm_pred_succ as i32), + } +} diff --git a/src/instruction.rs b/src/instruction.rs index d5bacfa..364c568 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -6,6 +6,7 @@ pub mod c_extension; pub mod m_extension; pub mod priv_extension; pub mod zicsr_extension; +pub mod zifencei_extension; use core::fmt::{self, Display, Formatter}; @@ -15,6 +16,7 @@ use c_extension::COpcode; use m_extension::MOpcode; use priv_extension::PrivOpcode; use zicsr_extension::ZicsrOpcode; +use zifencei_extension::ZifenceiOpcode; /// Instruction #[derive(Debug, PartialEq)] @@ -422,6 +424,8 @@ pub enum OpcodeKind { A(AOpcode), /// Compressed Instructions C(COpcode), + /// Instruction-Fetch Fence, + Zifencei(ZifenceiOpcode), /// Control and Status Register Instructions Zicsr(ZicsrOpcode), /// Privileged Instructions @@ -435,6 +439,7 @@ impl Display for OpcodeKind { Self::M(opc) => write!(f, "{opc}"), Self::A(opc) => write!(f, "{opc}"), Self::C(opc) => write!(f, "{opc}"), + Self::Zifencei(opc) => write!(f, "{opc}"), Self::Zicsr(opc) => write!(f, "{opc}"), Self::Priv(opc) => write!(f, "{opc}"), } @@ -449,6 +454,7 @@ impl OpcodeKind { Self::M(opc) => opc.get_format(), Self::A(opc) => opc.get_format(), Self::C(opc) => opc.get_format(), + Self::Zifencei(opc) => opc.get_format(), Self::Zicsr(opc) => opc.get_format(), Self::Priv(opc) => opc.get_format(), } diff --git a/src/instruction/base_i.rs b/src/instruction/base_i.rs index 14b8917..8b09ad0 100644 --- a/src/instruction/base_i.rs +++ b/src/instruction/base_i.rs @@ -44,7 +44,6 @@ pub enum BaseIOpcode { SRA, OR, AND, - FENCE, ECALL, EBREAK, @@ -103,7 +102,6 @@ impl Display for BaseIOpcode { BaseIOpcode::SRA => write!(f, "sra"), BaseIOpcode::OR => write!(f, "or"), BaseIOpcode::AND => write!(f, "and"), - BaseIOpcode::FENCE => write!(f, "fence"), BaseIOpcode::ECALL => write!(f, "ecall"), BaseIOpcode::EBREAK => write!(f, "ebreak"), BaseIOpcode::LWU => write!(f, "lwu"), @@ -172,9 +170,7 @@ impl Opcode for BaseIOpcode { } BaseIOpcode::JAL => InstFormat::Jformat, BaseIOpcode::LUI | BaseIOpcode::AUIPC => InstFormat::Uformat, - BaseIOpcode::ECALL | BaseIOpcode::FENCE | BaseIOpcode::EBREAK => { - InstFormat::Uncategorized - } + BaseIOpcode::ECALL | BaseIOpcode::EBREAK => InstFormat::Uncategorized, } } } diff --git a/src/instruction/zifencei_extension.rs b/src/instruction/zifencei_extension.rs new file mode 100644 index 0000000..a54be21 --- /dev/null +++ b/src/instruction/zifencei_extension.rs @@ -0,0 +1,27 @@ +//! Zicsr extension Instruction. + +use super::{InstFormat, Opcode}; +use core::fmt::{self, Display, Formatter}; + +/// Insturctions in Zicsr Extension. +#[allow(non_camel_case_types)] +#[derive(Debug, PartialEq)] +pub enum ZifenceiOpcode { + FENCE, +} + +impl Display for ZifenceiOpcode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + ZifenceiOpcode::FENCE => write!(f, "fence.i"), + } + } +} + +impl Opcode for ZifenceiOpcode { + fn get_format(&self) -> InstFormat { + match self { + ZifenceiOpcode::FENCE => InstFormat::Uncategorized, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 17e11e0..f0d1df6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,8 +31,9 @@ mod instruction; // re-export pub use crate::decode::{Decode, DecodingError}; pub use crate::instruction::{ - a_extension::AOpcode, base_i::BaseIOpcode, c_extension::COpcode, priv_extension::PrivOpcode, - zicsr_extension::ZicsrOpcode, InstFormat, Instruction, OpcodeKind, + a_extension::AOpcode, base_i::BaseIOpcode, c_extension::COpcode, m_extension::MOpcode, + priv_extension::PrivOpcode, zicsr_extension::ZicsrOpcode, zifencei_extension::ZifenceiOpcode, + InstFormat, Instruction, OpcodeKind, }; /// Target isa. @@ -55,6 +56,8 @@ enum Extensions { A, /// Compressed Instructions C, + /// Instruction-Fetch Fence + Zifencei, /// Control and Status Register Instructions Zicsr, /// Privileged Instructions From 58e3e557641c453551730bca55575d8f07c096b3 Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 14:54:21 +0900 Subject: [PATCH 09/14] [fix] fix importing in doc comment --- src/decode.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/decode.rs b/src/decode.rs index 0780168..9c2348b 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -18,9 +18,7 @@ fn only_rv64(opcode: T, isa: Isa) -> Result { /// /// # Example /// ``` -/// use raki::Isa; -/// use raki::decode::{Decode, DecodingError}; -/// use raki::instruction::Instruction; +/// use raki::{Isa, Decode, DecodingError, Instruction}; /// /// // try to decode illegal instruction. /// let illegal_inst: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000; @@ -65,8 +63,7 @@ pub enum DecodingError { /// `decode` method is implemented for u16/u32. /// thus, just call `decode` as method of u16/u32. /// ``` -/// use raki::Isa; -/// use raki::decode::Decode; +/// use raki::{Isa, Decode}; /// /// let inst: u32 = 0b1110_1110_1100_0010_1000_0010_1001_0011; /// println!("{:?}", inst.decode(Isa::Rv64)); From 88cf2b01f9fc9a4e4cb601765cd7f9d7697e41e7 Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 14:56:23 +0900 Subject: [PATCH 10/14] [refactor] fix clippy warnings in zifencei module --- src/decode/inst_32/zifencei_extension.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/decode/inst_32/zifencei_extension.rs b/src/decode/inst_32/zifencei_extension.rs index 71611d7..5c74674 100644 --- a/src/decode/inst_32/zifencei_extension.rs +++ b/src/decode/inst_32/zifencei_extension.rs @@ -19,6 +19,7 @@ pub fn parse_rd(inst: u32, opkind: &ZifenceiOpcode) -> Option { } } +#[allow(clippy::unnecessary_wraps)] pub fn parse_rs1(inst: u32, opkind: &ZifenceiOpcode) -> Option { let rs1: usize = inst.slice(19, 15) as usize; @@ -34,7 +35,7 @@ pub fn parse_rs2(_inst: u32, opkind: &ZifenceiOpcode) -> Option { } } -#[allow(clippy::cast_possible_wrap)] +#[allow(clippy::cast_possible_wrap, clippy::unnecessary_wraps)] pub fn parse_imm(inst: u32, opkind: &ZifenceiOpcode) -> Option { let fm_pred_succ: u32 = inst.slice(31, 20); match opkind { From 2c4bb752bf45fa5365a7cdea37bacd859be1ae45 Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 14:57:23 +0900 Subject: [PATCH 11/14] [update] add `Support` section to README.md --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index cc494a3..1472305 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,22 @@ println!("{inst}"); // addi t0, t0, -276 ``` +## Support +- [x] BaseI (RV32I, RV64I) +- [x] M +- [x] A +- [ ] D +- [ ] G +- [ ] Q +- [x] C +- [ ] B +- [ ] P +- [ ] V +- [ ] H +- [x] Zicsr +- [x] Zifencei +- [ ] Priv (Now only supports `mret`, `sret`, `wfi`, `sfence.vma`) + ## License This crate is licensed under MIT. See `LICENSE` for details. From 53040dedd7e859165b2f5685bb1cbd53c7a9393f Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 15:09:21 +0900 Subject: [PATCH 12/14] [fix] fix example in README.md --- README.md | 20 +++++++++++--------- src/instruction/zifencei_extension.rs | 2 +- src/lib.rs | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1472305..5f164df 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,18 @@ RISC-V instruction decoder written in Rust. ## Usage Call the `decode` as u16/u32 method. ```rust -use raki::Isa; -use raki::decode::Decode; -use raki::instruction::Instruction; +use raki::{BaseIOpcode, Decode, Instruction, Isa, OpcodeKind}; -let inst: u32 = 0b1110_1110_1100_0010_1000_0010_1001_0011; -let inst: Instruction = match inst.decode(Isa::Rv32) { - Ok(inst) => inst, - Err(e) => panic!("decoding failed due to {e:?}"), -}; -println!("{inst}"); +fn main() { + let inst_bytes: u32 = 0b1110_1110_1100_0010_1000_0010_1001_0011; + let inst: Instruction = match inst_bytes.decode(Isa::Rv32) { + Ok(inst) => inst, + Err(e) => panic!("decoding failed due to {e:?}"), + }; + + assert_eq!(inst.opc, OpcodeKind::BaseI(BaseIOpcode::ADDI)); + println!("{inst}"); +} // --output-- // addi t0, t0, -276 ``` diff --git a/src/instruction/zifencei_extension.rs b/src/instruction/zifencei_extension.rs index a54be21..e3169b7 100644 --- a/src/instruction/zifencei_extension.rs +++ b/src/instruction/zifencei_extension.rs @@ -3,7 +3,7 @@ use super::{InstFormat, Opcode}; use core::fmt::{self, Display, Formatter}; -/// Insturctions in Zicsr Extension. +/// Insturctions in Zifencei Extension. #[allow(non_camel_case_types)] #[derive(Debug, PartialEq)] pub enum ZifenceiOpcode { diff --git a/src/lib.rs b/src/lib.rs index f0d1df6..e7f8b7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ //! ``` //! use raki::{BaseIOpcode, Decode, Instruction, Isa, OpcodeKind}; //! -//! fn test() { +//! fn example() { //! let inst_bytes: u32 = 0b1110_1110_1100_0010_1000_0010_1001_0011; //! let inst: Instruction = match inst_bytes.decode(Isa::Rv32) { //! Ok(inst) => inst, From 4d8e98c24772658aeb79f5f3b6bb1c5abfe2220e Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 15:09:59 +0900 Subject: [PATCH 13/14] [!][update] update crate version to 1.0.0-beta.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b57f2e1..77d48ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "raki" -version = "0.1.3" +version = "1.0.0-beta.1" edition = "2021" authors = ["Norimasa Takana "] repository = "https://github.com/Alignof/raki" From 28ce666e537f79950f94784ae86991ab7a4d0109 Mon Sep 17 00:00:00 2001 From: Alingof Date: Tue, 13 Aug 2024 15:28:13 +0900 Subject: [PATCH 14/14] [add] add fields for crates.io to README.md --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 77d48ea..eecee70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,10 @@ edition = "2021" authors = ["Norimasa Takana "] repository = "https://github.com/Alignof/raki" keywords = ["risc-v", "decoder"] +categories = ["encoding", "embedded", "hardware-support", "no-std", "parsing"] description = "RISC-V instruction decoder written in Rust." license = "MIT" +readme = "README.md" [lints.clippy] pedantic = "warn"