restore naming convention "cs_r" on DFI Interface
[gram.git] / gram / phy / fakephy.py
1 # This file is Copyright (c) 2015-2020 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2020 Antmicro <www.antmicro.com>
3 # License: BSD
4
5 # SDRAM simulation PHY at DFI level tested with SDR/DDR/DDR2/LPDDR/DDR3
6 # TODO:
7 # - add multirank support.
8
9 from nmigen import *
10 from nmigen.utils import log2_int
11
12 from gram.common import burst_lengths
13 from gram.phy.dfi import *
14 from gram.modules import _speedgrade_timings, _technology_timings
15
16 from lambdasoc.periph import Peripheral
17
18 from functools import reduce
19 from operator import or_
20
21 import struct
22
23 SDRAM_VERBOSE_OFF = 0
24 SDRAM_VERBOSE_STD = 1
25 SDRAM_VERBOSE_DBG = 2
26
27 #def Display(*args):
28 # return Signal().eq(0)
29
30 def Assert(*args):
31 return Signal().eq(0)
32
33
34 # Bank Model ---------------------------------------------------------------------------------------
35
36 class BankModel(Elaboratable):
37 def __init__(self, data_width, nrows, ncols, burst_length, nphases, we_granularity, init):
38 self.activate = Signal()
39 self.activate_row = Signal(range(nrows))
40 self.precharge = Signal()
41
42 self.write = Signal()
43 self.write_col = Signal(range(ncols))
44 self.write_data = Signal(data_width)
45 self.write_mask = Signal(data_width//8)
46
47 self.read = Signal()
48 self.read_col = Signal(range(ncols))
49 self.read_data = Signal(data_width)
50 self.nphases = nphases
51 self.nrows = nrows
52 self.ncols = ncols
53 self.burst_length = burst_length
54 self.data_width = data_width
55 self.we_granularity = we_granularity
56 self.init = init
57
58 def elaborate(self, platform):
59 m = Module()
60
61 nrows = self.nrows
62 ncols = self.ncols
63 burst_length = self.burst_length
64 data_width = self.data_width
65 we_granularity = self.we_granularity
66 init = self.init
67
68 active = Signal()
69 row = Signal(range(nrows))
70
71 with m.If(self.precharge):
72 m.d.sync += active.eq(0)
73 with m.Elif(self.activate):
74 m.d.sync += [
75 active.eq(1),
76 row.eq(self.activate_row),
77 ]
78
79 bank_mem_len = nrows*ncols//(burst_length*self.nphases)
80 mem = Memory(width=data_width, depth=1024, init=init)
81 write_port = mem.write_port(granularity=we_granularity)
82 read_port = mem.read_port(domain="comb")
83 m.submodules += read_port, write_port
84
85 wraddr = Signal(range(bank_mem_len))
86 rdaddr = Signal(range(bank_mem_len))
87
88 m.d.comb += [
89 wraddr.eq((row*ncols | self.write_col)[log2_int(burst_length*self.nphases):] % mem.depth),
90 rdaddr.eq((row*ncols | self.read_col)[log2_int(burst_length*self.nphases):] % mem.depth),
91 ]
92
93 with m.If(active):
94 m.d.comb += [
95 write_port.addr.eq(wraddr),
96 write_port.data.eq(self.write_data),
97 ]
98
99 with m.If(we_granularity):
100 m.d.comb += write_port.en.eq(Repl(self.write, data_width//8) & ~self.write_mask)
101 with m.Else():
102 m.d.comb += write_port.en.eq(self.write)
103
104 with m.If(self.read):
105 m.d.comb += [
106 read_port.addr.eq(rdaddr),
107 self.read_data.eq(read_port.data),
108 ]
109
110 return m
111
112
113 class _DQSBUFMSettingManager(Elaboratable):
114 """DQSBUFM setting manager.
115
116 The DQSBUFM primitive requires a very basic sequence when updating
117 read delay or other parameters. This elaboratable generates this
118 sequence from CSR events.
119
120 Parameters
121 ----------
122 rdly_slr : CSR
123 CSR storing the rdly value.
124
125 Attributes
126 ----------
127 pause : Signal(), out
128 Pause signal for DQSBUFM.
129 readclksel : Signal(3), out
130 Readclksel signal for DQSBUFM.
131 """
132 def __init__(self, rdly_csr):
133 self.rdly_csr = rdly_csr
134
135 self.pause = Signal()
136 self.readclksel = Signal(3)
137
138 def elaborate(self, platform):
139 m = Module()
140
141 with m.FSM():
142 with m.State("Idle"):
143 with m.If(self.rdly_csr.w_stb):
144 m.d.sync += self.pause.eq(1)
145 m.next = "RdlyUpdateRequested"
146
147 with m.State("RdlyUpdateRequested"):
148 m.d.sync += self.readclksel.eq(self.rdly_csr.w_data)
149 m.next = "ResetPause"
150
151 with m.State("ResetPause"):
152 m.d.sync += self.pause.eq(0)
153 m.next = "Idle"
154
155 return m
156
157
158 # DFI Phase Model ----------------------------------------------------------------------------------
159
160 class DFIPhaseModel(Elaboratable):
161 def __init__(self, dfi, n):
162 self.phase = dfi.phases[n]
163
164 self.bank = self.phase.bank
165 self.address = self.phase.address
166
167 self.wrdata = self.phase.wrdata
168 self.wrdata_mask = self.phase.wrdata_mask
169
170 self.rddata = self.phase.rddata
171 self.rddata_valid = self.phase.rddata_valid
172
173 self.activate = Signal()
174 self.precharge = Signal()
175 self.write = Signal()
176 self.read = Signal()
177
178 def elaborate(self, platform):
179 m = Module()
180
181 with m.If(~self.phase.cs_n & self.phase.ras & ~self.phase.cas):
182 m.d.comb += [
183 self.activate.eq(~self.phase.we),
184 self.precharge.eq(self.phase.we),
185 ]
186
187 with m.If(~self.phase.cs_n & ~self.phase.ras & self.phase.cas):
188 m.d.comb += [
189 self.write.eq(self.phase.we),
190 self.read.eq(~self.phase.we),
191 ]
192
193 return m
194
195 # DFI Timings Checker ------------------------------------------------------------------------------
196
197 class SDRAMCMD:
198 def __init__(self, name: str, enc: int, idx: int):
199 self.name = name
200 self.enc = enc
201 self.idx = idx
202
203
204 class TimingRule:
205 def __init__(self, prev: str, curr: str, delay: int):
206 self.name = prev + "->" + curr
207 self.prev = prev
208 self.curr = curr
209 self.delay = delay
210
211
212 class DFITimingsChecker(Elaboratable):
213 CMDS = [
214 # Name, cs & ras & cas & we value
215 ("PRE", "1101"), # Precharge
216 ("REF", "1110"), # Self refresh
217 ("ACT", "1100"), # Activate
218 ("RD", "1010"), # Read
219 ("WR", "1011"), # Write
220 ("ZQCS", "1001"), # ZQCS
221 ]
222
223 RULES = [
224 # tRP
225 ("PRE", "ACT", "tRP"),
226 ("PRE", "REF", "tRP"),
227 # tRCD
228 ("ACT", "WR", "tRCD"),
229 ("ACT", "RD", "tRCD"),
230 # tRAS
231 ("ACT", "PRE", "tRAS"),
232 # tRFC
233 ("REF", "PRE", "tRFC"),
234 ("REF", "ACT", "tRFC"),
235 # tCCD
236 ("WR", "RD", "tCCD"),
237 ("WR", "WR", "tCCD"),
238 ("RD", "RD", "tCCD"),
239 ("RD", "WR", "tCCD"),
240 # tRC
241 ("ACT", "ACT", "tRC"),
242 # tWR
243 ("WR", "PRE", "tWR"),
244 # tWTR
245 ("WR", "RD", "tWTR"),
246 # tZQCS
247 ("ZQCS", "ACT", "tZQCS"),
248 ]
249
250 def add_cmds(self):
251 self.cmds = {}
252 for idx, (name, pattern) in enumerate(self.CMDS):
253 self.cmds[name] = SDRAMCMD(name, int(pattern, 2), idx)
254
255 def add_rule(self, prev, curr, delay):
256 if not isinstance(delay, int):
257 delay = self.timings[delay]
258 self.rules.append(TimingRule(prev, curr, delay))
259
260 def add_rules(self):
261 self.rules = []
262 for rule in self.RULES:
263 self.add_rule(*rule)
264
265 # Convert ns to ps
266 def ns_to_ps(self, val):
267 return int(val * 1e3)
268
269 def ck_ns_to_ps(self, val, tck):
270 c, t = val
271 c = 0 if c is None else c * tck
272 t = 0 if t is None else t
273 return self.ns_to_ps(max(c, t))
274
275 def prepare_timings(self, timings, refresh_mode, memtype):
276 CK_NS = ["tRFC", "tWTR", "tFAW", "tCCD", "tRRD", "tZQCS"]
277 REF = ["tREFI", "tRFC"]
278 self.timings = timings
279 new_timings = {}
280
281 tck = self.timings["tCK"]
282
283 for key, val in self.timings.items():
284 if refresh_mode is not None and key in REF:
285 val = val[refresh_mode]
286
287 if val is None:
288 val = 0
289 elif key in CK_NS:
290 val = self.ck_ns_to_ps(val, tck)
291 else:
292 val = self.ns_to_ps(val)
293
294 new_timings[key] = val
295
296 new_timings["tRC"] = new_timings["tRAS"] + new_timings["tRP"]
297
298 # Adjust timings relative to write burst - tWR & tWTR
299 wrburst = burst_lengths[memtype] if memtype == "SDR" else burst_lengths[memtype] // 2
300 wrburst = (new_timings["tCK"] * (wrburst - 1))
301 new_timings["tWR"] = new_timings["tWR"] + wrburst
302 new_timings["tWTR"] = new_timings["tWTR"] + wrburst
303
304 self.timings = new_timings
305
306 def __init__(self, dfi, nbanks, nphases, timings, refresh_mode, memtype, verbose=False):
307 self.prepare_timings(timings, refresh_mode, memtype)
308 self.add_cmds()
309 self.add_rules()
310 self.nphases = nphases
311 self.nbanks = nbanks
312 self.dfi = dfi
313 self.timings = timings
314 self.refresh_mode = refresh_mode
315 self.memtype = memtype
316 self.verbose = verbose
317
318 def elaborate(self, platform):
319 m = Module()
320
321 cnt = Signal(64)
322 m.d.sync += cnt.eq(cnt+self.nphases)
323
324 phases = self.dfi.phases
325 nbanks = self.nbanks
326 timings = self.timings
327 refresh_mode = self.refresh_mode
328 memtype = self.memtype
329 verbose = self.verbose
330
331 last_cmd_ps = [[Signal.like(cnt) for _ in range(len(self.cmds))] for _ in range(nbanks)]
332 last_cmd = [Signal(4) for i in range(nbanks)]
333
334 act_ps = Array([Signal().like(cnt) for i in range(4)])
335 act_curr = Signal(range(4))
336
337 ref_issued = Signal(self.nphases)
338
339 for np, phase in enumerate(phases):
340 ps = Signal().like(cnt)
341 m.d.comb += ps.eq((cnt + np)*int(self.timings["tCK"]))
342 state = Signal(4)
343 m.d.comb += state.eq(Cat(phase.we, phase.cas, phase.ras,
344 phase.cs_n))
345 all_banks = Signal()
346
347 m.d.comb += all_banks.eq(
348 (self.cmds["REF"].enc == state) |
349 ((self.cmds["PRE"].enc == state) & phase.address[10])
350 )
351
352 # tREFI
353 m.d.comb += ref_issued[np].eq(self.cmds["REF"].enc == state)
354
355 # Print debug information
356 # TODO: find a way to bring back logging
357 # if verbose:
358 # for _, cmd in self.cmds.items():
359 # self.sync += [
360 # If(state == cmd.enc,
361 # If(all_banks,
362 # Display("[%016dps] P%0d " + cmd.name, ps, np)
363 # ).Else(
364 # Display("[%016dps] P%0d B%0d " + cmd.name, ps, np, phase.bank)
365 # )
366 # )
367 # ]
368
369 # Bank command monitoring
370 for i in range(nbanks):
371 for _, curr in self.cmds.items():
372 cmd_recv = Signal()
373 m.d.comb += cmd_recv.eq(((phase.bank == i) | all_banks) & (state == curr.enc))
374
375 # Checking rules from self.rules
376 for _, prev in self.cmds.items():
377 for rule in self.rules:
378 if rule.prev == prev.name and rule.curr == curr.name:
379 # Display("[%016dps] {} violation on bank %0d".format(rule.name), ps, i)
380 m.d.sync += Assert(~(cmd_recv & (last_cmd[i] == prev.enc) & (ps < (last_cmd_ps[i][prev.idx] + rule.delay))))
381
382 # Save command timestamp in an array
383 with m.If(cmd_recv):
384 m.d.comb += [
385 last_cmd_ps[i][curr.idx].eq(ps),
386 last_cmd[i].eq(state),
387 ]
388
389 # tRRD & tFAW
390 if curr.name == "ACT":
391 act_next = Signal().like(act_curr)
392 m.d.comb += act_next.eq(act_curr+1)
393
394 # act_curr points to newest ACT timestamp
395 #Display("[%016dps] tRRD violation on bank %0d", ps, i)
396 #m.d.sync += Assert(~(cmd_recv & (ps < (act_ps[act_curr] + int(self.timings["tRRD"])))))
397
398 # act_next points to the oldest ACT timestamp
399 #Display("[%016dps] tFAW violation on bank %0d", ps, i)
400 #m.d.sync += Assert(~(cmd_recv & (ps < (act_ps[act_next] + int(self.timings["tFAW"])))))
401
402 # Save ACT timestamp in a circular buffer
403 with m.If(cmd_recv):
404 m.d.sync += [
405 act_ps[act_next].eq(ps),
406 act_curr.eq(act_next),
407 ]
408
409 # tREFI
410 ref_ps = Signal().like(cnt)
411 ref_ps_mod = Signal().like(cnt)
412 ref_ps_diff = Signal(signed(64))
413 curr_diff = Signal().like(ref_ps_diff)
414
415 m.d.comb += curr_diff.eq(ps - (ref_ps + int(self.timings["tREFI"])))
416
417 # Work in 64ms periods
418 with m.If(ref_ps_mod < int(64e9)):
419 m.d.sync += ref_ps_mod.eq(ref_ps_mod + int(self.nphases * self.timings["tCK"]))
420 with m.Else():
421 m.d.sync += ref_ps_mod.eq(0)
422
423 # Update timestamp and difference
424 with m.If(ref_issued != 0):
425 m.d.sync += [
426 ref_ps.eq(ps),
427 ref_ps_diff.eq(ref_ps_diff - curr_diff),
428 ]
429
430 #Display("[%016dps] tREFI violation (64ms period): %0d", ps, ref_ps_diff)
431 m.d.sync += Assert(~((ref_ps_mod == 0) & (ref_ps_diff > 0)))
432
433 # Report any refresh periods longer than tREFI
434 # TODO: find a way to bring back logging
435 # if verbose:
436 # ref_done = Signal()
437 # self.sync += [
438 # If(ref_issued != 0,
439 # ref_done.eq(1),
440 # If(~ref_done,
441 # Display("[%016dps] Late refresh", ps)
442 # )
443 # )
444 # ]
445
446 # self.sync += [
447 # If((curr_diff > 0) & ref_done & (ref_issued == 0),
448 # Display("[%016dps] tREFI violation", ps),
449 # ref_done.eq(0)
450 # )
451 # ]
452
453 # There is a maximum delay between refreshes on >=DDR
454 ref_limit = {"1x": 9, "2x": 17, "4x": 36}
455 if memtype != "SDR":
456 refresh_mode = "1x" if refresh_mode is None else refresh_mode
457 ref_done = Signal()
458 with m.If(ref_issued != 0):
459 m.d.sync += ref_done.eq(1)
460
461 with m.If((ref_issued == 0) & ref_done &
462 (ref_ps > (ps + int(ref_limit[refresh_mode] * self.timings['tREFI'])))):
463 m.d.sync += ref_done.eq(0)
464 # self.sync += [
465 # If((ref_issued == 0) & ref_done &
466 # (ref_ps > (ps + ref_limit[refresh_mode] * self.timings['tREFI'])),
467 # Display("[%016dps] tREFI violation (too many postponed refreshes)", ps),
468 # ref_done.eq(0)
469 # )
470 # ]
471
472 return m
473
474
475 class FakePHY(Peripheral, Elaboratable):
476 def __prepare_bank_init_data(self, init, nbanks, nrows, ncols, data_width, address_mapping):
477 mem_size = (self.settings.databits//8)*(nrows*ncols*nbanks)
478 bank_size = mem_size // nbanks
479 column_size = bank_size // nrows
480 model_bank_size = bank_size // (data_width//8)
481 model_column_size = model_bank_size // nrows
482 model_data_ratio = data_width // 32
483 data_width_bytes = data_width // 8
484 bank_init = [[] for i in range(nbanks)]
485
486 # Pad init if too short
487 if len(init)%data_width_bytes != 0:
488 init.extend([0]*(data_width_bytes-len(init)%data_width_bytes))
489
490
491 # Convert init data width from 32-bit to data_width if needed
492 if model_data_ratio > 1:
493 new_init = [0]*(len(init)//model_data_ratio)
494 for i in range(0, len(init), model_data_ratio):
495 ints = init[i:i+model_data_ratio]
496 strs = "".join("{:08x}".format(x) for x in reversed(ints))
497 new_init[i//model_data_ratio] = int(strs, 16)
498 init = new_init
499 elif model_data_ratio == 0:
500 assert data_width_bytes in [1, 2]
501 model_data_ratio = 4 // data_width_bytes
502 struct_unpack_patterns = {1: "4B", 2: "2H"}
503 new_init = [0]*int(len(init)*model_data_ratio)
504 for i in range(len(init)):
505 new_init[model_data_ratio*i:model_data_ratio*(i+1)] = struct.unpack(
506 struct_unpack_patterns[data_width_bytes],
507 struct.pack("I", init[i])
508 )[0:model_data_ratio]
509 init = new_init
510
511 if address_mapping == "ROW_BANK_COL":
512 for row in range(nrows):
513 for bank in range(nbanks):
514 start = (row*nbanks*model_column_size + bank*model_column_size)
515 end = min(start + model_column_size, len(init))
516 if start > len(init):
517 break
518 bank_init[bank].extend(init[start:end])
519 elif address_mapping == "BANK_ROW_COL":
520 for bank in range(nbanks):
521 start = bank*model_bank_size
522 end = min(start + model_bank_size, len(init))
523 if start > len(init):
524 break
525 bank_init[bank] = init[start:end]
526
527 return bank_init
528
529 def __init__(self, module, settings, clk_freq=100e6,
530 we_granularity = 8,
531 init = [],
532 address_mapping = "ROW_BANK_COL",
533 verbosity = SDRAM_VERBOSE_OFF):
534 super().__init__(name="fakephy")
535
536 # Parameters -------------------------------------------------------------------------------
537 self.burst_length = {
538 "SDR": 1,
539 "DDR": 2,
540 "LPDDR": 2,
541 "DDR2": 2,
542 "DDR3": 2,
543 "DDR4": 2,
544 }[settings.memtype]
545
546 self.addressbits = module.geom_settings.addressbits
547 self.bankbits = module.geom_settings.bankbits
548 self.rowbits = module.geom_settings.rowbits
549 self.colbits = module.geom_settings.colbits
550
551 self.settings = settings
552 self.module = module
553
554 self.verbosity = verbosity
555 self.clk_freq = clk_freq
556 self.we_granularity = we_granularity
557
558 self.init = init
559
560 # CSR
561 databits = self.settings.dfi_databits
562 bank = self.csr_bank()
563
564 self.burstdet = bank.csr(databits//8, "rw")
565
566 self.rdly = []
567 self.rdly += [bank.csr(3, "rw", name="rdly_p0")]
568 self.rdly += [bank.csr(3, "rw", name="rdly_p1")]
569
570 self._bridge = self.bridge(data_width=32, granularity=8, alignment=2)
571 self.bus = self._bridge.bus
572
573 # DFI Interface ----------------------------------------------------------------------------
574 self.dfi = Interface(
575 addressbits = self.addressbits,
576 bankbits = self.bankbits,
577 nranks = self.settings.nranks,
578 databits = self.settings.dfi_databits,
579 nphases = self.settings.nphases,
580 name="phy"
581 )
582
583 def elaborate(self, platform):
584 m = Module()
585
586 nphases = self.settings.nphases
587 nbanks = 2**self.bankbits
588 nrows = 2**self.rowbits
589 ncols = 2**self.colbits
590 data_width = self.settings.dfi_databits*self.settings.nphases
591
592 # CSR Bridge
593 m.submodules.bridge = self._bridge
594
595 # fake burstdet
596 databits = self.settings.dfi_databits
597 burstdet_reg = Signal(databits//8, reset_less=True)
598 m.d.comb += self.burstdet.r_data.eq(burstdet_reg)
599
600 # Burstdet clear
601 with m.If(self.burstdet.w_stb):
602 m.d.sync += burstdet_reg.eq(0)
603
604
605 # DFI phases ---------------------------------------------------
606 phases = []
607 for i in range(self.settings.nphases):
608 phase = DFIPhaseModel(self.dfi, i)
609 m.submodules['phase%d' % i] = phase
610 phases.append(phase)
611
612 dqsbufm_manager = _DQSBUFMSettingManager(self.rdly[i])
613 m.submodules["dqsbufm_manager%i" % i] = dqsbufm_manager
614
615 # DFI timing checker -----------------------------------------------------------------------
616 if self.verbosity > SDRAM_VERBOSE_OFF:
617 timings = {"tCK": (1e9 / self.clk_freq) / nphases}
618
619 for name in _speedgrade_timings + _technology_timings:
620 timings[name] = self.module.get(name)
621
622 timing_checker = DFITimingsChecker(
623 dfi = self.dfi,
624 nbanks = nbanks,
625 nphases = nphases,
626 timings = timings,
627 refresh_mode = self.module.timing_settings.fine_refresh_mode,
628 memtype = self.settings.memtype,
629 verbose = self.verbosity > SDRAM_VERBOSE_DBG)
630 m.submodules.timing_checker = timing_checker
631
632 # Bank init data ---------------------------------------------------------------------------
633 bank_init = [None for i in range(nbanks)]
634
635 if self.init:
636 bank_init = self.__prepare_bank_init_data(
637 init = self.init,
638 nbanks = nbanks,
639 nrows = nrows,
640 ncols = ncols,
641 data_width = data_width,
642 address_mapping = address_mapping
643 )
644
645 # Banks ------------------------------------------------------------------------------------
646 banks = [BankModel(
647 data_width = data_width,
648 nrows = nrows,
649 ncols = ncols,
650 burst_length = self.burst_length,
651 nphases = nphases,
652 we_granularity = self.we_granularity,
653 init = bank_init[i]) for i in range(nbanks)]
654 m.submodules += banks
655
656 # Connect DFI phases to Banks (CMDs, Write datapath) ---------------------------------------
657 for nb, bank in enumerate(banks):
658 # Bank activate
659 activates = Signal(len(phases))
660 with m.Switch(activates):
661 for np, phase in enumerate(phases):
662 m.d.comb += activates[np].eq(phase.activate)
663 with m.Case(2**np):
664 m.d.comb += [
665 bank.activate.eq(phase.bank == nb),
666 bank.activate_row.eq(phase.address)
667 ]
668
669 # Bank precharge
670 precharges = Signal(len(phases))
671 with m.Switch(precharges):
672 for np, phase in enumerate(phases):
673 m.d.comb += precharges[np].eq(phase.precharge)
674 with m.Case(2**np):
675 m.d.comb += bank.precharge.eq((phase.bank == nb) | phase.address[10])
676
677 # Bank writes
678 bank_write = Signal()
679 bank_write_col = Signal(range(ncols))
680 writes = Signal(len(phases))
681 with m.Switch(writes):
682 for np, phase in enumerate(phases):
683 m.d.comb += writes[np].eq(phase.write)
684 with m.Case(2**np):
685 m.d.comb += [
686 bank_write.eq(phase.bank == nb),
687 bank_write_col.eq(phase.address)
688 ]
689 m.d.comb += [
690 bank.write_data.eq(Cat(*[phase.wrdata for phase in phases])),
691 bank.write_mask.eq(Cat(*[phase.wrdata_mask for phase in phases]))
692 ]
693
694 # Simulate write latency
695 for i in range(self.settings.write_latency):
696 new_bank_write = Signal()
697 new_bank_write_col = Signal(range(ncols))
698 m.d.sync += [
699 new_bank_write.eq(bank_write),
700 new_bank_write_col.eq(bank_write_col)
701 ]
702 bank_write = new_bank_write
703 bank_write_col = new_bank_write_col
704
705 m.d.comb += [
706 bank.write.eq(bank_write),
707 bank.write_col.eq(bank_write_col)
708 ]
709
710 # Bank reads
711 reads = Signal(len(phases))
712 with m.Switch(reads):
713 for np, phase in enumerate(phases):
714 m.d.comb += reads[np].eq(phase.read)
715 with m.Case(2**np):
716 m.d.comb += [
717 bank.read.eq(phase.bank == nb),
718 bank.read_col.eq(phase.address),
719 ]
720
721 # Connect Banks to DFI phases (CMDs, Read datapath) ----------------------------------------
722 banks_read = Signal()
723 banks_read_data = Signal(data_width)
724 m.d.comb += [
725 banks_read.eq(reduce(or_, [bank.read for bank in banks])),
726 banks_read_data.eq(reduce(or_, [bank.read_data for bank in banks]))
727 ]
728
729 # Simulate read latency --------------------------------------------------------------------
730 for i in range(self.settings.read_latency):
731 new_banks_read = Signal()
732 new_banks_read_data = Signal(data_width)
733 m.d.sync += [
734 new_banks_read.eq(banks_read),
735 new_banks_read_data.eq(banks_read_data)
736 ]
737 banks_read = new_banks_read
738 banks_read_data = new_banks_read_data
739
740 m.d.comb += [
741 Cat(*[phase.rddata_valid for phase in phases]).eq(banks_read),
742 Cat(*[phase.rddata for phase in phases]).eq(banks_read_data)
743 ]
744
745 return m