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