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