irv/
lib.rs

1use std::num::{NonZeroU32, NonZeroU64};
2
3pub use irv_traits::*;
4
5mod instruction;
6mod memory;
7
8pub use memory::Memory;
9
10/// Exceptions that can be encountered during the execution of an instruction
11/// by a [BaseHart].
12///
13/// These mostly line up with the list of exceptions defined by the RISC-V
14/// privlieged specification, though with some minor differences and omissions.
15#[derive(Debug)]
16pub enum Exception {
17    InstructionAddressMisaligned { address: Option<NonZeroU64> },
18    InstructionAccessFault { address: Option<NonZeroU64> },
19    IllegalInstruction { instruction: Option<NonZeroU32> },
20    Breakpoint { address: Option<NonZeroU64> },
21    LoadAddressMisaligned { address: Option<NonZeroU64> },
22    LoadAccessFault { address: Option<NonZeroU64> },
23    StoreAmoAddressMisaligned { address: Option<NonZeroU64> },
24    StoreAmoAccessFault { address: Option<NonZeroU64> },
25    EnvironmentCall,
26}
27
28/// A simple implementation of a processor that implements only machine mode.
29pub struct BaseHart<B, C> {
30    /// The system bus for reading and writing physical memory addresses.
31    pub bus: B,
32    /// The address of the currently-executing instruction, if one is being executed.
33    pub pc: u64,
34    /// The address of the next instruction.
35    pub next: u64,
36    /// The general-purpose registers x0 through x31.
37    pub gpr: [u64; 32],
38    /// The state of all control and status registers.
39    pub csr: C,
40    /// The result that will be returned after the current instruction finishes.
41    result: Result<(), Exception>,
42}
43
44impl<B, C> BaseHart<B, C> {
45    /// Creates a new `Hart` that connects to `bus` for memory accesses.
46    pub fn new(bus: B, csr: C) -> BaseHart<B, C> {
47        BaseHart {
48            bus,
49            pc: 0,
50            next: 0,
51            gpr: [0; 32],
52            csr,
53            result: Ok(()),
54        }
55    }
56
57    /// Executes one instruction at the given address, returning the offset
58    /// that should be applied to the PC to execute the next instruction.
59    ///
60    /// The address may have any alignment; only PC offsets are checked for
61    /// alignment.
62    pub fn execute(&mut self) -> Result<(), Exception>
63    where
64        B: Bus<u64, u64> + Bus<u64, u32> + Bus<u64, u16> + Bus<u64, u8>,
65        C: Csr,
66    {
67        // Fetch
68        let raw: u32 = match self.bus.load(self.pc) {
69            Ok(raw) => raw,
70            Err(BusError::AccessFault) => {
71                return Err(Exception::InstructionAccessFault {
72                    address: NonZeroU64::new(self.pc),
73                })
74            }
75            Err(BusError::AddressMisaligned) => {
76                return Err(Exception::InstructionAddressMisaligned {
77                    address: NonZeroU64::new(self.pc),
78                })
79            }
80        };
81
82        // Decode the part that will be matched on
83        let funct3_opcode = raw & 0b1111111 | raw >> 5 & 0b111 << 7;
84
85        // Set x0 back to zero in case it was set by a previous instruction
86        self.gpr[0] = 0;
87
88        // Calculate the address of the next instruction.
89        self.next = self.pc.wrapping_add(4);
90
91        // Match on the opcode (and funct3) to decode the rest of the
92        // instruction and execute it
93        let instruction = match funct3_opcode {
94            0b000_0110111 | 0b001_0110111 | 0b010_0110111 | 0b011_0110111 | 0b100_0110111
95            | 0b101_0110111 | 0b110_0110111 | 0b111_0110111 => instruction::lui,
96            0b000_0010111 | 0b001_0010111 | 0b010_0010111 | 0b011_0010111 | 0b100_0010111
97            | 0b101_0010111 | 0b110_0010111 | 0b111_0010111 => instruction::auipc,
98            0b000_1101111 | 0b001_1101111 | 0b010_1101111 | 0b011_1101111 | 0b100_1101111
99            | 0b101_1101111 | 0b110_1101111 | 0b111_1101111 => instruction::jal,
100            0b000_1100111 => instruction::jalr,
101            0b000_1100011 => instruction::beq,
102            0b001_1100011 => instruction::bne,
103            0b100_1100011 => instruction::blt,
104            0b101_1100011 => instruction::bge,
105            0b110_1100011 => instruction::bltu,
106            0b111_1100011 => instruction::bgeu,
107            0b000_0000011 => instruction::lb,
108            0b001_0000011 => instruction::lh,
109            0b010_0000011 => instruction::lw,
110            0b011_0000011 => instruction::ld,
111            0b100_0000011 => instruction::lbu,
112            0b101_0000011 => instruction::lhu,
113            0b110_0000011 => instruction::lwu,
114            0b000_0100011 => instruction::sb,
115            0b001_0100011 => instruction::sh,
116            0b010_0100011 => instruction::sw,
117            0b011_0100011 => instruction::sd,
118            0b000_0010011 => instruction::addi,
119            0b000_0011011 => instruction::addiw,
120            0b010_0010011 => instruction::slti,
121            0b011_0010011 => instruction::sltiu,
122            0b100_0010011 => instruction::xori,
123            0b110_0010011 => instruction::ori,
124            0b111_0010011 => instruction::andi,
125            0b001_0010011 => instruction::slli,
126            0b101_0010011 => instruction::srxi,
127            0b001_0011011 => instruction::slliw,
128            0b101_0011011 => instruction::srxiw,
129            0b000_0110011 => instruction::add_sub,
130            0b000_0111011 => instruction::addw_subw,
131            0b001_0110011 => instruction::sll,
132            0b010_0110011 => instruction::slt,
133            0b011_0110011 => instruction::sltu,
134            0b100_0110011 => instruction::xor,
135            0b101_0110011 => instruction::srx,
136            0b110_0110011 => instruction::or,
137            0b111_0110011 => instruction::and,
138            0b001_0111011 => instruction::sllw,
139            0b101_0111011 => instruction::srxw,
140            0b000_0001111 => instruction::fence,
141            0b001_0001111 => instruction::fence_i,
142            0b000_1110011 => instruction::ecall_ebreak,
143            0b001_1110011 => instruction::csrrw,
144            0b010_1110011 => instruction::csrrs,
145            0b011_1110011 => instruction::csrrc,
146            0b101_1110011 => instruction::csrrwi,
147            0b110_1110011 => instruction::csrrsi,
148            0b111_1110011 => instruction::csrrci,
149            _ => |hart: &mut BaseHart<B, C>, raw| {
150                hart.raise(Exception::IllegalInstruction {
151                    instruction: NonZeroU32::new(raw),
152                })
153            },
154        };
155
156        instruction(self, raw);
157
158        self.pc = self.next;
159
160        let mut result = Ok(());
161        std::mem::swap(&mut self.result, &mut result);
162        result
163    }
164
165    /// Sets up state for the given exception to be raised after execution is finished.
166    fn raise(&mut self, exception: Exception) {
167        self.result = Err(exception);
168    }
169}