Skip to main content

gwr_models/memory/
mod.rs

1// Copyright (c) 2023 Graphcore Ltd. All rights reserved.
2
3use std::cell::RefCell;
4use std::fmt::{self, Display};
5use std::rc::Rc;
6
7use async_trait::async_trait;
8use gwr_components::delay::Delay;
9use gwr_components::{port_rx, take_option};
10use gwr_engine::engine::Engine;
11use gwr_engine::port::{InPort, OutPort, PortStateResult};
12use gwr_engine::sim_error;
13use gwr_engine::time::clock::Clock;
14use gwr_engine::time::compute_adjusted_value_and_rate;
15use gwr_engine::traits::{Runnable, SimObject};
16use gwr_engine::types::{AccessType, SimError, SimResult};
17use gwr_model_builder::{EntityDisplay, EntityGet};
18use gwr_track::entity::Entity;
19use gwr_track::tracker::aka::Aka;
20use gwr_track::{build_aka, debug};
21
22use crate::log_stats;
23use crate::memory::traits::{AccessMemory, ReadMemory};
24
25pub mod cache;
26pub mod memory_access;
27pub mod memory_access_gen;
28pub mod memory_map;
29pub mod traits;
30
31#[derive(Copy, Clone, Debug, PartialEq)]
32pub enum CacheHintType {
33    Allocate,
34    NoAllocate,
35}
36
37#[derive(Clone)]
38pub struct MemoryConfig {
39    base_address: u64,
40    capacity_bytes: usize,
41    bw_bytes_per_cycle: usize,
42    delay_ticks: usize,
43}
44
45impl MemoryConfig {
46    #[must_use]
47    pub fn new(
48        base_address: u64,
49        capacity_bytes: usize,
50        bw_bytes_per_cycle: usize,
51        delay_ticks: usize,
52    ) -> Self {
53        Self {
54            base_address,
55            capacity_bytes,
56            bw_bytes_per_cycle,
57            delay_ticks,
58        }
59    }
60}
61
62#[derive(Clone, Default)]
63pub struct MemoryStats {
64    bytes_read: usize,
65    bytes_written: usize,
66}
67
68pub struct MemoryStatsDisplay {
69    prefix: String,
70    time_now_ns: f64,
71    bytes_read: usize,
72    bytes_written: usize,
73}
74
75impl MemoryStatsDisplay {
76    #[must_use]
77    pub fn new(
78        prefix: impl Into<String>,
79        time_now_ns: f64,
80        bytes_read: usize,
81        bytes_written: usize,
82    ) -> Self {
83        Self {
84            prefix: prefix.into(),
85            time_now_ns,
86            bytes_read,
87            bytes_written,
88        }
89    }
90}
91
92impl Display for MemoryStatsDisplay {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        let (read_value, read_per_second) =
95            compute_adjusted_value_and_rate(self.time_now_ns, self.bytes_read);
96        let (write_value, write_per_second) =
97            compute_adjusted_value_and_rate(self.time_now_ns, self.bytes_written);
98
99        writeln!(f, "{}:", self.prefix)?;
100        writeln!(
101            f,
102            "  Read: {} bytes, {read_value:.2}, {read_per_second:.2}/s",
103            self.bytes_read
104        )?;
105        write!(
106            f,
107            "  Written: {} bytes, {write_value:.2}, {write_per_second:.2}/s",
108            self.bytes_written
109        )
110    }
111}
112
113#[derive(EntityGet, EntityDisplay)]
114pub struct Memory<T>
115where
116    T: SimObject + AccessMemory,
117{
118    entity: Rc<Entity>,
119    clock: Clock,
120    config: MemoryConfig,
121    stats: RefCell<MemoryStats>,
122
123    response_delay: Rc<Delay<T>>,
124    response_tx: RefCell<Option<OutPort<T>>>,
125    rx: RefCell<Option<InPort<T>>>,
126}
127
128impl<T> Memory<T>
129where
130    T: SimObject + AccessMemory,
131{
132    pub fn new_and_register_with_renames(
133        engine: &Engine,
134        clock: &Clock,
135        parent: &Rc<Entity>,
136        name: &str,
137        aka: Option<&Aka>,
138        config: MemoryConfig,
139    ) -> Result<Rc<Self>, SimError> {
140        let entity = Rc::new(Entity::new(parent, name));
141
142        let rx = InPort::new_with_renames(engine, clock, &entity, "rx", aka);
143
144        let response_delay_aka = build_aka!(aka, &entity, &[("tx", "tx")]);
145        let response_delay = Delay::new_and_register_with_renames(
146            engine,
147            clock,
148            &entity,
149            "delay",
150            Some(&response_delay_aka),
151            config.delay_ticks,
152        );
153
154        // Create a local port to drive into the response delay
155        let mut response_tx = OutPort::new(&entity, "response");
156        response_tx
157            .connect(response_delay.port_rx())
158            .expect("Internal ports should connect without error");
159
160        let rc_self = Rc::new(Self {
161            entity,
162            clock: clock.clone(),
163            config,
164            stats: RefCell::new(MemoryStats::default()),
165            response_delay,
166            rx: RefCell::new(Some(rx)),
167            response_tx: RefCell::new(Some(response_tx)),
168        });
169        engine.register(rc_self.clone());
170        Ok(rc_self)
171    }
172
173    pub fn new_and_register(
174        engine: &Engine,
175        clock: &Clock,
176        parent: &Rc<Entity>,
177        name: &str,
178        config: MemoryConfig,
179    ) -> Result<Rc<Self>, SimError> {
180        Self::new_and_register_with_renames(engine, clock, parent, name, None, config)
181    }
182
183    pub fn connect_port_tx(&self, port_state: PortStateResult<T>) -> SimResult {
184        self.response_delay.connect_port_tx(port_state)
185    }
186
187    pub fn port_rx(&self) -> PortStateResult<T> {
188        port_rx!(self.rx, state)
189    }
190
191    #[must_use]
192    pub fn bytes_written(&self) -> usize {
193        self.stats.borrow().bytes_written
194    }
195
196    #[must_use]
197    pub fn bytes_read(&self) -> usize {
198        self.stats.borrow().bytes_read
199    }
200
201    #[must_use]
202    pub fn base_address(&self) -> u64 {
203        self.config.base_address
204    }
205
206    #[must_use]
207    pub fn capacity_bytes(&self) -> usize {
208        self.config.capacity_bytes
209    }
210
211    pub fn dump_stats(&self, time_now_ns: f64) {
212        let stats = self.stats.borrow();
213        log_stats(
214            &self.entity,
215            MemoryStatsDisplay::new(
216                format!("Memory {}", self.entity.full_name()),
217                time_now_ns,
218                stats.bytes_read,
219                stats.bytes_written,
220            ),
221        );
222    }
223}
224
225#[async_trait(?Send)]
226impl<T> Runnable for Memory<T>
227where
228    T: SimObject + AccessMemory,
229{
230    async fn run(&self) -> SimResult {
231        let mut rx = take_option!(self.rx);
232        let mut response_tx = take_option!(self.response_tx);
233
234        loop {
235            let access = rx.get()?.await;
236            debug!(self.entity ; "Memory access {}", access);
237
238            let begin = access.dst_addr();
239            let payload_bytes = access.access_size_bytes();
240            let end = begin + (payload_bytes as u64) - 1;
241
242            let config = &self.config;
243            assert!(
244                begin >= config.base_address
245                    && end < (config.base_address + config.capacity_bytes as u64),
246                "Out of bounds memory access received [0x{begin:x},0x{end:x}] not in [0x{:x},0x{:x}]",
247                config.base_address,
248                config.base_address + config.capacity_bytes as u64
249            );
250
251            let access_type = access.access_type();
252            match access_type {
253                AccessType::ReadRequest => {
254                    self.stats.borrow_mut().bytes_read += payload_bytes;
255                    let response = access.to_response(self)?;
256                    response_tx.put(response)?.await;
257                }
258                AccessType::WriteRequest => {
259                    self.stats.borrow_mut().bytes_written += payload_bytes;
260                }
261                AccessType::WriteNonPostedRequest => {
262                    self.stats.borrow_mut().bytes_written += payload_bytes;
263                    let response = access.to_response(self)?;
264                    response_tx.put(response)?.await;
265                }
266                AccessType::ReadResponse | AccessType::WriteNonPostedResponse => {
267                    return sim_error!("{}: unsupported {access_type} received", self.entity);
268                }
269                AccessType::Control => {
270                    todo!("control handling")
271                }
272            }
273
274            let ticks = payload_bytes.div_ceil(config.bw_bytes_per_cycle) as u64;
275            self.clock.wait_ticks(ticks).await;
276        }
277    }
278}
279
280impl<T> ReadMemory for Memory<T>
281where
282    T: SimObject + AccessMemory,
283{
284    fn read(&self) -> Vec<u8> {
285        Vec::new()
286    }
287}