simple_risc/simple_risc.rs
1//! An example system of an extremely simplified RISC
2//! (Reduced Instruction Set Computer) microcontroller
3//! with toy machine code, forming a system that
4//! can be verified by machine-check.
5//!
6//! Only the things specific to the microcontroller will
7//! be commented here. See the example "counter"
8//! for a basic description of a machine-check system.
9//!
10//! Some of the properties that hold and are verifiable:
11//! - Register 1 is set to 1 before reaching the start
12//! of the main loop:
13//! `AF![reg[1] == 1 && as_unsigned(pc) < 3]`
14//! - It is always possible to reach program location 9:
15//! `AG![EF![pc == 9]]`
16//! (use the decay strategy when verifying this)
17//! - Program locations above 9 are never reached.
18//! `AG![as_unsigned(pc) <= 9]`
19//! (use the decay strategy when verifying this)
20
21#[machine_check::machine_description]
22mod machine_module {
23 use ::machine_check::{Bitvector, BitvectorArray};
24 use ::std::{
25 clone::Clone,
26 cmp::{Eq, PartialEq},
27 fmt::Debug,
28 hash::Hash,
29 };
30
31 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
32 pub struct Input {
33 // Proper inputs to this microcontroller
34 // are addresses that can be read.
35 gpio_read: BitvectorArray<4, 8>,
36 // Registers and data are uninitialized
37 // at microcontroller reset, which corresponds to
38 // finite-state machine init.
39 uninit_reg: BitvectorArray<2, 8>,
40 uninit_data: BitvectorArray<8, 8>,
41 }
42 impl ::machine_check::Input for Input {}
43
44 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
45 pub struct State {
46 // Microcontroller program counter.
47 // Stores the address of instruction
48 // in program memory that will be executed next.
49 pc: Bitvector<7>,
50 // Four (2^2) 8-bit working registers.
51 reg: BitvectorArray<2, 8>,
52 // 256 (2^8) 8-bit data cells.
53 data: BitvectorArray<8, 8>,
54 }
55 impl ::machine_check::State for State {}
56 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
57 pub struct System {
58 // 128 (2^7) 12-bit program memory instructions.
59 pub progmem: BitvectorArray<7, 12>,
60 }
61 impl ::machine_check::Machine for System {
62 type Input = Input;
63 type State = State;
64
65 fn init(&self, input: &Input) -> State {
66 // Only initialize Program Counter to 0 at reset.
67 // Leave working registers and data uninitialized.
68 State {
69 pc: Bitvector::<7>::new(0),
70 reg: Clone::clone(&input.uninit_reg),
71 data: Clone::clone(&input.uninit_data),
72 }
73 }
74 fn next(&self, state: &State, input: &Input) -> State {
75 // Fetch the instruction to execute from program memory.
76 let instruction = self.progmem[state.pc];
77 // Increment the program counter.
78 let mut pc = state.pc + Bitvector::<7>::new(1);
79 // Clone registers and data.
80 let mut reg = Clone::clone(&state.reg);
81 let mut data = Clone::clone(&state.data);
82
83 // Perform instruction-specific behaviour.
84 ::machine_check::bitmask_switch!(instruction {
85 "00dd_00--_aabb" => { // add
86 reg[d] = reg[a] + reg[b];
87 }
88 "00dd_01--_gggg" => { // read input
89 reg[d] = input.gpio_read[g];
90 }
91 "00rr_1kkk_kkkk" => { // jump if bit 0 is set
92 if reg[r] & Bitvector::<8>::new(1)
93 == Bitvector::<8>::new(1) {
94 pc = k;
95 };
96 }
97 "01dd_kkkk_kkkk" => { // load immediate
98 reg[d] = k;
99 }
100 "10dd_nnnn_nnnn" => { // load direct
101 reg[d] = data[n];
102 }
103 "11ss_nnnn_nnnn" => { // store direct
104 data[n] = reg[s];
105 }
106 });
107
108 // Return the state.
109 State { pc, reg, data }
110 }
111 }
112}
113
114use machine_check::{Bitvector, BitvectorArray};
115
116fn main() {
117 let toy_program = [
118 // (0) set r0 to zero
119 Bitvector::new(0b0100_0000_0000),
120 // (1) set r1 to one
121 Bitvector::new(0b0101_0000_0001),
122 // (2) set r2 to zero
123 Bitvector::new(0b0110_0000_0000),
124 // --- main loop ---
125 // (3) store r1 content to data location 0
126 Bitvector::new(0b1100_0000_0000),
127 // (4) store r2 content to data location 1
128 Bitvector::new(0b1100_0000_0001),
129 // (5) read input location 0 to r3
130 Bitvector::new(0b0011_0100_0000),
131 // (6) jump to (3) if r3 bit 0 is set
132 Bitvector::new(0b0011_1000_0011),
133 // (7) increment r2
134 Bitvector::new(0b0010_0000_1001),
135 // (8) store r2 content to data location 1
136 Bitvector::new(0b1110_0000_0001),
137 // (9) jump to (3)
138 Bitvector::new(0b0001_1000_0011),
139 ];
140
141 // load toy program to program memory, filling unused locations with 0
142 let mut progmem = BitvectorArray::new_filled(Bitvector::new(0));
143 for (index, instruction) in toy_program.into_iter().enumerate() {
144 progmem[Bitvector::new(index as u64)] = instruction;
145 }
146 let system = machine_module::System { progmem };
147 machine_check::run(system);
148}