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