1b53722f58b59588791f4b1588ab3f49a8ac31b2
[gram.git] / gram / common.py
1 # This file is Copyright (c) 2016-2019 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2018 John Sully <john@csquare.ca>
3 # This file is Copyright (c) 2018 bunnie <bunnie@kosagi.com>
4 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
5 # License: BSD
6
7 import math
8 from functools import reduce
9 from operator import add
10 from collections import OrderedDict
11
12 from nmigen import *
13 from nmigen.asserts import Assert, Assume
14 from nmigen.hdl.rec import *
15 from nmigen.utils import log2_int
16
17 import gram.stream as stream
18
19 # Helpers ------------------------------------------------------------------------------------------
20
21 burst_lengths = {
22 "SDR": 1,
23 "DDR": 4,
24 "LPDDR": 4,
25 "DDR2": 4,
26 "DDR3": 8,
27 "DDR4": 8
28 }
29
30
31 def get_cl_cw(memtype, tck):
32 f_to_cl_cwl = OrderedDict()
33 if memtype == "DDR2":
34 f_to_cl_cwl[400e6] = (3, 2)
35 f_to_cl_cwl[533e6] = (4, 3)
36 f_to_cl_cwl[677e6] = (5, 4)
37 f_to_cl_cwl[800e6] = (6, 5)
38 f_to_cl_cwl[1066e6] = (7, 5)
39 elif memtype == "DDR3":
40 f_to_cl_cwl[800e6] = (6, 5)
41 f_to_cl_cwl[1066e6] = (7, 6)
42 f_to_cl_cwl[1333e6] = (10, 7)
43 f_to_cl_cwl[1600e6] = (11, 8)
44 elif memtype == "DDR4":
45 f_to_cl_cwl[1600e6] = (11, 9)
46 else:
47 raise ValueError
48 for f, (cl, cwl) in f_to_cl_cwl.items():
49 if tck >= 2/f:
50 return cl, cwl
51 raise ValueError
52
53
54 def get_sys_latency(nphases, cas_latency):
55 return math.ceil(cas_latency/nphases)
56
57
58 def get_sys_phases(nphases, sys_latency, cas_latency):
59 dat_phase = sys_latency*nphases - cas_latency
60 cmd_phase = (dat_phase - 1) % nphases
61 return cmd_phase, dat_phase
62
63 # Settings -----------------------------------------------------------------------------------------
64
65
66 class Settings:
67 def set_attributes(self, attributes):
68 for k, v in attributes.items():
69 setattr(self, k, v)
70
71
72 class PhySettings(Settings):
73 def __init__(self, phytype, memtype, databits, dfi_databits,
74 nphases,
75 rdphase, wrphase,
76 rdcmdphase, wrcmdphase,
77 cl, read_latency, write_latency, nranks=1, cwl=None):
78 self.set_attributes(locals())
79 self.cwl = cl if cwl is None else cwl
80 self.is_rdimm = False
81
82 # Optional DDR3/DDR4 electrical settings:
83 # rtt_nom: Non-Writes on-die termination impedance
84 # rtt_wr: Writes on-die termination impedance
85 # ron: Output driver impedance
86 def add_electrical_settings(self, rtt_nom, rtt_wr, ron):
87 assert self.memtype in ["DDR3", "DDR4"]
88 self.set_attributes(locals())
89
90
91 class GeomSettings(Settings):
92 def __init__(self, bankbits, rowbits, colbits):
93 self.set_attributes(locals())
94 self.addressbits = max(rowbits, colbits)
95
96
97 class TimingSettings(Settings):
98 def __init__(self, tRP, tRCD, tWR, tWTR, tREFI, tRFC, tFAW, tCCD, tRRD, tRC, tRAS, tZQCS):
99 self.set_attributes(locals())
100
101 # Layouts/Interface --------------------------------------------------------------------------------
102
103
104 def cmd_layout(address_width):
105 return [
106 ("valid", 1, DIR_FANOUT),
107 ("ready", 1, DIR_FANIN),
108 ("we", 1, DIR_FANOUT),
109 ("addr", address_width, DIR_FANOUT),
110 ("lock", 1, DIR_FANIN), # only used internally
111
112 ("wdata_ready", 1, DIR_FANIN),
113 ("rdata_valid", 1, DIR_FANIN)
114 ]
115
116
117 def data_layout(data_width):
118 return [
119 ("wdata", data_width, DIR_FANOUT),
120 ("wdata_we", data_width//8, DIR_FANOUT),
121 ("rdata", data_width, DIR_FANIN)
122 ]
123
124
125 def cmd_description(address_width):
126 return [
127 ("we", 1),
128 ("addr", address_width)
129 ]
130
131
132 def wdata_description(data_width):
133 return [
134 ("data", data_width),
135 ("we", data_width//8)
136 ]
137
138
139 def rdata_description(data_width):
140 return [("data", data_width)]
141
142
143 def cmd_request_layout(a, ba):
144 return [
145 ("a", a),
146 ("ba", ba),
147 ("cas", 1),
148 ("ras", 1),
149 ("we", 1)
150 ]
151
152
153 def cmd_request_rw_layout(a, ba):
154 return cmd_request_layout(a, ba) + [
155 ("is_cmd", 1),
156 ("is_read", 1),
157 ("is_write", 1)
158 ]
159
160
161 class gramInterface(Record):
162 def __init__(self, address_align, settings):
163 rankbits = log2_int(settings.phy.nranks)
164 self.address_align = address_align
165 self.address_width = settings.geom.rowbits + \
166 settings.geom.colbits + rankbits - address_align
167 self.data_width = settings.phy.dfi_databits*settings.phy.nphases
168 self.nbanks = settings.phy.nranks*(2**settings.geom.bankbits)
169 self.nranks = settings.phy.nranks
170 self.settings = settings
171
172 layout = [("bank"+str(i), cmd_layout(self.address_width))
173 for i in range(self.nbanks)]
174 layout += data_layout(self.data_width)
175 Record.__init__(self, layout)
176
177 # Ports --------------------------------------------------------------------------------------------
178
179
180 class gramNativePort(Settings):
181 def __init__(self, mode, address_width, data_width, clock_domain="sync", id=0):
182 self.set_attributes(locals())
183
184 if mode not in ["both", "read", "write"]:
185 raise ValueError("mode must be either both/read/write, not {!r}".format(mode))
186
187 self.lock = Signal()
188
189 self.cmd = stream.Endpoint(cmd_description(address_width))
190 self.wdata = stream.Endpoint(wdata_description(data_width))
191 self.rdata = stream.Endpoint(rdata_description(data_width))
192
193 self.flush = Signal()
194
195 self.data_width = data_width
196
197 def get_bank_address(self, bank_bits, cba_shift):
198 cba_upper = cba_shift + bank_bits
199 return self.cmd.addr[cba_shift:cba_upper]
200
201 def get_row_column_address(self, bank_bits, rca_bits, cba_shift):
202 cba_upper = cba_shift + bank_bits
203 if cba_shift < rca_bits:
204 if cba_shift:
205 return Cat(self.cmd.addr[:cba_shift], self.cmd.addr[cba_upper:])
206 else:
207 return self.cmd.addr[cba_upper:]
208 else:
209 return self.cmd.addr[:cba_shift]
210
211
212 # Timing Controllers -------------------------------------------------------------------------------
213
214 class tXXDController(Elaboratable):
215 def __init__(self, txxd):
216 self.valid = Signal()
217 self.ready = ready = Signal(reset=txxd is None, attrs={"no_retiming": True})
218 self._txxd = txxd
219
220 def elaborate(self, platform):
221 m = Module()
222
223 if self._txxd is not None:
224 count = Signal(range(max(self._txxd, 2)))
225 with m.If(self.valid):
226 m.d.sync += [
227 count.eq(self._txxd-1),
228 self.ready.eq((self._txxd - 1) == 0),
229 ]
230 with m.Elif(~self.ready):
231 m.d.sync += count.eq(count-1)
232 with m.If(count == 1):
233 m.d.sync += self.ready.eq(1)
234
235 if platform == "formal":
236 if self._txxd is not None and self._txxd > 0:
237 hasSeenValid = Signal()
238 with m.If(self.valid):
239 m.d.sync += hasSeenValid.eq(1)
240
241 m.d.sync += Assert((hasSeenValid & (count == 0)).implies(self.ready == 1))
242
243 return m
244
245
246 class tFAWController(Elaboratable):
247 def __init__(self, tfaw):
248 self.valid = Signal()
249 self.ready = Signal(reset=1, attrs={"no_retiming": True})
250 self._tfaw = tfaw
251
252 def elaborate(self, platform):
253 m = Module()
254
255 if self._tfaw is not None:
256 count = Signal(range(max(self._tfaw, 2)))
257 window = Signal(self._tfaw)
258 m.d.sync += window.eq(Cat(self.valid, window))
259 m.d.comb += count.eq(reduce(add, [window[i] for i in range(self._tfaw)]))
260 with m.If(count < 4):
261 with m.If(count == 3):
262 m.d.sync += self.ready.eq(~self.valid)
263 with m.Else():
264 m.d.sync += self.ready.eq(1)
265
266 return m