Skip to main content

ab_riscv_primitives/instructions/rv64/zce/
zcmp.rs

1//! RV64 Zcmp extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::hart::HartConfig;
7use crate::instructions::rv32::zce::zcmp::{ZcmpRegister, ZcmpUrlist};
8use crate::instructions::rv64::c::zca::Rv64ZcaInstruction;
9use crate::instructions::utils::{I24, Shamt};
10use crate::instructions::{ImplementedExtension, Instruction, IsaExtension};
11use crate::registers::general_purpose::Register;
12use ab_riscv_macros::instruction;
13use core::fmt;
14
15/// Zcmp compressed instruction set
16#[instruction(
17    inherit = [Rv64ZcaInstruction, Rv64ZcmpOnlyInstruction],
18)]
19#[derive(Debug, Clone, Copy)]
20#[derive_const(PartialEq, Eq)]
21pub enum Rv64ZcmpInstruction<Hart>
22where
23    Hart: HartConfig, {}
24
25#[instruction]
26const impl<Reg, Hart> Instruction for Rv64ZcmpInstruction<Hart>
27where
28    Reg: [const] Register<Type = u64>,
29    Hart: [const] HartConfig<Reg = Reg>,
30{
31    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[IsaExtension::new("zcmp", 1, 0)];
32
33    const ALIGNMENT: u8 = align_of::<u16>() as u8;
34
35    type Hart = Hart;
36
37    #[inline(always)]
38    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
39    fn try_decode(instruction: u32) -> Option<Self> {
40        None
41    }
42
43    #[inline(always)]
44    fn size(&self) -> u8 {
45        size_of::<u16>() as u8
46    }
47}
48
49#[instruction]
50impl<Reg, Hart> fmt::Display for Rv64ZcmpInstruction<Hart>
51where
52    Reg: Register,
53    Hart: HartConfig<Reg = Reg>,
54{
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match self {}
57    }
58}
59
60/// Instruction that contains isolated Zcmp instructions without inheriting Zca for testing purposes
61#[instruction]
62#[derive(Debug, Clone, Copy)]
63#[derive_const(PartialEq, Eq)]
64#[doc(hidden)]
65pub enum Rv64ZcmpOnlyInstruction<Hart>
66where
67    Hart: HartConfig,
68{
69    /// CM.PUSH - push reg_list, decrement sp by `stack_adj`
70    ///
71    /// `stack_adj = urlist.stack_adj_base() + spimm * 16` from the encoding.
72    CmPush {
73        urlist: ZcmpUrlist<Hart::Reg>,
74        stack_adj: u8,
75    },
76    /// CM.POP - pop reg_list, increment sp by `stack_adj` (no return)
77    CmPop {
78        urlist: ZcmpUrlist<Hart::Reg>,
79        stack_adj: u8,
80    },
81    /// CM.POPRETZ - pop reg_list, set a0=0, increment sp, return
82    CmPopretz {
83        urlist: ZcmpUrlist<Hart::Reg>,
84        stack_adj: u8,
85    },
86    /// CM.POPRET - pop reg_list, increment sp, return
87    CmPopret {
88        urlist: ZcmpUrlist<Hart::Reg>,
89        stack_adj: u8,
90    },
91    /// CM.MVA01S - a0 = r1s', a1 = r2s'.
92    ///
93    /// The fields are called both r1s/r2s and rs1/rs2 in the spec, rs1/rs2 is used here for
94    /// consistency with other instructions.
95    CmMva01s { rs1: Hart::Reg, rs2: Hart::Reg },
96    /// CM.MVSA01 - r1s' = a0, r2s' = a1  (r1s' != r2s').
97    ///
98    /// The fields are called both r1s/r2s and rs1/rs2 in the spec, rs1/rs2 is used here for
99    /// consistency with other instructions.
100    CmMvsa01 { rs1: Hart::Reg, rs2: Hart::Reg },
101}
102
103#[instruction]
104const impl<Reg, Hart> Instruction for Rv64ZcmpOnlyInstruction<Hart>
105where
106    Reg: [const] ZcmpRegister<Type = u64>,
107    Hart: [const] HartConfig<Reg = Reg>,
108{
109    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[];
110
111    const ALIGNMENT: u8 = align_of::<u16>() as u8;
112
113    type Hart = Hart;
114
115    #[inline(always)]
116    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
117    fn try_decode(instruction: u32) -> Option<Self> {
118        /// Map the Zcmp 3-bit "s-register" field to an absolute register number.
119        /// 000->x8(s0), 001->x9(s1), 010->x18(s2)..111->x23(s7)
120        #[inline(always)]
121        const fn sreg_bits(field: u8) -> u8 {
122            match field {
123                0 => 8,
124                1 => 9,
125                f => f + 16,
126            }
127        }
128
129        let inst = instruction as u16;
130        let quadrant = inst & 0b11;
131        let funct3 = ((inst >> 13) & 0b111) as u8;
132
133        // All Zcmp instructions: Q10, funct3=101
134        if quadrant != 0b10 || funct3 != 0b101 {
135            None?;
136        }
137
138        let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
139
140        match funct2_12_11 {
141            // CM.PUSH / CM.POP / CM.POPRETZ / CM.POPRET
142            0b11 => {
143                let op_sel = ((inst >> 9) & 0b11) as u8;
144                let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
145                let spimm = ((inst >> 2) & 0b11) as u8;
146                let stack_adj = urlist.stack_adj_base() + spimm * 16;
147                match op_sel {
148                    0b00 => Some(Self::CmPush { urlist, stack_adj }),
149                    0b01 => Some(Self::CmPop { urlist, stack_adj }),
150                    0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
151                    0b11 => Some(Self::CmPopret { urlist, stack_adj }),
152                    _ => None,
153                }
154            }
155            // CM.MVA01S / CM.MVSA01: require bit 10 = 1 (full funct6 = 101_011)
156            0b01 => {
157                if (inst >> 10) & 1 != 1 {
158                    None?;
159                }
160
161                let r1s_bits = ((inst >> 7) & 0b111) as u8;
162                let funct2 = ((inst >> 5) & 0b11) as u8;
163                let r2s_bits = ((inst >> 2) & 0b111) as u8;
164
165                // Reg::from_bits returns None for registers inaccessible in the current ISA
166                // variant. Under RVE this covers field > 1 (i.e. r1sc/r2sc > 1 in the spec
167                // pseudocode), which maps to x18-x23 - registers that do not exist in the E
168                // extension.
169                let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
170                let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
171
172                // funct2[6:5]: 0b11 -> CM.MVA01S, 0b01 -> CM.MVSA01, others reserved
173                match funct2 {
174                    0b11 => Some(Self::CmMva01s { rs1: r1s, rs2: r2s }),
175                    0b01 => {
176                        // CM.MVSA01 requires r1s' != r2s'
177                        if r1s_bits == r2s_bits {
178                            None?;
179                        }
180                        Some(Self::CmMvsa01 { rs1: r1s, rs2: r2s })
181                    }
182                    _ => None,
183                }
184            }
185            // funct2_12_11 values 0b00 and 0b10 are not defined by Zcmp
186            _ => None,
187        }
188    }
189
190    #[inline(always)]
191    fn size(&self) -> u8 {
192        size_of::<u16>() as u8
193    }
194}
195
196#[instruction]
197impl<Reg, Hart> fmt::Display for Rv64ZcmpOnlyInstruction<Hart>
198where
199    Reg: Register,
200    Hart: HartConfig<Reg = Reg>,
201{
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        match self {
204            Self::CmPush { urlist, stack_adj } => {
205                write!(f, "cm.push {urlist}, -{stack_adj}")
206            }
207            Self::CmPop { urlist, stack_adj } => {
208                write!(f, "cm.pop {urlist}, {stack_adj}")
209            }
210            Self::CmPopretz { urlist, stack_adj } => {
211                write!(f, "cm.popretz {urlist}, {stack_adj}")
212            }
213            Self::CmPopret { urlist, stack_adj } => {
214                write!(f, "cm.popret {urlist}, {stack_adj}")
215            }
216            Self::CmMva01s { rs1, rs2 } => write!(f, "cm.mva01s {rs1}, {rs2}"),
217            Self::CmMvsa01 { rs1, rs2 } => write!(f, "cm.mvsa01 {rs1}, {rs2}"),
218        }
219    }
220}