add LoadStore State enum
[soc.git] / src / soc / fu / ldst / loadstore.py
1 from nmigen import (Elaboratable, Module, Signal, Shape, unsigned, Cat, Mux,
2 Record, Memory,
3 Const)
4 from nmutil.util import rising_edge
5 from enum import Enum, unique
6
7 from soc.experiment.dcache import DCache
8 from soc.experiment.pimem import PortInterfaceBase
9 from soc.experiment.mem_types import LoadStore1ToMMUType
10 from soc.experiment.mem_types import MMUToLoadStore1Type
11
12 from soc.minerva.wishbone import make_wb_layout
13 from soc.bus.sram import SRAM
14
15
16 @unique
17 class State(Enum):
18 IDLE = 0 # ready for instruction
19 SECOND_REQ = 1 # send 2nd request of unaligned xfer
20 ACK_WAIT = 2 # waiting for ack from dcache
21 MMU_LOOKUP = 3 # waiting for MMU to look up translation
22 TLBIE_WAIT = 4 # waiting for MMU to finish doing a tlbie
23 FINISH_LFS = 5 # write back converted SP data for lfs*
24 COMPLETE = 6 # extra cycle to complete an operation
25
26
27 # glue logic for microwatt mmu and dcache
28 class LoadStore1(PortInterfaceBase):
29 def __init__(self, pspec):
30 self.pspec = pspec
31 self.disable_cache = (hasattr(pspec, "disable_cache") and
32 pspec.disable_cache == True)
33 regwid = pspec.reg_wid
34 addrwid = pspec.addr_wid
35
36 super().__init__(regwid, addrwid)
37 self.dcache = DCache()
38 self.d_in = self.dcache.d_in
39 self.d_out = self.dcache.d_out
40 self.l_in = LoadStore1ToMMUType()
41 self.l_out = MMUToLoadStore1Type()
42 # TODO microwatt
43 self.mmureq = Signal()
44 self.derror = Signal()
45
46 # TODO, convert dcache wb_in/wb_out to "standard" nmigen Wishbone bus
47 self.dbus = Record(make_wb_layout(pspec))
48
49 # for creating a single clock blip to DCache
50 self.d_valid = Signal()
51 self.d_w_data = Signal(64) # XXX
52 self.d_w_valid = Signal()
53 self.d_validblip = Signal()
54
55 # DSISR and DAR cached values. note that the MMU FSM is where
56 # these are accessed by OP_MTSPR/OP_MFSPR, on behalf of LoadStore1.
57 # by contrast microwatt has the spr set/get done *in* loadstore1.vhdl
58 self.dsisr = Signal(64)
59 self.dar = Signal(64)
60
61 def set_wr_addr(self, m, addr, mask):
62 # this gets complicated: actually a FSM is needed which
63 # first checks dcache, then if that fails (in virt mode)
64 # it checks the MMU instead.
65 #m.d.comb += self.l_in.valid.eq(1)
66 #m.d.comb += self.l_in.addr.eq(addr)
67 #m.d.comb += self.l_in.load.eq(0)
68 m.d.comb += self.d_in.load.eq(0)
69 m.d.comb += self.d_in.byte_sel.eq(mask)
70 m.d.comb += self.d_in.addr.eq(addr)
71 # option to disable the cache entirely for write
72 if self.disable_cache:
73 m.d.comb += self.d_in.nc.eq(1)
74 return None
75
76 def set_rd_addr(self, m, addr, mask):
77 # this gets complicated: actually a FSM is needed which
78 # first checks dcache, then if that fails (in virt mode)
79 # it checks the MMU instead.
80 #m.d.comb += self.l_in.valid.eq(1)
81 #m.d.comb += self.l_in.load.eq(1)
82 #m.d.comb += self.l_in.addr.eq(addr)
83 m.d.comb += self.d_valid.eq(1)
84 m.d.comb += self.d_in.valid.eq(self.d_validblip)
85 m.d.comb += self.d_in.load.eq(1)
86 m.d.comb += self.d_in.byte_sel.eq(mask)
87 m.d.comb += self.d_in.addr.eq(addr)
88 # BAD HACK! disable cacheing on LD when address is 0xCxxx_xxxx
89 # this is for peripherals. same thing done in Microwatt loadstore1.vhdl
90 with m.If(addr[28:] == Const(0xc, 4)):
91 m.d.comb += self.d_in.nc.eq(1)
92 # option to disable the cache entirely for read
93 if self.disable_cache:
94 m.d.comb += self.d_in.nc.eq(1)
95 return None #FIXME return value
96
97 def set_wr_data(self, m, data, wen):
98 # do the "blip" on write data
99 m.d.comb += self.d_valid.eq(1)
100 m.d.comb += self.d_in.valid.eq(self.d_validblip)
101 # put data into comb which is picked up in main elaborate()
102 m.d.comb += self.d_w_valid.eq(1)
103 m.d.comb += self.d_w_data.eq(data)
104 #m.d.sync += self.d_in.byte_sel.eq(wen) # this might not be needed
105 st_ok = self.d_out.valid # TODO indicates write data is valid
106 #st_ok = Const(1, 1)
107 return st_ok
108
109 def get_rd_data(self, m):
110 ld_ok = self.d_out.valid # indicates read data is valid
111 data = self.d_out.data # actual read data
112 return data, ld_ok
113
114 """
115 if d_in.error = '1' then
116 if d_in.cache_paradox = '1' then
117 -- signal an interrupt straight away
118 exception := '1';
119 dsisr(63 - 38) := not r2.req.load;
120 -- XXX there is no architected bit for this
121 -- (probably should be a machine check in fact)
122 dsisr(63 - 35) := d_in.cache_paradox;
123 else
124 -- Look up the translation for TLB miss
125 -- and also for permission error and RC error
126 -- in case the PTE has been updated.
127 mmureq := '1';
128 v.state := MMU_LOOKUP;
129 v.stage1_en := '0';
130 end if;
131 end if;
132 """
133
134 def elaborate(self, platform):
135 m = super().elaborate(platform)
136 comb = m.d.comb
137
138 # create dcache module
139 m.submodules.dcache = dcache = self.dcache
140
141 # temp vars
142 d_out, l_out, dbus = self.d_out, self.l_out, self.dbus
143
144 with m.If(d_out.error):
145 with m.If(d_out.cache_paradox):
146 comb += self.derror.eq(1)
147 """
148 sync += self.dsisr[63 - 38].eq(~r2.req.load)
149 # -- XXX there is no architected bit for this
150 # -- (probably should be a machine check in fact)
151 sync += self.dsisr[63 - 35].eq(d_in.cache_paradox)
152 """
153 with m.Else():
154 # Look up the translation for TLB miss
155 # and also for permission error and RC error
156 # in case the PTE has been updated.
157 comb += self.mmureq.eq(1)
158 # v.state := MMU_LOOKUP;
159 # v.stage1_en := '0';
160
161 exc = self.pi.exc_o
162
163 #happened, alignment, instr_fault, invalid,
164 comb += exc.happened.eq(d_out.error | l_out.err)
165 comb += exc.invalid.eq(l_out.invalid)
166
167 #badtree, perm_error, rc_error, segment_fault
168 comb += exc.badtree.eq(l_out.badtree)
169 comb += exc.perm_error.eq(l_out.perm_error)
170 comb += exc.rc_error.eq(l_out.rc_error)
171 comb += exc.segment_fault.eq(l_out.segerr)
172
173 # TODO connect those signals somewhere
174 #print(d_out.valid) -> no error
175 #print(d_out.store_done) -> no error
176 #print(d_out.cache_paradox) -> ?
177 #print(l_out.done) -> no error
178
179 # TODO some exceptions set SPRs
180
181 # TODO, connect dcache wb_in/wb_out to "standard" nmigen Wishbone bus
182 comb += dbus.adr.eq(dcache.wb_out.adr)
183 comb += dbus.dat_w.eq(dcache.wb_out.dat)
184 comb += dbus.sel.eq(dcache.wb_out.sel)
185 comb += dbus.cyc.eq(dcache.wb_out.cyc)
186 comb += dbus.stb.eq(dcache.wb_out.stb)
187 comb += dbus.we.eq(dcache.wb_out.we)
188
189 comb += dcache.wb_in.dat.eq(dbus.dat_r)
190 comb += dcache.wb_in.ack.eq(dbus.ack)
191 if hasattr(dbus, "stall"):
192 comb += dcache.wb_in.stall.eq(dbus.stall)
193
194 # create a blip (single pulse) on valid read/write request
195 m.d.comb += self.d_validblip.eq(rising_edge(m, self.d_valid))
196
197 # write out d data only when flag set
198 with m.If(self.d_w_valid):
199 m.d.sync += self.d_in.data.eq(self.d_w_data)
200 with m.Else():
201 m.d.sync += self.d_in.data.eq(0)
202
203 return m
204
205 def ports(self):
206 yield from super().ports()
207 # TODO: memory ports
208
209
210 class TestSRAMLoadStore1(LoadStore1):
211 def __init__(self, pspec):
212 super().__init__(pspec)
213 pspec = self.pspec
214 # small 32-entry Memory
215 if (hasattr(pspec, "dmem_test_depth") and
216 isinstance(pspec.dmem_test_depth, int)):
217 depth = pspec.dmem_test_depth
218 else:
219 depth = 32
220 print("TestSRAMBareLoadStoreUnit depth", depth)
221
222 self.mem = Memory(width=pspec.reg_wid, depth=depth)
223
224 def elaborate(self, platform):
225 m = super().elaborate(platform)
226 comb = m.d.comb
227 m.submodules.sram = sram = SRAM(memory=self.mem, granularity=8,
228 features={'cti', 'bte', 'err'})
229 dbus = self.dbus
230
231 # directly connect the wishbone bus of LoadStoreUnitInterface to SRAM
232 # note: SRAM is a target (slave), dbus is initiator (master)
233 fanouts = ['dat_w', 'sel', 'cyc', 'stb', 'we', 'cti', 'bte']
234 fanins = ['dat_r', 'ack', 'err']
235 for fanout in fanouts:
236 print("fanout", fanout, getattr(sram.bus, fanout).shape(),
237 getattr(dbus, fanout).shape())
238 comb += getattr(sram.bus, fanout).eq(getattr(dbus, fanout))
239 comb += getattr(sram.bus, fanout).eq(getattr(dbus, fanout))
240 for fanin in fanins:
241 comb += getattr(dbus, fanin).eq(getattr(sram.bus, fanin))
242 # connect address
243 comb += sram.bus.adr.eq(dbus.adr)
244
245 return m
246