targets: switch to add_ethernet method instead of EthernetSoC.
[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 wishbone
22 from litex.soc.interconnect import wishbone2csr
23 from litex.soc.interconnect import axi
24
25 # TODO:
26 # - replace raise with exit on logging error.
27 # - cleanup SoCCSRRegion
28
29 logging.basicConfig(level=logging.INFO)
30
31 # Helpers ------------------------------------------------------------------------------------------
32
33 def auto_int(x):
34 return int(x, 0)
35
36 def colorer(s, color="bright"):
37 header = {
38 "bright": "\x1b[1m",
39 "green": "\x1b[32m",
40 "cyan": "\x1b[36m",
41 "red": "\x1b[31m",
42 "yellow": "\x1b[33m",
43 "underline": "\x1b[4m"}[color]
44 trailer = "\x1b[0m"
45 return header + str(s) + trailer
46
47 def build_time(with_time=True):
48 fmt = "%Y-%m-%d %H:%M:%S" if with_time else "%Y-%m-%d"
49 return datetime.datetime.fromtimestamp(time.time()).strftime(fmt)
50
51 # SoCConstant --------------------------------------------------------------------------------------
52
53 def SoCConstant(value):
54 return value
55
56 # SoCRegion ----------------------------------------------------------------------------------------
57
58 class SoCRegion:
59 def __init__(self, origin=None, size=None, mode="rw", cached=True, linker=False):
60 self.logger = logging.getLogger("SoCRegion")
61 self.origin = origin
62 self.size = size
63 if size != 2**log2_int(size, False):
64 self.logger.info("Region size {} internally from {} to {}.".format(
65 colorer("rounded", color="cyan"),
66 colorer("0x{:08x}".format(size)),
67 colorer("0x{:08x}".format(2**log2_int(size, False)))))
68 self.size_pow2 = 2**log2_int(size, False)
69 self.mode = mode
70 self.cached = cached
71 self.linker = linker
72
73 def decoder(self, bus):
74 origin = self.origin
75 size = self.size_pow2
76 if (origin & (size - 1)) != 0:
77 self.logger.error("Origin needs to be aligned on size:")
78 self.logger.error(self)
79 raise
80 origin >>= int(log2(bus.data_width//8)) # bytes to words aligned
81 size >>= int(log2(bus.data_width//8)) # bytes to words aligned
82 return lambda a: (a[log2_int(size):] == (origin >> log2_int(size)))
83
84 def __str__(self):
85 r = ""
86 if self.origin is not None:
87 r += "Origin: {}, ".format(colorer("0x{:08x}".format(self.origin)))
88 if self.size is not None:
89 r += "Size: {}, ".format(colorer("0x{:08x}".format(self.size)))
90 r += "Mode: {}, ".format(colorer(self.mode.upper()))
91 r += "Cached: {} ".format(colorer(self.cached))
92 r += "Linker: {}".format(colorer(self.linker))
93 return r
94
95 class SoCIORegion(SoCRegion): pass
96
97 # SoCCSRRegion -------------------------------------------------------------------------------------
98
99 class SoCCSRRegion:
100 def __init__(self, origin, busword, obj):
101 self.origin = origin
102 self.busword = busword
103 self.obj = obj
104
105 # SoCBusHandler ------------------------------------------------------------------------------------
106
107 class SoCBusHandler(Module):
108 supported_standard = ["wishbone"]
109 supported_data_width = [32, 64]
110 supported_address_width = [32]
111
112 # Creation -------------------------------------------------------------------------------------
113 def __init__(self, standard, data_width=32, address_width=32, timeout=1e6, reserved_regions={}):
114 self.logger = logging.getLogger("SoCBusHandler")
115 self.logger.info("Creating Bus Handler...")
116
117 # Check Standard
118 if standard not in self.supported_standard:
119 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
120 colorer("Bus standard", color="red"),
121 colorer(standard),
122 colorer(", ".join(self.supported_standard))))
123 raise
124
125 # Check Data Width
126 if data_width not in self.supported_data_width:
127 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
128 colorer("Data Width", color="red"),
129 colorer(data_width),
130 colorer(", ".join(str(x) for x in self.supported_data_width))))
131 raise
132
133 # Check Address Width
134 if address_width not in self.supported_address_width:
135 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
136 colorer("Address Width", color="red"),
137 colorer(data_width),
138 colorer(", ".join(str(x) for x in self.supported_address_width))))
139 raise
140
141 # Create Bus
142 self.standard = standard
143 self.data_width = data_width
144 self.address_width = address_width
145 self.masters = {}
146 self.slaves = {}
147 self.regions = {}
148 self.io_regions = {}
149 self.timeout = timeout
150 self.logger.info("{}-bit {} Bus, {}GiB Address Space.".format(
151 colorer(data_width), colorer(standard), colorer(2**address_width/2**30)))
152
153 # Adding reserved regions
154 self.logger.info("Adding {} Bus Regions...".format(colorer("reserved", color="cyan")))
155 for name, region in reserved_regions.items():
156 if isinstance(region, int):
157 region = SoCRegion(origin=region, size=0x1000000)
158 self.add_region(name, region)
159
160 self.logger.info("Bus Handler {}.".format(colorer("created", color="green")))
161
162 # Add/Allog/Check Regions ----------------------------------------------------------------------
163 def add_region(self, name, region):
164 allocated = False
165 if name in self.regions.keys() or name in self.io_regions.keys():
166 self.logger.error("{} already declared as Region:".format(colorer(name, color="red")))
167 self.logger.error(self)
168 raise
169 # Check if SoCIORegion
170 if isinstance(region, SoCIORegion):
171 self.io_regions[name] = region
172 overlap = self.check_regions_overlap(self.io_regions)
173 if overlap is not None:
174 self.logger.error("IO Region {} between {} and {}:".format(
175 colorer("overlap", color="red"),
176 colorer(overlap[0]),
177 colorer(overlap[1])))
178 self.logger.error(str(self.io_regions[overlap[0]]))
179 self.logger.error(str(self.io_regions[overlap[1]]))
180 raise
181 self.logger.info("{} Region {} at {}.".format(
182 colorer(name, color="underline"),
183 colorer("added", color="green"),
184 str(region)))
185 # Check if SoCRegion
186 elif isinstance(region, SoCRegion):
187 # If no origin specified, allocate region.
188 if region.origin is None:
189 allocated = True
190 region = self.alloc_region(name, region.size, region.cached)
191 self.regions[name] = region
192 # Else add region and check for overlaps.
193 else:
194 if not region.cached:
195 if not self.check_region_is_io(region):
196 self.logger.error("{} Region {}: {}.".format(
197 colorer(name),
198 colorer("not in IO region", color="red"),
199 str(region)))
200 self.logger.error(self)
201 raise
202 self.regions[name] = region
203 overlap = self.check_regions_overlap(self.regions)
204 if overlap is not None:
205 self.logger.error("Region {} between {} and {}:".format(
206 colorer("overlap", color="red"),
207 colorer(overlap[0]),
208 colorer(overlap[1])))
209 self.logger.error(str(self.regions[overlap[0]]))
210 self.logger.error(str(self.regions[overlap[1]]))
211 raise
212 self.logger.info("{} Region {} at {}.".format(
213 colorer(name, color="underline"),
214 colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
215 str(region)))
216 else:
217 self.logger.error("{} is not a supported Region.".format(colorer(name, color="red")))
218 raise
219
220 def alloc_region(self, name, size, cached=True):
221 self.logger.info("Allocating {} Region of size {}...".format(
222 colorer("Cached" if cached else "IO"),
223 colorer("0x{:08x}".format(size))))
224
225 # Limit Search Regions
226 if cached == False:
227 search_regions = self.io_regions
228 else:
229 search_regions = {"main": SoCRegion(origin=0x00000000, size=2**self.address_width-1)}
230
231 # Iterate on Search_Regions to find a Candidate
232 for _, search_region in search_regions.items():
233 origin = search_region.origin
234 while (origin + size) < (search_region.origin + search_region.size_pow2):
235 # Create a Candicate.
236 candidate = SoCRegion(origin=origin, size=size, cached=cached)
237 overlap = False
238 # Check Candidate does not overlap with allocated existing regions
239 for _, allocated in self.regions.items():
240 if self.check_regions_overlap({"0": allocated, "1": candidate}) is not None:
241 origin = allocated.origin + allocated.size_pow2
242 overlap = True
243 break
244 if not overlap:
245 # If no overlap, the Candidate is selected
246 return candidate
247
248 self.logger.error("Not enough Address Space to allocate Region.")
249 raise
250
251 def check_regions_overlap(self, regions, check_linker=False):
252 i = 0
253 while i < len(regions):
254 n0 = list(regions.keys())[i]
255 r0 = regions[n0]
256 for n1 in list(regions.keys())[i+1:]:
257 r1 = regions[n1]
258 if r0.linker or r1.linker:
259 if not check_linker:
260 continue
261 if r0.origin >= (r1.origin + r1.size_pow2):
262 continue
263 if r1.origin >= (r0.origin + r0.size_pow2):
264 continue
265 return (n0, n1)
266 i += 1
267 return None
268
269 def check_region_is_in(self, region, container):
270 is_in = True
271 if not (region.origin >= container.origin):
272 is_in = False
273 if not ((region.origin + region.size) < (container.origin + container.size)):
274 is_in = False
275 return is_in
276
277 def check_region_is_io(self, region):
278 is_io = False
279 for _, io_region in self.io_regions.items():
280 if self.check_region_is_in(region, io_region):
281 is_io = True
282 return is_io
283
284 # Add Master/Slave -----------------------------------------------------------------------------
285 def add_adapter(self, name, interface):
286 if interface.data_width != self.data_width:
287 self.logger.info("{} Bus {} from {}-bit to {}-bit.".format(
288 colorer(name),
289 colorer("converted", color="cyan"),
290 colorer(interface.data_width),
291 colorer(self.data_width)))
292 new_interface = wishbone.Interface(data_width=self.data_width)
293 self.submodules += wishbone.Converter(interface, new_interface)
294 return new_interface
295 else:
296 return interface
297
298 def add_master(self, name=None, master=None):
299 if name is None:
300 name = "master{:d}".format(len(self.masters))
301 if name in self.masters.keys():
302 self.logger.error("{} {} as Bus Master:".format(
303 colorer(name),
304 colorer("already declared", color="red")))
305 self.logger.error(self)
306 raise
307 master = self.add_adapter(name, master)
308 self.masters[name] = master
309 self.logger.info("{} {} as Bus Master.".format(
310 colorer(name, color="underline"),
311 colorer("added", color="green")))
312
313 def add_slave(self, name=None, slave=None, region=None):
314 no_name = name is None
315 no_region = region is None
316 if no_name and no_region:
317 self.logger.error("Please {} {} or/and {} of Bus Slave.".format(
318 colorer("specify", color="red"),
319 colorer("name"),
320 colorer("region")))
321 raise
322 if no_name:
323 name = "slave{:d}".format(len(self.slaves))
324 if no_region:
325 region = self.regions.get(name, None)
326 if region is None:
327 self.logger.error("{} Region {}.".format(
328 colorer(name),
329 colorer("not found", color="red")))
330 raise
331 else:
332 self.add_region(name, region)
333 if name in self.slaves.keys():
334 self.logger.error("{} {} as Bus Slave:".format(
335 colorer(name),
336 colorer("already declared", color="red")))
337 self.logger.error(self)
338 raise
339 slave = self.add_adapter(name, slave)
340 self.slaves[name] = slave
341 self.logger.info("{} {} as Bus Slave.".format(
342 colorer(name, color="underline"),
343 colorer("added", color="green")))
344
345 # Str ------------------------------------------------------------------------------------------
346 def __str__(self):
347 r = "{}-bit {} Bus, {}GiB Address Space.\n".format(
348 colorer(self.data_width), colorer(self.standard), colorer(2**self.address_width/2**30))
349 r += "IO Regions: ({})\n".format(len(self.io_regions.keys())) if len(self.io_regions.keys()) else ""
350 io_regions = {k: v for k, v in sorted(self.io_regions.items(), key=lambda item: item[1].origin)}
351 for name, region in io_regions.items():
352 r += colorer(name, color="underline") + " "*(20-len(name)) + ": " + str(region) + "\n"
353 r += "Bus Regions: ({})\n".format(len(self.regions.keys())) if len(self.regions.keys()) else ""
354 regions = {k: v for k, v in sorted(self.regions.items(), key=lambda item: item[1].origin)}
355 for name, region in regions.items():
356 r += colorer(name, color="underline") + " "*(20-len(name)) + ": " + str(region) + "\n"
357 r += "Bus Masters: ({})\n".format(len(self.masters.keys())) if len(self.masters.keys()) else ""
358 for name in self.masters.keys():
359 r += "- {}\n".format(colorer(name, color="underline"))
360 r += "Bus Slaves: ({})\n".format(len(self.slaves.keys())) if len(self.slaves.keys()) else ""
361 for name in self.slaves.keys():
362 r += "- {}\n".format(colorer(name, color="underline"))
363 r = r[:-1]
364 return r
365
366 # SoCLocHandler --------------------------------------------------------------------------------------
367
368 class SoCLocHandler(Module):
369 # Creation -------------------------------------------------------------------------------------
370 def __init__(self, name, n_locs):
371 self.name = name
372 self.locs = {}
373 self.n_locs = n_locs
374
375 # Add ------------------------------------------------------------------------------------------
376 def add(self, name, n=None, use_loc_if_exists=False):
377 allocated = False
378 if not (use_loc_if_exists and name in self.locs.keys()):
379 if name in self.locs.keys():
380 self.logger.error("{} {} name {}.".format(
381 colorer(name), self.name, colorer("already used", color="red")))
382 self.logger.error(self)
383 raise
384 if n in self.locs.values():
385 self.logger.error("{} {} Location {}.".format(
386 colorer(n), self.name, colorer("already used", color="red")))
387 self.logger.error(self)
388 raise
389 if n is None:
390 allocated = True
391 n = self.alloc(name)
392 else:
393 if n < 0:
394 self.logger.error("{} {} Location should be {}.".format(
395 colorer(n),
396 self.name,
397 colorer("positive", color="red")))
398 raise
399 if n > self.n_locs:
400 self.logger.error("{} {} Location {} than maximum: {}.".format(
401 colorer(n),
402 self.name,
403 colorer("higher", color="red"),
404 colorer(self.n_locs)))
405 raise
406 self.locs[name] = n
407 else:
408 n = self.locs[name]
409 self.logger.info("{} {} {} at Location {}.".format(
410 colorer(name, color="underline"),
411 self.name,
412 colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
413 colorer(n)))
414
415 # Alloc ----------------------------------------------------------------------------------------
416 def alloc(self, name):
417 for n in range(self.n_locs):
418 if n not in self.locs.values():
419 return n
420 self.logger.error("Not enough Locations.")
421 self.logger.error(self)
422 raise
423
424 # Str ------------------------------------------------------------------------------------------
425 def __str__(self):
426 r = "{} Locations: ({})\n".format(self.name, len(self.locs.keys())) if len(self.locs.keys()) else ""
427 locs = {k: v for k, v in sorted(self.locs.items(), key=lambda item: item[1])}
428 length = 0
429 for name in locs.keys():
430 if len(name) > length: length = len(name)
431 for name in locs.keys():
432 r += "- {}{}: {}\n".format(colorer(name, color="underline"), " "*(length + 1 - len(name)), colorer(self.locs[name]))
433 return r
434
435 # SoCCSRHandler ------------------------------------------------------------------------------------
436
437 class SoCCSRHandler(SoCLocHandler):
438 supported_data_width = [8, 32]
439 supported_address_width = [14+i for i in range(4)]
440 supported_alignment = [32, 64]
441 supported_paging = [0x800*2**i for i in range(4)]
442
443 # Creation -------------------------------------------------------------------------------------
444 def __init__(self, data_width=32, address_width=14, alignment=32, paging=0x800, reserved_csrs={}):
445 SoCLocHandler.__init__(self, "CSR", n_locs=alignment//8*(2**address_width)//paging)
446 self.logger = logging.getLogger("SoCCSRHandler")
447 self.logger.info("Creating CSR Handler...")
448
449 # Check Data Width
450 if data_width not in self.supported_data_width:
451 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
452 colorer("Data Width", color="red"),
453 colorer(data_width),
454 colorer(", ".join(str(x) for x in self.supported_data_width))))
455 raise
456
457 # Check Address Width
458 if address_width not in self.supported_address_width:
459 self.logger.error("Unsupported {} {} supporteds: {:s}".format(
460 colorer("Address Width", color="red"),
461 colorer(address_width),
462 colorer(", ".join(str(x) for x in self.supported_address_width))))
463 raise
464
465 # Check Alignment
466 if alignment not in self.supported_alignment:
467 self.logger.error("Unsupported {}: {} supporteds: {:s}".format(
468 colorer("Alignment", color="red"),
469 colorer(alignment),
470 colorer(", ".join(str(x) for x in self.supported_alignment))))
471 raise
472 if data_width > alignment:
473 self.logger.error("Alignment ({}) {} Data Width ({})".format(
474 colorer(alignment),
475 colorer("should be >=", color="red"),
476 colorer(data_width)))
477 raise
478
479 # Check Paging
480 if paging not in self.supported_paging:
481 self.logger.error("Unsupported {} 0x{}, supporteds: {:s}".format(
482 colorer("Paging", color="red"),
483 colorer("{:x}".format(paging)),
484 colorer(", ".join("0x{:x}".format(x) for x in self.supported_paging))))
485 raise
486
487 # Create CSR Handler
488 self.data_width = data_width
489 self.address_width = address_width
490 self.alignment = alignment
491 self.paging = paging
492 self.masters = {}
493 self.regions = {}
494 self.logger.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).".format(
495 colorer(self.data_width),
496 colorer(self.alignment),
497 colorer(2**self.address_width/2**10),
498 colorer(self.paging),
499 colorer(self.n_locs)))
500
501 # Adding reserved CSRs
502 self.logger.info("Adding {} CSRs...".format(colorer("reserved", color="cyan")))
503 for name, n in reserved_csrs.items():
504 self.add(name, n)
505
506 self.logger.info("CSR Handler {}.".format(colorer("created", color="green")))
507
508 # Update CSR Alignment ----------------------------------------------------------------------------
509 def update_alignment(self, alignment):
510 # Check Alignment
511 if alignment not in self.supported_alignment:
512 self.logger.error("Unsupported {}: {} supporteds: {:s}".format(
513 colorer("Alignment", color="red"),
514 colorer(alignment),
515 colorer(", ".join(str(x) for x in self.supported_alignment))))
516 raise
517 self.logger.info("Alignment {} from {}-bit to {}-bit.".format(
518 colorer("updated", color="cyan"),
519 colorer(self.alignment),
520 colorer(alignment)))
521 self.alignment = alignment
522
523 # Add Master -----------------------------------------------------------------------------------
524 def add_master(self, name=None, master=None):
525 if name is None:
526 name = "master{:d}".format(len(self.masters))
527 if name in self.masters.keys():
528 self.logger.error("{} {} as CSR Master:".format(
529 colorer(name),
530 colorer("already declared", color="red")))
531 self.logger.error(self)
532 raise
533 if master.data_width != self.data_width:
534 self.logger.error("{} Master/Handler Data Width {} ({} vs {}).".format(
535 colorer(name),
536 colorer("missmatch", color="red"),
537 colorer(master.data_width),
538 colorer(self.data_width)))
539 raise
540 self.masters[name] = master
541 self.logger.info("{} {} as CSR Master.".format(
542 colorer(name, color="underline"),
543 colorer("added", color="green")))
544
545 # Add Region -----------------------------------------------------------------------------------
546 def add_region(self, name, region):
547 # FIXME: add checks
548 self.regions[name] = region
549
550 # Address map ----------------------------------------------------------------------------------
551 def address_map(self, name, memory):
552 if memory is not None:
553 name = name + "_" + memory.name_override
554 if self.locs.get(name, None) is None:
555 self.logger.error("CSR {} {}.".format(
556 colorer(name),
557 colorer("not found", color="red")))
558 self.logger.error(self)
559 raise
560 return self.locs[name]
561
562 # Str ------------------------------------------------------------------------------------------
563 def __str__(self):
564 r = "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
565 colorer(self.data_width),
566 colorer(self.alignment),
567 colorer(2**self.address_width/2**10),
568 colorer(self.paging),
569 colorer(self.n_locs))
570 r += SoCLocHandler.__str__(self)
571 r = r[:-1]
572 return r
573
574 # SoCIRQHandler ------------------------------------------------------------------------------------
575
576 class SoCIRQHandler(SoCLocHandler):
577 # Creation -------------------------------------------------------------------------------------
578 def __init__(self, n_irqs=32, reserved_irqs={}):
579 SoCLocHandler.__init__(self, "IRQ", n_locs=n_irqs)
580 self.logger = logging.getLogger("SoCIRQHandler")
581 self.logger.info("Creating IRQ Handler...")
582
583 # Check IRQ Number
584 if n_irqs > 32:
585 self.logger.error("Unsupported IRQs number: {} supporteds: {:s}".format(
586 colorer(n, color="red"), colorer("Up to 32", color="green")))
587 raise
588
589 # Create IRQ Handler
590 self.logger.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs)))
591
592 # Adding reserved IRQs
593 self.logger.info("Adding {} IRQs...".format(colorer("reserved", color="cyan")))
594 for name, n in reserved_irqs.items():
595 self.add(name, n)
596
597 self.logger.info("IRQ Handler {}.".format(colorer("created", color="green")))
598
599 # Str ------------------------------------------------------------------------------------------
600 def __str__(self):
601 r ="IRQ Handler (up to {} Locations).\n".format(colorer(self.n_locs))
602 r += SoCLocHandler.__str__(self)
603 r = r[:-1]
604 return r
605
606 # SoCController ------------------------------------------------------------------------------------
607
608 class SoCController(Module, AutoCSR):
609 def __init__(self):
610 self._reset = CSRStorage(1, description="""
611 Write a ``1`` to this register to reset the SoC.""")
612 self._scratch = CSRStorage(32, reset=0x12345678, description="""
613 Use this register as a scratch space to verify that software read/write accesses
614 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
615 can be used to verify endianness.""")
616 self._bus_errors = CSRStatus(32, description="""
617 Total number of Wishbone bus errors (timeouts) since last reset.""")
618
619 # # #
620
621 # Reset
622 self.reset = Signal()
623 self.comb += self.reset.eq(self._reset.re)
624
625 # Bus errors
626 self.bus_error = Signal()
627 bus_errors = Signal(32)
628 self.sync += \
629 If(bus_errors != (2**len(bus_errors)-1),
630 If(self.bus_error, bus_errors.eq(bus_errors + 1))
631 )
632 self.comb += self._bus_errors.status.eq(bus_errors)
633
634 # SoC ----------------------------------------------------------------------------------------------
635
636 class SoC(Module):
637 mem_map = {}
638 def __init__(self, platform, sys_clk_freq,
639
640 bus_standard = "wishbone",
641 bus_data_width = 32,
642 bus_address_width = 32,
643 bus_timeout = 1e6,
644 bus_reserved_regions = {},
645
646 csr_data_width = 32,
647 csr_address_width = 14,
648 csr_alignment = 32,
649 csr_paging = 0x800,
650 csr_reserved_csrs = {},
651
652 irq_n_irqs = 32,
653 irq_reserved_irqs = {},
654 ):
655
656 self.logger = logging.getLogger("SoC")
657 self.logger.info(colorer(" __ _ __ _ __ ", color="bright"))
658 self.logger.info(colorer(" / / (_) /____ | |/_/ ", color="bright"))
659 self.logger.info(colorer(" / /__/ / __/ -_)> < ", color="bright"))
660 self.logger.info(colorer(" /____/_/\\__/\\__/_/|_| ", color="bright"))
661 self.logger.info(colorer(" Build your hardware, easily!", color="bright"))
662
663 self.logger.info(colorer("-"*80, color="bright"))
664 self.logger.info(colorer("Creating SoC... ({})".format(build_time())))
665 self.logger.info(colorer("-"*80, color="bright"))
666 self.logger.info("FPGA device : {}.".format(platform.device))
667 self.logger.info("System clock: {:3.2f}MHz.".format(sys_clk_freq/1e6))
668
669 # SoC attributes ---------------------------------------------------------------------------
670 self.platform = platform
671 self.sys_clk_freq = sys_clk_freq
672 self.constants = {}
673 self.csr_regions = {}
674
675 # SoC Bus Handler --------------------------------------------------------------------------
676 self.submodules.bus = SoCBusHandler(
677 standard = bus_standard,
678 data_width = bus_data_width,
679 address_width = bus_address_width,
680 timeout = bus_timeout,
681 reserved_regions = bus_reserved_regions,
682 )
683
684 # SoC Bus Handler --------------------------------------------------------------------------
685 self.submodules.csr = SoCCSRHandler(
686 data_width = csr_data_width,
687 address_width = csr_address_width,
688 alignment = csr_alignment,
689 paging = csr_paging,
690 reserved_csrs = csr_reserved_csrs,
691 )
692
693 # SoC IRQ Handler --------------------------------------------------------------------------
694 self.submodules.irq = SoCIRQHandler(
695 n_irqs = irq_n_irqs,
696 reserved_irqs = irq_reserved_irqs
697 )
698
699 self.logger.info(colorer("-"*80, color="bright"))
700 self.logger.info(colorer("Initial SoC:"))
701 self.logger.info(colorer("-"*80, color="bright"))
702 self.logger.info(self.bus)
703 self.logger.info(self.csr)
704 self.logger.info(self.irq)
705 self.logger.info(colorer("-"*80, color="bright"))
706
707 self.add_config("CLOCK_FREQUENCY", int(sys_clk_freq))
708
709 # SoC Helpers ----------------------------------------------------------------------------------
710 def check_if_exists(self, name):
711 if hasattr(self, name):
712 self.logger.error("{} SubModule already {}.".format(
713 colorer(name),
714 colorer("declared", color="red")))
715 raise
716
717 def add_constant(self, name, value=None):
718 name = name.upper()
719 if name in self.constants.keys():
720 self.logger.error("{} Constant already {}.".format(
721 colorer(name),
722 colorer("declared", color="red")))
723 raise
724 self.constants[name] = SoCConstant(value)
725
726 def add_config(self, name, value):
727 name = "CONFIG_" + name
728 if isinstance(value, str):
729 self.add_constant(name + "_" + value)
730 else:
731 self.add_constant(name, value)
732
733 # SoC Main Components --------------------------------------------------------------------------
734 def add_controller(self, name="ctrl"):
735 self.check_if_exists(name)
736 setattr(self.submodules, name, SoCController())
737 self.csr.add(name, use_loc_if_exists=True)
738
739 def add_ram(self, name, origin, size, contents=[], mode="rw"):
740 ram_bus = wishbone.Interface(data_width=self.bus.data_width)
741 ram = wishbone.SRAM(size, bus=ram_bus, init=contents, read_only=(mode == "r"))
742 self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
743 self.check_if_exists(name)
744 self.logger.info("RAM {} {} {}.".format(
745 colorer(name),
746 colorer("added", color="green"),
747 self.bus.regions[name]))
748 setattr(self.submodules, name, ram)
749
750 def add_rom(self, name, origin, size, contents=[]):
751 self.add_ram(name, origin, size, contents, mode="r")
752
753 def add_csr_bridge(self, origin):
754 self.submodules.csr_bridge = wishbone2csr.WB2CSR(
755 bus_csr = csr_bus.Interface(
756 address_width = self.csr.address_width,
757 data_width = self.csr.data_width))
758 csr_size = 2**(self.csr.address_width + 2)
759 csr_region = SoCRegion(origin=origin, size=csr_size, cached=False)
760 self.bus.add_slave("csr", self.csr_bridge.wishbone, csr_region)
761 self.csr.add_master(name="bridge", master=self.csr_bridge.csr)
762 self.add_config("CSR_DATA_WIDTH", self.csr.data_width)
763 self.add_config("CSR_ALIGNMENT", self.csr.alignment)
764
765 def add_cpu(self, name="vexriscv", variant="standard", reset_address=None):
766 if name not in cpu.CPUS.keys():
767 self.logger.error("{} CPU {}, supporteds: {}".format(
768 colorer(name),
769 colorer("not supported", color="red"),
770 colorer(", ".join(cpu.CPUS.keys()))))
771 raise
772 # Add CPU
773 self.submodules.cpu = cpu.CPUS[name](self.platform, variant)
774 # Update SoC with CPU constraints
775 for n, (origin, size) in enumerate(self.cpu.io_regions.items()):
776 self.bus.add_region("io{}".format(n), SoCIORegion(origin=origin, size=size, cached=False))
777 self.mem_map.update(self.cpu.mem_map) # FIXME
778 self.csr.update_alignment(self.cpu.data_width)
779 # Add Bus Masters/CSR/IRQs
780 if not isinstance(self.cpu, cpu.CPUNone):
781 if reset_address is None:
782 reset_address = self.mem_map["rom"]
783 self.cpu.set_reset_address(reset_address)
784 for n, cpu_bus in enumerate(self.cpu.buses):
785 self.bus.add_master(name="cpu_bus{}".format(n), master=cpu_bus)
786 self.csr.add("cpu", use_loc_if_exists=True)
787 for name, loc in self.cpu.interrupts.items():
788 self.irq.add(name, loc)
789 if hasattr(self, "ctrl"):
790 self.comb += self.cpu.reset.eq(self.ctrl.reset)
791 self.add_config("CPU_RESET_ADDR", reset_address)
792 # Add constants
793 self.add_config("CPU_TYPE", str(name))
794 self.add_config("CPU_VARIANT", str(variant.split('+')[0]))
795
796 def add_timer(self, name="timer0"):
797 self.check_if_exists(name)
798 setattr(self.submodules, name, Timer())
799 self.csr.add(name, use_loc_if_exists=True)
800 self.irq.add(name, use_loc_if_exists=True)
801
802 # SoC finalization -----------------------------------------------------------------------------
803 def do_finalize(self):
804 self.logger.info(colorer("-"*80, color="bright"))
805 self.logger.info(colorer("Finalized SoC:"))
806 self.logger.info(colorer("-"*80, color="bright"))
807 self.logger.info(self.bus)
808 self.logger.info(self.csr)
809 self.logger.info(self.irq)
810 self.logger.info(colorer("-"*80, color="bright"))
811
812 # SoC Bus Interconnect ---------------------------------------------------------------------
813 bus_masters = self.bus.masters.values()
814 bus_slaves = [(self.bus.regions[n].decoder(self.bus), s) for n, s in self.bus.slaves.items()]
815 if len(bus_masters) and len(bus_slaves):
816 self.submodules.bus_interconnect = wishbone.InterconnectShared(
817 masters = bus_masters,
818 slaves = bus_slaves,
819 register = True,
820 timeout_cycles = self.bus.timeout)
821 if hasattr(self, "ctrl") and self.bus.timeout is not None:
822 self.comb += self.ctrl.bus_error.eq(self.bus_interconnect.timeout.error)
823
824 # SoC CSR Interconnect ---------------------------------------------------------------------
825 self.submodules.csr_bankarray = csr_bus.CSRBankArray(self,
826 address_map = self.csr.address_map,
827 data_width = self.csr.data_width,
828 address_width = self.csr.address_width,
829 alignment = self.csr.alignment,
830 paging = self.csr.paging,
831 soc_bus_data_width = self.bus.data_width)
832 if len(self.csr.masters):
833 self.submodules.csr_interconnect = csr_bus.InterconnectShared(
834 masters = list(self.csr.masters.values()),
835 slaves = self.csr_bankarray.get_buses())
836
837 # Add CSRs regions
838 for name, csrs, mapaddr, rmap in self.csr_bankarray.banks:
839 self.csr.add_region(name, SoCCSRRegion(
840 origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
841 busword = self.csr.data_width,
842 obj = csrs))
843
844 # Add Memory regions
845 for name, memory, mapaddr, mmap in self.csr_bankarray.srams:
846 self.csr.add_region(name + "_" + memory.name_override, SoCCSRRegion(
847 origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
848 busword = self.csr.data_width,
849 obj = memory))
850
851 # Sort CSR regions by origin
852 self.csr.regions = {k: v for k, v in sorted(self.csr.regions.items(), key=lambda item: item[1].origin)}
853
854 # Add CSRs / Config items to constants
855 for name, constant in self.csr_bankarray.constants:
856 self.add_constant(name + "_" + constant.name, constant.value.value)
857
858 # SoC CPU Check ----------------------------------------------------------------------------
859 if not isinstance(self.cpu, cpu.CPUNone):
860 if "sram" not in self.bus.regions.keys():
861 self.logger.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
862 colorer("sram"),
863 colorer("defined", color="red")))
864 self.logger.error(self.bus)
865 raise
866 cpu_reset_address_valid = False
867 for name, container in self.bus.regions.items():
868 if self.bus.check_region_is_in(
869 region = SoCRegion(origin=self.cpu.reset_address, size=self.bus.data_width//8),
870 container = container):
871 cpu_reset_address_valid = True
872 if name == "rom":
873 self.cpu.use_rom = True
874 if not cpu_reset_address_valid:
875 self.logger.error("CPU needs {} to be in a {} Region.".format(
876 colorer("reset address 0x{:08x}".format(self.cpu.reset_address)),
877 colorer("defined", color="red")))
878 self.logger.error(self.bus)
879 raise
880
881 # SoC IRQ Interconnect ---------------------------------------------------------------------
882 if hasattr(self, "cpu"):
883 if hasattr(self.cpu, "interrupt"):
884 for name, loc in sorted(self.irq.locs.items()):
885 if name in self.cpu.interrupts.keys():
886 continue
887 if hasattr(self, name):
888 module = getattr(self, name)
889 if not hasattr(module, "ev"):
890 self.logger.error("EventManager {} in {} SubModule.".format(
891 colorer("not found", color="red"),
892 colorer(name)))
893 raise
894 self.comb += self.cpu.interrupt[loc].eq(module.ev.irq)
895 self.add_constant(name + "_INTERRUPT", loc)
896
897 # SoC build ------------------------------------------------------------------------------------
898 def build(self, *args, **kwargs):
899 return self.platform.build(self, *args, **kwargs)
900
901 # LiteXSoC -----------------------------------------------------------------------------------------
902
903 class LiteXSoC(SoC):
904 # Add Identifier -------------------------------------------------------------------------------
905 def add_identifier(self, name="identifier", identifier="LiteX SoC", with_build_time=True):
906 self.check_if_exists(name)
907 if with_build_time:
908 identifier += " " + build_time()
909 setattr(self.submodules, name, Identifier(identifier))
910 self.csr.add(name + "_mem", use_loc_if_exists=True)
911
912 # Add UART -------------------------------------------------------------------------------------
913 def add_uart(self, name, baudrate=115200, fifo_depth=16):
914 from litex.soc.cores import uart
915 if name in ["stub", "stream"]:
916 self.submodules.uart = uart.UART(tx_fifo_depth=0, rx_fifo_depth=0)
917 if name == "stub":
918 self.comb += self.uart.sink.ready.eq(1)
919 elif name == "bridge":
920 self.submodules.uart = uart.UARTWishboneBridge(
921 pads = self.platform.request("serial"),
922 clk_freq = self.sys_clk_freq,
923 baudrate = baudrate)
924 self.bus.add_master(name="uart_bridge", master=self.uart.wishbone)
925 elif name == "crossover":
926 self.submodules.uart = uart.UARTCrossover()
927 else:
928 if name == "jtag_atlantic":
929 from litex.soc.cores.jtag import JTAGAtlantic
930 self.submodules.uart_phy = JTAGAtlantic()
931 elif name == "jtag_uart":
932 from litex.soc.cores.jtag import JTAGPHY
933 self.submodules.uart_phy = JTAGPHY(device=self.platform.device)
934 else:
935 self.submodules.uart_phy = uart.UARTPHY(
936 pads = self.platform.request(name),
937 clk_freq = self.sys_clk_freq,
938 baudrate = baudrate)
939 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
940 tx_fifo_depth = fifo_depth,
941 rx_fifo_depth = fifo_depth))
942 self.csr.add("uart_phy", use_loc_if_exists=True)
943 self.csr.add("uart", use_loc_if_exists=True)
944 self.irq.add("uart", use_loc_if_exists=True)
945
946 # Add SDRAM ------------------------------------------------------------------------------------
947 def add_sdram(self, name, phy, module, origin, size=None,
948 l2_cache_size = 8192,
949 l2_cache_min_data_width = 128,
950 l2_cache_reverse = True,
951 l2_cache_full_memory_we = True,
952 **kwargs):
953
954 # Imports
955 from litedram.core import LiteDRAMCore
956 from litedram.frontend.wishbone import LiteDRAMWishbone2Native
957 from litedram.frontend.axi import LiteDRAMAXI2Native
958
959 # LiteDRAM core
960 self.submodules.sdram = LiteDRAMCore(
961 phy = phy,
962 geom_settings = module.geom_settings,
963 timing_settings = module.timing_settings,
964 clk_freq = self.sys_clk_freq,
965 **kwargs)
966 self.csr.add("sdram")
967
968 # LiteDRAM port
969 port = self.sdram.crossbar.get_port()
970 port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2
971
972 # SDRAM size
973 sdram_size = 2**(module.geom_settings.bankbits +
974 module.geom_settings.rowbits +
975 module.geom_settings.colbits)*phy.settings.databits//8
976 if size is not None:
977 sdram_size = min(sdram_size, size)
978 self.bus.add_region("main_ram", SoCRegion(origin=origin, size=sdram_size))
979
980 # SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
981 if self.cpu.name == "rocket":
982 # Rocket has its own I/D L1 cache: connect directly to LiteDRAM when possible.
983 if port.data_width == self.cpu.mem_axi.data_width:
984 self.logger.info("Matching AXI MEM data width ({})\n".format(port.data_width))
985 self.submodules += LiteDRAMAXI2Native(
986 axi = self.cpu.mem_axi,
987 port = port,
988 base_address = self.bus.regions["main_ram"].origin)
989 else:
990 self.logger.info("Converting MEM data width: {} to {} via Wishbone".format(
991 port.data_width,
992 self.cpu.mem_axi.data_width))
993 # FIXME: replace WB data-width converter with native AXI converter!!!
994 mem_wb = wishbone.Interface(
995 data_width = self.cpu.mem_axi.data_width,
996 adr_width = 32-log2_int(self.cpu.mem_axi.data_width//8))
997 # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
998 mem_a2w = ResetInserter()(axi.AXI2Wishbone(
999 axi = self.cpu.mem_axi,
1000 wishbone = mem_wb,
1001 base_address = 0))
1002 self.comb += mem_a2w.reset.eq(ResetSignal() | self.cpu.reset)
1003 self.submodules += mem_a2w
1004 litedram_wb = wishbone.Interface(port.data_width)
1005 self.submodules += LiteDRAMWishbone2Native(
1006 wishbone = litedram_wb,
1007 port = port,
1008 base_address = origin)
1009 self.submodules += wishbone.Converter(mem_wb, litedram_wb)
1010 elif self.with_wishbone:
1011 # Wishbone Slave SDRAM interface
1012 wb_sdram = wishbone.Interface()
1013 self.bus.add_slave("main_ram", wb_sdram)
1014
1015 # L2 Cache
1016 if l2_cache_size != 0:
1017 # Insert L2 cache inbetween Wishbone bus and LiteDRAM
1018 l2_cache_size = max(l2_cache_size, int(2*port.data_width/8)) # Use minimal size if lower
1019 l2_cache_size = 2**int(log2(l2_cache_size)) # Round to nearest power of 2
1020 l2_cache_data_width = max(port.data_width, l2_cache_min_data_width)
1021 l2_cache = wishbone.Cache(
1022 cachesize = l2_cache_size//4,
1023 master = wb_sdram,
1024 slave = wishbone.Interface(l2_cache_data_width),
1025 reverse = l2_cache_reverse)
1026 if l2_cache_full_memory_we:
1027 l2_cache = FullMemoryWE()(l2_cache)
1028 self.submodules.l2_cache = l2_cache
1029 litedram_wb = self.l2_cache.slave
1030 else:
1031 litedram_wb = wishbone.Interface(port.data_width)
1032 self.submodules += wishbone.Converter(wb_sdram, litedram_wb)
1033 self.add_config("L2_SIZE", l2_cache_size)
1034
1035 # Wishbone Slave <--> LiteDRAM bridge
1036 self.submodules.wishbone_bridge = LiteDRAMWishbone2Native(litedram_wb, port,
1037 base_address = self.bus.regions["main_ram"].origin)
1038
1039 # Add Ethernet ---------------------------------------------------------------------------------
1040 def add_ethernet(self, phy):
1041 # Imports
1042 from liteeth.mac import LiteEthMAC
1043 # MAC
1044 self.submodules.ethmac = LiteEthMAC(
1045 phy = phy,
1046 dw = 32,
1047 interface = "wishbone",
1048 endianness = self.cpu.endianness)
1049 ethmac_region = SoCRegion(origin=self.mem_map.get("ethmac", None), size=0x2000, cached=False)
1050 self.bus.add_slave(name="ethmac", slave=self.ethmac.bus, region=ethmac_region)
1051 self.add_csr("ethmac")
1052 self.add_interrupt("ethmac")
1053 # Timing constraints
1054 if hasattr(phy, "crg"):
1055 eth_rx_clk = phy.crg.cd_eth_rx.clk
1056 eth_tx_clk = phy.crg.cd_eth_tx.clk
1057 else:
1058 eth_rx_clk = phy.cd_eth_rx.clk
1059 eth_tx_clk = phy.cd_eth_tx.clk
1060 self.platform.add_period_constraint(eth_rx_clk, 1e9/phy.rx_clk_freq)
1061 self.platform.add_period_constraint(eth_tx_clk, 1e9/phy.tx_clk_freq)
1062 self.platform.add_false_path_constraints(
1063 self.crg.cd_sys.clk,
1064 eth_rx_clk,
1065 eth_tx_clk)
1066
1067 # Add SPI Flash --------------------------------------------------------------------------------
1068 def add_spi_flash(self, name="spiflash", mode="4x", dummy_cycles=None, clk_freq=None):
1069 assert dummy_cycles is not None # FIXME: Get dummy_cycles from SPI Flash
1070 assert mode in ["4x"] # FIXME: Add 1x support.
1071 if clk_freq is None: clk_freq = self.clk_freq/2 # FIXME: Get max clk_freq from SPI Flash
1072 spiflash = SpiFlash(
1073 pads = self.platform.request(name + mode),
1074 dummy = dummy_cycles,
1075 div = ceil(self.clk_freq/clk_freq),
1076 with_bitbang = True,
1077 endianness = self.cpu.endianness)
1078 spiflash.add_clk_primitive(self.platform.device)
1079 setattr(self.submodules, name, spiflash)
1080 self.add_memory_region(name, self.mem_map[name], 0x1000000) # FIXME: Get size from SPI Flash
1081 self.add_wb_slave(self.mem_map[name], spiflash.bus)
1082 self.add_csr(name)
1083
1084 # Add SPI SDCard -------------------------------------------------------------------------------
1085 def add_spi_sdcard(self, name="spisdcard", clk_freq=400e3):
1086 pads = self.platform.request(name)
1087 if hasattr(pads, "rst"):
1088 self.comb += pads.rst.eq(0)
1089 spisdcard = SPIMaster(pads, 8, self.sys_clk_freq, 400e3)
1090 setattr(self.submodules, name, spisdcard)
1091 self.add_csr(name)