1use std::{cell::RefCell, collections::{HashMap, VecDeque}, ffi::CString, path::Path, rc::Rc};
2
3use crate::drain::Drain;
4
5pub struct MemReq<const WIDTH: usize> {
6 pub id: usize,
7 pub addr: u64,
8 pub wbe: [bool; WIDTH],
9 pub wdata: [u8; WIDTH],
10}
11
12pub struct MemResp<const WIDTH: usize> {
13 pub id: usize,
14 pub rdata: [u8; WIDTH],
15}
16
17impl<const WIDTH: usize> crate::crossbar::Routable<u64> for MemReq<WIDTH> {
18 fn addr(&self) -> u64 {
19 self.addr
20 }
21}
22
23pub trait DelaySimulator {
24 fn tick(&mut self);
25 fn push(&mut self, addr: u64, is_write: bool);
26 fn pop(&mut self) -> Option<u64>;
27}
28
29#[derive(Default)]
30pub struct NoDelay {
31 queue: VecDeque<u64>,
32}
33
34impl DelaySimulator for NoDelay {
35 fn tick(&mut self) {}
36 fn push(&mut self, addr: u64, _is_write: bool) {
37 self.queue.push_back(addr);
38 }
39
40 fn pop(&mut self) -> Option<u64> {
41 self.queue.pop_front()
42 }
43}
44
45struct AddrProgress {
46 sent: u64,
47 recv: u64,
48 is_write: bool,
49}
50
51impl AddrProgress {
52 fn next_send(&self, base: u64, transfer: u64) -> u64 {
53 base + transfer * self.sent
54 }
55}
56
57struct Progress<const WIDTH: usize> {
58 transfer_width: u64,
59
60 progress: HashMap<u64, AddrProgress>,
61 done: VecDeque<u64>,
62}
63
64impl<const WIDTH: usize> Default for Progress<WIDTH> {
65 fn default() -> Self {
66 Progress {
67 transfer_width: WIDTH as u64,
68 progress: HashMap::new(),
69 done: VecDeque::new(),
70 }
71 }
72}
73
74impl<const WIDTH: usize> Progress<WIDTH> {
75 fn add(&mut self, addr: u64, is_write: bool) {
76 assert_eq!(addr % (WIDTH as u64), 0);
77 assert!(self.progress.insert(addr, AddrProgress {
78 sent: 0,
79 recv: 0,
80 is_write,
81 }).is_none());
82 }
83
84 fn step(&mut self, addr: u64) {
85 let aligned = addr - addr % WIDTH as u64;
86 let multiplicity = self.multiplicity();
87 match self.progress.entry(aligned) {
88 std::collections::hash_map::Entry::Occupied(mut o) => {
89 let prog = o.get_mut();
90 assert_eq!(aligned + prog.recv * self.transfer_width, addr); if prog.recv == multiplicity - 1 {
92 o.remove();
93 self.done.push_back(aligned);
94 } else {
95 prog.recv += 1;
96 }
97 }
98 std::collections::hash_map::Entry::Vacant(_) => panic!("Unexpected memory response"),
99 }
100 }
101
102 fn pop(&mut self) -> Option<u64> {
103 self.done.pop_front()
104 }
105
106 fn multiplicity(&self) -> u64 {
107 WIDTH as u64 / self.transfer_width
108 }
109}
110
111pub struct DRAMSim<const WIDTH: usize> {
112 sys: dramsim3::MemorySystem,
113 prog: Rc<RefCell<Progress<WIDTH>>>
114}
115
116impl<const WIDTH: usize> DRAMSim<WIDTH> {
117 pub fn new<Config: AsRef<Path>, Dir: AsRef<Path>>(config: Config, dir: Dir) -> Self {
118 let prog: Rc<RefCell<Progress<WIDTH>>> = Default::default();
119 let prog_cb = prog.clone();
120
121 let config_cstr = CString::new(config.as_ref().as_os_str().as_encoded_bytes()).unwrap();
122 let dir_cstr = CString::new(dir.as_ref().as_os_str().as_encoded_bytes()).unwrap();
123 let sys = dramsim3::MemorySystem::new(&config_cstr, &dir_cstr, move |addr, _is_write| {
124 prog_cb.borrow_mut().step(addr)
125 });
126
127 let transfer_width = sys.bus_bits() * sys.burst_length() / 8;
128 prog.borrow_mut().transfer_width = transfer_width as u64;
129
130 DRAMSim { sys, prog }
131 }
132}
133
134impl<const WIDTH: usize> DelaySimulator for DRAMSim<WIDTH> {
135 fn tick(&mut self) {
136 self.sys.tick();
137 let mut prog = self.prog.borrow_mut();
138 let multiplicity = prog.multiplicity();
139 let transfer_width = prog.transfer_width;
140 for (aligned, addr_prog) in prog.progress.iter_mut() {
141 if addr_prog.sent != multiplicity {
142 let next_addr = addr_prog.next_send(*aligned, transfer_width);
143 if self.sys.can_add(next_addr, addr_prog.is_write) {
144 self.sys.add(next_addr, addr_prog.is_write);
145 addr_prog.sent += 1;
146 }
147 }
148 }
149 }
150
151 fn push(&mut self, addr: u64, is_write: bool) {
152 self.prog.borrow_mut().add(addr, is_write);
153 }
154
155 fn pop(&mut self) -> Option<u64> {
156 self.prog.borrow_mut().pop()
157 }
158}
159
160pub struct Mem<D: DelaySimulator, const WIDTH: usize> {
161 sim: D,
162 content: HashMap<u64, [u8; WIDTH]>,
163 inflights: HashMap<u64, usize>,
164}
165
166impl<D: DelaySimulator, const WIDTH: usize> Mem<D, WIDTH> {
167 pub fn new(sim: D) -> Self {
168 Mem {
169 sim,
170 content: HashMap::new(),
171 inflights: HashMap::new(),
172 }
173 }
174}
175
176impl<D: DelaySimulator, const WIDTH: usize> Drain for Mem<D, WIDTH> {
177 type Req = MemReq<WIDTH>;
178 type Resp = MemResp<WIDTH>;
179 fn tick(&mut self) {
180 self.sim.tick();
181 }
182
183 fn push(&mut self, req: MemReq<WIDTH>) {
184 if self.inflights.insert(req.addr, req.id).is_some() {
185 panic!("Duplicated inflight memory requests");
186 }
187 self.sim.push(req.addr, req.wbe.contains(&true));
188 match self.content.entry(req.addr) {
189 std::collections::hash_map::Entry::Occupied(mut o) => {
190 for (c, (w, be)) in o.get_mut().iter_mut().zip(req.wdata.iter().zip(req.wbe.iter())) {
191 if *be { *c = *w; }
192 }
193 },
194 std::collections::hash_map::Entry::Vacant(v) => {
195 let mut buf = [0; WIDTH];
196 for (c, (w, be)) in buf.iter_mut().zip(req.wdata.iter().zip(req.wbe.iter())) {
197 if *be { *c = *w; }
198 }
199 v.insert(buf);
200 },
201 }
202 }
203
204 fn pop(&mut self) -> Option<MemResp<WIDTH>> {
205 self.sim.pop().map(|addr| {
206 let rdata = self.content.get(&addr).cloned().unwrap_or([0; WIDTH]);
207 let id = self.inflights.remove(&addr).expect("Unexpected memory response");
208 MemResp {
209 id,
210 rdata,
211 }
212 })
213 }
214}
215
216#[test]
217fn test_simple_sram() {
218 let mem: Mem<_, 256> = Mem::new(NoDelay::default());
219 test_simple(mem, 1);
220}
221
222#[test]
223fn test_simple_dram() {
224 let mut cfg = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
225 cfg.push("resources/test/DDR4_8Gb_x16_3200.ini");
226
227 let tmpdir = tempfile::tempdir().unwrap();
228
229 let dramsim: DRAMSim<256> = DRAMSim::new(cfg, &tmpdir);
230 let mem: Mem<_, 256> = Mem::new(dramsim);
231 test_simple(mem, 20);
232}
233
234#[cfg(test)]
235fn test_simple<D: DelaySimulator>(mut mem: Mem<D, 256>, wait: usize) {
236 use rand::Rng;
237
238 let mut rng = rand::thread_rng();
239 let mut buf = [0; 256];
240 rng.fill(&mut buf);
241
242 mem.tick();
243 mem.push(MemReq {
244 id: 1,
245 addr: 0x80004000,
246 wbe: [true; 256],
247 wdata: buf.clone(),
248 });
249
250 let mut done = false;
251 for t in 0..wait {
252 if let Some(r) = mem.pop() {
253 assert_eq!(r.id, 1);
254 done = true;
255 println!("Done at tick {}", t);
256 break;
257 }
258 mem.tick();
259 }
260 assert!(done);
261
262 mem.tick();
263 mem.push(MemReq {
264 id: 2,
265 addr: 0x80004000,
266 wbe: [false; 256],
267 wdata: [0; 256],
268 });
269
270 let mut done = false;
271 for t in 0..wait {
272 if let Some(r) = mem.pop() {
273 assert_eq!(r.id, 2);
274 assert_eq!(r.rdata, buf);
275 println!("Done at tick {}", t);
276 done = true;
277 break;
278 }
279 mem.tick();
280 }
281 assert!(done);
282}