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