Add copyright
[gram.git] / gram / frontend / adaptation.py
1 # This file is Copyright (c) 2016-2019 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
3 # License: BSD
4
5 from nmigen import *
6 from nmigen.compat import Case
7
8 from gram.common import *
9 import gram.stream as stream
10
11 # LiteDRAMNativePortCDC ----------------------------------------------------------------------------
12
13 class gramNativePortCDC(Elaboratable):
14 def __init__(self, port_from, port_to,
15 cmd_depth = 4,
16 wdata_depth = 16,
17 rdata_depth = 16):
18 assert port_from.address_width == port_to.address_width
19 assert port_from.data_width == port_to.data_width
20 assert port_from.mode == port_to.mode
21
22 self._port_from = port_from
23 self._port_to = port_to
24 self._cmd_depth = cmd_depth
25 self._wdata_depth = wdata_depth
26 self._rdata_depth = rdata_depth
27
28 def elaborate(self, platform):
29 m = Module()
30
31 port_from = self._port_from
32 port_to = self._port_to
33 cmd_depth = self._cmd_depth
34 wdata_depth = self._wdata_depth
35 rdata_depth = self._rdata_depth
36
37 address_width = port_from.address_width
38 data_width = port_from.data_width
39 mode = port_from.mode
40 clock_domain_from = port_from.clock_domain
41 clock_domain_to = port_to.clock_domain
42
43 cmd_fifo = stream.AsyncFIFO(
44 [("we", 1), ("addr", address_width)], cmd_depth)
45 cmd_fifo = ClockDomainsRenamer(
46 {"write": clock_domain_from,
47 "read": clock_domain_to})(cmd_fifo)
48 m.submodules += cmd_fifo
49 m.submodules += stream.Pipeline(
50 port_from.cmd, cmd_fifo, port_to.cmd)
51
52 if mode == "write" or mode == "both":
53 wdata_fifo = stream.AsyncFIFO(
54 [("data", data_width), ("we", data_width//8)], wdata_depth)
55 wdata_fifo = ClockDomainsRenamer(
56 {"write": clock_domain_from,
57 "read": clock_domain_to})(wdata_fifo)
58 m.submodules += wdata_fifo
59 m.submodules += stream.Pipeline(
60 port_from.wdata, wdata_fifo, port_to.wdata)
61
62 if mode == "read" or mode == "both":
63 rdata_fifo = stream.AsyncFIFO([("data", data_width)], rdata_depth)
64 rdata_fifo = ClockDomainsRenamer(
65 {"write": clock_domain_to,
66 "read": clock_domain_from})(rdata_fifo)
67 m.submodules += rdata_fifo
68 m.submodules += stream.Pipeline(
69 port_to.rdata, rdata_fifo, port_from.rdata)
70
71 return m
72
73 # LiteDRAMNativePortDownConverter ------------------------------------------------------------------
74
75 class gramNativePortDownConverter(Elaboratable):
76 """LiteDRAM port DownConverter
77
78 This module reduces user port data width to fit controller data width.
79 With N = port_from.data_width/port_to.data_width:
80 - Address is adapted (multiplied by N + internal increments)
81 - A write from the user is splitted and generates N writes to the
82 controller.
83 - A read from the user generates N reads to the controller and returned
84 datas are regrouped in a single data presented to the user.
85 """
86 def __init__(self, port_from, port_to, reverse=False):
87 assert port_from.clock_domain == port_to.clock_domain
88 assert port_from.data_width > port_to.data_width
89 assert port_from.mode == port_to.mode
90 if port_from.data_width % port_to.data_width:
91 raise ValueError("Ratio must be an int")
92
93 self._port_from = port_from
94 self._port_to = port_to
95 self._reverse = reverse
96
97 def elaborate(self, platform):
98 m = Module()
99
100 port_from = self._port_from
101 port_to = self._port_to
102 reverse = self._reverse
103
104 ratio = port_from.data_width//port_to.data_width
105 mode = port_from.mode
106
107 counter = Signal(max=ratio)
108 counter_reset = Signal()
109 counter_ce = Signal()
110
111 with m.If(counter_reset):
112 m.d.sync += counter.eq(0)
113 with m.Elif(counter_ce):
114 m.d.sync += counter.eq(counter+1)
115
116 with m.FSM():
117 with m.State("Idle"):
118 m.d.comb += counter_reset.eq(1)
119 with m.If(port_from.cmd.valid):
120 m.next = "Convert"
121
122 with m.State("Convert"):
123 m.d.comb += [
124 port_to.cmd.valid.eq(1),
125 port_to.cmd.we.eq(port_from.cmd.we),
126 port_to.cmd.addr.eq(port_from.cmd.addr*ratio + counter),
127 ]
128 with m.If(port_to.cmd.ready):
129 m.d.comb += counter_ce.eq(1)
130 with m.If(counter == ratio - 1):
131 m.d.comb += port_from.cmd.ready.eq(1)
132 m.next = "Idle"
133
134 if mode == "write" or mode == "both":
135 wdata_converter = stream.StrideConverter(
136 port_from.wdata.description,
137 port_to.wdata.description,
138 reverse=reverse)
139 m.submodules += wdata_converter
140 m.submodules += stream.Pipeline(
141 port_from.wdata, wdata_converter, port_to.wdata)
142
143 if mode == "read" or mode == "both":
144 rdata_converter = stream.StrideConverter(
145 port_to.rdata.description,
146 port_from.rdata.description,
147 reverse=reverse)
148 m.submodules += rdata_converter
149 m.submodules += stream.Pipeline(
150 port_to.rdata, rdata_converter, port_from.rdata)
151
152 return m
153
154 # LiteDRAMNativeWritePortUpConverter ---------------------------------------------------------------
155
156 class gramNativeWritePortUpConverter(Elaboratable):
157 # TODO: finish and remove hack
158 """LiteDRAM write port UpConverter
159
160 This module increase user port data width to fit controller data width.
161 With N = port_to.data_width/port_from.data_width:
162 - Address is adapted (divided by N)
163 - N writes from user are regrouped in a single one to the controller
164 (when possible, ie when consecutive and bursting)
165 """
166 def __init__(self, port_from, port_to, reverse=False):
167 assert port_from.clock_domain == port_to.clock_domain
168 assert port_from.data_width < port_to.data_width
169 assert port_from.mode == port_to.mode
170 assert port_from.mode == "write"
171 if port_to.data_width % port_from.data_width:
172 raise ValueError("Ratio must be an int")
173
174 self._port_from = port_from
175 self._port_to = port_to
176 self._reverse = reverse
177
178 def elaborate(self, platform):
179 m = Module()
180
181 port_from = self._port_from
182 port_to = self._port_to
183 reverse = self._reverse
184
185 ratio = port_to.data_width//port_from.data_width
186
187 we = Signal()
188 address = Signal(port_to.address_width)
189
190 counter = Signal(max=ratio)
191 counter_reset = Signal()
192 counter_ce = Signal()
193 self.sync += \
194 If(counter_reset,
195 counter.eq(0)
196 ).Elif(counter_ce,
197 counter.eq(counter + 1)
198 )
199
200 with m.FSM():
201 with m.State("Idle"):
202 m.d.comb += port_from.cmd.ready.eq(1)
203 with m.If(port_from.cmd.valid):
204 m.d.sync += [
205 we.eq(port_from.cmd.we),
206 address.eq(port_from.cmd.addr),
207 ]
208 m.next = "Receive"
209
210 with m.State("Receive"):
211 m.d.comb += port_from.cmd.ready.eq(1)
212 with m.If(port_from.cmd.valid):
213 m.d.comb += counter_ce.eq(1)
214 with m.If(counter == ratio-1):
215 m.next = "Generate"
216
217 with m.State("Generate"):
218 m.d.comb += [
219 port_to.cmd.valid.eq(1),
220 port_to.cmd.we.eq(we),
221 port_to.cmd.addr.eq(address[log2_int(ratio):]),
222 ]
223 with m.If(port_to.cmd.ready):
224 m.next = "Idle"
225
226 wdata_converter = stream.StrideConverter(
227 port_from.wdata.description,
228 port_to.wdata.description,
229 reverse=reverse)
230 m.submodules += wdata_converter
231 m.submodules += stream.Pipeline(
232 port_from.wdata,
233 wdata_converter,
234 port_to.wdata)
235
236 return m
237
238 # LiteDRAMNativeReadPortUpConverter ----------------------------------------------------------------
239
240 class gramNativeReadPortUpConverter(Elaboratable):
241 """LiteDRAM port UpConverter
242
243 This module increase user port data width to fit controller data width.
244 With N = port_to.data_width/port_from.data_width:
245 - Address is adapted (divided by N)
246 - N read from user are regrouped in a single one to the controller
247 (when possible, ie when consecutive and bursting)
248 """
249 def __init__(self, port_from, port_to, reverse=False):
250 assert port_from.clock_domain == port_to.clock_domain
251 assert port_from.data_width < port_to.data_width
252 assert port_from.mode == port_to.mode
253 assert port_from.mode == "read"
254 if port_to.data_width % port_from.data_width:
255 raise ValueError("Ratio must be an int")
256
257 self._port_from = port_from
258 self._port_to = port_to
259 self._reverse = reverse
260
261 def elaborate(self, platform):
262 m = Module()
263
264 port_from = self._port_from
265 port_to = self._port_to
266 reverse = self._reverse
267
268 ratio = port_to.data_width//port_from.data_width
269
270 # Command ----------------------------------------------------------------------------------
271
272 cmd_buffer = stream.SyncFIFO([("sel", ratio)], 4)
273 m.submodules += cmd_buffer
274
275 counter = Signal(range(ratio))
276 counter_ce = Signal()
277 with m.If(counter_ce):
278 m.d.sync += counter.eq(counter+1)
279
280 with m.If(port_from.cmd.valid):
281 with m.If(counter == 0):
282 m.d.comb += [
283 port_to.cmd.valid.eq(1),
284 port_to.cmd.addr.eq(port_from.cmd.addr[log2_int(ratio):]),
285 port_from.cmd.ready.eq(port_to.cmd.ready),
286 counter_ce.eq(port_to.cmd.ready),
287 ]
288 with m.Else():
289 m.d.comb += [
290 port_from.cmd.ready.eq(1),
291 counter_ce.eq(1),
292 ]
293
294 # TODO: fix sel
295 with m.If(port_to.cmd.valid & port_to.cmd.ready):
296 m.d.comb += [
297 cmd_buffer.sink.valid.eq(1),
298 cmd_buffer.sink.sel.eq(2**ratio-1),
299 ]
300
301 # Datapath ---------------------------------------------------------------------------------
302
303 rdata_buffer = stream.Buffer(port_to.rdata.description)
304 rdata_converter = stream.StrideConverter(
305 port_to.rdata.description,
306 port_from.rdata.description,
307 reverse=reverse)
308 m.submodules += rdata_buffer, rdata_converter
309
310 rdata_chunk = Signal(ratio, reset=1)
311 rdata_chunk_valid = Signal()
312 with m.If(rdata_converter.source.valid & rdata_converter.source.ready):
313 m.d.sync += rdata_chunk.eq(Cat(rdata_chunk[ratio-1], rdata_chunk[:ratio-1]))
314
315 m.d.comb += [
316 port_to.rdata.connect(rdata_buffer.sink),
317 rdata_buffer.source.connect(rdata_converter.sink),
318 rdata_chunk_valid.eq((cmd_buffer.source.sel & rdata_chunk) != 0),
319 cmd_buffer.source.ready.eq(rdata_converter.source.ready & rdata_chunk[ratio-1]),
320 ]
321
322 with m.If(port_from.flush):
323 m.d.comb += rdata_converter.source.ready.eq(1)
324 with m.Elif(cmd_buffer.source.valid):
325 with m.If(rdata_chunk_valid):
326 m.d.comb += [
327 port_from.rdata.valid.eq(rdata_converter.source.valid),
328 port_from.rdata.data.eq(rdata_converter.source.data),
329 rdata_converter.source.ready.eq(port_from.rdata.ready),
330 ]
331 with m.Else():
332 m.d.comb += rdata_converter.source.ready.eq(1)
333
334 return m
335
336 # LiteDRAMNativePortConverter ----------------------------------------------------------------------
337
338 class LiteDRAMNativePortConverter(Elaboratable):
339 def __init__(self, port_from, port_to, reverse=False):
340 assert port_from.clock_domain == port_to.clock_domain
341 assert port_from.mode == port_to.mode
342
343 self._port_from = port_from
344 self._port_to = port_to
345 self._reverse = reverse
346
347 def elaborate(self, platform):
348 m = Module()
349
350 port_from = self._port_from
351 port_to = self._port_to
352 reverse = self._reverse
353
354 mode = port_from.mode
355
356 if port_from.data_width > port_to.data_width:
357 converter = gramNativePortDownConverter(port_from, port_to, reverse)
358 m.submodules += converter
359 elif port_from.data_width < port_to.data_width:
360 if mode == "write":
361 converter = gramNativeWritePortUpConverter(port_from, port_to, reverse)
362 elif mode == "read":
363 converter = gramNativeReadPortUpConverter(port_from, port_to, reverse)
364 else:
365 raise NotImplementedError
366 m.submodules += converter
367 else:
368 m.d.comb += [
369 port_from.cmd.connect(port_to.cmd),
370 port_from.wdata.connect(port_to.wdata),
371 port_to.rdata.connect(port_from.rdata)
372 ]
373
374 return m