1#![allow(clippy::upper_case_acronyms)]
6
7pub use cameleon_impl_macros::{memory, register_map};
8
9use thiserror::Error;
10
11pub type MemoryResult<T> = std::result::Result<T, MemoryError>;
12
13#[derive(Debug, Error)]
14pub enum MemoryError {
15 #[error("attempt to read unreadable address")]
16 AddressNotReadable,
17
18 #[error("attempt to write to unwritable address")]
19 AddressNotWritable,
20
21 #[error("attempt to access non-existent memory location")]
22 InvalidAddress,
23
24 #[error("invalid register data: {0}")]
25 InvalidRegisterData(std::borrow::Cow<'static, str>),
26}
27
28pub mod prelude {
29 pub use super::{MemoryRead, MemoryWrite, Register};
30}
31
32pub trait MemoryRead {
33 fn read_raw(&self, range: std::ops::Range<usize>) -> MemoryResult<&[u8]>;
34
35 fn access_right<T: Register>(&self) -> AccessRight;
36
37 fn read<T: Register>(&self) -> MemoryResult<T::Ty>;
40}
41
42pub trait MemoryWrite {
43 fn write_raw(&mut self, addr: usize, buf: &[u8]) -> MemoryResult<()>;
44
45 fn write<T: Register>(&mut self, data: T::Ty) -> MemoryResult<()>;
48
49 fn set_access_right<T: Register>(&mut self, access_right: AccessRight);
50
51 fn register_observer<T, U>(&mut self, observer: U)
52 where
53 T: Register,
54 U: MemoryObserver + 'static;
55}
56
57pub trait MemoryObserver: Send {
58 fn update(&self);
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub enum AccessRight {
64 NA,
66 RO,
68 WO,
70 RW,
72}
73
74impl AccessRight {
75 #[must_use]
76 pub const fn is_readable(self) -> bool {
77 self.as_num() & 0b1 == 1
78 }
79
80 #[must_use]
81 pub const fn is_writable(self) -> bool {
82 self.as_num() >> 1_i32 == 1
83 }
84
85 #[must_use]
86 pub const fn as_str(self) -> &'static str {
87 match self {
88 Self::NA => "NA",
89 Self::RO => "RO",
90 Self::WO => "WO",
91 Self::RW => "RW",
92 }
93 }
94
95 #[doc(hidden)]
96 #[must_use]
97 pub const fn as_num(self) -> u8 {
98 match self {
99 Self::NA => 0b00,
100 Self::RO => 0b01,
101 Self::WO => 0b10,
102 Self::RW => 0b11,
103 }
104 }
105
106 #[doc(hidden)]
107 #[must_use]
108 pub fn meet(self, rhs: Self) -> Self {
109 use AccessRight::{NA, RO, RW, WO};
110 match self {
111 RW => {
112 if rhs == RW {
113 RW
114 } else {
115 rhs
116 }
117 }
118 RO => {
119 if rhs.is_readable() {
120 self
121 } else {
122 NA
123 }
124 }
125 WO => {
126 if rhs.is_writable() {
127 self
128 } else {
129 NA
130 }
131 }
132 NA => NA,
133 }
134 }
135
136 #[doc(hidden)]
137 #[must_use]
138 pub fn from_num(num: u8) -> Self {
139 debug_assert!(num >> 2_i32 == 0);
140 match num {
141 0b00 => Self::NA,
142 0b01 => Self::RO,
143 0b10 => Self::WO,
144 0b11 => Self::RW,
145 _ => unreachable!(),
146 }
147 }
148}
149
150#[doc(hidden)]
151pub struct MemoryProtection {
152 inner: Vec<u8>,
153 memory_size: usize,
154}
155
156impl MemoryProtection {
157 #[must_use]
158 pub fn new(memory_size: usize) -> Self {
159 let len = if memory_size == 0 {
160 0
161 } else {
162 (memory_size - 1) / 4 + 1
163 };
164 let inner = vec![0; len];
165 Self { inner, memory_size }
166 }
167
168 pub fn set_access_right(&mut self, address: usize, access_right: AccessRight) {
169 let block = &mut self.inner[address / 4];
170 let offset = address % 4 * 2;
171 let mask = !(0b11 << offset);
172 *block = (*block & mask) | access_right.as_num() << offset;
173 }
174
175 #[must_use]
176 pub fn access_right(&self, address: usize) -> AccessRight {
177 let block = self.inner[address / 4];
178 let offset = address % 4 * 2;
179 AccessRight::from_num(block >> offset & 0b11)
180 }
181
182 pub fn access_right_with_range(&self, range: impl IntoIterator<Item = usize>) -> AccessRight {
183 range
184 .into_iter()
185 .fold(AccessRight::RW, |acc, i| acc.meet(self.access_right(i)))
186 }
187
188 pub fn set_access_right_with_range(
189 &mut self,
190 range: impl IntoIterator<Item = usize>,
191 access_right: AccessRight,
192 ) {
193 range
194 .into_iter()
195 .for_each(|i| self.set_access_right(i, access_right));
196 }
197
198 pub fn verify_address(&self, address: usize) -> MemoryResult<()> {
199 if self.memory_size <= address {
200 Err(MemoryError::InvalidAddress)
201 } else {
202 Ok(())
203 }
204 }
205
206 pub fn verify_address_with_range(
207 &self,
208 range: impl IntoIterator<Item = usize>,
209 ) -> MemoryResult<()> {
210 for i in range {
211 self.verify_address(i)?;
212 }
213 Ok(())
214 }
215}
216
217pub trait Register {
218 type Ty;
219
220 const ADDRESS: usize;
221 const LENGTH: usize;
222 const ACCESS_RIGHT: AccessRight;
223
224 fn parse(data: &[u8]) -> MemoryResult<Self::Ty>;
225 fn serialize(data: Self::Ty) -> MemoryResult<Vec<u8>>;
226
227 fn write(data: Self::Ty, memory: &mut [u8]) -> MemoryResult<()> {
228 let data = Self::serialize(data)?;
229 let range = Self::range();
230
231 memory[range].copy_from_slice(data.as_slice());
232 Ok(())
233 }
234
235 fn read(memory: &[u8]) -> MemoryResult<Self::Ty> {
236 let range = Self::range();
237 Self::parse(&memory[range])
238 }
239
240 #[must_use]
241 fn range() -> std::ops::Range<usize> {
242 Self::ADDRESS..Self::ADDRESS + Self::LENGTH
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::AccessRight::{NA, RO, RW, WO};
249 use super::*;
250
251 #[test]
252 fn test_protection() {
253 let mut protection = MemoryProtection::new(5);
255 protection.set_access_right(0, RO);
256 protection.set_access_right(1, RW);
257 protection.set_access_right(2, NA);
258 protection.set_access_right(3, WO);
259 protection.set_access_right(4, RO);
260
261 assert_eq!(protection.inner.len(), 2);
262 assert_eq!(protection.access_right(0), RO);
263 assert_eq!(protection.access_right(1), RW);
264 assert_eq!(protection.access_right(2), NA);
265 assert_eq!(protection.access_right(3), WO);
266 assert_eq!(protection.access_right(4), RO);
267
268 assert_eq!(protection.access_right_with_range(0..2), RO);
269 assert_eq!(protection.access_right_with_range(2..4), NA);
270 assert_eq!(protection.access_right_with_range(3..5), NA);
271 }
272
273 #[test]
274 fn test_verify_address() {
275 let protection = MemoryProtection::new(5);
276 assert!(protection.verify_address(0).is_ok());
277 assert!(protection.verify_address(4).is_ok());
278 assert!(protection.verify_address(5).is_err());
279 assert!(protection.verify_address_with_range(2..5).is_ok());
280 assert!(protection.verify_address_with_range(2..6).is_err());
281 }
282}