rv32-rs/src/lib.rs
src/lib.rsRaw
1use anyhow::{anyhow, Result};
2
3pub struct RV32 {
4 regs: [u32; 32], // registers
5 mem: Vec<u8>, // memory
6 pc: u32,
7}
8
9impl RV32 {
10 pub fn new(mem: Vec<u8>) -> Self {
11 Self {
12 regs: [0; 32],
13 pc: 0,
14 mem,
15 }
16 }
17 pub fn set_register(&mut self, index: usize, value: u32) {
18 assert!(index < 32, "register index must <32");
19 if index != 0 {
20 self.regs[index] = value;
21 }
22 }
23 // validate the access
24 fn range(&self, address: u32, size: usize) -> Result<std::ops::Range<usize>> {
25 if !address.is_multiple_of(size as u32) {
26 return Err(anyhow!("misaligned at address: {}", address));
27 }
28 let start = address as usize;
29 let end = start
30 .checked_add(size)
31 .filter(|&end| end <= self.mem.len())
32 .ok_or(anyhow!("access fault, address: {}", address))?;
33 Ok(start..end)
34 }
35 fn read(&self, address: u32, size: usize) -> Result<u32> {
36 let range = self.range(address, size)?;
37 let mut bytes = [0; 4];
38 bytes[..size].copy_from_slice(&self.mem[range]);
39 Ok(u32::from_le_bytes(bytes))
40 }
41 pub fn step(&mut self) -> Result<()> {
42 let instruction = self.read(self.pc, 4)?;
43 let next_pc = self.pc.wrapping_add(4);
44
45 // R-type instruction:
46 // R-type:
47 // 31 25 24 20 19 15 14 12 11 7 6 0
48 // ┌───────────┬────────────┬────────────┬────────┬────────┬─────────┐
49 // │ funct7 │ rs2 │ rs1 │ funct3 │ rd │ opcode │
50 // └───────────┴────────────┴────────────┴────────┴────────┴─────────┘
51 let rd = ((instruction >> 7) & 31) as usize;
52 let funct3 = (instruction >> 12) & 7;
53 let rs1 = ((instruction >> 15) & 31) as usize;
54 let rs2 = ((instruction >> 20) & 31) as usize;
55 let funct7 = instruction >> 25;
56
57 let a = self.regs[rs1];
58 let b = self.regs[rs2];
59 let result: Option<u32>;
60
61 // 0x7f=0111_1111, match the lowest 7 bits to read the opcode
62 match instruction & 0x7f {
63 // R-type (register-register) integer operations
64 0x33 => {
65 result = Some(match (funct7, funct3) {
66 // ADD: add rs1 and rs2, wrapping to 32 bits
67 (0, 0) => a.wrapping_add(b),
68 // SUB: subtract rs2 from rs1, wrapping to 32 bits
69 (0x20, 0) => a.wrapping_sub(b),
70 // SLL: shift rs1 left by the low five bits of rs2
71 (0, 1) => a << (b & 31),
72 // SLT: set rd to one if rs1 is less than rs2 as signed
73 // values, else zero
74 (0, 2) => u32::from((a as i32) < (b as i32)),
75 // SLTU: set rd to one if rs1 is less than rs2 as unsigned
76 // values, else zero
77 (0, 3) => u32::from(a < b),
78 // XOR: write the bitwise XOR of rs1 and rs2 into rd
79 (0, 4) => a ^ b,
80 // SRL: shift rs1 right by the low five bits of rs2, filling
81 // with zeros
82 (0, 5) => a >> (b & 31),
83 // SRA: shift rs1 right by the low five bits of rs2,
84 // preserving its sign
85 (0x20, 5) => ((a as i32) >> (b & 31)) as u32,
86 // OR: write the bitwise OR of rs1 and rs2 into rd
87 (0, 6) => a | b,
88 // AND: write the bitwise AND of rs1 and rs2 into rd
89 (0, 7) => a & b,
90
91 // Note: MUL,DIV,REM are not part of RV32I, but they are in
92 // the M extension.
93
94 // MUL: write the low 32 bits of the product of rs1 and rs2
95 (1, 0) => a.wrapping_mul(b),
96 // DIV: signed quotient toward zero; zero divisors return
97 // all ones
98 (1, 4) => {
99 if b == 0 {
100 u32::MAX
101 } else {
102 (a as i32).wrapping_div(b as i32) as u32
103 }
104 }
105 // REM: signed remainder; zero divisors return the dividend
106 (1, 6) => {
107 if b == 0 {
108 a
109 } else {
110 (a as i32).wrapping_rem(b as i32) as u32
111 }
112 }
113
114 _ => return Err(anyhow!("illegal instruction (A): {}", instruction)),
115 });
116 }
117 _ => return Err(anyhow!("illegal instruction (B): {}", instruction)),
118 }
119
120 if next_pc & 3 != 0 {
121 return Err(anyhow!("misaligned, address: {}", next_pc,));
122 }
123 if let Some(value) = result {
124 self.set_register(rd, value);
125 }
126 self.pc = next_pc;
127 Ok(())
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 const OPCODE_R: u32 = 0b0110011;
136
137 // encodes R-type instruction
138 fn encode_r(name: &str, rd: u32, rs1: u32, rs2: u32) -> u32 {
139 let (funct7, funct3) = match name {
140 "add" => (0b0000000, 0b000),
141 "sub" => (0b0100000, 0b000),
142 "sll" => (0b0000000, 0b001),
143 "slt" => (0b0000000, 0b010),
144 "sltu" => (0b0000000, 0b011),
145 "xor" => (0b0000000, 0b100),
146 "srl" => (0b0000000, 0b101),
147 "sra" => (0b0100000, 0b101),
148 "or" => (0b0000000, 0b110),
149 "and" => (0b0000000, 0b111),
150 // M extension
151 "mul" => (0b0000001, 0b000),
152 "div" => (0b0000001, 0b100),
153 "rem" => (0b0000001, 0b110),
154 _ => panic!("unknown R-type instruction: {name}"),
155 };
156
157 (funct7 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | OPCODE_R
158 }
159
160 #[test]
161 fn test_ops_r_type() {
162 let test_vectors = vec![
163 // (op, rs1, rs2, rd)
164 ("add", 10, 20, 30),
165 ("add", u32::MAX, 1, 0), // overflow
166 ("sub", 30, 20, 10),
167 ("sub", 0, 1, u32::MAX), // overflow
168 ("sll", 1, 4, 16),
169 ("sll", 1, 31, 0x8000_0000), // SLL maximum shift
170 ("sll", 1, 32, 1), // SLL shift wrapping
171 ("slt", (-10i32) as u32, 20, 1),
172 ("sltu", 10, 20, 1),
173 ("slt", 0xffff_ffff, 0, 1), // signed comparison: -1 < 0
174 ("slt", 0xffff_ffff, 0, 1), // signed vs unsigned distinction
175 ("sltu", 0xffff_ffff, 0, 0),
176 ("xor", 0b1010, 0b1100, 0b0110),
177 ("srl", 0x8000_0000, 4, 0x0800_0000),
178 ("srl", 0x8000_0000, 1, 0x4000_0000), // logical shift should zero-fill
179 ("sra", 0x8000_0000, 4, 0xf800_0000),
180 ("sra", 0x8000_0000, 1, 0xc000_0000), // arithmetic shift should sign-fill
181 ("sra", 0xffff_ffff, 31, 0xffff_ffff), // arithmetic shift of -1 stays -1
182 ("or", 0b1010, 0b1100, 0b1110),
183 ("and", 0b1010, 0b1100, 0b1000),
184 ];
185 test_ops_r_types_opt(test_vectors);
186 }
187 #[test]
188 fn test_ops_m_extension() {
189 let test_vectors = vec![
190 ("mul", 10, 20, 200),
191 ("mul", 0, 123, 0),
192 ("mul", 1, u32::MAX, u32::MAX),
193 ("mul", u32::MAX, 2, u32::MAX - 1), // result wraps to lower 32 bits
194 ("mul", (-1i32) as u32, 10, (-10i32) as u32), // -1 * 10 = -10
195 ("mul", (-1i32) as u32, (-1i32) as u32, 1), // -1 * -1 = 1
196 ("mul", 0x8000_0000, 2, 0), // INT_MIN * 2 = 0 modulo 2^32
197 ("mul", 0x8000_0000, 0xffff_ffff, 0x8000_0000), // INT_MIN * -1 also wraps to INT_MIN
198 ("div", 20, 5, 4),
199 ("div", 21, 5, 4), // truncates toward zero
200 ("div", 0, 10, 0),
201 ("div", 10, 1, 10),
202 ("div", 10, (-1i32) as u32, (-10i32) as u32),
203 ("div", (-20i32) as u32, 5, (-4i32) as u32), // negative dividend
204 ("div", 20, (-5i32) as u32, (-4i32) as u32), // negative divisor
205 ("div", (-20i32) as u32, (-5i32) as u32, 4), // negative / negative
206 ("div", (-21i32) as u32, 5, (-4i32) as u32), // truncation toward zero: -21 / 5 = -4
207 // RISC-V special case:
208 ("div", 0x8000_0000, 0xffff_ffff, 0x8000_0000), // INT_MIN / -1 = INT_MIN (overflow)
209 ("div", 10, 0, u32::MAX), // division by zero returns -1 for signed DIV
210 ("div", 0x8000_0000, 0, u32::MAX),
211 ("rem", 20, 5, 0),
212 ("rem", 21, 5, 1),
213 ("rem", 0, 10, 0),
214 ("rem", 10, 1, 0),
215 ("rem", 23, 5, 3), // Positive remainder.
216 ("rem", (-23i32) as u32, 5, (-3i32) as u32), // Remainder follows the sign of the dividend.
217 ("rem", 23, (-5i32) as u32, 3), // Negative divisor does not change the sign of the remainder.
218 ("rem", (-23i32) as u32, (-5i32) as u32, (-3i32) as u32), // Negative / negative.
219 ("rem", 3, 5, 3), // Dividend smaller than divisor.
220 // RISC-V special case:
221 ("rem", 0x8000_0000, 0xffff_ffff, 0), // INT_MIN % -1 = 0.
222 ("rem", 123, 0, 123), // Remainder by zero returns the dividend.
223 ("rem", u32::MAX, 0, u32::MAX),
224 ("rem", 0x8000_0000, 0, 0x8000_0000),
225 ];
226 test_ops_r_types_opt(test_vectors);
227 }
228 fn test_ops_r_types_opt(test_vectors: Vec<(&str, u32, u32, u32)>) {
229 for (name, rs1_value, rs2_value, expected) in test_vectors {
230 let rd = 3;
231 let rs1 = 1;
232 let rs2 = 2;
233
234 let instruction = encode_r(name, rd, rs1, rs2);
235
236 let mut cpu = RV32::new(instruction.to_le_bytes().to_vec());
237
238 cpu.regs[rs1 as usize] = rs1_value;
239 cpu.regs[rs2 as usize] = rs2_value;
240
241 cpu.step().unwrap();
242
243 assert_eq!(
244 cpu.regs[rd as usize], expected,
245 "{name} failed:
246 rs1={rs1_value:#010x}, rs2={rs2_value:#010x},
247 expected={expected:#010x}, got={:#010x}",
248 cpu.regs[rd as usize],
249 );
250 }
251 }
252}