link MSR.PR into MMU FSM OP_FETCH_FAILED
[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 = self.dcache
77
78 # link mmu and dcache together
79 m.submodules.mmu = mmu = self.mmu
80 ldst = self.ldst # managed externally: do not add here
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
84 l_in, l_out = mmu.l_in, mmu.l_out
85 d_in, d_out = dcache.d_in, dcache.d_out
86
87 # link ldst and MMU together
88 comb += l_in.eq(ldst.m_out)
89 comb += ldst.m_in.eq(l_out)
90
91 i_data, o_data = self.p.i_data, self.n.o_data
92 op = i_data.ctx.op
93 nia_i = op.nia
94 msr_i = op.msr
95 a_i, b_i, spr1_i = i_data.ra, i_data.rb, i_data.spr1
96 o, exc_o, spr1_o = o_data.o, o_data.exception, o_data.spr1
97
98 # busy/done signals
99 busy = Signal(name="mmu_fsm_busy")
100 done = Signal(name="mmu_fsm_done")
101 m.d.comb += self.n.o_valid.eq(busy & done)
102 m.d.comb += self.p.o_ready.eq(~busy)
103
104 # take copy of X-Form SPR field
105 x_fields = self.fields.FormXFX
106 spr = Signal(len(x_fields.SPR))
107 comb += spr.eq(decode_spr_num(x_fields.SPR))
108
109 # ok so we have to "pulse" the MMU (or dcache) rather than
110 # hold the valid hi permanently. guess what this does...
111 valid = Signal()
112 blip = Signal()
113 m.d.comb += blip.eq(rising_edge(m, valid))
114
115 with m.If(~busy):
116 with m.If(self.p.i_valid):
117 sync += busy.eq(1)
118 with m.Else():
119
120 # based on the Micro-Op, we work out which of MMU or DCache
121 # should "action" the operation. one of MMU or DCache gets
122 # enabled ("valid") and we twiddle our thumbs until it
123 # responds ("done").
124
125 # WIP: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
126
127 with m.Switch(op.insn_type):
128
129 ##########
130 # OP_MTSPR
131 ##########
132
133 with m.Case(MicrOp.OP_MTSPR):
134 comb += Display("MMUTEST: OP_MTSPR: spr=%i", spr)
135 # despite redirection this FU **MUST** behave exactly
136 # like the SPR FU. this **INCLUDES** updating the SPR
137 # regfile because the CSV file entry for OP_MTSPR
138 # categorically defines and requires the expectation
139 # that the CompUnit **WILL** write to the regfile.
140 comb += spr1_o.data.eq(a_i)
141 comb += spr1_o.ok.eq(1)
142 # subset SPR: first check a few bits
143 # XXX NOTE this must now cover **FOUR** values: this
144 # test might not be adequate. DSISR, DAR, PGTBL and PID
145 # must ALL be covered here.
146 with m.If(~spr[9] & ~spr[5]):
147 comb += self.debug0.eq(3)
148 #if matched update local cached value
149 #commented out because there is a driver conflict
150 comb += ldst.sprval_in.eq(a_i)
151 comb += ldst.mmu_set_spr.eq(1)
152 with m.If(spr[0]):
153 comb += ldst.mmu_set_dar.eq(1)
154 with m.Else():
155 comb += ldst.mmu_set_dsisr.eq(1)
156 comb += done.eq(1)
157 # pass it over to the MMU instead
158 with m.Else():
159 # PGTBL and PID
160 comb += self.debug0.eq(4)
161 # blip the MMU and wait for it to complete
162 comb += valid.eq(1) # start "pulse"
163 comb += l_in.valid.eq(blip) # start
164 comb += l_in.mtspr.eq(1) # mtspr mode
165 comb += l_in.sprn.eq(spr) # which SPR
166 comb += l_in.rs.eq(a_i) # incoming operand (RS)
167 comb += done.eq(1) # FIXME l_out.done
168
169 ##########
170 # OP_MFSPR
171 ##########
172
173 with m.Case(MicrOp.OP_MFSPR):
174 comb += Display("MMUTEST: OP_MFSPR: spr=%i returns=%i",
175 spr, spr1_i)
176 # partial SPR number decoding perfectly fine
177 with m.If(spr[9] | spr[5]):
178 # identified as an MMU OP_MFSPR, contact the MMU.
179 # interestingly, the read is combinatorial: no need
180 # to set "valid", just set the SPR number
181 comb += l_in.sprn.eq(spr) # which SPR
182 comb += o.data.eq(l_out.sprval)
183 with m.Else():
184 # identified as DSISR or DAR. again: read the SPR
185 # directly, combinatorial access
186 with m.If(spr[0]):
187 comb += o.data.eq(ldst.dar)
188 with m.Else():
189 comb += o.data.eq(ldst.dsisr)
190
191 comb += o.ok.eq(1)
192 comb += done.eq(1)
193
194 ##########
195 # OP_TLBIE
196 ##########
197
198 with m.Case(MicrOp.OP_TLBIE):
199 comb += Display("MMUTEST: OP_TLBIE: insn_bits=%i", spr)
200 # pass TLBIE request to MMU (spec: v3.0B p1034)
201 # note that the spr is *not* an actual spr number, it's
202 # just that those bits happen to match with field bits
203 # RIC, PRS, R
204 comb += Display("TLBIE: %i %i", spr, l_out.done)
205 comb += valid.eq(1) # start "pulse"
206 comb += l_in.valid.eq(blip) # start
207 comb += l_in.tlbie.eq(1) # mtspr mode
208 comb += l_in.sprn.eq(spr) # use sprn to send insn bits
209 comb += l_in.addr.eq(b_i) # incoming operand (RB)
210 comb += done.eq(l_out.done) # zzzz
211 comb += self.debug0.eq(2)
212
213 ##########
214 # OP_FETCH_FAILED
215 ##########
216
217 with m.Case(MicrOp.OP_FETCH_FAILED):
218 comb += Display("MMUTEST: OP_FETCH_FAILED: @%x", nia_i)
219 # trigger an instruction fetch failed MMU event.
220 # PowerDecoder2 drops svstate.pc into NIA for us
221 # really, this should be direct communication with the
222 # MMU, rather than going through LoadStore1. but, doing
223 # so allows for the opportunity to prevent LoadStore1
224 # from accepting any other LD/ST requests.
225 comb += valid.eq(1) # start "pulse"
226 comb += ldst.instr_fault.eq(blip)
227 comb += ldst.priv_mode.eq(msr_i[MSR.PR])
228 comb += ldst.maddr.eq(nia_i)
229 # XXX should not access this!
230 mmu_done_delay = Signal()
231 sync += mmu_done_delay.eq(mmu.d_in.done)
232 comb += done.eq(mmu_done_delay)
233 comb += self.debug0.eq(3)
234 # LDST unit contains exception data, which (messily)
235 # is copied over, here. not ideal but it will do for now
236 comb += exc_o.eq(ldst.pi.exc_o)
237
238 ############
239 # OP_ILLEGAL
240 ############
241
242 with m.Case(MicrOp.OP_ILLEGAL):
243 comb += self.illegal.eq(1)
244
245 with m.If(self.n.i_ready & self.n.o_valid):
246 sync += busy.eq(0)
247
248 return m
249
250 def __iter__(self):
251 yield from self.p
252 yield from self.n
253
254 def ports(self):
255 return list(self)