fix issue with priv_mode not being passed correctly to MMU
[soc.git] / src / soc / fu / mmu / fsm.py
1 """
2 Based on microwatt mmu.vhdl
3
4 * https://bugs.libre-soc.org/show_bug.cgi?id=491
5 * https://bugs.libre-soc.org/show_bug.cgi?id=450
6 """
7
8 from nmigen import Elaboratable, Module, Signal, Shape, unsigned, Cat, Mux
9 from nmigen import Record, Memory
10 from nmigen import Const
11 from soc.fu.mmu.pipe_data import MMUInputData, MMUOutputData, MMUPipeSpec
12 from nmutil.singlepipe import ControlBase
13 from nmutil.util import rising_edge
14
15 from soc.experiment.mmu import MMU
16
17 from openpower.consts import MSR
18 from openpower.decoder.power_fields import DecodeFields
19 from openpower.decoder.power_fieldsn import SignalBitRange
20 from openpower.decoder.power_decoder2 import decode_spr_num
21 from openpower.decoder.power_enums import MicrOp
22
23 from soc.experiment.mem_types import LoadStore1ToMMUType
24 from soc.experiment.mem_types import MMUToLoadStore1Type
25
26 from soc.fu.ldst.loadstore import LoadStore1, TestSRAMLoadStore1
27 from nmutil.util import Display
28
29
30 class FSMMMUStage(ControlBase):
31 """FSM MMU
32
33 FSM-based MMU: must call set_ldst_interface and pass in an instance
34 of a LoadStore1. this to comply with the ConfigMemoryPortInterface API
35
36 this Function Unit is extremely unusual in that it actually stores a
37 "thing" rather than "processes inputs and produces outputs". hence
38 why it has to be a FSM. linking up LD/ST however is going to have
39 to be done back in Issuer (or Core). sorted: call set_ldst_interface
40 """
41 def __init__(self, pspec):
42 super().__init__()
43 self.pspec = pspec
44
45 # set up p/n data
46 self.p.i_data = MMUInputData(pspec)
47 self.n.o_data = MMUOutputData(pspec)
48 self.exc_o = self.n.o_data.exception # AllFunctionUnits needs this
49
50 self.mmu = MMU()
51
52 # debugging output for gtkw
53 self.debug0 = Signal(4)
54 self.illegal = Signal()
55
56 # for SPR field number access
57 i = self.p.i_data
58 self.fields = DecodeFields(SignalBitRange, [i.ctx.op.insn])
59 self.fields.create_specs()
60
61 def set_ldst_interface(self, ldst):
62 """must be called back in Core, after FUs have been set up.
63 one of those will be the MMU (us!) but the LoadStore1 instance
64 must be set up in ConfigMemoryPortInterface. sigh.
65 """
66 # incoming PortInterface
67 self.ldst = ldst
68 self.dcache = self.ldst.dcache
69 self.icache = self.ldst.icache
70 self.pi = self.ldst.pi
71
72 def elaborate(self, platform):
73 assert hasattr(self, "dcache"), "remember to call set_ldst_interface"
74 m = super().elaborate(platform)
75 comb, sync = m.d.comb, m.d.sync
76 dcache, icache = self.dcache, self.icache
77 ldst = self.ldst # managed externally: do not add here
78
79 # link mmu, dcache and icache together
80 m.submodules.mmu = mmu = self.mmu
81 m.d.comb += dcache.m_in.eq(mmu.d_out) # MMUToDCacheType
82 m.d.comb += mmu.d_in.eq(dcache.m_out) # DCacheToMMUType
83 m.d.comb += icache.m_in.eq(mmu.i_out) # MMUToICacheType
84
85 l_in, l_out = mmu.l_in, mmu.l_out
86 d_in, d_out = dcache.d_in, dcache.d_out
87
88 # link ldst and MMU together
89 comb += l_in.eq(ldst.m_out)
90 comb += ldst.m_in.eq(l_out)
91
92 i_data, o_data = self.p.i_data, self.n.o_data
93 op = i_data.ctx.op
94 cia_i = op.cia
95 msr_i = op.msr
96 a_i, b_i, spr1_i = i_data.ra, i_data.rb, i_data.spr1
97 o, exc_o, spr1_o = o_data.o, o_data.exception, o_data.spr1
98
99 # busy/done signals
100 busy = Signal(name="mmu_fsm_busy")
101 done = Signal(name="mmu_fsm_done")
102 m.d.comb += self.n.o_valid.eq(busy & done)
103 m.d.comb += self.p.o_ready.eq(~busy)
104
105 # take copy of X-Form SPR field
106 x_fields = self.fields.FormXFX
107 spr = Signal(len(x_fields.SPR))
108 comb += spr.eq(decode_spr_num(x_fields.SPR))
109
110 # ok so we have to "pulse" the MMU (or dcache) rather than
111 # hold the valid hi permanently. guess what this does...
112 valid = Signal()
113 blip = Signal()
114 m.d.comb += blip.eq(rising_edge(m, valid))
115
116 with m.If(~busy):
117 with m.If(self.p.i_valid):
118 sync += busy.eq(1)
119 with m.Else():
120
121 # based on the Micro-Op, we work out which of MMU or DCache
122 # should "action" the operation. one of MMU or DCache gets
123 # enabled ("valid") and we twiddle our thumbs until it
124 # responds ("done").
125
126 # WIP: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
127
128 with m.Switch(op.insn_type):
129
130 ##########
131 # OP_MTSPR
132 ##########
133
134 with m.Case(MicrOp.OP_MTSPR):
135 comb += Display("MMUTEST: OP_MTSPR: spr=%i", spr)
136 # despite redirection this FU **MUST** behave exactly
137 # like the SPR FU. this **INCLUDES** updating the SPR
138 # regfile because the CSV file entry for OP_MTSPR
139 # categorically defines and requires the expectation
140 # that the CompUnit **WILL** write to the regfile.
141 comb += spr1_o.data.eq(a_i)
142 comb += spr1_o.ok.eq(1)
143 # subset SPR: first check a few bits
144 # XXX NOTE this must now cover **FOUR** values: this
145 # test might not be adequate. DSISR, DAR, PGTBL and PID
146 # must ALL be covered here.
147 with m.If(~spr[9] & ~spr[5]):
148 comb += self.debug0.eq(3)
149 #if matched update local cached value
150 #commented out because there is a driver conflict
151 comb += ldst.sprval_in.eq(a_i)
152 comb += ldst.mmu_set_spr.eq(1)
153 with m.If(spr[0]):
154 comb += ldst.mmu_set_dar.eq(1)
155 with m.Else():
156 comb += ldst.mmu_set_dsisr.eq(1)
157 comb += done.eq(1)
158 # pass it over to the MMU instead
159 with m.Else():
160 # PGTBL and PID
161 comb += self.debug0.eq(4)
162 # blip the MMU and wait for it to complete
163 comb += valid.eq(1) # start "pulse"
164 comb += l_in.valid.eq(blip) # start
165 comb += l_in.mtspr.eq(1) # mtspr mode
166 comb += l_in.sprn.eq(spr) # which SPR
167 comb += l_in.rs.eq(a_i) # incoming operand (RS)
168 comb += done.eq(1) # FIXME l_out.done
169
170 ##########
171 # OP_MFSPR
172 ##########
173
174 with m.Case(MicrOp.OP_MFSPR):
175 comb += Display("MMUTEST: OP_MFSPR: spr=%i returns=%i",
176 spr, spr1_i)
177 # partial SPR number decoding perfectly fine
178 with m.If(spr[9] | spr[5]):
179 # identified as an MMU OP_MFSPR, contact the MMU.
180 # interestingly, the read is combinatorial: no need
181 # to set "valid", just set the SPR number
182 comb += l_in.sprn.eq(spr) # which SPR
183 comb += o.data.eq(l_out.sprval)
184 with m.Else():
185 # identified as DSISR or DAR. again: read the SPR
186 # directly, combinatorial access
187 with m.If(spr[0]):
188 comb += o.data.eq(ldst.dar)
189 with m.Else():
190 comb += o.data.eq(ldst.dsisr)
191
192 comb += o.ok.eq(1)
193 comb += done.eq(1)
194
195 ##########
196 # OP_TLBIE
197 ##########
198
199 with m.Case(MicrOp.OP_TLBIE):
200 comb += Display("MMUTEST: OP_TLBIE: insn_bits=%i", spr)
201 # pass TLBIE request to MMU (spec: v3.0B p1034)
202 # note that the spr is *not* an actual spr number, it's
203 # just that those bits happen to match with field bits
204 # RIC, PRS, R
205 comb += Display("TLBIE: %i %i", spr, l_out.done)
206 comb += valid.eq(1) # start "pulse"
207 comb += l_in.valid.eq(blip) # start
208 comb += l_in.tlbie.eq(1) # mtspr mode
209 comb += l_in.sprn.eq(spr) # use sprn to send insn bits
210 comb += l_in.addr.eq(b_i) # incoming operand (RB)
211 comb += done.eq(l_out.done) # zzzz
212 comb += self.debug0.eq(2)
213
214 ##########
215 # OP_FETCH_FAILED
216 ##########
217
218 with m.Case(MicrOp.OP_FETCH_FAILED):
219 comb += Display("MMUTEST: OP_FETCH_FAILED: @%x", cia_i)
220 # trigger an instruction fetch failed MMU event.
221 # PowerDecoder2 drops svstate.pc into NIA for us
222 # really, this should be direct communication with the
223 # MMU, rather than going through LoadStore1. but, doing
224 # so allows for the opportunity to prevent LoadStore1
225 # from accepting any other LD/ST requests.
226 comb += valid.eq(1) # start "pulse"
227 comb += ldst.instr_fault.eq(blip)
228 comb += ldst.priv_mode.eq(~msr_i[MSR.PR])
229 comb += ldst.maddr.eq(cia_i)
230 # XXX should not access this!
231 comb += done.eq(ldst.done)
232 comb += self.debug0.eq(3)
233 # LDST unit contains exception data, which (messily)
234 # is copied over, here. not ideal but it will do for now
235 comb += exc_o.eq(ldst.pi.exc_o)
236
237 ############
238 # OP_ILLEGAL
239 ############
240
241 with m.Case(MicrOp.OP_ILLEGAL):
242 comb += self.illegal.eq(1)
243
244 with m.If(self.n.i_ready & self.n.o_valid):
245 sync += busy.eq(0)
246
247 return m
248
249 def __iter__(self):
250 yield from self.p
251 yield from self.n
252
253 def ports(self):
254 return list(self)