hook up MSR into MMU (TODO, use a lot less bits)
[soc.git] / src / soc / fu / mmu / fsm.py
1 from nmigen import Elaboratable, Module, Signal, Shape, unsigned, Cat, Mux
2 from nmigen import Const
3 from soc.fu.mmu.pipe_data import MMUInputData, MMUOutputData, MMUPipeSpec
4 from nmutil.singlepipe import ControlBase
5 from nmutil.util import rising_edge
6
7 from soc.experiment.mmu import MMU
8 from soc.experiment.dcache import DCache
9
10 from openpower.consts import MSR
11 from openpower.decoder.power_fields import DecodeFields
12 from openpower.decoder.power_fieldsn import SignalBitRange
13 from openpower.decoder.power_decoder2 import decode_spr_num
14 from openpower.decoder.power_enums import MicrOp, XER_bits
15
16 from soc.experiment.pimem import PortInterface
17 from soc.experiment.pimem import PortInterfaceBase
18
19 from soc.experiment.mem_types import LoadStore1ToDCacheType, LoadStore1ToMMUType
20 from soc.experiment.mem_types import DCacheToLoadStore1Type, MMUToLoadStore1Type
21
22
23 # glue logic for microwatt mmu and dcache
24 class LoadStore1(PortInterfaceBase):
25 def __init__(self, regwid=64, addrwid=4):
26 super().__init__(regwid, addrwid)
27 self.d_in = LoadStore1ToDCacheType()
28 self.d_out = DCacheToLoadStore1Type()
29 self.l_in = LoadStore1ToMMUType()
30 self.l_out = MMUToLoadStore1Type()
31 # for debugging with gtkwave only
32 self.debug1 = Signal()
33 self.debug2 = Signal()
34 # TODO microwatt
35 self.mmureq = Signal()
36 self.derror = Signal()
37
38 def set_wr_addr(self, m, addr, mask):
39 #m.d.comb += self.d_in.valid.eq(1)
40 #m.d.comb += self.l_in.valid.eq(1)
41 #m.d.comb += self.d_in.load.eq(0)
42 #m.d.comb += self.l_in.load.eq(0)
43 # set phys addr on both units
44 m.d.comb += self.d_in.addr.eq(addr)
45 m.d.comb += self.l_in.addr.eq(addr)
46 # TODO set mask
47 return None
48
49 def set_rd_addr(self, m, addr, mask):
50 m.d.comb += self.d_in.valid.eq(1)
51 m.d.comb += self.l_in.valid.eq(1)
52 m.d.comb += self.d_in.load.eq(1)
53 m.d.comb += self.l_in.load.eq(1)
54 m.d.comb += self.d_in.addr.eq(addr)
55 m.d.comb += self.l_in.addr.eq(addr)
56 m.d.comb += self.debug1.eq(1)
57 # m.d.comb += self.debug2.eq(1)
58 return None #FIXME return value
59
60 def set_wr_data(self, m, data, wen):
61 m.d.comb += self.d_in.data.eq(data)
62 # TODO set wen
63 st_ok = Const(1, 1)
64 return st_ok
65
66 def get_rd_data(self, m):
67 ld_ok = Const(1, 1)
68 data = self.d_out.data
69 return data, ld_ok
70
71 """
72 if d_in.error = '1' then
73 if d_in.cache_paradox = '1' then
74 -- signal an interrupt straight away
75 exception := '1';
76 dsisr(63 - 38) := not r2.req.load;
77 -- XXX there is no architected bit for this
78 -- (probably should be a machine check in fact)
79 dsisr(63 - 35) := d_in.cache_paradox;
80 else
81 -- Look up the translation for TLB miss
82 -- and also for permission error and RC error
83 -- in case the PTE has been updated.
84 mmureq := '1';
85 v.state := MMU_LOOKUP;
86 v.stage1_en := '0';
87 end if;
88 end if;
89 """
90
91 def elaborate(self, platform):
92 m = super().elaborate(platform)
93
94 d_out = self.d_out
95 l_out = self.l_out
96
97 with m.If(d_out.error):
98 with m.If(d_out.cache_paradox):
99 m.d.comb += self.derror.eq(1)
100 # dsisr(63 - 38) := not r2.req.load;
101 # -- XXX there is no architected bit for this
102 # -- (probably should be a machine check in fact)
103 # dsisr(63 - 35) := d_in.cache_paradox;
104 with m.Else():
105 # Look up the translation for TLB miss
106 # and also for permission error and RC error
107 # in case the PTE has been updated.
108 m.d.comb += self.mmureq.eq(1)
109 # v.state := MMU_LOOKUP;
110 # v.stage1_en := '0';
111
112 exc = self.pi.exception_o
113
114 #happened, alignment, instr_fault, invalid,
115 m.d.comb += exc.happened.eq(d_out.error | l_out.err)
116 m.d.comb += exc.invalid.eq(l_out.invalid)
117
118 #badtree, perm_error, rc_error, segment_fault
119 m.d.comb += exc.badtree.eq(l_out.badtree)
120 m.d.comb += exc.perm_error.eq(l_out.perm_error)
121 m.d.comb += exc.rc_error.eq(l_out.rc_error)
122 m.d.comb += exc.segment_fault.eq(l_out.segerr)
123
124 # TODO connect those signals somewhere
125 #print(d_out.valid) -> no error
126 #print(d_out.store_done) -> no error
127 #print(d_out.cache_paradox) -> ?
128 #print(l_out.done) -> no error
129
130 # TODO some exceptions set SPRs
131
132 return m
133
134 def ports(self):
135 yield from super().ports()
136 # TODO: memory ports
137
138
139 class FSMMMUStage(ControlBase):
140 def __init__(self, pspec):
141 super().__init__()
142 self.pspec = pspec
143
144 # set up p/n data
145 self.p.data_i = MMUInputData(pspec)
146 self.n.data_o = MMUOutputData(pspec)
147
148 # incoming PortInterface
149 self.ldst = LoadStore1() # TODO make this depend on pspec
150 self.pi = self.ldst.pi
151
152 # this Function Unit is extremely unusual in that it actually stores a
153 # "thing" rather than "processes inputs and produces outputs". hence
154 # why it has to be a FSM. linking up LD/ST however is going to have
155 # to be done back in Issuer (or Core)
156
157 self.mmu = MMU()
158 self.dcache = DCache()
159
160 # make life a bit easier in Core
161 self.pspec.mmu = self.mmu
162 self.pspec.dcache = self.dcache
163
164 # debugging output for gtkw
165 self.debug0 = Signal(4)
166 self.debug1 = Signal()
167 #self.debug2 = Signal(64)
168 #self.debug3 = Signal(64)
169 self.illegal = Signal()
170
171 # for SPR field number access
172 i = self.p.data_i
173 self.fields = DecodeFields(SignalBitRange, [i.ctx.op.insn])
174 self.fields.create_specs()
175
176 def elaborate(self, platform):
177 m = super().elaborate(platform)
178 comb = m.d.comb
179
180 # link mmu and dcache together
181 m.submodules.dcache = dcache = self.dcache
182 m.submodules.mmu = mmu = self.mmu
183 m.submodules.ldst = ldst = self.ldst
184 m.d.comb += dcache.m_in.eq(mmu.d_out)
185 m.d.comb += mmu.d_in.eq(dcache.m_out)
186 l_in, l_out = mmu.l_in, mmu.l_out
187 d_in, d_out = dcache.d_in, dcache.d_out
188 wb_out, wb_in = dcache.wb_out, dcache.wb_in
189
190 # link ldst and dcache together
191 comb += l_in.eq(self.ldst.l_in)
192 comb += self.ldst.l_out.eq(l_out)
193 comb += d_in.eq(self.ldst.d_in)
194 comb += self.ldst.d_out.eq(self.dcache.d_out)
195
196 data_i, data_o = self.p.data_i, self.n.data_o
197 a_i, b_i, o, spr1_o = data_i.ra, data_i.rb, data_o.o, data_o.spr1
198 op = data_i.ctx.op
199 msr_i = op.msr
200
201 # TODO: link these SPRs somewhere
202 dsisr = Signal(64)
203 dar = Signal(64)
204
205 # busy/done signals
206 busy = Signal()
207 done = Signal()
208 m.d.comb += self.n.valid_o.eq(busy & done)
209 m.d.comb += self.p.ready_o.eq(~busy)
210
211 # take copy of X-Form SPR field
212 x_fields = self.fields.FormXFX
213 spr = Signal(len(x_fields.SPR))
214 comb += spr.eq(decode_spr_num(x_fields.SPR))
215
216 # based on MSR bits, set priv and virt mode. TODO: 32-bit mode
217 comb += d_in.priv_mode.eq(~msr_i[MSR.PR])
218 comb += d_in.virt_mode.eq(msr_i[MSR.DR])
219 #comb += d_in.mode_32bit.eq(msr_i[MSR.SF]) # ?? err
220
221 # ok so we have to "pulse" the MMU (or dcache) rather than
222 # hold the valid hi permanently. guess what this does...
223 valid = Signal()
224 blip = Signal()
225 m.d.comb += blip.eq(rising_edge(m, valid))
226
227 with m.If(~busy):
228 with m.If(self.p.valid_i):
229 m.d.sync += busy.eq(1)
230 with m.Else():
231
232 # based on the Micro-Op, we work out which of MMU or DCache
233 # should "action" the operation. one of MMU or DCache gets
234 # enabled ("valid") and we twiddle our thumbs until it
235 # responds ("done").
236
237 # FIXME: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
238
239 with m.Switch(op.insn_type):
240 with m.Case(MicrOp.OP_MTSPR):
241 # despite redirection this FU **MUST** behave exactly
242 # like the SPR FU. this **INCLUDES** updating the SPR
243 # regfile because the CSV file entry for OP_MTSPR
244 # categorically defines and requires the expectation
245 # that the CompUnit **WILL** write to the regfile.
246 comb += spr1_o.data.eq(spr)
247 comb += spr1_o.ok.eq(1)
248 # subset SPR: first check a few bits
249 with m.If(~spr[9] & ~spr[5]):
250 comb += self.debug0.eq(3)
251 with m.If(spr[0]):
252 comb += dsisr.eq(a_i[:32])
253 with m.Else():
254 comb += dar.eq(a_i)
255 comb += done.eq(1)
256 # pass it over to the MMU instead
257 with m.Else():
258 comb += self.debug0.eq(4)
259 # blip the MMU and wait for it to complete
260 comb += valid.eq(1) # start "pulse"
261 comb += l_in.valid.eq(blip) # start
262 comb += l_in.mtspr.eq(1) # mtspr mode
263 comb += l_in.sprn.eq(spr) # which SPR
264 comb += l_in.rs.eq(a_i) # incoming operand (RS)
265 comb += done.eq(1) # FIXME l_out.done
266
267 with m.Case(MicrOp.OP_MFSPR):
268 # subset SPR: first check a few bits
269 with m.If(~spr[9] & ~spr[5]):
270 comb += self.debug0.eq(5)
271 with m.If(spr[0]):
272 comb += o.data.eq(dsisr)
273 with m.Else():
274 comb += o.data.eq(dar)
275 comb += o.ok.eq(1)
276 comb += done.eq(1)
277 # pass it over to the MMU instead
278 with m.Else():
279 comb += self.debug0.eq(6)
280 # blip the MMU and wait for it to complete
281 comb += valid.eq(1) # start "pulse"
282 comb += l_in.valid.eq(blip) # start
283 comb += l_in.mtspr.eq(0) # mfspr!=mtspr
284 comb += l_in.sprn.eq(spr) # which SPR
285 comb += l_in.rs.eq(a_i) # incoming operand (RS)
286 comb += o.data.eq(l_out.sprval) # SPR from MMU
287 comb += o.ok.eq(l_out.done) # only when l_out valid
288 comb += done.eq(1) # FIXME l_out.done
289
290 with m.Case(MicrOp.OP_DCBZ):
291 # activate dcbz mode (spec: v3.0B p850)
292 comb += valid.eq(1) # start "pulse"
293 comb += d_in.valid.eq(blip) # start
294 comb += d_in.dcbz.eq(1) # dcbz mode
295 comb += d_in.addr.eq(a_i + b_i) # addr is (RA|0) + RB
296 comb += done.eq(d_out.store_done) # TODO
297 comb += self.debug0.eq(1)
298
299 with m.Case(MicrOp.OP_TLBIE):
300 # pass TLBIE request to MMU (spec: v3.0B p1034)
301 # note that the spr is *not* an actual spr number, it's
302 # just that those bits happen to match with field bits
303 # RIC, PRS, R
304 comb += valid.eq(1) # start "pulse"
305 comb += l_in.valid.eq(blip) # start
306 comb += l_in.tlbie.eq(1) # mtspr mode
307 comb += l_in.sprn.eq(spr) # use sprn to send insn bits
308 comb += l_in.addr.eq(b_i) # incoming operand (RB)
309 comb += done.eq(l_out.done) # zzzz
310 comb += self.debug0.eq(2)
311 with m.Case(MicrOp.OP_ILLEGAL):
312 comb += self.illegal.eq(1)
313
314 with m.If(self.n.ready_i & self.n.valid_o):
315 m.d.sync += busy.eq(0)
316
317 return m
318
319 def __iter__(self):
320 yield from self.p
321 yield from self.n
322
323 def ports(self):
324 return list(self)