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
7 from milkymist
.asmicon
.multiplexer
import *
9 # Row:Bank:Col address mapping
11 def __init__(self
, geom_settings
, address_align
):
12 self
.geom_settings
= geom_settings
13 self
.address_align
= address_align
15 self
._b
1 = self
.geom_settings
.col_a
- self
.address_align
16 self
._b
2 = self
._b
1 + self
.geom_settings
.bank_a
18 def row(self
, address
):
19 if isinstance(address
, int):
20 return address
>> self
._b
2
22 return address
[self
._b
2:]
24 def bank(self
, address
):
25 if isinstance(address
, int):
26 return (address
& (2**self
._b
2 - 1)) >> self
._b
1
28 return address
[self
._b
1:self
._b
2]
30 def col(self
, address
):
31 if isinstance(address
, int):
32 return (address
& (2**self
._b
1 - 1)) << self
.address_align
34 return Cat(Replicate(0, self
.address_align
), address
[:self
._b
1])
37 def __init__(self
, slicer
, bankn
, slots
):
42 self
.nslots
= len(self
.slots
)
45 self
.tag
= Signal(max=self
.nslots
)
46 self
.adr
= Signal(self
.slots
[0].adr
.nbits
)
49 # derived classes should drive rr.request
50 self
.rr
= RoundRobin(self
.nslots
, SP_CE
)
52 def get_fragment(self
):
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
]),
63 (self
.slicer
.bank(self
.adr
) == self
.bankn
) \
64 & (state
== SLOT_PENDING
)),
65 rr
.ce
.eq(self
.ack | ~self
.stb
),
68 comb
+= [If((rr
.grant
== i
) & self
.stb
& self
.ack
, slot
.process
.eq(1))
69 for i
, slot
in enumerate(self
.slots
)]
71 return Fragment(comb
) + rr
.get_fragment()
73 class _SimpleSelector(_Selector
):
74 def get_fragment(self
):
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
)
82 return Fragment(comb
) + super().get_fragment()
84 class _FullSelector(_Selector
):
85 def get_fragment(self
):
90 # List outstanding requests for our bank
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
)
98 outstandings
.append(outstanding
)
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
)
105 openrow_n
.eq(self
.slicer
.row(self
.adr
)),
107 openrow
.eq(openrow_n
)
109 openrow
.eq(openrow_r
)
113 If(self
.stb
& self
.ack
,
114 openrow_r
.eq(openrow_n
)
118 for slot
, os
in zip(self
.slots
, outstandings
):
120 comb
.append(hit
.eq((self
.slicer
.row(slot
.adr
) == openrow
) & os
))
123 # Determine best request
124 rr
= RoundRobin(self
.nslots
, SP_CE
)
126 comb
.append(has_hit
.eq(optree("|", hits
)))
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
,
138 if self
.slots
[0].time
:
139 # Implement anti-starvation timer
141 for slot
, os
in zip(self
.slots
, outstandings
):
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
)
152 return Fragment(comb
, sync
) + super().get_fragment()
155 def __init__(self
, source
):
160 self
.tag
= Signal(self
.source
.tag
.bv
)
161 self
.adr
= Signal(self
.source
.adr
.bv
)
164 def get_fragment(self
):
167 en
.eq(self
.ack | ~self
.stb
),
168 self
.source
.ack
.eq(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
)
178 return Fragment(comb
, sync
)
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
187 self
.full_selector
= full_selector
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))
194 def get_fragment(self
):
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
)
205 selector
= _SimpleSelector(slicer
, self
.bankn
, self
.slots
)
209 has_openrow
= Signal()
210 openrow
= Signal(self
.geom_settings
.row_a
)
212 comb
.append(hit
.eq(openrow
== slicer
.row(cmdsource
.adr
)))
213 track_open
= Signal()
214 track_close
= Signal()
218 openrow
.eq(slicer
.row(cmdsource
.adr
))
228 self
.cmd
.ba
.eq(self
.bankn
),
230 self
.cmd
.a
.eq(slicer
.row(cmdsource
.adr
))
232 self
.cmd
.a
.eq(slicer
.col(cmdsource
.adr
))
236 comb
.append(self
.cmd
.tag
.eq(cmdsource
.tag
))
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))
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)
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)
258 fsm
.next_state(fsm
.REFRESH
)
259 ).Elif(cmdsource
.stb
,
262 # NB: write-to-read specification is enforced by multiplexer
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
)
270 fsm
.next_state(fsm
.PRECHARGE
)
273 fsm
.next_state(fsm
.ACTIVATE
)
277 fsm
.act(fsm
.PRECHARGE
,
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.
284 If(self
.cmd
.ack
, fsm
.next_state(fsm
.TRP
)),
285 self
.cmd
.ras_n
.eq(0),
289 fsm
.act(fsm
.ACTIVATE
,
293 If(self
.cmd
.ack
, fsm
.next_state(fsm
.TRCD
)),
297 self
.refresh_gnt
.eq(precharge_ok
),
299 If(~self
.refresh_req
, fsm
.next_state(fsm
.REGULAR
))
302 if self
.full_selector
:
303 buf_fragment
= buf
.get_fragment()
305 buf_fragment
= Fragment()
306 return Fragment(comb
, sync
) + \
307 selector
.get_fragment() + \