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
9 from soc
.experiment
.dcache
import DCache
11 from openpower
.consts
import MSR
12 from openpower
.decoder
.power_fields
import DecodeFields
13 from openpower
.decoder
.power_fieldsn
import SignalBitRange
14 from openpower
.decoder
.power_decoder2
import decode_spr_num
15 from openpower
.decoder
.power_enums
import MicrOp
, XER_bits
17 from soc
.experiment
.pimem
import PortInterface
18 from soc
.experiment
.pimem
import PortInterfaceBase
20 from soc
.experiment
.mem_types
import LoadStore1ToDCacheType
, LoadStore1ToMMUType
21 from soc
.experiment
.mem_types
import DCacheToLoadStore1Type
, MMUToLoadStore1Type
23 from soc
.minerva
.wishbone
import make_wb_layout
24 from soc
.bus
.sram
import SRAM
27 # glue logic for microwatt mmu and dcache
28 class LoadStore1(PortInterfaceBase
):
29 def __init__(self
, pspec
):
31 regwid
= pspec
.reg_wid
32 addrwid
= pspec
.addr_wid
34 super().__init
__(regwid
, addrwid
)
35 self
.dcache
= DCache()
36 self
.d_in
= self
.dcache
.d_in
37 self
.d_out
= self
.dcache
.d_out
38 self
.l_in
= LoadStore1ToMMUType()
39 self
.l_out
= MMUToLoadStore1Type()
40 # for debugging with gtkwave only
41 self
.debug1
= Signal()
42 self
.debug2
= Signal()
44 self
.mmureq
= Signal()
45 self
.derror
= Signal()
47 # TODO, convert dcache wb_in/wb_out to "standard" nmigen Wishbone bus
48 self
.dbus
= Record(make_wb_layout(pspec
))
50 def set_wr_addr(self
, m
, addr
, mask
):
51 #m.d.comb += self.l_in.valid.eq(1)
52 #m.d.comb += self.l_in.addr.eq(addr)
53 #m.d.comb += self.l_in.load.eq(0)
54 m
.d
.comb
+= self
.d_in
.valid
.eq(1)
55 m
.d
.comb
+= self
.d_in
.load
.eq(0)
56 m
.d
.comb
+= self
.d_in
.byte_sel
.eq(mask
)
57 # set phys addr on both units
58 m
.d
.comb
+= self
.d_in
.addr
.eq(addr
)
62 def set_rd_addr(self
, m
, addr
, mask
):
63 #m.d.comb += self.l_in.valid.eq(1)
64 #m.d.comb += self.l_in.load.eq(1)
65 #m.d.comb += self.l_in.addr.eq(addr)
66 m
.d
.comb
+= self
.d_in
.valid
.eq(1)
67 m
.d
.comb
+= self
.d_in
.load
.eq(1)
68 m
.d
.comb
+= self
.d_in
.byte_sel
.eq(mask
)
69 m
.d
.comb
+= self
.d_in
.addr
.eq(addr
)
70 m
.d
.comb
+= self
.debug1
.eq(1)
71 # m.d.comb += self.debug2.eq(1)
72 return None #FIXME return value
74 def set_wr_data(self
, m
, data
, wen
):
75 m
.d
.comb
+= self
.d_in
.data
.eq(data
)
80 def get_rd_data(self
, m
):
81 ld_ok
= self
.d_out
.valid
# indicates read data is valid
82 data
= self
.d_out
.data
# actual read data
86 if d_in.error = '1' then
87 if d_in.cache_paradox = '1' then
88 -- signal an interrupt straight away
90 dsisr(63 - 38) := not r2.req.load;
91 -- XXX there is no architected bit for this
92 -- (probably should be a machine check in fact)
93 dsisr(63 - 35) := d_in.cache_paradox;
95 -- Look up the translation for TLB miss
96 -- and also for permission error and RC error
97 -- in case the PTE has been updated.
99 v.state := MMU_LOOKUP;
105 def elaborate(self
, platform
):
106 m
= super().elaborate(platform
)
109 # create dcache module
110 m
.submodules
.dcache
= dcache
= self
.dcache
113 d_out
, l_out
, dbus
= self
.d_out
, self
.l_out
, self
.dbus
115 with m
.If(d_out
.error
):
116 with m
.If(d_out
.cache_paradox
):
117 comb
+= self
.derror
.eq(1)
118 # dsisr(63 - 38) := not r2.req.load;
119 # -- XXX there is no architected bit for this
120 # -- (probably should be a machine check in fact)
121 # dsisr(63 - 35) := d_in.cache_paradox;
123 # Look up the translation for TLB miss
124 # and also for permission error and RC error
125 # in case the PTE has been updated.
126 comb
+= self
.mmureq
.eq(1)
127 # v.state := MMU_LOOKUP;
128 # v.stage1_en := '0';
130 exc
= self
.pi
.exception_o
132 #happened, alignment, instr_fault, invalid,
133 comb
+= exc
.happened
.eq(d_out
.error | l_out
.err
)
134 comb
+= exc
.invalid
.eq(l_out
.invalid
)
136 #badtree, perm_error, rc_error, segment_fault
137 comb
+= exc
.badtree
.eq(l_out
.badtree
)
138 comb
+= exc
.perm_error
.eq(l_out
.perm_error
)
139 comb
+= exc
.rc_error
.eq(l_out
.rc_error
)
140 comb
+= exc
.segment_fault
.eq(l_out
.segerr
)
142 # TODO connect those signals somewhere
143 #print(d_out.valid) -> no error
144 #print(d_out.store_done) -> no error
145 #print(d_out.cache_paradox) -> ?
146 #print(l_out.done) -> no error
148 # TODO some exceptions set SPRs
150 # TODO, connect dcache wb_in/wb_out to "standard" nmigen Wishbone bus
151 comb
+= dbus
.adr
.eq(dcache
.wb_out
.adr
)
152 comb
+= dbus
.dat_w
.eq(dcache
.wb_out
.dat
)
153 comb
+= dbus
.sel
.eq(dcache
.wb_out
.sel
)
154 comb
+= dbus
.cyc
.eq(dcache
.wb_out
.cyc
)
155 comb
+= dbus
.stb
.eq(dcache
.wb_out
.stb
)
156 comb
+= dbus
.we
.eq(dcache
.wb_out
.we
)
158 comb
+= dcache
.wb_in
.dat
.eq(dbus
.dat_r
)
159 comb
+= dcache
.wb_in
.ack
.eq(dbus
.ack
)
160 if hasattr(dbus
, "stall"):
161 comb
+= dcache
.wb_in
.stall
.eq(dbus
.stall
)
166 yield from super().ports()
170 class TestSRAMLoadStore1(LoadStore1
):
171 def __init__(self
, pspec
):
172 super().__init
__(pspec
)
174 # small 32-entry Memory
175 if (hasattr(pspec
, "dmem_test_depth") and
176 isinstance(pspec
.dmem_test_depth
, int)):
177 depth
= pspec
.dmem_test_depth
180 print("TestSRAMBareLoadStoreUnit depth", depth
)
182 self
.mem
= Memory(width
=pspec
.reg_wid
, depth
=depth
)
184 def elaborate(self
, platform
):
185 m
= super().elaborate(platform
)
187 m
.submodules
.sram
= sram
= SRAM(memory
=self
.mem
, granularity
=8,
188 features
={'cti', 'bte', 'err'})
191 # directly connect the wishbone bus of LoadStoreUnitInterface to SRAM
192 # note: SRAM is a target (slave), dbus is initiator (master)
193 fanouts
= ['dat_w', 'sel', 'cyc', 'stb', 'we', 'cti', 'bte']
194 fanins
= ['dat_r', 'ack', 'err']
195 for fanout
in fanouts
:
196 print("fanout", fanout
, getattr(sram
.bus
, fanout
).shape(),
197 getattr(dbus
, fanout
).shape())
198 comb
+= getattr(sram
.bus
, fanout
).eq(getattr(dbus
, fanout
))
199 comb
+= getattr(sram
.bus
, fanout
).eq(getattr(dbus
, fanout
))
201 comb
+= getattr(dbus
, fanin
).eq(getattr(sram
.bus
, fanin
))
203 comb
+= sram
.bus
.adr
.eq(dbus
.adr
)
208 class FSMMMUStage(ControlBase
):
211 FSM-based MMU: must call set_ldst_interface and pass in an instance
212 of a LoadStore1. this to comply with the ConfigMemoryPortInterface API
214 def __init__(self
, pspec
):
219 self
.p
.data_i
= MMUInputData(pspec
)
220 self
.n
.data_o
= MMUOutputData(pspec
)
222 # this Function Unit is extremely unusual in that it actually stores a
223 # "thing" rather than "processes inputs and produces outputs". hence
224 # why it has to be a FSM. linking up LD/ST however is going to have
225 # to be done back in Issuer (or Core)
229 # make life a bit easier in Core XXX mustn't really do this,
230 # pspec is designed for config variables, rather than passing
231 # things around. have to think about it, design a way to do
232 # it that makes "sense"
233 # comment out for now self.pspec.mmu = self.mmu
234 # comment out for now self.pspec.dcache = self.dcache
236 # debugging output for gtkw
237 self
.debug0
= Signal(4)
238 self
.debug1
= Signal()
239 #self.debug2 = Signal(64)
240 #self.debug3 = Signal(64)
241 self
.illegal
= Signal()
243 # for SPR field number access
245 self
.fields
= DecodeFields(SignalBitRange
, [i
.ctx
.op
.insn
])
246 self
.fields
.create_specs()
248 def set_ldst_interface(self
, ldst
):
249 """must be called back in Core, after FUs have been set up.
250 one of those will be the MMU (us!) but the LoadStore1 instance
251 must be set up in ConfigMemoryPortInterface. sigh.
253 # incoming PortInterface
255 self
.dcache
= self
.ldst
.dcache
256 self
.pi
= self
.ldst
.pi
258 def elaborate(self
, platform
):
259 assert hasattr(self
, "dcache"), "remember to call set_ldst_interface"
260 m
= super().elaborate(platform
)
264 # link mmu and dcache together
265 m
.submodules
.mmu
= mmu
= self
.mmu
266 ldst
= self
.ldst
# managed externally: do not add here
267 m
.d
.comb
+= dcache
.m_in
.eq(mmu
.d_out
) # MMUToDCacheType
268 m
.d
.comb
+= mmu
.d_in
.eq(dcache
.m_out
) # DCacheToMMUType
270 l_in
, l_out
= mmu
.l_in
, mmu
.l_out
271 d_in
, d_out
= dcache
.d_in
, dcache
.d_out
272 wb_out
, wb_in
= dcache
.wb_out
, dcache
.wb_in
274 # link ldst and MMU together
275 comb
+= l_in
.eq(ldst
.l_in
)
276 comb
+= ldst
.l_out
.eq(l_out
)
278 data_i
, data_o
= self
.p
.data_i
, self
.n
.data_o
279 a_i
, b_i
, o
, spr1_o
= data_i
.ra
, data_i
.rb
, data_o
.o
, data_o
.spr1
283 # TODO: link these SPRs somewhere
290 m
.d
.comb
+= self
.n
.valid_o
.eq(busy
& done
)
291 m
.d
.comb
+= self
.p
.ready_o
.eq(~busy
)
293 # take copy of X-Form SPR field
294 x_fields
= self
.fields
.FormXFX
295 spr
= Signal(len(x_fields
.SPR
))
296 comb
+= spr
.eq(decode_spr_num(x_fields
.SPR
))
298 # based on MSR bits, set priv and virt mode. TODO: 32-bit mode
299 comb
+= d_in
.priv_mode
.eq(~msr_i
[MSR
.PR
])
300 comb
+= d_in
.virt_mode
.eq(msr_i
[MSR
.DR
])
301 #comb += d_in.mode_32bit.eq(msr_i[MSR.SF]) # ?? err
303 # ok so we have to "pulse" the MMU (or dcache) rather than
304 # hold the valid hi permanently. guess what this does...
307 m
.d
.comb
+= blip
.eq(rising_edge(m
, valid
))
310 with m
.If(self
.p
.valid_i
):
311 m
.d
.sync
+= busy
.eq(1)
314 # based on the Micro-Op, we work out which of MMU or DCache
315 # should "action" the operation. one of MMU or DCache gets
316 # enabled ("valid") and we twiddle our thumbs until it
319 # FIXME: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
321 with m
.Switch(op
.insn_type
):
322 with m
.Case(MicrOp
.OP_MTSPR
):
323 # despite redirection this FU **MUST** behave exactly
324 # like the SPR FU. this **INCLUDES** updating the SPR
325 # regfile because the CSV file entry for OP_MTSPR
326 # categorically defines and requires the expectation
327 # that the CompUnit **WILL** write to the regfile.
328 comb
+= spr1_o
.data
.eq(spr
)
329 comb
+= spr1_o
.ok
.eq(1)
330 # subset SPR: first check a few bits
331 with m
.If(~spr
[9] & ~spr
[5]):
332 comb
+= self
.debug0
.eq(3)
334 comb
+= dsisr
.eq(a_i
[:32])
338 # pass it over to the MMU instead
340 comb
+= self
.debug0
.eq(4)
341 # blip the MMU and wait for it to complete
342 comb
+= valid
.eq(1) # start "pulse"
343 comb
+= l_in
.valid
.eq(blip
) # start
344 comb
+= l_in
.mtspr
.eq(1) # mtspr mode
345 comb
+= l_in
.sprn
.eq(spr
) # which SPR
346 comb
+= l_in
.rs
.eq(a_i
) # incoming operand (RS)
347 comb
+= done
.eq(1) # FIXME l_out.done
349 with m
.Case(MicrOp
.OP_MFSPR
):
350 # subset SPR: first check a few bits
351 with m
.If(~spr
[9] & ~spr
[5]):
352 comb
+= self
.debug0
.eq(5)
354 comb
+= o
.data
.eq(dsisr
)
356 comb
+= o
.data
.eq(dar
)
359 # pass it over to the MMU instead
361 comb
+= self
.debug0
.eq(6)
362 # blip the MMU and wait for it to complete
363 comb
+= valid
.eq(1) # start "pulse"
364 comb
+= l_in
.valid
.eq(blip
) # start
365 comb
+= l_in
.mtspr
.eq(0) # mfspr!=mtspr
366 comb
+= l_in
.sprn
.eq(spr
) # which SPR
367 comb
+= l_in
.rs
.eq(a_i
) # incoming operand (RS)
368 comb
+= o
.data
.eq(l_out
.sprval
) # SPR from MMU
369 comb
+= o
.ok
.eq(l_out
.done
) # only when l_out valid
370 comb
+= done
.eq(1) # FIXME l_out.done
372 # XXX this one is going to have to go through LDSTCompUnit
373 # because it's LDST that has control over dcache
374 # (through PortInterface). or, another means is devised
375 # so as not to have double-drivers of d_in.valid and addr
377 #with m.Case(MicrOp.OP_DCBZ):
378 # # activate dcbz mode (spec: v3.0B p850)
379 # comb += valid.eq(1) # start "pulse"
380 # comb += d_in.valid.eq(blip) # start
381 # comb += d_in.dcbz.eq(1) # dcbz mode
382 # comb += d_in.addr.eq(a_i + b_i) # addr is (RA|0) + RB
383 # comb += done.eq(d_out.store_done) # TODO
384 # comb += self.debug0.eq(1)
386 with m
.Case(MicrOp
.OP_TLBIE
):
387 # pass TLBIE request to MMU (spec: v3.0B p1034)
388 # note that the spr is *not* an actual spr number, it's
389 # just that those bits happen to match with field bits
391 comb
+= valid
.eq(1) # start "pulse"
392 comb
+= l_in
.valid
.eq(blip
) # start
393 comb
+= l_in
.tlbie
.eq(1) # mtspr mode
394 comb
+= l_in
.sprn
.eq(spr
) # use sprn to send insn bits
395 comb
+= l_in
.addr
.eq(b_i
) # incoming operand (RB)
396 comb
+= done
.eq(l_out
.done
) # zzzz
397 comb
+= self
.debug0
.eq(2)
398 with m
.Case(MicrOp
.OP_ILLEGAL
):
399 comb
+= self
.illegal
.eq(1)
401 with m
.If(self
.n
.ready_i
& self
.n
.valid_o
):
402 m
.d
.sync
+= busy
.eq(0)