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
)
51 # debugging output for gtkw
52 self
.debug0
= Signal(4)
53 self
.illegal
= Signal()
55 # for SPR field number access
57 self
.fields
= DecodeFields(SignalBitRange
, [i
.ctx
.op
.insn
])
58 self
.fields
.create_specs()
60 def set_ldst_interface(self
, ldst
):
61 """must be called back in Core, after FUs have been set up.
62 one of those will be the MMU (us!) but the LoadStore1 instance
63 must be set up in ConfigMemoryPortInterface. sigh.
65 # incoming PortInterface
67 self
.dcache
= self
.ldst
.dcache
68 self
.icache
= self
.ldst
.icache
69 self
.pi
= self
.ldst
.pi
71 def elaborate(self
, platform
):
72 assert hasattr(self
, "dcache"), "remember to call set_ldst_interface"
73 m
= super().elaborate(platform
)
74 comb
, sync
= m
.d
.comb
, m
.d
.sync
77 # link mmu and dcache together
78 m
.submodules
.mmu
= mmu
= self
.mmu
79 ldst
= self
.ldst
# managed externally: do not add here
80 m
.d
.comb
+= dcache
.m_in
.eq(mmu
.d_out
) # MMUToDCacheType
81 m
.d
.comb
+= mmu
.d_in
.eq(dcache
.m_out
) # DCacheToMMUType
83 l_in
, l_out
= mmu
.l_in
, mmu
.l_out
84 d_in
, d_out
= dcache
.d_in
, dcache
.d_out
86 # link ldst and MMU together
87 comb
+= l_in
.eq(ldst
.m_out
)
88 comb
+= ldst
.m_in
.eq(l_out
)
90 i_data
, o_data
= self
.p
.i_data
, self
.n
.o_data
93 a_i
, b_i
, spr1_i
= i_data
.ra
, i_data
.rb
, i_data
.spr1
94 o
, exc_o
, spr1_o
= o_data
.o
, o_data
.exception
, o_data
.spr1
97 busy
= Signal(name
="mmu_fsm_busy")
98 done
= Signal(name
="mmu_fsm_done")
99 m
.d
.comb
+= self
.n
.o_valid
.eq(busy
& done
)
100 m
.d
.comb
+= self
.p
.o_ready
.eq(~busy
)
102 # take copy of X-Form SPR field
103 x_fields
= self
.fields
.FormXFX
104 spr
= Signal(len(x_fields
.SPR
))
105 comb
+= spr
.eq(decode_spr_num(x_fields
.SPR
))
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
.i_valid
):
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 # WIP: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
125 with m
.Switch(op
.insn_type
):
131 with m
.Case(MicrOp
.OP_MTSPR
):
132 comb
+= Display("MMUTEST: OP_MTSPR: spr=%i", spr
)
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 #commented out because there is a driver conflict
148 comb
+= ldst
.sprval_in
.eq(a_i
)
149 comb
+= ldst
.mmu_set_spr
.eq(1)
151 comb
+= ldst
.mmu_set_dar
.eq(1)
153 comb
+= ldst
.mmu_set_dsisr
.eq(1)
155 # pass it over to the MMU instead
158 comb
+= self
.debug0
.eq(4)
159 # blip the MMU and wait for it to complete
160 comb
+= valid
.eq(1) # start "pulse"
161 comb
+= l_in
.valid
.eq(blip
) # start
162 comb
+= l_in
.mtspr
.eq(1) # mtspr mode
163 comb
+= l_in
.sprn
.eq(spr
) # which SPR
164 comb
+= l_in
.rs
.eq(a_i
) # incoming operand (RS)
165 comb
+= done
.eq(1) # FIXME l_out.done
171 with m
.Case(MicrOp
.OP_MFSPR
):
172 comb
+= Display("MMUTEST: OP_MFSPR: spr=%i returns=%i",
174 # partial SPR number decoding perfectly fine
175 with m
.If(spr
[9] | spr
[5]):
176 # identified as an MMU OP_MFSPR, contact the MMU.
177 # interestingly, the read is combinatorial: no need
178 # to set "valid", just set the SPR number
179 comb
+= l_in
.sprn
.eq(spr
) # which SPR
180 comb
+= o
.data
.eq(l_out
.sprval
)
182 # identified as DSISR or DAR. again: read the SPR
183 # directly, combinatorial access
185 comb
+= o
.data
.eq(ldst
.dar
)
187 comb
+= o
.data
.eq(ldst
.dsisr
)
196 with m
.Case(MicrOp
.OP_TLBIE
):
197 comb
+= Display("MMUTEST: OP_TLBIE: insn_bits=%i", spr
)
198 # pass TLBIE request to MMU (spec: v3.0B p1034)
199 # note that the spr is *not* an actual spr number, it's
200 # just that those bits happen to match with field bits
202 comb
+= Display("TLBIE: %i %i", spr
, l_out
.done
)
203 comb
+= valid
.eq(1) # start "pulse"
204 comb
+= l_in
.valid
.eq(blip
) # start
205 comb
+= l_in
.tlbie
.eq(1) # mtspr mode
206 comb
+= l_in
.sprn
.eq(spr
) # use sprn to send insn bits
207 comb
+= l_in
.addr
.eq(b_i
) # incoming operand (RB)
208 comb
+= done
.eq(l_out
.done
) # zzzz
209 comb
+= self
.debug0
.eq(2)
215 with m
.Case(MicrOp
.OP_FETCH_FAILED
):
216 comb
+= Display("MMUTEST: OP_FETCH_FAILED: @%x", nia_i
)
217 # trigger an instruction fetch failed MMU event.
218 # PowerDecoder2 drops svstate.pc into NIA for us
219 # really, this should be direct communication with the
220 # MMU, rather than going through LoadStore1. but, doing
221 # so allows for the opportunity to prevent LoadStore1
222 # from accepting any other LD/ST requests.
223 comb
+= valid
.eq(1) # start "pulse"
224 comb
+= ldst
.instr_fault
.eq(blip
)
225 comb
+= ldst
.maddr
.eq(nia_i
)
226 comb
+= done
.eq(mmu
.d_in
.done
) # XXX should not access this!
227 comb
+= self
.debug0
.eq(3)
228 # LDST unit contains exception data, which (messily)
229 # is copied over, here. not ideal but it will do for now
230 comb
+= exc_o
.eq(ldst
.pi
.exc_o
)
236 with m
.Case(MicrOp
.OP_ILLEGAL
):
237 comb
+= self
.illegal
.eq(1)
239 with m
.If(self
.n
.i_ready
& self
.n
.o_valid
):