Skip to main content

ab_riscv_primitives/instructions/rv64/zce/
zcb.rs

1//! RV64 Zcb extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::instructions::Instruction;
7use crate::registers::general_purpose::Register;
8use ab_riscv_macros::instruction;
9use core::fmt;
10
11/// RISC-V RV64 Zcb compressed instruction set.
12///
13/// All register operands are prime-field (x8–x15) registers.
14#[instruction]
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum Rv64ZcbInstruction<Reg> {
17    // Q00 loads / stores
18    /// C.LBU  rd' = zero_extend(mem8\[rs1' + uimm])  uimm ∈ {0,1,2,3}
19    CLbu { rd: Reg, rs1: Reg, uimm: u8 },
20    /// C.LH   rd' = sign_extend(mem16\[rs1' + uimm])  uimm ∈ {0,2}
21    CLh { rd: Reg, rs1: Reg, uimm: u8 },
22    /// C.LHU  rd' = zero_extend(mem16\[rs1' + uimm])  uimm ∈ {0,2}
23    CLhu { rd: Reg, rs1: Reg, uimm: u8 },
24    /// C.SB   mem8\[rs1' + uimm] = rs2'  uimm ∈ {0,1,2,3}
25    CSb { rs1: Reg, rs2: Reg, uimm: u8 },
26    /// C.SH   mem16\[rs1' + uimm] = rs2'  uimm ∈ {0,2}
27    CSh { rs1: Reg, rs2: Reg, uimm: u8 },
28
29    // Q01 unary bit-manipulation
30    /// C.ZEXT.B  rd' = rd' & 0xff
31    CZextB { rd: Reg },
32    /// C.SEXT.B  rd' = sext(rd'\[7:0])  (requires Zbb)
33    CSextB { rd: Reg },
34    /// C.ZEXT.H  rd' = rd' & 0xffff  (requires Zbb)
35    CZextH { rd: Reg },
36    /// C.SEXT.H  rd' = sext(rd'\[15:0])  (requires Zbb)
37    CSextH { rd: Reg },
38    /// C.ZEXT.W  rd' = rd' & 0xffff_ffff  (requires Zba)
39    CZextW { rd: Reg },
40    /// C.NOT  rd' = ~rd'
41    CNot { rd: Reg },
42
43    // Q01 binary
44    /// C.MUL  rd' = (rd' * rs2')\[XLEN-1:0]  (requires M or Zmmul)
45    CMul { rd: Reg, rs2: Reg },
46}
47
48#[instruction]
49impl<Reg> const Instruction for Rv64ZcbInstruction<Reg>
50where
51    Reg: [const] Register<Type = u64>,
52{
53    type Reg = Reg;
54
55    #[inline(always)]
56    fn try_decode(instruction: u32) -> Option<Self> {
57        /// Map a 3-bit "prime" register field to an absolute register number
58        #[inline(always)]
59        const fn prime_reg_bits(bits: u8) -> u8 {
60            bits + 8
61        }
62
63        let inst = instruction as u16;
64        let quadrant = inst & 0b11;
65        let funct3 = ((inst >> 13) & 0b111) as u8;
66
67        match quadrant {
68            // Q00 funct3=100: C.LBU / C.LHU / C.LH / C.SB / C.SH
69            0b00 if funct3 == 0b100 => {
70                let sub = ((inst >> 10) & 0b111) as u8;
71                let rs1_bits = prime_reg_bits(((inst >> 7) & 0b111) as u8);
72                let rd_rs2_bits = prime_reg_bits(((inst >> 2) & 0b111) as u8);
73
74                match sub {
75                    // C.LBU  uimm[1]=inst[5], uimm[0]=inst[6]
76                    0b000 => {
77                        let uimm = ((((inst >> 5) & 1) << 1) | ((inst >> 6) & 1)) as u8;
78                        let rs1 = Reg::from_bits(rs1_bits)?;
79                        let rd = Reg::from_bits(rd_rs2_bits)?;
80                        Some(Self::CLbu { rd, rs1, uimm })
81                    }
82                    // C.LHU (funct1=inst[6]=0) / C.LH (funct1=inst[6]=1)
83                    // uimm[1]=inst[5], uimm[0]=0 (halfword aligned)
84                    0b001 => {
85                        let funct1 = ((inst >> 6) & 1) as u8;
86                        let uimm = (((inst >> 5) & 1) as u8) << 1;
87                        let rs1 = Reg::from_bits(rs1_bits)?;
88                        let rd = Reg::from_bits(rd_rs2_bits)?;
89                        if funct1 == 0 {
90                            Some(Self::CLhu { rd, rs1, uimm })
91                        } else {
92                            Some(Self::CLh { rd, rs1, uimm })
93                        }
94                    }
95                    // C.SB  uimm[1]=inst[5], uimm[0]=inst[6]
96                    0b010 => {
97                        let uimm = ((((inst >> 5) & 1) << 1) | ((inst >> 6) & 1)) as u8;
98                        let rs1 = Reg::from_bits(rs1_bits)?;
99                        let rs2 = Reg::from_bits(rd_rs2_bits)?;
100                        Some(Self::CSb { rs1, rs2, uimm })
101                    }
102                    // C.SH  funct1=inst[6]=0, uimm[1]=inst[5]
103                    0b011 => {
104                        if ((inst >> 6) & 1) != 0 {
105                            None?;
106                        }
107                        let uimm = (((inst >> 5) & 1) as u8) << 1;
108                        let rs1 = Reg::from_bits(rs1_bits)?;
109                        let rs2 = Reg::from_bits(rd_rs2_bits)?;
110                        Some(Self::CSh { rs1, rs2, uimm })
111                    }
112                    _ => None,
113                }
114            }
115
116            // Q01 funct3=100, funct2[11:10]=11, bit12=1: unary ops and C.MUL
117            //
118            // Encoding layout (per ratified Zcb spec):
119            //   funct2b = inst[6:5]
120            //   0b11 => unary ops, sub-op = inst[4:2]
121            //   0b10 => C.MUL, rs2' = inst[4:2]
122            //   0b00, 0b01 => reserved
123            0b01 if funct3 == 0b100 => {
124                let funct2_11_10 = ((inst >> 10) & 0b11) as u8;
125                let bit12 = (inst >> 12) & 1;
126
127                if funct2_11_10 != 0b11 || bit12 == 0 {
128                    None?;
129                }
130
131                let rd_rs1_bits = prime_reg_bits(((inst >> 7) & 0b111) as u8);
132                let funct2b = ((inst >> 5) & 0b11) as u8;
133                let rs2_sub = ((inst >> 2) & 0b111) as u8;
134
135                match funct2b {
136                    // Unary ops: funct2b=0b11, sub-op in inst[4:2]
137                    0b11 => {
138                        let rd = Reg::from_bits(rd_rs1_bits)?;
139                        match rs2_sub {
140                            0b000 => Some(Self::CZextB { rd }),
141                            0b001 => Some(Self::CSextB { rd }),
142                            0b010 => Some(Self::CZextH { rd }),
143                            0b011 => Some(Self::CSextH { rd }),
144                            0b100 => Some(Self::CZextW { rd }),
145                            0b101 => Some(Self::CNot { rd }),
146                            // 110, 111 reserved
147                            _ => None,
148                        }
149                    }
150                    // C.MUL: funct2b=0b10, rs2' = inst[4:2]
151                    0b10 => {
152                        let rd = Reg::from_bits(rd_rs1_bits)?;
153                        let rs2 = Reg::from_bits(prime_reg_bits(rs2_sub))?;
154                        Some(Self::CMul { rd, rs2 })
155                    }
156                    // funct2b=0b00 and funct2b=0b01 are reserved in Zcb
157                    _ => None,
158                }
159            }
160
161            _ => None,
162        }
163    }
164
165    #[inline(always)]
166    fn alignment() -> u8 {
167        align_of::<u16>() as u8
168    }
169
170    #[inline(always)]
171    fn size(&self) -> u8 {
172        size_of::<u16>() as u8
173    }
174}
175
176impl<Reg> fmt::Display for Rv64ZcbInstruction<Reg>
177where
178    Reg: fmt::Display,
179{
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        match self {
182            Self::CLbu { rd, rs1, uimm } => write!(f, "c.lbu {rd}, {uimm}({rs1})"),
183            Self::CLh { rd, rs1, uimm } => write!(f, "c.lh {rd}, {uimm}({rs1})"),
184            Self::CLhu { rd, rs1, uimm } => write!(f, "c.lhu {rd}, {uimm}({rs1})"),
185            Self::CSb { rs1, rs2, uimm } => write!(f, "c.sb {rs2}, {uimm}({rs1})"),
186            Self::CSh { rs1, rs2, uimm } => write!(f, "c.sh {rs2}, {uimm}({rs1})"),
187            Self::CZextB { rd } => write!(f, "c.zext.b {rd}"),
188            Self::CSextB { rd } => write!(f, "c.sext.b {rd}"),
189            Self::CZextH { rd } => write!(f, "c.zext.h {rd}"),
190            Self::CSextH { rd } => write!(f, "c.sext.h {rd}"),
191            Self::CZextW { rd } => write!(f, "c.zext.w {rd}"),
192            Self::CNot { rd } => write!(f, "c.not {rd}"),
193            Self::CMul { rd, rs2 } => write!(f, "c.mul {rd}, {rs2}"),
194        }
195    }
196}