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
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
11 from soc
.minerva
.wishbone
import make_wb_layout
12 from soc
.bus
.sram
import SRAM
15 # glue logic for microwatt mmu and dcache
16 class LoadStore1(PortInterfaceBase
):
17 def __init__(self
, 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
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()
31 self
.mmureq
= Signal()
32 self
.derror
= Signal()
34 # TODO, convert dcache wb_in/wb_out to "standard" nmigen Wishbone bus
35 self
.dbus
= Record(make_wb_layout(pspec
))
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()
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)
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)
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
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
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
103 if d_in.error = '1' then
104 if d_in.cache_paradox = '1' then
105 -- signal an interrupt straight away
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;
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.
116 v.state := MMU_LOOKUP;
122 def elaborate(self
, platform
):
123 m
= super().elaborate(platform
)
126 # create dcache module
127 m
.submodules
.dcache
= dcache
= self
.dcache
130 d_out
, l_out
, dbus
= self
.d_out
, self
.l_out
, self
.dbus
132 with m
.If(d_out
.error
):
133 with m
.If(d_out
.cache_paradox
):
134 comb
+= self
.derror
.eq(1)
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)
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';
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
)
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
)
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
167 # TODO some exceptions set SPRs
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
)
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
)
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
))
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
)
189 m
.d
.sync
+= self
.d_in
.data
.eq(0)
194 yield from super().ports()
198 class TestSRAMLoadStore1(LoadStore1
):
199 def __init__(self
, pspec
):
200 super().__init
__(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
208 print("TestSRAMBareLoadStoreUnit depth", depth
)
210 self
.mem
= Memory(width
=pspec
.reg_wid
, depth
=depth
)
212 def elaborate(self
, platform
):
213 m
= super().elaborate(platform
)
215 m
.submodules
.sram
= sram
= SRAM(memory
=self
.mem
, granularity
=8,
216 features
={'cti', 'bte', 'err'})
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
))
229 comb
+= getattr(dbus
, fanin
).eq(getattr(sram
.bus
, fanin
))
231 comb
+= sram
.bus
.adr
.eq(dbus
.adr
)