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>
5 # SDRAM simulation PHY at DFI level tested with SDR/DDR/DDR2/LPDDR/DDR3
7 # - add multirank support.
10 from nmigen
.utils
import log2_int
12 from gram
.common
import burst_lengths
13 from gram
.phy
.dfi
import *
14 from gram
.modules
import _speedgrade_timings
, _technology_timings
16 from lambdasoc
.periph
import Peripheral
18 from functools
import reduce
19 from operator
import or_
28 # return Signal().eq(0)
34 # Bank Model ---------------------------------------------------------------------------------------
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()
43 self
.write_col
= Signal(range(ncols
))
44 self
.write_data
= Signal(data_width
)
45 self
.write_mask
= Signal(data_width
//8)
48 self
.read_col
= Signal(range(ncols
))
49 self
.read_data
= Signal(data_width
)
50 self
.nphases
= nphases
53 self
.burst_length
= burst_length
54 self
.data_width
= data_width
55 self
.we_granularity
= we_granularity
58 def elaborate(self
, platform
):
63 burst_length
= self
.burst_length
64 data_width
= self
.data_width
65 we_granularity
= self
.we_granularity
69 row
= Signal(range(nrows
))
71 with m
.If(self
.precharge
):
72 m
.d
.sync
+= active
.eq(0)
73 with m
.Elif(self
.activate
):
76 row
.eq(self
.activate_row
),
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
85 wraddr
= Signal(range(bank_mem_len
))
86 rdaddr
= Signal(range(bank_mem_len
))
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
),
95 write_port
.addr
.eq(wraddr
),
96 write_port
.data
.eq(self
.write_data
),
99 with m
.If(we_granularity
):
100 m
.d
.comb
+= write_port
.en
.eq(Repl(self
.write
, data_width
//8) & ~self
.write_mask
)
102 m
.d
.comb
+= write_port
.en
.eq(self
.write
)
104 with m
.If(self
.read
):
106 read_port
.addr
.eq(rdaddr
),
107 self
.read_data
.eq(read_port
.data
),
113 class _DQSBUFMSettingManager(Elaboratable
):
114 """DQSBUFM setting manager.
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.
123 CSR storing the rdly value.
127 pause : Signal(), out
128 Pause signal for DQSBUFM.
129 readclksel : Signal(3), out
130 Readclksel signal for DQSBUFM.
132 def __init__(self
, rdly_csr
):
133 self
.rdly_csr
= rdly_csr
135 self
.pause
= Signal()
136 self
.readclksel
= Signal(3)
138 def elaborate(self
, platform
):
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"
147 with m
.State("RdlyUpdateRequested"):
148 m
.d
.sync
+= self
.readclksel
.eq(self
.rdly_csr
.w_data
)
149 m
.next
= "ResetPause"
151 with m
.State("ResetPause"):
152 m
.d
.sync
+= self
.pause
.eq(0)
158 # DFI Phase Model ----------------------------------------------------------------------------------
160 class DFIPhaseModel(Elaboratable
):
161 def __init__(self
, dfi
, n
):
162 self
.phase
= dfi
.phases
[n
]
164 self
.bank
= self
.phase
.bank
165 self
.address
= self
.phase
.address
167 self
.wrdata
= self
.phase
.wrdata
168 self
.wrdata_mask
= self
.phase
.wrdata_mask
170 self
.rddata
= self
.phase
.rddata
171 self
.rddata_valid
= self
.phase
.rddata_valid
173 self
.activate
= Signal()
174 self
.precharge
= Signal()
175 self
.write
= Signal()
178 def elaborate(self
, platform
):
181 with m
.If(~self
.phase
.cs_n
& self
.phase
.ras
& ~self
.phase
.cas
):
183 self
.activate
.eq(~self
.phase
.we
),
184 self
.precharge
.eq(self
.phase
.we
),
187 with m
.If(~self
.phase
.cs_n
& ~self
.phase
.ras
& self
.phase
.cas
):
189 self
.write
.eq(self
.phase
.we
),
190 self
.read
.eq(~self
.phase
.we
),
195 # DFI Timings Checker ------------------------------------------------------------------------------
198 def __init__(self
, name
: str, enc
: int, idx
: int):
205 def __init__(self
, prev
: str, curr
: str, delay
: int):
206 self
.name
= prev
+ "->" + curr
212 class DFITimingsChecker(Elaboratable
):
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
225 ("PRE", "ACT", "tRP"),
226 ("PRE", "REF", "tRP"),
228 ("ACT", "WR", "tRCD"),
229 ("ACT", "RD", "tRCD"),
231 ("ACT", "PRE", "tRAS"),
233 ("REF", "PRE", "tRFC"),
234 ("REF", "ACT", "tRFC"),
236 ("WR", "RD", "tCCD"),
237 ("WR", "WR", "tCCD"),
238 ("RD", "RD", "tCCD"),
239 ("RD", "WR", "tCCD"),
241 ("ACT", "ACT", "tRC"),
243 ("WR", "PRE", "tWR"),
245 ("WR", "RD", "tWTR"),
247 ("ZQCS", "ACT", "tZQCS"),
252 for idx
, (name
, pattern
) in enumerate(self
.CMDS
):
253 self
.cmds
[name
] = SDRAMCMD(name
, int(pattern
, 2), idx
)
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
))
262 for rule
in self
.RULES
:
266 def ns_to_ps(self
, val
):
267 return int(val
* 1e3
)
269 def ck_ns_to_ps(self
, val
, tck
):
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
))
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
281 tck
= self
.timings
["tCK"]
283 for key
, val
in self
.timings
.items():
284 if refresh_mode
is not None and key
in REF
:
285 val
= val
[refresh_mode
]
290 val
= self
.ck_ns_to_ps(val
, tck
)
292 val
= self
.ns_to_ps(val
)
294 new_timings
[key
] = val
296 new_timings
["tRC"] = new_timings
["tRAS"] + new_timings
["tRP"]
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
304 self
.timings
= new_timings
306 def __init__(self
, dfi
, nbanks
, nphases
, timings
, refresh_mode
, memtype
, verbose
=False):
307 self
.prepare_timings(timings
, refresh_mode
, memtype
)
310 self
.nphases
= nphases
313 self
.timings
= timings
314 self
.refresh_mode
= refresh_mode
315 self
.memtype
= memtype
316 self
.verbose
= verbose
318 def elaborate(self
, platform
):
322 m
.d
.sync
+= cnt
.eq(cnt
+self
.nphases
)
324 phases
= self
.dfi
.phases
326 timings
= self
.timings
327 refresh_mode
= self
.refresh_mode
328 memtype
= self
.memtype
329 verbose
= self
.verbose
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
)]
334 act_ps
= Array([Signal().like(cnt
) for i
in range(4)])
335 act_curr
= Signal(range(4))
337 ref_issued
= Signal(self
.nphases
)
339 for np
, phase
in enumerate(phases
):
340 ps
= Signal().like(cnt
)
341 m
.d
.comb
+= ps
.eq((cnt
+ np
)*int(self
.timings
["tCK"]))
343 m
.d
.comb
+= state
.eq(Cat(phase
.we
, phase
.cas
, phase
.ras
,
347 m
.d
.comb
+= all_banks
.eq(
348 (self
.cmds
["REF"].enc
== state
) |
349 ((self
.cmds
["PRE"].enc
== state
) & phase
.address
[10])
353 m
.d
.comb
+= ref_issued
[np
].eq(self
.cmds
["REF"].enc
== state
)
355 # Print debug information
356 # TODO: find a way to bring back logging
358 # for _, cmd in self.cmds.items():
360 # If(state == cmd.enc,
362 # Display("[%016dps] P%0d " + cmd.name, ps, np)
364 # Display("[%016dps] P%0d B%0d " + cmd.name, ps, np, phase.bank)
369 # Bank command monitoring
370 for i
in range(nbanks
):
371 for _
, curr
in self
.cmds
.items():
373 m
.d
.comb
+= cmd_recv
.eq(((phase
.bank
== i
) | all_banks
) & (state
== curr
.enc
))
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
))))
382 # Save command timestamp in an array
385 last_cmd_ps
[i
][curr
.idx
].eq(ps
),
386 last_cmd
[i
].eq(state
),
390 if curr
.name
== "ACT":
391 act_next
= Signal().like(act_curr
)
392 m
.d
.comb
+= act_next
.eq(act_curr
+1)
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"])))))
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"])))))
402 # Save ACT timestamp in a circular buffer
405 act_ps
[act_next
].eq(ps
),
406 act_curr
.eq(act_next
),
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
)
415 m
.d
.comb
+= curr_diff
.eq(ps
- (ref_ps
+ int(self
.timings
["tREFI"])))
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"]))
421 m
.d
.sync
+= ref_ps_mod
.eq(0)
423 # Update timestamp and difference
424 with m
.If(ref_issued
!= 0):
427 ref_ps_diff
.eq(ref_ps_diff
- curr_diff
),
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)))
433 # Report any refresh periods longer than tREFI
434 # TODO: find a way to bring back logging
436 # ref_done = Signal()
438 # If(ref_issued != 0,
441 # Display("[%016dps] Late refresh", ps)
447 # If((curr_diff > 0) & ref_done & (ref_issued == 0),
448 # Display("[%016dps] tREFI violation", ps),
453 # There is a maximum delay between refreshes on >=DDR
454 ref_limit
= {"1x": 9, "2x": 17, "4x": 36}
456 refresh_mode
= "1x" if refresh_mode
is None else refresh_mode
458 with m
.If(ref_issued
!= 0):
459 m
.d
.sync
+= ref_done
.eq(1)
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)
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),
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
)]
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
))
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)
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
]
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
):
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
):
525 bank_init
[bank
] = init
[start
:end
]
529 def __init__(self
, module
, settings
, clk_freq
=100e6
,
532 address_mapping
= "ROW_BANK_COL",
533 verbosity
= SDRAM_VERBOSE_OFF
):
534 super().__init
__(name
="fakephy")
536 # Parameters -------------------------------------------------------------------------------
537 self
.burst_length
= {
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
551 self
.settings
= settings
554 self
.verbosity
= verbosity
555 self
.clk_freq
= clk_freq
556 self
.we_granularity
= we_granularity
561 databits
= self
.settings
.dfi_databits
562 bank
= self
.csr_bank()
564 self
.burstdet
= bank
.csr(databits
//8, "rw")
567 self
.rdly
+= [bank
.csr(3, "rw", name
="rdly_p0")]
568 self
.rdly
+= [bank
.csr(3, "rw", name
="rdly_p1")]
570 self
._bridge
= self
.bridge(data_width
=32, granularity
=8, alignment
=2)
571 self
.bus
= self
._bridge
.bus
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
,
583 def elaborate(self
, platform
):
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
593 m
.submodules
.bridge
= self
._bridge
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
)
601 with m
.If(self
.burstdet
.w_stb
):
602 m
.d
.sync
+= burstdet_reg
.eq(0)
605 # DFI phases ---------------------------------------------------
607 for i
in range(self
.settings
.nphases
):
608 phase
= DFIPhaseModel(self
.dfi
, i
)
609 m
.submodules
['phase%d' % i
] = phase
612 dqsbufm_manager
= _DQSBUFMSettingManager(self
.rdly
[i
])
613 m
.submodules
["dqsbufm_manager%i" % i
] = dqsbufm_manager
615 # DFI timing checker -----------------------------------------------------------------------
616 if self
.verbosity
> SDRAM_VERBOSE_OFF
:
617 timings
= {"tCK": (1e9
/ self
.clk_freq
) / nphases
}
619 for name
in _speedgrade_timings
+ _technology_timings
:
620 timings
[name
] = self
.module
.get(name
)
622 timing_checker
= DFITimingsChecker(
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
632 # Bank init data ---------------------------------------------------------------------------
633 bank_init
= [None for i
in range(nbanks
)]
636 bank_init
= self
.__prepare
_bank
_init
_data
(
641 data_width
= data_width
,
642 address_mapping
= address_mapping
645 # Banks ------------------------------------------------------------------------------------
647 data_width
= data_width
,
650 burst_length
= self
.burst_length
,
652 we_granularity
= self
.we_granularity
,
653 init
= bank_init
[i
]) for i
in range(nbanks
)]
654 m
.submodules
+= banks
656 # Connect DFI phases to Banks (CMDs, Write datapath) ---------------------------------------
657 for nb
, bank
in enumerate(banks
):
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
)
665 bank
.activate
.eq(phase
.bank
== nb
),
666 bank
.activate_row
.eq(phase
.address
)
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
)
675 m
.d
.comb
+= bank
.precharge
.eq((phase
.bank
== nb
) | phase
.address
[10])
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
)
686 bank_write
.eq(phase
.bank
== nb
),
687 bank_write_col
.eq(phase
.address
)
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
]))
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
))
699 new_bank_write
.eq(bank_write
),
700 new_bank_write_col
.eq(bank_write_col
)
702 bank_write
= new_bank_write
703 bank_write_col
= new_bank_write_col
706 bank
.write
.eq(bank_write
),
707 bank
.write_col
.eq(bank_write_col
)
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
)
717 bank
.read
.eq(phase
.bank
== nb
),
718 bank
.read_col
.eq(phase
.address
),
721 # Connect Banks to DFI phases (CMDs, Read datapath) ----------------------------------------
722 banks_read
= Signal()
723 banks_read_data
= Signal(data_width
)
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
]))
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
)
734 new_banks_read
.eq(banks_read
),
735 new_banks_read_data
.eq(banks_read_data
)
737 banks_read
= new_banks_read
738 banks_read_data
= new_banks_read_data
741 Cat(*[phase
.rddata_valid
for phase
in phases
]).eq(banks_read
),
742 Cat(*[phase
.rddata
for phase
in phases
]).eq(banks_read_data
)