extra parameterification of minerva LoadStoreUnits
[soc.git] / src / soc / minerva / units / loadstore.py
1 from nmigen import Elaboratable, Module, Signal, Record, Cat, Const, Mux
2 from nmigen.utils import log2_int
3 from nmigen.lib.fifo import SyncFIFO
4
5 from soc.minerva.cache import L1Cache
6 from soc.minerva.wishbone import wishbone_layout, WishboneArbiter, Cycle
7
8
9 __all__ = ["LoadStoreUnitInterface", "BareLoadStoreUnit",
10 "CachedLoadStoreUnit"]
11
12
13 class LoadStoreUnitInterface:
14 def __init__(self, addr_wid=32, mask_wid=4, data_wid=32):
15 self.dbus = Record(wishbone_layout)
16 self.mask_wid = mask_wid
17 self.addr_wid = addr_wid
18 self.data_wid = data_wid
19 self.adr_lsbs = log2_int_mask_wid) # LSBs of addr covered by mask
20 badwid = addr_wid-log2_int(self.adr_lsbs) # TODO: is this correct?
21
22 # INPUTS
23 self.x_addr_i = Signal(addr_wid) # address used for loads/stores
24 self.x_mask_i = Signal(mask_wid) # Mask of which bytes to write
25 self.x_ld_i = Signal() # set to do a memory load
26 self.x_st_i = Signal() # set to do a memory store
27 self.x_st_data_i = Signal(data_wid) # The data to write when storing
28
29 self.x_stall_i = Signal() # do nothing until low
30 self.x_valid_i = Signal() # Whether x pipeline stage is
31 # currently enabled (I
32 # think?). Set to 1 for #now
33 self.m_stall_i = Signal() # do nothing until low
34 self.m_valid_i = Signal() # Whether m pipeline stage is
35 # currently enabled. Set
36 # to 1 for now
37
38 # OUTPUTS
39 self.x_busy_o = Signal() # set when the memory is busy
40 self.m_busy_o = Signal() # set when the memory is busy
41
42 self.m_ld_data_o = Signal(data_wid) # Data returned from memory read
43 # Data validity is NOT indicated by m_valid_i or x_valid_i as
44 # those are inputs. I believe it is valid on the next cycle
45 # after raising m_load where busy is low
46
47 self.m_load_err_o = Signal() # if there was an error when loading
48 self.m_store_err_o = Signal() # if there was an error when storing
49 self.m_badaddr_o = Signal(badwid) # The address of the load/store error
50
51
52 class BareLoadStoreUnit(LoadStoreUnitInterface, Elaboratable):
53 def elaborate(self, platform):
54 m = Module()
55
56 with m.If(self.dbus.cyc):
57 with m.If(self.dbus.ack | self.dbus.err | ~self.m_valid_i):
58 m.d.sync += [
59 self.dbus.cyc.eq(0),
60 self.dbus.stb.eq(0),
61 self.m_ld_data_o.eq(self.dbus.dat_r)
62 ]
63 with m.Elif((self.x_ld_i | self.x_st_i) &
64 self.x_valid_i & ~self.x_stall_i):
65 m.d.sync += [
66 self.dbus.cyc.eq(1),
67 self.dbus.stb.eq(1),
68 self.dbus.adr.eq(self.x_addr_i[self.adr_lsbs:]),
69 self.dbus.sel.eq(self.x_mask_i),
70 self.dbus.we.eq(self.x_st_i),
71 self.dbus.dat_w.eq(self.x_st_data_i)
72 ]
73
74 with m.If(self.dbus.cyc & self.dbus.err):
75 m.d.sync += [
76 self.m_load_err_o.eq(~self.dbus.we),
77 self.m_store_err_o.eq(self.dbus.we),
78 self.m_badaddr_o.eq(self.dbus.adr)
79 ]
80 with m.Elif(~self.m_stall_i):
81 m.d.sync += [
82 self.m_load_err_o.eq(0),
83 self.m_store_err_o.eq(0)
84 ]
85
86 m.d.comb += self.x_busy_o.eq(self.dbus.cyc)
87
88 with m.If(self.m_load_err_o | self.m_store_err_o):
89 m.d.comb += self.m_busy_o.eq(0)
90 with m.Else():
91 m.d.comb += self.m_busy_o.eq(self.dbus.cyc)
92
93 return m
94
95
96 class CachedLoadStoreUnit(LoadStoreUnitInterface, Elaboratable):
97 def __init__(self, *dcache_args, addr_wid=32, mask_wid=4, data_wid=32):
98 super().__init__(addr_wid, mask_wid, data_wid)
99
100 self.dcache_args = dcache_args
101
102 self.x_fence_i = Signal()
103 self.x_flush = Signal()
104 self.m_addr = Signal(addr_wid)
105 self.m_load = Signal()
106 self.m_store = Signal()
107
108 def elaborate(self, platform):
109 m = Module()
110
111 dcache = m.submodules.dcache = L1Cache(*self.dcache_args)
112
113 x_dcache_select = Signal()
114 m_dcache_select = Signal()
115
116 m.d.comb += x_dcache_select.eq((self.x_addr_i >= dcache.base) &
117 (self.x_addr_i < dcache.limit))
118 with m.If(~self.x_stall_i):
119 m.d.sync += m_dcache_select.eq(x_dcache_select)
120
121 m.d.comb += [
122 dcache.s1_addr.eq(self.x_addr_i[self.adr_lsbs:]),
123 dcache.s1_flush.eq(self.x_flush),
124 dcache.s1_stall.eq(self.x_stall_i),
125 dcache.s1_valid.eq(self.x_valid_i & x_dcache_select),
126 dcache.s2_addr.eq(self.m_addr[self.adr_lsbs:]),
127 dcache.s2_re.eq(self.m_load),
128 dcache.s2_evict.eq(self.m_store),
129 dcache.s2_valid.eq(self.m_valid_i & m_dcache_select)
130 ]
131
132 wrbuf_w_data = Record([("addr", self.addr_wid-self.adr_lsbs),
133 ("mask", self.mask_wid),
134 ("data", self.data_wid)])
135 wrbuf_r_data = Record.like(wrbuf_w_data)
136 wrbuf = m.submodules.wrbuf = SyncFIFO(width=len(wrbuf_w_data),
137 depth=dcache.nwords)
138 m.d.comb += [
139 wrbuf.w_data.eq(wrbuf_w_data),
140 wrbuf_w_data.addr.eq(self.x_addr_i[self.adr_lsbs:]),
141 wrbuf_w_data.mask.eq(self.x_mask_i),
142 wrbuf_w_data.data.eq(self.x_st_data_i),
143 wrbuf.w_en.eq(self.x_st_i & self.x_valid_i &
144 x_dcache_select & ~self.x_stall_i),
145 wrbuf_r_data.eq(wrbuf.r_data),
146 ]
147
148 dbus_arbiter = m.submodules.dbus_arbiter = WishboneArbiter()
149 m.d.comb += dbus_arbiter.bus.connect(self.dbus)
150
151 wrbuf_port = dbus_arbiter.port(priority=0)
152 with m.If(wrbuf_port.cyc):
153 with m.If(wrbuf_port.ack | wrbuf_port.err):
154 m.d.sync += [
155 wrbuf_port.cyc.eq(0),
156 wrbuf_port.stb.eq(0)
157 ]
158 m.d.comb += wrbuf.r_en.eq(1)
159 with m.Elif(wrbuf.r_rdy):
160 m.d.sync += [
161 wrbuf_port.cyc.eq(1),
162 wrbuf_port.stb.eq(1),
163 wrbuf_port.adr.eq(wrbuf_r_data.addr),
164 wrbuf_port.sel.eq(wrbuf_r_data.mask),
165 wrbuf_port.dat_w.eq(wrbuf_r_data.data)
166 ]
167 m.d.comb += wrbuf_port.we.eq(Const(1))
168
169 dcache_port = dbus_arbiter.port(priority=1)
170 cti = Mux(dcache.bus_last, Cycle.END, Cycle.INCREMENT)
171 m.d.comb += [
172 dcache_port.cyc.eq(dcache.bus_re),
173 dcache_port.stb.eq(dcache.bus_re),
174 dcache_port.adr.eq(dcache.bus_addr),
175 dcache_port.cti.eq(cti),
176 dcache_port.bte.eq(Const(log2_int(dcache.nwords) - 1)),
177 dcache.bus_valid.eq(dcache_port.ack),
178 dcache.bus_error.eq(dcache_port.err),
179 dcache.bus_rdata.eq(dcache_port.dat_r)
180 ]
181
182 bare_port = dbus_arbiter.port(priority=2)
183 bare_rdata = Signal.like(bare_port.dat_r)
184 with m.If(bare_port.cyc):
185 with m.If(bare_port.ack | bare_port.err | ~self.m_valid_i):
186 m.d.sync += [
187 bare_port.cyc.eq(0),
188 bare_port.stb.eq(0),
189 bare_rdata.eq(bare_port.dat_r)
190 ]
191 with m.Elif((self.x_ld_i | self.x_st_i) &
192 ~x_dcache_select & self.x_valid_i & ~self.x_stall_i):
193 m.d.sync += [
194 bare_port.cyc.eq(1),
195 bare_port.stb.eq(1),
196 bare_port.adr.eq(self.x_addr_i[self.adr_lsbs:]),
197 bare_port.sel.eq(self.x_mask_i),
198 bare_port.we.eq(self.x_st_i),
199 bare_port.dat_w.eq(self.x_st_data_i)
200 ]
201
202 with m.If(self.dbus.cyc & self.dbus.err):
203 m.d.sync += [
204 self.m_load_err_o.eq(~self.dbus.we),
205 self.m_store_err_o.eq(self.dbus.we),
206 self.m_badaddr_o.eq(self.dbus.adr)
207 ]
208 with m.Elif(~self.m_stall_i):
209 m.d.sync += [
210 self.m_load_err_o.eq(0),
211 self.m_store_err_o.eq(0)
212 ]
213
214 with m.If(self.x_fence_i):
215 m.d.comb += self.x_busy_o.eq(wrbuf.r_rdy)
216 with m.Elif(x_dcache_select):
217 m.d.comb += self.x_busy_o.eq(self.x_st_i & ~wrbuf.w_rdy)
218 with m.Else():
219 m.d.comb += self.x_busy_o.eq(bare_port.cyc)
220
221 with m.If(self.m_load_err_o | self.m_store_err_o):
222 m.d.comb += [
223 self.m_busy_o.eq(0),
224 self.m_ld_data_o.eq(0)
225 ]
226 with m.Elif(m_dcache_select):
227 m.d.comb += [
228 self.m_busy_o.eq(dcache.s2_re & dcache.s2_miss),
229 self.m_ld_data_o.eq(dcache.s2_rdata)
230 ]
231 with m.Else():
232 m.d.comb += [
233 self.m_busy_o.eq(bare_port.cyc),
234 self.m_ld_data_o.eq(bare_rdata)
235 ]
236
237 return m