asmicon: simple selector option
[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(Constant(0, BV(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(BV(bits_for(self.nslots-1)))
46 self.adr = Signal(self.slots[0].adr.bv)
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(BV(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),
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(BV(self.slicer.geom_settings.row_a))
102 openrow_n = Signal(BV(self.slicer.geom_settings.row_a))
103 openrow = Signal(BV(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=False):
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 else:
203 selector = _SimpleSelector(slicer, self.bankn, self.slots)
204 buf = _Buffer(selector)
205
206 # Row tracking
207 has_openrow = Signal()
208 openrow = Signal(BV(self.geom_settings.row_a))
209 hit = Signal()
210 comb.append(hit.eq(openrow == slicer.row(buf.adr)))
211 track_open = Signal()
212 track_close = Signal()
213 sync += [
214 If(track_open,
215 has_openrow.eq(1),
216 openrow.eq(slicer.row(buf.adr))
217 ),
218 If(track_close,
219 has_openrow.eq(0)
220 )
221 ]
222
223 # Address generation
224 s_row_adr = Signal()
225 comb += [
226 self.cmd.ba.eq(self.bankn),
227 If(s_row_adr,
228 self.cmd.a.eq(slicer.row(buf.adr))
229 ).Else(
230 self.cmd.a.eq(slicer.col(buf.adr))
231 )
232 ]
233
234 comb.append(self.cmd.tag.eq(buf.tag))
235
236 # Control and command generation FSM
237 fsm = FSM("REGULAR", "PRECHARGE", "ACTIVATE", "REFRESH", delayed_enters=[
238 ("TRP", "ACTIVATE", self.timing_settings.tRP-1),
239 ("TRCD", "REGULAR", self.timing_settings.tRCD-1)
240 ])
241 fsm.act(fsm.REGULAR,
242 If(self.refresh_req,
243 fsm.next_state(fsm.REFRESH)
244 ).Elif(buf.stb,
245 If(has_openrow,
246 If(hit,
247 self.cmd.stb.eq(1),
248 buf.ack.eq(self.cmd.ack),
249 self.cmd.is_read.eq(~buf.we),
250 self.cmd.is_write.eq(buf.we),
251 self.cmd.cas_n.eq(0),
252 self.cmd.we_n.eq(~buf.we)
253 ).Else(
254 fsm.next_state(fsm.PRECHARGE)
255 )
256 ).Else(
257 fsm.next_state(fsm.ACTIVATE)
258 )
259 )
260 )
261 fsm.act(fsm.PRECHARGE,
262 # Notes:
263 # 1. we are presenting the column address, A10 is always low
264 # 2. since we always go to the ACTIVATE state, we do not need
265 # to assert track_close.
266 self.cmd.stb.eq(1),
267 If(self.cmd.ack, fsm.next_state(fsm.TRP)),
268 self.cmd.ras_n.eq(0),
269 self.cmd.we_n.eq(0)
270 )
271 fsm.act(fsm.ACTIVATE,
272 s_row_adr.eq(1),
273 track_open.eq(1),
274 self.cmd.stb.eq(1),
275 If(self.cmd.ack, fsm.next_state(fsm.TRCD)),
276 self.cmd.ras_n.eq(0)
277 )
278 fsm.act(fsm.REFRESH,
279 self.refresh_gnt.eq(1),
280 track_close.eq(1),
281 If(~self.refresh_req, fsm.next_state(fsm.REGULAR))
282 )
283
284 return Fragment(comb, sync) + \
285 selector.get_fragment() + \
286 buf.get_fragment() + \
287 fsm.get_fragment()