2 from nmigen
.asserts
import *
3 from nmigen
.utils
import log2_int
5 from nmigen_soc
import wishbone
6 from nmigen_soc
.memory
import MemoryMap
8 from . import Peripheral
10 from ..cores
import litedram
13 __all__
= ["WritebackCache", "SDRAMPeripheral"]
16 class WritebackCache(Elaboratable
):
19 A write-back cache designed to bridge the SoC interconnect to LiteDRAM.
23 dram_port : :class:`litedram.NativePort`
28 Initiator bus data width.
30 Initiator bus granularity.
32 Dirty initialization. Defaults to ``False``. May be useful for simulation.
36 intr_bus : :class:`nmigen_soc.wishbone.Interface`
37 Initiator bus, with support for incremental bursts.
39 def __init__(self
, dram_port
, *, size
, data_width
, granularity
=None, dirty_init
=False):
40 if not isinstance(dram_port
, litedram
.NativePort
):
41 raise TypeError("DRAM port must be an instance of lambdasoc.cores.litedram.NativePort, "
44 if not isinstance(size
, int) or size
<= 0 or size
& size
- 1:
45 raise ValueError("Cache size must be a positive power of two integer, not {!r}"
47 if not isinstance(data_width
, int) or data_width
<= 0 or data_width
& data_width
- 1:
48 raise ValueError("Data width must be a positive power of two integer, not {!r}"
50 if dram_port
.data_width
% data_width
!= 0:
51 raise ValueError("DRAM port data width must be a multiple of data width, but {} is "
52 "not a multiple of {}"
53 .format(dram_port
.data_width
, data_width
))
55 self
.intr_bus
= wishbone
.Interface(
56 addr_width
= dram_port
.addr_width
+ log2_int(dram_port
.data_width
// data_width
),
57 data_width
= data_width
,
58 granularity
= granularity
,
59 features
= {"cti", "bte"},
62 addr_width
= self
.intr_bus
.addr_width
+ log2_int(data_width
// granularity
),
63 data_width
= granularity
,
66 intr_map
.add_window(dram_port
.memory_map
)
67 except AttributeError:
69 self
.intr_bus
.memory_map
= intr_map
71 self
.dram_port
= dram_port
74 self
.dirty_init
= bool(dirty_init
)
76 def elaborate(self
, platform
):
79 ratio
= self
.dram_port
.data_width
// self
.intr_bus
.data_width
80 nb_lines
= (self
.size
* self
.intr_bus
.granularity
) // self
.dram_port
.data_width
83 ("offset", log2_int(ratio
)),
84 ("line", log2_int(nb_lines
)),
85 ("tag", len(self
.intr_bus
.adr
) - log2_int(nb_lines
) - log2_int(ratio
)),
87 m
.d
.comb
+= intr_adr
.eq(self
.intr_bus
.adr
),
89 intr_adr_next
= Record
.like(intr_adr
)
91 with m
.Switch(self
.intr_bus
.bte
):
92 with m
.Case(wishbone
.BurstTypeExt
.LINEAR
):
93 m
.d
.comb
+= intr_adr_next
.eq(intr_adr
+ 1)
94 with m
.Case(wishbone
.BurstTypeExt
.WRAP_4
):
95 m
.d
.comb
+= intr_adr_next
[:2].eq(intr_adr
[:2] + 1)
96 m
.d
.comb
+= intr_adr_next
[2:].eq(intr_adr
[2:])
97 with m
.Case(wishbone
.BurstTypeExt
.WRAP_8
):
98 m
.d
.comb
+= intr_adr_next
[:3].eq(intr_adr
[:3] + 1)
99 m
.d
.comb
+= intr_adr_next
[3:].eq(intr_adr
[3:])
100 with m
.Case(wishbone
.BurstTypeExt
.WRAP_16
):
101 m
.d
.comb
+= intr_adr_next
[:4].eq(intr_adr
[:4] + 1)
102 m
.d
.comb
+= intr_adr_next
[4:].eq(intr_adr
[4:])
104 tag_rp_data
= Record([
105 ("tag", intr_adr
.tag
.shape()),
108 tag_wp_data
= Record
.like(tag_rp_data
)
110 tag_mem
= Memory(width
=len(tag_rp_data
), depth
=nb_lines
)
112 tag_mem
.init
= [-1 for _
in range(nb_lines
)]
114 m
.submodules
.tag_rp
= tag_rp
= tag_mem
.read_port(transparent
=False)
115 m
.submodules
.tag_wp
= tag_wp
= tag_mem
.write_port()
119 tag_rp_data
.eq(tag_rp
.data
),
120 tag_wp
.data
.eq(tag_wp_data
),
123 dat_mem
= Memory(width
=self
.dram_port
.data_width
, depth
=nb_lines
)
124 m
.submodules
.dat_rp
= dat_rp
= dat_mem
.read_port(transparent
=False)
125 m
.submodules
.dat_wp
= dat_wp
= dat_mem
.write_port(granularity
=self
.intr_bus
.granularity
)
128 intr_bus_r
= Record
.like(self
.intr_bus
)
129 intr_adr_r
= Record
.like(intr_adr
)
130 m
.d
.comb
+= intr_adr_r
.eq(intr_bus_r
.adr
)
133 with m
.State("CHECK"):
135 intr_bus_r
.cyc
.eq(self
.intr_bus
.cyc
),
136 intr_bus_r
.stb
.eq(self
.intr_bus
.stb
),
137 intr_bus_r
.adr
.eq(self
.intr_bus
.adr
),
139 # Tag/Data memory read
140 with m
.If(self
.intr_bus
.cyc
& self
.intr_bus
.stb
):
141 with m
.If(self
.intr_bus
.ack
& (self
.intr_bus
.cti
== wishbone
.CycleType
.INCR_BURST
)):
143 tag_rp
.addr
.eq(intr_adr_next
.line
),
144 dat_rp
.addr
.eq(intr_adr_next
.line
),
148 tag_rp
.addr
.eq(intr_adr
.line
),
149 dat_rp
.addr
.eq(intr_adr
.line
),
151 with m
.If(~intr_bus_r
.cyc | ~intr_bus_r
.stb | self
.intr_bus
.ack
):
157 self
.intr_bus
.dat_r
.eq(
158 dat_rp
.data
.word_select(intr_adr
.offset
, len(self
.intr_bus
.dat_r
))
161 # Tag/Data memory write
163 tag_wp
.addr
.eq(intr_adr
.line
),
164 tag_wp_data
.tag
.eq(intr_adr
.tag
),
165 tag_wp_data
.dirty
.eq(1),
166 dat_wp
.addr
.eq(intr_adr
.line
),
167 dat_wp
.data
.eq(Repl(self
.intr_bus
.dat_w
, ratio
)),
169 with m
.If(self
.intr_bus
.cyc
& self
.intr_bus
.stb
):
170 with m
.If(intr_adr
.tag
== tag_rp_data
.tag
):
171 m
.d
.comb
+= self
.intr_bus
.ack
.eq(intr_bus_r
.cyc
& intr_bus_r
.stb
)
172 with m
.If(self
.intr_bus
.we
& self
.intr_bus
.ack
):
175 dat_wp
.en
.word_select(intr_adr
.offset
, len(self
.intr_bus
.sel
)).eq(self
.intr_bus
.sel
),
177 with m
.Elif(intr_bus_r
.cyc
& intr_bus_r
.stb
):
179 intr_bus_r
.cyc
.eq(0),
180 intr_bus_r
.stb
.eq(0),
182 with m
.If(tag_rp_data
.dirty
):
187 with m
.State("EVICT"):
188 evict_done
= Record([("cmd", 1), ("w", 1)])
189 with m
.If(evict_done
.all()):
190 m
.d
.sync
+= evict_done
.eq(0)
194 self
.dram_port
.cmd
.valid
.eq(~evict_done
.cmd
),
195 self
.dram_port
.cmd
.last
.eq(0),
196 self
.dram_port
.cmd
.addr
.eq(Cat(intr_adr_r
.line
, tag_rp_data
.tag
)),
197 self
.dram_port
.cmd
.we
.eq(1),
199 with m
.If(self
.dram_port
.cmd
.valid
& self
.dram_port
.cmd
.ready
):
200 m
.d
.sync
+= evict_done
.cmd
.eq(1)
203 self
.dram_port
.w
.valid
.eq(~evict_done
.w
),
204 self
.dram_port
.w
.we
.eq(Repl(Const(1), self
.dram_port
.data_width
// 8)),
205 self
.dram_port
.w
.data
.eq(dat_rp
.data
),
207 with m
.If(self
.dram_port
.w
.valid
& self
.dram_port
.w
.ready
):
208 m
.d
.sync
+= evict_done
.w
.eq(1)
210 with m
.State("REFILL"):
211 refill_done
= Record([("cmd", 1), ("r", 1)])
212 with m
.If(refill_done
.all()):
213 m
.d
.sync
+= refill_done
.eq(0)
217 self
.dram_port
.cmd
.valid
.eq(~refill_done
.cmd
),
218 self
.dram_port
.cmd
.last
.eq(1),
219 self
.dram_port
.cmd
.addr
.eq(Cat(intr_adr_r
.line
, intr_adr_r
.tag
)),
220 self
.dram_port
.cmd
.we
.eq(0),
222 with m
.If(self
.dram_port
.cmd
.valid
& self
.dram_port
.cmd
.ready
):
223 m
.d
.sync
+= refill_done
.cmd
.eq(1)
226 self
.dram_port
.r
.ready
.eq(~refill_done
.r
),
227 tag_wp
.addr
.eq(intr_adr_r
.line
),
228 tag_wp
.en
.eq((self
.dram_port
.r
.valid
& self
.dram_port
.r
.ready
)),
229 tag_wp_data
.tag
.eq(intr_adr_r
.tag
),
230 tag_wp_data
.dirty
.eq(0),
231 dat_wp
.addr
.eq(intr_adr_r
.line
),
232 dat_wp
.en
.eq(Repl((self
.dram_port
.r
.valid
& self
.dram_port
.r
.ready
), len(dat_wp
.en
))),
233 dat_wp
.data
.eq(self
.dram_port
.r
.data
),
235 with m
.If(self
.dram_port
.r
.valid
& self
.dram_port
.r
.ready
):
236 m
.d
.sync
+= refill_done
.r
.eq(1)
238 if platform
== "formal":
239 with m
.If(Initial()):
241 Assume(fsm
.ongoing("CHECK")),
242 Assume(~intr_bus_r
.cyc
),
243 Assume(~evict_done
.any()),
244 Assume(~refill_done
.any()),
250 class SDRAMPeripheral(Peripheral
, Elaboratable
):
251 """SDRAM controller peripheral.
255 core : :class:`litedram.Core`
258 Cache size, in bytes.
259 cache_dirty_init : boot
260 Initialize cache as dirty. Defaults to `False`.
262 def __init__(self
, *, core
, cache_size
, cache_dirty_init
=False):
265 if not isinstance(core
, litedram
.Core
):
266 raise TypeError("LiteDRAM core must be an instance of lambdasoc.cores.litedram.Core, "
271 data_width
= core
.ctrl_bus
.data_width
272 granularity
= core
.ctrl_bus
.granularity
274 self
._data
_bus
= self
.window(
275 addr_width
= core
.user_port
.addr_width
276 + log2_int(core
.user_port
.data_width
// 8)
277 - log2_int(data_width
// granularity
),
278 data_width
= data_width
,
279 granularity
= granularity
,
280 features
= {"cti", "bte"},
282 self
._ctrl
_bus
= self
.window(
283 addr_width
= core
._ctrl
_bus
.addr_width
,
284 data_width
= core
._ctrl
_bus
.data_width
,
285 granularity
= core
._ctrl
_bus
.granularity
,
289 self
._cache
= WritebackCache(
292 data_width
= data_width
,
293 granularity
= granularity
,
294 dirty_init
= cache_dirty_init
,
297 self
._ctrl
_bus
.memory_map
.add_window(core
.ctrl_bus
.memory_map
)
298 self
._data
_bus
.memory_map
.add_window(self
._cache
.intr_bus
.memory_map
)
300 self
._bridge
= self
.bridge(data_width
=data_width
, granularity
=granularity
)
301 self
.bus
= self
._bridge
.bus
303 def elaborate(self
, platform
):
306 m
.submodules
.bridge
= self
._bridge
307 m
.submodules
.cache
= self
._cache
308 m
.submodules
.core
= self
.core
311 self
._cache
.intr_bus
.adr
.eq(self
._data
_bus
.adr
),
312 self
._cache
.intr_bus
.cyc
.eq(self
._data
_bus
.cyc
),
313 self
._cache
.intr_bus
.stb
.eq(self
._data
_bus
.stb
),
314 self
._cache
.intr_bus
.sel
.eq(self
._data
_bus
.sel
),
315 self
._cache
.intr_bus
.we
.eq(self
._data
_bus
.we
),
316 self
._cache
.intr_bus
.dat_w
.eq(self
._data
_bus
.dat_w
),
317 self
._cache
.intr_bus
.cti
.eq(self
._data
_bus
.cti
),
318 self
._cache
.intr_bus
.bte
.eq(self
._data
_bus
.bte
),
319 self
._data
_bus
.ack
.eq(self
._cache
.intr_bus
.ack
),
320 self
._data
_bus
.dat_r
.eq(self
._cache
.intr_bus
.dat_r
),
322 self
.core
.ctrl_bus
.adr
.eq(self
._ctrl
_bus
.adr
),
323 self
.core
.ctrl_bus
.cyc
.eq(self
._ctrl
_bus
.cyc
),
324 self
.core
.ctrl_bus
.stb
.eq(self
._ctrl
_bus
.stb
),
325 self
.core
.ctrl_bus
.sel
.eq(self
._ctrl
_bus
.sel
),
326 self
.core
.ctrl_bus
.we
.eq(self
._ctrl
_bus
.we
),
327 self
.core
.ctrl_bus
.dat_w
.eq(self
._ctrl
_bus
.dat_w
),
328 self
._ctrl
_bus
.ack
.eq(self
.core
.ctrl_bus
.ack
),
329 self
._ctrl
_bus
.dat_r
.eq(self
.core
.ctrl_bus
.dat_r
),