LoadStore1 tidyup
[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_valid = Signal()
52 self.d_validblip = Signal()
53
54 # DSISR and DAR cached values. note that the MMU FSM is where
55 # these are accessed by OP_MTSPR/OP_MFSPR, on behalf of LoadStore1.
56 # by contrast microwatt has the spr set/get done *in* loadstore1.vhdl
57 self.dsisr = Signal(64)
58 self.dar = Signal(64)
59
60 # state info for LD/ST
61 self.done = Signal()
62 # latch most of the input request
63 self.load = Signal()
64 self.tlbie = Signal()
65 self.dcbz = Signal()
66 self.addr = Signal(64)
67 self.store_data = Signal(64)
68 self.load_data = Signal(64)
69 self.byte_sel = Signal(8)
70 self.update = Signal()
71 #self.xerc : xer_common_t;
72 #self.reserve = Signal()
73 #self.atomic = Signal()
74 #self.atomic_last = Signal()
75 #self.rc = Signal()
76 self.nc = Signal() # non-cacheable access
77 self.virt_mode = Signal()
78 self.priv_mode = Signal()
79 self.state = Signal(State)
80 self.instr_fault = Signal()
81 self.align_intr = Signal()
82 self.busy = Signal()
83 self.wait_dcache = Signal()
84 self.wait_mmu = Signal()
85 #self.mode_32bit = Signal()
86 self.wr_sel = Signal(2)
87 self.interrupt = Signal()
88 #self.intr_vec : integer range 0 to 16#fff#;
89 #self.nia = Signal(64)
90 #self.srr1 = Signal(16)
91
92 def set_wr_addr(self, m, addr, mask):
93 m.d.comb += self.load.eq(0) # store operation
94
95 m.d.comb += self.d_in.load.eq(0)
96 m.d.comb += self.byte_sel.eq(mask)
97 m.d.comb += self.addr.eq(addr)
98 # option to disable the cache entirely for write
99 if self.disable_cache:
100 m.d.comb += self.nc.eq(1)
101 return None
102
103 def set_rd_addr(self, m, addr, mask):
104 m.d.comb += self.d_valid.eq(1)
105 m.d.comb += self.d_in.valid.eq(self.d_validblip)
106 m.d.comb += self.load.eq(1) # load operation
107 m.d.comb += self.d_in.load.eq(1)
108 m.d.comb += self.byte_sel.eq(mask)
109 m.d.comb += self.addr.eq(addr)
110 # BAD HACK! disable cacheing on LD when address is 0xCxxx_xxxx
111 # this is for peripherals. same thing done in Microwatt loadstore1.vhdl
112 with m.If(addr[28:] == Const(0xc, 4)):
113 m.d.comb += self.nc.eq(1)
114 # option to disable the cache entirely for read
115 if self.disable_cache:
116 m.d.comb += self.nc.eq(1)
117 return None #FIXME return value
118
119 def set_wr_data(self, m, data, wen):
120 # do the "blip" on write data
121 m.d.comb += self.d_valid.eq(1)
122 m.d.comb += self.d_in.valid.eq(self.d_validblip)
123 # put data into comb which is picked up in main elaborate()
124 m.d.comb += self.d_w_valid.eq(1)
125 m.d.comb += self.store_data.eq(data)
126 #m.d.sync += self.d_in.byte_sel.eq(wen) # this might not be needed
127 st_ok = self.done # TODO indicates write data is valid
128 return st_ok
129
130 def get_rd_data(self, m):
131 ld_ok = self.done # indicates read data is valid
132 data = self.load_data # actual read data
133 return data, ld_ok
134
135 """
136 if d_in.error = '1' then
137 if d_in.cache_paradox = '1' then
138 -- signal an interrupt straight away
139 exception := '1';
140 dsisr(63 - 38) := not r2.req.load;
141 -- XXX there is no architected bit for this
142 -- (probably should be a machine check in fact)
143 dsisr(63 - 35) := d_in.cache_paradox;
144 else
145 -- Look up the translation for TLB miss
146 -- and also for permission error and RC error
147 -- in case the PTE has been updated.
148 mmureq := '1';
149 v.state := MMU_LOOKUP;
150 v.stage1_en := '0';
151 end if;
152 end if;
153 """
154
155 def elaborate(self, platform):
156 m = super().elaborate(platform)
157 comb, sync = m.d.comb, m.d.sync
158
159 # create dcache module
160 m.submodules.dcache = dcache = self.dcache
161
162 # temp vars
163 d_in, d_out, l_out, dbus = self.d_in, self.d_out, self.l_out, self.dbus
164
165 with m.If(d_out.error):
166 with m.If(d_out.cache_paradox):
167 sync += self.derror.eq(1)
168 sync += self.dsisr[63 - 38].eq(~self.load)
169 # XXX there is no architected bit for this
170 # (probably should be a machine check in fact)
171 sync += self.dsisr[63 - 35].eq(d_out.cache_paradox)
172
173 with m.Else():
174 # Look up the translation for TLB miss
175 # and also for permission error and RC error
176 # in case the PTE has been updated.
177 sync += self.mmureq.eq(1)
178 sync += self.state.eq(State.MMU_LOOKUP)
179
180 exc = self.pi.exc_o
181
182 # happened, alignment, instr_fault, invalid,
183 comb += exc.happened.eq(d_out.error | l_out.err)
184 comb += exc.invalid.eq(l_out.invalid)
185
186 # badtree, perm_error, rc_error, segment_fault
187 comb += exc.badtree.eq(l_out.badtree)
188 comb += exc.perm_error.eq(l_out.perm_error)
189 comb += exc.rc_error.eq(l_out.rc_error)
190 comb += exc.segment_fault.eq(l_out.segerr)
191
192 # TODO some exceptions set SPRs
193
194 # TODO, connect dcache wb_in/wb_out to "standard" nmigen Wishbone bus
195 comb += dbus.adr.eq(dcache.wb_out.adr)
196 comb += dbus.dat_w.eq(dcache.wb_out.dat)
197 comb += dbus.sel.eq(dcache.wb_out.sel)
198 comb += dbus.cyc.eq(dcache.wb_out.cyc)
199 comb += dbus.stb.eq(dcache.wb_out.stb)
200 comb += dbus.we.eq(dcache.wb_out.we)
201
202 comb += dcache.wb_in.dat.eq(dbus.dat_r)
203 comb += dcache.wb_in.ack.eq(dbus.ack)
204 if hasattr(dbus, "stall"):
205 comb += dcache.wb_in.stall.eq(dbus.stall)
206
207 # create a blip (single pulse) on valid read/write request
208 m.d.comb += self.d_validblip.eq(rising_edge(m, self.d_valid))
209
210 # write out d data only when flag set
211 with m.If(self.d_w_valid):
212 m.d.sync += d_in.data.eq(self.store_data)
213 with m.Else():
214 m.d.sync += d_in.data.eq(0)
215
216 m.d.comb += d_in.load.eq(self.load)
217 m.d.comb += d_in.byte_sel.eq(self.byte_sel)
218 m.d.comb += d_in.addr.eq(self.addr)
219 m.d.comb += d_in.nc.eq(self.nc)
220 m.d.comb += self.done.eq(d_out.valid)
221 m.d.comb += self.load_data.eq(d_out.data)
222
223 return m
224
225 def ports(self):
226 yield from super().ports()
227 # TODO: memory ports
228
229
230 class TestSRAMLoadStore1(LoadStore1):
231 def __init__(self, pspec):
232 super().__init__(pspec)
233 pspec = self.pspec
234 # small 32-entry Memory
235 if (hasattr(pspec, "dmem_test_depth") and
236 isinstance(pspec.dmem_test_depth, int)):
237 depth = pspec.dmem_test_depth
238 else:
239 depth = 32
240 print("TestSRAMBareLoadStoreUnit depth", depth)
241
242 self.mem = Memory(width=pspec.reg_wid, depth=depth)
243
244 def elaborate(self, platform):
245 m = super().elaborate(platform)
246 comb = m.d.comb
247 m.submodules.sram = sram = SRAM(memory=self.mem, granularity=8,
248 features={'cti', 'bte', 'err'})
249 dbus = self.dbus
250
251 # directly connect the wishbone bus of LoadStoreUnitInterface to SRAM
252 # note: SRAM is a target (slave), dbus is initiator (master)
253 fanouts = ['dat_w', 'sel', 'cyc', 'stb', 'we', 'cti', 'bte']
254 fanins = ['dat_r', 'ack', 'err']
255 for fanout in fanouts:
256 print("fanout", fanout, getattr(sram.bus, fanout).shape(),
257 getattr(dbus, fanout).shape())
258 comb += getattr(sram.bus, fanout).eq(getattr(dbus, fanout))
259 comb += getattr(sram.bus, fanout).eq(getattr(dbus, fanout))
260 for fanin in fanins:
261 comb += getattr(dbus, fanin).eq(getattr(sram.bus, fanin))
262 # connect address
263 comb += sram.bus.adr.eq(dbus.adr)
264
265 return m
266