1use 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 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}