LoadStore1: add rules for MMU_LOOKUP
[soc.git] / src / soc / fu / ldst / loadstore.py
1 """LoadStore1 FSM.
2
3 based on microwatt loadstore1.vhdl
4
5 Links:
6
7 * https://bugs.libre-soc.org/show_bug.cgi?id=465
8
9 """
10
11 from nmigen import (Elaboratable, Module, Signal, Shape, unsigned, Cat, Mux,
12 Record, Memory,
13 Const)
14 from nmutil.util import rising_edge
15 from enum import Enum, unique
16
17 from soc.experiment.dcache import DCache
18 from soc.experiment.pimem import PortInterfaceBase
19 from soc.experiment.mem_types import LoadStore1ToMMUType
20 from soc.experiment.mem_types import MMUToLoadStore1Type
21
22 from soc.minerva.wishbone import make_wb_layout
23 from soc.bus.sram import SRAM
24
25
26 @unique
27 class State(Enum):
28 IDLE = 0 # ready for instruction
29 ACK_WAIT = 1 # waiting for ack from dcache
30 MMU_LOOKUP = 2 # waiting for MMU to look up translation
31 TLBIE_WAIT = 3 # waiting for MMU to finish doing a tlbie
32 COMPLETE = 4 # extra cycle to complete an operation
33
34
35 # glue logic for microwatt mmu and dcache
36 class LoadStore1(PortInterfaceBase):
37 def __init__(self, pspec):
38 self.pspec = pspec
39 self.disable_cache = (hasattr(pspec, "disable_cache") and
40 pspec.disable_cache == True)
41 regwid = pspec.reg_wid
42 addrwid = pspec.addr_wid
43
44 super().__init__(regwid, addrwid)
45 self.dcache = DCache()
46 self.d_in = self.dcache.d_in
47 self.d_out = self.dcache.d_out
48 self.l_in = LoadStore1ToMMUType()
49 self.l_out = MMUToLoadStore1Type()
50 # TODO microwatt
51 self.mmureq = Signal()
52 self.derror = Signal()
53
54 # TODO, convert dcache wb_in/wb_out to "standard" nmigen Wishbone bus
55 self.dbus = Record(make_wb_layout(pspec))
56
57 # for creating a single clock blip to DCache
58 self.d_valid = Signal()
59 self.d_w_valid = Signal()
60 self.d_validblip = Signal()
61
62 # DSISR and DAR cached values. note that the MMU FSM is where
63 # these are accessed by OP_MTSPR/OP_MFSPR, on behalf of LoadStore1.
64 # by contrast microwatt has the spr set/get done *in* loadstore1.vhdl
65 self.dsisr = Signal(64)
66 self.dar = Signal(64)
67
68 # state info for LD/ST
69 self.done = Signal()
70 # latch most of the input request
71 self.load = Signal()
72 self.tlbie = Signal()
73 self.dcbz = Signal()
74 self.addr = Signal(64)
75 self.store_data = Signal(64)
76 self.load_data = Signal(64)
77 self.byte_sel = Signal(8)
78 self.update = Signal()
79 #self.xerc : xer_common_t;
80 #self.reserve = Signal()
81 #self.atomic = Signal()
82 #self.atomic_last = Signal()
83 #self.rc = Signal()
84 self.nc = Signal() # non-cacheable access
85 self.virt_mode = Signal()
86 self.priv_mode = Signal()
87 self.state = Signal(State)
88 self.instr_fault = Signal()
89 self.align_intr = Signal()
90 self.busy = Signal()
91 self.wait_dcache = Signal()
92 self.wait_mmu = Signal()
93 #self.mode_32bit = Signal()
94 self.wr_sel = Signal(2)
95 self.interrupt = Signal()
96 #self.intr_vec : integer range 0 to 16#fff#;
97 #self.nia = Signal(64)
98 #self.srr1 = Signal(16)
99
100 def set_wr_addr(self, m, addr, mask, misalign):
101 m.d.comb += self.load.eq(0) # store operation
102
103 m.d.comb += self.d_in.load.eq(0)
104 m.d.comb += self.byte_sel.eq(mask)
105 m.d.comb += self.addr.eq(addr)
106 m.d.comb += self.align_intr.eq(misalign)
107 # option to disable the cache entirely for write
108 if self.disable_cache:
109 m.d.comb += self.nc.eq(1)
110 return None
111
112 def set_rd_addr(self, m, addr, mask, misalign):
113 m.d.comb += self.d_valid.eq(1)
114 m.d.comb += self.d_in.valid.eq(self.d_validblip)
115 m.d.comb += self.load.eq(1) # load operation
116 m.d.comb += self.d_in.load.eq(1)
117 m.d.comb += self.byte_sel.eq(mask)
118 m.d.comb += self.align_intr.eq(misalign)
119 m.d.comb += self.addr.eq(addr)
120 # BAD HACK! disable cacheing on LD when address is 0xCxxx_xxxx
121 # this is for peripherals. same thing done in Microwatt loadstore1.vhdl
122 with m.If(addr[28:] == Const(0xc, 4)):
123 m.d.comb += self.nc.eq(1)
124 # option to disable the cache entirely for read
125 if self.disable_cache:
126 m.d.comb += self.nc.eq(1)
127 return None #FIXME return value
128
129 def set_wr_data(self, m, data, wen):
130 # do the "blip" on write data
131 m.d.comb += self.d_valid.eq(1)
132 m.d.comb += self.d_in.valid.eq(self.d_validblip)
133 # put data into comb which is picked up in main elaborate()
134 m.d.comb += self.d_w_valid.eq(1)
135 m.d.comb += self.store_data.eq(data)
136 #m.d.sync += self.d_in.byte_sel.eq(wen) # this might not be needed
137 st_ok = self.done # TODO indicates write data is valid
138 return st_ok
139
140 def get_rd_data(self, m):
141 ld_ok = self.done # indicates read data is valid
142 data = self.load_data # actual read data
143 return data, ld_ok
144
145 """
146 if d_in.error = '1' then
147 if d_in.cache_paradox = '1' then
148 -- signal an interrupt straight away
149 exception := '1';
150 dsisr(63 - 38) := not r2.req.load;
151 -- XXX there is no architected bit for this
152 -- (probably should be a machine check in fact)
153 dsisr(63 - 35) := d_in.cache_paradox;
154 else
155 -- Look up the translation for TLB miss
156 -- and also for permission error and RC error
157 -- in case the PTE has been updated.
158 mmureq := '1';
159 v.state := MMU_LOOKUP;
160 v.stage1_en := '0';
161 end if;
162 end if;
163 """
164
165 def elaborate(self, platform):
166 m = super().elaborate(platform)
167 comb, sync = m.d.comb, m.d.sync
168
169 # create dcache module
170 m.submodules.dcache = dcache = self.dcache
171
172 # temp vars
173 d_in, d_out, l_out, dbus = self.d_in, self.d_out, self.l_out, self.dbus
174
175 # fsm skeleton
176 with m.Switch(self.state):
177 with m.Case(State.IDLE):
178 with m.If(self.d_validblip):
179 sync += self.state.eq(State.ACK_WAIT)
180
181 with m.Case(State.ACK_WAIT): # waiting for completion
182 with m.If(d_out.error):
183 with m.If(d_out.cache_paradox):
184 sync += self.derror.eq(1)
185 sync += self.state.eq(State.IDLE)
186 sync += self.dsisr[63 - 38].eq(~self.load)
187 # XXX there is no architected bit for this
188 # (probably should be a machine check in fact)
189 sync += self.dsisr[63 - 35].eq(d_out.cache_paradox)
190
191 with m.Else():
192 # Look up the translation for TLB miss
193 # and also for permission error and RC error
194 # in case the PTE has been updated.
195 sync += self.mmureq.eq(1)
196 sync += self.state.eq(State.MMU_LOOKUP)
197 with m.If(d_out.valid):
198 m.d.comb += self.done.eq(1)
199 sync += self.state.eq(State.IDLE)
200 with m.If(self.load):
201 m.d.comb += self.load_data.eq(d_out.data)
202
203 with m.Case(State.MMU_LOOKUP):
204 with m.If(l_out.done):
205 with m.If(self.instr_fault==0):
206 # retry the request now that the MMU has
207 # installed a TLB entry
208 sync += self.state.eq(State.ACK_WAIT)
209 with m.If(l_out.err):
210 sync += self.dsisr[63 - 33].eq(l_out.invalid)
211 sync += self.dsisr[63 - 36].eq(l_out.perm_error)
212 sync += self.dsisr[63 - 38].eq(self.load)
213 sync += self.dsisr[63 - 44].eq(l_out.badtree)
214 sync += self.dsisr[63 - 45].eq(l_out.rc_error)
215
216 '''
217 if m_in.done = '1' then # actually l_out.done
218 if r.instr_fault = '0' then
219 # retry the request now that the MMU has
220 # installed a TLB entry
221 v.state := ACK_WAIT;
222 end if;
223 end if;
224 if m_in.err = '1' then # actually l_out.err
225 dsisr(63 - 33) := m_in.invalid;
226 dsisr(63 - 36) := m_in.perm_error;
227 dsisr(63 - 38) := not r.load;
228 dsisr(63 - 44) := m_in.badtree;
229 dsisr(63 - 45) := m_in.rc_error;
230 end if;
231 '''
232 pass
233
234 with m.Case(State.TLBIE_WAIT):
235 pass
236 with m.Case(State.COMPLETE):
237 pass
238
239 # happened, alignment, instr_fault, invalid.
240 # note that all of these flow through - eventually to the TRAP
241 # pipeline, via PowerDecoder2.
242 exc = self.pi.exc_o
243 comb += exc.happened.eq(d_out.error | l_out.err | self.align_intr)
244 comb += exc.invalid.eq(l_out.invalid)
245 comb += exc.alignment.eq(self.align_intr)
246
247 # badtree, perm_error, rc_error, segment_fault
248 comb += exc.badtree.eq(l_out.badtree)
249 comb += exc.perm_error.eq(l_out.perm_error)
250 comb += exc.rc_error.eq(l_out.rc_error)
251 comb += exc.segment_fault.eq(l_out.segerr)
252
253 # TODO some exceptions set SPRs
254
255 # TODO, connect dcache wb_in/wb_out to "standard" nmigen Wishbone bus
256 comb += dbus.adr.eq(dcache.wb_out.adr)
257 comb += dbus.dat_w.eq(dcache.wb_out.dat)
258 comb += dbus.sel.eq(dcache.wb_out.sel)
259 comb += dbus.cyc.eq(dcache.wb_out.cyc)
260 comb += dbus.stb.eq(dcache.wb_out.stb)
261 comb += dbus.we.eq(dcache.wb_out.we)
262
263 comb += dcache.wb_in.dat.eq(dbus.dat_r)
264 comb += dcache.wb_in.ack.eq(dbus.ack)
265 if hasattr(dbus, "stall"):
266 comb += dcache.wb_in.stall.eq(dbus.stall)
267
268 # create a blip (single pulse) on valid read/write request
269 m.d.comb += self.d_validblip.eq(rising_edge(m, self.d_valid))
270
271 # write out d data only when flag set
272 with m.If(self.d_w_valid):
273 m.d.sync += d_in.data.eq(self.store_data)
274 with m.Else():
275 m.d.sync += d_in.data.eq(0)
276
277 # this must move into the FSM, conditionally noticing that
278 # the "blip" comes from self.d_validblip.
279 # task 1: look up in dcache
280 # task 2: if dcache fails, look up in MMU.
281 # do **NOT** confuse the two.
282 m.d.comb += d_in.load.eq(self.load)
283 m.d.comb += d_in.byte_sel.eq(self.byte_sel)
284 m.d.comb += d_in.addr.eq(self.addr)
285 m.d.comb += d_in.nc.eq(self.nc)
286
287 # XXX these should be possible to remove but for some reason
288 # cannot be... yet. TODO, investigate
289 m.d.comb += self.done.eq(d_out.valid)
290 m.d.comb += self.load_data.eq(d_out.data)
291
292 return m
293
294 def ports(self):
295 yield from super().ports()
296 # TODO: memory ports
297
298
299 class TestSRAMLoadStore1(LoadStore1):
300 def __init__(self, pspec):
301 super().__init__(pspec)
302 pspec = self.pspec
303 # small 32-entry Memory
304 if (hasattr(pspec, "dmem_test_depth") and
305 isinstance(pspec.dmem_test_depth, int)):
306 depth = pspec.dmem_test_depth
307 else:
308 depth = 32
309 print("TestSRAMBareLoadStoreUnit depth", depth)
310
311 self.mem = Memory(width=pspec.reg_wid, depth=depth)
312
313 def elaborate(self, platform):
314 m = super().elaborate(platform)
315 comb = m.d.comb
316 m.submodules.sram = sram = SRAM(memory=self.mem, granularity=8,
317 features={'cti', 'bte', 'err'})
318 dbus = self.dbus
319
320 # directly connect the wishbone bus of LoadStoreUnitInterface to SRAM
321 # note: SRAM is a target (slave), dbus is initiator (master)
322 fanouts = ['dat_w', 'sel', 'cyc', 'stb', 'we', 'cti', 'bte']
323 fanins = ['dat_r', 'ack', 'err']
324 for fanout in fanouts:
325 print("fanout", fanout, getattr(sram.bus, fanout).shape(),
326 getattr(dbus, fanout).shape())
327 comb += getattr(sram.bus, fanout).eq(getattr(dbus, fanout))
328 comb += getattr(sram.bus, fanout).eq(getattr(dbus, fanout))
329 for fanin in fanins:
330 comb += getattr(dbus, fanin).eq(getattr(sram.bus, fanin))
331 # connect address
332 comb += sram.bus.adr.eq(dbus.adr)
333
334 return m
335