Initial commit
[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 migen import *
5
6 from litex.soc.interconnect import stream
7
8 from litedram.common import *
9
10 # LiteDRAMNativePortCDC ----------------------------------------------------------------------------
11
12 class LiteDRAMNativePortCDC(Module):
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 address_width = port_from.address_width
22 data_width = port_from.data_width
23 mode = port_from.mode
24 clock_domain_from = port_from.clock_domain
25 clock_domain_to = port_to.clock_domain
26
27 # # #
28
29 cmd_fifo = stream.AsyncFIFO(
30 [("we", 1), ("addr", address_width)], cmd_depth)
31 cmd_fifo = ClockDomainsRenamer(
32 {"write": clock_domain_from,
33 "read": clock_domain_to})(cmd_fifo)
34 self.submodules += cmd_fifo
35 self.submodules += stream.Pipeline(
36 port_from.cmd, cmd_fifo, port_to.cmd)
37
38 if mode == "write" or mode == "both":
39 wdata_fifo = stream.AsyncFIFO(
40 [("data", data_width), ("we", data_width//8)], wdata_depth)
41 wdata_fifo = ClockDomainsRenamer(
42 {"write": clock_domain_from,
43 "read": clock_domain_to})(wdata_fifo)
44 self.submodules += wdata_fifo
45 self.submodules += stream.Pipeline(
46 port_from.wdata, wdata_fifo, port_to.wdata)
47
48 if mode == "read" or mode == "both":
49 rdata_fifo = stream.AsyncFIFO([("data", data_width)], rdata_depth)
50 rdata_fifo = ClockDomainsRenamer(
51 {"write": clock_domain_to,
52 "read": clock_domain_from})(rdata_fifo)
53 self.submodules += rdata_fifo
54 self.submodules += stream.Pipeline(
55 port_to.rdata, rdata_fifo, port_from.rdata)
56
57 # LiteDRAMNativePortDownConverter ------------------------------------------------------------------
58
59 class LiteDRAMNativePortDownConverter(Module):
60 """LiteDRAM port DownConverter
61
62 This module reduces user port data width to fit controller data width.
63 With N = port_from.data_width/port_to.data_width:
64 - Address is adapted (multiplied by N + internal increments)
65 - A write from the user is splitted and generates N writes to the
66 controller.
67 - A read from the user generates N reads to the controller and returned
68 datas are regrouped in a single data presented to the user.
69 """
70 def __init__(self, port_from, port_to, reverse=False):
71 assert port_from.clock_domain == port_to.clock_domain
72 assert port_from.data_width > port_to.data_width
73 assert port_from.mode == port_to.mode
74 if port_from.data_width % port_to.data_width:
75 raise ValueError("Ratio must be an int")
76
77 # # #
78
79 ratio = port_from.data_width//port_to.data_width
80 mode = port_from.mode
81
82 counter = Signal(max=ratio)
83 counter_reset = Signal()
84 counter_ce = Signal()
85 self.sync += \
86 If(counter_reset,
87 counter.eq(0)
88 ).Elif(counter_ce,
89 counter.eq(counter + 1)
90 )
91
92 self.submodules.fsm = fsm = FSM(reset_state="IDLE")
93 fsm.act("IDLE",
94 counter_reset.eq(1),
95 If(port_from.cmd.valid,
96 NextState("CONVERT")
97 )
98 )
99 fsm.act("CONVERT",
100 port_to.cmd.valid.eq(1),
101 port_to.cmd.we.eq(port_from.cmd.we),
102 port_to.cmd.addr.eq(port_from.cmd.addr*ratio + counter),
103 If(port_to.cmd.ready,
104 counter_ce.eq(1),
105 If(counter == ratio - 1,
106 port_from.cmd.ready.eq(1),
107 NextState("IDLE")
108 )
109 )
110 )
111
112 if mode == "write" or mode == "both":
113 wdata_converter = stream.StrideConverter(
114 port_from.wdata.description,
115 port_to.wdata.description,
116 reverse=reverse)
117 self.submodules += wdata_converter
118 self.submodules += stream.Pipeline(
119 port_from.wdata, wdata_converter, port_to.wdata)
120
121 if mode == "read" or mode == "both":
122 rdata_converter = stream.StrideConverter(
123 port_to.rdata.description,
124 port_from.rdata.description,
125 reverse=reverse)
126 self.submodules += rdata_converter
127 self.submodules += stream.Pipeline(
128 port_to.rdata, rdata_converter, port_from.rdata)
129
130 # LiteDRAMNativeWritePortUpConverter ---------------------------------------------------------------
131
132 class LiteDRAMNativeWritePortUpConverter(Module):
133 # TODO: finish and remove hack
134 """LiteDRAM write port UpConverter
135
136 This module increase user port data width to fit controller data width.
137 With N = port_to.data_width/port_from.data_width:
138 - Address is adapted (divided by N)
139 - N writes from user are regrouped in a single one to the controller
140 (when possible, ie when consecutive and bursting)
141 """
142 def __init__(self, port_from, port_to, reverse=False):
143 assert port_from.clock_domain == port_to.clock_domain
144 assert port_from.data_width < port_to.data_width
145 assert port_from.mode == port_to.mode
146 assert port_from.mode == "write"
147 if port_to.data_width % port_from.data_width:
148 raise ValueError("Ratio must be an int")
149
150 # # #
151
152 ratio = port_to.data_width//port_from.data_width
153
154 we = Signal()
155 address = Signal(port_to.address_width)
156
157 counter = Signal(max=ratio)
158 counter_reset = Signal()
159 counter_ce = Signal()
160 self.sync += \
161 If(counter_reset,
162 counter.eq(0)
163 ).Elif(counter_ce,
164 counter.eq(counter + 1)
165 )
166
167 self.submodules.fsm = fsm = FSM(reset_state="IDLE")
168 fsm.act("IDLE",
169 port_from.cmd.ready.eq(1),
170 If(port_from.cmd.valid,
171 counter_ce.eq(1),
172 NextValue(we, port_from.cmd.we),
173 NextValue(address, port_from.cmd.addr),
174 NextState("RECEIVE")
175 )
176 )
177 fsm.act("RECEIVE",
178 port_from.cmd.ready.eq(1),
179 If(port_from.cmd.valid,
180 counter_ce.eq(1),
181 If(counter == ratio-1,
182 NextState("GENERATE")
183 )
184 )
185 )
186 fsm.act("GENERATE",
187 port_to.cmd.valid.eq(1),
188 port_to.cmd.we.eq(we),
189 port_to.cmd.addr.eq(address[log2_int(ratio):]),
190 If(port_to.cmd.ready,
191 NextState("IDLE")
192 )
193 )
194
195 wdata_converter = stream.StrideConverter(
196 port_from.wdata.description,
197 port_to.wdata.description,
198 reverse=reverse)
199 self.submodules += wdata_converter
200 self.submodules += stream.Pipeline(
201 port_from.wdata,
202 wdata_converter,
203 port_to.wdata)
204
205 # LiteDRAMNativeReadPortUpConverter ----------------------------------------------------------------
206
207 class LiteDRAMNativeReadPortUpConverter(Module):
208 """LiteDRAM port UpConverter
209
210 This module increase user port data width to fit controller data width.
211 With N = port_to.data_width/port_from.data_width:
212 - Address is adapted (divided by N)
213 - N read from user are regrouped in a single one to the controller
214 (when possible, ie when consecutive and bursting)
215 """
216 def __init__(self, port_from, port_to, reverse=False):
217 assert port_from.clock_domain == port_to.clock_domain
218 assert port_from.data_width < port_to.data_width
219 assert port_from.mode == port_to.mode
220 assert port_from.mode == "read"
221 if port_to.data_width % port_from.data_width:
222 raise ValueError("Ratio must be an int")
223
224 # # #
225
226 ratio = port_to.data_width//port_from.data_width
227
228
229 # Command ----------------------------------------------------------------------------------
230
231 cmd_buffer = stream.SyncFIFO([("sel", ratio)], 4)
232 self.submodules += cmd_buffer
233
234 counter = Signal(max=ratio)
235 counter_ce = Signal()
236 self.sync += \
237 If(counter_ce,
238 counter.eq(counter + 1)
239 )
240
241 self.comb += \
242 If(port_from.cmd.valid,
243 If(counter == 0,
244 port_to.cmd.valid.eq(1),
245 port_to.cmd.addr.eq(port_from.cmd.addr[log2_int(ratio):]),
246 port_from.cmd.ready.eq(port_to.cmd.ready),
247 counter_ce.eq(port_to.cmd.ready)
248 ).Else(
249 port_from.cmd.ready.eq(1),
250 counter_ce.eq(1)
251 )
252 )
253
254 # TODO: fix sel
255 self.comb += \
256 If(port_to.cmd.valid & port_to.cmd.ready,
257 cmd_buffer.sink.valid.eq(1),
258 cmd_buffer.sink.sel.eq(2**ratio-1)
259 )
260
261 # Datapath ---------------------------------------------------------------------------------
262
263 rdata_buffer = stream.Buffer(port_to.rdata.description)
264 rdata_converter = stream.StrideConverter(
265 port_to.rdata.description,
266 port_from.rdata.description,
267 reverse=reverse)
268 self.submodules += rdata_buffer, rdata_converter
269
270 rdata_chunk = Signal(ratio, reset=1)
271 rdata_chunk_valid = Signal()
272 self.sync += \
273 If(rdata_converter.source.valid &
274 rdata_converter.source.ready,
275 rdata_chunk.eq(Cat(rdata_chunk[ratio-1], rdata_chunk[:ratio-1]))
276 )
277
278 self.comb += [
279 port_to.rdata.connect(rdata_buffer.sink),
280 rdata_buffer.source.connect(rdata_converter.sink),
281 rdata_chunk_valid.eq((cmd_buffer.source.sel & rdata_chunk) != 0),
282 If(port_from.flush,
283 rdata_converter.source.ready.eq(1)
284 ).Elif(cmd_buffer.source.valid,
285 If(rdata_chunk_valid,
286 port_from.rdata.valid.eq(rdata_converter.source.valid),
287 port_from.rdata.data.eq(rdata_converter.source.data),
288 rdata_converter.source.ready.eq(port_from.rdata.ready)
289 ).Else(
290 rdata_converter.source.ready.eq(1)
291 )
292 ),
293 cmd_buffer.source.ready.eq(
294 rdata_converter.source.ready & rdata_chunk[ratio-1])
295 ]
296
297 # LiteDRAMNativePortConverter ----------------------------------------------------------------------
298
299 class LiteDRAMNativePortConverter(Module):
300 def __init__(self, port_from, port_to, reverse=False):
301 assert port_from.clock_domain == port_to.clock_domain
302 assert port_from.mode == port_to.mode
303
304 # # #
305
306 mode = port_from.mode
307
308 if port_from.data_width > port_to.data_width:
309 converter = LiteDRAMNativePortDownConverter(port_from, port_to, reverse)
310 self.submodules += converter
311 elif port_from.data_width < port_to.data_width:
312 if mode == "write":
313 converter = LiteDRAMNativeWritePortUpConverter(port_from, port_to, reverse)
314 elif mode == "read":
315 converter = LiteDRAMNativeReadPortUpConverter(port_from, port_to, reverse)
316 else:
317 raise NotImplementedError
318 self.submodules += converter
319 else:
320 self.comb += [
321 port_from.cmd.connect(port_to.cmd),
322 port_from.wdata.connect(port_to.wdata),
323 port_to.rdata.connect(port_from.rdata)
324 ]