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 _ => return Err(anyhow!("illegal instruction (A): {}", instruction)),
91 });
92 }
93 _ => return Err(anyhow!("illegal instruction (B): {}", instruction)),
94 }
95
96 if next_pc & 3 != 0 {
97 return Err(anyhow!("misaligned, address: {}", next_pc,));
98 }
99 if let Some(value) = result {
100 self.set_register(rd, value);
101 }
102 self.pc = next_pc;
103 Ok(())
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 const OPCODE_R: u32 = 0b0110011;
112
113 // encodes R-type instruction
114 fn encode_r(name: &str, rd: u32, rs1: u32, rs2: u32) -> u32 {
115 let (funct7, funct3) = match name {
116 "add" => (0b0000000, 0b000),
117 "sub" => (0b0100000, 0b000),
118 "sll" => (0b0000000, 0b001),
119 "slt" => (0b0000000, 0b010),
120 "sltu" => (0b0000000, 0b011),
121 "xor" => (0b0000000, 0b100),
122 "srl" => (0b0000000, 0b101),
123 "sra" => (0b0100000, 0b101),
124 "or" => (0b0000000, 0b110),
125 "and" => (0b0000000, 0b111),
126 _ => panic!("unknown R-type instruction: {name}"),
127 };
128
129 (funct7 << 25) | (rs2 << 20) | (rs1 << 15) | (funct3 << 12) | (rd << 7) | OPCODE_R
130 }
131
132 #[test]
133 fn test_r_type_operations() {
134 let test_vectors = [
135 // (op, rs1, rs2, rd)
136 ("add", 10, 20, 30),
137 ("add", u32::MAX, 1, 0), // overflow
138 ("sub", 30, 20, 10),
139 ("sub", 0, 1, u32::MAX), // overflow
140 ("sll", 1, 4, 16),
141 ("sll", 1, 31, 0x8000_0000), // SLL maximum shift
142 ("sll", 1, 32, 1), // SLL shift wrapping
143 ("slt", (-10i32) as u32, 20, 1),
144 ("sltu", 10, 20, 1),
145 ("slt", 0xffff_ffff, 0, 1), // Signed comparison: -1 < 0
146 ("slt", 0xffff_ffff, 0, 1), // Signed vs unsigned distinction
147 ("sltu", 0xffff_ffff, 0, 0),
148 ("xor", 0b1010, 0b1100, 0b0110),
149 ("srl", 0x8000_0000, 4, 0x0800_0000),
150 ("srl", 0x8000_0000, 1, 0x4000_0000), // Logical shift should zero-fill
151 ("sra", 0x8000_0000, 4, 0xf800_0000),
152 ("sra", 0x8000_0000, 1, 0xc000_0000), // Arithmetic shift should sign-fill
153 ("sra", 0xffff_ffff, 31, 0xffff_ffff), // Arithmetic shift of -1 stays -1
154 ("or", 0b1010, 0b1100, 0b1110),
155 ("and", 0b1010, 0b1100, 0b1000),
156 ];
157
158 for (name, rs1_value, rs2_value, expected) in test_vectors {
159 let rd = 3;
160 let rs1 = 1;
161 let rs2 = 2;
162
163 let instruction = encode_r(name, rd, rs1, rs2);
164
165 let mut cpu = RV32::new(instruction.to_le_bytes().to_vec());
166
167 cpu.regs[rs1 as usize] = rs1_value;
168 cpu.regs[rs2 as usize] = rs2_value;
169
170 cpu.step().unwrap();
171
172 assert_eq!(
173 cpu.regs[rd as usize], expected,
174 "{name} failed:
175 rs1={rs1_value:#010x}, rs2={rs2_value:#010x},
176 expected={expected:#010x}, got={:#010x}",
177 cpu.regs[rd as usize],
178 );
179 }
180 }
181}