Skip to main content

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

1//! RV32 Zkne extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::hart::HartConfig;
7use crate::instructions::rv32::zk::zkn::zknd::Rv32AesBs;
8use crate::instructions::{ImplementedExtension, Instruction, IsaExtension};
9use crate::registers::general_purpose::Register;
10use ab_riscv_macros::instruction;
11use core::fmt;
12
13/// RISC-V RV32 Zkne instructions (AES encryption)
14#[instruction]
15#[derive(Debug, Clone, Copy)]
16#[derive_const(PartialEq, Eq)]
17pub enum Rv32ZkneInstruction<Hart>
18where
19    Hart: HartConfig,
20{
21    /// AES final round encryption step: SubBytes on one byte of rs2, rotated to the byte lane
22    /// selected by bs, XOR'd into rs1.
23    ///
24    /// `rd = rs1 ^ rol32(SBOX[(rs2 >> (bs*8)) & 0xff] as u32, bs*8)`
25    Aes32Esi {
26        rd: Hart::Reg,
27        rs1: Hart::Reg,
28        rs2: Hart::Reg,
29        bs: Rv32AesBs,
30    },
31    /// AES middle round encryption step: SubBytes + partial MixColumns on one byte of rs2, rotated
32    /// to the byte lane selected by bs, XOR'd into rs1.
33    ///
34    /// `rd = rs1 ^ rol32(MixColByte(SBOX[(rs2 >> (bs*8)) & 0xff]), bs*8)`
35    Aes32Esmi {
36        rd: Hart::Reg,
37        rs1: Hart::Reg,
38        rs2: Hart::Reg,
39        bs: Rv32AesBs,
40    },
41}
42
43/// Encoding layout (R-type, opcode 0x33, funct3 0x0):
44///
45/// ```text
46/// [31:30] bs       - 2-bit byte select
47/// [29:25] funct5   - 0b1_0001 (aes32esi) / 0b1_0011 (aes32esmi)
48/// [24:20] rs2
49/// [19:15] rs1
50/// [14:12] funct3   - 0b000
51/// [11:7]  rd
52/// [6:0]   opcode   - 0b011_0011 (OP)
53/// ```
54///
55/// Ratified match/mask values (from riscv-opcodes):
56///   MATCH_AES32ESI  = 0x2200_0033, MASK_AES32ESI  = 0x3e00_707f
57///   MATCH_AES32ESMI = 0x2600_0033, MASK_AES32ESMI = 0x3e00_707f
58///
59/// `rd` and `rs1` are independent fields. The assembler convention places
60/// the accumulator in both rd and rs1 (the `rt` pattern), but the hardware
61/// does not require rd == rs1 and the decoder must not enforce it.
62#[instruction]
63const impl<Reg, Hart> Instruction for Rv32ZkneInstruction<Hart>
64where
65    Reg: [const] Register<Type = u32>,
66    Hart: [const] HartConfig<Reg = Reg>,
67{
68    const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[IsaExtension::new("zkne", 1, 0)];
69
70    const ALIGNMENT: u8 = align_of::<u32>() as u8;
71
72    type Hart = Hart;
73
74    #[inline(always)]
75    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
76    fn try_decode(instruction: u32) -> Option<Self> {
77        let opcode = (instruction & 0b111_1111) as u8;
78        let rd_bits = ((instruction >> 7) & 0x1f) as u8;
79        let funct3 = ((instruction >> 12) & 0b111) as u8;
80        let rs1_bits = ((instruction >> 15) & 0x1f) as u8;
81        let rs2_bits = ((instruction >> 20) & 0x1f) as u8;
82        let funct5 = ((instruction >> 25) & 0b1_1111) as u8;
83        let bs_bits = ((instruction >> 30) & 0b11) as u8;
84
85        // R-type OP opcode only
86        if opcode != 0b011_0011 {
87            None?;
88        }
89        if funct3 != 0b000 {
90            None?;
91        }
92
93        let rd = Reg::from_bits(rd_bits)?;
94        let rs1 = Reg::from_bits(rs1_bits)?;
95        let rs2 = Reg::from_bits(rs2_bits)?;
96        let bs = Rv32AesBs::from_bits(bs_bits)?;
97
98        match funct5 {
99            // aes32esi:  bs[31:30] | 0b1_0001[29:25]
100            0b1_0001 => Some(Self::Aes32Esi { rd, rs1, rs2, bs }),
101            // aes32esmi: bs[31:30] | 0b1_0011[29:25]
102            0b1_0011 => Some(Self::Aes32Esmi { rd, rs1, rs2, bs }),
103            _ => None,
104        }
105    }
106
107    #[inline(always)]
108    fn size(&self) -> u8 {
109        size_of::<u32>() as u8
110    }
111}
112
113#[instruction]
114impl<Reg, Hart> fmt::Display for Rv32ZkneInstruction<Hart>
115where
116    Reg: fmt::Display,
117    Hart: HartConfig<Reg = Reg>,
118{
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self {
121            Self::Aes32Esi { rd, rs1, rs2, bs } => {
122                write!(f, "aes32esi {rd}, {rs1}, {rs2}, {bs}")
123            }
124            Self::Aes32Esmi { rd, rs1, rs2, bs } => {
125                write!(f, "aes32esmi {rd}, {rs1}, {rs2}, {bs}")
126            }
127        }
128    }
129}