1use std::cell::RefCell;
37use std::fmt::{self, Display};
38use std::rc::Rc;
39
40use async_trait::async_trait;
41use gwr_components::arbiter::Arbiter;
42use gwr_components::arbiter::policy::RoundRobin;
43use gwr_components::delay::Delay;
44use gwr_components::{connect_tx, port_rx, take_option};
45use gwr_engine::engine::Engine;
46use gwr_engine::executor::Spawner;
47use gwr_engine::port::{InPort, OutPort, PortStateResult};
48use gwr_engine::sim_error;
49use gwr_engine::time::clock::Clock;
50use gwr_engine::time::compute_adjusted_value_and_rate;
51use gwr_engine::traits::{Runnable, SimObject};
52use gwr_engine::types::{AccessType, SimError, SimResult};
53use gwr_model_builder::{EntityDisplay, EntityGet};
54use gwr_track::entity::Entity;
55use gwr_track::tracker::aka::Aka;
56use gwr_track::{build_aka, trace};
57
58use crate::log_stats;
59#[cfg(test)]
60use crate::memory::memory_access::MemoryAccess;
61use crate::memory::traits::{AccessMemory, ReadMemory};
62
63type Tag = u64;
64type Index = usize;
65
66#[derive(Clone)]
67pub struct CacheConfig {
68 line_size_bytes: usize,
69 bw_bytes_per_cycle: usize,
70 num_sets: usize,
71 num_ways: usize,
72 delay_ticks: usize,
73}
74
75impl CacheConfig {
76 #[must_use]
77 pub fn new(
78 line_size_bytes: usize,
79 bw_bytes_per_cycle: usize,
80 num_sets: usize,
81 num_ways: usize,
82 delay_ticks: usize,
83 ) -> Self {
84 Self {
85 line_size_bytes,
86 bw_bytes_per_cycle,
87 num_sets,
88 num_ways,
89 delay_ticks,
90 }
91 }
92}
93
94#[derive(Clone, Default)]
95struct CacheMetrics {
96 payload_bytes_read: usize,
97 payload_bytes_written: usize,
98 num_hits: usize,
99 num_misses: usize,
100}
101
102pub struct CacheStatsDisplay {
103 prefix: String,
104 time_now_ns: f64,
105 payload_bytes_read: usize,
106 payload_bytes_written: usize,
107 num_hits: usize,
108 num_misses: usize,
109}
110
111impl CacheStatsDisplay {
112 #[must_use]
113 pub fn new(
114 prefix: impl Into<String>,
115 time_now_ns: f64,
116 payload_bytes_read: usize,
117 payload_bytes_written: usize,
118 num_hits: usize,
119 num_misses: usize,
120 ) -> Self {
121 Self {
122 prefix: prefix.into(),
123 time_now_ns,
124 payload_bytes_read,
125 payload_bytes_written,
126 num_hits,
127 num_misses,
128 }
129 }
130}
131
132impl Display for CacheStatsDisplay {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 let (read_value, read_per_second) =
135 compute_adjusted_value_and_rate(self.time_now_ns, self.payload_bytes_read);
136 let (write_value, write_per_second) =
137 compute_adjusted_value_and_rate(self.time_now_ns, self.payload_bytes_written);
138 let num_accesses = self.num_hits + self.num_misses;
139 let hit_rate = if num_accesses == 0 {
140 0.0
141 } else {
142 self.num_hits as f64 / num_accesses as f64 * 100.0
143 };
144
145 writeln!(f, "{}:", self.prefix)?;
146 writeln!(
147 f,
148 " Payload read: {} bytes, {read_value:.2}, {read_per_second:.2}/s",
149 self.payload_bytes_read
150 )?;
151 writeln!(
152 f,
153 " Payload written: {} bytes, {write_value:.2}, {write_per_second:.2}/s",
154 self.payload_bytes_written
155 )?;
156 write!(
157 f,
158 " Hits: {}, misses: {}, hit rate: {hit_rate:.2}%",
159 self.num_hits, self.num_misses
160 )
161 }
162}
163
164#[derive(Copy, Clone, Debug, Default, PartialEq)]
165enum EntryState {
166 #[default]
167 Available,
168 Allocated,
169 ValidData,
170}
171
172#[derive(Default, Clone)]
173struct CacheEntry {
174 state: EntryState,
175 tag: Tag,
176}
177
178type Set = Vec<CacheEntry>;
181type Sets = Vec<Set>;
183
184struct CacheContents<T>
185where
186 T: SimObject + AccessMemory,
187{
188 config: CacheConfig,
189 sets: Sets,
190 waiting_for_response: Vec<(Tag, Index, T)>,
191 lru_indices: Vec<usize>,
192}
193
194impl<T> CacheContents<T>
195where
196 T: SimObject + AccessMemory,
197{
198 fn new(config: CacheConfig) -> Self {
199 let sets = vec![vec![CacheEntry::default(); config.num_ways]; config.num_sets];
200 let lru_indices = vec![0; config.num_sets];
201 Self {
202 config,
203 sets,
204 waiting_for_response: Vec::new(),
205 lru_indices,
206 }
207 }
208
209 fn tag_and_index_for_addr(&self, addr: u64) -> (Tag, Index) {
222 let index = (addr as usize / self.config.line_size_bytes) % self.config.num_sets;
223 let tag = addr / self.config.line_size_bytes as u64 / self.config.num_sets as u64;
224 (tag, index)
225 }
226
227 fn state_for(&self, addr: u64) -> Option<EntryState> {
228 let (tag, index) = self.tag_and_index_for_addr(addr);
229 for i in 0..self.config.num_ways {
230 if (self.sets[index][i].state != EntryState::Available)
231 && self.sets[index][i].tag == tag
232 {
233 return Some(self.sets[index][i].state);
234 }
235 }
236 None
237 }
238
239 fn allocate(&mut self, addr: u64) {
240 let (tag, index) = self.tag_and_index_for_addr(addr);
241
242 let insert_index = self.lru_indices[index];
243 self.lru_indices[index] = (self.lru_indices[index] + 1) % self.config.num_ways;
244
245 self.sets[index][insert_index].tag = tag;
246 self.sets[index][insert_index].state = EntryState::Allocated;
247 }
248
249 fn set_data_valid(&mut self, addr: u64) {
250 let (tag, index) = self.tag_and_index_for_addr(addr);
251
252 for i in 0..self.config.num_ways {
253 if (self.sets[index][i].state != EntryState::Available)
254 && self.sets[index][i].tag == tag
255 {
256 self.sets[index][i].state = EntryState::ValidData;
257 break;
258 }
259 }
260 }
261
262 fn invalidate(&mut self, addr: u64) {
263 let (tag, index) = self.tag_and_index_for_addr(addr);
264
265 for i in 0..self.config.num_ways {
266 if self.sets[index][i].tag == tag {
267 self.sets[index][i].state = EntryState::Available;
268 self.sets[index][i].tag = 0;
269 break;
270 }
271 }
272 }
273
274 fn add_waiting_for_response(&mut self, request: T) {
275 let (tag, index) = self.tag_and_index_for_addr(request.dst_addr());
276 self.waiting_for_response.push((tag, index, request));
277 }
278
279 fn get_requests_waiting_for_response(&mut self, response: &T) -> Option<Vec<T>> {
280 let (response_tag, response_index) = self.tag_and_index_for_addr(response.dst_addr());
281
282 if self
284 .waiting_for_response
285 .iter()
286 .any(|(tag, index, _x)| *tag == response_tag && *index == response_index)
287 {
288 let all: Vec<(Tag, Index, T)> = std::mem::take(&mut self.waiting_for_response);
289 let (matching, not_matching) = all
290 .into_iter()
291 .partition(|(tag, index, _x)| *tag == response_tag && *index == response_index);
292 self.waiting_for_response = not_matching;
293 let matching = matching.into_iter().map(|(_, _, x)| x).collect();
294 Some(matching)
295 } else {
296 None
297 }
298 }
299}
300
301impl<T> ReadMemory for CacheContents<T>
302where
303 T: SimObject + AccessMemory,
304{
305 fn read(&self) -> Vec<u8> {
306 Vec::new()
307 }
308}
309
310impl<T> ReadMemory for RefCell<CacheContents<T>>
311where
312 T: SimObject + AccessMemory,
313{
314 fn read(&self) -> Vec<u8> {
315 Vec::new()
316 }
317}
318
319#[derive(EntityGet, EntityDisplay)]
320pub struct Cache<T>
321where
322 T: SimObject + AccessMemory,
323{
324 entity: Rc<Entity>,
325
326 clock: Clock,
327 spawner: Spawner,
328 metrics: Rc<RefCell<CacheMetrics>>,
329 contents: Rc<RefCell<CacheContents<T>>>,
330
331 response_delay: RefCell<Option<Rc<Delay<T>>>>,
332 request_delay: RefCell<Option<Rc<Delay<T>>>>,
333
334 dev_rx: RefCell<Option<InPort<T>>>,
335 mem_rx: RefCell<Option<InPort<T>>>,
336
337 bw_bytes_per_cycle: usize,
338
339 req: RefCell<Option<OutPort<T>>>,
341 rsp_arb_0: RefCell<Option<OutPort<T>>>,
342 rsp_arb_1: RefCell<Option<OutPort<T>>>,
343}
344
345impl<T> Cache<T>
346where
347 T: SimObject + AccessMemory,
348{
349 pub fn new_and_register_with_renames(
351 engine: &Engine,
352 clock: &Clock,
353 parent: &Rc<Entity>,
354 name: &str,
355 aka: Option<&Aka>,
356 config: CacheConfig,
357 ) -> Result<Rc<Self>, SimError> {
358 let bw_bytes_per_cycle = config.bw_bytes_per_cycle;
359 let entity = Rc::new(Entity::new(parent, name));
360
361 let policy = Box::new(RoundRobin::new());
362 let response_arbiter =
363 Arbiter::new_and_register(engine, clock, &entity, "rsp_arb", 2, policy);
364
365 let response_delay_aka = build_aka!(aka, &entity, &[("dev_tx", "tx")]);
366 let response_delay = Delay::new_and_register_with_renames(
367 engine,
368 clock,
369 &entity,
370 "rsp_delay",
371 Some(&response_delay_aka),
372 config.delay_ticks,
373 );
374
375 let request_delay_aka = build_aka!(aka, &entity, &[("mem_tx", "tx")]);
376 let request_delay = Delay::new_and_register_with_renames(
377 engine,
378 clock,
379 &entity,
380 "req_delay",
381 Some(&request_delay_aka),
382 config.delay_ticks,
383 );
384
385 response_arbiter
386 .connect_port_tx(response_delay.port_rx())
387 .expect("Internal ports should connect without error");
388
389 let mut req = OutPort::new(&entity, "req_arb_0");
391 req.connect(request_delay.port_rx())
392 .expect("Internal ports should connect without error");
393
394 let mut rsp_arb_0 = OutPort::new(&entity, "rsp_arb_0");
395 rsp_arb_0
396 .connect(response_arbiter.port_rx_i(0))
397 .expect("Internal ports should connect without error");
398
399 let mut rsp_arb_1 = OutPort::new(&entity, "rsp_arb_1");
400 rsp_arb_1
401 .connect(response_arbiter.port_rx_i(1))
402 .expect("Internal ports should connect without error");
403
404 let dev_rx = InPort::new_with_renames(engine, clock, &entity, "dev_rx", aka);
405 let mem_rx = InPort::new_with_renames(engine, clock, &entity, "mem_rx", aka);
406
407 let spawner = engine.spawner();
408 let rc_self = Rc::new(Self {
409 entity,
410 clock: clock.clone(),
411 spawner,
412 metrics: Rc::new(RefCell::new(CacheMetrics::default())),
413 contents: Rc::new(RefCell::new(CacheContents::new(config))),
414 response_delay: RefCell::new(Some(response_delay)),
415 request_delay: RefCell::new(Some(request_delay)),
416 dev_rx: RefCell::new(Some(dev_rx)),
417 mem_rx: RefCell::new(Some(mem_rx)),
418 bw_bytes_per_cycle,
419
420 req: RefCell::new(Some(req)),
421 rsp_arb_0: RefCell::new(Some(rsp_arb_0)),
422 rsp_arb_1: RefCell::new(Some(rsp_arb_1)),
423 });
424 engine.register(rc_self.clone());
425 Ok(rc_self)
426 }
427
428 pub fn new_and_register(
430 engine: &Engine,
431 clock: &Clock,
432 parent: &Rc<Entity>,
433 name: &str,
434 config: CacheConfig,
435 ) -> Result<Rc<Self>, SimError> {
436 Self::new_and_register_with_renames(engine, clock, parent, name, None, config)
437 }
438
439 pub fn connect_port_dev_tx(&self, port_state: PortStateResult<T>) -> SimResult {
440 connect_tx!(self.response_delay, connect_port_tx ; port_state)
441 }
442
443 pub fn connect_port_mem_tx(&self, port_state: PortStateResult<T>) -> SimResult {
444 connect_tx!(self.request_delay, connect_port_tx ; port_state)
445 }
446
447 pub fn port_dev_rx(&self) -> PortStateResult<T> {
448 port_rx!(self.dev_rx, state)
449 }
450
451 pub fn port_mem_rx(&self) -> PortStateResult<T> {
452 port_rx!(self.mem_rx, state)
453 }
454
455 #[must_use]
456 pub fn payload_bytes_read(&self) -> usize {
457 self.metrics.borrow().payload_bytes_read
458 }
459
460 #[must_use]
461 pub fn payload_bytes_written(&self) -> usize {
462 self.metrics.borrow().payload_bytes_written
463 }
464
465 #[must_use]
466 pub fn num_hits(&self) -> usize {
467 self.metrics.borrow().num_hits
468 }
469
470 #[must_use]
471 pub fn num_misses(&self) -> usize {
472 self.metrics.borrow().num_misses
473 }
474
475 pub fn dump_stats(&self, time_now_ns: f64) {
476 let metrics = self.metrics.borrow();
477 log_stats(
478 &self.entity,
479 CacheStatsDisplay::new(
480 format!("Cache {}", self.entity.full_name()),
481 time_now_ns,
482 metrics.payload_bytes_read,
483 metrics.payload_bytes_written,
484 metrics.num_hits,
485 metrics.num_misses,
486 ),
487 );
488 }
489}
490
491struct RxHandlingState<T>
492where
493 T: SimObject + AccessMemory,
494{
495 entity: Rc<Entity>,
496 rx: InPort<T>,
497 clock: Clock,
498 contents: Rc<RefCell<CacheContents<T>>>,
499 metrics: Rc<RefCell<CacheMetrics>>,
500 bw_bytes_per_cycle: usize,
501}
502
503#[async_trait(?Send)]
504impl<T> Runnable for Cache<T>
505where
506 T: SimObject + AccessMemory,
507{
508 async fn run(&self) -> SimResult {
509 {
510 let state = RxHandlingState {
512 entity: self.entity.clone(),
513 rx: take_option!(self.dev_rx),
514 clock: self.clock.clone(),
515 contents: self.contents.clone(),
516 metrics: self.metrics.clone(),
517 bw_bytes_per_cycle: self.bw_bytes_per_cycle,
518 };
519 let req = take_option!(self.req);
520 let rsp_arb_1 = take_option!(self.rsp_arb_1);
521 self.spawner
522 .spawn(async move { run_dev_rx(state, req, rsp_arb_1).await });
523 }
524
525 let state = RxHandlingState {
527 entity: self.entity.clone(),
528 rx: take_option!(self.mem_rx),
529 clock: self.clock.clone(),
530 contents: self.contents.clone(),
531 metrics: self.metrics.clone(),
532 bw_bytes_per_cycle: self.bw_bytes_per_cycle,
533 };
534 let rsp_arb_0 = take_option!(self.rsp_arb_0);
535 run_mem_rx(state, rsp_arb_0).await
536 }
537}
538
539async fn run_dev_rx<T>(
540 mut state: RxHandlingState<T>,
541 mut req: OutPort<T>,
542 mut rsp_arb_1: OutPort<T>,
543) -> SimResult
544where
545 T: SimObject + AccessMemory,
546{
547 loop {
548 let request = state.rx.get()?.await;
549 trace!(state.entity ; "Device request {}", request);
550 let total_bytes = request.total_bytes();
551 handle_request(&state, &mut req, &mut rsp_arb_1, request).await?;
552 let ticks = total_bytes.div_ceil(state.bw_bytes_per_cycle);
553 state.clock.wait_ticks(ticks as u64).await;
554 }
555}
556
557async fn handle_request<T>(
558 state: &RxHandlingState<T>,
559 req: &mut OutPort<T>,
560 rsp_arb_1: &mut OutPort<T>,
561 request: T,
562) -> SimResult
563where
564 T: SimObject + AccessMemory,
565{
566 let addr = request.dst_addr();
567 let access_type = request.access_type();
568 match access_type {
569 AccessType::Control => {
570 state.contents.borrow_mut().invalidate(addr);
571 }
572 AccessType::ReadRequest => {
573 state.metrics.borrow_mut().payload_bytes_read += request.access_size_bytes();
574 let line_state = state.contents.borrow().state_for(addr);
575 match line_state {
576 Some(EntryState::ValidData) => {
577 let response = request.to_response(state.contents.as_ref())?;
578 rsp_arb_1.put(response)?.await;
579 state.metrics.borrow_mut().num_hits += 1;
580 }
581 Some(EntryState::Allocated) => {
582 state
584 .contents
585 .borrow_mut()
586 .add_waiting_for_response(request);
587 state.metrics.borrow_mut().num_hits += 1;
588 }
589 Some(EntryState::Available) | None => {
590 state.contents.borrow_mut().allocate(addr);
591 req.put(request)?.await;
592 state.metrics.borrow_mut().num_misses += 1;
593 }
594 }
595 }
596
597 AccessType::WriteRequest | AccessType::WriteNonPostedRequest => {
598 state.metrics.borrow_mut().payload_bytes_written += request.access_size_bytes();
599 state.contents.borrow_mut().invalidate(addr);
600 req.put(request)?.await;
601 }
602
603 AccessType::ReadResponse | AccessType::WriteNonPostedResponse => {
604 return sim_error!(
605 "{}: unsupported AccessType from device: {access_type}",
606 state.entity
607 );
608 }
609 }
610
611 Ok(())
612}
613
614async fn run_mem_rx<T>(mut state: RxHandlingState<T>, mut rsp_arb_0: OutPort<T>) -> SimResult
615where
616 T: SimObject + AccessMemory,
617{
618 loop {
619 let response = state.rx.get()?.await;
620 trace!(state.entity ; "Memory response {}", response);
621 let total_bytes = response.total_bytes();
622 handle_response(&state, &mut rsp_arb_0, response).await?;
623 let ticks = total_bytes.div_ceil(state.bw_bytes_per_cycle);
624 state.clock.wait_ticks(ticks as u64).await;
625 }
626}
627
628async fn handle_response<T>(
629 state: &RxHandlingState<T>,
630 rsp_arb_0: &mut OutPort<T>,
631 access: T,
632) -> SimResult
633where
634 T: SimObject + AccessMemory,
635{
636 let access_type = access.access_type();
637 match access_type {
638 AccessType::Control => {
639 }
641 AccessType::WriteNonPostedResponse => {
642 rsp_arb_0.put(access)?.await;
644 }
645 AccessType::ReadRequest | AccessType::WriteRequest | AccessType::WriteNonPostedRequest => {
646 return sim_error!(
647 "{}: unsupported {access_type} on response port",
648 state.entity
649 );
650 }
651 AccessType::ReadResponse => {
652 state
653 .contents
654 .borrow_mut()
655 .set_data_valid(access.dst_addr());
656 let matching = state
657 .contents
658 .borrow_mut()
659 .get_requests_waiting_for_response(&access);
660
661 rsp_arb_0.put(access)?.await;
663
664 if let Some(m) = matching {
666 for x in m {
667 let response = x.to_response(state.contents.as_ref())?;
668 rsp_arb_0.put(response)?.await;
669 }
670 }
671 }
672 }
673
674 Ok(())
675}
676
677#[test]
678fn basic_ways() {
679 let line_size_bytes = 32;
680 let bw_bytes_per_cycle = 32;
681 let num_sets = 1024;
682 let num_ways = 4;
683 let config = CacheConfig::new(line_size_bytes, bw_bytes_per_cycle, num_sets, num_ways, 8);
684 let mut state: CacheContents<MemoryAccess> = CacheContents::new(config);
685
686 let mut addrs = Vec::new();
687 let mut addr = 0x0100_0000;
688 for _ in 0..num_ways + 1 {
689 addrs.push(addr);
690 addr += (line_size_bytes * num_sets * num_ways) as u64;
691 }
692
693 for addr in addrs.iter().take(num_ways) {
694 assert_eq!(state.state_for(*addr), None);
695 state.allocate(*addr);
696 assert_eq!(state.state_for(*addr), Some(EntryState::Allocated));
697 }
698
699 state.allocate(addrs[num_ways]);
700
701 assert_eq!(state.state_for(addrs[0]), None);
703 for i in 0..num_ways {
704 assert_eq!(state.state_for(addrs[i + 1]), Some(EntryState::Allocated));
706 }
707}
708
709#[test]
710fn invalidate() {
711 let num_ways = 4;
712 let config = CacheConfig::new(32, 32, 1024, num_ways, 8);
713 let mut state: CacheContents<MemoryAccess> = CacheContents::new(config);
714
715 let addr = 0x40000;
716 state.allocate(addr);
717 assert_eq!(state.state_for(addr), Some(EntryState::Allocated));
718 state.invalidate(addr);
719 assert_eq!(state.state_for(addr), None);
720}