uncore_sim/
mem.rs

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); // Sequential response
91          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}