Replace Signal(bits_for(... with Signal(max=...
[litex.git] / milkymist / asmicon / bankmachine.py
1 from migen.fhdl.structure import *
2 from migen.bus.asmibus import *
3 from migen.corelogic.roundrobin import *
4 from migen.corelogic.fsm import FSM
5 from migen.corelogic.misc import optree
6
7 from milkymist.asmicon.multiplexer import *
8
9 # Row:Bank:Col address mapping
10 class _AddressSlicer:
11 def __init__(self, geom_settings, address_align):
12 self.geom_settings = geom_settings
13 self.address_align = address_align
14
15 self._b1 = self.geom_settings.col_a - self.address_align
16 self._b2 = self._b1 + self.geom_settings.bank_a
17
18 def row(self, address):
19 if isinstance(address, int):
20 return address >> self._b2
21 else:
22 return address[self._b2:]
23
24 def bank(self, address):
25 if isinstance(address, int):
26 return (address & (2**self._b2 - 1)) >> self._b1
27 else:
28 return address[self._b1:self._b2]
29
30 def col(self, address):
31 if isinstance(address, int):
32 return (address & (2**self._b1 - 1)) << self.address_align
33 else:
34 return Cat(Replicate(0, self.address_align), address[:self._b1])
35
36 class _Selector:
37 def __init__(self, slicer, bankn, slots):
38 self.slicer = slicer
39 self.bankn = bankn
40 self.slots = slots
41
42 self.nslots = len(self.slots)
43 self.stb = Signal()
44 self.ack = Signal()
45 self.tag = Signal(max=self.nslots)
46 self.adr = Signal(self.slots[0].adr.nbits)
47 self.we = Signal()
48
49 # derived classes should drive rr.request
50 self.rr = RoundRobin(self.nslots, SP_CE)
51
52 def get_fragment(self):
53 comb = []
54 rr = self.rr
55
56 # Multiplex
57 state = Signal(2)
58 comb += [
59 state.eq(Array(slot.state for slot in self.slots)[rr.grant]),
60 self.adr.eq(Array(slot.adr for slot in self.slots)[rr.grant]),
61 self.we.eq(Array(slot.we for slot in self.slots)[rr.grant]),
62 self.stb.eq(
63 (self.slicer.bank(self.adr) == self.bankn) \
64 & (state == SLOT_PENDING)),
65 rr.ce.eq(self.ack | ~self.stb),
66 self.tag.eq(rr.grant)
67 ]
68 comb += [If((rr.grant == i) & self.stb & self.ack, slot.process.eq(1))
69 for i, slot in enumerate(self.slots)]
70
71 return Fragment(comb) + rr.get_fragment()
72
73 class _SimpleSelector(_Selector):
74 def get_fragment(self):
75 comb = []
76 for i, slot in enumerate(self.slots):
77 comb.append(self.rr.request[i].eq(
78 (self.slicer.bank(slot.adr) == self.bankn) & \
79 (slot.state == SLOT_PENDING)
80 ))
81
82 return Fragment(comb) + super().get_fragment()
83
84 class _FullSelector(_Selector):
85 def get_fragment(self):
86 comb = []
87 sync = []
88 rr = self.rr
89
90 # List outstanding requests for our bank
91 outstandings = []
92 for slot in self.slots:
93 outstanding = Signal()
94 comb.append(outstanding.eq(
95 (self.slicer.bank(slot.adr) == self.bankn) & \
96 (slot.state == SLOT_PENDING)
97 ))
98 outstandings.append(outstanding)
99
100 # Row tracking
101 openrow_r = Signal(self.slicer.geom_settings.row_a)
102 openrow_n = Signal(self.slicer.geom_settings.row_a)
103 openrow = Signal(self.slicer.geom_settings.row_a)
104 comb += [
105 openrow_n.eq(self.slicer.row(self.adr)),
106 If(self.stb,
107 openrow.eq(openrow_n)
108 ).Else(
109 openrow.eq(openrow_r)
110 )
111 ]
112 sync += [
113 If(self.stb & self.ack,
114 openrow_r.eq(openrow_n)
115 )
116 ]
117 hits = []
118 for slot, os in zip(self.slots, outstandings):
119 hit = Signal()
120 comb.append(hit.eq((self.slicer.row(slot.adr) == openrow) & os))
121 hits.append(hit)
122
123 # Determine best request
124 rr = RoundRobin(self.nslots, SP_CE)
125 has_hit = Signal()
126 comb.append(has_hit.eq(optree("|", hits)))
127
128 best_hit = [rr.request[i].eq(hit)
129 for i, hit in enumerate(hits)]
130 best_fallback = [rr.request[i].eq(os)
131 for i, os in enumerate(outstandings)]
132 select_stmt = If(has_hit,
133 *best_hit
134 ).Else(
135 *best_fallback
136 )
137
138 if self.slots[0].time:
139 # Implement anti-starvation timer
140 matures = []
141 for slot, os in zip(self.slots, outstandings):
142 mature = Signal()
143 comb.append(mature.eq(slot.mature & os))
144 matures.append(mature)
145 has_mature = Signal()
146 comb.append(has_mature.eq(optree("|", matures)))
147 best_mature = [rr.request[i].eq(mature)
148 for i, mature in enumerate(matures)]
149 select_stmt = If(has_mature, *best_mature).Else(select_stmt)
150 comb.append(select_stmt)
151
152 return Fragment(comb, sync) + super().get_fragment()
153
154 class _Buffer:
155 def __init__(self, source):
156 self.source = source
157
158 self.stb = Signal()
159 self.ack = Signal()
160 self.tag = Signal(self.source.tag.bv)
161 self.adr = Signal(self.source.adr.bv)
162 self.we = Signal()
163
164 def get_fragment(self):
165 en = Signal()
166 comb = [
167 en.eq(self.ack | ~self.stb),
168 self.source.ack.eq(en)
169 ]
170 sync = [
171 If(en,
172 self.stb.eq(self.source.stb),
173 self.tag.eq(self.source.tag),
174 self.adr.eq(self.source.adr),
175 self.we.eq(self.source.we)
176 )
177 ]
178 return Fragment(comb, sync)
179
180 class BankMachine:
181 def __init__(self, geom_settings, timing_settings, address_align, bankn, slots, full_selector):
182 self.geom_settings = geom_settings
183 self.timing_settings = timing_settings
184 self.address_align = address_align
185 self.bankn = bankn
186 self.slots = slots
187 self.full_selector = full_selector
188
189 self.refresh_req = Signal()
190 self.refresh_gnt = Signal()
191 self.cmd = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a,
192 bits_for(len(slots)-1))
193
194 def get_fragment(self):
195 comb = []
196 sync = []
197
198 # Sub components
199 slicer = _AddressSlicer(self.geom_settings, self.address_align)
200 if self.full_selector:
201 selector = _FullSelector(slicer, self.bankn, self.slots)
202 buf = _Buffer(selector)
203 cmdsource = buf
204 else:
205 selector = _SimpleSelector(slicer, self.bankn, self.slots)
206 cmdsource = selector
207
208 # Row tracking
209 has_openrow = Signal()
210 openrow = Signal(self.geom_settings.row_a)
211 hit = Signal()
212 comb.append(hit.eq(openrow == slicer.row(cmdsource.adr)))
213 track_open = Signal()
214 track_close = Signal()
215 sync += [
216 If(track_open,
217 has_openrow.eq(1),
218 openrow.eq(slicer.row(cmdsource.adr))
219 ),
220 If(track_close,
221 has_openrow.eq(0)
222 )
223 ]
224
225 # Address generation
226 s_row_adr = Signal()
227 comb += [
228 self.cmd.ba.eq(self.bankn),
229 If(s_row_adr,
230 self.cmd.a.eq(slicer.row(cmdsource.adr))
231 ).Else(
232 self.cmd.a.eq(slicer.col(cmdsource.adr))
233 )
234 ]
235
236 comb.append(self.cmd.tag.eq(cmdsource.tag))
237
238 # Respect write-to-precharge specification
239 precharge_ok = Signal()
240 t_unsafe_precharge = 2 + self.timing_settings.tWR - 1
241 unsafe_precharge_count = Signal(max=t_unsafe_precharge+1)
242 comb.append(precharge_ok.eq(unsafe_precharge_count == 0))
243 sync += [
244 If(self.cmd.stb & self.cmd.ack & self.cmd.is_write,
245 unsafe_precharge_count.eq(t_unsafe_precharge)
246 ).Elif(~precharge_ok,
247 unsafe_precharge_count.eq(unsafe_precharge_count-1)
248 )
249 ]
250
251 # Control and command generation FSM
252 fsm = FSM("REGULAR", "PRECHARGE", "ACTIVATE", "REFRESH", delayed_enters=[
253 ("TRP", "ACTIVATE", self.timing_settings.tRP-1),
254 ("TRCD", "REGULAR", self.timing_settings.tRCD-1)
255 ])
256 fsm.act(fsm.REGULAR,
257 If(self.refresh_req,
258 fsm.next_state(fsm.REFRESH)
259 ).Elif(cmdsource.stb,
260 If(has_openrow,
261 If(hit,
262 # NB: write-to-read specification is enforced by multiplexer
263 self.cmd.stb.eq(1),
264 cmdsource.ack.eq(self.cmd.ack),
265 self.cmd.is_read.eq(~cmdsource.we),
266 self.cmd.is_write.eq(cmdsource.we),
267 self.cmd.cas_n.eq(0),
268 self.cmd.we_n.eq(~cmdsource.we)
269 ).Else(
270 fsm.next_state(fsm.PRECHARGE)
271 )
272 ).Else(
273 fsm.next_state(fsm.ACTIVATE)
274 )
275 )
276 )
277 fsm.act(fsm.PRECHARGE,
278 # Notes:
279 # 1. we are presenting the column address, A10 is always low
280 # 2. since we always go to the ACTIVATE state, we do not need
281 # to assert track_close.
282 If(precharge_ok,
283 self.cmd.stb.eq(1),
284 If(self.cmd.ack, fsm.next_state(fsm.TRP)),
285 self.cmd.ras_n.eq(0),
286 self.cmd.we_n.eq(0)
287 )
288 )
289 fsm.act(fsm.ACTIVATE,
290 s_row_adr.eq(1),
291 track_open.eq(1),
292 self.cmd.stb.eq(1),
293 If(self.cmd.ack, fsm.next_state(fsm.TRCD)),
294 self.cmd.ras_n.eq(0)
295 )
296 fsm.act(fsm.REFRESH,
297 self.refresh_gnt.eq(precharge_ok),
298 track_close.eq(1),
299 If(~self.refresh_req, fsm.next_state(fsm.REGULAR))
300 )
301
302 if self.full_selector:
303 buf_fragment = buf.get_fragment()
304 else:
305 buf_fragment = Fragment()
306 return Fragment(comb, sync) + \
307 selector.get_fragment() + \
308 buf_fragment + \
309 fsm.get_fragment()