1 from nmigen
import Elaboratable
, Module
, Signal
, Shape
, unsigned
, Cat
, Mux
2 from nmigen
import Const
3 from soc
.fu
.mmu
.pipe_data
import MMUInputData
, MMUOutputData
, MMUPipeSpec
4 from nmutil
.singlepipe
import ControlBase
5 from nmutil
.util
import rising_edge
7 from soc
.experiment
.mmu
import MMU
8 from soc
.experiment
.dcache
import DCache
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
, XER_bits
16 from soc
.experiment
.pimem
import PortInterface
17 from soc
.experiment
.pimem
import PortInterfaceBase
19 from soc
.experiment
.mem_types
import LoadStore1ToDCacheType
, LoadStore1ToMMUType
20 from soc
.experiment
.mem_types
import DCacheToLoadStore1Type
, MMUToLoadStore1Type
23 # glue logic for microwatt mmu and dcache
24 class LoadStore1(PortInterfaceBase
):
25 def __init__(self
, regwid
=64, addrwid
=4):
26 super().__init
__(regwid
, addrwid
)
27 self
.dcache
= DCache()
28 self
.d_in
= self
.dcache
.d_in
29 self
.d_out
= self
.dcache
.d_out
30 self
.l_in
= LoadStore1ToMMUType()
31 self
.l_out
= MMUToLoadStore1Type()
32 # for debugging with gtkwave only
33 self
.debug1
= Signal()
34 self
.debug2
= Signal()
36 self
.mmureq
= Signal()
37 self
.derror
= Signal()
39 # TODO, convert dcache wb_in/wb_out to "standard" nmigen Wishbone bus
40 # self.bus = Interface(...)
42 def set_wr_addr(self
, m
, addr
, mask
):
43 #m.d.comb += self.d_in.valid.eq(1)
44 #m.d.comb += self.l_in.valid.eq(1)
45 #m.d.comb += self.d_in.load.eq(0)
46 #m.d.comb += self.l_in.load.eq(0)
47 # set phys addr on both units
48 m
.d
.comb
+= self
.d_in
.addr
.eq(addr
)
49 m
.d
.comb
+= self
.l_in
.addr
.eq(addr
)
53 def set_rd_addr(self
, m
, addr
, mask
):
54 m
.d
.comb
+= self
.d_in
.valid
.eq(1)
55 m
.d
.comb
+= self
.l_in
.valid
.eq(1)
56 m
.d
.comb
+= self
.d_in
.load
.eq(1)
57 m
.d
.comb
+= self
.l_in
.load
.eq(1)
58 m
.d
.comb
+= self
.d_in
.addr
.eq(addr
)
59 m
.d
.comb
+= self
.l_in
.addr
.eq(addr
)
60 m
.d
.comb
+= self
.debug1
.eq(1)
61 # m.d.comb += self.debug2.eq(1)
62 return None #FIXME return value
64 def set_wr_data(self
, m
, data
, wen
):
65 m
.d
.comb
+= self
.d_in
.data
.eq(data
)
70 def get_rd_data(self
, m
):
71 ld_ok
= self
.d_out
.valid
# indicates read data is valid
72 data
= self
.d_out
.data
# actual read data
76 if d_in.error = '1' then
77 if d_in.cache_paradox = '1' then
78 -- signal an interrupt straight away
80 dsisr(63 - 38) := not r2.req.load;
81 -- XXX there is no architected bit for this
82 -- (probably should be a machine check in fact)
83 dsisr(63 - 35) := d_in.cache_paradox;
85 -- Look up the translation for TLB miss
86 -- and also for permission error and RC error
87 -- in case the PTE has been updated.
89 v.state := MMU_LOOKUP;
95 def elaborate(self
, platform
):
96 m
= super().elaborate(platform
)
101 # create dcache module
102 m
.submodules
.dcache
= self
.dcache
104 with m
.If(d_out
.error
):
105 with m
.If(d_out
.cache_paradox
):
106 m
.d
.comb
+= self
.derror
.eq(1)
107 # dsisr(63 - 38) := not r2.req.load;
108 # -- XXX there is no architected bit for this
109 # -- (probably should be a machine check in fact)
110 # dsisr(63 - 35) := d_in.cache_paradox;
112 # Look up the translation for TLB miss
113 # and also for permission error and RC error
114 # in case the PTE has been updated.
115 m
.d
.comb
+= self
.mmureq
.eq(1)
116 # v.state := MMU_LOOKUP;
117 # v.stage1_en := '0';
119 exc
= self
.pi
.exception_o
121 #happened, alignment, instr_fault, invalid,
122 m
.d
.comb
+= exc
.happened
.eq(d_out
.error | l_out
.err
)
123 m
.d
.comb
+= exc
.invalid
.eq(l_out
.invalid
)
125 #badtree, perm_error, rc_error, segment_fault
126 m
.d
.comb
+= exc
.badtree
.eq(l_out
.badtree
)
127 m
.d
.comb
+= exc
.perm_error
.eq(l_out
.perm_error
)
128 m
.d
.comb
+= exc
.rc_error
.eq(l_out
.rc_error
)
129 m
.d
.comb
+= exc
.segment_fault
.eq(l_out
.segerr
)
131 # TODO connect those signals somewhere
132 #print(d_out.valid) -> no error
133 #print(d_out.store_done) -> no error
134 #print(d_out.cache_paradox) -> ?
135 #print(l_out.done) -> no error
137 # TODO some exceptions set SPRs
142 yield from super().ports()
146 class FSMMMUStage(ControlBase
):
147 def __init__(self
, pspec
):
152 self
.p
.data_i
= MMUInputData(pspec
)
153 self
.n
.data_o
= MMUOutputData(pspec
)
155 # incoming PortInterface
156 self
.ldst
= LoadStore1() # TODO make this depend on pspec
157 self
.dcache
= self
.ldst
.dcache
158 self
.pi
= self
.ldst
.pi
160 # this Function Unit is extremely unusual in that it actually stores a
161 # "thing" rather than "processes inputs and produces outputs". hence
162 # why it has to be a FSM. linking up LD/ST however is going to have
163 # to be done back in Issuer (or Core)
167 # make life a bit easier in Core
168 self
.pspec
.mmu
= self
.mmu
169 self
.pspec
.dcache
= self
.dcache
171 # debugging output for gtkw
172 self
.debug0
= Signal(4)
173 self
.debug1
= Signal()
174 #self.debug2 = Signal(64)
175 #self.debug3 = Signal(64)
176 self
.illegal
= Signal()
178 # for SPR field number access
180 self
.fields
= DecodeFields(SignalBitRange
, [i
.ctx
.op
.insn
])
181 self
.fields
.create_specs()
183 def elaborate(self
, platform
):
184 m
= super().elaborate(platform
)
188 # link mmu and dcache together
189 m
.submodules
.mmu
= mmu
= self
.mmu
190 m
.submodules
.ldst
= ldst
= self
.ldst
191 m
.d
.comb
+= dcache
.m_in
.eq(mmu
.d_out
)
192 m
.d
.comb
+= mmu
.d_in
.eq(dcache
.m_out
)
194 l_in
, l_out
= mmu
.l_in
, mmu
.l_out
195 d_in
, d_out
= dcache
.d_in
, dcache
.d_out
196 wb_out
, wb_in
= dcache
.wb_out
, dcache
.wb_in
198 # link ldst and MMU together
199 comb
+= l_in
.eq(ldst
.l_in
)
200 comb
+= ldst
.l_out
.eq(l_out
)
202 data_i
, data_o
= self
.p
.data_i
, self
.n
.data_o
203 a_i
, b_i
, o
, spr1_o
= data_i
.ra
, data_i
.rb
, data_o
.o
, data_o
.spr1
207 # TODO: link these SPRs somewhere
214 m
.d
.comb
+= self
.n
.valid_o
.eq(busy
& done
)
215 m
.d
.comb
+= self
.p
.ready_o
.eq(~busy
)
217 # take copy of X-Form SPR field
218 x_fields
= self
.fields
.FormXFX
219 spr
= Signal(len(x_fields
.SPR
))
220 comb
+= spr
.eq(decode_spr_num(x_fields
.SPR
))
222 # based on MSR bits, set priv and virt mode. TODO: 32-bit mode
223 comb
+= d_in
.priv_mode
.eq(~msr_i
[MSR
.PR
])
224 comb
+= d_in
.virt_mode
.eq(msr_i
[MSR
.DR
])
225 #comb += d_in.mode_32bit.eq(msr_i[MSR.SF]) # ?? err
227 # ok so we have to "pulse" the MMU (or dcache) rather than
228 # hold the valid hi permanently. guess what this does...
231 m
.d
.comb
+= blip
.eq(rising_edge(m
, valid
))
234 with m
.If(self
.p
.valid_i
):
235 m
.d
.sync
+= busy
.eq(1)
238 # based on the Micro-Op, we work out which of MMU or DCache
239 # should "action" the operation. one of MMU or DCache gets
240 # enabled ("valid") and we twiddle our thumbs until it
243 # FIXME: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
245 with m
.Switch(op
.insn_type
):
246 with m
.Case(MicrOp
.OP_MTSPR
):
247 # despite redirection this FU **MUST** behave exactly
248 # like the SPR FU. this **INCLUDES** updating the SPR
249 # regfile because the CSV file entry for OP_MTSPR
250 # categorically defines and requires the expectation
251 # that the CompUnit **WILL** write to the regfile.
252 comb
+= spr1_o
.data
.eq(spr
)
253 comb
+= spr1_o
.ok
.eq(1)
254 # subset SPR: first check a few bits
255 with m
.If(~spr
[9] & ~spr
[5]):
256 comb
+= self
.debug0
.eq(3)
258 comb
+= dsisr
.eq(a_i
[:32])
262 # pass it over to the MMU instead
264 comb
+= self
.debug0
.eq(4)
265 # blip the MMU and wait for it to complete
266 comb
+= valid
.eq(1) # start "pulse"
267 comb
+= l_in
.valid
.eq(blip
) # start
268 comb
+= l_in
.mtspr
.eq(1) # mtspr mode
269 comb
+= l_in
.sprn
.eq(spr
) # which SPR
270 comb
+= l_in
.rs
.eq(a_i
) # incoming operand (RS)
271 comb
+= done
.eq(1) # FIXME l_out.done
273 with m
.Case(MicrOp
.OP_MFSPR
):
274 # subset SPR: first check a few bits
275 with m
.If(~spr
[9] & ~spr
[5]):
276 comb
+= self
.debug0
.eq(5)
278 comb
+= o
.data
.eq(dsisr
)
280 comb
+= o
.data
.eq(dar
)
283 # pass it over to the MMU instead
285 comb
+= self
.debug0
.eq(6)
286 # blip the MMU and wait for it to complete
287 comb
+= valid
.eq(1) # start "pulse"
288 comb
+= l_in
.valid
.eq(blip
) # start
289 comb
+= l_in
.mtspr
.eq(0) # mfspr!=mtspr
290 comb
+= l_in
.sprn
.eq(spr
) # which SPR
291 comb
+= l_in
.rs
.eq(a_i
) # incoming operand (RS)
292 comb
+= o
.data
.eq(l_out
.sprval
) # SPR from MMU
293 comb
+= o
.ok
.eq(l_out
.done
) # only when l_out valid
294 comb
+= done
.eq(1) # FIXME l_out.done
296 # XXX this one is going to have to go through LDSTCompUnit
297 # because it's LDST that has control over dcache
298 # (through PortInterface). or, another means is devised
299 # so as not to have double-drivers of d_in.valid and addr
301 #with m.Case(MicrOp.OP_DCBZ):
302 # # activate dcbz mode (spec: v3.0B p850)
303 # comb += valid.eq(1) # start "pulse"
304 # comb += d_in.valid.eq(blip) # start
305 # comb += d_in.dcbz.eq(1) # dcbz mode
306 # comb += d_in.addr.eq(a_i + b_i) # addr is (RA|0) + RB
307 # comb += done.eq(d_out.store_done) # TODO
308 # comb += self.debug0.eq(1)
310 with m
.Case(MicrOp
.OP_TLBIE
):
311 # pass TLBIE request to MMU (spec: v3.0B p1034)
312 # note that the spr is *not* an actual spr number, it's
313 # just that those bits happen to match with field bits
315 comb
+= valid
.eq(1) # start "pulse"
316 comb
+= l_in
.valid
.eq(blip
) # start
317 comb
+= l_in
.tlbie
.eq(1) # mtspr mode
318 comb
+= l_in
.sprn
.eq(spr
) # use sprn to send insn bits
319 comb
+= l_in
.addr
.eq(b_i
) # incoming operand (RB)
320 comb
+= done
.eq(l_out
.done
) # zzzz
321 comb
+= self
.debug0
.eq(2)
322 with m
.Case(MicrOp
.OP_ILLEGAL
):
323 comb
+= self
.illegal
.eq(1)
325 with m
.If(self
.n
.ready_i
& self
.n
.valid_o
):
326 m
.d
.sync
+= busy
.eq(0)