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