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