integration/soc/add_sdram: update rules to connect main bus to dram.
[litex.git] / litex / soc / integration / soc.py
1 # This file is Copyright (c) 2014-2020 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
3 # This file is Copyright (c) 2019 Gabriel L. Somlo <somlo@cmu.edu>
4 # License: BSD
5
6 import logging
7 import time
8 import datetime
9 from math import log2, ceil
10
11 from migen import *
12
13 from litex.soc.cores import cpu
14 from litex.soc.cores.identifier import Identifier
15 from litex.soc.cores.timer import Timer
16 from litex.soc.cores.spi_flash import SpiFlash
17 from litex.soc.cores.spi import SPIMaster
18
19 from litex.soc.interconnect.csr import *
20 from litex.soc.interconnect import csr_bus
21 from litex.soc.interconnect import stream
22 from litex.soc.interconnect import wishbone
23 from litex.soc.interconnect import axi
24
25 logging.basicConfig(level=logging.INFO)
26
27 # Helpers ------------------------------------------------------------------------------------------
28
29 def auto_int(x):
30 return int(x, 0)
31
32 def colorer(s, color="bright"):
33 header = {
34 "bright": "\x1b[1m",
35 "green": "\x1b[32m",
36 "cyan": "\x1b[36m",
37 "red": "\x1b[31m",
38 "yellow": "\x1b[33m",
39 "underline": "\x1b[4m"}[color]
40 trailer = "\x1b[0m"
41 return header + str(s) + trailer
42
43 def build_time(with_time=True):
44 fmt = "%Y-%m-%d %H:%M:%S" if with_time else "%Y-%m-%d"
45 return datetime.datetime.fromtimestamp(time.time()).strftime(fmt)
46
47 # SoCConstant --------------------------------------------------------------------------------------
48
49 def SoCConstant(value):
50 return value
51
52 # SoCRegion ----------------------------------------------------------------------------------------
53
54 class SoCRegion:
55 def __init__(self, origin=None, size=None, mode="rw", cached=True, linker=False):
56 self.logger = logging.getLogger("SoCRegion")
57 self.origin = origin
58 self.size = size
59 if size != 2**log2_int(size, False):
60 self.logger.info("Region size {} internally from {} to {}.".format(
61 colorer("rounded", color="cyan"),
62 colorer("0x{:08x}".format(size)),
63 colorer("0x{:08x}".format(2**log2_int(size, False)))))
64 self.size_pow2 = 2**log2_int(size, False)
65 self.mode = mode
66 self.cached = cached
67 self.linker = linker
68
69 def decoder(self, bus):
70 origin = self.origin
71 size = self.size_pow2
72 if (origin & (size - 1)) != 0:
73 self.logger.error("Origin needs to be aligned on size:")
74 self.logger.error(self)
75 raise
76 if (origin == 0) and (size == 2**bus.address_width):
77 return lambda a : True
78 origin >>= int(log2(bus.data_width//8)) # bytes to words aligned
79 size >>= int(log2(bus.data_width//8)) # bytes to words aligned
80 return lambda a: (a[log2_int(size):] == (origin >> log2_int(size)))
81
82 def __str__(self):
83 r = ""
84 if self.origin is not None:
85 r += "Origin: {}, ".format(colorer("0x{:08x}".format(self.origin)))
86 if self.size is not None:
87 r += "Size: {}, ".format(colorer("0x{:08x}".format(self.size)))
88 r += "Mode: {}, ".format(colorer(self.mode.upper()))
89 r += "Cached: {} ".format(colorer(self.cached))
90 r += "Linker: {}".format(colorer(self.linker))
91 return r
92
93 class SoCIORegion(SoCRegion): pass
94
95 # SoCCSRRegion -------------------------------------------------------------------------------------
96
97 class SoCCSRRegion:
98 def __init__(self, origin, busword, obj):
99 self.origin = origin
100 self.busword = busword
101 self.obj = obj
102
103 # SoCBusHandler ------------------------------------------------------------------------------------
104
105 class SoCBusHandler(Module):
106 supported_standard = ["wishbone", "axi-lite"]
107 supported_data_width = [32, 64]
108 supported_address_width = [32]
109
110 # Creation -------------------------------------------------------------------------------------
111 def __init__(self, name="SoCBusHandler", standard="wishbone", data_width=32, address_width=32, timeout=1e6, reserved_regions={}):
112 self.logger = logging.getLogger(name)
113 self.logger.info("Creating Bus Handler...")
114
115 # Check Standard
116 if standard not in self.supported_standard:
117 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
118 colorer("Bus standard", color="red"),
119 colorer(standard),
120 colorer(", ".join(self.supported_standard))))
121 raise
122
123 # Check Data Width
124 if data_width not in self.supported_data_width:
125 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
126 colorer("Data Width", color="red"),
127 colorer(data_width),
128 colorer(", ".join(str(x) for x in self.supported_data_width))))
129 raise
130
131 # Check Address Width
132 if address_width not in self.supported_address_width:
133 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
134 colorer("Address Width", color="red"),
135 colorer(address_width),
136 colorer(", ".join(str(x) for x in self.supported_address_width))))
137 raise
138
139 # Create Bus
140 self.standard = standard
141 self.data_width = data_width
142 self.address_width = address_width
143 self.masters = {}
144 self.slaves = {}
145 self.regions = {}
146 self.io_regions = {}
147 self.timeout = timeout
148 self.logger.info("{}-bit {} Bus, {}GiB Address Space.".format(
149 colorer(data_width), colorer(standard), colorer(2**address_width/2**30)))
150
151 # Adding reserved regions
152 self.logger.info("Adding {} Bus Regions...".format(colorer("reserved", color="cyan")))
153 for name, region in reserved_regions.items():
154 if isinstance(region, int):
155 region = SoCRegion(origin=region, size=0x1000000)
156 self.add_region(name, region)
157
158 self.logger.info("Bus Handler {}.".format(colorer("created", color="green")))
159
160 # Add/Allog/Check Regions ----------------------------------------------------------------------
161 def add_region(self, name, region):
162 allocated = False
163 if name in self.regions.keys() or name in self.io_regions.keys():
164 self.logger.error("{} already declared as Region:".format(colorer(name, color="red")))
165 self.logger.error(self)
166 raise
167 # Check if SoCIORegion
168 if isinstance(region, SoCIORegion):
169 self.io_regions[name] = region
170 overlap = self.check_regions_overlap(self.io_regions)
171 if overlap is not None:
172 self.logger.error("IO Region {} between {} and {}:".format(
173 colorer("overlap", color="red"),
174 colorer(overlap[0]),
175 colorer(overlap[1])))
176 self.logger.error(str(self.io_regions[overlap[0]]))
177 self.logger.error(str(self.io_regions[overlap[1]]))
178 raise
179 self.logger.info("{} Region {} at {}.".format(
180 colorer(name, color="underline"),
181 colorer("added", color="green"),
182 str(region)))
183 # Check if SoCRegion
184 elif isinstance(region, SoCRegion):
185 # If no origin specified, allocate region.
186 if region.origin is None:
187 allocated = True
188 region = self.alloc_region(name, region.size, region.cached)
189 self.regions[name] = region
190 # Else add region and check for overlaps.
191 else:
192 if not region.cached:
193 if not self.check_region_is_io(region):
194 self.logger.error("{} Region {}: {}.".format(
195 colorer(name),
196 colorer("not in IO region", color="red"),
197 str(region)))
198 self.logger.error(self)
199 raise
200 self.regions[name] = region
201 overlap = self.check_regions_overlap(self.regions)
202 if overlap is not None:
203 self.logger.error("Region {} between {} and {}:".format(
204 colorer("overlap", color="red"),
205 colorer(overlap[0]),
206 colorer(overlap[1])))
207 self.logger.error(str(self.regions[overlap[0]]))
208 self.logger.error(str(self.regions[overlap[1]]))
209 raise
210 self.logger.info("{} Region {} at {}.".format(
211 colorer(name, color="underline"),
212 colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
213 str(region)))
214 else:
215 self.logger.error("{} is not a supported Region.".format(colorer(name, color="red")))
216 raise
217
218 def alloc_region(self, name, size, cached=True):
219 self.logger.info("Allocating {} Region of size {}...".format(
220 colorer("Cached" if cached else "IO"),
221 colorer("0x{:08x}".format(size))))
222
223 # Limit Search Regions
224 if cached == False:
225 search_regions = self.io_regions
226 else:
227 search_regions = {"main": SoCRegion(origin=0x00000000, size=2**self.address_width-1)}
228
229 # Iterate on Search_Regions to find a Candidate
230 for _, search_region in search_regions.items():
231 origin = search_region.origin
232 while (origin + size) < (search_region.origin + search_region.size_pow2):
233 # Create a Candicate.
234 candidate = SoCRegion(origin=origin, size=size, cached=cached)
235 overlap = False
236 # Check Candidate does not overlap with allocated existing regions
237 for _, allocated in self.regions.items():
238 if self.check_regions_overlap({"0": allocated, "1": candidate}) is not None:
239 origin = allocated.origin + allocated.size_pow2
240 overlap = True
241 break
242 if not overlap:
243 # If no overlap, the Candidate is selected
244 return candidate
245
246 self.logger.error("Not enough Address Space to allocate Region.")
247 raise
248
249 def check_regions_overlap(self, regions, check_linker=False):
250 i = 0
251 while i < len(regions):
252 n0 = list(regions.keys())[i]
253 r0 = regions[n0]
254 for n1 in list(regions.keys())[i+1:]:
255 r1 = regions[n1]
256 if r0.linker or r1.linker:
257 if not check_linker:
258 continue
259 if r0.origin >= (r1.origin + r1.size_pow2):
260 continue
261 if r1.origin >= (r0.origin + r0.size_pow2):
262 continue
263 return (n0, n1)
264 i += 1
265 return None
266
267 def check_region_is_in(self, region, container):
268 is_in = True
269 if not (region.origin >= container.origin):
270 is_in = False
271 if not ((region.origin + region.size) < (container.origin + container.size)):
272 is_in = False
273 return is_in
274
275 def check_region_is_io(self, region):
276 is_io = False
277 for _, io_region in self.io_regions.items():
278 if self.check_region_is_in(region, io_region):
279 is_io = True
280 return is_io
281
282 # Add Master/Slave -----------------------------------------------------------------------------
283 def add_adapter(self, name, interface, direction="m2s"):
284 assert direction in ["m2s", "s2m"]
285
286 # Data width conversion
287 if interface.data_width != self.data_width:
288 interface_cls = type(interface)
289 converter_cls = {
290 wishbone.Interface: wishbone.Converter,
291 axi.AXILiteInterface: axi.AXILiteConverter,
292 }[interface_cls]
293 converted_interface = interface_cls(data_width=self.data_width)
294 if direction == "m2s":
295 master, slave = interface, converted_interface
296 elif direction == "s2m":
297 master, slave = converted_interface, interface
298 converter = converter_cls(master=master, slave=slave)
299 self.submodules += converter
300 else:
301 converted_interface = interface
302
303 # Wishbone <-> AXILite bridging
304 main_bus_cls = {
305 "wishbone": wishbone.Interface,
306 "axi-lite": axi.AXILiteInterface,
307 }[self.standard]
308 if isinstance(converted_interface, main_bus_cls):
309 bridged_interface = converted_interface
310 else:
311 bridged_interface = main_bus_cls(data_width=self.data_width)
312 if direction == "m2s":
313 master, slave = converted_interface, bridged_interface
314 elif direction == "s2m":
315 master, slave = bridged_interface, converted_interface
316 bridge_cls = {
317 (wishbone.Interface, axi.AXILiteInterface): axi.Wishbone2AXILite,
318 (axi.AXILiteInterface, wishbone.Interface): axi.AXILite2Wishbone,
319 }[type(master), type(slave)]
320 bridge = bridge_cls(master, slave)
321 self.submodules += bridge
322
323 if type(interface) != type(bridged_interface) or interface.data_width != bridged_interface.data_width:
324 fmt = "{name} Bus {converted} from {frombus} {frombits}-bit to {tobus} {tobits}-bit."
325 bus_names = {
326 wishbone.Interface: "Wishbone",
327 axi.AXILiteInterface: "AXI Lite",
328 }
329 self.logger.info(fmt.format(
330 name = colorer(name),
331 converted = colorer("converted", color="cyan"),
332 frombus = colorer(bus_names[type(interface)]),
333 frombits = colorer(interface.data_width),
334 tobus = colorer(bus_names[type(bridged_interface)]),
335 tobits = colorer(bridged_interface.data_width)))
336 return bridged_interface
337
338 def add_master(self, name=None, master=None):
339 if name is None:
340 name = "master{:d}".format(len(self.masters))
341 if name in self.masters.keys():
342 self.logger.error("{} {} as Bus Master:".format(
343 colorer(name),
344 colorer("already declared", color="red")))
345 self.logger.error(self)
346 raise
347 master = self.add_adapter(name, master, "m2s")
348 self.masters[name] = master
349 self.logger.info("{} {} as Bus Master.".format(
350 colorer(name, color="underline"),
351 colorer("added", color="green")))
352
353 def add_slave(self, name=None, slave=None, region=None):
354 no_name = name is None
355 no_region = region is None
356 if no_name and no_region:
357 self.logger.error("Please {} {} or/and {} of Bus Slave.".format(
358 colorer("specify", color="red"),
359 colorer("name"),
360 colorer("region")))
361 raise
362 if no_name:
363 name = "slave{:d}".format(len(self.slaves))
364 if no_region:
365 region = self.regions.get(name, None)
366 if region is None:
367 self.logger.error("{} Region {}.".format(
368 colorer(name),
369 colorer("not found", color="red")))
370 raise
371 else:
372 self.add_region(name, region)
373 if name in self.slaves.keys():
374 self.logger.error("{} {} as Bus Slave:".format(
375 colorer(name),
376 colorer("already declared", color="red")))
377 self.logger.error(self)
378 raise
379 slave = self.add_adapter(name, slave, "s2m")
380 self.slaves[name] = slave
381 self.logger.info("{} {} as Bus Slave.".format(
382 colorer(name, color="underline"),
383 colorer("added", color="green")))
384
385 # Str ------------------------------------------------------------------------------------------
386 def __str__(self):
387 r = "{}-bit {} Bus, {}GiB Address Space.\n".format(
388 colorer(self.data_width), colorer(self.standard), colorer(2**self.address_width/2**30))
389 r += "IO Regions: ({})\n".format(len(self.io_regions.keys())) if len(self.io_regions.keys()) else ""
390 io_regions = {k: v for k, v in sorted(self.io_regions.items(), key=lambda item: item[1].origin)}
391 for name, region in io_regions.items():
392 r += colorer(name, color="underline") + " "*(20-len(name)) + ": " + str(region) + "\n"
393 r += "Bus Regions: ({})\n".format(len(self.regions.keys())) if len(self.regions.keys()) else ""
394 regions = {k: v for k, v in sorted(self.regions.items(), key=lambda item: item[1].origin)}
395 for name, region in regions.items():
396 r += colorer(name, color="underline") + " "*(20-len(name)) + ": " + str(region) + "\n"
397 r += "Bus Masters: ({})\n".format(len(self.masters.keys())) if len(self.masters.keys()) else ""
398 for name in self.masters.keys():
399 r += "- {}\n".format(colorer(name, color="underline"))
400 r += "Bus Slaves: ({})\n".format(len(self.slaves.keys())) if len(self.slaves.keys()) else ""
401 for name in self.slaves.keys():
402 r += "- {}\n".format(colorer(name, color="underline"))
403 r = r[:-1]
404 return r
405
406 # SoCLocHandler --------------------------------------------------------------------------------------
407
408 class SoCLocHandler(Module):
409 # Creation -------------------------------------------------------------------------------------
410 def __init__(self, name, n_locs):
411 self.name = name
412 self.locs = {}
413 self.n_locs = n_locs
414
415 # Add ------------------------------------------------------------------------------------------
416 def add(self, name, n=None, use_loc_if_exists=False):
417 allocated = False
418 if not (use_loc_if_exists and name in self.locs.keys()):
419 if name in self.locs.keys():
420 self.logger.error("{} {} name {}.".format(
421 colorer(name), self.name, colorer("already used", color="red")))
422 self.logger.error(self)
423 raise
424 if n in self.locs.values():
425 self.logger.error("{} {} Location {}.".format(
426 colorer(n), self.name, colorer("already used", color="red")))
427 self.logger.error(self)
428 raise
429 if n is None:
430 allocated = True
431 n = self.alloc(name)
432 else:
433 if n < 0:
434 self.logger.error("{} {} Location should be {}.".format(
435 colorer(n),
436 self.name,
437 colorer("positive", color="red")))
438 raise
439 if n > self.n_locs:
440 self.logger.error("{} {} Location {} than maximum: {}.".format(
441 colorer(n),
442 self.name,
443 colorer("higher", color="red"),
444 colorer(self.n_locs)))
445 raise
446 self.locs[name] = n
447 else:
448 n = self.locs[name]
449 self.logger.info("{} {} {} at Location {}.".format(
450 colorer(name, color="underline"),
451 self.name,
452 colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
453 colorer(n)))
454
455 # Alloc ----------------------------------------------------------------------------------------
456 def alloc(self, name):
457 for n in range(self.n_locs):
458 if n not in self.locs.values():
459 return n
460 self.logger.error("Not enough Locations.")
461 self.logger.error(self)
462 raise
463
464 # Str ------------------------------------------------------------------------------------------
465 def __str__(self):
466 r = "{} Locations: ({})\n".format(self.name, len(self.locs.keys())) if len(self.locs.keys()) else ""
467 locs = {k: v for k, v in sorted(self.locs.items(), key=lambda item: item[1])}
468 length = 0
469 for name in locs.keys():
470 if len(name) > length: length = len(name)
471 for name in locs.keys():
472 r += "- {}{}: {}\n".format(colorer(name, color="underline"), " "*(length + 1 - len(name)), colorer(self.locs[name]))
473 return r
474
475 # SoCCSRHandler ------------------------------------------------------------------------------------
476
477 class SoCCSRHandler(SoCLocHandler):
478 supported_data_width = [8, 32]
479 supported_address_width = [14+i for i in range(4)]
480 supported_alignment = [32]
481 supported_paging = [0x800*2**i for i in range(4)]
482 supported_ordering = ["big", "little"]
483
484 # Creation -------------------------------------------------------------------------------------
485 def __init__(self, data_width=32, address_width=14, alignment=32, paging=0x800, ordering="big", reserved_csrs={}):
486 SoCLocHandler.__init__(self, "CSR", n_locs=alignment//8*(2**address_width)//paging)
487 self.logger = logging.getLogger("SoCCSRHandler")
488 self.logger.info("Creating CSR Handler...")
489
490 # Check Data Width
491 if data_width not in self.supported_data_width:
492 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
493 colorer("Data Width", color="red"),
494 colorer(data_width),
495 colorer(", ".join(str(x) for x in self.supported_data_width))))
496 raise
497
498 # Check Address Width
499 if address_width not in self.supported_address_width:
500 self.logger.error("Unsupported {} {} supporteds: {:s}".format(
501 colorer("Address Width", color="red"),
502 colorer(address_width),
503 colorer(", ".join(str(x) for x in self.supported_address_width))))
504 raise
505
506 # Check Alignment
507 if alignment not in self.supported_alignment:
508 self.logger.error("Unsupported {}: {} supporteds: {:s}".format(
509 colorer("Alignment", color="red"),
510 colorer(alignment),
511 colorer(", ".join(str(x) for x in self.supported_alignment))))
512 raise
513 if data_width > alignment:
514 self.logger.error("Alignment ({}) {} Data Width ({})".format(
515 colorer(alignment),
516 colorer("should be >=", color="red"),
517 colorer(data_width)))
518 raise
519
520 # Check Paging
521 if paging not in self.supported_paging:
522 self.logger.error("Unsupported {} 0x{}, supporteds: {:s}".format(
523 colorer("Paging", color="red"),
524 colorer("{:x}".format(paging)),
525 colorer(", ".join("0x{:x}".format(x) for x in self.supported_paging))))
526 raise
527
528 # Check Ordering
529 if ordering not in self.supported_ordering:
530 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
531 colorer("Ordering", color="red"),
532 colorer("{}".format(paging)),
533 colorer(", ".join("{}".format(x) for x in self.supported_ordering))))
534 raise
535
536 # Create CSR Handler
537 self.data_width = data_width
538 self.address_width = address_width
539 self.alignment = alignment
540 self.paging = paging
541 self.ordering = ordering
542 self.masters = {}
543 self.regions = {}
544 self.logger.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging, {} Ordering (Up to {} Locations).".format(
545 colorer(self.data_width),
546 colorer(self.alignment),
547 colorer(2**self.address_width/2**10),
548 colorer(self.paging),
549 colorer(self.ordering),
550 colorer(self.n_locs)))
551
552 # Adding reserved CSRs
553 self.logger.info("Adding {} CSRs...".format(colorer("reserved", color="cyan")))
554 for name, n in reserved_csrs.items():
555 self.add(name, n)
556
557 self.logger.info("CSR Handler {}.".format(colorer("created", color="green")))
558
559 # Add Master -----------------------------------------------------------------------------------
560 def add_master(self, name=None, master=None):
561 if name is None:
562 name = "master{:d}".format(len(self.masters))
563 if name in self.masters.keys():
564 self.logger.error("{} {} as CSR Master:".format(
565 colorer(name),
566 colorer("already declared", color="red")))
567 self.logger.error(self)
568 raise
569 if master.data_width != self.data_width:
570 self.logger.error("{} Master/Handler Data Width {} ({} vs {}).".format(
571 colorer(name),
572 colorer("missmatch", color="red"),
573 colorer(master.data_width),
574 colorer(self.data_width)))
575 raise
576 self.masters[name] = master
577 self.logger.info("{} {} as CSR Master.".format(
578 colorer(name, color="underline"),
579 colorer("added", color="green")))
580
581 # Add Region -----------------------------------------------------------------------------------
582 def add_region(self, name, region):
583 # FIXME: add checks
584 self.regions[name] = region
585
586 # Address map ----------------------------------------------------------------------------------
587 def address_map(self, name, memory):
588 if memory is not None:
589 name = name + "_" + memory.name_override
590 if self.locs.get(name, None) is None:
591 self.logger.error("CSR {} {}.".format(
592 colorer(name),
593 colorer("not found", color="red")))
594 self.logger.error(self)
595 raise
596 return self.locs[name]
597
598 # Str ------------------------------------------------------------------------------------------
599 def __str__(self):
600 r = "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging, {} Ordering (Up to {} Locations).\n".format(
601 colorer(self.data_width),
602 colorer(self.alignment),
603 colorer(2**self.address_width/2**10),
604 colorer(self.paging),
605 colorer(self.ordering),
606 colorer(self.n_locs))
607 r += SoCLocHandler.__str__(self)
608 r = r[:-1]
609 return r
610
611 # SoCIRQHandler ------------------------------------------------------------------------------------
612
613 class SoCIRQHandler(SoCLocHandler):
614 # Creation -------------------------------------------------------------------------------------
615 def __init__(self, n_irqs=32, reserved_irqs={}):
616 SoCLocHandler.__init__(self, "IRQ", n_locs=n_irqs)
617 self.logger = logging.getLogger("SoCIRQHandler")
618 self.logger.info("Creating IRQ Handler...")
619
620 # Check IRQ Number
621 if n_irqs > 32:
622 self.logger.error("Unsupported IRQs number: {} supporteds: {:s}".format(
623 colorer(n, color="red"), colorer("Up to 32", color="green")))
624 raise
625
626 # Create IRQ Handler
627 self.logger.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs)))
628
629 # Adding reserved IRQs
630 self.logger.info("Adding {} IRQs...".format(colorer("reserved", color="cyan")))
631 for name, n in reserved_irqs.items():
632 self.add(name, n)
633
634 self.logger.info("IRQ Handler {}.".format(colorer("created", color="green")))
635
636 # Str ------------------------------------------------------------------------------------------
637 def __str__(self):
638 r ="IRQ Handler (up to {} Locations).\n".format(colorer(self.n_locs))
639 r += SoCLocHandler.__str__(self)
640 r = r[:-1]
641 return r
642
643 # SoCController ------------------------------------------------------------------------------------
644
645 class SoCController(Module, AutoCSR):
646 def __init__(self,
647 with_reset = True,
648 with_scratch = True,
649 with_errors = True):
650
651 if with_reset:
652 self._reset = CSRStorage(1, description="""Write a ``1`` to this register to reset the SoC.""")
653 if with_scratch:
654 self._scratch = CSRStorage(32, reset=0x12345678, description="""
655 Use this register as a scratch space to verify that software read/write accesses
656 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
657 can be used to verify endianness.""")
658 if with_errors:
659 self._bus_errors = CSRStatus(32, description="Total number of Wishbone bus errors (timeouts) since start.")
660
661 # # #
662
663 # Reset
664 if with_reset:
665 self.reset = Signal()
666 self.comb += self.reset.eq(self._reset.re)
667
668 # Errors
669 if with_errors:
670 self.bus_error = Signal()
671 bus_errors = Signal(32)
672 self.sync += [
673 If(bus_errors != (2**len(bus_errors)-1),
674 If(self.bus_error, bus_errors.eq(bus_errors + 1))
675 )
676 ]
677 self.comb += self._bus_errors.status.eq(bus_errors)
678
679 # SoC ----------------------------------------------------------------------------------------------
680
681 class SoC(Module):
682 mem_map = {}
683 def __init__(self, platform, sys_clk_freq,
684 bus_standard = "wishbone",
685 bus_data_width = 32,
686 bus_address_width = 32,
687 bus_timeout = 1e6,
688 bus_reserved_regions = {},
689
690 csr_data_width = 32,
691 csr_address_width = 14,
692 csr_paging = 0x800,
693 csr_ordering = "big",
694 csr_reserved_csrs = {},
695
696 irq_n_irqs = 32,
697 irq_reserved_irqs = {},
698 ):
699
700 self.logger = logging.getLogger("SoC")
701 self.logger.info(colorer(" __ _ __ _ __ ", color="bright"))
702 self.logger.info(colorer(" / / (_) /____ | |/_/ ", color="bright"))
703 self.logger.info(colorer(" / /__/ / __/ -_)> < ", color="bright"))
704 self.logger.info(colorer(" /____/_/\\__/\\__/_/|_| ", color="bright"))
705 self.logger.info(colorer(" Build your hardware, easily!", color="bright"))
706
707 self.logger.info(colorer("-"*80, color="bright"))
708 self.logger.info(colorer("Creating SoC... ({})".format(build_time())))
709 self.logger.info(colorer("-"*80, color="bright"))
710 self.logger.info("FPGA device : {}.".format(platform.device))
711 self.logger.info("System clock: {:3.2f}MHz.".format(sys_clk_freq/1e6))
712
713 # SoC attributes ---------------------------------------------------------------------------
714 self.platform = platform
715 self.sys_clk_freq = sys_clk_freq
716 self.constants = {}
717 self.csr_regions = {}
718
719 # SoC Bus Handler --------------------------------------------------------------------------
720 self.submodules.bus = SoCBusHandler(
721 standard = bus_standard,
722 data_width = bus_data_width,
723 address_width = bus_address_width,
724 timeout = bus_timeout,
725 reserved_regions = bus_reserved_regions,
726 )
727
728 # SoC Bus Handler --------------------------------------------------------------------------
729 self.submodules.csr = SoCCSRHandler(
730 data_width = csr_data_width,
731 address_width = csr_address_width,
732 alignment = 32,
733 paging = csr_paging,
734 ordering = csr_ordering,
735 reserved_csrs = csr_reserved_csrs,
736 )
737
738 # SoC IRQ Handler --------------------------------------------------------------------------
739 self.submodules.irq = SoCIRQHandler(
740 n_irqs = irq_n_irqs,
741 reserved_irqs = irq_reserved_irqs
742 )
743
744 self.logger.info(colorer("-"*80, color="bright"))
745 self.logger.info(colorer("Initial SoC:"))
746 self.logger.info(colorer("-"*80, color="bright"))
747 self.logger.info(self.bus)
748 self.logger.info(self.csr)
749 self.logger.info(self.irq)
750 self.logger.info(colorer("-"*80, color="bright"))
751
752 self.add_config("CLOCK_FREQUENCY", int(sys_clk_freq))
753
754 # SoC Helpers ----------------------------------------------------------------------------------
755 def check_if_exists(self, name):
756 if hasattr(self, name):
757 self.logger.error("{} SubModule already {}.".format(
758 colorer(name),
759 colorer("declared", color="red")))
760 raise
761
762 def add_constant(self, name, value=None):
763 name = name.upper()
764 if name in self.constants.keys():
765 self.logger.error("{} Constant already {}.".format(
766 colorer(name),
767 colorer("declared", color="red")))
768 raise
769 self.constants[name] = SoCConstant(value)
770
771 def add_config(self, name, value=None):
772 name = "CONFIG_" + name
773 if isinstance(value, str):
774 self.add_constant(name + "_" + value)
775 else:
776 self.add_constant(name, value)
777
778 # SoC Main Components --------------------------------------------------------------------------
779 def add_controller(self, name="ctrl", **kwargs):
780 self.check_if_exists(name)
781 setattr(self.submodules, name, SoCController(**kwargs))
782 self.csr.add(name, use_loc_if_exists=True)
783
784 def add_ram(self, name, origin, size, contents=[], mode="rw"):
785 ram_cls = {
786 "wishbone": wishbone.SRAM,
787 "axi-lite": axi.AXILiteSRAM,
788 }[self.bus.standard]
789 interface_cls = {
790 "wishbone": wishbone.Interface,
791 "axi-lite": axi.AXILiteInterface,
792 }[self.bus.standard]
793 ram_bus = interface_cls(data_width=self.bus.data_width)
794 ram = ram_cls(size, bus=ram_bus, init=contents, read_only=(mode == "r"))
795 self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
796 self.check_if_exists(name)
797 self.logger.info("RAM {} {} {}.".format(
798 colorer(name),
799 colorer("added", color="green"),
800 self.bus.regions[name]))
801 setattr(self.submodules, name, ram)
802
803 def add_rom(self, name, origin, size, contents=[]):
804 self.add_ram(name, origin, size, contents, mode="r")
805
806 def add_csr_bridge(self, origin):
807 csr_bridge_cls = {
808 "wishbone": wishbone.Wishbone2CSR,
809 "axi-lite": axi.AXILite2CSR,
810 }[self.bus.standard]
811 self.submodules.csr_bridge = csr_bridge_cls(
812 bus_csr = csr_bus.Interface(
813 address_width = self.csr.address_width,
814 data_width = self.csr.data_width))
815 csr_size = 2**(self.csr.address_width + 2)
816 csr_region = SoCRegion(origin=origin, size=csr_size, cached=False)
817 bus = getattr(self.csr_bridge, self.bus.standard.replace('-', '_'))
818 self.bus.add_slave("csr", bus, csr_region)
819 self.csr.add_master(name="bridge", master=self.csr_bridge.csr)
820 self.add_config("CSR_DATA_WIDTH", self.csr.data_width)
821 self.add_config("CSR_ALIGNMENT", self.csr.alignment)
822
823 def add_cpu(self, name="vexriscv", variant="standard", cls=None, reset_address=None):
824 if name not in cpu.CPUS.keys():
825 self.logger.error("{} CPU {}, supporteds: {}.".format(
826 colorer(name),
827 colorer("not supported", color="red"),
828 colorer(", ".join(cpu.CPUS.keys()))))
829 raise
830 # Add CPU
831 cpu_cls = cls if cls is not None else cpu.CPUS[name]
832 if variant not in cpu_cls.variants:
833 self.logger.error("{} CPU variant {}, supporteds: {}.".format(
834 colorer(variant),
835 colorer("not supported", color="red"),
836 colorer(", ".join(cpu_cls.variants))))
837 raise
838 self.submodules.cpu = cpu_cls(self.platform, variant)
839 # Update SoC with CPU constraints
840 for n, (origin, size) in enumerate(self.cpu.io_regions.items()):
841 self.bus.add_region("io{}".format(n), SoCIORegion(origin=origin, size=size, cached=False))
842 self.mem_map.update(self.cpu.mem_map) # FIXME
843 # Add Bus Masters/CSR/IRQs
844 if not isinstance(self.cpu, (cpu.CPUNone, cpu.Zynq7000)):
845 if reset_address is None:
846 reset_address = self.mem_map["rom"]
847 self.cpu.set_reset_address(reset_address)
848 for n, cpu_bus in enumerate(self.cpu.periph_buses):
849 self.bus.add_master(name="cpu_bus{}".format(n), master=cpu_bus)
850 self.csr.add("cpu", use_loc_if_exists=True)
851 if hasattr(self.cpu, "interrupt"):
852 for name, loc in self.cpu.interrupts.items():
853 self.irq.add(name, loc)
854 self.add_config("CPU_HAS_INTERRUPT")
855
856 # Create optional DMA Bus (for Cache Coherence)
857 if hasattr(self.cpu, "dma_bus"):
858 self.submodules.dma_bus = SoCBusHandler(
859 name = "SoCDMABusHandler",
860 standard = "wishbone",
861 data_width = self.bus.data_width,
862 address_width = self.bus.address_width,
863 )
864 dma_bus = wishbone.Interface(data_width=self.bus.data_width)
865 self.dma_bus.add_slave("dma", slave=dma_bus, region=SoCRegion(origin=0x00000000, size=0x100000000)) # FIXME: covers lower 4GB only
866 self.submodules += wishbone.Converter(dma_bus, self.cpu.dma_bus)
867
868 # Connect SoCController's reset to CPU reset
869 if hasattr(self, "ctrl"):
870 if hasattr(self.ctrl, "reset"):
871 self.comb += self.cpu.reset.eq(self.ctrl.reset)
872 self.add_config("CPU_RESET_ADDR", reset_address)
873 # Add constants
874 self.add_config("CPU_TYPE", str(name))
875 self.add_config("CPU_VARIANT", str(variant.split('+')[0]))
876 self.add_constant("CONFIG_CPU_HUMAN_NAME", getattr(self.cpu, "human_name", "Unknown"))
877 if hasattr(self.cpu, "nop"):
878 self.add_constant("CONFIG_CPU_NOP", self.cpu.nop)
879
880 def add_timer(self, name="timer0"):
881 self.check_if_exists(name)
882 setattr(self.submodules, name, Timer())
883 self.csr.add(name, use_loc_if_exists=True)
884 if hasattr(self.cpu, "interrupt"):
885 self.irq.add(name, use_loc_if_exists=True)
886
887 # SoC finalization -----------------------------------------------------------------------------
888 def do_finalize(self):
889 self.logger.info(colorer("-"*80, color="bright"))
890 self.logger.info(colorer("Finalized SoC:"))
891 self.logger.info(colorer("-"*80, color="bright"))
892 self.logger.info(self.bus)
893 if hasattr(self, "dma_bus"):
894 self.logger.info(self.dma_bus)
895 self.logger.info(self.csr)
896 self.logger.info(self.irq)
897 self.logger.info(colorer("-"*80, color="bright"))
898
899 interconnect_p2p_cls = {
900 "wishbone": wishbone.InterconnectPointToPoint,
901 "axi-lite": axi.AXILiteInterconnectPointToPoint,
902 }[self.bus.standard]
903 interconnect_shared_cls = {
904 "wishbone": wishbone.InterconnectShared,
905 "axi-lite": axi.AXILiteInterconnectShared,
906 }[self.bus.standard]
907
908 # SoC Bus Interconnect ---------------------------------------------------------------------
909 if len(self.bus.masters) and len(self.bus.slaves):
910 # If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
911 if ((len(self.bus.masters) == 1) and
912 (len(self.bus.slaves) == 1) and
913 (next(iter(self.bus.regions.values())).origin == 0)):
914 self.submodules.bus_interconnect = interconnect_p2p_cls(
915 master = next(iter(self.bus.masters.values())),
916 slave = next(iter(self.bus.slaves.values())))
917 # Otherwise, use InterconnectShared.
918 else:
919 self.submodules.bus_interconnect = interconnect_shared_cls(
920 masters = self.bus.masters.values(),
921 slaves = [(self.bus.regions[n].decoder(self.bus), s) for n, s in self.bus.slaves.items()],
922 register = True,
923 timeout_cycles = self.bus.timeout)
924 if hasattr(self, "ctrl") and self.bus.timeout is not None:
925 if hasattr(self.ctrl, "bus_error"):
926 self.comb += self.ctrl.bus_error.eq(self.bus_interconnect.timeout.error)
927 self.bus.logger.info("Interconnect: {} ({} <-> {}).".format(
928 colorer(self.bus_interconnect.__class__.__name__),
929 colorer(len(self.bus.masters)),
930 colorer(len(self.bus.slaves))))
931 self.add_constant("CONFIG_BUS_STANDARD", self.bus.standard.upper())
932 self.add_constant("CONFIG_BUS_DATA_WIDTH", self.bus.data_width)
933 self.add_constant("CONFIG_BUS_ADDRESS_WIDTH", self.bus.address_width)
934
935 # SoC DMA Bus Interconnect (Cache Coherence) -----------------------------------------------
936 if hasattr(self, "dma_bus"):
937 if len(self.dma_bus.masters) and len(self.dma_bus.slaves):
938 # If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
939 if ((len(self.dma_bus.masters) == 1) and
940 (len(self.dma_bus.slaves) == 1) and
941 (next(iter(self.dma_bus.regions.values())).origin == 0)):
942 self.submodules.dma_bus_interconnect = wishbone.InterconnectPointToPoint(
943 master = next(iter(self.dma_bus.masters.values())),
944 slave = next(iter(self.dma_bus.slaves.values())))
945 # Otherwise, use InterconnectShared.
946 else:
947 self.submodules.dma_bus_interconnect = wishbone.InterconnectShared(
948 masters = self.dma_bus.masters.values(),
949 slaves = [(self.dma_bus.regions[n].decoder(self.dma_bus), s) for n, s in self.dma_bus.slaves.items()],
950 register = True)
951 self.bus.logger.info("DMA Interconnect: {} ({} <-> {}).".format(
952 colorer(self.dma_bus_interconnect.__class__.__name__),
953 colorer(len(self.dma_bus.masters)),
954 colorer(len(self.dma_bus.slaves))))
955 self.add_constant("CONFIG_CPU_HAS_DMA_BUS")
956
957 # SoC CSR Interconnect ---------------------------------------------------------------------
958 self.submodules.csr_bankarray = csr_bus.CSRBankArray(self,
959 address_map = self.csr.address_map,
960 data_width = self.csr.data_width,
961 address_width = self.csr.address_width,
962 alignment = self.csr.alignment,
963 paging = self.csr.paging,
964 ordering = self.csr.ordering,
965 soc_bus_data_width = self.bus.data_width)
966 if len(self.csr.masters):
967 self.submodules.csr_interconnect = csr_bus.InterconnectShared(
968 masters = list(self.csr.masters.values()),
969 slaves = self.csr_bankarray.get_buses())
970
971 # Add CSRs regions
972 for name, csrs, mapaddr, rmap in self.csr_bankarray.banks:
973 self.csr.add_region(name, SoCCSRRegion(
974 origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
975 busword = self.csr.data_width,
976 obj = csrs))
977
978 # Add Memory regions
979 for name, memory, mapaddr, mmap in self.csr_bankarray.srams:
980 self.csr.add_region(name + "_" + memory.name_override, SoCCSRRegion(
981 origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
982 busword = self.csr.data_width,
983 obj = memory))
984
985 # Sort CSR regions by origin
986 self.csr.regions = {k: v for k, v in sorted(self.csr.regions.items(), key=lambda item: item[1].origin)}
987
988 # Add CSRs / Config items to constants
989 for name, constant in self.csr_bankarray.constants:
990 self.add_constant(name + "_" + constant.name, constant.value.value)
991
992 # SoC CPU Check ----------------------------------------------------------------------------
993 if not isinstance(self.cpu, (cpu.CPUNone, cpu.Zynq7000)):
994 if "sram" not in self.bus.regions.keys():
995 self.logger.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
996 colorer("sram"),
997 colorer("defined", color="red")))
998 self.logger.error(self.bus)
999 raise
1000 cpu_reset_address_valid = False
1001 for name, container in self.bus.regions.items():
1002 if self.bus.check_region_is_in(
1003 region = SoCRegion(origin=self.cpu.reset_address, size=self.bus.data_width//8),
1004 container = container):
1005 cpu_reset_address_valid = True
1006 if name == "rom":
1007 self.cpu.use_rom = True
1008 if not cpu_reset_address_valid:
1009 self.logger.error("CPU needs {} to be in a {} Region.".format(
1010 colorer("reset address 0x{:08x}".format(self.cpu.reset_address)),
1011 colorer("defined", color="red")))
1012 self.logger.error(self.bus)
1013 raise
1014
1015 # SoC IRQ Interconnect ---------------------------------------------------------------------
1016 if hasattr(self, "cpu"):
1017 if hasattr(self.cpu, "interrupt"):
1018 for name, loc in sorted(self.irq.locs.items()):
1019 if name in self.cpu.interrupts.keys():
1020 continue
1021 if hasattr(self, name):
1022 module = getattr(self, name)
1023 if not hasattr(module, "ev"):
1024 self.logger.error("EventManager {} in {} SubModule.".format(
1025 colorer("not found", color="red"),
1026 colorer(name)))
1027 raise
1028 self.comb += self.cpu.interrupt[loc].eq(module.ev.irq)
1029 self.add_constant(name + "_INTERRUPT", loc)
1030
1031 # SoC build ------------------------------------------------------------------------------------
1032 def build(self, *args, **kwargs):
1033 self.build_name = kwargs.pop("build_name", self.platform.name)
1034 kwargs.update({"build_name": self.build_name})
1035 return self.platform.build(self, *args, **kwargs)
1036
1037 # LiteXSoC -----------------------------------------------------------------------------------------
1038
1039 class LiteXSoC(SoC):
1040 # Add Identifier -------------------------------------------------------------------------------
1041 def add_identifier(self, name="identifier", identifier="LiteX SoC", with_build_time=True):
1042 self.check_if_exists(name)
1043 if with_build_time:
1044 identifier += " " + build_time()
1045 setattr(self.submodules, name, Identifier(identifier))
1046 self.csr.add(name + "_mem", use_loc_if_exists=True)
1047
1048 # Add UART -------------------------------------------------------------------------------------
1049 def add_uart(self, name, baudrate=115200, fifo_depth=16):
1050 from litex.soc.cores import uart
1051
1052 # Stub / Stream
1053 if name in ["stub", "stream"]:
1054 self.submodules.uart = uart.UART(tx_fifo_depth=0, rx_fifo_depth=0)
1055 if name == "stub":
1056 self.comb += self.uart.sink.ready.eq(1)
1057
1058 # UARTBone / Bridge
1059 elif name in ["uartbone", "bridge"]:
1060 self.add_uartbone(baudrate=baudrate)
1061
1062 # Crossover
1063 elif name in ["crossover"]:
1064 self.submodules.uart = uart.UARTCrossover()
1065
1066 # Model/Sim
1067 elif name in ["model", "sim"]:
1068 self.submodules.uart_phy = uart.RS232PHYModel(self.platform.request("serial"))
1069 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
1070 tx_fifo_depth = fifo_depth,
1071 rx_fifo_depth = fifo_depth))
1072
1073 # JTAG Atlantic
1074 elif name in ["jtag_atlantic"]:
1075 from litex.soc.cores.jtag import JTAGAtlantic
1076 self.submodules.uart_phy = JTAGAtlantic()
1077 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
1078 tx_fifo_depth = fifo_depth,
1079 rx_fifo_depth = fifo_depth))
1080
1081 # JTAG UART
1082 elif name in ["jtag_uart"]:
1083 from litex.soc.cores.jtag import JTAGPHY
1084 self.submodules.uart_phy = JTAGPHY(device=self.platform.device)
1085 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
1086 tx_fifo_depth = fifo_depth,
1087 rx_fifo_depth = fifo_depth))
1088
1089 # USB ACM (with ValentyUSB core)
1090 elif name in ["usb_acm"]:
1091 import valentyusb.usbcore.io as usbio
1092 import valentyusb.usbcore.cpu.cdc_eptri as cdc_eptri
1093 usb_pads = self.platform.request("usb")
1094 usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup)
1095 self.submodules.uart = cdc_eptri.CDCUsb(usb_iobuf)
1096
1097 # Classic UART
1098 else:
1099 self.submodules.uart_phy = uart.UARTPHY(
1100 pads = self.platform.request(name),
1101 clk_freq = self.sys_clk_freq,
1102 baudrate = baudrate)
1103 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
1104 tx_fifo_depth = fifo_depth,
1105 rx_fifo_depth = fifo_depth))
1106
1107 self.csr.add("uart_phy", use_loc_if_exists=True)
1108 self.csr.add("uart", use_loc_if_exists=True)
1109 if hasattr(self.cpu, "interrupt"):
1110 self.irq.add("uart", use_loc_if_exists=True)
1111 else:
1112 self.add_constant("UART_POLLING")
1113
1114 # Add UARTbone ---------------------------------------------------------------------------------
1115 def add_uartbone(self, name="serial", baudrate=115200):
1116 from litex.soc.cores import uart
1117 self.submodules.uartbone = uart.UARTBone(
1118 pads = self.platform.request(name),
1119 clk_freq = self.sys_clk_freq,
1120 baudrate = baudrate)
1121 self.bus.add_master(name="uartbone", master=self.uartbone.wishbone)
1122
1123 # Add SDRAM ------------------------------------------------------------------------------------
1124 def add_sdram(self, name, phy, module, origin, size=None, with_soc_interconnect=True,
1125 l2_cache_size = 8192,
1126 l2_cache_min_data_width = 128,
1127 l2_cache_reverse = True,
1128 l2_cache_full_memory_we = True,
1129 **kwargs):
1130
1131 # Imports
1132 from litedram.common import LiteDRAMNativePort
1133 from litedram.core import LiteDRAMCore
1134 from litedram.frontend.wishbone import LiteDRAMWishbone2Native
1135 from litedram.frontend.axi import LiteDRAMAXI2Native
1136
1137 # LiteDRAM core
1138 self.submodules.sdram = LiteDRAMCore(
1139 phy = phy,
1140 geom_settings = module.geom_settings,
1141 timing_settings = module.timing_settings,
1142 clk_freq = self.sys_clk_freq,
1143 **kwargs)
1144 self.csr.add("sdram")
1145
1146 # Save SPD data to be able to verify it at runtime
1147 if hasattr(module, "_spd_data"):
1148 # pack the data into words of bus width
1149 bytes_per_word = self.bus.data_width // 8
1150 mem = [0] * ceil(len(module._spd_data) / bytes_per_word)
1151 for i in range(len(mem)):
1152 for offset in range(bytes_per_word):
1153 mem[i] <<= 8
1154 if self.cpu.endianness == "little":
1155 offset = bytes_per_word - 1 - offset
1156 spd_byte = i * bytes_per_word + offset
1157 if spd_byte < len(module._spd_data):
1158 mem[i] |= module._spd_data[spd_byte]
1159 self.add_rom(
1160 name="spd",
1161 origin=self.mem_map.get("spd", None),
1162 size=len(module._spd_data),
1163 contents=mem,
1164 )
1165
1166 if not with_soc_interconnect: return
1167
1168 # Compute/Check SDRAM size
1169 sdram_size = 2**(module.geom_settings.bankbits +
1170 module.geom_settings.rowbits +
1171 module.geom_settings.colbits)*phy.settings.databits//8
1172 if size is not None:
1173 sdram_size = min(sdram_size, size)
1174
1175 # Add SDRAM region
1176 self.bus.add_region("main_ram", SoCRegion(origin=origin, size=sdram_size))
1177
1178 # Add CPU's direct memory buses (if not already declared) ----------------------------------
1179 if hasattr(self.cpu, "add_memory_buses"):
1180 self.cpu.add_memory_buses(
1181 address_width = 32,
1182 data_width = self.sdram.crossbar.controller.data_width
1183 )
1184
1185 # Connect CPU's direct memory buses to LiteDRAM --------------------------------------------
1186 if len(self.cpu.memory_buses):
1187 # When CPU has at least a direct memory bus, connect them directly to LiteDRAM.
1188 for mem_bus in self.cpu.memory_buses:
1189 # Request a LiteDRAM native port.
1190 port = self.sdram.crossbar.get_port()
1191 port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2.
1192
1193 # Check if bus is an AXI bus and connect it.
1194 if isinstance(mem_bus, axi.AXIInterface):
1195 # If same data_width, connect it directly.
1196 if port.data_width == mem_bus.data_width:
1197 self.logger.info("Matching AXI MEM data width ({})\n".format(port.data_width))
1198 self.submodules += LiteDRAMAXI2Native(
1199 axi = self.cpu.mem_axi,
1200 port = port,
1201 base_address = self.bus.regions["main_ram"].origin)
1202 # If different data_width, do the adaptation and connect it via Wishbone.
1203 else:
1204 self.logger.info("Converting MEM data width: {} to {} via Wishbone".format(
1205 port.data_width,
1206 self.cpu.mem_axi.data_width))
1207 # FIXME: replace WB data-width converter with native AXI converter!!!
1208 mem_wb = wishbone.Interface(
1209 data_width = self.cpu.mem_axi.data_width,
1210 adr_width = 32-log2_int(self.cpu.mem_axi.data_width//8))
1211 # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
1212 mem_a2w = ResetInserter()(axi.AXI2Wishbone(
1213 axi = self.cpu.mem_axi,
1214 wishbone = mem_wb,
1215 base_address = 0))
1216 self.comb += mem_a2w.reset.eq(ResetSignal() | self.cpu.reset)
1217 self.submodules += mem_a2w
1218 litedram_wb = wishbone.Interface(port.data_width)
1219 self.submodules += LiteDRAMWishbone2Native(
1220 wishbone = litedram_wb,
1221 port = port,
1222 base_address = origin)
1223 self.submodules += wishbone.Converter(mem_wb, litedram_wb)
1224 # Check if bus is a Native bus and connect it.
1225 if isinstance(mem_bus, LiteDRAMNativePort):
1226 # If same data_width, connect it directly.
1227 if port.data_width == mem_bus.data_width:
1228 self.comb += mem_bus.cmd.connect(port.cmd)
1229 self.comb += mem_bus.wdata.connect(port.wdata)
1230 self.comb += port.rdata.connect(mem_bus.rdata)
1231 # Else raise Error.
1232 else:
1233 raise NotImplementedError
1234
1235 # Connect Main bus to LiteDRAM (with optional L2 Cache) ------------------------------------
1236 connect_main_bus_to_dram = (
1237 # No memory buses.
1238 (not len(self.cpu.memory_buses)) or
1239 # Memory buses but no DMA bus.
1240 (len(self.cpu.memory_buses) and not hasattr(self.cpu, "dma_bus"))
1241 )
1242 if connect_main_bus_to_dram:
1243 # Request a LiteDRAM native port.
1244 port = self.sdram.crossbar.get_port()
1245 port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2.
1246
1247 # Create Wishbone Slave.
1248 wb_sdram = wishbone.Interface()
1249 self.bus.add_slave("main_ram", wb_sdram)
1250
1251 # L2 Cache
1252 if l2_cache_size != 0:
1253 # Insert L2 cache inbetween Wishbone bus and LiteDRAM
1254 l2_cache_size = max(l2_cache_size, int(2*port.data_width/8)) # Use minimal size if lower
1255 l2_cache_size = 2**int(log2(l2_cache_size)) # Round to nearest power of 2
1256 l2_cache_data_width = max(port.data_width, l2_cache_min_data_width)
1257 l2_cache = wishbone.Cache(
1258 cachesize = l2_cache_size//4,
1259 master = wb_sdram,
1260 slave = wishbone.Interface(l2_cache_data_width),
1261 reverse = l2_cache_reverse)
1262 if l2_cache_full_memory_we:
1263 l2_cache = FullMemoryWE()(l2_cache)
1264 self.submodules.l2_cache = l2_cache
1265 litedram_wb = self.l2_cache.slave
1266 else:
1267 litedram_wb = wishbone.Interface(port.data_width)
1268 self.submodules += wishbone.Converter(wb_sdram, litedram_wb)
1269 self.add_config("L2_SIZE", l2_cache_size)
1270
1271 # Wishbone Slave <--> LiteDRAM bridge
1272 self.submodules.wishbone_bridge = LiteDRAMWishbone2Native(
1273 wishbone = litedram_wb,
1274 port = port,
1275 base_address = self.bus.regions["main_ram"].origin)
1276
1277 # Add Ethernet ---------------------------------------------------------------------------------
1278 def add_ethernet(self, name="ethmac", phy=None):
1279 # Imports
1280 from liteeth.mac import LiteEthMAC
1281 # MAC
1282 ethmac = LiteEthMAC(
1283 phy = phy,
1284 dw = 32,
1285 interface = "wishbone",
1286 endianness = self.cpu.endianness)
1287 setattr(self.submodules, name, ethmac)
1288 ethmac_region = SoCRegion(origin=self.mem_map.get(name, None), size=0x2000, cached=False)
1289 self.bus.add_slave(name=name, slave=ethmac.bus, region=ethmac_region)
1290 self.add_csr(name)
1291 self.add_interrupt(name)
1292 # Timing constraints
1293 if hasattr(phy, "crg"):
1294 eth_rx_clk = phy.crg.cd_eth_rx.clk
1295 eth_tx_clk = phy.crg.cd_eth_tx.clk
1296 else:
1297 eth_rx_clk = phy.cd_eth_rx.clk
1298 eth_tx_clk = phy.cd_eth_tx.clk
1299 self.platform.add_period_constraint(eth_rx_clk, 1e9/phy.rx_clk_freq)
1300 self.platform.add_period_constraint(eth_tx_clk, 1e9/phy.tx_clk_freq)
1301 self.platform.add_false_path_constraints(
1302 self.crg.cd_sys.clk,
1303 eth_rx_clk,
1304 eth_tx_clk)
1305
1306 # Add Etherbone --------------------------------------------------------------------------------
1307 def add_etherbone(self, name="etherbone", phy=None,
1308 mac_address = 0x10e2d5000000,
1309 ip_address = "192.168.1.50",
1310 udp_port = 1234):
1311 # Imports
1312 from liteeth.core import LiteEthUDPIPCore
1313 from liteeth.frontend.etherbone import LiteEthEtherbone
1314 # Core
1315 ethcore = LiteEthUDPIPCore(
1316 phy = self.ethphy,
1317 mac_address = mac_address,
1318 ip_address = ip_address,
1319 clk_freq = self.clk_freq)
1320 ethcore = ClockDomainsRenamer("eth_tx")(ethcore)
1321 self.submodules.ethcore = ethcore
1322
1323 # Clock domain renaming
1324 self.clock_domains.cd_etherbone = ClockDomain("etherbone")
1325 self.comb += self.cd_etherbone.clk.eq(ClockSignal("sys"))
1326 self.comb += self.cd_etherbone.rst.eq(ResetSignal("sys"))
1327
1328 # Etherbone
1329 etherbone = LiteEthEtherbone(ethcore.udp, udp_port, cd="etherbone")
1330 setattr(self.submodules, name, etherbone)
1331 self.add_wb_master(etherbone.wishbone.bus)
1332 # Timing constraints
1333 if hasattr(phy, "crg"):
1334 eth_rx_clk = phy.crg.cd_eth_rx.clk
1335 eth_tx_clk = phy.crg.cd_eth_tx.clk
1336 else:
1337 eth_rx_clk = phy.cd_eth_rx.clk
1338 eth_tx_clk = phy.cd_eth_tx.clk
1339 self.platform.add_period_constraint(eth_rx_clk, 1e9/phy.rx_clk_freq)
1340 self.platform.add_period_constraint(eth_tx_clk, 1e9/phy.tx_clk_freq)
1341 self.platform.add_false_path_constraints(
1342 self.crg.cd_sys.clk,
1343 eth_rx_clk,
1344 eth_tx_clk)
1345
1346 # Add SPI Flash --------------------------------------------------------------------------------
1347 def add_spi_flash(self, name="spiflash", mode="4x", dummy_cycles=None, clk_freq=None):
1348 assert dummy_cycles is not None # FIXME: Get dummy_cycles from SPI Flash
1349 assert mode in ["1x", "4x"]
1350 if clk_freq is None: clk_freq = self.clk_freq/2 # FIXME: Get max clk_freq from SPI Flash
1351 spiflash = SpiFlash(
1352 pads = self.platform.request(name if mode == "1x" else name + mode),
1353 dummy = dummy_cycles,
1354 div = ceil(self.clk_freq/clk_freq),
1355 with_bitbang = True,
1356 endianness = self.cpu.endianness)
1357 spiflash.add_clk_primitive(self.platform.device)
1358 setattr(self.submodules, name, spiflash)
1359 self.add_memory_region(name, self.mem_map[name], 0x1000000) # FIXME: Get size from SPI Flash
1360 self.add_wb_slave(self.mem_map[name], spiflash.bus)
1361 self.add_csr(name)
1362
1363 # Add SPI SDCard -------------------------------------------------------------------------------
1364 def add_spi_sdcard(self, name="spisdcard", spi_clk_freq=400e3):
1365 pads = self.platform.request(name)
1366 if hasattr(pads, "rst"):
1367 self.comb += pads.rst.eq(0)
1368 spisdcard = SPIMaster(pads, 8, self.sys_clk_freq, spi_clk_freq)
1369 spisdcard.add_clk_divider()
1370 setattr(self.submodules, name, spisdcard)
1371 self.add_csr(name)
1372
1373 # Add SDCard -----------------------------------------------------------------------------------
1374 def add_sdcard(self, name="sdcard", mode="read+write", use_emulator=False):
1375 assert mode in ["read", "write", "read+write"]
1376 # Imports
1377 from litesdcard.emulator import SDEmulator
1378 from litesdcard.phy import SDPHY
1379 from litesdcard.core import SDCore
1380 from litesdcard.frontend.dma import SDBlock2MemDMA, SDMem2BlockDMA
1381
1382 # Emulator / Pads
1383 if use_emulator:
1384 sdemulator = SDEmulator(self.platform)
1385 self.submodules += sdemulator
1386 sdcard_pads = sdemulator.pads
1387 else:
1388 sdcard_pads = self.platform.request(name)
1389
1390 # Core
1391 self.submodules.sdphy = SDPHY(sdcard_pads, self.platform.device, self.clk_freq)
1392 self.submodules.sdcore = SDCore(self.sdphy)
1393 self.add_csr("sdphy")
1394 self.add_csr("sdcore")
1395
1396 # Block2Mem DMA
1397 if "read" in mode:
1398 bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.address_width)
1399 self.submodules.sdblock2mem = SDBlock2MemDMA(bus=bus, endianness=self.cpu.endianness)
1400 self.comb += self.sdcore.source.connect(self.sdblock2mem.sink)
1401 dma_bus = self.bus if not hasattr(self, "dma_bus") else self.dma_bus
1402 dma_bus.add_master("sdblock2mem", master=bus)
1403 self.add_csr("sdblock2mem")
1404
1405 # Mem2Block DMA
1406 if "write" in mode:
1407 bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.address_width)
1408 self.submodules.sdmem2block = SDMem2BlockDMA(bus=bus, endianness=self.cpu.endianness)
1409 self.comb += self.sdmem2block.source.connect(self.sdcore.sink)
1410 dma_bus = self.bus if not hasattr(self, "dma_bus") else self.dma_bus
1411 dma_bus.add_master("sdmem2block", master=bus)
1412 self.add_csr("sdmem2block")