2 Based on microwatt mmu.vhdl
4 * https://bugs.libre-soc.org/show_bug.cgi?id=491
5 * https://bugs.libre-soc.org/show_bug.cgi?id=450
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
15 from soc
.experiment
.mmu
import MMU
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
23 from soc
.experiment
.mem_types
import LoadStore1ToMMUType
24 from soc
.experiment
.mem_types
import MMUToLoadStore1Type
26 from soc
.fu
.ldst
.loadstore
import LoadStore1
, TestSRAMLoadStore1
27 from nmutil
.util
import Display
30 class FSMMMUStage(ControlBase
):
33 FSM-based MMU: must call set_ldst_interface and pass in an instance
34 of a LoadStore1. this to comply with the ConfigMemoryPortInterface API
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). sorted: call set_ldst_interface
41 def __init__(self
, pspec
):
46 self
.p
.i_data
= MMUInputData(pspec
)
47 self
.n
.o_data
= MMUOutputData(pspec
)
48 self
.exc_o
= self
.n
.o_data
.exception
# AllFunctionUnits needs this
52 # debugging output for gtkw
53 self
.debug0
= Signal(4)
54 self
.illegal
= Signal()
56 # for SPR field number access
58 self
.fields
= DecodeFields(SignalBitRange
, [i
.ctx
.op
.insn
])
59 self
.fields
.create_specs()
61 def set_ldst_interface(self
, ldst
):
62 """must be called back in Core, after FUs have been set up.
63 one of those will be the MMU (us!) but the LoadStore1 instance
64 must be set up in ConfigMemoryPortInterface. sigh.
66 # incoming PortInterface
68 self
.dcache
= self
.ldst
.dcache
69 self
.icache
= self
.ldst
.icache
70 self
.pi
= self
.ldst
.pi
72 def elaborate(self
, platform
):
73 assert hasattr(self
, "dcache"), "remember to call set_ldst_interface"
74 m
= super().elaborate(platform
)
75 comb
, sync
= m
.d
.comb
, m
.d
.sync
78 # link mmu and dcache together
79 m
.submodules
.mmu
= mmu
= self
.mmu
80 ldst
= self
.ldst
# managed externally: do not add here
81 m
.d
.comb
+= dcache
.m_in
.eq(mmu
.d_out
) # MMUToDCacheType
82 m
.d
.comb
+= mmu
.d_in
.eq(dcache
.m_out
) # DCacheToMMUType
84 l_in
, l_out
= mmu
.l_in
, mmu
.l_out
85 d_in
, d_out
= dcache
.d_in
, dcache
.d_out
87 # link ldst and MMU together
88 comb
+= l_in
.eq(ldst
.m_out
)
89 comb
+= ldst
.m_in
.eq(l_out
)
91 i_data
, o_data
= self
.p
.i_data
, self
.n
.o_data
94 a_i
, b_i
, spr1_i
= i_data
.ra
, i_data
.rb
, i_data
.spr1
95 o
, exc_o
, spr1_o
= o_data
.o
, o_data
.exception
, o_data
.spr1
98 busy
= Signal(name
="mmu_fsm_busy")
99 done
= Signal(name
="mmu_fsm_done")
100 m
.d
.comb
+= self
.n
.o_valid
.eq(busy
& done
)
101 m
.d
.comb
+= self
.p
.o_ready
.eq(~busy
)
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
))
108 # ok so we have to "pulse" the MMU (or dcache) rather than
109 # hold the valid hi permanently. guess what this does...
112 m
.d
.comb
+= blip
.eq(rising_edge(m
, valid
))
115 with m
.If(self
.p
.i_valid
):
119 # based on the Micro-Op, we work out which of MMU or DCache
120 # should "action" the operation. one of MMU or DCache gets
121 # enabled ("valid") and we twiddle our thumbs until it
124 # WIP: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
126 with m
.Switch(op
.insn_type
):
132 with m
.Case(MicrOp
.OP_MTSPR
):
133 comb
+= Display("MMUTEST: OP_MTSPR: spr=%i", spr
)
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 might not be 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 #commented out because there is a driver conflict
149 comb
+= ldst
.sprval_in
.eq(a_i
)
150 comb
+= ldst
.mmu_set_spr
.eq(1)
152 comb
+= ldst
.mmu_set_dar
.eq(1)
154 comb
+= ldst
.mmu_set_dsisr
.eq(1)
156 # pass it over to the MMU instead
159 comb
+= self
.debug0
.eq(4)
160 # blip the MMU and wait for it to complete
161 comb
+= valid
.eq(1) # start "pulse"
162 comb
+= l_in
.valid
.eq(blip
) # start
163 comb
+= l_in
.mtspr
.eq(1) # mtspr mode
164 comb
+= l_in
.sprn
.eq(spr
) # which SPR
165 comb
+= l_in
.rs
.eq(a_i
) # incoming operand (RS)
166 comb
+= done
.eq(1) # FIXME l_out.done
172 with m
.Case(MicrOp
.OP_MFSPR
):
173 comb
+= Display("MMUTEST: OP_MFSPR: spr=%i returns=%i",
175 # partial SPR number decoding perfectly fine
176 with m
.If(spr
[9] | spr
[5]):
177 # identified as an MMU OP_MFSPR, contact the MMU.
178 # interestingly, the read is combinatorial: no need
179 # to set "valid", just set the SPR number
180 comb
+= l_in
.sprn
.eq(spr
) # which SPR
181 comb
+= o
.data
.eq(l_out
.sprval
)
183 # identified as DSISR or DAR. again: read the SPR
184 # directly, combinatorial access
186 comb
+= o
.data
.eq(ldst
.dar
)
188 comb
+= o
.data
.eq(ldst
.dsisr
)
197 with m
.Case(MicrOp
.OP_TLBIE
):
198 comb
+= Display("MMUTEST: OP_TLBIE: insn_bits=%i", spr
)
199 # pass TLBIE request to MMU (spec: v3.0B p1034)
200 # note that the spr is *not* an actual spr number, it's
201 # just that those bits happen to match with field bits
203 comb
+= Display("TLBIE: %i %i", spr
, l_out
.done
)
204 comb
+= valid
.eq(1) # start "pulse"
205 comb
+= l_in
.valid
.eq(blip
) # start
206 comb
+= l_in
.tlbie
.eq(1) # mtspr mode
207 comb
+= l_in
.sprn
.eq(spr
) # use sprn to send insn bits
208 comb
+= l_in
.addr
.eq(b_i
) # incoming operand (RB)
209 comb
+= done
.eq(l_out
.done
) # zzzz
210 comb
+= self
.debug0
.eq(2)
216 with m
.Case(MicrOp
.OP_FETCH_FAILED
):
217 comb
+= Display("MMUTEST: OP_FETCH_FAILED: @%x", nia_i
)
218 # trigger an instruction fetch failed MMU event.
219 # PowerDecoder2 drops svstate.pc into NIA for us
220 # really, this should be direct communication with the
221 # MMU, rather than going through LoadStore1. but, doing
222 # so allows for the opportunity to prevent LoadStore1
223 # from accepting any other LD/ST requests.
224 comb
+= valid
.eq(1) # start "pulse"
225 comb
+= ldst
.instr_fault
.eq(blip
)
226 comb
+= ldst
.maddr
.eq(nia_i
)
227 comb
+= done
.eq(mmu
.d_in
.done
) # XXX should not access this!
228 comb
+= self
.debug0
.eq(3)
229 # LDST unit contains exception data, which (messily)
230 # is copied over, here. not ideal but it will do for now
231 comb
+= exc_o
.eq(ldst
.pi
.exc_o
)
237 with m
.Case(MicrOp
.OP_ILLEGAL
):
238 comb
+= self
.illegal
.eq(1)
240 with m
.If(self
.n
.i_ready
& self
.n
.o_valid
):