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
8 from soc
.experiment
.mmu
import MMU
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
16 from soc
.experiment
.mem_types
import LoadStore1ToMMUType
17 from soc
.experiment
.mem_types
import MMUToLoadStore1Type
19 from soc
.fu
.ldst
.loadstore
import LoadStore1
, TestSRAMLoadStore1
22 class FSMMMUStage(ControlBase
):
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
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
33 def __init__(self
, pspec
):
38 self
.p
.data_i
= MMUInputData(pspec
)
39 self
.n
.data_o
= MMUOutputData(pspec
)
43 # debugging output for gtkw
44 self
.debug0
= Signal(4)
45 self
.illegal
= Signal()
47 # for SPR field number access
49 self
.fields
= DecodeFields(SignalBitRange
, [i
.ctx
.op
.insn
])
50 self
.fields
.create_specs()
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.
57 # incoming PortInterface
59 self
.dcache
= self
.ldst
.dcache
60 self
.pi
= self
.ldst
.pi
62 def elaborate(self
, platform
):
63 assert hasattr(self
, "dcache"), "remember to call set_ldst_interface"
64 m
= super().elaborate(platform
)
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
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
78 # link ldst and MMU together
79 comb
+= l_in
.eq(ldst
.l_in
)
80 comb
+= ldst
.l_out
.eq(l_out
)
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
88 # these are set / got here *ON BEHALF* of LoadStore1
89 dsisr
, dar
= ldst
.dsisr
, ldst
.dar
94 m
.d
.comb
+= self
.n
.valid_o
.eq(busy
& done
)
95 m
.d
.comb
+= self
.p
.ready_o
.eq(~busy
)
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
))
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
107 # ok so we have to "pulse" the MMU (or dcache) rather than
108 # hold the valid hi permanently. guess what this does...
111 m
.d
.comb
+= blip
.eq(rising_edge(m
, valid
))
114 with m
.If(self
.p
.valid_i
):
115 m
.d
.sync
+= busy
.eq(1)
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
123 # FIXME: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
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
142 sync
+= dsisr
.eq(a_i
[:32])
146 # pass it over to the MMU instead
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
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)
162 # comb += o.data.eq(dsisr)
164 # comb += o.data.eq(dar)
165 #do NOT return cached values
166 comb
+= o
.data
.eq(spr1_i
)
169 # pass it over to the MMU instead
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
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
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)
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
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)
209 with m
.Case(MicrOp
.OP_ILLEGAL
):
210 comb
+= self
.illegal
.eq(1)
212 with m
.If(self
.n
.ready_i
& self
.n
.valid_o
):
213 m
.d
.sync
+= busy
.eq(0)