gram.core.bankmachine: Remove unused local variables in BankMachine
[gram.git] / gram / core / bankmachine.py
1 # This file is Copyright (c) 2015 Sebastien Bourdeauducq <sb@m-labs.hk>
2 # This file is Copyright (c) 2016-2019 Florent Kermarrec <florent@enjoy-digital.fr>
3 # This file is Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
4 # License: BSD
5
6 import math
7
8 from nmigen import *
9
10 from gram.common import *
11 from gram.core.multiplexer import *
12 from gram.compat import delayed_enter
13 import gram.stream as stream
14
15 __ALL__ = ["BankMachine"]
16
17 class _AddressSlicer(Elaboratable):
18 def __init__(self, addrbits, colbits, address_align):
19 self._address_align = address_align
20 self._split = colbits - address_align
21
22 self.address = Signal(addrbits)
23 self.row = Signal(addrbits-self._split)
24 self.col = Signal(address_align+self._split)
25
26 def elaborate(self, platform):
27 m = Module()
28
29 m.d.comb += [
30 self.row.eq(self.address[self._split:]),
31 self.col.eq(Cat(Repl(0, self._address_align), self.address[:self._split]))
32 ]
33
34 return m
35
36 class BankMachine(Elaboratable):
37 """Converts requests from ports into DRAM commands
38
39 BankMachine abstracts single DRAM bank by keeping track of the currently
40 selected row. It converts requests from gramCrossbar to targetted
41 to that bank into DRAM commands that go to the Multiplexer, inserting any
42 needed activate/precharge commands (with optional auto-precharge). It also
43 keeps track and enforces some DRAM timings (other timings are enforced in
44 the Multiplexer).
45
46 BankMachines work independently from the data path (which connects
47 gramCrossbar with the Multiplexer directly).
48
49 Stream of requests from gramCrossbar is being queued, so that reqeust
50 can be "looked ahead", and auto-precharge can be performed (if enabled in
51 settings).
52
53 Lock (cmd_layout.lock) is used to synchronise with gramCrossbar. It is
54 being held when:
55 - there is a valid command awaiting in `cmd_buffer_lookahead` - this buffer
56 becomes ready simply when the next data gets fetched to the `cmd_buffer`
57 - there is a valid command in `cmd_buffer` - `cmd_buffer` becomes ready
58 when the BankMachine sends wdata_ready/rdata_valid back to the crossbar
59
60 Parameters
61 ----------
62 n : int
63 Bank number
64 address_width : int
65 LiteDRAMInterface address width
66 address_align : int
67 Address alignment depending on burst length
68 nranks : int
69 Number of separate DRAM chips (width of chip select)
70 settings : ControllerSettings
71 LiteDRAMController settings
72
73 Attributes
74 ----------
75 req : Record(cmd_layout)
76 Stream of requests from gramCrossbar
77 refresh_req : Signal(), in
78 Indicates that refresh needs to be done, connects to Refresher.cmd.valid
79 refresh_gnt : Signal(), out
80 Indicates that refresh permission has been granted, satisfying timings
81 cmd : Endpoint(cmd_request_rw_layout)
82 Stream of commands to the Multiplexer
83 """
84
85 def __init__(self, n, address_width, address_align, nranks, settings):
86 self.settings = settings
87 self.req = req = Record(cmd_layout(address_width))
88 self.refresh_req = Signal()
89 self.refresh_gnt = Signal()
90
91 a = settings.geom.addressbits
92 ba = settings.geom.bankbits + log2_int(nranks)
93 self.cmd = stream.Endpoint(cmd_request_rw_layout(a, ba))
94
95 self._address_align = address_align
96 self._n = n
97
98 def elaborate(self, platform):
99 m = Module()
100
101 auto_precharge = Signal()
102
103 # Command buffer ---------------------------------------------------------------------------
104 cmd_buffer_layout = [("we", 1), ("addr", len(self.req.addr))]
105 cmd_buffer_lookahead = stream.SyncFIFO(
106 cmd_buffer_layout, self.settings.cmd_buffer_depth,
107 buffered=self.settings.cmd_buffer_buffered)
108 # 1 depth buffer to detect row change
109 cmd_buffer = stream.Buffer(cmd_buffer_layout)
110 m.submodules += cmd_buffer_lookahead, cmd_buffer
111 m.d.comb += [
112 cmd_buffer_lookahead.sink.valid.eq(self.req.valid),
113 self.req.ready.eq(cmd_buffer_lookahead.sink.ready),
114 cmd_buffer_lookahead.sink.payload.we.eq(self.req.we),
115 cmd_buffer_lookahead.sink.payload.addr.eq(self.req.addr),
116 cmd_buffer_lookahead.source.connect(cmd_buffer.sink),
117 cmd_buffer.source.ready.eq(self.req.wdata_ready | self.req.rdata_valid),
118 self.req.lock.eq(cmd_buffer_lookahead.source.valid | cmd_buffer.source.valid),
119 ]
120
121 m.submodules.lookahead_slicer = lookahead_slicer = _AddressSlicer(len(cmd_buffer_lookahead.source.addr),
122 self.settings.geom.colbits, self._address_align)
123 m.submodules.current_slicer = current_slicer = _AddressSlicer(len(cmd_buffer.source.addr),
124 self.settings.geom.colbits, self._address_align)
125 m.d.comb += [
126 current_slicer.address.eq(cmd_buffer.source.addr),
127 lookahead_slicer.address.eq(cmd_buffer_lookahead.source.addr),
128 ]
129
130 # Row tracking -----------------------------------------------------------------------------
131 row = Signal(self.settings.geom.rowbits)
132 row_opened = Signal()
133 row_hit = Signal()
134 row_open = Signal()
135 row_close = Signal()
136 m.d.comb += row_hit.eq(row == current_slicer.row)
137 with m.If(row_close):
138 m.d.sync += row_opened.eq(0)
139 with m.Elif(row_open):
140 m.d.sync += [
141 row_opened.eq(1),
142 row.eq(current_slicer.row),
143 ]
144
145 # Address generation -----------------------------------------------------------------------
146 row_col_n_addr_sel = Signal()
147 m.d.comb += self.cmd.ba.eq(self._n)
148 with m.If(row_col_n_addr_sel):
149 m.d.comb += self.cmd.a.eq(current_slicer.row)
150 with m.Else():
151 m.d.comb += self.cmd.a.eq((auto_precharge << 10) | current_slicer.col)
152
153 # tWTP / tRC / tRAS controllers
154 write_latency = math.ceil(self.settings.phy.cwl / self.settings.phy.nphases)
155 precharge_time = write_latency + self.settings.timing.tWR + self.settings.timing.tCCD # AL=0
156 m.submodules.twtpcon = twtpcon = tXXDController(precharge_time)
157 m.d.comb += twtpcon.valid.eq(self.cmd.valid & self.cmd.ready & self.cmd.is_write)
158
159 m.submodules.trccon = trccon = tXXDController(self.settings.timing.tRC)
160 m.submodules.trascon = trascon = tXXDController(self.settings.timing.tRAS)
161 valid_ready_row_open = Signal()
162 m.d.comb += [
163 valid_ready_row_open.eq(self.cmd.valid & self.cmd.ready & row_open),
164 trccon.valid.eq(valid_ready_row_open),
165 trascon.valid.eq(valid_ready_row_open),
166 ]
167
168 # Auto Precharge generation ----------------------------------------------------------------
169 # generate auto precharge when current and next cmds are to different rows
170 if self.settings.with_auto_precharge:
171 with m.If(cmd_buffer_lookahead.source.valid & cmd_buffer.source.valid):
172 with m.If(lookahead_slicer.row != current_slicer.row):
173 m.d.comb += auto_precharge.eq(row_close == 0)
174
175 # Control and command generation FSM -------------------------------------------------------
176 # Note: tRRD, tFAW, tCCD, tWTR timings are enforced by the multiplexer
177 with m.FSM():
178 with m.State("Regular"):
179 with m.If(self.refresh_req):
180 with m.If(row_opened):
181 m.next = "Precharge-For-Refresh"
182 with m.Else():
183 m.next = "Refresh"
184 with m.Elif(cmd_buffer.source.valid):
185 with m.If(row_opened):
186 with m.If(row_hit):
187 m.d.comb += [
188 self.cmd.valid.eq(1),
189 self.cmd.cas.eq(1),
190 ]
191 with m.If(cmd_buffer.source.we):
192 m.d.comb += [
193 self.req.wdata_ready.eq(self.cmd.ready),
194 self.cmd.is_write.eq(1),
195 self.cmd.we.eq(1),
196 ]
197 with m.Else():
198 m.d.comb += [
199 self.req.rdata_valid.eq(self.cmd.ready),
200 self.cmd.is_read.eq(1),
201 ]
202 with m.If(self.cmd.ready & auto_precharge):
203 m.next = "Autoprecharge"
204 with m.Else():
205 m.next = "Precharge"
206 with m.Else():
207 m.next = "Activate"
208
209 with m.State("Precharge"):
210 m.d.comb += row_close.eq(1)
211
212 with m.If(twtpcon.ready & trascon.ready):
213 m.d.comb += [
214 self.cmd.valid.eq(1),
215 self.cmd.ras.eq(1),
216 self.cmd.we.eq(1),
217 self.cmd.is_cmd.eq(1),
218 ]
219
220 with m.If(self.cmd.ready):
221 m.next = "tRP"
222
223 with m.State("Precharge-For-Refresh"):
224 m.d.comb += row_close.eq(1)
225
226 with m.If(twtpcon.ready & trascon.ready):
227 m.d.comb += [
228 self.cmd.valid.eq(1),
229 self.cmd.ras.eq(1),
230 self.cmd.we.eq(1),
231 self.cmd.is_cmd.eq(1),
232 ]
233
234 with m.If(self.cmd.ready):
235 m.next = "Refresh"
236
237 with m.State("Autoprecharge"):
238 m.d.comb += row_close.eq(1)
239
240 with m.If(twtpcon.ready & trascon.ready):
241 m.next = "tRP"
242
243 with m.State("Activate"):
244 with m.If(trccon.ready):
245 m.d.comb += [
246 row_col_n_addr_sel.eq(1),
247 row_open.eq(1),
248 self.cmd.valid.eq(1),
249 self.cmd.is_cmd.eq(1),
250 self.cmd.ras.eq(1),
251 ]
252 with m.If(self.cmd.ready):
253 m.next = "tRCD"
254
255 with m.State("Refresh"):
256 m.d.comb += [
257 row_close.eq(1),
258 self.cmd.is_cmd.eq(1),
259 ]
260
261 with m.If(twtpcon.ready):
262 m.d.comb += self.refresh_gnt.eq(1)
263 with m.If(~self.refresh_req):
264 m.next = "Regular"
265
266 delayed_enter(m, "tRP", "Activate", self.settings.timing.tRP - 1)
267 delayed_enter(m, "tRCD", "Regular", self.settings.timing.tRCD - 1)
268
269 return m