1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#![no_std]

mod compressed;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RType(pub u32);
impl RType {
    pub fn funct7(&self) -> u32 { (self.0 >> 25) & 0x7f }
    pub fn rs2(&self) -> u32 { (self.0 >> 20) & 0x1f }
    pub fn rs1(&self) -> u32 { (self.0 >> 15) & 0x1f }
    pub fn funct3(&self) -> u32 { (self.0 >> 12) & 0x7 }
    pub fn rd(&self) -> u32 { (self.0 >> 7) & 0x1f }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct CsrType(u32);
impl CsrType {
    pub fn csr(&self) -> u32 { (self.0 >> 20) }
    pub fn rs1(&self) -> u32 { (self.0 >> 15) & 0x1f }
    pub fn rd(&self) -> u32 { (self.0 >> 7) & 0x1f }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct CsrIType(u32);
impl CsrIType {
    pub fn csr(&self) -> u32 { (self.0 >> 20) }
    pub fn zimm(&self) -> u32 { (self.0 >> 15) & 0x1f }
    pub fn rd(&self) -> u32 { (self.0 >> 7) & 0x1f }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct IType(u32);
impl IType {
    pub fn imm(&self) -> u32 { (self.0 >> 20) }
    pub fn rs1(&self) -> u32 { (self.0 >> 15) & 0x1f }
    pub fn rd(&self) -> u32 { (self.0 >> 7) & 0x1f }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct SType(u32);
impl SType {
    pub fn imm(&self) -> u32 { ((self.0 >> 20) & 0xfe0) | ((self.0 >> 7) & 0x1f) }
    pub fn rs1(&self) -> u32 { (self.0 >> 15) & 0x1f }
    pub fn rs2(&self) -> u32 { (self.0 >> 20) & 0x1f }
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct BType(u32);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct UType(u32);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct JType(u32);

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Instruction {
    // Load
    Lb(IType),
    Lh(IType),
    Lw(IType),
    Lbu(IType),
    Lhu(IType),
    Lwu(IType),
    Ld(IType),

    // Store
    Sb(SType),
    Sh(SType),
    Sw(SType),
    Sd(SType),

    // System
    Ecall,
    Ebreak,
    Uret,
    Sret,
    Mret,
    Wfi,
    SfenceVma(RType),
    Csrrw(CsrType),
    Csrrs(CsrType),
    Csrrc(CsrType),
    Csrrwi(CsrIType),
    Csrrsi(CsrIType),
    Csrrci(CsrIType),

    Illegal,
}

fn decode_opcode(i: u32) -> u32 { i & 0x7f }

pub fn try_decode(i: u32) -> Option<Instruction> {
    match i & 0b11 {
        0b00 => compressed::try_decode_q00(i),
        0b01 => compressed::try_decode_q01(i),
        0b10 => compressed::try_decode_q10(i),
        0b11 => match decode_opcode(i) {
            0b1110011 => try_decode_system(i),
            0b0000011 => try_decode_load(i),
            0b0100011 => try_decode_store(i),
            _ => None,
        }
        _ => unreachable!(),
    }
}

pub fn try_decode_load(i: u32) -> Option<Instruction> {
    match (i >> 12) & 0b111 {
        0b000 => Some(Instruction::Lb(IType(i))),
        0b001 => Some(Instruction::Lh(IType(i))),
        0b010 => Some(Instruction::Lw(IType(i))),
        0b011 => Some(Instruction::Ld(IType(i))),
        0b100 => Some(Instruction::Lbu(IType(i))),
        0b101 => Some(Instruction::Lhu(IType(i))),
        0b110 => Some(Instruction::Lwu(IType(i))),
        0b111 => None,
        _ => unreachable!(),
    }
}

pub fn try_decode_store(i: u32) -> Option<Instruction> {
    match (i >> 12) & 0b111 {
        0b000 => Some(Instruction::Sb(SType(i))),
        0b001 => Some(Instruction::Sh(SType(i))),
        0b010 => Some(Instruction::Sw(SType(i))),
        0b011 => Some(Instruction::Sd(SType(i))),
        _ => None,
    }
}

pub fn try_decode_system(i: u32) -> Option<Instruction> {
    match i {
        // Environment Call and Breakpoint
        0b000000000000_00000_000_00000_1110011 => return Some(Instruction::Ecall),
        0b000000000001_00000_000_00000_1110011 => return Some(Instruction::Ebreak),
        // Trap-Return Instructions
        0b0000000_00010_00000_000_00000_1110011 => return Some(Instruction::Uret),
        0b0001000_00010_00000_000_00000_1110011 => return Some(Instruction::Sret),
        0b0011000_00010_00000_000_00000_1110011 => return Some(Instruction::Mret),
        // Interrupt-Management Instructions
        0b0001000_00101_00000_000_00000_1110011 => return Some(Instruction::Wfi),
        _ => {},
    }

    match (i >> 12) & 0b111 {
        0b001 => return Some(Instruction::Csrrw(CsrType(i))),
        0b010 => return Some(Instruction::Csrrs(CsrType(i))),
        0b011 => return Some(Instruction::Csrrc(CsrType(i))),
        0b101 => return Some(Instruction::Csrrwi(CsrIType(i))),
        0b110 => return Some(Instruction::Csrrsi(CsrIType(i))),
        0b111 => return Some(Instruction::Csrrci(CsrIType(i))),
        _ => {}
    }

    // Memory-Management Instructions
    const SFENCE_VMA_MASK: u32 =  0b1111111_00000_00000_111_11111_1111111;
    const SFENCE_VMA_VALUE: u32 = 0b0001001_00000_00000_000_00000_1110011;
    if i & SFENCE_VMA_MASK == SFENCE_VMA_VALUE {
        return Some(Instruction::SfenceVma(RType(i)));
    }

    None
}

/// Return the length (in bytes) of an instruction given the low 16 bits of it. The current spec
/// reserves a bit pattern for instructions of length >= 192 bits, but for simplicity this function
/// just returns 24 in that case.
pub fn instruction_length(i: u16) -> usize {
    if i & 0b11 != 0b11 {
        2
    } else if i & 0b11100 != 0b11100 {
        4
    } else if i & 0b111111 == 0b011111 {
        6
    } else if i & 0b1111111 == 0b011111 {
        8
    } else {
        10 + 2 * ((i >> 12) & 0b111) as usize
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}