lib_rv32_mcu/
memory.rs

1use std::{fs, path::Path};
2
3use log::info;
4use serde::{Deserialize, Serialize};
5
6pub use lib_rv32_isa::traits::Memory as MemoryTrait;
7use lib_rv32_isa::{common::bit_slice, RiscvError};
8
9/// Heap allocated, little-endian implementation of memory.
10#[derive(Clone, Serialize, Deserialize)]
11pub struct Memory {
12    pub size: usize,
13    mem: Vec<u8>,
14}
15
16impl Memory {
17    /// Allocate a memory with the given size.
18    pub fn new(size: usize) -> Self {
19        assert!(size % 4 == 0);
20        assert!(size > 0);
21
22        Memory {
23            size,
24            mem: vec![0; size],
25        }
26    }
27
28    /// Read a little-endian number from a byte-vector of arbitrary size.
29    fn read(&self, base: usize, size: usize, log: bool) -> Result<u32, RiscvError> {
30        // Check if read falls on a word, half-word, or byte boundary.
31        if base % size != 0 {
32            return Err(RiscvError::MemoryAlignmentError(base as u32));
33        // Check that the read is within bounds.
34        } else if base >= self.size as usize {
35            return Err(RiscvError::MemoryOutOfBoundsError(base as u32));
36        }
37
38        let data = self.mem[base..base + size]
39            .iter()
40            .enumerate()
41            .map(|(i, b)| ((*b as u32) << (i * 8)) as u32)
42            .sum();
43
44        if log {
45            match size {
46                1 => info!("(byte *)0x{:08x} = 0x{:x} ({})", base, data, data as i32),
47                2 => info!(
48                    "(half-word *)0x{:08x} = 0x{:x} ({})",
49                    base, data, data as i32
50                ),
51                4 => info!("(word *)0x{:08x} = 0x{:x} ({})", base, data, data as i32),
52                _ => (),
53            }
54        }
55
56        Ok(data)
57    }
58
59    /// Write a little-endian number of arbitrary size.
60    fn write(&mut self, base: usize, data: u32, size: usize, log: bool) -> Result<(), RiscvError> {
61        if log {
62            match size {
63                1 => info!("(byte *)0x{:08x} <- 0x{:x} ({})", base, data, data as i32),
64                2 => info!(
65                    "(half-word *)0x{:08x} <- 0x{:x} ({})",
66                    base, data, data as i32
67                ),
68                4 => info!("(word *)0x{:08x} <- 0x{:x} ({})", base, data, data as i32),
69                _ => (),
70            }
71        }
72
73        // Check if read falls on a word, half-word, or byte boundary.
74        if base % size != 0 {
75            return Err(RiscvError::MemoryAlignmentError(base as u32));
76        // Check that the read is within bounds.
77        } else if base >= self.size as usize {
78            return Err(RiscvError::MemoryOutOfBoundsError(base as u32));
79        }
80
81        for (i, b) in self.mem[base..base + size].iter_mut().enumerate() {
82            *b = bit_slice!(data, 8 * (i + 1), 8 * i) as u8;
83        }
84
85        Ok(())
86    }
87
88    /// Program the memory from a vector of little-endian bytes.
89    pub fn program_le_bytes(&mut self, bytes: &[u8]) -> Result<(), RiscvError> {
90        for (word_addr, chunk) in bytes.chunks(4).enumerate() {
91            for (byte_offset, byte) in chunk.iter().enumerate() {
92                if let Err(why) = self.write(word_addr * 4 + byte_offset, *byte as u32, 1, false) {
93                    return Err(why);
94                }
95            }
96        }
97        Ok(())
98    }
99
100    /// Program the memory from a vector of words.
101    pub fn program_words(&mut self, words: &[u32]) -> Result<(), RiscvError> {
102        for (addr, word) in words.iter().enumerate() {
103            if let Err(why) = self.write(addr * 4, *word as u32, 4, false) {
104                return Err(why);
105            }
106        }
107        Ok(())
108    }
109
110    /// Program the memory from a binary file generally created by gcc or clang.
111    pub fn program_from_file(&mut self, path: &Path) -> Result<u32, RiscvError> {
112        let prog_bytes = fs::read(&path).expect("Could not read binary.");
113        match self.program_le_bytes(&prog_bytes) {
114            Err(why) => Err(why),
115            Ok(_) => Ok(prog_bytes.len() as u32),
116        }
117    }
118}
119
120// Implement the trait that allows us to execute instructions on this memory.
121impl MemoryTrait for Memory {
122    fn fetch(&self, pc: u32) -> Result<u32, RiscvError> {
123        self.read(pc as usize, 4, false)
124    }
125
126    fn read_word(&self, addr: u32) -> Result<u32, RiscvError> {
127        self.read(addr as usize, 4, true)
128    }
129
130    fn read_half_word(&self, addr: u32) -> Result<u32, RiscvError> {
131        match self.read(addr as usize, 2, true) {
132            Ok(d) => Ok(d),
133            Err(why) => Err(why),
134        }
135    }
136    fn read_byte(&self, addr: u32) -> Result<u32, RiscvError> {
137        match self.read(addr as usize, 1, true) {
138            Ok(d) => Ok(d),
139            Err(why) => Err(why),
140        }
141    }
142
143    fn write_word(&mut self, addr: u32, data: u32) -> Result<(), RiscvError> {
144        self.write(addr as usize, data, 4, true)
145    }
146
147    fn write_half_word(&mut self, addr: u32, data: u32) -> Result<(), RiscvError> {
148        self.write(addr as usize, data as u32, 2, true)
149    }
150
151    fn write_byte(&mut self, addr: u32, data: u32) -> Result<(), RiscvError> {
152        self.write(addr as usize, data as u32, 1, true)
153    }
154}
155
156#[cfg(test)]
157mod test {
158    use super::*;
159
160    #[test]
161    #[should_panic]
162    fn test_create_misaligned() {
163        let _ = Memory::new(3);
164    }
165
166    #[test]
167    #[should_panic]
168    fn test_create_zero() {
169        let _ = Memory::new(0);
170    }
171
172    #[test]
173    fn test_out_of_bounds() {
174        let mem = Memory::new(1024);
175        match mem.read_byte(1028) {
176            Err(why) => assert_eq!(why, RiscvError::MemoryOutOfBoundsError(1028)),
177            _ => panic!(),
178        };
179    }
180
181    #[test]
182    fn test_misaligned() {
183        let mut mem = Memory::new(1024);
184
185        match mem.read_half_word(3) {
186            Err(why) => assert_eq!(why, RiscvError::MemoryAlignmentError(3)),
187            _ => panic!(),
188        };
189        match mem.read_word(2) {
190            Err(why) => assert_eq!(why, RiscvError::MemoryAlignmentError(2)),
191            _ => panic!(),
192        };
193        match mem.write_half_word(3, 0) {
194            Err(why) => assert_eq!(why, RiscvError::MemoryAlignmentError(3)),
195            _ => panic!(),
196        };
197        match mem.write_word(2, 0) {
198            Err(why) => assert_eq!(why, RiscvError::MemoryAlignmentError(2)),
199            _ => panic!(),
200        };
201    }
202
203    #[test]
204    fn test_create() {
205        let mem = Memory::new(1024);
206        assert_eq!(1024, mem.size);
207    }
208
209    #[test]
210    fn test_byte() {
211        let mut mem = Memory::new(1024);
212
213        for data in 0..0xFF {
214            for addr in 0..16 {
215                mem.write_byte(addr, data).unwrap();
216                assert_eq!(data, mem.read_byte(addr).unwrap());
217            }
218        }
219    }
220
221    #[test]
222    fn test_half_word_write() {
223        const ADDR: u32 = 0x02;
224        let mut mem = Memory::new(1024);
225
226        mem.write_half_word(ADDR, 0x1712).unwrap();
227
228        // Is it little-endian?
229        assert_eq!(mem.mem[ADDR as usize], 0x12);
230        assert_eq!(mem.mem[(ADDR + 1) as usize], 0x17);
231    }
232
233    #[test]
234    fn test_half_word_read() {
235        const ADDR: u32 = 0x02;
236        let mut mem = Memory::new(1024);
237
238        // mem[ADDR] = 0x1712;
239        mem.mem[ADDR as usize] = 0x12;
240        mem.mem[(ADDR + 1) as usize] = 0x17;
241
242        assert_eq!(0x1712, mem.read_half_word(ADDR).unwrap());
243    }
244
245    #[test]
246    fn test_half_word_read_write() {
247        const ADDR: u32 = 0x02;
248        let mut mem = Memory::new(1024);
249        for data in 0..0xFFFF {
250            mem.write_half_word(ADDR, data).unwrap();
251            assert_eq!(data, mem.read_half_word(ADDR).unwrap());
252        }
253    }
254
255    #[test]
256    fn test_word_write() {
257        const ADDR: u32 = 0x04;
258        let mut mem = Memory::new(1024);
259
260        mem.write_word(ADDR, 0x76821712).unwrap();
261
262        // Is it little-endian?
263        assert_eq!(mem.mem[ADDR as usize], 0x12);
264        assert_eq!(mem.mem[(ADDR + 1) as usize], 0x17);
265        assert_eq!(mem.mem[(ADDR + 2) as usize], 0x82);
266        assert_eq!(mem.mem[(ADDR + 3) as usize], 0x76);
267    }
268
269    #[test]
270    fn test_word_read() {
271        const ADDR: u32 = 0x04;
272        let mut mem = Memory::new(1024);
273
274        // mem[ADDR] = 0x1712;
275        mem.mem[ADDR as usize] = 0x12;
276        mem.mem[(ADDR + 1) as usize] = 0x17;
277        mem.mem[(ADDR + 2) as usize] = 0x82;
278        mem.mem[(ADDR + 3) as usize] = 0x76;
279
280        assert_eq!(0x76821712, mem.read_word(ADDR).unwrap());
281    }
282
283    #[test]
284    fn test_word_read_write() {
285        const ADDR: u32 = 0x04;
286        let mut mem = Memory::new(1024);
287        for data in 0xFE000000..0xFE100000 {
288            mem.write_word(ADDR, data).unwrap();
289            assert_eq!(data, mem.read_word(ADDR).unwrap());
290        }
291    }
292
293    #[test]
294    fn test_program_little_endian() {
295        const NUM: u32 = 0x12345678;
296        const LE_BYTES: [u8; 4] = [0x78, 0x56, 0x34, 0x12];
297
298        let mut mem = Memory::new(1024);
299        mem.program_le_bytes(&LE_BYTES).unwrap();
300
301        assert_eq!(NUM, mem.read_word(0).unwrap());
302    }
303}