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 ----------------------------------------------------------------------------
13 class gramNativePortCDC(Elaboratable
):
14 def __init__(self
, port_from
, port_to
,
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
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
28 def elaborate(self
, platform
):
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
37 address_width
= port_from
.address_width
38 data_width
= port_from
.data_width
40 clock_domain_from
= port_from
.clock_domain
41 clock_domain_to
= port_to
.clock_domain
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
)
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
)
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
)
73 # LiteDRAMNativePortDownConverter ------------------------------------------------------------------
75 class gramNativePortDownConverter(Elaboratable
):
76 """LiteDRAM port DownConverter
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
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.
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")
93 self
._port
_from
= port_from
94 self
._port
_to
= port_to
95 self
._reverse
= reverse
97 def elaborate(self
, platform
):
100 port_from
= self
._port
_from
101 port_to
= self
._port
_to
102 reverse
= self
._reverse
104 ratio
= port_from
.data_width
//port_to
.data_width
105 mode
= port_from
.mode
107 counter
= Signal(max=ratio
)
108 counter_reset
= Signal()
109 counter_ce
= Signal()
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)
117 with m
.State("Idle"):
118 m
.d
.comb
+= counter_reset
.eq(1)
119 with m
.If(port_from
.cmd
.valid
):
122 with m
.State("Convert"):
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
),
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)
134 if mode
== "write" or mode
== "both":
135 wdata_converter
= stream
.StrideConverter(
136 port_from
.wdata
.description
,
137 port_to
.wdata
.description
,
139 m
.submodules
+= wdata_converter
140 m
.submodules
+= stream
.Pipeline(
141 port_from
.wdata
, wdata_converter
, port_to
.wdata
)
143 if mode
== "read" or mode
== "both":
144 rdata_converter
= stream
.StrideConverter(
145 port_to
.rdata
.description
,
146 port_from
.rdata
.description
,
148 m
.submodules
+= rdata_converter
149 m
.submodules
+= stream
.Pipeline(
150 port_to
.rdata
, rdata_converter
, port_from
.rdata
)
154 # LiteDRAMNativeWritePortUpConverter ---------------------------------------------------------------
156 class gramNativeWritePortUpConverter(Elaboratable
):
157 # TODO: finish and remove hack
158 """LiteDRAM write port UpConverter
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)
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")
174 self
._port
_from
= port_from
175 self
._port
_to
= port_to
176 self
._reverse
= reverse
178 def elaborate(self
, platform
):
181 port_from
= self
._port
_from
182 port_to
= self
._port
_to
183 reverse
= self
._reverse
185 ratio
= port_to
.data_width
//port_from
.data_width
188 address
= Signal(port_to
.address_width
)
190 counter
= Signal(max=ratio
)
191 counter_reset
= Signal()
192 counter_ce
= Signal()
197 counter
.eq(counter
+ 1)
201 with m
.State("Idle"):
202 m
.d
.comb
+= port_from
.cmd
.ready
.eq(1)
203 with m
.If(port_from
.cmd
.valid
):
205 we
.eq(port_from
.cmd
.we
),
206 address
.eq(port_from
.cmd
.addr
),
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):
217 with m
.State("Generate"):
219 port_to
.cmd
.valid
.eq(1),
220 port_to
.cmd
.we
.eq(we
),
221 port_to
.cmd
.addr
.eq(address
[log2_int(ratio
):]),
223 with m
.If(port_to
.cmd
.ready
):
226 wdata_converter
= stream
.StrideConverter(
227 port_from
.wdata
.description
,
228 port_to
.wdata
.description
,
230 m
.submodules
+= wdata_converter
231 m
.submodules
+= stream
.Pipeline(
238 # LiteDRAMNativeReadPortUpConverter ----------------------------------------------------------------
240 class gramNativeReadPortUpConverter(Elaboratable
):
241 """LiteDRAM port UpConverter
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)
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")
257 self
._port
_from
= port_from
258 self
._port
_to
= port_to
259 self
._reverse
= reverse
261 def elaborate(self
, platform
):
264 port_from
= self
._port
_from
265 port_to
= self
._port
_to
266 reverse
= self
._reverse
268 ratio
= port_to
.data_width
//port_from
.data_width
270 # Command ----------------------------------------------------------------------------------
272 cmd_buffer
= stream
.SyncFIFO([("sel", ratio
)], 4)
273 m
.submodules
+= cmd_buffer
275 counter
= Signal(range(ratio
))
276 counter_ce
= Signal()
277 with m
.If(counter_ce
):
278 m
.d
.sync
+= counter
.eq(counter
+1)
280 with m
.If(port_from
.cmd
.valid
):
281 with m
.If(counter
== 0):
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
),
290 port_from
.cmd
.ready
.eq(1),
295 with m
.If(port_to
.cmd
.valid
& port_to
.cmd
.ready
):
297 cmd_buffer
.sink
.valid
.eq(1),
298 cmd_buffer
.sink
.sel
.eq(2**ratio
-1),
301 # Datapath ---------------------------------------------------------------------------------
303 rdata_buffer
= stream
.Buffer(port_to
.rdata
.description
)
304 rdata_converter
= stream
.StrideConverter(
305 port_to
.rdata
.description
,
306 port_from
.rdata
.description
,
308 m
.submodules
+= rdata_buffer
, rdata_converter
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]))
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]),
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
):
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
),
332 m
.d
.comb
+= rdata_converter
.source
.ready
.eq(1)
336 # LiteDRAMNativePortConverter ----------------------------------------------------------------------
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
343 self
._port
_from
= port_from
344 self
._port
_to
= port_to
345 self
._reverse
= reverse
347 def elaborate(self
, platform
):
350 port_from
= self
._port
_from
351 port_to
= self
._port
_to
352 reverse
= self
._reverse
354 mode
= port_from
.mode
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
:
361 converter
= gramNativeWritePortUpConverter(port_from
, port_to
, reverse
)
363 converter
= gramNativeReadPortUpConverter(port_from
, port_to
, reverse
)
365 raise NotImplementedError
366 m
.submodules
+= converter
369 port_from
.cmd
.connect(port_to
.cmd
),
370 port_from
.wdata
.connect(port_to
.wdata
),
371 port_to
.rdata
.connect(port_from
.rdata
)