Skip to content

Commit 70efc2a

Browse files
manchangfengxuPlucky923
authored andcommitted
feat: add XTheadCondMov RISC-V extension and enable it for RV32GC
- Implement XTheadCondMovExtension to decode th.mveqz and th.mvnez as 32-bit R-type custom-0 instructions - Register the extension in the RISC-V extensions module and add an XTHEADCONDMOV bit in extension_masks - Enable the XTHEADCONDMOV bit in RiscVDecoder::rv32gc so RV32 decoders can recognize T-Head conditional moves - Add unit tests for XTheadCondMov decoding and ensure the existing RISC-V test suite still passes Signed-off-by: manchangfengxu <manchangfengxu@openatom.club>
1 parent 7ff8b47 commit 70efc2a

File tree

3 files changed

+208
-4
lines changed

3 files changed

+208
-4
lines changed

robustone-core/src/riscv/decoder.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ impl RiscVDecoder {
4242
| extension_masks::M
4343
| extension_masks::A
4444
| extension_masks::F
45-
| extension_masks::C,
45+
| extension_masks::C
46+
| extension_masks::XTHEADCONDMOV,
4647
)
4748
}
4849

robustone-core/src/riscv/extensions/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,22 @@ pub trait InstructionExtension: Sync {
7070

7171
// Standard RISC-V extension modules
7272
pub mod rva; // RVA - Atomic Instructions
73-
pub mod rvc;
73+
pub mod rvc; // RVC - Compressed Instructions
7474
pub mod rvd; // RVD - Double-Precision Floating-Point
7575
pub mod rvf; // RVF - Single-Precision Floating-Point
7676
pub mod rvi; // RV32I/RV64I - Base Integer Instruction Set
77-
pub mod rvm; // RVM - Multiply and Divide Instructions // RVC - Compressed Instructions
77+
pub mod rvm; // RVM - Multiply and Divide Instructions
78+
79+
// XuanTie vendor extension modules
80+
pub mod xtheadcondmov; // XTheadCondMov - Conditional Move Instructions
7881

7982
use rva::RvaExtension;
8083
use rvc::RvcExtension;
8184
use rvd::RvdExtension;
8285
use rvf::RvfExtension;
8386
use rvi::RviExtension;
8487
use rvm::RvmExtension;
88+
use xtheadcondmov::XTheadCondMovExtension;
8589

8690
/// Create all available standard RISC-V extensions.
8791
pub fn create_extensions() -> Vec<Box<dyn InstructionExtension>> {
@@ -92,15 +96,17 @@ pub fn create_extensions() -> Vec<Box<dyn InstructionExtension>> {
9296
Box::new(RvfExtension::new()),
9397
Box::new(RvdExtension::new()),
9498
Box::new(RvcExtension::new()),
99+
Box::new(XTheadCondMovExtension::new()),
95100
]
96101
}
97102

98-
/// Extension bit masks for standard RISC-V extensions
103+
/// Extension bit masks for standard RISC-V and XuanTie extensions.
99104
pub mod extension_masks {
100105
pub const I: u32 = 0b001; // Base Integer Instruction Set
101106
pub const M: u32 = 0b010; // Multiply and Divide
102107
pub const A: u32 = 0b100; // Atomic Instructions
103108
pub const F: u32 = 0b1000; // Single-Precision Floating-Point
104109
pub const D: u32 = 0b10000; // Double-Precision Floating-Point
105110
pub const C: u32 = 0b100000; // Compressed Instructions
111+
pub const XTHEADCONDMOV: u32 = 0b1000000; // XTheadCondMov - Conditional Move
106112
}
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
//! XTheadCondMov (Conditional Move) Extension
2+
//!
3+
//! This module implements the XuanTie conditional move extension (XTheadCondMov),
4+
//! which provides instructions to conditionally move register values based on
5+
//! whether another register is zero or non-zero.
6+
7+
use super::super::decoder::{RiscVDecodedInstruction, Xlen};
8+
use super::super::shared::{
9+
operands::convenience,
10+
registers::{RegisterManager, RegisterNameProvider},
11+
};
12+
use super::super::types::*;
13+
use super::InstructionExtension;
14+
use crate::error::DisasmError;
15+
use crate::riscv::extensions::extension_masks;
16+
17+
/// XTheadCondMov Conditional Move Extension
18+
pub struct XTheadCondMovExtension {
19+
register_manager: RegisterManager,
20+
}
21+
22+
impl XTheadCondMovExtension {
23+
/// Create a new XTheadCondMov extension instance.
24+
pub fn new() -> Self {
25+
Self {
26+
register_manager: RegisterManager::new(),
27+
}
28+
}
29+
30+
// XTheadCondMov encoding constants
31+
const OPCODE: u32 = 0x0B; // custom-0
32+
const FUNCT3: u8 = 0x1; // Arithmetic
33+
const FUNCT5: u8 = 0x08; // XTheadCondMov identifier
34+
35+
// funct2 values to distinguish instructions
36+
const FUNCT2_MVEQZ: u8 = 0x00; // Move if equal to zero
37+
const FUNCT2_MVNEZ: u8 = 0x01; // Move if not equal to zero
38+
39+
/// Decode an R-type conditional move instruction.
40+
fn decode_r_type(
41+
&self,
42+
mnemonic: &str,
43+
rd: u8,
44+
rs1: u8,
45+
rs2: u8,
46+
) -> Result<RiscVDecodedInstruction, DisasmError> {
47+
Ok(RiscVDecodedInstruction {
48+
mnemonic: mnemonic.to_string(),
49+
operands: format!(
50+
"{}, {}, {}",
51+
self.register_manager.int_register_name(rd),
52+
self.register_manager.int_register_name(rs1),
53+
self.register_manager.int_register_name(rs2)
54+
),
55+
format: RiscVInstructionFormat::R,
56+
size: 4,
57+
operands_detail: vec![
58+
convenience::register(rd, Access::write()),
59+
convenience::register(rs1, Access::read()),
60+
convenience::register(rs2, Access::read()),
61+
],
62+
})
63+
}
64+
}
65+
66+
impl InstructionExtension for XTheadCondMovExtension {
67+
fn name(&self) -> &'static str {
68+
"XTheadCondMov"
69+
}
70+
71+
fn is_enabled(&self, extensions: u32) -> bool {
72+
// XTheadCondMov extension bit
73+
extensions & extension_masks::XTHEADCONDMOV != 0
74+
}
75+
76+
fn try_decode_standard(
77+
&self,
78+
opcode: u32,
79+
funct3: u8,
80+
funct7: u8,
81+
rd: u8,
82+
rs1: u8,
83+
rs2: u8,
84+
_funct12: u32,
85+
_imm_i: i64,
86+
_imm_s: i64,
87+
_imm_b: i64,
88+
_imm_u: i64,
89+
_imm_j: i64,
90+
_xlen: Xlen,
91+
) -> Option<Result<RiscVDecodedInstruction, DisasmError>> {
92+
// Check if opcode matches XTheadCondMov custom-0 space
93+
if opcode != Self::OPCODE {
94+
return None;
95+
}
96+
97+
// Check if funct3 matches Arithmetic encoding
98+
if funct3 != Self::FUNCT3 {
99+
return None;
100+
}
101+
102+
// Extract funct5 (bits[6:2]) and funct2 (bits[1:0]) from funct7
103+
let funct5 = (funct7 >> 2) & 0x1F;
104+
let funct2 = funct7 & 0x3;
105+
106+
// Check if funct5 matches XTheadCondMov identifier
107+
if funct5 != Self::FUNCT5 {
108+
return None;
109+
}
110+
111+
// Decode based on funct2 to distinguish between mveqz and mvnez
112+
match funct2 {
113+
Self::FUNCT2_MVEQZ => Some(self.decode_r_type("th.mveqz", rd, rs1, rs2)),
114+
Self::FUNCT2_MVNEZ => Some(self.decode_r_type("th.mvnez", rd, rs1, rs2)),
115+
_ => Some(Err(DisasmError::DecodingError(
116+
"Invalid XTheadCondMov funct2".to_string(),
117+
))),
118+
}
119+
}
120+
121+
fn try_decode_compressed(
122+
&self,
123+
_instruction: u16,
124+
_opcode: u8,
125+
_funct3: u8,
126+
_xlen: Xlen,
127+
_rd_full: u8,
128+
_rs1_full: u8,
129+
_rs2_full: u8,
130+
_rdp: u8,
131+
_rs1p: u8,
132+
_rs2p: u8,
133+
_nzuimm_ciw: u16,
134+
_uimm_cl: u16,
135+
_uimm_cs: u16,
136+
_imm_ci: i64,
137+
_imm_cj: i64,
138+
_imm_cb: i64,
139+
_uimm_css: u16,
140+
_uimm_clsp: u16,
141+
_uimm_fldsp: u16,
142+
) -> Option<Result<RiscVDecodedInstruction, DisasmError>> {
143+
// XTheadCondMov extension does not provide compressed instruction variants
144+
None
145+
}
146+
}
147+
148+
impl Default for XTheadCondMovExtension {
149+
fn default() -> Self {
150+
Self::new()
151+
}
152+
}
153+
154+
#[cfg(test)]
155+
mod tests {
156+
use super::*;
157+
158+
#[test]
159+
fn test_mveqz_decoding() {
160+
let ext = XTheadCondMovExtension::new();
161+
162+
// th.mveqz ra, sp, gp (x1, x2, x3)
163+
// opcode=0x0B, funct3=0x1, funct7=(0x08<<2)|0x00=0x20, rd=1, rs1=2, rs2=3
164+
let result = ext.try_decode_standard(0x0B, 0x1, 0x20, 1, 2, 3, 0, 0, 0, 0, 0, 0, Xlen::X32);
165+
166+
assert!(result.is_some());
167+
let instr = result.unwrap().unwrap();
168+
assert_eq!(instr.mnemonic, "th.mveqz");
169+
assert_eq!(instr.operands, "ra, sp, gp");
170+
assert_eq!(instr.size, 4);
171+
}
172+
173+
#[test]
174+
fn test_mvnez_decoding() {
175+
let ext = XTheadCondMovExtension::new();
176+
177+
// th.mvnez x1, x2, x3
178+
// opcode=0x0B, funct3=0x1, funct7=(0x08<<2)|0x01=0x21, rd=1, rs1=2, rs2=3
179+
let result = ext.try_decode_standard(0x0B, 0x1, 0x21, 1, 2, 3, 0, 0, 0, 0, 0, 0, Xlen::X32);
180+
181+
assert!(result.is_some());
182+
let instr = result.unwrap().unwrap();
183+
assert_eq!(instr.mnemonic, "th.mvnez");
184+
assert_eq!(instr.operands, "ra, sp, gp");
185+
assert_eq!(instr.size, 4);
186+
}
187+
188+
#[test]
189+
fn test_non_matching_opcode() {
190+
let ext = XTheadCondMovExtension::new();
191+
192+
// Different opcode should not match
193+
let result = ext.try_decode_standard(0x33, 0x1, 0x20, 1, 2, 3, 0, 0, 0, 0, 0, 0, Xlen::X32);
194+
195+
assert!(result.is_none());
196+
}
197+
}

0 commit comments

Comments
 (0)