add incoming PortInterface to be connected to LoadStoreCompUnit
[soc.git] / src / soc / fu / mmu / fsm.py
1 from nmigen import Elaboratable, Module, Signal, Shape, unsigned, Cat, Mux
2 from soc.fu.mmu.pipe_data import MMUInputData, MMUOutputData, MMUPipeSpec
3 from nmutil.singlepipe import ControlBase
4 from nmutil.util import rising_edge
5
6 from soc.experiment.mmu import MMU
7 from soc.experiment.dcache import DCache
8
9 from soc.decoder.power_fields import DecodeFields
10 from soc.decoder.power_fieldsn import SignalBitRange
11 from soc.decoder.power_decoder2 import decode_spr_num
12 from soc.decoder.power_enums import MicrOp, SPR, XER_bits
13
14 from soc.experiment.pimem import PortInterface
15
16 class FSMMMUStage(ControlBase):
17 def __init__(self, pspec):
18 super().__init__()
19 self.pspec = pspec
20
21 # set up p/n data
22 self.p.data_i = MMUInputData(pspec)
23 self.n.data_o = MMUOutputData(pspec)
24
25 # incoming PortInterface
26 self.pi = PortInterface("mmupi")
27
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)
32
33 self.mmu = MMU()
34 self.dcache = DCache()
35
36 # make life a bit easier in Core
37 self.pspec.mmu = self.mmu
38 self.pspec.dcache = self.dcache
39
40 # for SPR field number access
41 i = self.p.data_i
42 self.fields = DecodeFields(SignalBitRange, [i.ctx.op.insn])
43 self.fields.create_specs()
44
45 def elaborate(self, platform):
46 m = super().elaborate(platform)
47 comb = m.d.comb
48
49 # link mmu and dcache together
50 m.submodules.dcache = dcache = self.dcache
51 m.submodules.mmu = mmu = self.mmu
52 m.d.comb += dcache.m_in.eq(mmu.d_out)
53 m.d.comb += mmu.d_in.eq(dcache.m_out)
54 l_in, l_out = mmu.l_in, mmu.l_out
55 d_in, d_out = dcache.d_in, dcache.d_out
56
57 data_i, data_o = self.p.data_i, self.n.data_o
58 a_i, b_i, o = data_i.ra, data_i.rb, data_o.o
59 op = data_i.ctx.op
60
61 # TODO: link these SPRs somewhere
62 dsisr = Signal(64)
63 dar = Signal(64)
64
65 # busy/done signals
66 busy = Signal()
67 done = Signal()
68 m.d.comb += self.n.valid_o.eq(busy & done)
69 m.d.comb += self.p.ready_o.eq(~busy)
70
71 # take copy of X-Form SPR field
72 x_fields = self.fields.FormXFX
73 spr = Signal(len(x_fields.SPR))
74 comb += spr.eq(decode_spr_num(x_fields.SPR))
75
76 # ok so we have to "pulse" the MMU (or dcache) rather than
77 # hold the valid hi permanently. guess what this does...
78 valid = Signal()
79 blip = Signal()
80 m.d.comb += blip.eq(rising_edge(m, valid))
81
82 with m.If(~busy):
83 with m.If(self.p.valid_i):
84 m.d.sync += busy.eq(1)
85 with m.Else():
86
87 # based on the Micro-Op, we work out which of MMU or DCache
88 # should "action" the operation. one of MMU or DCache gets
89 # enabled ("valid") and we twiddle our thumbs until it
90 # responds ("done").
91 with m.Switch(op):
92
93 with m.Case(MicrOp.OP_MTSPR):
94 # subset SPR: first check a few bits
95 with m.If(~spr[9] & ~spr[5]):
96 with m.If(spr[0]):
97 comb += dsisr.eq(a_i[:32])
98 with m.Else():
99 comb += dar.eq(a_i)
100 comb += done.eq(1)
101 # pass it over to the MMU instead
102 with m.Else():
103 # blip the MMU and wait for it to complete
104 comb += valid.eq(1) # start "pulse"
105 comb += l_in.valid.eq(blip) # start
106 comb += l_in.mtspr.eq(1) # mtspr mode
107 comb += l_in.sprn.eq(spr) # which SPR
108 comb += l_in.rs.eq(a_i) # incoming operand (RS)
109 comb += done.eq(l_out.done) # zzzz
110
111 with m.Case(MicrOp.OP_MFSPR):
112 # subset SPR: first check a few bits
113 with m.If(~spr[9] & ~spr[5]):
114 with m.If(spr[0]):
115 comb += o.data.eq(dsisr)
116 with m.Else():
117 comb += o.data.eq(dar)
118 comb += o.ok.eq(1)
119 comb += done.eq(1)
120 # pass it over to the MMU instead
121 with m.Else():
122 # blip the MMU and wait for it to complete
123 comb += valid.eq(1) # start "pulse"
124 comb += l_in.valid.eq(blip) # start
125 comb += l_in.mtspr.eq(1) # mtspr mode
126 comb += l_in.sprn.eq(spr) # which SPR
127 comb += l_in.rs.eq(a_i) # incoming operand (RS)
128 comb += o.data.eq(l_out.sprval) # SPR from MMU
129 comb += o.ok.eq(l_out.done) # only when l_out valid
130 comb += done.eq(l_out.done) # zzzz
131
132 with m.Case(MicrOp.OP_DCBZ):
133 # activate dcbz mode (spec: v3.0B p850)
134 comb += valid.eq(1) # start "pulse"
135 comb += d_in.valid.eq(blip) # start
136 comb += d_in.dcbz.eq(1) # dcbz mode
137 comb += d_in.addr.eq(a_i + b_i) # addr is (RA|0) + RB
138 comb += done.eq(l_out.done) # zzzz
139
140 with m.Case(MicrOp.OP_TLBIE):
141 # pass TLBIE request to MMU (spec: v3.0B p1034)
142 # note that the spr is *not* an actual spr number, it's
143 # just that those bits happen to match with field bits
144 # RIC, PRS, R
145 comb += valid.eq(1) # start "pulse"
146 comb += l_in.valid.eq(blip) # start
147 comb += l_in.tlbie.eq(1) # mtspr mode
148 comb += l_in.sprn.eq(spr) # use sprn to send insn bits
149 comb += l_in.addr.eq(b_i) # incoming operand (RB)
150 comb += done.eq(l_out.done) # zzzz
151
152 with m.If(self.n.ready_i & self.n.valid_o):
153 m.d.sync += busy.eq(0)
154
155 return m
156
157 def __iter__(self):
158 yield from self.p
159 yield from self.n
160
161 def ports(self):
162 return list(self)