preference is to create a temp variable for comb and sync and use that
[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 """
6
7 from nmigen import Elaboratable, Module, Signal, Shape, unsigned, Cat, Mux
8 from nmigen import Record, Memory
9 from nmigen import Const
10 from soc.fu.mmu.pipe_data import MMUInputData, MMUOutputData, MMUPipeSpec
11 from nmutil.singlepipe import ControlBase
12 from nmutil.util import rising_edge
13
14 from soc.experiment.mmu import MMU
15
16 from openpower.consts import MSR
17 from openpower.decoder.power_fields import DecodeFields
18 from openpower.decoder.power_fieldsn import SignalBitRange
19 from openpower.decoder.power_decoder2 import decode_spr_num
20 from openpower.decoder.power_enums import MicrOp
21
22 from soc.experiment.mem_types import LoadStore1ToMMUType
23 from soc.experiment.mem_types import MMUToLoadStore1Type
24
25 from soc.fu.ldst.loadstore import LoadStore1, TestSRAMLoadStore1
26
27
28 class FSMMMUStage(ControlBase):
29 """FSM MMU
30
31 FSM-based MMU: must call set_ldst_interface and pass in an instance
32 of a LoadStore1. this to comply with the ConfigMemoryPortInterface API
33
34 this Function Unit is extremely unusual in that it actually stores a
35 "thing" rather than "processes inputs and produces outputs". hence
36 why it has to be a FSM. linking up LD/ST however is going to have
37 to be done back in Issuer (or Core). sorted: call set_ldst_interface
38 """
39 def __init__(self, pspec):
40 super().__init__()
41 self.pspec = pspec
42
43 # set up p/n data
44 self.p.data_i = MMUInputData(pspec)
45 self.n.data_o = MMUOutputData(pspec)
46
47 self.mmu = MMU()
48
49 # debugging output for gtkw
50 self.debug0 = Signal(4)
51 self.illegal = Signal()
52
53 # for SPR field number access
54 i = self.p.data_i
55 self.fields = DecodeFields(SignalBitRange, [i.ctx.op.insn])
56 self.fields.create_specs()
57
58 def set_ldst_interface(self, ldst):
59 """must be called back in Core, after FUs have been set up.
60 one of those will be the MMU (us!) but the LoadStore1 instance
61 must be set up in ConfigMemoryPortInterface. sigh.
62 """
63 # incoming PortInterface
64 self.ldst = ldst
65 self.dcache = self.ldst.dcache
66 self.pi = self.ldst.pi
67
68 def elaborate(self, platform):
69 assert hasattr(self, "dcache"), "remember to call set_ldst_interface"
70 m = super().elaborate(platform)
71 comb, sync = m.d.comb, m.d.sync
72 dcache = self.dcache
73
74 # link mmu and dcache together
75 m.submodules.mmu = mmu = self.mmu
76 ldst = self.ldst # managed externally: do not add here
77 m.d.comb += dcache.m_in.eq(mmu.d_out) # MMUToDCacheType
78 m.d.comb += mmu.d_in.eq(dcache.m_out) # DCacheToMMUType
79
80 l_in, l_out = mmu.l_in, mmu.l_out
81 d_in, d_out = dcache.d_in, dcache.d_out
82 wb_out, wb_in = dcache.wb_out, dcache.wb_in
83
84 # link ldst and MMU together
85 comb += l_in.eq(ldst.l_in)
86 comb += ldst.l_out.eq(l_out)
87
88 data_i, data_o = self.p.data_i, self.n.data_o
89 a_i, b_i, o, spr1_o = data_i.ra, data_i.rb, data_o.o, data_o.spr1
90 op = data_i.ctx.op
91 msr_i = op.msr
92 spr1_i = data_i.spr1
93
94 # these are set / got here *ON BEHALF* of LoadStore1
95 dsisr, dar = ldst.dsisr, ldst.dar
96
97 # busy/done signals
98 busy = Signal()
99 done = Signal()
100 m.d.comb += self.n.valid_o.eq(busy & done)
101 m.d.comb += self.p.ready_o.eq(~busy)
102
103 # take copy of X-Form SPR field
104 x_fields = self.fields.FormXFX
105 spr = Signal(len(x_fields.SPR))
106 comb += spr.eq(decode_spr_num(x_fields.SPR))
107
108 # based on MSR bits, set priv and virt mode. TODO: 32-bit mode
109 comb += d_in.priv_mode.eq(~msr_i[MSR.PR])
110 comb += d_in.virt_mode.eq(msr_i[MSR.DR])
111 #comb += d_in.mode_32bit.eq(msr_i[MSR.SF]) # ?? err
112
113 # ok so we have to "pulse" the MMU (or dcache) rather than
114 # hold the valid hi permanently. guess what this does...
115 valid = Signal()
116 blip = Signal()
117 m.d.comb += blip.eq(rising_edge(m, valid))
118
119 with m.If(~busy):
120 with m.If(self.p.valid_i):
121 sync += busy.eq(1)
122 with m.Else():
123
124 # based on the Micro-Op, we work out which of MMU or DCache
125 # should "action" the operation. one of MMU or DCache gets
126 # enabled ("valid") and we twiddle our thumbs until it
127 # responds ("done").
128
129 # FIXME: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
130
131 with m.Switch(op.insn_type):
132 with m.Case(MicrOp.OP_MTSPR):
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 with m.If(spr[0]):
148 sync += dsisr.eq(a_i[:32])
149 with m.Else():
150 sync += dar.eq(a_i)
151 comb += done.eq(1)
152 # pass it over to the MMU instead
153 with m.Else():
154 comb += self.debug0.eq(4)
155 # blip the MMU and wait for it to complete
156 comb += valid.eq(1) # start "pulse"
157 comb += l_in.valid.eq(blip) # start
158 comb += l_in.mtspr.eq(1) # mtspr mode
159 comb += l_in.sprn.eq(spr) # which SPR
160 comb += l_in.rs.eq(a_i) # incoming operand (RS)
161 comb += done.eq(1) # FIXME l_out.done
162
163 with m.Case(MicrOp.OP_MFSPR):
164 # subset SPR: first check a few bits
165 #with m.If(~spr[9] & ~spr[5]):
166 # comb += self.debug0.eq(5)
167 #with m.If(spr[0]):
168 # comb += o.data.eq(dsisr)
169 #with m.Else():
170 # comb += o.data.eq(dar)
171 #do NOT return cached values
172 comb += o.data.eq(spr1_i)
173 comb += o.ok.eq(1)
174 comb += done.eq(1)
175 # pass it over to the MMU instead
176 #with m.Else():
177 # comb += self.debug0.eq(6)
178 # # blip the MMU and wait for it to complete
179 # comb += valid.eq(1) # start "pulse"
180 # comb += l_in.valid.eq(blip) # start
181 # comb += l_in.mtspr.eq(0) # mfspr!=mtspr
182 # comb += l_in.sprn.eq(spr) # which SPR
183 # comb += l_in.rs.eq(a_i) # incoming operand (RS)
184 # comb += o.data.eq(l_out.sprval) # SPR from MMU
185 # comb += o.ok.eq(l_out.done) # only when l_out valid
186 # comb += done.eq(1) # FIXME l_out.done
187
188 # XXX this one is going to have to go through LDSTCompUnit
189 # because it's LDST that has control over dcache
190 # (through PortInterface). or, another means is devised
191 # so as not to have double-drivers of d_in.valid and addr
192 #
193 #with m.Case(MicrOp.OP_DCBZ):
194 # # activate dcbz mode (spec: v3.0B p850)
195 # comb += valid.eq(1) # start "pulse"
196 # comb += d_in.valid.eq(blip) # start
197 # comb += d_in.dcbz.eq(1) # dcbz mode
198 # comb += d_in.addr.eq(a_i + b_i) # addr is (RA|0) + RB
199 # comb += done.eq(d_out.store_done) # TODO
200 # comb += self.debug0.eq(1)
201
202 with m.Case(MicrOp.OP_TLBIE):
203 # pass TLBIE request to MMU (spec: v3.0B p1034)
204 # note that the spr is *not* an actual spr number, it's
205 # just that those bits happen to match with field bits
206 # RIC, PRS, R
207 comb += valid.eq(1) # start "pulse"
208 comb += l_in.valid.eq(blip) # start
209 comb += l_in.tlbie.eq(1) # mtspr mode
210 comb += l_in.sprn.eq(spr) # use sprn to send insn bits
211 comb += l_in.addr.eq(b_i) # incoming operand (RB)
212 comb += done.eq(l_out.done) # zzzz
213 comb += self.debug0.eq(2)
214
215 with m.Case(MicrOp.OP_ILLEGAL):
216 comb += self.illegal.eq(1)
217
218 with m.If(self.n.ready_i & self.n.valid_o):
219 sync += busy.eq(0)
220
221 return m
222
223 def __iter__(self):
224 yield from self.p
225 yield from self.n
226
227 def ports(self):
228 return list(self)