80c4d9ac20568c45c533e3b59c34e55a711c63cd
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>
6 from nmigen
.compat
import Case
8 from gram
.common
import *
9 import gram
.stream
as stream
11 # LiteDRAMNativePortCDC ----------------------------------------------------------------------------
14 class gramNativePortCDC(Elaboratable
):
15 def __init__(self
, port_from
, port_to
,
19 assert port_from
.address_width
== port_to
.address_width
20 assert port_from
.data_width
== port_to
.data_width
21 assert port_from
.mode
== port_to
.mode
23 self
._port
_from
= port_from
24 self
._port
_to
= port_to
25 self
._cmd
_depth
= cmd_depth
26 self
._wdata
_depth
= wdata_depth
27 self
._rdata
_depth
= rdata_depth
29 def elaborate(self
, platform
):
32 port_from
= self
._port
_from
33 port_to
= self
._port
_to
34 cmd_depth
= self
._cmd
_depth
35 wdata_depth
= self
._wdata
_depth
36 rdata_depth
= self
._rdata
_depth
38 address_width
= port_from
.address_width
39 data_width
= port_from
.data_width
41 clock_domain_from
= port_from
.clock_domain
42 clock_domain_to
= port_to
.clock_domain
44 cmd_fifo
= stream
.AsyncFIFO(
45 [("we", 1), ("addr", address_width
)], cmd_depth
)
46 cmd_fifo
= ClockDomainsRenamer(
47 {"write": clock_domain_from
,
48 "read": clock_domain_to
})(cmd_fifo
)
49 m
.submodules
+= cmd_fifo
50 m
.submodules
+= stream
.Pipeline(
51 port_from
.cmd
, cmd_fifo
, port_to
.cmd
)
53 if mode
== "write" or mode
== "both":
54 wdata_fifo
= stream
.AsyncFIFO(
55 [("data", data_width
), ("we", data_width
//8)], wdata_depth
)
56 wdata_fifo
= ClockDomainsRenamer(
57 {"write": clock_domain_from
,
58 "read": clock_domain_to
})(wdata_fifo
)
59 m
.submodules
+= wdata_fifo
60 m
.submodules
+= stream
.Pipeline(
61 port_from
.wdata
, wdata_fifo
, port_to
.wdata
)
63 if mode
== "read" or mode
== "both":
64 rdata_fifo
= stream
.AsyncFIFO([("data", data_width
)], rdata_depth
)
65 rdata_fifo
= ClockDomainsRenamer(
66 {"write": clock_domain_to
,
67 "read": clock_domain_from
})(rdata_fifo
)
68 m
.submodules
+= rdata_fifo
69 m
.submodules
+= stream
.Pipeline(
70 port_to
.rdata
, rdata_fifo
, port_from
.rdata
)
74 # LiteDRAMNativePortDownConverter ------------------------------------------------------------------
77 class gramNativePortDownConverter(Elaboratable
):
78 """LiteDRAM port DownConverter
80 This module reduces user port data width to fit controller data width.
81 With N = port_from.data_width/port_to.data_width:
82 - Address is adapted (multiplied by N + internal increments)
83 - A write from the user is splitted and generates N writes to the
85 - A read from the user generates N reads to the controller and returned
86 datas are regrouped in a single data presented to the user.
89 def __init__(self
, port_from
, port_to
, reverse
=False):
90 assert port_from
.clock_domain
== port_to
.clock_domain
91 assert port_from
.data_width
> port_to
.data_width
92 assert port_from
.mode
== port_to
.mode
93 if port_from
.data_width
% port_to
.data_width
:
94 raise ValueError("Ratio must be an int")
96 self
._port
_from
= port_from
97 self
._port
_to
= port_to
98 self
._reverse
= reverse
100 def elaborate(self
, platform
):
103 port_from
= self
._port
_from
104 port_to
= self
._port
_to
105 reverse
= self
._reverse
107 ratio
= port_from
.data_width
//port_to
.data_width
108 mode
= port_from
.mode
110 counter
= Signal(range(ratio
))
111 counter_reset
= Signal()
112 counter_ce
= Signal()
114 with m
.If(counter_reset
):
115 m
.d
.sync
+= counter
.eq(0)
116 with m
.Elif(counter_ce
):
117 m
.d
.sync
+= counter
.eq(counter
+1)
120 with m
.State("Idle"):
121 m
.d
.comb
+= counter_reset
.eq(1)
122 with m
.If(port_from
.cmd
.valid
):
125 with m
.State("Convert"):
127 port_to
.cmd
.valid
.eq(1),
128 port_to
.cmd
.we
.eq(port_from
.cmd
.we
),
129 port_to
.cmd
.addr
.eq(port_from
.cmd
.addr
*ratio
+ counter
),
131 with m
.If(port_to
.cmd
.ready
):
132 m
.d
.comb
+= counter_ce
.eq(1)
133 with m
.If(counter
== ratio
- 1):
134 m
.d
.comb
+= port_from
.cmd
.ready
.eq(1)
137 if mode
== "write" or mode
== "both":
138 wdata_converter
= stream
.StrideConverter(
139 port_from
.wdata
.description
,
140 port_to
.wdata
.description
,
142 m
.submodules
+= wdata_converter
143 m
.submodules
+= stream
.Pipeline(
144 port_from
.wdata
, wdata_converter
, port_to
.wdata
)
146 if mode
== "read" or mode
== "both":
147 rdata_converter
= stream
.StrideConverter(
148 port_to
.rdata
.description
,
149 port_from
.rdata
.description
,
151 m
.submodules
+= rdata_converter
152 m
.submodules
+= stream
.Pipeline(
153 port_to
.rdata
, rdata_converter
, port_from
.rdata
)
157 # LiteDRAMNativeWritePortUpConverter ---------------------------------------------------------------
160 class gramNativeWritePortUpConverter(Elaboratable
):
161 # TODO: finish and remove hack
162 """LiteDRAM write port UpConverter
164 This module increase user port data width to fit controller data width.
165 With N = port_to.data_width/port_from.data_width:
166 - Address is adapted (divided by N)
167 - N writes from user are regrouped in a single one to the controller
168 (when possible, ie when consecutive and bursting)
171 def __init__(self
, port_from
, port_to
, reverse
=False):
172 assert port_from
.clock_domain
== port_to
.clock_domain
173 assert port_from
.data_width
< port_to
.data_width
174 assert port_from
.mode
== port_to
.mode
175 assert port_from
.mode
== "write"
176 if port_to
.data_width
% port_from
.data_width
:
177 raise ValueError("Ratio must be an int")
179 self
._port
_from
= port_from
180 self
._port
_to
= port_to
181 self
._reverse
= reverse
183 def elaborate(self
, platform
):
186 port_from
= self
._port
_from
187 port_to
= self
._port
_to
188 reverse
= self
._reverse
190 ratio
= port_to
.data_width
//port_from
.data_width
193 address
= Signal(port_to
.address_width
)
195 counter
= Signal(max=ratio
)
196 counter_reset
= Signal()
197 counter_ce
= Signal()
202 counter
.eq(counter
+ 1)
206 with m
.State("Idle"):
207 m
.d
.comb
+= port_from
.cmd
.ready
.eq(1)
208 with m
.If(port_from
.cmd
.valid
):
210 we
.eq(port_from
.cmd
.we
),
211 address
.eq(port_from
.cmd
.addr
),
215 with m
.State("Receive"):
216 m
.d
.comb
+= port_from
.cmd
.ready
.eq(1)
217 with m
.If(port_from
.cmd
.valid
):
218 m
.d
.comb
+= counter_ce
.eq(1)
219 with m
.If(counter
== ratio
-1):
222 with m
.State("Generate"):
224 port_to
.cmd
.valid
.eq(1),
225 port_to
.cmd
.we
.eq(we
),
226 port_to
.cmd
.addr
.eq(address
[log2_int(ratio
):]),
228 with m
.If(port_to
.cmd
.ready
):
231 wdata_converter
= stream
.StrideConverter(
232 port_from
.wdata
.description
,
233 port_to
.wdata
.description
,
235 m
.submodules
+= wdata_converter
236 m
.submodules
+= stream
.Pipeline(
243 # LiteDRAMNativeReadPortUpConverter ----------------------------------------------------------------
246 class gramNativeReadPortUpConverter(Elaboratable
):
247 """LiteDRAM port UpConverter
249 This module increase user port data width to fit controller data width.
250 With N = port_to.data_width/port_from.data_width:
251 - Address is adapted (divided by N)
252 - N read from user are regrouped in a single one to the controller
253 (when possible, ie when consecutive and bursting)
256 def __init__(self
, port_from
, port_to
, reverse
=False):
257 assert port_from
.clock_domain
== port_to
.clock_domain
258 assert port_from
.data_width
< port_to
.data_width
259 assert port_from
.mode
== port_to
.mode
260 assert port_from
.mode
== "read"
261 if port_to
.data_width
% port_from
.data_width
:
262 raise ValueError("Ratio must be an int")
264 self
._port
_from
= port_from
265 self
._port
_to
= port_to
266 self
._reverse
= reverse
268 def elaborate(self
, platform
):
271 port_from
= self
._port
_from
272 port_to
= self
._port
_to
273 reverse
= self
._reverse
275 ratio
= port_to
.data_width
//port_from
.data_width
277 # Command ----------------------------------------------------------------------------------
279 cmd_buffer
= stream
.SyncFIFO([("sel", ratio
)], 4)
280 m
.submodules
+= cmd_buffer
282 counter
= Signal(range(ratio
))
283 counter_ce
= Signal()
284 with m
.If(counter_ce
):
285 m
.d
.sync
+= counter
.eq(counter
+1)
287 with m
.If(port_from
.cmd
.valid
):
288 with m
.If(counter
== 0):
290 port_to
.cmd
.valid
.eq(1),
291 port_to
.cmd
.addr
.eq(port_from
.cmd
.addr
[log2_int(ratio
):]),
292 port_from
.cmd
.ready
.eq(port_to
.cmd
.ready
),
293 counter_ce
.eq(port_to
.cmd
.ready
),
297 port_from
.cmd
.ready
.eq(1),
302 with m
.If(port_to
.cmd
.valid
& port_to
.cmd
.ready
):
304 cmd_buffer
.sink
.valid
.eq(1),
305 cmd_buffer
.sink
.sel
.eq(2**ratio
-1),
308 # Datapath ---------------------------------------------------------------------------------
310 rdata_buffer
= stream
.Buffer(port_to
.rdata
.description
)
311 rdata_converter
= stream
.StrideConverter(
312 port_to
.rdata
.description
,
313 port_from
.rdata
.description
,
315 m
.submodules
+= rdata_buffer
, rdata_converter
317 rdata_chunk
= Signal(ratio
, reset
=1)
318 rdata_chunk_valid
= Signal()
319 with m
.If(rdata_converter
.source
.valid
& rdata_converter
.source
.ready
):
320 m
.d
.sync
+= rdata_chunk
.eq(
321 Cat(rdata_chunk
[ratio
-1], rdata_chunk
[:ratio
-1]))
324 port_to
.rdata
.connect(rdata_buffer
.sink
),
325 rdata_buffer
.source
.connect(rdata_converter
.sink
),
326 rdata_chunk_valid
.eq((cmd_buffer
.source
.sel
& rdata_chunk
) != 0),
327 cmd_buffer
.source
.ready
.eq(
328 rdata_converter
.source
.ready
& rdata_chunk
[ratio
-1]),
331 with m
.If(port_from
.flush
):
332 m
.d
.comb
+= rdata_converter
.source
.ready
.eq(1)
333 with m
.Elif(cmd_buffer
.source
.valid
):
334 with m
.If(rdata_chunk_valid
):
336 port_from
.rdata
.valid
.eq(rdata_converter
.source
.valid
),
337 port_from
.rdata
.data
.eq(rdata_converter
.source
.data
),
338 rdata_converter
.source
.ready
.eq(port_from
.rdata
.ready
),
341 m
.d
.comb
+= rdata_converter
.source
.ready
.eq(1)
345 # LiteDRAMNativePortConverter ----------------------------------------------------------------------
348 class gramNativePortConverter(Elaboratable
):
349 def __init__(self
, port_from
, port_to
, reverse
=False):
350 assert port_from
.clock_domain
== port_to
.clock_domain
351 assert port_from
.mode
== port_to
.mode
353 self
._port
_from
= port_from
354 self
._port
_to
= port_to
355 self
._reverse
= reverse
357 def elaborate(self
, platform
):
360 port_from
= self
._port
_from
361 port_to
= self
._port
_to
362 reverse
= self
._reverse
364 mode
= port_from
.mode
366 if port_from
.data_width
> port_to
.data_width
:
367 converter
= gramNativePortDownConverter(
368 port_from
, port_to
, reverse
)
369 m
.submodules
+= converter
370 elif port_from
.data_width
< port_to
.data_width
:
372 converter
= gramNativeWritePortUpConverter(
373 port_from
, port_to
, reverse
)
375 converter
= gramNativeReadPortUpConverter(
376 port_from
, port_to
, reverse
)
378 raise NotImplementedError
379 m
.submodules
+= converter
382 port_from
.cmd
.connect(port_to
.cmd
),
383 port_from
.wdata
.connect(port_to
.wdata
),
384 port_to
.rdata
.connect(port_from
.rdata
)