update comments and docstrings
[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 this Function Unit is extremely unusual in that it actually stores a
29 "thing" rather than "processes inputs and produces outputs". hence
30 why it has to be a FSM. linking up LD/ST however is going to have
31 to be done back in Issuer (or Core). sorted: call set_ldst_interface
32 """
33 def __init__(self, pspec):
34 super().__init__()
35 self.pspec = pspec
36
37 # set up p/n data
38 self.p.data_i = MMUInputData(pspec)
39 self.n.data_o = MMUOutputData(pspec)
40
41 self.mmu = MMU()
42
43 # debugging output for gtkw
44 self.debug0 = Signal(4)
45 self.illegal = Signal()
46
47 # for SPR field number access
48 i = self.p.data_i
49 self.fields = DecodeFields(SignalBitRange, [i.ctx.op.insn])
50 self.fields.create_specs()
51
52 def set_ldst_interface(self, ldst):
53 """must be called back in Core, after FUs have been set up.
54 one of those will be the MMU (us!) but the LoadStore1 instance
55 must be set up in ConfigMemoryPortInterface. sigh.
56 """
57 # incoming PortInterface
58 self.ldst = ldst
59 self.dcache = self.ldst.dcache
60 self.pi = self.ldst.pi
61
62 def elaborate(self, platform):
63 assert hasattr(self, "dcache"), "remember to call set_ldst_interface"
64 m = super().elaborate(platform)
65 comb = m.d.comb
66 dcache = self.dcache
67
68 # link mmu and dcache together
69 m.submodules.mmu = mmu = self.mmu
70 ldst = self.ldst # managed externally: do not add here
71 m.d.comb += dcache.m_in.eq(mmu.d_out) # MMUToDCacheType
72 m.d.comb += mmu.d_in.eq(dcache.m_out) # DCacheToMMUType
73
74 l_in, l_out = mmu.l_in, mmu.l_out
75 d_in, d_out = dcache.d_in, dcache.d_out
76 wb_out, wb_in = dcache.wb_out, dcache.wb_in
77
78 # link ldst and MMU together
79 comb += l_in.eq(ldst.l_in)
80 comb += ldst.l_out.eq(l_out)
81
82 data_i, data_o = self.p.data_i, self.n.data_o
83 a_i, b_i, o, spr1_o = data_i.ra, data_i.rb, data_o.o, data_o.spr1
84 op = data_i.ctx.op
85 msr_i = op.msr
86 spr1_i = data_i.spr1
87
88 # these are set / got here *ON BEHALF* of LoadStore1
89 dsisr, dar = ldst.dsisr, ldst.dar
90
91 # busy/done signals
92 busy = Signal()
93 done = Signal()
94 m.d.comb += self.n.valid_o.eq(busy & done)
95 m.d.comb += self.p.ready_o.eq(~busy)
96
97 # take copy of X-Form SPR field
98 x_fields = self.fields.FormXFX
99 spr = Signal(len(x_fields.SPR))
100 comb += spr.eq(decode_spr_num(x_fields.SPR))
101
102 # based on MSR bits, set priv and virt mode. TODO: 32-bit mode
103 comb += d_in.priv_mode.eq(~msr_i[MSR.PR])
104 comb += d_in.virt_mode.eq(msr_i[MSR.DR])
105 #comb += d_in.mode_32bit.eq(msr_i[MSR.SF]) # ?? err
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.valid_i):
115 m.d.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 # FIXME: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
124
125 with m.Switch(op.insn_type):
126 with m.Case(MicrOp.OP_MTSPR):
127 # despite redirection this FU **MUST** behave exactly
128 # like the SPR FU. this **INCLUDES** updating the SPR
129 # regfile because the CSV file entry for OP_MTSPR
130 # categorically defines and requires the expectation
131 # that the CompUnit **WILL** write to the regfile.
132 comb += spr1_o.data.eq(a_i)
133 comb += spr1_o.ok.eq(1)
134 # subset SPR: first check a few bits
135 # XXX NOTE this must now cover **FOUR** values: this
136 # test might not be adequate. DSISR, DAR, PGTBL and PID
137 # must ALL be covered here.
138 with m.If(~spr[9] & ~spr[5]):
139 comb += self.debug0.eq(3)
140 #if matched update local cached value
141 with m.If(spr[0]):
142 sync += dsisr.eq(a_i[:32])
143 with m.Else():
144 sync += dar.eq(a_i)
145 comb += done.eq(1)
146 # pass it over to the MMU instead
147 with m.Else():
148 comb += self.debug0.eq(4)
149 # blip the MMU and wait for it to complete
150 comb += valid.eq(1) # start "pulse"
151 comb += l_in.valid.eq(blip) # start
152 comb += l_in.mtspr.eq(1) # mtspr mode
153 comb += l_in.sprn.eq(spr) # which SPR
154 comb += l_in.rs.eq(a_i) # incoming operand (RS)
155 comb += done.eq(1) # FIXME l_out.done
156
157 with m.Case(MicrOp.OP_MFSPR):
158 # subset SPR: first check a few bits
159 #with m.If(~spr[9] & ~spr[5]):
160 # comb += self.debug0.eq(5)
161 #with m.If(spr[0]):
162 # comb += o.data.eq(dsisr)
163 #with m.Else():
164 # comb += o.data.eq(dar)
165 #do NOT return cached values
166 comb += o.data.eq(spr1_i)
167 comb += o.ok.eq(1)
168 comb += done.eq(1)
169 # pass it over to the MMU instead
170 #with m.Else():
171 # comb += self.debug0.eq(6)
172 # # blip the MMU and wait for it to complete
173 # comb += valid.eq(1) # start "pulse"
174 # comb += l_in.valid.eq(blip) # start
175 # comb += l_in.mtspr.eq(0) # mfspr!=mtspr
176 # comb += l_in.sprn.eq(spr) # which SPR
177 # comb += l_in.rs.eq(a_i) # incoming operand (RS)
178 # comb += o.data.eq(l_out.sprval) # SPR from MMU
179 # comb += o.ok.eq(l_out.done) # only when l_out valid
180 # comb += done.eq(1) # FIXME l_out.done
181
182 # XXX this one is going to have to go through LDSTCompUnit
183 # because it's LDST that has control over dcache
184 # (through PortInterface). or, another means is devised
185 # so as not to have double-drivers of d_in.valid and addr
186 #
187 #with m.Case(MicrOp.OP_DCBZ):
188 # # activate dcbz mode (spec: v3.0B p850)
189 # comb += valid.eq(1) # start "pulse"
190 # comb += d_in.valid.eq(blip) # start
191 # comb += d_in.dcbz.eq(1) # dcbz mode
192 # comb += d_in.addr.eq(a_i + b_i) # addr is (RA|0) + RB
193 # comb += done.eq(d_out.store_done) # TODO
194 # comb += self.debug0.eq(1)
195
196 with m.Case(MicrOp.OP_TLBIE):
197 # pass TLBIE request to MMU (spec: v3.0B p1034)
198 # note that the spr is *not* an actual spr number, it's
199 # just that those bits happen to match with field bits
200 # RIC, PRS, R
201 comb += valid.eq(1) # start "pulse"
202 comb += l_in.valid.eq(blip) # start
203 comb += l_in.tlbie.eq(1) # mtspr mode
204 comb += l_in.sprn.eq(spr) # use sprn to send insn bits
205 comb += l_in.addr.eq(b_i) # incoming operand (RB)
206 comb += done.eq(l_out.done) # zzzz
207 comb += self.debug0.eq(2)
208
209 with m.Case(MicrOp.OP_ILLEGAL):
210 comb += self.illegal.eq(1)
211
212 with m.If(self.n.ready_i & self.n.valid_o):
213 m.d.sync += busy.eq(0)
214
215 return m
216
217 def __iter__(self):
218 yield from self.p
219 yield from self.n
220
221 def ports(self):
222 return list(self)