dae7dffca7be62bc1bf7460cc6a7b42856d7d598
3 based on microwatt loadstore1.vhdl
7 * https://bugs.libre-soc.org/show_bug.cgi?id=465
11 from nmigen
import (Elaboratable
, Module
, Signal
, Shape
, unsigned
, Cat
, Mux
,
14 from nmutil
.util
import rising_edge
15 from enum
import Enum
, unique
17 from soc
.experiment
.dcache
import DCache
18 from soc
.experiment
.pimem
import PortInterfaceBase
19 from soc
.experiment
.mem_types
import LoadStore1ToMMUType
20 from soc
.experiment
.mem_types
import MMUToLoadStore1Type
22 from soc
.minerva
.wishbone
import make_wb_layout
23 from soc
.bus
.sram
import SRAM
28 IDLE
= 0 # ready for instruction
29 ACK_WAIT
= 1 # waiting for ack from dcache
30 MMU_LOOKUP
= 2 # waiting for MMU to look up translation
31 TLBIE_WAIT
= 3 # waiting for MMU to finish doing a tlbie
32 COMPLETE
= 4 # extra cycle to complete an operation
35 # glue logic for microwatt mmu and dcache
36 class LoadStore1(PortInterfaceBase
):
37 def __init__(self
, pspec
):
39 self
.disable_cache
= (hasattr(pspec
, "disable_cache") and
40 pspec
.disable_cache
== True)
41 regwid
= pspec
.reg_wid
42 addrwid
= pspec
.addr_wid
44 super().__init
__(regwid
, addrwid
)
45 self
.dcache
= DCache()
46 self
.d_in
= self
.dcache
.d_in
47 self
.d_out
= self
.dcache
.d_out
48 self
.l_in
= LoadStore1ToMMUType()
49 self
.l_out
= MMUToLoadStore1Type()
51 self
.mmureq
= Signal()
52 self
.derror
= Signal()
54 # TODO, convert dcache wb_in/wb_out to "standard" nmigen Wishbone bus
55 self
.dbus
= Record(make_wb_layout(pspec
))
57 # for creating a single clock blip to DCache
58 self
.d_valid
= Signal()
59 self
.d_w_valid
= Signal()
60 self
.d_validblip
= Signal()
62 # DSISR and DAR cached values. note that the MMU FSM is where
63 # these are accessed by OP_MTSPR/OP_MFSPR, on behalf of LoadStore1.
64 # by contrast microwatt has the spr set/get done *in* loadstore1.vhdl
65 self
.dsisr
= Signal(64)
68 # state info for LD/ST
70 # latch most of the input request
74 self
.addr
= Signal(64)
75 self
.store_data
= Signal(64)
76 self
.load_data
= Signal(64)
77 self
.byte_sel
= Signal(8)
78 self
.update
= Signal()
79 #self.xerc : xer_common_t;
80 #self.reserve = Signal()
81 #self.atomic = Signal()
82 #self.atomic_last = Signal()
84 self
.nc
= Signal() # non-cacheable access
85 self
.virt_mode
= Signal()
86 self
.priv_mode
= Signal()
87 self
.state
= Signal(State
)
88 self
.instr_fault
= Signal()
89 self
.align_intr
= Signal()
91 self
.wait_dcache
= Signal()
92 self
.wait_mmu
= Signal()
93 #self.mode_32bit = Signal()
94 self
.wr_sel
= Signal(2)
95 self
.interrupt
= Signal()
96 #self.intr_vec : integer range 0 to 16#fff#;
97 #self.nia = Signal(64)
98 #self.srr1 = Signal(16)
100 def set_wr_addr(self
, m
, addr
, mask
, misalign
):
101 m
.d
.comb
+= self
.load
.eq(0) # store operation
103 m
.d
.comb
+= self
.d_in
.load
.eq(0)
104 m
.d
.comb
+= self
.byte_sel
.eq(mask
)
105 m
.d
.comb
+= self
.addr
.eq(addr
)
106 m
.d
.comb
+= self
.align_intr
.eq(misalign
)
107 # option to disable the cache entirely for write
108 if self
.disable_cache
:
109 m
.d
.comb
+= self
.nc
.eq(1)
112 def set_rd_addr(self
, m
, addr
, mask
, misalign
):
113 m
.d
.comb
+= self
.d_valid
.eq(1)
114 m
.d
.comb
+= self
.d_in
.valid
.eq(self
.d_validblip
)
115 m
.d
.comb
+= self
.load
.eq(1) # load operation
116 m
.d
.comb
+= self
.d_in
.load
.eq(1)
117 m
.d
.comb
+= self
.byte_sel
.eq(mask
)
118 m
.d
.comb
+= self
.align_intr
.eq(misalign
)
119 m
.d
.comb
+= self
.addr
.eq(addr
)
120 # BAD HACK! disable cacheing on LD when address is 0xCxxx_xxxx
121 # this is for peripherals. same thing done in Microwatt loadstore1.vhdl
122 with m
.If(addr
[28:] == Const(0xc, 4)):
123 m
.d
.comb
+= self
.nc
.eq(1)
124 # option to disable the cache entirely for read
125 if self
.disable_cache
:
126 m
.d
.comb
+= self
.nc
.eq(1)
127 return None #FIXME return value
129 def set_wr_data(self
, m
, data
, wen
):
130 # do the "blip" on write data
131 m
.d
.comb
+= self
.d_valid
.eq(1)
132 m
.d
.comb
+= self
.d_in
.valid
.eq(self
.d_validblip
)
133 # put data into comb which is picked up in main elaborate()
134 m
.d
.comb
+= self
.d_w_valid
.eq(1)
135 m
.d
.comb
+= self
.store_data
.eq(data
)
136 #m.d.sync += self.d_in.byte_sel.eq(wen) # this might not be needed
137 st_ok
= self
.done
# TODO indicates write data is valid
140 def get_rd_data(self
, m
):
141 ld_ok
= self
.done
# indicates read data is valid
142 data
= self
.load_data
# actual read data
146 if d_in.error = '1' then
147 if d_in.cache_paradox = '1' then
148 -- signal an interrupt straight away
150 dsisr(63 - 38) := not r2.req.load;
151 -- XXX there is no architected bit for this
152 -- (probably should be a machine check in fact)
153 dsisr(63 - 35) := d_in.cache_paradox;
155 -- Look up the translation for TLB miss
156 -- and also for permission error and RC error
157 -- in case the PTE has been updated.
159 v.state := MMU_LOOKUP;
165 def elaborate(self
, platform
):
166 m
= super().elaborate(platform
)
167 comb
, sync
= m
.d
.comb
, m
.d
.sync
169 # create dcache module
170 m
.submodules
.dcache
= dcache
= self
.dcache
173 d_in
, d_out
, l_out
, dbus
= self
.d_in
, self
.d_out
, self
.l_out
, self
.dbus
176 with m
.Switch(self
.state
):
177 with m
.Case(State
.IDLE
):
178 with m
.If(self
.d_validblip
):
179 sync
+= self
.state
.eq(State
.ACK_WAIT
)
181 with m
.Case(State
.ACK_WAIT
): # waiting for completion
182 with m
.If(d_out
.error
):
183 with m
.If(d_out
.cache_paradox
):
184 sync
+= self
.derror
.eq(1)
185 sync
+= self
.state
.eq(State
.IDLE
)
186 sync
+= self
.dsisr
[63 - 38].eq(~self
.load
)
187 # XXX there is no architected bit for this
188 # (probably should be a machine check in fact)
189 sync
+= self
.dsisr
[63 - 35].eq(d_out
.cache_paradox
)
192 # Look up the translation for TLB miss
193 # and also for permission error and RC error
194 # in case the PTE has been updated.
195 sync
+= self
.mmureq
.eq(1)
196 sync
+= self
.state
.eq(State
.MMU_LOOKUP
)
197 with m
.If(d_out
.valid
):
198 m
.d
.comb
+= self
.done
.eq(1)
199 sync
+= self
.state
.eq(State
.IDLE
)
200 with m
.If(self
.load
):
201 m
.d
.comb
+= self
.load_data
.eq(d_out
.data
)
203 with m
.Case(State
.MMU_LOOKUP
):
205 if m_in.done = '1' then
206 if r.instr_fault = '0' then
207 # retry the request now that the MMU has
208 #installed a TLB entry
210 if r.last_dword = '0' then
211 v.state := SECOND_REQ;
217 if m_in.err = '1' then
219 dsisr(63 - 33) := m_in.invalid;
220 dsisr(63 - 36) := m_in.perm_error;
221 dsisr(63 - 38) := not r.load;
222 dsisr(63 - 44) := m_in.badtree;
223 dsisr(63 - 45) := m_in.rc_error;
228 with m
.Case(State
.TLBIE_WAIT
):
230 with m
.Case(State
.COMPLETE
):
235 # happened, alignment, instr_fault, invalid.
236 # note that all of these flow through - eventually to the TRAP
237 # pipeline, via PowerDecoder2.
238 comb
+= exc
.happened
.eq(d_out
.error | l_out
.err | self
.align_intr
)
239 comb
+= exc
.invalid
.eq(l_out
.invalid
)
240 comb
+= exc
.alignment
.eq(self
.align_intr
)
242 # badtree, perm_error, rc_error, segment_fault
243 comb
+= exc
.badtree
.eq(l_out
.badtree
)
244 comb
+= exc
.perm_error
.eq(l_out
.perm_error
)
245 comb
+= exc
.rc_error
.eq(l_out
.rc_error
)
246 comb
+= exc
.segment_fault
.eq(l_out
.segerr
)
248 # TODO some exceptions set SPRs
250 # TODO, connect dcache wb_in/wb_out to "standard" nmigen Wishbone bus
251 comb
+= dbus
.adr
.eq(dcache
.wb_out
.adr
)
252 comb
+= dbus
.dat_w
.eq(dcache
.wb_out
.dat
)
253 comb
+= dbus
.sel
.eq(dcache
.wb_out
.sel
)
254 comb
+= dbus
.cyc
.eq(dcache
.wb_out
.cyc
)
255 comb
+= dbus
.stb
.eq(dcache
.wb_out
.stb
)
256 comb
+= dbus
.we
.eq(dcache
.wb_out
.we
)
258 comb
+= dcache
.wb_in
.dat
.eq(dbus
.dat_r
)
259 comb
+= dcache
.wb_in
.ack
.eq(dbus
.ack
)
260 if hasattr(dbus
, "stall"):
261 comb
+= dcache
.wb_in
.stall
.eq(dbus
.stall
)
263 # create a blip (single pulse) on valid read/write request
264 m
.d
.comb
+= self
.d_validblip
.eq(rising_edge(m
, self
.d_valid
))
266 # write out d data only when flag set
267 with m
.If(self
.d_w_valid
):
268 m
.d
.sync
+= d_in
.data
.eq(self
.store_data
)
270 m
.d
.sync
+= d_in
.data
.eq(0)
272 # this must move into the FSM, conditionally noticing that
273 # the "blip" comes from self.d_validblip.
274 # task 1: look up in dcache
275 # task 2: if dcache fails, look up in MMU.
276 # do **NOT** confuse the two.
277 m
.d
.comb
+= d_in
.load
.eq(self
.load
)
278 m
.d
.comb
+= d_in
.byte_sel
.eq(self
.byte_sel
)
279 m
.d
.comb
+= d_in
.addr
.eq(self
.addr
)
280 m
.d
.comb
+= d_in
.nc
.eq(self
.nc
)
282 # XXX these should be possible to remove but for some reason
283 # cannot be... yet. TODO, investigate
284 m
.d
.comb
+= self
.done
.eq(d_out
.valid
)
285 m
.d
.comb
+= self
.load_data
.eq(d_out
.data
)
290 yield from super().ports()
294 class TestSRAMLoadStore1(LoadStore1
):
295 def __init__(self
, pspec
):
296 super().__init
__(pspec
)
298 # small 32-entry Memory
299 if (hasattr(pspec
, "dmem_test_depth") and
300 isinstance(pspec
.dmem_test_depth
, int)):
301 depth
= pspec
.dmem_test_depth
304 print("TestSRAMBareLoadStoreUnit depth", depth
)
306 self
.mem
= Memory(width
=pspec
.reg_wid
, depth
=depth
)
308 def elaborate(self
, platform
):
309 m
= super().elaborate(platform
)
311 m
.submodules
.sram
= sram
= SRAM(memory
=self
.mem
, granularity
=8,
312 features
={'cti', 'bte', 'err'})
315 # directly connect the wishbone bus of LoadStoreUnitInterface to SRAM
316 # note: SRAM is a target (slave), dbus is initiator (master)
317 fanouts
= ['dat_w', 'sel', 'cyc', 'stb', 'we', 'cti', 'bte']
318 fanins
= ['dat_r', 'ack', 'err']
319 for fanout
in fanouts
:
320 print("fanout", fanout
, getattr(sram
.bus
, fanout
).shape(),
321 getattr(dbus
, fanout
).shape())
322 comb
+= getattr(sram
.bus
, fanout
).eq(getattr(dbus
, fanout
))
323 comb
+= getattr(sram
.bus
, fanout
).eq(getattr(dbus
, fanout
))
325 comb
+= getattr(dbus
, fanin
).eq(getattr(sram
.bus
, fanin
))
327 comb
+= sram
.bus
.adr
.eq(dbus
.adr
)