Skip to main content

ab_riscv_primitives/instructions/rv32/zk/zkn/
zknd.rs

1//! RV32 Zknd 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/// 2-bit byte-select immediate for RV32 AES instructions.
12///
13/// Selects which byte of `rs2` is fed into the S-box: `bs ∈ {0,1,2,3}`.
14#[derive(Debug, Clone, Copy)]
15#[derive_const(PartialEq, Eq)]
16#[repr(u8)]
17pub enum Rv32AesBs {
18    B0 = 0,
19    B1 = 1,
20    B2 = 2,
21    B3 = 3,
22}
23
24impl From<Rv32AesBs> for u8 {
25    #[inline(always)]
26    fn from(bs: Rv32AesBs) -> Self {
27        bs as u8
28    }
29}
30
31impl From<Rv32AesBs> for usize {
32    #[inline(always)]
33    fn from(bs: Rv32AesBs) -> Self {
34        usize::from(bs as u8)
35    }
36}
37
38impl fmt::Display for Rv32AesBs {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        fmt::Display::fmt(&(*self as u8), f)
41    }
42}
43
44impl Rv32AesBs {
45    /// Create from raw 2-bit value. Returns `None` if `bits > 3`.
46    #[inline(always)]
47    pub const fn from_bits(bits: u8) -> Option<Self> {
48        match bits {
49            0 => Some(Self::B0),
50            1 => Some(Self::B1),
51            2 => Some(Self::B2),
52            3 => Some(Self::B3),
53            _ => None,
54        }
55    }
56}
57
58/// RISC-V RV32 Zknd instructions (AES decryption)
59#[instruction]
60#[derive(Debug, Clone, Copy)]
61#[derive_const(PartialEq, Eq)]
62pub enum Rv32ZkndInstruction<Reg> {
63    /// AES final round decryption step: InvSubBytes on one byte of rs2,
64    /// rotated to the byte lane selected by bs, XOR'd into rs1.
65    ///
66    /// `rd = rs1 ^ rol32(INV_SBOX[(rs2 >> (bs*8)) & 0xff] as u32, bs*8)`
67    Aes32Dsi {
68        rd: Reg,
69        rs1: Reg,
70        rs2: Reg,
71        bs: Rv32AesBs,
72    },
73    /// AES middle round decryption step: InvSubBytes + partial InvMixColumns
74    /// on one byte of rs2, rotated to the byte lane selected by bs, XOR'd into rs1.
75    ///
76    /// `rd = rs1 ^ rol32(InvMixColByte(INV_SBOX[(rs2 >> (bs*8)) & 0xff]), bs*8)`
77    Aes32Dsmi {
78        rd: Reg,
79        rs1: Reg,
80        rs2: Reg,
81        bs: Rv32AesBs,
82    },
83}
84
85/// Encoding layout (R-type, opcode 0x33, funct3 0x0):
86///
87/// ```text
88/// [31:30] bs       - 2-bit byte select
89/// [29:25] funct5   - 0b1_0101 (aes32dsi) / 0b1_0111 (aes32dsmi)
90/// [24:20] rs2
91/// [19:15] rs1
92/// [14:12] funct3   - 0b000
93/// [11:7]  rd
94/// [6:0]   opcode   - 0b011_0011 (OP)
95/// ```
96///
97/// Ratified match/mask values (from riscv-opcodes):
98///   MATCH_AES32DSI  = 0x2a00_0033, MASK_AES32DSI  = 0x3e00_707f
99///   MATCH_AES32DSMI = 0x2e00_0033, MASK_AES32DSMI = 0x3e00_707f
100///
101/// `rd` and `rs1` are independent fields. The assembler convention places
102/// the accumulator in both rd and rs1 (the `rt` pattern), but the hardware
103/// does not require rd == rs1 and the decoder must not enforce it.
104#[instruction]
105const impl<Reg> Instruction for Rv32ZkndInstruction<Reg>
106where
107    Reg: [const] Register<Type = u32>,
108{
109    type Reg = Reg;
110
111    #[inline(always)]
112    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
113    fn try_decode(instruction: u32) -> Option<Self> {
114        let opcode = (instruction & 0b111_1111) as u8;
115        let rd_bits = ((instruction >> 7) & 0x1f) as u8;
116        let funct3 = ((instruction >> 12) & 0b111) as u8;
117        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
118        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
119        let funct5 = ((instruction >> 25) & 0b1_1111) as u8;
120        let bs_bits = ((instruction >> 30) & 0b11) as u8;
121
122        // R-type OP opcode only
123        if opcode != 0b011_0011 {
124            None?;
125        }
126        if funct3 != 0b000 {
127            None?;
128        }
129
130        let rd = Reg::from_bits(rd_bits)?;
131        let rs1 = Reg::from_bits(rs1_bits)?;
132        let rs2 = Reg::from_bits(rs2_bits)?;
133        let bs = Rv32AesBs::from_bits(bs_bits)?;
134
135        match funct5 {
136            // aes32dsi:  bs[31:30] | 0b1_0101[29:25]
137            0b1_0101 => Some(Self::Aes32Dsi { rd, rs1, rs2, bs }),
138            // aes32dsmi: bs[31:30] | 0b1_0111[29:25]
139            0b1_0111 => Some(Self::Aes32Dsmi { rd, rs1, rs2, bs }),
140            _ => None,
141        }
142    }
143
144    #[inline(always)]
145    fn alignment() -> u8 {
146        align_of::<u32>() as u8
147    }
148
149    #[inline(always)]
150    fn size(&self) -> u8 {
151        size_of::<u32>() as u8
152    }
153}
154
155#[instruction]
156impl<Reg> fmt::Display for Rv32ZkndInstruction<Reg>
157where
158    Reg: fmt::Display,
159{
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        match self {
162            Self::Aes32Dsi { rd, rs1, rs2, bs } => {
163                write!(f, "aes32dsi {rd}, {rs1}, {rs2}, {bs}")
164            }
165            Self::Aes32Dsmi { rd, rs1, rs2, bs } => {
166                write!(f, "aes32dsmi {rd}, {rs1}, {rs2}, {bs}")
167            }
168        }
169    }
170}