Merge pull request #502 from shuffle2/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 self._reset = CSRStorage(1, description="""
616 Write a ``1`` to this register to reset the SoC.""")
617 self._scratch = CSRStorage(32, reset=0x12345678, description="""
618 Use this register as a scratch space to verify that software read/write accesses
619 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
620 can be used to verify endianness.""")
621 self._bus_errors = CSRStatus(32, description="""
622 Total number of Wishbone bus errors (timeouts) since last reset.""")
623
624 # # #
625
626 # Reset
627 self.reset = Signal()
628 self.comb += self.reset.eq(self._reset.re)
629
630 # Bus errors
631 self.bus_error = Signal()
632 bus_errors = Signal(32)
633 self.sync += \
634 If(bus_errors != (2**len(bus_errors)-1),
635 If(self.bus_error, bus_errors.eq(bus_errors + 1))
636 )
637 self.comb += self._bus_errors.status.eq(bus_errors)
638
639 # SoC ----------------------------------------------------------------------------------------------
640
641 class SoC(Module):
642 mem_map = {}
643 def __init__(self, platform, sys_clk_freq,
644
645 bus_standard = "wishbone",
646 bus_data_width = 32,
647 bus_address_width = 32,
648 bus_timeout = 1e6,
649 bus_reserved_regions = {},
650
651 csr_data_width = 32,
652 csr_address_width = 14,
653 csr_alignment = 32,
654 csr_paging = 0x800,
655 csr_reserved_csrs = {},
656
657 irq_n_irqs = 32,
658 irq_reserved_irqs = {},
659 ):
660
661 self.logger = logging.getLogger("SoC")
662 self.logger.info(colorer(" __ _ __ _ __ ", color="bright"))
663 self.logger.info(colorer(" / / (_) /____ | |/_/ ", color="bright"))
664 self.logger.info(colorer(" / /__/ / __/ -_)> < ", color="bright"))
665 self.logger.info(colorer(" /____/_/\\__/\\__/_/|_| ", color="bright"))
666 self.logger.info(colorer(" Build your hardware, easily!", color="bright"))
667
668 self.logger.info(colorer("-"*80, color="bright"))
669 self.logger.info(colorer("Creating SoC... ({})".format(build_time())))
670 self.logger.info(colorer("-"*80, color="bright"))
671 self.logger.info("FPGA device : {}.".format(platform.device))
672 self.logger.info("System clock: {:3.2f}MHz.".format(sys_clk_freq/1e6))
673
674 # SoC attributes ---------------------------------------------------------------------------
675 self.platform = platform
676 self.sys_clk_freq = sys_clk_freq
677 self.constants = {}
678 self.csr_regions = {}
679
680 # SoC Bus Handler --------------------------------------------------------------------------
681 self.submodules.bus = SoCBusHandler(
682 standard = bus_standard,
683 data_width = bus_data_width,
684 address_width = bus_address_width,
685 timeout = bus_timeout,
686 reserved_regions = bus_reserved_regions,
687 )
688
689 # SoC Bus Handler --------------------------------------------------------------------------
690 self.submodules.csr = SoCCSRHandler(
691 data_width = csr_data_width,
692 address_width = csr_address_width,
693 alignment = csr_alignment,
694 paging = csr_paging,
695 reserved_csrs = csr_reserved_csrs,
696 )
697
698 # SoC IRQ Handler --------------------------------------------------------------------------
699 self.submodules.irq = SoCIRQHandler(
700 n_irqs = irq_n_irqs,
701 reserved_irqs = irq_reserved_irqs
702 )
703
704 self.logger.info(colorer("-"*80, color="bright"))
705 self.logger.info(colorer("Initial SoC:"))
706 self.logger.info(colorer("-"*80, color="bright"))
707 self.logger.info(self.bus)
708 self.logger.info(self.csr)
709 self.logger.info(self.irq)
710 self.logger.info(colorer("-"*80, color="bright"))
711
712 self.add_config("CLOCK_FREQUENCY", int(sys_clk_freq))
713
714 # SoC Helpers ----------------------------------------------------------------------------------
715 def check_if_exists(self, name):
716 if hasattr(self, name):
717 self.logger.error("{} SubModule already {}.".format(
718 colorer(name),
719 colorer("declared", color="red")))
720 raise
721
722 def add_constant(self, name, value=None):
723 name = name.upper()
724 if name in self.constants.keys():
725 self.logger.error("{} Constant already {}.".format(
726 colorer(name),
727 colorer("declared", color="red")))
728 raise
729 self.constants[name] = SoCConstant(value)
730
731 def add_config(self, name, value=None):
732 name = "CONFIG_" + name
733 if isinstance(value, str):
734 self.add_constant(name + "_" + value)
735 else:
736 self.add_constant(name, value)
737
738 # SoC Main Components --------------------------------------------------------------------------
739 def add_controller(self, name="ctrl"):
740 self.check_if_exists(name)
741 setattr(self.submodules, name, SoCController())
742 self.csr.add(name, use_loc_if_exists=True)
743
744 def add_ram(self, name, origin, size, contents=[], mode="rw"):
745 ram_bus = wishbone.Interface(data_width=self.bus.data_width)
746 ram = wishbone.SRAM(size, bus=ram_bus, init=contents, read_only=(mode == "r"))
747 self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
748 self.check_if_exists(name)
749 self.logger.info("RAM {} {} {}.".format(
750 colorer(name),
751 colorer("added", color="green"),
752 self.bus.regions[name]))
753 setattr(self.submodules, name, ram)
754
755 def add_rom(self, name, origin, size, contents=[]):
756 self.add_ram(name, origin, size, contents, mode="r")
757
758 def add_csr_bridge(self, origin):
759 self.submodules.csr_bridge = wishbone2csr.WB2CSR(
760 bus_csr = csr_bus.Interface(
761 address_width = self.csr.address_width,
762 data_width = self.csr.data_width))
763 csr_size = 2**(self.csr.address_width + 2)
764 csr_region = SoCRegion(origin=origin, size=csr_size, cached=False)
765 self.bus.add_slave("csr", self.csr_bridge.wishbone, csr_region)
766 self.csr.add_master(name="bridge", master=self.csr_bridge.csr)
767 self.add_config("CSR_DATA_WIDTH", self.csr.data_width)
768 self.add_config("CSR_ALIGNMENT", self.csr.alignment)
769
770 def add_cpu(self, name="vexriscv", variant="standard", cls=None, reset_address=None):
771 if name not in cpu.CPUS.keys():
772 self.logger.error("{} CPU {}, supporteds: {}".format(
773 colorer(name),
774 colorer("not supported", color="red"),
775 colorer(", ".join(cpu.CPUS.keys()))))
776 raise
777 # Add CPU
778 cpu_cls = cls if cls is not None else cpu.CPUS[name]
779 self.submodules.cpu = cpu_cls(self.platform, variant)
780 # Update SoC with CPU constraints
781 for n, (origin, size) in enumerate(self.cpu.io_regions.items()):
782 self.bus.add_region("io{}".format(n), SoCIORegion(origin=origin, size=size, cached=False))
783 self.mem_map.update(self.cpu.mem_map) # FIXME
784 self.csr.update_alignment(self.cpu.data_width)
785 # Add Bus Masters/CSR/IRQs
786 if not isinstance(self.cpu, cpu.CPUNone):
787 if reset_address is None:
788 reset_address = self.mem_map["rom"]
789 self.cpu.set_reset_address(reset_address)
790 for n, cpu_bus in enumerate(self.cpu.periph_buses):
791 self.bus.add_master(name="cpu_bus{}".format(n), master=cpu_bus)
792 self.csr.add("cpu", use_loc_if_exists=True)
793 if hasattr(self.cpu, "interrupt"):
794 for name, loc in self.cpu.interrupts.items():
795 self.irq.add(name, loc)
796 self.add_config("CPU_HAS_INTERRUPT")
797 if hasattr(self, "ctrl"):
798 self.comb += self.cpu.reset.eq(self.ctrl.reset)
799 self.add_config("CPU_RESET_ADDR", reset_address)
800 # Add constants
801 self.add_config("CPU_TYPE", str(name))
802 self.add_config("CPU_VARIANT", str(variant.split('+')[0]))
803 self.add_constant("CONFIG_CPU_HUMAN_NAME", getattr(self.cpu, "human_name", "Unknown"))
804 if hasattr(self.cpu, "nop"):
805 self.add_constant("CONFIG_CPU_NOP", self.cpu.nop)
806
807 def add_timer(self, name="timer0"):
808 self.check_if_exists(name)
809 setattr(self.submodules, name, Timer())
810 self.csr.add(name, use_loc_if_exists=True)
811 if hasattr(self.cpu, "interrupt"):
812 self.irq.add(name, use_loc_if_exists=True)
813
814 # SoC finalization -----------------------------------------------------------------------------
815 def do_finalize(self):
816 self.logger.info(colorer("-"*80, color="bright"))
817 self.logger.info(colorer("Finalized SoC:"))
818 self.logger.info(colorer("-"*80, color="bright"))
819 self.logger.info(self.bus)
820 self.logger.info(self.csr)
821 self.logger.info(self.irq)
822 self.logger.info(colorer("-"*80, color="bright"))
823
824 # SoC Bus Interconnect ---------------------------------------------------------------------
825 bus_masters = self.bus.masters.values()
826 bus_slaves = [(self.bus.regions[n].decoder(self.bus), s) for n, s in self.bus.slaves.items()]
827 if len(bus_masters) and len(bus_slaves):
828 self.submodules.bus_interconnect = wishbone.InterconnectShared(
829 masters = bus_masters,
830 slaves = bus_slaves,
831 register = True,
832 timeout_cycles = self.bus.timeout)
833 if hasattr(self, "ctrl") and self.bus.timeout is not None:
834 self.comb += self.ctrl.bus_error.eq(self.bus_interconnect.timeout.error)
835
836 # SoC CSR Interconnect ---------------------------------------------------------------------
837 self.submodules.csr_bankarray = csr_bus.CSRBankArray(self,
838 address_map = self.csr.address_map,
839 data_width = self.csr.data_width,
840 address_width = self.csr.address_width,
841 alignment = self.csr.alignment,
842 paging = self.csr.paging,
843 soc_bus_data_width = self.bus.data_width)
844 if len(self.csr.masters):
845 self.submodules.csr_interconnect = csr_bus.InterconnectShared(
846 masters = list(self.csr.masters.values()),
847 slaves = self.csr_bankarray.get_buses())
848
849 # Add CSRs regions
850 for name, csrs, mapaddr, rmap in self.csr_bankarray.banks:
851 self.csr.add_region(name, SoCCSRRegion(
852 origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
853 busword = self.csr.data_width,
854 obj = csrs))
855
856 # Add Memory regions
857 for name, memory, mapaddr, mmap in self.csr_bankarray.srams:
858 self.csr.add_region(name + "_" + memory.name_override, SoCCSRRegion(
859 origin = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
860 busword = self.csr.data_width,
861 obj = memory))
862
863 # Sort CSR regions by origin
864 self.csr.regions = {k: v for k, v in sorted(self.csr.regions.items(), key=lambda item: item[1].origin)}
865
866 # Add CSRs / Config items to constants
867 for name, constant in self.csr_bankarray.constants:
868 self.add_constant(name + "_" + constant.name, constant.value.value)
869
870 # SoC CPU Check ----------------------------------------------------------------------------
871 if not isinstance(self.cpu, cpu.CPUNone):
872 if "sram" not in self.bus.regions.keys():
873 self.logger.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
874 colorer("sram"),
875 colorer("defined", color="red")))
876 self.logger.error(self.bus)
877 raise
878 cpu_reset_address_valid = False
879 for name, container in self.bus.regions.items():
880 if self.bus.check_region_is_in(
881 region = SoCRegion(origin=self.cpu.reset_address, size=self.bus.data_width//8),
882 container = container):
883 cpu_reset_address_valid = True
884 if name == "rom":
885 self.cpu.use_rom = True
886 if not cpu_reset_address_valid:
887 self.logger.error("CPU needs {} to be in a {} Region.".format(
888 colorer("reset address 0x{:08x}".format(self.cpu.reset_address)),
889 colorer("defined", color="red")))
890 self.logger.error(self.bus)
891 raise
892
893 # SoC IRQ Interconnect ---------------------------------------------------------------------
894 if hasattr(self, "cpu"):
895 if hasattr(self.cpu, "interrupt"):
896 for name, loc in sorted(self.irq.locs.items()):
897 if name in self.cpu.interrupts.keys():
898 continue
899 if hasattr(self, name):
900 module = getattr(self, name)
901 if not hasattr(module, "ev"):
902 self.logger.error("EventManager {} in {} SubModule.".format(
903 colorer("not found", color="red"),
904 colorer(name)))
905 raise
906 self.comb += self.cpu.interrupt[loc].eq(module.ev.irq)
907 self.add_constant(name + "_INTERRUPT", loc)
908
909 # SoC build ------------------------------------------------------------------------------------
910 def build(self, *args, **kwargs):
911 return self.platform.build(self, *args, **kwargs)
912
913 # LiteXSoC -----------------------------------------------------------------------------------------
914
915 class LiteXSoC(SoC):
916 # Add Identifier -------------------------------------------------------------------------------
917 def add_identifier(self, name="identifier", identifier="LiteX SoC", with_build_time=True):
918 self.check_if_exists(name)
919 if with_build_time:
920 identifier += " " + build_time()
921 setattr(self.submodules, name, Identifier(identifier))
922 self.csr.add(name + "_mem", use_loc_if_exists=True)
923
924 # Add UART -------------------------------------------------------------------------------------
925 def add_uart(self, name, baudrate=115200, fifo_depth=16):
926 from litex.soc.cores import uart
927
928 # Stub / Stream
929 if name in ["stub", "stream"]:
930 self.submodules.uart = uart.UART(tx_fifo_depth=0, rx_fifo_depth=0)
931 if name == "stub":
932 self.comb += self.uart.sink.ready.eq(1)
933
934 # UARTBone / Bridge
935 elif name in ["uartbone", "bridge"]:
936 self.add_uartbone(baudrate=baudrate)
937
938 # Crossover
939 elif name in ["crossover"]:
940 self.submodules.uart = uart.UARTCrossover()
941
942 # Model/Sim
943 elif name in ["model", "sim"]:
944 self.submodules.uart_phy = uart.RS232PHYModel(self.platform.request("serial"))
945 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
946 tx_fifo_depth = fifo_depth,
947 rx_fifo_depth = fifo_depth))
948
949 # JTAG Atlantic
950 elif name in ["jtag_atlantic"]:
951 from litex.soc.cores.jtag import JTAGAtlantic
952 self.submodules.uart_phy = JTAGAtlantic()
953 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
954 tx_fifo_depth = fifo_depth,
955 rx_fifo_depth = fifo_depth))
956
957 # JTAG UART
958 elif name in ["jtag_uart"]:
959 from litex.soc.cores.jtag import JTAGPHY
960 self.submodules.uart_phy = JTAGPHY(device=self.platform.device)
961 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
962 tx_fifo_depth = fifo_depth,
963 rx_fifo_depth = fifo_depth))
964
965 # USB ACM (with ValentyUSB core)
966 elif name in ["usb_acm"]:
967 import valentyusb.usbcore.io as usbio
968 import valentyusb.usbcore.cpu.cdc_eptri as cdc_eptri
969 usb_pads = self.platform.request("usb")
970 usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup)
971 self.submodules.uart = cdc_eptri.CDCUsb(usb_iobuf)
972
973 # Classic UART
974 else:
975 self.submodules.uart_phy = uart.UARTPHY(
976 pads = self.platform.request(name),
977 clk_freq = self.sys_clk_freq,
978 baudrate = baudrate)
979 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
980 tx_fifo_depth = fifo_depth,
981 rx_fifo_depth = fifo_depth))
982
983 self.csr.add("uart_phy", use_loc_if_exists=True)
984 self.csr.add("uart", use_loc_if_exists=True)
985 if hasattr(self.cpu, "interrupt"):
986 self.irq.add("uart", use_loc_if_exists=True)
987 else:
988 self.add_constant("UART_POLLING")
989
990 # Add UARTbone ---------------------------------------------------------------------------------
991 def add_uartbone(self, name="serial", baudrate=115200):
992 from litex.soc.cores import uart
993 self.submodules.uartbone = uart.UARTBone(
994 pads = self.platform.request(name),
995 clk_freq = self.sys_clk_freq,
996 baudrate = baudrate)
997 self.bus.add_master(name="uartbone", master=self.uartbone.wishbone)
998
999 # Add SDRAM ------------------------------------------------------------------------------------
1000 def add_sdram(self, name, phy, module, origin, size=None,
1001 l2_cache_size = 8192,
1002 l2_cache_min_data_width = 128,
1003 l2_cache_reverse = True,
1004 l2_cache_full_memory_we = True,
1005 **kwargs):
1006
1007 # Imports
1008 from litedram.common import LiteDRAMNativePort
1009 from litedram.core import LiteDRAMCore
1010 from litedram.frontend.wishbone import LiteDRAMWishbone2Native
1011 from litedram.frontend.axi import LiteDRAMAXI2Native
1012
1013 # LiteDRAM core
1014 self.submodules.sdram = LiteDRAMCore(
1015 phy = phy,
1016 geom_settings = module.geom_settings,
1017 timing_settings = module.timing_settings,
1018 clk_freq = self.sys_clk_freq,
1019 **kwargs)
1020 self.csr.add("sdram")
1021
1022 # Compute/Check SDRAM size
1023 sdram_size = 2**(module.geom_settings.bankbits +
1024 module.geom_settings.rowbits +
1025 module.geom_settings.colbits)*phy.settings.databits//8
1026 if size is not None:
1027 sdram_size = min(sdram_size, size)
1028
1029 # Add SDRAM region
1030 self.bus.add_region("main_ram", SoCRegion(origin=origin, size=sdram_size))
1031
1032 # SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
1033 if len(self.cpu.memory_buses):
1034 # When CPU has at least a direct memory bus, connect them directly to LiteDRAM.
1035 for mem_bus in self.cpu.memory_buses:
1036 # Request a LiteDRAM native port.
1037 port = self.sdram.crossbar.get_port()
1038 port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2.
1039
1040 # Check if bus is an AXI bus and connect it.
1041 if isinstance(mem_bus, axi.AXIInterface):
1042 # If same data_width, connect it directly.
1043 if port.data_width == mem_bus.data_width:
1044 self.logger.info("Matching AXI MEM data width ({})\n".format(port.data_width))
1045 self.submodules += LiteDRAMAXI2Native(
1046 axi = self.cpu.mem_axi,
1047 port = port,
1048 base_address = self.bus.regions["main_ram"].origin)
1049 # If different data_width, do the adaptation and connect it via Wishbone.
1050 else:
1051 self.logger.info("Converting MEM data width: {} to {} via Wishbone".format(
1052 port.data_width,
1053 self.cpu.mem_axi.data_width))
1054 # FIXME: replace WB data-width converter with native AXI converter!!!
1055 mem_wb = wishbone.Interface(
1056 data_width = self.cpu.mem_axi.data_width,
1057 adr_width = 32-log2_int(self.cpu.mem_axi.data_width//8))
1058 # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
1059 mem_a2w = ResetInserter()(axi.AXI2Wishbone(
1060 axi = self.cpu.mem_axi,
1061 wishbone = mem_wb,
1062 base_address = 0))
1063 self.comb += mem_a2w.reset.eq(ResetSignal() | self.cpu.reset)
1064 self.submodules += mem_a2w
1065 litedram_wb = wishbone.Interface(port.data_width)
1066 self.submodules += LiteDRAMWishbone2Native(
1067 wishbone = litedram_wb,
1068 port = port,
1069 base_address = origin)
1070 self.submodules += wishbone.Converter(mem_wb, litedram_wb)
1071 # Check if bus is a Native bus and connect it.
1072 if isinstance(mem_bus, LiteDRAMNativePort):
1073 # If same data_width, connect it directly.
1074 if port.data_width == mem_bus.data_width:
1075 self.comb += mem_bus.cmd.connect(port.cmd)
1076 self.comb += mem_bus.wdata.connect(port.wdata)
1077 self.comb += port.rdata.connect(mem_bus.rdata)
1078 # Else raise Error.
1079 else:
1080 raise NotImplementedError
1081 else:
1082 # When CPU has no direct memory interface, create a Wishbone Slave interface to LiteDRAM.
1083
1084 # Request a LiteDRAM native port.
1085 port = self.sdram.crossbar.get_port()
1086 port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2.
1087
1088 # Create Wishbone Slave.
1089 wb_sdram = wishbone.Interface()
1090 self.bus.add_slave("main_ram", wb_sdram)
1091
1092 # L2 Cache
1093 if l2_cache_size != 0:
1094 # Insert L2 cache inbetween Wishbone bus and LiteDRAM
1095 l2_cache_size = max(l2_cache_size, int(2*port.data_width/8)) # Use minimal size if lower
1096 l2_cache_size = 2**int(log2(l2_cache_size)) # Round to nearest power of 2
1097 l2_cache_data_width = max(port.data_width, l2_cache_min_data_width)
1098 l2_cache = wishbone.Cache(
1099 cachesize = l2_cache_size//4,
1100 master = wb_sdram,
1101 slave = wishbone.Interface(l2_cache_data_width),
1102 reverse = l2_cache_reverse)
1103 if l2_cache_full_memory_we:
1104 l2_cache = FullMemoryWE()(l2_cache)
1105 self.submodules.l2_cache = l2_cache
1106 litedram_wb = self.l2_cache.slave
1107 else:
1108 litedram_wb = wishbone.Interface(port.data_width)
1109 self.submodules += wishbone.Converter(wb_sdram, litedram_wb)
1110 self.add_config("L2_SIZE", l2_cache_size)
1111
1112 # Wishbone Slave <--> LiteDRAM bridge
1113 self.submodules.wishbone_bridge = LiteDRAMWishbone2Native(litedram_wb, port,
1114 base_address = self.bus.regions["main_ram"].origin)
1115
1116 # Add Ethernet ---------------------------------------------------------------------------------
1117 def add_ethernet(self, name="ethmac", phy=None):
1118 # Imports
1119 from liteeth.mac import LiteEthMAC
1120 # MAC
1121 ethmac = LiteEthMAC(
1122 phy = phy,
1123 dw = 32,
1124 interface = "wishbone",
1125 endianness = self.cpu.endianness)
1126 setattr(self.submodules, name, ethmac)
1127 ethmac_region = SoCRegion(origin=self.mem_map.get(name, None), size=0x2000, cached=False)
1128 self.bus.add_slave(name=name, slave=ethmac.bus, region=ethmac_region)
1129 self.add_csr(name)
1130 self.add_interrupt(name)
1131 # Timing constraints
1132 if hasattr(phy, "crg"):
1133 eth_rx_clk = phy.crg.cd_eth_rx.clk
1134 eth_tx_clk = phy.crg.cd_eth_tx.clk
1135 else:
1136 eth_rx_clk = phy.cd_eth_rx.clk
1137 eth_tx_clk = phy.cd_eth_tx.clk
1138 self.platform.add_period_constraint(eth_rx_clk, 1e9/phy.rx_clk_freq)
1139 self.platform.add_period_constraint(eth_tx_clk, 1e9/phy.tx_clk_freq)
1140 self.platform.add_false_path_constraints(
1141 self.crg.cd_sys.clk,
1142 eth_rx_clk,
1143 eth_tx_clk)
1144
1145 # Add Etherbone --------------------------------------------------------------------------------
1146 def add_etherbone(self, name="etherbone", phy=None, clock_domain=None,
1147 mac_address = 0x10e2d5000000,
1148 ip_address = "192.168.1.50",
1149 udp_port = 1234):
1150 # Imports
1151 from liteeth.core import LiteEthUDPIPCore
1152 from liteeth.frontend.etherbone import LiteEthEtherbone
1153 # Core
1154 ethcore = LiteEthUDPIPCore(
1155 phy = self.ethphy,
1156 mac_address = mac_address,
1157 ip_address = ip_address,
1158 clk_freq = self.clk_freq)
1159 if clock_domain is not None: # FIXME: Could probably be avoided.
1160 ethcore = ClockDomainsRenamer("eth_tx")(ethcore)
1161 self.submodules += ethcore
1162
1163 # Clock domain renaming
1164 if clock_domain is not None: # FIXME: Could probably be avoided.
1165 self.clock_domains.cd_etherbone = ClockDomain("etherbone")
1166 self.comb += self.cd_etherbone.clk.eq(ClockSignal(clock_domain))
1167 self.comb += self.cd_etherbone.rst.eq(ResetSignal(clock_domain))
1168 clock_domain = "etherbone"
1169 else:
1170 clock_domain = "sys"
1171
1172 # Etherbone
1173 etherbone = LiteEthEtherbone(ethcore.udp, udp_port, cd=clock_domain)
1174 setattr(self.submodules, name, etherbone)
1175 self.add_wb_master(etherbone.wishbone.bus)
1176 # Timing constraints
1177 if hasattr(phy, "crg"):
1178 eth_rx_clk = phy.crg.cd_eth_rx.clk
1179 eth_tx_clk = phy.crg.cd_eth_tx.clk
1180 else:
1181 eth_rx_clk = phy.cd_eth_rx.clk
1182 eth_tx_clk = phy.cd_eth_tx.clk
1183 self.platform.add_period_constraint(eth_rx_clk, 1e9/phy.rx_clk_freq)
1184 self.platform.add_period_constraint(eth_tx_clk, 1e9/phy.tx_clk_freq)
1185 self.platform.add_false_path_constraints(
1186 self.crg.cd_sys.clk,
1187 eth_rx_clk,
1188 eth_tx_clk)
1189
1190 # Add SPI Flash --------------------------------------------------------------------------------
1191 def add_spi_flash(self, name="spiflash", mode="4x", dummy_cycles=None, clk_freq=None):
1192 assert dummy_cycles is not None # FIXME: Get dummy_cycles from SPI Flash
1193 assert mode in ["4x"] # FIXME: Add 1x support.
1194 if clk_freq is None: clk_freq = self.clk_freq/2 # FIXME: Get max clk_freq from SPI Flash
1195 spiflash = SpiFlash(
1196 pads = self.platform.request(name + mode),
1197 dummy = dummy_cycles,
1198 div = ceil(self.clk_freq/clk_freq),
1199 with_bitbang = True,
1200 endianness = self.cpu.endianness)
1201 spiflash.add_clk_primitive(self.platform.device)
1202 setattr(self.submodules, name, spiflash)
1203 self.add_memory_region(name, self.mem_map[name], 0x1000000) # FIXME: Get size from SPI Flash
1204 self.add_wb_slave(self.mem_map[name], spiflash.bus)
1205 self.add_csr(name)
1206
1207 # Add SPI SDCard -------------------------------------------------------------------------------
1208 def add_spi_sdcard(self, name="spisdcard", clk_freq=400e3):
1209 pads = self.platform.request(name)
1210 if hasattr(pads, "rst"):
1211 self.comb += pads.rst.eq(0)
1212 spisdcard = SPIMaster(pads, 8, self.sys_clk_freq, 400e3)
1213 spisdcard.add_clk_divider()
1214 setattr(self.submodules, name, spisdcard)
1215 self.add_csr(name)