Merge pull request #526 from rprinz08/master
[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, direction="m2s"):
286 assert direction in ["m2s", "s2m"]
287 if interface.data_width != self.data_width:
288 self.logger.info("{} Bus {} from {}-bit to {}-bit.".format(
289 colorer(name),
290 colorer("converted", color="cyan"),
291 colorer(interface.data_width),
292 colorer(self.data_width)))
293 new_interface = wishbone.Interface(data_width=self.data_width)
294 if direction == "m2s":
295 converter = wishbone.Converter(master=interface, slave=new_interface)
296 if direction == "s2m":
297 converter = wishbone.Converter(master=new_interface, slave=interface)
298 self.submodules += converter
299 return new_interface
300 else:
301 return interface
302
303 def add_master(self, name=None, master=None):
304 if name is None:
305 name = "master{:d}".format(len(self.masters))
306 if name in self.masters.keys():
307 self.logger.error("{} {} as Bus Master:".format(
308 colorer(name),
309 colorer("already declared", color="red")))
310 self.logger.error(self)
311 raise
312 master = self.add_adapter(name, master, "m2s")
313 self.masters[name] = master
314 self.logger.info("{} {} as Bus Master.".format(
315 colorer(name, color="underline"),
316 colorer("added", color="green")))
317
318 def add_slave(self, name=None, slave=None, region=None):
319 no_name = name is None
320 no_region = region is None
321 if no_name and no_region:
322 self.logger.error("Please {} {} or/and {} of Bus Slave.".format(
323 colorer("specify", color="red"),
324 colorer("name"),
325 colorer("region")))
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("{} Region {}.".format(
333 colorer(name),
334 colorer("not found", color="red")))
335 raise
336 else:
337 self.add_region(name, region)
338 if name in self.slaves.keys():
339 self.logger.error("{} {} as Bus Slave:".format(
340 colorer(name),
341 colorer("already declared", color="red")))
342 self.logger.error(self)
343 raise
344 slave = self.add_adapter(name, slave, "s2m")
345 self.slaves[name] = slave
346 self.logger.info("{} {} as Bus Slave.".format(
347 colorer(name, color="underline"),
348 colorer("added", color="green")))
349
350 # Str ------------------------------------------------------------------------------------------
351 def __str__(self):
352 r = "{}-bit {} Bus, {}GiB Address Space.\n".format(
353 colorer(self.data_width), colorer(self.standard), colorer(2**self.address_width/2**30))
354 r += "IO Regions: ({})\n".format(len(self.io_regions.keys())) if len(self.io_regions.keys()) else ""
355 io_regions = {k: v for k, v in sorted(self.io_regions.items(), key=lambda item: item[1].origin)}
356 for name, region in io_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 {}.".format(
386 colorer(name), self.name, colorer("already used", color="red")))
387 self.logger.error(self)
388 raise
389 if n in self.locs.values():
390 self.logger.error("{} {} Location {}.".format(
391 colorer(n), self.name, colorer("already used", color="red")))
392 self.logger.error(self)
393 raise
394 if n is None:
395 allocated = True
396 n = self.alloc(name)
397 else:
398 if n < 0:
399 self.logger.error("{} {} Location should be {}.".format(
400 colorer(n),
401 self.name,
402 colorer("positive", color="red")))
403 raise
404 if n > self.n_locs:
405 self.logger.error("{} {} Location {} than maximum: {}.".format(
406 colorer(n),
407 self.name,
408 colorer("higher", color="red"),
409 colorer(self.n_locs)))
410 raise
411 self.locs[name] = n
412 else:
413 n = self.locs[name]
414 self.logger.info("{} {} {} at Location {}.".format(
415 colorer(name, color="underline"),
416 self.name,
417 colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
418 colorer(n)))
419
420 # Alloc ----------------------------------------------------------------------------------------
421 def alloc(self, name):
422 for n in range(self.n_locs):
423 if n not in self.locs.values():
424 return n
425 self.logger.error("Not enough Locations.")
426 self.logger.error(self)
427 raise
428
429 # Str ------------------------------------------------------------------------------------------
430 def __str__(self):
431 r = "{} Locations: ({})\n".format(self.name, len(self.locs.keys())) if len(self.locs.keys()) else ""
432 locs = {k: v for k, v in sorted(self.locs.items(), key=lambda item: item[1])}
433 length = 0
434 for name in locs.keys():
435 if len(name) > length: length = len(name)
436 for name in locs.keys():
437 r += "- {}{}: {}\n".format(colorer(name, color="underline"), " "*(length + 1 - len(name)), colorer(self.locs[name]))
438 return r
439
440 # SoCCSRHandler ------------------------------------------------------------------------------------
441
442 class SoCCSRHandler(SoCLocHandler):
443 supported_data_width = [8, 32]
444 supported_address_width = [14+i for i in range(4)]
445 supported_alignment = [32, 64]
446 supported_paging = [0x800*2**i for i in range(4)]
447
448 # Creation -------------------------------------------------------------------------------------
449 def __init__(self, data_width=32, address_width=14, alignment=32, paging=0x800, reserved_csrs={}):
450 SoCLocHandler.__init__(self, "CSR", n_locs=alignment//8*(2**address_width)//paging)
451 self.logger = logging.getLogger("SoCCSRHandler")
452 self.logger.info("Creating CSR Handler...")
453
454 # Check Data Width
455 if data_width not in self.supported_data_width:
456 self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
457 colorer("Data Width", color="red"),
458 colorer(data_width),
459 colorer(", ".join(str(x) for x in self.supported_data_width))))
460 raise
461
462 # Check Address Width
463 if address_width not in self.supported_address_width:
464 self.logger.error("Unsupported {} {} supporteds: {:s}".format(
465 colorer("Address Width", color="red"),
466 colorer(address_width),
467 colorer(", ".join(str(x) for x in self.supported_address_width))))
468 raise
469
470 # Check Alignment
471 if alignment not in self.supported_alignment:
472 self.logger.error("Unsupported {}: {} supporteds: {:s}".format(
473 colorer("Alignment", color="red"),
474 colorer(alignment),
475 colorer(", ".join(str(x) for x in self.supported_alignment))))
476 raise
477 if data_width > alignment:
478 self.logger.error("Alignment ({}) {} Data Width ({})".format(
479 colorer(alignment),
480 colorer("should be >=", color="red"),
481 colorer(data_width)))
482 raise
483
484 # Check Paging
485 if paging not in self.supported_paging:
486 self.logger.error("Unsupported {} 0x{}, supporteds: {:s}".format(
487 colorer("Paging", color="red"),
488 colorer("{:x}".format(paging)),
489 colorer(", ".join("0x{:x}".format(x) for x in self.supported_paging))))
490 raise
491
492 # Create CSR Handler
493 self.data_width = data_width
494 self.address_width = address_width
495 self.alignment = alignment
496 self.paging = paging
497 self.masters = {}
498 self.regions = {}
499 self.logger.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).".format(
500 colorer(self.data_width),
501 colorer(self.alignment),
502 colorer(2**self.address_width/2**10),
503 colorer(self.paging),
504 colorer(self.n_locs)))
505
506 # Adding reserved CSRs
507 self.logger.info("Adding {} CSRs...".format(colorer("reserved", color="cyan")))
508 for name, n in reserved_csrs.items():
509 self.add(name, n)
510
511 self.logger.info("CSR Handler {}.".format(colorer("created", color="green")))
512
513 # Update CSR Alignment ----------------------------------------------------------------------------
514 def update_alignment(self, alignment):
515 # Check Alignment
516 if alignment not in self.supported_alignment:
517 self.logger.error("Unsupported {}: {} supporteds: {:s}".format(
518 colorer("Alignment", color="red"),
519 colorer(alignment),
520 colorer(", ".join(str(x) for x in self.supported_alignment))))
521 raise
522 self.logger.info("Alignment {} from {}-bit to {}-bit.".format(
523 colorer("updated", color="cyan"),
524 colorer(self.alignment),
525 colorer(alignment)))
526 self.alignment = alignment
527
528 # Add Master -----------------------------------------------------------------------------------
529 def add_master(self, name=None, master=None):
530 if name is None:
531 name = "master{:d}".format(len(self.masters))
532 if name in self.masters.keys():
533 self.logger.error("{} {} as CSR Master:".format(
534 colorer(name),
535 colorer("already declared", color="red")))
536 self.logger.error(self)
537 raise
538 if master.data_width != self.data_width:
539 self.logger.error("{} Master/Handler Data Width {} ({} vs {}).".format(
540 colorer(name),
541 colorer("missmatch", color="red"),
542 colorer(master.data_width),
543 colorer(self.data_width)))
544 raise
545 self.masters[name] = master
546 self.logger.info("{} {} as CSR Master.".format(
547 colorer(name, color="underline"),
548 colorer("added", color="green")))
549
550 # Add Region -----------------------------------------------------------------------------------
551 def add_region(self, name, region):
552 # FIXME: add checks
553 self.regions[name] = region
554
555 # Address map ----------------------------------------------------------------------------------
556 def address_map(self, name, memory):
557 if memory is not None:
558 name = name + "_" + memory.name_override
559 if self.locs.get(name, None) is None:
560 self.logger.error("CSR {} {}.".format(
561 colorer(name),
562 colorer("not found", color="red")))
563 self.logger.error(self)
564 raise
565 return self.locs[name]
566
567 # Str ------------------------------------------------------------------------------------------
568 def __str__(self):
569 r = "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
570 colorer(self.data_width),
571 colorer(self.alignment),
572 colorer(2**self.address_width/2**10),
573 colorer(self.paging),
574 colorer(self.n_locs))
575 r += SoCLocHandler.__str__(self)
576 r = r[:-1]
577 return r
578
579 # SoCIRQHandler ------------------------------------------------------------------------------------
580
581 class SoCIRQHandler(SoCLocHandler):
582 # Creation -------------------------------------------------------------------------------------
583 def __init__(self, n_irqs=32, reserved_irqs={}):
584 SoCLocHandler.__init__(self, "IRQ", n_locs=n_irqs)
585 self.logger = logging.getLogger("SoCIRQHandler")
586 self.logger.info("Creating IRQ Handler...")
587
588 # Check IRQ Number
589 if n_irqs > 32:
590 self.logger.error("Unsupported IRQs number: {} supporteds: {:s}".format(
591 colorer(n, color="red"), colorer("Up to 32", color="green")))
592 raise
593
594 # Create IRQ Handler
595 self.logger.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs)))
596
597 # Adding reserved IRQs
598 self.logger.info("Adding {} IRQs...".format(colorer("reserved", color="cyan")))
599 for name, n in reserved_irqs.items():
600 self.add(name, n)
601
602 self.logger.info("IRQ Handler {}.".format(colorer("created", color="green")))
603
604 # Str ------------------------------------------------------------------------------------------
605 def __str__(self):
606 r ="IRQ Handler (up to {} Locations).\n".format(colorer(self.n_locs))
607 r += SoCLocHandler.__str__(self)
608 r = r[:-1]
609 return r
610
611 # SoCController ------------------------------------------------------------------------------------
612
613 class SoCController(Module, AutoCSR):
614 def __init__(self,
615 with_reset = True,
616 with_scratch = True,
617 with_errors = True,
618 with_uptime = False):
619
620 if with_reset:
621 self._reset = CSRStorage(1, description="""Write a ``1`` to this register to reset the SoC.""")
622 if with_scratch:
623 self._scratch = CSRStorage(32, reset=0x12345678, description="""
624 Use this register as a scratch space to verify that software read/write accesses
625 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
626 can be used to verify endianness.""")
627 if with_errors:
628 self._bus_errors = CSRStatus(32, description="Total number of Wishbone bus errors (timeouts) since start.")
629
630 if with_uptime:
631 self._uptime_latch = CSRStorage(description="Write a ``1`` to latch current uptime to ``time`` register.")
632 self._uptime = CSRStatus(64, description="Latched uptime since start (in ``sys_clk`` cycles).")
633
634 # # #
635
636 # Reset
637 if with_reset:
638 self.reset = Signal()
639 self.comb += self.reset.eq(self._reset.re)
640
641 # Errors
642 if with_errors:
643 self.bus_error = Signal()
644 bus_errors = Signal(32)
645 self.sync += [
646 If(bus_errors != (2**len(bus_errors)-1),
647 If(self.bus_error, bus_errors.eq(bus_errors + 1))
648 )
649 ]
650 self.comb += self._bus_errors.status.eq(bus_errors)
651
652 # Uptime
653 if with_uptime:
654 uptime = Signal(64, reset_less=True)
655 self.sync += uptime.eq(uptime + 1)
656 self.sync += If(self._uptime_latch.re, self._uptime.status.eq(uptime))
657
658 # SoC ----------------------------------------------------------------------------------------------
659
660 class SoC(Module):
661 mem_map = {}
662 def __init__(self, platform, sys_clk_freq,
663
664 bus_standard = "wishbone",
665 bus_data_width = 32,
666 bus_address_width = 32,
667 bus_timeout = 1e6,
668 bus_reserved_regions = {},
669
670 csr_data_width = 32,
671 csr_address_width = 14,
672 csr_alignment = 32,
673 csr_paging = 0x800,
674 csr_reserved_csrs = {},
675
676 irq_n_irqs = 32,
677 irq_reserved_irqs = {},
678 ):
679
680 self.logger = logging.getLogger("SoC")
681 self.logger.info(colorer(" __ _ __ _ __ ", color="bright"))
682 self.logger.info(colorer(" / / (_) /____ | |/_/ ", color="bright"))
683 self.logger.info(colorer(" / /__/ / __/ -_)> < ", color="bright"))
684 self.logger.info(colorer(" /____/_/\\__/\\__/_/|_| ", color="bright"))
685 self.logger.info(colorer(" Build your hardware, easily!", color="bright"))
686
687 self.logger.info(colorer("-"*80, color="bright"))
688 self.logger.info(colorer("Creating SoC... ({})".format(build_time())))
689 self.logger.info(colorer("-"*80, color="bright"))
690 self.logger.info("FPGA device : {}.".format(platform.device))
691 self.logger.info("System clock: {:3.2f}MHz.".format(sys_clk_freq/1e6))
692
693 # SoC attributes ---------------------------------------------------------------------------
694 self.platform = platform
695 self.sys_clk_freq = sys_clk_freq
696 self.constants = {}
697 self.csr_regions = {}
698
699 # SoC Bus Handler --------------------------------------------------------------------------
700 self.submodules.bus = SoCBusHandler(
701 standard = bus_standard,
702 data_width = bus_data_width,
703 address_width = bus_address_width,
704 timeout = bus_timeout,
705 reserved_regions = bus_reserved_regions,
706 )
707
708 # SoC Bus Handler --------------------------------------------------------------------------
709 self.submodules.csr = SoCCSRHandler(
710 data_width = csr_data_width,
711 address_width = csr_address_width,
712 alignment = csr_alignment,
713 paging = csr_paging,
714 reserved_csrs = csr_reserved_csrs,
715 )
716
717 # SoC IRQ Handler --------------------------------------------------------------------------
718 self.submodules.irq = SoCIRQHandler(
719 n_irqs = irq_n_irqs,
720 reserved_irqs = irq_reserved_irqs
721 )
722
723 self.logger.info(colorer("-"*80, color="bright"))
724 self.logger.info(colorer("Initial SoC:"))
725 self.logger.info(colorer("-"*80, color="bright"))
726 self.logger.info(self.bus)
727 self.logger.info(self.csr)
728 self.logger.info(self.irq)
729 self.logger.info(colorer("-"*80, color="bright"))
730
731 self.add_config("CLOCK_FREQUENCY", int(sys_clk_freq))
732
733 # SoC Helpers ----------------------------------------------------------------------------------
734 def check_if_exists(self, name):
735 if hasattr(self, name):
736 self.logger.error("{} SubModule already {}.".format(
737 colorer(name),
738 colorer("declared", color="red")))
739 raise
740
741 def add_constant(self, name, value=None):
742 name = name.upper()
743 if name in self.constants.keys():
744 self.logger.error("{} Constant already {}.".format(
745 colorer(name),
746 colorer("declared", color="red")))
747 raise
748 self.constants[name] = SoCConstant(value)
749
750 def add_config(self, name, value=None):
751 name = "CONFIG_" + name
752 if isinstance(value, str):
753 self.add_constant(name + "_" + value)
754 else:
755 self.add_constant(name, value)
756
757 # SoC Main Components --------------------------------------------------------------------------
758 def add_controller(self, name="ctrl", **kwargs):
759 self.check_if_exists(name)
760 setattr(self.submodules, name, SoCController(**kwargs))
761 self.csr.add(name, use_loc_if_exists=True)
762
763 def add_ram(self, name, origin, size, contents=[], mode="rw"):
764 ram_bus = wishbone.Interface(data_width=self.bus.data_width)
765 ram = wishbone.SRAM(size, bus=ram_bus, init=contents, read_only=(mode == "r"))
766 self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
767 self.check_if_exists(name)
768 self.logger.info("RAM {} {} {}.".format(
769 colorer(name),
770 colorer("added", color="green"),
771 self.bus.regions[name]))
772 setattr(self.submodules, name, ram)
773
774 def add_rom(self, name, origin, size, contents=[]):
775 self.add_ram(name, origin, size, contents, mode="r")
776
777 def add_csr_bridge(self, origin):
778 self.submodules.csr_bridge = wishbone2csr.WB2CSR(
779 bus_csr = csr_bus.Interface(
780 address_width = self.csr.address_width,
781 data_width = self.csr.data_width))
782 csr_size = 2**(self.csr.address_width + 2)
783 csr_region = SoCRegion(origin=origin, size=csr_size, cached=False)
784 self.bus.add_slave("csr", self.csr_bridge.wishbone, csr_region)
785 self.csr.add_master(name="bridge", master=self.csr_bridge.csr)
786 self.add_config("CSR_DATA_WIDTH", self.csr.data_width)
787 self.add_config("CSR_ALIGNMENT", self.csr.alignment)
788
789 def add_cpu(self, name="vexriscv", variant="standard", cls=None, reset_address=None):
790 if name not in cpu.CPUS.keys():
791 self.logger.error("{} CPU {}, supporteds: {}".format(
792 colorer(name),
793 colorer("not supported", color="red"),
794 colorer(", ".join(cpu.CPUS.keys()))))
795 raise
796 # Add CPU
797 cpu_cls = cls if cls is not None else cpu.CPUS[name]
798 self.submodules.cpu = cpu_cls(self.platform, variant)
799 # Update SoC with CPU constraints
800 for n, (origin, size) in enumerate(self.cpu.io_regions.items()):
801 self.bus.add_region("io{}".format(n), SoCIORegion(origin=origin, size=size, cached=False))
802 self.mem_map.update(self.cpu.mem_map) # FIXME
803 # Add Bus Masters/CSR/IRQs
804 if not isinstance(self.cpu, cpu.CPUNone):
805 self.csr.update_alignment(self.cpu.data_width)
806 if reset_address is None:
807 reset_address = self.mem_map["rom"]
808 self.cpu.set_reset_address(reset_address)
809 for n, cpu_bus in enumerate(self.cpu.periph_buses):
810 self.bus.add_master(name="cpu_bus{}".format(n), master=cpu_bus)
811 self.csr.add("cpu", use_loc_if_exists=True)
812 if hasattr(self.cpu, "interrupt"):
813 for name, loc in self.cpu.interrupts.items():
814 self.irq.add(name, loc)
815 self.add_config("CPU_HAS_INTERRUPT")
816
817
818 if hasattr(self, "ctrl"):
819 if hasattr(self.ctrl, "reset"):
820 self.comb += self.cpu.reset.eq(self.ctrl.reset)
821 self.add_config("CPU_RESET_ADDR", reset_address)
822 # Add constants
823 self.add_config("CPU_TYPE", str(name))
824 self.add_config("CPU_VARIANT", str(variant.split('+')[0]))
825 self.add_constant("CONFIG_CPU_HUMAN_NAME", getattr(self.cpu, "human_name", "Unknown"))
826 if hasattr(self.cpu, "nop"):
827 self.add_constant("CONFIG_CPU_NOP", self.cpu.nop)
828
829 def add_timer(self, name="timer0"):
830 self.check_if_exists(name)
831 setattr(self.submodules, name, Timer())
832 self.csr.add(name, use_loc_if_exists=True)
833 if hasattr(self.cpu, "interrupt"):
834 self.irq.add(name, use_loc_if_exists=True)
835
836 # SoC finalization -----------------------------------------------------------------------------
837 def do_finalize(self):
838 self.logger.info(colorer("-"*80, color="bright"))
839 self.logger.info(colorer("Finalized SoC:"))
840 self.logger.info(colorer("-"*80, color="bright"))
841 self.logger.info(self.bus)
842 self.logger.info(self.csr)
843 self.logger.info(self.irq)
844 self.logger.info(colorer("-"*80, color="bright"))
845
846 # SoC Bus Interconnect ---------------------------------------------------------------------
847 if len(self.bus.masters) and len(self.bus.slaves):
848 # If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
849 if ((len(self.bus.masters) == 1) and
850 (len(self.bus.slaves) == 1) and
851 (next(iter(self.bus.regions.values())).origin == 0)):
852 self.submodules.bus_interconnect = wishbone.InterconnectPointToPoint(
853 master = next(iter(self.bus.masters.values())),
854 slave = next(iter(self.bus.slaves.values())))
855 # Otherwise, use InterconnectShared.
856 else:
857 self.submodules.bus_interconnect = wishbone.InterconnectShared(
858 masters = self.bus.masters.values(),
859 slaves = [(self.bus.regions[n].decoder(self.bus), s) for n, s in self.bus.slaves.items()],
860 register = True,
861 timeout_cycles = self.bus.timeout)
862 if hasattr(self, "ctrl") and self.bus.timeout is not None:
863 if hasattr(self.ctrl, "bus_error"):
864 self.comb += self.ctrl.bus_error.eq(self.bus_interconnect.timeout.error)
865 self.bus.logger.info("Interconnect: {} ({} <-> {}).".format(
866 colorer(self.bus_interconnect.__class__.__name__),
867 colorer(len(self.bus.masters)),
868 colorer(len(self.bus.slaves))))
869
870 # SoC CSR Interconnect ---------------------------------------------------------------------
871 self.submodules.csr_bankarray = csr_bus.CSRBankArray(self,
872 address_map = self.csr.address_map,
873 data_width = self.csr.data_width,
874 address_width = self.csr.address_width,
875 alignment = self.csr.alignment,
876 paging = self.csr.paging,
877 soc_bus_data_width = self.bus.data_width)
878 if len(self.csr.masters):
879 self.submodules.csr_interconnect = csr_bus.InterconnectShared(
880 masters = list(self.csr.masters.values()),
881 slaves = self.csr_bankarray.get_buses())
882
883 # Add CSRs regions
884 for name, csrs, mapaddr, rmap in self.csr_bankarray.banks:
885 self.csr.add_region(name, SoCCSRRegion(
886 origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
887 busword = self.csr.data_width,
888 obj = csrs))
889
890 # Add Memory regions
891 for name, memory, mapaddr, mmap in self.csr_bankarray.srams:
892 self.csr.add_region(name + "_" + memory.name_override, SoCCSRRegion(
893 origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
894 busword = self.csr.data_width,
895 obj = memory))
896
897 # Sort CSR regions by origin
898 self.csr.regions = {k: v for k, v in sorted(self.csr.regions.items(), key=lambda item: item[1].origin)}
899
900 # Add CSRs / Config items to constants
901 for name, constant in self.csr_bankarray.constants:
902 self.add_constant(name + "_" + constant.name, constant.value.value)
903
904 # SoC CPU Check ----------------------------------------------------------------------------
905 if not isinstance(self.cpu, cpu.CPUNone):
906 if "sram" not in self.bus.regions.keys():
907 self.logger.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
908 colorer("sram"),
909 colorer("defined", color="red")))
910 self.logger.error(self.bus)
911 raise
912 cpu_reset_address_valid = False
913 for name, container in self.bus.regions.items():
914 if self.bus.check_region_is_in(
915 region = SoCRegion(origin=self.cpu.reset_address, size=self.bus.data_width//8),
916 container = container):
917 cpu_reset_address_valid = True
918 if name == "rom":
919 self.cpu.use_rom = True
920 if not cpu_reset_address_valid:
921 self.logger.error("CPU needs {} to be in a {} Region.".format(
922 colorer("reset address 0x{:08x}".format(self.cpu.reset_address)),
923 colorer("defined", color="red")))
924 self.logger.error(self.bus)
925 raise
926
927 # SoC IRQ Interconnect ---------------------------------------------------------------------
928 if hasattr(self, "cpu"):
929 if hasattr(self.cpu, "interrupt"):
930 for name, loc in sorted(self.irq.locs.items()):
931 if name in self.cpu.interrupts.keys():
932 continue
933 if hasattr(self, name):
934 module = getattr(self, name)
935 if not hasattr(module, "ev"):
936 self.logger.error("EventManager {} in {} SubModule.".format(
937 colorer("not found", color="red"),
938 colorer(name)))
939 raise
940 self.comb += self.cpu.interrupt[loc].eq(module.ev.irq)
941 self.add_constant(name + "_INTERRUPT", loc)
942
943 # SoC build ------------------------------------------------------------------------------------
944 def build(self, *args, **kwargs):
945 return self.platform.build(self, *args, **kwargs)
946
947 # LiteXSoC -----------------------------------------------------------------------------------------
948
949 class LiteXSoC(SoC):
950 # Add Identifier -------------------------------------------------------------------------------
951 def add_identifier(self, name="identifier", identifier="LiteX SoC", with_build_time=True):
952 self.check_if_exists(name)
953 if with_build_time:
954 identifier += " " + build_time()
955 setattr(self.submodules, name, Identifier(identifier))
956 self.csr.add(name + "_mem", use_loc_if_exists=True)
957
958 # Add UART -------------------------------------------------------------------------------------
959 def add_uart(self, name, baudrate=115200, fifo_depth=16):
960 from litex.soc.cores import uart
961
962 # Stub / Stream
963 if name in ["stub", "stream"]:
964 self.submodules.uart = uart.UART(tx_fifo_depth=0, rx_fifo_depth=0)
965 if name == "stub":
966 self.comb += self.uart.sink.ready.eq(1)
967
968 # UARTBone / Bridge
969 elif name in ["uartbone", "bridge"]:
970 self.add_uartbone(baudrate=baudrate)
971
972 # Crossover
973 elif name in ["crossover"]:
974 self.submodules.uart = uart.UARTCrossover()
975
976 # Model/Sim
977 elif name in ["model", "sim"]:
978 self.submodules.uart_phy = uart.RS232PHYModel(self.platform.request("serial"))
979 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
980 tx_fifo_depth = fifo_depth,
981 rx_fifo_depth = fifo_depth))
982
983 # JTAG Atlantic
984 elif name in ["jtag_atlantic"]:
985 from litex.soc.cores.jtag import JTAGAtlantic
986 self.submodules.uart_phy = JTAGAtlantic()
987 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
988 tx_fifo_depth = fifo_depth,
989 rx_fifo_depth = fifo_depth))
990
991 # JTAG UART
992 elif name in ["jtag_uart"]:
993 from litex.soc.cores.jtag import JTAGPHY
994 self.submodules.uart_phy = JTAGPHY(device=self.platform.device)
995 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
996 tx_fifo_depth = fifo_depth,
997 rx_fifo_depth = fifo_depth))
998
999 # USB ACM (with ValentyUSB core)
1000 elif name in ["usb_acm"]:
1001 import valentyusb.usbcore.io as usbio
1002 import valentyusb.usbcore.cpu.cdc_eptri as cdc_eptri
1003 usb_pads = self.platform.request("usb")
1004 usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup)
1005 self.submodules.uart = cdc_eptri.CDCUsb(usb_iobuf)
1006
1007 # Classic UART
1008 else:
1009 self.submodules.uart_phy = uart.UARTPHY(
1010 pads = self.platform.request(name),
1011 clk_freq = self.sys_clk_freq,
1012 baudrate = baudrate)
1013 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
1014 tx_fifo_depth = fifo_depth,
1015 rx_fifo_depth = fifo_depth))
1016
1017 self.csr.add("uart_phy", use_loc_if_exists=True)
1018 self.csr.add("uart", use_loc_if_exists=True)
1019 if hasattr(self.cpu, "interrupt"):
1020 self.irq.add("uart", use_loc_if_exists=True)
1021 else:
1022 self.add_constant("UART_POLLING")
1023
1024 # Add UARTbone ---------------------------------------------------------------------------------
1025 def add_uartbone(self, name="serial", baudrate=115200):
1026 from litex.soc.cores import uart
1027 self.submodules.uartbone = uart.UARTBone(
1028 pads = self.platform.request(name),
1029 clk_freq = self.sys_clk_freq,
1030 baudrate = baudrate)
1031 self.bus.add_master(name="uartbone", master=self.uartbone.wishbone)
1032
1033 # Add SDRAM ------------------------------------------------------------------------------------
1034 def add_sdram(self, name, phy, module, origin, size=None, with_soc_interconnect=True,
1035 l2_cache_size = 8192,
1036 l2_cache_min_data_width = 128,
1037 l2_cache_reverse = True,
1038 l2_cache_full_memory_we = True,
1039 **kwargs):
1040
1041 # Imports
1042 from litedram.common import LiteDRAMNativePort
1043 from litedram.core import LiteDRAMCore
1044 from litedram.frontend.wishbone import LiteDRAMWishbone2Native
1045 from litedram.frontend.axi import LiteDRAMAXI2Native
1046
1047 # LiteDRAM core
1048 self.submodules.sdram = LiteDRAMCore(
1049 phy = phy,
1050 geom_settings = module.geom_settings,
1051 timing_settings = module.timing_settings,
1052 clk_freq = self.sys_clk_freq,
1053 **kwargs)
1054 self.csr.add("sdram")
1055
1056 if not with_soc_interconnect: return
1057
1058 # Compute/Check SDRAM size
1059 sdram_size = 2**(module.geom_settings.bankbits +
1060 module.geom_settings.rowbits +
1061 module.geom_settings.colbits)*phy.settings.databits//8
1062 if size is not None:
1063 sdram_size = min(sdram_size, size)
1064
1065 # Add SDRAM region
1066 self.bus.add_region("main_ram", SoCRegion(origin=origin, size=sdram_size))
1067
1068 # SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
1069 if len(self.cpu.memory_buses):
1070 # When CPU has at least a direct memory bus, connect them directly to LiteDRAM.
1071 for mem_bus in self.cpu.memory_buses:
1072 # Request a LiteDRAM native port.
1073 port = self.sdram.crossbar.get_port()
1074 port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2.
1075
1076 # Check if bus is an AXI bus and connect it.
1077 if isinstance(mem_bus, axi.AXIInterface):
1078 # If same data_width, connect it directly.
1079 if port.data_width == mem_bus.data_width:
1080 self.logger.info("Matching AXI MEM data width ({})\n".format(port.data_width))
1081 self.submodules += LiteDRAMAXI2Native(
1082 axi = self.cpu.mem_axi,
1083 port = port,
1084 base_address = self.bus.regions["main_ram"].origin)
1085 # If different data_width, do the adaptation and connect it via Wishbone.
1086 else:
1087 self.logger.info("Converting MEM data width: {} to {} via Wishbone".format(
1088 port.data_width,
1089 self.cpu.mem_axi.data_width))
1090 # FIXME: replace WB data-width converter with native AXI converter!!!
1091 mem_wb = wishbone.Interface(
1092 data_width = self.cpu.mem_axi.data_width,
1093 adr_width = 32-log2_int(self.cpu.mem_axi.data_width//8))
1094 # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
1095 mem_a2w = ResetInserter()(axi.AXI2Wishbone(
1096 axi = self.cpu.mem_axi,
1097 wishbone = mem_wb,
1098 base_address = 0))
1099 self.comb += mem_a2w.reset.eq(ResetSignal() | self.cpu.reset)
1100 self.submodules += mem_a2w
1101 litedram_wb = wishbone.Interface(port.data_width)
1102 self.submodules += LiteDRAMWishbone2Native(
1103 wishbone = litedram_wb,
1104 port = port,
1105 base_address = origin)
1106 self.submodules += wishbone.Converter(mem_wb, litedram_wb)
1107 # Check if bus is a Native bus and connect it.
1108 if isinstance(mem_bus, LiteDRAMNativePort):
1109 # If same data_width, connect it directly.
1110 if port.data_width == mem_bus.data_width:
1111 self.comb += mem_bus.cmd.connect(port.cmd)
1112 self.comb += mem_bus.wdata.connect(port.wdata)
1113 self.comb += port.rdata.connect(mem_bus.rdata)
1114 # Else raise Error.
1115 else:
1116 raise NotImplementedError
1117 else:
1118 # When CPU has no direct memory interface, create a Wishbone Slave interface to LiteDRAM.
1119
1120 # Request a LiteDRAM native port.
1121 port = self.sdram.crossbar.get_port()
1122 port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2.
1123
1124 # Create Wishbone Slave.
1125 wb_sdram = wishbone.Interface()
1126 self.bus.add_slave("main_ram", wb_sdram)
1127
1128 # L2 Cache
1129 if l2_cache_size != 0:
1130 # Insert L2 cache inbetween Wishbone bus and LiteDRAM
1131 l2_cache_size = max(l2_cache_size, int(2*port.data_width/8)) # Use minimal size if lower
1132 l2_cache_size = 2**int(log2(l2_cache_size)) # Round to nearest power of 2
1133 l2_cache_data_width = max(port.data_width, l2_cache_min_data_width)
1134 l2_cache = wishbone.Cache(
1135 cachesize = l2_cache_size//4,
1136 master = wb_sdram,
1137 slave = wishbone.Interface(l2_cache_data_width),
1138 reverse = l2_cache_reverse)
1139 if l2_cache_full_memory_we:
1140 l2_cache = FullMemoryWE()(l2_cache)
1141 self.submodules.l2_cache = l2_cache
1142 litedram_wb = self.l2_cache.slave
1143 else:
1144 litedram_wb = wishbone.Interface(port.data_width)
1145 self.submodules += wishbone.Converter(wb_sdram, litedram_wb)
1146 self.add_config("L2_SIZE", l2_cache_size)
1147
1148 # Wishbone Slave <--> LiteDRAM bridge
1149 self.submodules.wishbone_bridge = LiteDRAMWishbone2Native(litedram_wb, port,
1150 base_address = self.bus.regions["main_ram"].origin)
1151
1152 # Add Ethernet ---------------------------------------------------------------------------------
1153 def add_ethernet(self, name="ethmac", phy=None):
1154 # Imports
1155 from liteeth.mac import LiteEthMAC
1156 # MAC
1157 ethmac = LiteEthMAC(
1158 phy = phy,
1159 dw = 32,
1160 interface = "wishbone",
1161 endianness = self.cpu.endianness)
1162 setattr(self.submodules, name, ethmac)
1163 ethmac_region = SoCRegion(origin=self.mem_map.get(name, None), size=0x2000, cached=False)
1164 self.bus.add_slave(name=name, slave=ethmac.bus, region=ethmac_region)
1165 self.add_csr(name)
1166 self.add_interrupt(name)
1167 # Timing constraints
1168 if hasattr(phy, "crg"):
1169 eth_rx_clk = phy.crg.cd_eth_rx.clk
1170 eth_tx_clk = phy.crg.cd_eth_tx.clk
1171 else:
1172 eth_rx_clk = phy.cd_eth_rx.clk
1173 eth_tx_clk = phy.cd_eth_tx.clk
1174 self.platform.add_period_constraint(eth_rx_clk, 1e9/phy.rx_clk_freq)
1175 self.platform.add_period_constraint(eth_tx_clk, 1e9/phy.tx_clk_freq)
1176 self.platform.add_false_path_constraints(
1177 self.crg.cd_sys.clk,
1178 eth_rx_clk,
1179 eth_tx_clk)
1180
1181 # Add Etherbone --------------------------------------------------------------------------------
1182 def add_etherbone(self, name="etherbone", phy=None, clock_domain=None,
1183 mac_address = 0x10e2d5000000,
1184 ip_address = "192.168.1.50",
1185 udp_port = 1234):
1186 # Imports
1187 from liteeth.core import LiteEthUDPIPCore
1188 from liteeth.frontend.etherbone import LiteEthEtherbone
1189 # Core
1190 ethcore = LiteEthUDPIPCore(
1191 phy = self.ethphy,
1192 mac_address = mac_address,
1193 ip_address = ip_address,
1194 clk_freq = self.clk_freq)
1195 if clock_domain is not None: # FIXME: Could probably be avoided.
1196 ethcore = ClockDomainsRenamer("eth_tx")(ethcore)
1197 self.submodules += ethcore
1198
1199 # Clock domain renaming
1200 if clock_domain is not None: # FIXME: Could probably be avoided.
1201 self.clock_domains.cd_etherbone = ClockDomain("etherbone")
1202 self.comb += self.cd_etherbone.clk.eq(ClockSignal(clock_domain))
1203 self.comb += self.cd_etherbone.rst.eq(ResetSignal(clock_domain))
1204 clock_domain = "etherbone"
1205 else:
1206 clock_domain = "sys"
1207
1208 # Etherbone
1209 etherbone = LiteEthEtherbone(ethcore.udp, udp_port, cd=clock_domain)
1210 setattr(self.submodules, name, etherbone)
1211 self.add_wb_master(etherbone.wishbone.bus)
1212 # Timing constraints
1213 if hasattr(phy, "crg"):
1214 eth_rx_clk = phy.crg.cd_eth_rx.clk
1215 eth_tx_clk = phy.crg.cd_eth_tx.clk
1216 else:
1217 eth_rx_clk = phy.cd_eth_rx.clk
1218 eth_tx_clk = phy.cd_eth_tx.clk
1219 self.platform.add_period_constraint(eth_rx_clk, 1e9/phy.rx_clk_freq)
1220 self.platform.add_period_constraint(eth_tx_clk, 1e9/phy.tx_clk_freq)
1221 self.platform.add_false_path_constraints(
1222 self.crg.cd_sys.clk,
1223 eth_rx_clk,
1224 eth_tx_clk)
1225
1226 # Add SPI Flash --------------------------------------------------------------------------------
1227 def add_spi_flash(self, name="spiflash", mode="4x", dummy_cycles=None, clk_freq=None):
1228 assert dummy_cycles is not None # FIXME: Get dummy_cycles from SPI Flash
1229 assert mode in ["1x", "4x"]
1230 if clk_freq is None: clk_freq = self.clk_freq/2 # FIXME: Get max clk_freq from SPI Flash
1231 spiflash = SpiFlash(
1232 pads = self.platform.request(name if mode == "1x" else name + mode),
1233 dummy = dummy_cycles,
1234 div = ceil(self.clk_freq/clk_freq),
1235 with_bitbang = True,
1236 endianness = self.cpu.endianness)
1237 spiflash.add_clk_primitive(self.platform.device)
1238 setattr(self.submodules, name, spiflash)
1239 self.add_memory_region(name, self.mem_map[name], 0x1000000) # FIXME: Get size from SPI Flash
1240 self.add_wb_slave(self.mem_map[name], spiflash.bus)
1241 self.add_csr(name)
1242
1243 # Add SPI SDCard -------------------------------------------------------------------------------
1244 def add_spi_sdcard(self, name="spisdcard", clk_freq=400e3):
1245 pads = self.platform.request(name)
1246 if hasattr(pads, "rst"):
1247 self.comb += pads.rst.eq(0)
1248 spisdcard = SPIMaster(pads, 8, self.sys_clk_freq, 400e3)
1249 spisdcard.add_clk_divider()
1250 setattr(self.submodules, name, spisdcard)
1251 self.add_csr(name)