1use std::fmt::Debug;
4use std::rc::Rc;
5
6#[doc(hidden)]
7pub use gwr_components::build_component_harness;
8use gwr_engine::types::AccessType;
9use gwr_track::entity::Entity;
10
11use crate::memory::CacheHintType;
12use crate::memory::memory_access::MemoryAccess;
13use crate::memory::memory_map::{DeviceId, MemoryMap};
14use crate::memory::traits::AccessMemory;
15
16#[macro_export]
25macro_rules! build_model_harness {
26 (
27 $(#[$meta:meta])*
28 $vis:vis harness $harness:ident <$item:ident> {
29 component: $component_field:ident : $component_ty:ty,
30 $($sections:tt)*
31 }
32 ) => {
33 $crate::build_model_harness! {
34 @normalize
35 [$(#[$meta])*]
36 [$vis]
37 [$harness]
38 [$item]
39
40 [$component_field: $component_ty]
41 []
42 []
43 []
44 []
45 $($sections)*
46 }
47 };
48
49 (
50 @normalize
51 [$($meta:tt)*]
52 [$vis:vis]
53 [$harness:ident]
54 [$item:ident]
55 [$component_field:ident : $component_ty:ty]
56 [$($rx_ports:tt)*]
57 [$($tx_ports:tt)*]
58 [$($rx_arrays:tt)*]
59 [$($tx_arrays:tt)*]
60 rx ports: { $($rx_section:tt)* }, $($rest:tt)*
61 ) => {
62 $crate::build_model_harness! {
63 @normalize
64 [$($meta)*] [$vis] [$harness] [$item] [$component_field: $component_ty]
65 [$($rx_section)*] [$($tx_ports)*] [$($rx_arrays)*] [$($tx_arrays)*]
66 $($rest)*
67 }
68 };
69
70 (
71 @normalize
72 [$($meta:tt)*]
73 [$vis:vis]
74 [$harness:ident]
75 [$item:ident]
76 [$component_field:ident : $component_ty:ty]
77 [$($rx_ports:tt)*]
78 [$($tx_ports:tt)*]
79 [$($rx_arrays:tt)*]
80 [$($tx_arrays:tt)*]
81 rx ports: { $($rx_section:tt)* }
82 ) => {
83 $crate::build_model_harness! {
84 @normalize
85 [$($meta)*] [$vis] [$harness] [$item] [$component_field: $component_ty]
86 [$($rx_section)*] [$($tx_ports)*] [$($rx_arrays)*] [$($tx_arrays)*]
87 }
88 };
89
90 (
91 @normalize
92 [$($meta:tt)*]
93 [$vis:vis]
94 [$harness:ident]
95 [$item:ident]
96 [$component_field:ident : $component_ty:ty]
97 [$($rx_ports:tt)*]
98 [$($tx_ports:tt)*]
99 [$($rx_arrays:tt)*]
100 [$($tx_arrays:tt)*]
101 tx ports: { $($tx_section:tt)* }, $($rest:tt)*
102 ) => {
103 $crate::build_model_harness! {
104 @normalize
105 [$($meta)*] [$vis] [$harness] [$item] [$component_field: $component_ty]
106 [$($rx_ports)*] [$($tx_section)*] [$($rx_arrays)*] [$($tx_arrays)*]
107 $($rest)*
108 }
109 };
110
111 (
112 @normalize
113 [$($meta:tt)*]
114 [$vis:vis]
115 [$harness:ident]
116 [$item:ident]
117 [$component_field:ident : $component_ty:ty]
118 [$($rx_ports:tt)*]
119 [$($tx_ports:tt)*]
120 [$($rx_arrays:tt)*]
121 [$($tx_arrays:tt)*]
122 tx ports: { $($tx_section:tt)* }
123 ) => {
124 $crate::build_model_harness! {
125 @normalize
126 [$($meta)*] [$vis] [$harness] [$item] [$component_field: $component_ty]
127 [$($rx_ports)*] [$($tx_section)*] [$($rx_arrays)*] [$($tx_arrays)*]
128 }
129 };
130
131 (
132 @normalize
133 [$($meta:tt)*]
134 [$vis:vis]
135 [$harness:ident]
136 [$item:ident]
137 [$component_field:ident : $component_ty:ty]
138 [$($rx_ports:tt)*]
139 [$($tx_ports:tt)*]
140 [$($rx_arrays:tt)*]
141 [$($tx_arrays:tt)*]
142 rx port arrays: { $($rx_array_section:tt)* }, $($rest:tt)*
143 ) => {
144 $crate::build_model_harness! {
145 @normalize
146 [$($meta)*] [$vis] [$harness] [$item] [$component_field: $component_ty]
147 [$($rx_ports)*] [$($tx_ports)*] [$($rx_array_section)*] [$($tx_arrays)*]
148 $($rest)*
149 }
150 };
151
152 (
153 @normalize
154 [$($meta:tt)*]
155 [$vis:vis]
156 [$harness:ident]
157 [$item:ident]
158 [$component_field:ident : $component_ty:ty]
159 [$($rx_ports:tt)*]
160 [$($tx_ports:tt)*]
161 [$($rx_arrays:tt)*]
162 [$($tx_arrays:tt)*]
163 rx port arrays: { $($rx_array_section:tt)* }
164 ) => {
165 $crate::build_model_harness! {
166 @normalize
167 [$($meta)*] [$vis] [$harness] [$item] [$component_field: $component_ty]
168 [$($rx_ports)*] [$($tx_ports)*] [$($rx_array_section)*] [$($tx_arrays)*]
169 }
170 };
171
172 (
173 @normalize
174 [$($meta:tt)*]
175 [$vis:vis]
176 [$harness:ident]
177 [$item:ident]
178 [$component_field:ident : $component_ty:ty]
179 [$($rx_ports:tt)*]
180 [$($tx_ports:tt)*]
181 [$($rx_arrays:tt)*]
182 [$($tx_arrays:tt)*]
183 tx port arrays: { $($tx_array_section:tt)* }, $($rest:tt)*
184 ) => {
185 $crate::build_model_harness! {
186 @normalize
187 [$($meta)*] [$vis] [$harness] [$item] [$component_field: $component_ty]
188 [$($rx_ports)*] [$($tx_ports)*] [$($rx_arrays)*] [$($tx_array_section)*]
189 $($rest)*
190 }
191 };
192
193 (
194 @normalize
195 [$($meta:tt)*]
196 [$vis:vis]
197 [$harness:ident]
198 [$item:ident]
199 [$component_field:ident : $component_ty:ty]
200 [$($rx_ports:tt)*]
201 [$($tx_ports:tt)*]
202 [$($rx_arrays:tt)*]
203 [$($tx_arrays:tt)*]
204 tx port arrays: { $($tx_array_section:tt)* }
205 ) => {
206 $crate::build_model_harness! {
207 @normalize
208 [$($meta)*] [$vis] [$harness] [$item] [$component_field: $component_ty]
209 [$($rx_ports)*] [$($tx_ports)*] [$($rx_arrays)*] [$($tx_array_section)*]
210 }
211 };
212
213 (
214 @normalize
215 [$($meta:tt)*]
216 [$vis:vis]
217 [$harness:ident]
218 [$item:ident]
219 [$component_field:ident : $component_ty:ty]
220 [$($rx_ports:tt)*]
221 [$($tx_ports:tt)*]
222 [$($rx_arrays:tt)*]
223 [$($tx_arrays:tt)*]
224 ) => {
225 $crate::build_model_harness! {
226 @impl
227 [$($meta)*] [$vis] [$harness] [$item] [$component_field: $component_ty]
228 rx ports: { $($rx_ports)* },
229 tx ports: { $($tx_ports)* },
230 rx port arrays: { $($rx_arrays)* },
231 tx port arrays: { $($tx_arrays)* },
232 }
233 };
234
235 (
236 @impl
237 [$($meta:tt)*]
238 [$vis:vis]
239 [$harness:ident]
240 [$item:ident]
241 [$component_field:ident : $component_ty:ty]
242 rx ports: { $($rx_variant:ident <$rx_ty:ty> => $rx_field:ident),* $(,)? },
243 tx ports: { $($tx_variant:ident <$tx_ty:ty> => $tx_field:ident),* $(,)? },
244 rx port arrays: {
245 $($rx_array_variant:ident <$rx_array_ty:ty> => $rx_array_field:ident {
246 count: $rx_array_count:ident
247 }),* $(,)?
248 },
249 tx port arrays: {
250 $($tx_array_variant:ident <$tx_array_ty:ty> => $tx_array_field:ident {
251 count: $tx_array_count:ident
252 }),* $(,)?
253 } $(,)?
254 ) => {
255 $crate::test_helpers::build_component_harness! {
256 @impl_model
257 [$($meta)*]
258 [$vis]
259 [$harness]
260 [$item]
261
262 [$crate::test_helpers::MemoryTxn]
263 [$crate::memory::traits::AccessMemory]
264 [$component_field: $component_ty]
265 rx ports: {
266 $(
267 $rx_variant <$rx_ty> => $rx_field
268 ),*
269 },
270 tx ports: {
271 $(
272 $tx_variant <$tx_ty> => $tx_field
273 ),*
274 },
275 rx port arrays: {
276 $(
277 $rx_array_variant <$rx_array_ty> => $rx_array_field {
278 count: $rx_array_count
279 }
280 ),*
281 },
282 tx port arrays: {
283 $(
284 $tx_array_variant <$tx_array_ty> => $tx_array_field {
285 count: $tx_array_count
286 }
287 ),*
288 },
289 }
290 };
291
292}
293
294#[derive(Clone, Debug)]
295pub struct MemoryTxn {
296 access_type: AccessType,
297 dst_addr: u64,
298 src_addr: Option<u64>,
299 bytes: Option<usize>,
300 total_bytes: Option<usize>,
301 destination: Option<u64>,
302 dst_device: Option<DeviceId>,
303 src_device: Option<DeviceId>,
304 cache_hint: Option<CacheHintType>,
305}
306
307impl MemoryTxn {
308 #[must_use]
309 pub fn new(access_type: AccessType, dst_addr: u64) -> Self {
310 Self {
311 access_type,
312 dst_addr,
313 src_addr: None,
314 bytes: None,
315 total_bytes: None,
316 destination: None,
317 dst_device: None,
318 src_device: None,
319 cache_hint: None,
320 }
321 }
322
323 #[must_use]
324 pub fn control(dst_addr: u64) -> Self {
325 Self::new(AccessType::Control, dst_addr)
326 }
327
328 #[must_use]
329 pub fn read_req(dst_addr: u64) -> Self {
330 Self::new(AccessType::ReadRequest, dst_addr)
331 }
332
333 #[must_use]
334 pub fn read_rsp(dst_addr: u64) -> Self {
335 Self::new(AccessType::ReadResponse, dst_addr)
336 }
337
338 #[must_use]
339 pub fn write_req(dst_addr: u64) -> Self {
340 Self::new(AccessType::WriteRequest, dst_addr)
341 }
342
343 #[must_use]
344 pub fn write_np_req(dst_addr: u64) -> Self {
345 Self::new(AccessType::WriteNonPostedRequest, dst_addr)
346 }
347
348 #[must_use]
349 pub fn write_np_rsp(dst_addr: u64) -> Self {
350 Self::new(AccessType::WriteNonPostedResponse, dst_addr)
351 }
352
353 #[must_use]
354 pub fn with_src_addr(mut self, src_addr: u64) -> Self {
355 self.src_addr = Some(src_addr);
356 self
357 }
358
359 #[must_use]
360 pub fn with_bytes(mut self, bytes: usize) -> Self {
361 self.bytes = Some(bytes);
362 self
363 }
364
365 #[must_use]
366 pub fn with_total_bytes(mut self, total_bytes: usize) -> Self {
367 self.total_bytes = Some(total_bytes);
368 self
369 }
370
371 #[must_use]
372 pub fn with_destination(mut self, destination: u64) -> Self {
373 self.destination = Some(destination);
374 self
375 }
376
377 #[must_use]
378 pub fn with_dst_device(mut self, dst_device: DeviceId) -> Self {
379 self.dst_device = Some(dst_device);
380 self
381 }
382
383 #[must_use]
384 pub fn with_src_device(mut self, src_device: DeviceId) -> Self {
385 self.src_device = Some(src_device);
386 self
387 }
388
389 #[must_use]
390 pub fn with_cache_hint(mut self, cache_hint: CacheHintType) -> Self {
391 self.cache_hint = Some(cache_hint);
392 self
393 }
394}
395
396pub trait MemoryAccessMatcher<T>
397where
398 T: AccessMemory + Debug,
399{
400 fn assert_matches(&self, step: &str, actual: &T);
401}
402
403impl<T> MemoryAccessMatcher<T> for MemoryTxn
404where
405 T: AccessMemory + Debug,
406{
407 fn assert_matches(&self, check_id: &str, actual: &T) {
408 assert_eq!(
409 actual.access_type(),
410 self.access_type,
411 "{check_id}: access type mismatch for actual {actual:?}",
412 );
413 assert_eq!(
414 actual.dst_addr(),
415 self.dst_addr,
416 "{check_id}: address mismatch for actual {actual:?}",
417 );
418 if let Some(src_addr) = self.src_addr {
419 assert_eq!(
420 actual.src_addr(),
421 src_addr,
422 "{check_id}: source address mismatch for actual {actual:?}",
423 );
424 }
425 if let Some(bytes) = self.bytes {
426 assert_eq!(
427 actual.access_size_bytes(),
428 bytes,
429 "{check_id}: byte count mismatch for actual {actual:?}",
430 );
431 }
432 if let Some(total_bytes) = self.total_bytes {
433 assert_eq!(
434 actual.total_bytes(),
435 total_bytes,
436 "{check_id}: total byte count mismatch for actual {actual:?}",
437 );
438 }
439 if let Some(destination) = self.destination {
440 assert_eq!(
441 actual.destination(),
442 destination,
443 "{check_id}: destination mismatch for actual {actual:?}",
444 );
445 }
446 if let Some(dst_device) = self.dst_device {
447 assert_eq!(
448 actual.dst_device(),
449 dst_device,
450 "{check_id}: dst device mismatch for actual {actual:?}",
451 );
452 }
453 if let Some(src_device) = self.src_device {
454 assert_eq!(
455 actual.src_device(),
456 src_device,
457 "{check_id}: src device mismatch for actual {actual:?}",
458 );
459 }
460 if let Some(cache_hint) = self.cache_hint {
461 assert_eq!(
462 actual.cache_hint(),
463 cache_hint,
464 "{check_id}: cache hint mismatch for actual {actual:?}",
465 );
466 }
467 }
468}
469
470impl<T> MemoryAccessMatcher<T> for T
471where
472 T: AccessMemory + Debug,
473{
474 fn assert_matches(&self, check_id: &str, actual: &T) {
475 MemoryTxn::new(self.access_type(), self.dst_addr())
476 .with_src_addr(self.src_addr())
477 .with_bytes(self.access_size_bytes())
478 .with_total_bytes(self.total_bytes())
479 .with_destination(self.destination())
480 .with_dst_device(self.dst_device())
481 .with_src_device(self.src_device())
482 .with_cache_hint(self.cache_hint())
483 .assert_matches(check_id, actual);
484 }
485}
486
487impl<T> gwr_components::test_helpers::ValueCheck<T> for MemoryTxn
488where
489 T: AccessMemory + Debug,
490{
491 fn assert_matches(&self, check_id: &str, actual: &T) {
492 MemoryAccessMatcher::assert_matches(self, check_id, actual);
493 }
494}
495
496impl gwr_components::test_helpers::ValueCheck<MemoryAccess> for MemoryAccess {
497 fn assert_matches(&self, check_id: &str, actual: &MemoryAccess) {
498 MemoryAccessMatcher::assert_matches(self, check_id, actual);
499 }
500}
501
502#[must_use]
503pub fn create_default_memory_map() -> MemoryMap {
504 let mut memory_map = MemoryMap::new();
505
506 memory_map.insert(0x0, u64::MAX, DeviceId(0)).unwrap();
508
509 memory_map
510}
511
512#[must_use]
513pub fn create_read(
514 created_by: &Rc<Entity>,
515 memory_map: &Rc<MemoryMap>,
516 num_bytes: usize,
517 dst_addr: u64,
518 src_addr: u64,
519 overhead_size_bytes: usize,
520) -> MemoryAccess {
521 let (dst_device, _) = memory_map.lookup(dst_addr).unwrap();
522 let (src_device, _) = memory_map.lookup(src_addr).unwrap();
523 MemoryAccess::new(
524 created_by,
525 AccessType::ReadRequest,
526 num_bytes,
527 dst_addr,
528 src_addr,
529 dst_device,
530 src_device,
531 overhead_size_bytes,
532 )
533}
534
535#[must_use]
536pub fn create_write(
537 created_by: &Rc<Entity>,
538 memory_map: &Rc<MemoryMap>,
539 num_bytes: usize,
540 dst_addr: u64,
541 src_addr: u64,
542 overhead_size_bytes: usize,
543) -> MemoryAccess {
544 let (dst_device, _) = memory_map.lookup(dst_addr).unwrap();
545 let (src_device, _) = memory_map.lookup(src_addr).unwrap();
546 MemoryAccess::new(
547 created_by,
548 AccessType::WriteRequest,
549 num_bytes,
550 dst_addr,
551 src_addr,
552 dst_device,
553 src_device,
554 overhead_size_bytes,
555 )
556}
557
558#[must_use]
559pub fn create_write_np(
560 created_by: &Rc<Entity>,
561 memory_map: &Rc<MemoryMap>,
562 num_bytes: usize,
563 dst_addr: u64,
564 src_addr: u64,
565 overhead_size_bytes: usize,
566) -> MemoryAccess {
567 let (dst_device, _) = memory_map.lookup(dst_addr).unwrap();
568 let (src_device, _) = memory_map.lookup(src_addr).unwrap();
569 MemoryAccess::new(
570 created_by,
571 AccessType::WriteNonPostedRequest,
572 num_bytes,
573 dst_addr,
574 src_addr,
575 dst_device,
576 src_device,
577 overhead_size_bytes,
578 )
579}