rrs_lib/
instruction_executor.rs

1// Copyright 2021 Gregory Chadwick <mail@gregchadwick.co.uk>
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5//! An [InstructionProcessor] that executes instructions.
6//!
7//! The [InstructionExecutor] takes a [HartState] and a [Memory]. The [HartState] is updated by the
8//! instruction execution using the [Memory] for all memory accesses. A [InstructionExecutor::step]
9//! function is provided to deal with reading the next instruction from the memory, updating the PC
10//! appropriately and wraps the call to [process_instruction()]`.
11//!
12//! # Example
13//!
14//! ```
15//! use rrs_lib::HartState;
16//! use rrs_lib::memories::VecMemory;
17//! use rrs_lib::instruction_executor::{InstructionExecutor, InstructionException};
18//!
19//! let mut hart = HartState::new();
20//! // Memory contains these instructions:
21//! // lui x2, 0x1234b
22//! // lui x3, 0xf387e
23//! // add x1, x2, x3
24//! let mut mem = VecMemory::new(vec![0x1234b137, 0xf387e1b7, 0x003100b3]);
25//!
26//! hart.pc = 0;
27//!
28//! let mut executor = InstructionExecutor {
29//!     hart_state: &mut hart,
30//!     mem: &mut mem,
31//! };
32//!
33//! assert_eq!(executor.step(), Ok(()));
34//! assert_eq!(executor.hart_state.registers[2], 0x1234b000);
35//! assert_eq!(executor.step(), Ok(()));
36//! assert_eq!(executor.hart_state.registers[3], 0xf387e000);
37//! assert_eq!(executor.step(), Ok(()));
38//! assert_eq!(executor.hart_state.registers[1], 0x05bc9000);
39//! // Memory only contains three instructions so next step will produce a fetch error
40//! assert_eq!(executor.step(), Err(InstructionException::FetchError(0xc)));
41//! ```
42
43use super::instruction_formats;
44use super::process_instruction;
45use super::{HartState, InstructionProcessor, MemAccessSize, Memory};
46use paste::paste;
47
48/// Different exceptions that can occur during instruction execution
49#[derive(Debug, PartialEq)]
50pub enum InstructionException {
51    // TODO: Better to name the fields?
52    IllegalInstruction(u32, u32),
53    FetchError(u32),
54    LoadAccessFault(u32),
55    StoreAccessFault(u32),
56    AlignmentFault(u32),
57}
58
59/// An `InstructionProcessor` that execute instructions, updating `hart_state` as appropriate.
60pub struct InstructionExecutor<'a, M: Memory> {
61    /// Memory used by load and store instructions
62    pub mem: &'a mut M,
63    pub hart_state: &'a mut HartState,
64}
65
66impl<'a, M: Memory> InstructionExecutor<'a, M> {
67    fn execute_reg_reg_op<F>(&mut self, dec_insn: instruction_formats::RType, op: F)
68    where
69        F: Fn(u32, u32) -> u32,
70    {
71        let a = self.hart_state.read_register(dec_insn.rs1);
72        let b = self.hart_state.read_register(dec_insn.rs2);
73        let result = op(a, b);
74        self.hart_state.write_register(dec_insn.rd, result);
75    }
76
77    fn execute_reg_imm_op<F>(&mut self, dec_insn: instruction_formats::IType, op: F)
78    where
79        F: Fn(u32, u32) -> u32,
80    {
81        let a = self.hart_state.read_register(dec_insn.rs1);
82        let b = dec_insn.imm as u32;
83        let result = op(a, b);
84        self.hart_state.write_register(dec_insn.rd, result);
85    }
86
87    fn execute_reg_imm_shamt_op<F>(&mut self, dec_insn: instruction_formats::ITypeShamt, op: F)
88    where
89        F: Fn(u32, u32) -> u32,
90    {
91        let a = self.hart_state.read_register(dec_insn.rs1);
92        let result = op(a, dec_insn.shamt);
93        self.hart_state.write_register(dec_insn.rd, result)
94    }
95
96    // Returns true if branch succeeds
97    fn execute_branch<F>(&mut self, dec_insn: instruction_formats::BType, cond: F) -> bool
98    where
99        F: Fn(u32, u32) -> bool,
100    {
101        let a = self.hart_state.read_register(dec_insn.rs1);
102        let b = self.hart_state.read_register(dec_insn.rs2);
103
104        if cond(a, b) {
105            let new_pc = self.hart_state.pc.wrapping_add(dec_insn.imm as u32);
106            self.hart_state.pc = new_pc;
107            true
108        } else {
109            false
110        }
111    }
112
113    fn execute_load(
114        &mut self,
115        dec_insn: instruction_formats::IType,
116        size: MemAccessSize,
117        signed: bool,
118    ) -> Result<(), InstructionException> {
119        let addr = self
120            .hart_state
121            .read_register(dec_insn.rs1)
122            .wrapping_add(dec_insn.imm as u32);
123
124        // Determine if address is aligned to size, returning an AlignmentFault as an error if it
125        // is not.
126        let align_mask = match size {
127            MemAccessSize::Byte => 0x0,
128            MemAccessSize::HalfWord => 0x1,
129            MemAccessSize::Word => 0x3,
130        };
131
132        if (addr & align_mask) != 0x0 {
133            return Err(InstructionException::AlignmentFault(addr));
134        }
135
136        // Attempt to read data from memory, returning a LoadAccessFault as an error if it is not.
137        let mut load_data = match self.mem.read_mem(addr, size) {
138            Some(d) => d,
139            None => {
140                return Err(InstructionException::LoadAccessFault(addr));
141            }
142        };
143
144        // Sign extend loaded data if required
145        if signed {
146            load_data = (match size {
147                MemAccessSize::Byte => (load_data as i8) as i32,
148                MemAccessSize::HalfWord => (load_data as i16) as i32,
149                MemAccessSize::Word => load_data as i32,
150            }) as u32;
151        }
152
153        // Write load data to destination register
154        self.hart_state.write_register(dec_insn.rd, load_data);
155        Ok(())
156    }
157
158    fn execute_store(
159        &mut self,
160        dec_insn: instruction_formats::SType,
161        size: MemAccessSize,
162    ) -> Result<(), InstructionException> {
163        let addr = self
164            .hart_state
165            .read_register(dec_insn.rs1)
166            .wrapping_add(dec_insn.imm as u32);
167        let data = self.hart_state.read_register(dec_insn.rs2);
168
169        let align_mask = match size {
170            MemAccessSize::Byte => 0x0,
171            MemAccessSize::HalfWord => 0x1,
172            MemAccessSize::Word => 0x3,
173        };
174
175        // Determine if address is aligned to size, returning an AlignmentFault as an error if it
176        // is not.
177        if (addr & align_mask) != 0x0 {
178            return Err(InstructionException::AlignmentFault(addr));
179        }
180
181        // Write store data to memory, returning a StoreAccessFault as an error if write fails.
182        if self.mem.write_mem(addr, size, data) {
183            Ok(())
184        } else {
185            Err(InstructionException::StoreAccessFault(addr))
186        }
187    }
188
189    /// Execute instruction pointed to by `hart_state.pc`
190    ///
191    /// Returns `Ok` where instruction execution was successful. `Err` with the relevant
192    /// [InstructionException] is returned when the instruction execution causes an exception.
193    pub fn step(&mut self) -> Result<(), InstructionException> {
194        self.hart_state.last_register_write = None;
195
196        // Fetch next instruction from memory
197        if let Some(next_insn) = self.mem.read_mem(self.hart_state.pc, MemAccessSize::Word) {
198            // Execute the instruction
199            let step_result = process_instruction(self, next_insn);
200
201            match step_result {
202                Some(Ok(pc_updated)) => {
203                    if !pc_updated {
204                        // Instruction didn't update PC so increment to next instruction
205                        self.hart_state.pc += 4;
206                    }
207                    Ok(())
208                }
209                // Instruction produced an error so return it
210                Some(Err(e)) => Err(e),
211                // Instruction decode failed so return an IllegalInstruction as an error
212                None => Err(InstructionException::IllegalInstruction(
213                    self.hart_state.pc,
214                    next_insn,
215                )),
216            }
217        } else {
218            // Return a FetchError as an error if instruction fetch fails
219            Err(InstructionException::FetchError(self.hart_state.pc))
220        }
221    }
222}
223
224fn sign_extend_u32(x: u32) -> i64 {
225    (x as i32) as i64
226}
227
228// Macros to implement various repeated operations (e.g. ALU reg op reg instructions).
229macro_rules! make_alu_op_reg_fn {
230    ($name:ident, $op_fn:expr) => {
231        paste! {
232            fn [<process_ $name>](
233                &mut self,
234                dec_insn: instruction_formats::RType
235            ) -> Self::InstructionResult {
236                self.execute_reg_reg_op(dec_insn, $op_fn);
237
238                Ok(false)
239            }
240        }
241    };
242}
243
244macro_rules! make_alu_op_imm_fn {
245    ($name:ident, $op_fn:expr) => {
246        paste! {
247            fn [<process_ $name i>](
248                &mut self,
249                dec_insn: instruction_formats::IType
250            ) -> Self::InstructionResult {
251                self.execute_reg_imm_op(dec_insn, $op_fn);
252
253                Ok(false)
254            }
255        }
256    };
257}
258
259macro_rules! make_alu_op_imm_shamt_fn {
260    ($name:ident, $op_fn:expr) => {
261        paste! {
262            fn [<process_ $name i>](
263                &mut self,
264                dec_insn: instruction_formats::ITypeShamt
265            ) -> Self::InstructionResult {
266                self.execute_reg_imm_shamt_op(dec_insn, $op_fn);
267
268                Ok(false)
269            }
270        }
271    };
272}
273
274macro_rules! make_alu_op_fns {
275    ($name:ident, $op_fn:expr) => {
276        make_alu_op_reg_fn! {$name, $op_fn}
277        make_alu_op_imm_fn! {$name, $op_fn}
278    };
279}
280
281macro_rules! make_shift_op_fns {
282    ($name:ident, $op_fn:expr) => {
283        make_alu_op_reg_fn! {$name, $op_fn}
284        make_alu_op_imm_shamt_fn! {$name, $op_fn}
285    };
286}
287
288macro_rules! make_branch_op_fn {
289    ($name:ident, $cond_fn:expr) => {
290        paste! {
291            fn [<process_ $name>](
292                &mut self,
293                dec_insn: instruction_formats::BType
294            ) -> Self::InstructionResult {
295                Ok(self.execute_branch(dec_insn, $cond_fn))
296            }
297        }
298    };
299}
300
301macro_rules! make_load_op_fn_inner {
302    ($name:ident, $size:ty, $signed: expr) => {
303        paste! {
304            fn [<process_ $name>](
305                &mut self,
306                dec_insn: instruction_formats::IType
307            ) -> Self::InstructionResult {
308                self.execute_load(dec_insn, $size, $signed)?;
309
310                Ok(false)
311            }
312        }
313    };
314}
315
316macro_rules! make_load_op_fn {
317    ($name:ident, $size:ty, signed) => {
318        make_load_op_fn_inner! {$name, $size, true}
319    };
320    ($name:ident, $size:ty, unsigned) => {
321        make_load_op_fn_inner! {$name, $size, false}
322    };
323}
324
325macro_rules! make_store_op_fn {
326    ($name:ident, $size:ty) => {
327        paste! {
328            fn [<process_ $name>](
329                &mut self,
330                dec_insn: instruction_formats::SType
331            ) -> Self::InstructionResult {
332                self.execute_store(dec_insn, $size)?;
333
334                Ok(false)
335            }
336        }
337    };
338}
339
340impl<'a, M: Memory> InstructionProcessor for InstructionExecutor<'a, M> {
341    /// Result is `Ok` when instruction execution is successful. `Ok(true) indicates the
342    /// instruction updated the PC and Ok(false) indicates it did not (so the PC must be
343    /// incremented to execute the next instruction).
344    type InstructionResult = Result<bool, InstructionException>;
345
346    make_alu_op_fns! {add, |a, b| a.wrapping_add(b)}
347    make_alu_op_reg_fn! {sub, |a, b| a.wrapping_sub(b)}
348    make_alu_op_fns! {slt, |a, b| if (a as i32) < (b as i32) {1} else {0}}
349    make_alu_op_fns! {sltu, |a, b| if a < b {1} else {0}}
350    make_alu_op_fns! {or, |a, b| a | b}
351    make_alu_op_fns! {and, |a, b| a & b}
352    make_alu_op_fns! {xor, |a, b| a ^ b}
353
354    make_shift_op_fns! {sll, |a, b| a << (b & 0x1f)}
355    make_shift_op_fns! {srl, |a, b| a >> (b & 0x1f)}
356    make_shift_op_fns! {sra, |a, b| ((a as i32) >> (b & 0x1f)) as u32}
357
358    fn process_lui(&mut self, dec_insn: instruction_formats::UType) -> Self::InstructionResult {
359        self.hart_state
360            .write_register(dec_insn.rd, dec_insn.imm as u32);
361
362        Ok(false)
363    }
364
365    fn process_auipc(&mut self, dec_insn: instruction_formats::UType) -> Self::InstructionResult {
366        let result = self.hart_state.pc.wrapping_add(dec_insn.imm as u32);
367        self.hart_state.write_register(dec_insn.rd, result);
368
369        Ok(false)
370    }
371
372    make_branch_op_fn! {beq, |a, b| a == b}
373    make_branch_op_fn! {bne, |a, b| a != b}
374    make_branch_op_fn! {blt, |a, b|  (a as i32) < (b as i32)}
375    make_branch_op_fn! {bltu, |a, b| a < b}
376    make_branch_op_fn! {bge, |a, b|  (a as i32) >= (b as i32)}
377    make_branch_op_fn! {bgeu, |a, b| a >= b}
378
379    make_load_op_fn! {lb, MemAccessSize::Byte, signed}
380    make_load_op_fn! {lbu, MemAccessSize::Byte, unsigned}
381    make_load_op_fn! {lh, MemAccessSize::HalfWord, signed}
382    make_load_op_fn! {lhu, MemAccessSize::HalfWord, unsigned}
383    make_load_op_fn! {lw, MemAccessSize::Word, unsigned}
384
385    make_store_op_fn! {sb, MemAccessSize::Byte}
386    make_store_op_fn! {sh, MemAccessSize::HalfWord}
387    make_store_op_fn! {sw, MemAccessSize::Word}
388
389    fn process_jal(&mut self, dec_insn: instruction_formats::JType) -> Self::InstructionResult {
390        let target_pc = self.hart_state.pc.wrapping_add(dec_insn.imm as u32);
391        self.hart_state
392            .write_register(dec_insn.rd, self.hart_state.pc + 4);
393        self.hart_state.pc = target_pc;
394
395        Ok(true)
396    }
397
398    fn process_jalr(&mut self, dec_insn: instruction_formats::IType) -> Self::InstructionResult {
399        let mut target_pc = self
400            .hart_state
401            .read_register(dec_insn.rs1)
402            .wrapping_add(dec_insn.imm as u32);
403        target_pc &= 0xfffffffe;
404
405        self.hart_state
406            .write_register(dec_insn.rd, self.hart_state.pc + 4);
407        self.hart_state.pc = target_pc;
408
409        Ok(true)
410    }
411
412    make_alu_op_reg_fn! {mul, |a, b| a.wrapping_mul(b)}
413    make_alu_op_reg_fn! {mulh, |a, b| (sign_extend_u32(a).wrapping_mul(sign_extend_u32(b)) >> 32) as u32}
414    make_alu_op_reg_fn! {mulhu, |a, b| (((a as u64).wrapping_mul(b as u64)) >> 32) as u32}
415    make_alu_op_reg_fn! {mulhsu, |a, b| (sign_extend_u32(a).wrapping_mul(b as i64) >> 32) as u32}
416
417    make_alu_op_reg_fn! {div, |a, b| if b == 0 {u32::MAX} else {((a as i32).wrapping_div(b as i32)) as u32}}
418    make_alu_op_reg_fn! {divu, |a, b| if b == 0 {u32::MAX} else {a / b}}
419    make_alu_op_reg_fn! {rem, |a, b| if b == 0 {a} else {((a as i32).wrapping_rem(b as i32)) as u32}}
420    make_alu_op_reg_fn! {remu, |a, b| if b == 0 {a} else {a % b}}
421
422    fn process_fence(&mut self, _dec_insn: instruction_formats::IType) -> Self::InstructionResult {
423        Ok(false)
424    }
425}