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
7 from nmigen_soc
.periph
import ConstantMap
9 from . import Peripheral
11 from ..cores
import litedram
14 __all__
= ["WritebackCache", "SDRAMPeripheral"]
17 class WritebackCache(Elaboratable
):
20 A write-back cache designed to bridge the SoC interconnect to LiteDRAM.
24 dram_port : :class:`litedram.NativePort`
29 Initiator bus data width.
31 Initiator bus granularity.
33 Dirty initialization. Defaults to ``False``. May be useful for simulation.
37 intr_bus : :class:`nmigen_soc.wishbone.Interface`
38 Initiator bus, with support for incremental bursts.
40 def __init__(self
, dram_port
, *, size
, data_width
, granularity
=None, dirty_init
=False):
41 if not isinstance(dram_port
, litedram
.NativePort
):
42 raise TypeError("DRAM port must be an instance of lambdasoc.cores.litedram.NativePort, "
45 if not isinstance(size
, int) or size
<= 0 or size
& size
- 1:
46 raise ValueError("Cache size must be a positive power of two integer, not {!r}"
48 if not isinstance(data_width
, int) or data_width
<= 0 or data_width
& data_width
- 1:
49 raise ValueError("Data width must be a positive power of two integer, not {!r}"
51 if dram_port
.data_width
% data_width
!= 0:
52 raise ValueError("DRAM port data width must be a multiple of data width, but {} is "
53 "not a multiple of {}"
54 .format(dram_port
.data_width
, data_width
))
56 self
.intr_bus
= wishbone
.Interface(
57 addr_width
= dram_port
.addr_width
+ log2_int(dram_port
.data_width
// data_width
),
58 data_width
= data_width
,
59 granularity
= granularity
,
60 features
= {"cti", "bte"},
63 addr_width
= self
.intr_bus
.addr_width
+ log2_int(data_width
// granularity
),
64 data_width
= granularity
,
67 intr_map
.add_window(dram_port
.memory_map
)
68 except AttributeError:
70 self
.intr_bus
.memory_map
= intr_map
72 self
.dram_port
= dram_port
75 self
.dirty_init
= bool(dirty_init
)
77 def elaborate(self
, platform
):
80 ratio
= self
.dram_port
.data_width
// self
.intr_bus
.data_width
81 nb_lines
= (self
.size
* self
.intr_bus
.granularity
) // self
.dram_port
.data_width
84 ("offset", log2_int(ratio
)),
85 ("line", log2_int(nb_lines
)),
86 ("tag", len(self
.intr_bus
.adr
) - log2_int(nb_lines
) - log2_int(ratio
)),
88 m
.d
.comb
+= intr_adr
.eq(self
.intr_bus
.adr
),
90 intr_adr_next
= Record
.like(intr_adr
)
92 with m
.Switch(self
.intr_bus
.bte
):
93 with m
.Case(wishbone
.BurstTypeExt
.LINEAR
):
94 m
.d
.comb
+= intr_adr_next
.eq(intr_adr
+ 1)
95 with m
.Case(wishbone
.BurstTypeExt
.WRAP_4
):
96 m
.d
.comb
+= intr_adr_next
[:2].eq(intr_adr
[:2] + 1)
97 m
.d
.comb
+= intr_adr_next
[2:].eq(intr_adr
[2:])
98 with m
.Case(wishbone
.BurstTypeExt
.WRAP_8
):
99 m
.d
.comb
+= intr_adr_next
[:3].eq(intr_adr
[:3] + 1)
100 m
.d
.comb
+= intr_adr_next
[3:].eq(intr_adr
[3:])
101 with m
.Case(wishbone
.BurstTypeExt
.WRAP_16
):
102 m
.d
.comb
+= intr_adr_next
[:4].eq(intr_adr
[:4] + 1)
103 m
.d
.comb
+= intr_adr_next
[4:].eq(intr_adr
[4:])
105 tag_rp_data
= Record([
106 ("tag", intr_adr
.tag
.shape()),
109 tag_wp_data
= Record
.like(tag_rp_data
)
111 tag_mem
= Memory(width
=len(tag_rp_data
), depth
=nb_lines
)
113 tag_mem
.init
= [-1 for _
in range(nb_lines
)]
115 m
.submodules
.tag_rp
= tag_rp
= tag_mem
.read_port(transparent
=False)
116 m
.submodules
.tag_wp
= tag_wp
= tag_mem
.write_port()
120 tag_rp_data
.eq(tag_rp
.data
),
121 tag_wp
.data
.eq(tag_wp_data
),
124 dat_mem
= Memory(width
=self
.dram_port
.data_width
, depth
=nb_lines
)
125 m
.submodules
.dat_rp
= dat_rp
= dat_mem
.read_port(transparent
=False)
126 m
.submodules
.dat_wp
= dat_wp
= dat_mem
.write_port(granularity
=self
.intr_bus
.granularity
)
129 intr_bus_r
= Record
.like(self
.intr_bus
)
130 intr_adr_r
= Record
.like(intr_adr
)
131 m
.d
.comb
+= intr_adr_r
.eq(intr_bus_r
.adr
)
134 with m
.State("CHECK"):
136 intr_bus_r
.cyc
.eq(self
.intr_bus
.cyc
),
137 intr_bus_r
.stb
.eq(self
.intr_bus
.stb
),
138 intr_bus_r
.adr
.eq(self
.intr_bus
.adr
),
140 # Tag/Data memory read
141 with m
.If(self
.intr_bus
.cyc
& self
.intr_bus
.stb
):
142 with m
.If(self
.intr_bus
.ack
& (self
.intr_bus
.cti
== wishbone
.CycleType
.INCR_BURST
)):
144 tag_rp
.addr
.eq(intr_adr_next
.line
),
145 dat_rp
.addr
.eq(intr_adr_next
.line
),
149 tag_rp
.addr
.eq(intr_adr
.line
),
150 dat_rp
.addr
.eq(intr_adr
.line
),
152 with m
.If(~intr_bus_r
.cyc | ~intr_bus_r
.stb | self
.intr_bus
.ack
):
158 self
.intr_bus
.dat_r
.eq(
159 dat_rp
.data
.word_select(intr_adr
.offset
, len(self
.intr_bus
.dat_r
))
162 # Tag/Data memory write
164 tag_wp
.addr
.eq(intr_adr
.line
),
165 tag_wp_data
.tag
.eq(intr_adr
.tag
),
166 tag_wp_data
.dirty
.eq(1),
167 dat_wp
.addr
.eq(intr_adr
.line
),
168 dat_wp
.data
.eq(Repl(self
.intr_bus
.dat_w
, ratio
)),
170 with m
.If(self
.intr_bus
.cyc
& self
.intr_bus
.stb
):
171 with m
.If(intr_adr
.tag
== tag_rp_data
.tag
):
172 m
.d
.comb
+= self
.intr_bus
.ack
.eq(intr_bus_r
.cyc
& intr_bus_r
.stb
)
173 with m
.If(self
.intr_bus
.we
& self
.intr_bus
.ack
):
176 dat_wp
.en
.word_select(intr_adr
.offset
, len(self
.intr_bus
.sel
)).eq(self
.intr_bus
.sel
),
178 with m
.Elif(intr_bus_r
.cyc
& intr_bus_r
.stb
):
180 intr_bus_r
.cyc
.eq(0),
181 intr_bus_r
.stb
.eq(0),
183 with m
.If(tag_rp_data
.dirty
):
188 with m
.State("EVICT"):
189 evict_done
= Record([("cmd", 1), ("w", 1)])
190 with m
.If(evict_done
.all()):
191 m
.d
.sync
+= evict_done
.eq(0)
195 self
.dram_port
.cmd
.valid
.eq(~evict_done
.cmd
),
196 self
.dram_port
.cmd
.last
.eq(0),
197 self
.dram_port
.cmd
.addr
.eq(Cat(intr_adr_r
.line
, tag_rp_data
.tag
)),
198 self
.dram_port
.cmd
.we
.eq(1),
200 with m
.If(self
.dram_port
.cmd
.valid
& self
.dram_port
.cmd
.ready
):
201 m
.d
.sync
+= evict_done
.cmd
.eq(1)
204 self
.dram_port
.w
.valid
.eq(~evict_done
.w
),
205 self
.dram_port
.w
.we
.eq(Repl(Const(1), self
.dram_port
.data_width
// 8)),
206 self
.dram_port
.w
.data
.eq(dat_rp
.data
),
208 with m
.If(self
.dram_port
.w
.valid
& self
.dram_port
.w
.ready
):
209 m
.d
.sync
+= evict_done
.w
.eq(1)
211 with m
.State("REFILL"):
212 refill_done
= Record([("cmd", 1), ("r", 1)])
213 with m
.If(refill_done
.all()):
214 m
.d
.sync
+= refill_done
.eq(0)
218 self
.dram_port
.cmd
.valid
.eq(~refill_done
.cmd
),
219 self
.dram_port
.cmd
.last
.eq(1),
220 self
.dram_port
.cmd
.addr
.eq(Cat(intr_adr_r
.line
, intr_adr_r
.tag
)),
221 self
.dram_port
.cmd
.we
.eq(0),
223 with m
.If(self
.dram_port
.cmd
.valid
& self
.dram_port
.cmd
.ready
):
224 m
.d
.sync
+= refill_done
.cmd
.eq(1)
227 self
.dram_port
.r
.ready
.eq(~refill_done
.r
),
228 tag_wp
.addr
.eq(intr_adr_r
.line
),
229 tag_wp
.en
.eq((self
.dram_port
.r
.valid
& self
.dram_port
.r
.ready
)),
230 tag_wp_data
.tag
.eq(intr_adr_r
.tag
),
231 tag_wp_data
.dirty
.eq(0),
232 dat_wp
.addr
.eq(intr_adr_r
.line
),
233 dat_wp
.en
.eq(Repl((self
.dram_port
.r
.valid
& self
.dram_port
.r
.ready
), len(dat_wp
.en
))),
234 dat_wp
.data
.eq(self
.dram_port
.r
.data
),
236 with m
.If(self
.dram_port
.r
.valid
& self
.dram_port
.r
.ready
):
237 m
.d
.sync
+= refill_done
.r
.eq(1)
239 if platform
== "formal":
240 with m
.If(Initial()):
242 Assume(fsm
.ongoing("CHECK")),
243 Assume(~intr_bus_r
.cyc
),
244 Assume(~evict_done
.any()),
245 Assume(~refill_done
.any()),
251 class SDRAMPeripheral(Peripheral
, Elaboratable
):
252 """SDRAM controller peripheral.
256 core : :class:`litedram.Core`
259 Cache size, in bytes.
260 cache_dirty_init : boot
261 Initialize cache as dirty. Defaults to `False`.
263 def __init__(self
, *, core
, cache_size
, cache_dirty_init
=False):
266 if not isinstance(core
, litedram
.Core
):
267 raise TypeError("LiteDRAM core must be an instance of lambdasoc.cores.litedram.Core, "
272 data_width
= core
.ctrl_bus
.data_width
273 granularity
= core
.ctrl_bus
.granularity
274 granularity_bits
= log2_int(data_width
// granularity
)
276 # Data path : bridge -> cache -> LiteDRAM user port
278 self
._data
_bus
= self
.window(
279 addr_width
= core
.user_port
.addr_width
280 + log2_int(core
.user_port
.data_width
// 8)
282 data_width
= data_width
,
283 granularity
= granularity
,
284 features
= {"cti", "bte"},
286 data_map
= MemoryMap(
287 addr_width
= self
._data
_bus
.addr_width
+ granularity_bits
,
288 data_width
= granularity
,
292 self
._cache
= WritebackCache(
295 data_width
= data_width
,
296 granularity
= granularity
,
297 dirty_init
= cache_dirty_init
,
299 data_map
.add_window(self
._cache
.intr_bus
.memory_map
)
301 self
._data
_bus
.memory_map
= data_map
303 # Control path : bridge -> LiteDRAM control port
305 self
._ctrl
_bus
= self
.window(
306 addr_width
= core
._ctrl
_bus
.addr_width
,
307 data_width
= data_width
,
308 granularity
= granularity
,
311 ctrl_map
= MemoryMap(
312 addr_width
= self
._ctrl
_bus
.addr_width
+ granularity_bits
,
313 data_width
= granularity
,
317 ctrl_map
.add_window(core
.ctrl_bus
.memory_map
)
319 self
._ctrl
_bus
.memory_map
= ctrl_map
321 self
._bridge
= self
.bridge(data_width
=data_width
, granularity
=granularity
)
322 self
.bus
= self
._bridge
.bus
325 def constant_map(self
):
327 SIZE
= self
.core
.size
,
328 CACHE_SIZE
= self
._cache
.size
,
331 def elaborate(self
, platform
):
334 m
.submodules
.bridge
= self
._bridge
335 m
.submodules
.cache
= self
._cache
336 m
.submodules
.core
= self
.core
339 self
._cache
.intr_bus
.adr
.eq(self
._data
_bus
.adr
),
340 self
._cache
.intr_bus
.cyc
.eq(self
._data
_bus
.cyc
),
341 self
._cache
.intr_bus
.stb
.eq(self
._data
_bus
.stb
),
342 self
._cache
.intr_bus
.sel
.eq(self
._data
_bus
.sel
),
343 self
._cache
.intr_bus
.we
.eq(self
._data
_bus
.we
),
344 self
._cache
.intr_bus
.dat_w
.eq(self
._data
_bus
.dat_w
),
345 self
._cache
.intr_bus
.cti
.eq(self
._data
_bus
.cti
),
346 self
._cache
.intr_bus
.bte
.eq(self
._data
_bus
.bte
),
347 self
._data
_bus
.ack
.eq(self
._cache
.intr_bus
.ack
),
348 self
._data
_bus
.dat_r
.eq(self
._cache
.intr_bus
.dat_r
),
350 self
.core
.ctrl_bus
.adr
.eq(self
._ctrl
_bus
.adr
),
351 self
.core
.ctrl_bus
.cyc
.eq(self
._ctrl
_bus
.cyc
),
352 self
.core
.ctrl_bus
.stb
.eq(self
._ctrl
_bus
.stb
),
353 self
.core
.ctrl_bus
.sel
.eq(self
._ctrl
_bus
.sel
),
354 self
.core
.ctrl_bus
.we
.eq(self
._ctrl
_bus
.we
),
355 self
.core
.ctrl_bus
.dat_w
.eq(self
._ctrl
_bus
.dat_w
),
356 self
._ctrl
_bus
.ack
.eq(self
.core
.ctrl_bus
.ack
),
357 self
._ctrl
_bus
.dat_r
.eq(self
.core
.ctrl_bus
.dat_r
),