whoops, import error
[soc.git] / src / soc / fu / mmu / fsm.py
1 from nmigen import Elaboratable, Module, Signal, Shape, unsigned, Cat, Mux
2 from nmigen import Record, Memory
3 from nmigen import Const
4 from soc.fu.mmu.pipe_data import MMUInputData, MMUOutputData, MMUPipeSpec
5 from nmutil.singlepipe import ControlBase
6 from nmutil.util import rising_edge
7
8 from soc.experiment.mmu import MMU
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
15
16 from soc.experiment.mem_types import LoadStore1ToMMUType
17 from soc.experiment.mem_types import MMUToLoadStore1Type
18
19 from soc.fu.ldst.loadstore import LoadStore1, TestSRAMLoadStore1
20
21
22 class FSMMMUStage(ControlBase):
23 """FSM MMU
24
25 FSM-based MMU: must call set_ldst_interface and pass in an instance
26 of a LoadStore1. this to comply with the ConfigMemoryPortInterface API
27 """
28 def __init__(self, pspec):
29 super().__init__()
30 self.pspec = pspec
31
32 # set up p/n data
33 self.p.data_i = MMUInputData(pspec)
34 self.n.data_o = MMUOutputData(pspec)
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)
40
41 self.mmu = MMU()
42
43 # make life a bit easier in Core XXX mustn't really do this,
44 # pspec is designed for config variables, rather than passing
45 # things around. have to think about it, design a way to do
46 # it that makes "sense"
47 # comment out for now self.pspec.mmu = self.mmu
48 # comment out for now self.pspec.dcache = self.dcache
49
50 # debugging output for gtkw
51 self.debug0 = Signal(4)
52 self.illegal = Signal()
53
54 # for SPR field number access
55 i = self.p.data_i
56 self.fields = DecodeFields(SignalBitRange, [i.ctx.op.insn])
57 self.fields.create_specs()
58
59 def set_ldst_interface(self, ldst):
60 """must be called back in Core, after FUs have been set up.
61 one of those will be the MMU (us!) but the LoadStore1 instance
62 must be set up in ConfigMemoryPortInterface. sigh.
63 """
64 # incoming PortInterface
65 self.ldst = ldst
66 self.dcache = self.ldst.dcache
67 self.pi = self.ldst.pi
68
69 def elaborate(self, platform):
70 assert hasattr(self, "dcache"), "remember to call set_ldst_interface"
71 m = super().elaborate(platform)
72 comb = m.d.comb
73 dcache = self.dcache
74
75 # link mmu and dcache together
76 m.submodules.mmu = mmu = self.mmu
77 ldst = self.ldst # managed externally: do not add here
78 m.d.comb += dcache.m_in.eq(mmu.d_out) # MMUToDCacheType
79 m.d.comb += mmu.d_in.eq(dcache.m_out) # DCacheToMMUType
80
81 l_in, l_out = mmu.l_in, mmu.l_out
82 d_in, d_out = dcache.d_in, dcache.d_out
83 wb_out, wb_in = dcache.wb_out, dcache.wb_in
84
85 # link ldst and MMU together
86 comb += l_in.eq(ldst.l_in)
87 comb += ldst.l_out.eq(l_out)
88
89 data_i, data_o = self.p.data_i, self.n.data_o
90 a_i, b_i, o, spr1_o = data_i.ra, data_i.rb, data_o.o, data_o.spr1
91 op = data_i.ctx.op
92 msr_i = op.msr
93 spr1_i = data_i.spr1
94
95 # these are set / got here *ON BEHALF* of LoadStore1
96 dsisr, dar = ldst.dsisr, ldst.dar
97
98 # busy/done signals
99 busy = Signal()
100 done = Signal()
101 m.d.comb += self.n.valid_o.eq(busy & done)
102 m.d.comb += self.p.ready_o.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 # based on MSR bits, set priv and virt mode. TODO: 32-bit mode
110 comb += d_in.priv_mode.eq(~msr_i[MSR.PR])
111 comb += d_in.virt_mode.eq(msr_i[MSR.DR])
112 #comb += d_in.mode_32bit.eq(msr_i[MSR.SF]) # ?? err
113
114 # ok so we have to "pulse" the MMU (or dcache) rather than
115 # hold the valid hi permanently. guess what this does...
116 valid = Signal()
117 blip = Signal()
118 m.d.comb += blip.eq(rising_edge(m, valid))
119
120 with m.If(~busy):
121 with m.If(self.p.valid_i):
122 m.d.sync += busy.eq(1)
123 with m.Else():
124
125 # based on the Micro-Op, we work out which of MMU or DCache
126 # should "action" the operation. one of MMU or DCache gets
127 # enabled ("valid") and we twiddle our thumbs until it
128 # responds ("done").
129
130 # FIXME: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
131
132 with m.Switch(op.insn_type):
133 with m.Case(MicrOp.OP_MTSPR):
134 # despite redirection this FU **MUST** behave exactly
135 # like the SPR FU. this **INCLUDES** updating the SPR
136 # regfile because the CSV file entry for OP_MTSPR
137 # categorically defines and requires the expectation
138 # that the CompUnit **WILL** write to the regfile.
139 comb += spr1_o.data.eq(a_i)
140 comb += spr1_o.ok.eq(1)
141 # subset SPR: first check a few bits
142 # XXX NOTE this must now cover **FOUR** values: this
143 # test is no longer adequate. DSISR, DAR, PGTBL and PID
144 # must ALL be covered here.
145 with m.If(~spr[9] & ~spr[5]):
146 comb += self.debug0.eq(3)
147 #if matched update local cached value
148 with m.If(spr[0]):
149 sync += dsisr.eq(a_i[:32])
150 with m.Else():
151 sync += dar.eq(a_i)
152 comb += done.eq(1)
153 # pass it over to the MMU instead
154 with m.Else():
155 comb += self.debug0.eq(4)
156 # blip the MMU and wait for it to complete
157 comb += valid.eq(1) # start "pulse"
158 comb += l_in.valid.eq(blip) # start
159 comb += l_in.mtspr.eq(1) # mtspr mode
160 comb += l_in.sprn.eq(spr) # which SPR
161 comb += l_in.rs.eq(a_i) # incoming operand (RS)
162 comb += done.eq(1) # FIXME l_out.done
163
164 with m.Case(MicrOp.OP_MFSPR):
165 # subset SPR: first check a few bits
166 #with m.If(~spr[9] & ~spr[5]):
167 # comb += self.debug0.eq(5)
168 #with m.If(spr[0]):
169 # comb += o.data.eq(dsisr)
170 #with m.Else():
171 # comb += o.data.eq(dar)
172 #do NOT return cached values
173 comb += o.data.eq(spr1_i)
174 comb += o.ok.eq(1)
175 comb += done.eq(1)
176 # pass it over to the MMU instead
177 #with m.Else():
178 # comb += self.debug0.eq(6)
179 # # blip the MMU and wait for it to complete
180 # comb += valid.eq(1) # start "pulse"
181 # comb += l_in.valid.eq(blip) # start
182 # comb += l_in.mtspr.eq(0) # mfspr!=mtspr
183 # comb += l_in.sprn.eq(spr) # which SPR
184 # comb += l_in.rs.eq(a_i) # incoming operand (RS)
185 # comb += o.data.eq(l_out.sprval) # SPR from MMU
186 # comb += o.ok.eq(l_out.done) # only when l_out valid
187 # comb += done.eq(1) # FIXME l_out.done
188
189 # XXX this one is going to have to go through LDSTCompUnit
190 # because it's LDST that has control over dcache
191 # (through PortInterface). or, another means is devised
192 # so as not to have double-drivers of d_in.valid and addr
193 #
194 #with m.Case(MicrOp.OP_DCBZ):
195 # # activate dcbz mode (spec: v3.0B p850)
196 # comb += valid.eq(1) # start "pulse"
197 # comb += d_in.valid.eq(blip) # start
198 # comb += d_in.dcbz.eq(1) # dcbz mode
199 # comb += d_in.addr.eq(a_i + b_i) # addr is (RA|0) + RB
200 # comb += done.eq(d_out.store_done) # TODO
201 # comb += self.debug0.eq(1)
202
203 with m.Case(MicrOp.OP_TLBIE):
204 # pass TLBIE request to MMU (spec: v3.0B p1034)
205 # note that the spr is *not* an actual spr number, it's
206 # just that those bits happen to match with field bits
207 # RIC, PRS, R
208 comb += valid.eq(1) # start "pulse"
209 comb += l_in.valid.eq(blip) # start
210 comb += l_in.tlbie.eq(1) # mtspr mode
211 comb += l_in.sprn.eq(spr) # use sprn to send insn bits
212 comb += l_in.addr.eq(b_i) # incoming operand (RB)
213 comb += done.eq(l_out.done) # zzzz
214 comb += self.debug0.eq(2)
215
216 with m.Case(MicrOp.OP_ILLEGAL):
217 comb += self.illegal.eq(1)
218
219 with m.If(self.n.ready_i & self.n.valid_o):
220 m.d.sync += busy.eq(0)
221
222 return m
223
224 def __iter__(self):
225 yield from self.p
226 yield from self.n
227
228 def ports(self):
229 return list(self)