code-comments
[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 class FSMMMUStage(ControlBase):
30 """FSM MMU
31
32 FSM-based MMU: must call set_ldst_interface and pass in an instance
33 of a LoadStore1. this to comply with the ConfigMemoryPortInterface API
34
35 this Function Unit is extremely unusual in that it actually stores a
36 "thing" rather than "processes inputs and produces outputs". hence
37 why it has to be a FSM. linking up LD/ST however is going to have
38 to be done back in Issuer (or Core). sorted: call set_ldst_interface
39 """
40 def __init__(self, pspec):
41 super().__init__()
42 self.pspec = pspec
43
44 # set up p/n data
45 self.p.i_data = MMUInputData(pspec)
46 self.n.o_data = MMUOutputData(pspec)
47
48 self.mmu = MMU()
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.i_data
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, sync = m.d.comb, m.d.sync
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
84 # link ldst and MMU together
85 comb += l_in.eq(ldst.m_out)
86 comb += ldst.m_in.eq(l_out)
87
88 i_data, o_data = self.p.i_data, self.n.o_data
89 a_i, b_i, o, spr1_o = i_data.ra, i_data.rb, o_data.o, o_data.spr1
90 op = i_data.ctx.op
91 spr1_i = i_data.spr1
92
93 # busy/done signals
94 busy = Signal()
95 done = Signal()
96 m.d.comb += self.n.o_valid.eq(busy & done)
97 m.d.comb += self.p.o_ready.eq(~busy)
98
99 # take copy of X-Form SPR field
100 x_fields = self.fields.FormXFX
101 spr = Signal(len(x_fields.SPR))
102 comb += spr.eq(decode_spr_num(x_fields.SPR))
103
104 # ok so we have to "pulse" the MMU (or dcache) rather than
105 # hold the valid hi permanently. guess what this does...
106 valid = Signal()
107 blip = Signal()
108 m.d.comb += blip.eq(rising_edge(m, valid))
109
110 with m.If(~busy):
111 with m.If(self.p.i_valid):
112 sync += busy.eq(1)
113 with m.Else():
114
115 # based on the Micro-Op, we work out which of MMU or DCache
116 # should "action" the operation. one of MMU or DCache gets
117 # enabled ("valid") and we twiddle our thumbs until it
118 # responds ("done").
119
120 # WIP: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
121
122 with m.Switch(op.insn_type):
123
124 ##########
125 # OP_MTSPR
126 ##########
127
128 with m.Case(MicrOp.OP_MTSPR):
129 comb += Display("MMUTEST: OP_MTSPR: spr=%i", spr)
130 # despite redirection this FU **MUST** behave exactly
131 # like the SPR FU. this **INCLUDES** updating the SPR
132 # regfile because the CSV file entry for OP_MTSPR
133 # categorically defines and requires the expectation
134 # that the CompUnit **WILL** write to the regfile.
135 comb += spr1_o.data.eq(a_i)
136 comb += spr1_o.ok.eq(1)
137 # subset SPR: first check a few bits
138 # XXX NOTE this must now cover **FOUR** values: this
139 # test might not be adequate. DSISR, DAR, PGTBL and PID
140 # must ALL be covered here.
141 with m.If(~spr[9] & ~spr[5]):
142 comb += self.debug0.eq(3)
143 #if matched update local cached value
144 #commented out because there is a driver conflict
145 comb += ldst.sprval_in.eq(a_i)
146 comb += ldst.mmu_set_spr.eq(1)
147 with m.If(spr[0]):
148 comb += ldst.mmu_set_dar.eq(1)
149 with m.Else():
150 comb += ldst.mmu_set_dsisr.eq(1)
151 comb += done.eq(1)
152 # pass it over to the MMU instead
153 with m.Else():
154 # PGTBL and PID
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 ##########
165 # OP_MFSPR
166 ##########
167
168 with m.Case(MicrOp.OP_MFSPR):
169 comb += Display("MMUTEST: OP_MFSPR: spr=%i returns=%i",
170 spr, spr1_i)
171 # partial SPR number decoding perfectly fine
172 with m.If(spr[9] | spr[5]):
173 # identified as an MMU OP_MFSPR, contact the MMU.
174 # interestingly, the read is combinatorial: no need
175 # to set "valid", just set the SPR number
176 comb += l_in.sprn.eq(spr) # which SPR
177 comb += o.data.eq(l_out.sprval)
178 with m.Else():
179 # identified as DSISR or DAR. again: read the SPR
180 # directly, combinatorial access
181 with m.If(spr[0]):
182 comb += o.data.eq(ldst.dar)
183 with m.Else():
184 comb += o.data.eq(ldst.dsisr)
185
186 comb += o.ok.eq(1)
187 comb += done.eq(1)
188
189 ##########
190 # OP_TLBIE
191 ##########
192
193 with m.Case(MicrOp.OP_TLBIE):
194 comb += Display("MMUTEST: OP_TLBIE: insn_bits=%i", spr)
195 # pass TLBIE request to MMU (spec: v3.0B p1034)
196 # note that the spr is *not* an actual spr number, it's
197 # just that those bits happen to match with field bits
198 # RIC, PRS, R
199 comb += Display("TLBIE: %i %i", spr, l_out.done)
200 comb += valid.eq(1) # start "pulse"
201 comb += l_in.valid.eq(blip) # start
202 comb += l_in.tlbie.eq(1) # mtspr mode
203 comb += l_in.sprn.eq(spr) # use sprn to send insn bits
204 comb += l_in.addr.eq(b_i) # incoming operand (RB)
205 comb += done.eq(l_out.done) # zzzz
206 comb += self.debug0.eq(2)
207
208 ############
209 # OP_ILLEGAL
210 ############
211
212 with m.Case(MicrOp.OP_ILLEGAL):
213 comb += self.illegal.eq(1)
214
215 with m.If(self.n.i_ready & self.n.o_valid):
216 sync += busy.eq(0)
217
218 return m
219
220 def __iter__(self):
221 yield from self.p
222 yield from self.n
223
224 def ports(self):
225 return list(self)