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>
9 from math
import log2
, ceil
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
19 from litex
.soc
.interconnect
.csr
import *
20 from litex
.soc
.interconnect
import csr_bus
21 from litex
.soc
.interconnect
import stream
22 from litex
.soc
.interconnect
import wishbone
23 from litex
.soc
.interconnect
import axi
25 logging
.basicConfig(level
=logging
.INFO
)
27 # Helpers ------------------------------------------------------------------------------------------
32 def colorer(s
, color
="bright"):
39 "underline": "\x1b[4m"}[color
]
41 return header
+ str(s
) + trailer
43 def build_time(with_time
=True):
44 fmt
= "%Y-%m-%d %H:%M:%S" if with_time
else "%Y-%m-%d"
45 return datetime
.datetime
.fromtimestamp(time
.time()).strftime(fmt
)
47 # SoCConstant --------------------------------------------------------------------------------------
49 def SoCConstant(value
):
52 # SoCRegion ----------------------------------------------------------------------------------------
55 def __init__(self
, origin
=None, size
=None, mode
="rw", cached
=True, linker
=False):
56 self
.logger
= logging
.getLogger("SoCRegion")
59 if size
!= 2**log2_int(size
, False):
60 self
.logger
.info("Region size {} internally from {} to {}.".format(
61 colorer("rounded", color
="cyan"),
62 colorer("0x{:08x}".format(size
)),
63 colorer("0x{:08x}".format(2**log2_int(size
, False)))))
64 self
.size_pow2
= 2**log2_int(size
, False)
69 def decoder(self
, bus
):
72 if (origin
& (size
- 1)) != 0:
73 self
.logger
.error("Origin needs to be aligned on size:")
74 self
.logger
.error(self
)
76 if (origin
== 0) and (size
== 2**bus
.address_width
):
77 return lambda a
: True
78 origin
>>= int(log2(bus
.data_width
//8)) # bytes to words aligned
79 size
>>= int(log2(bus
.data_width
//8)) # bytes to words aligned
80 return lambda a
: (a
[log2_int(size
):] == (origin
>> log2_int(size
)))
84 if self
.origin
is not None:
85 r
+= "Origin: {}, ".format(colorer("0x{:08x}".format(self
.origin
)))
86 if self
.size
is not None:
87 r
+= "Size: {}, ".format(colorer("0x{:08x}".format(self
.size
)))
88 r
+= "Mode: {}, ".format(colorer(self
.mode
.upper()))
89 r
+= "Cached: {} ".format(colorer(self
.cached
))
90 r
+= "Linker: {}".format(colorer(self
.linker
))
93 class SoCIORegion(SoCRegion
): pass
95 # SoCCSRRegion -------------------------------------------------------------------------------------
98 def __init__(self
, origin
, busword
, obj
):
100 self
.busword
= busword
103 # SoCBusHandler ------------------------------------------------------------------------------------
105 class SoCBusHandler(Module
):
106 supported_standard
= ["wishbone", "axi-lite"]
107 supported_data_width
= [32, 64]
108 supported_address_width
= [32]
110 # Creation -------------------------------------------------------------------------------------
111 def __init__(self
, name
="SoCBusHandler", standard
="wishbone", data_width
=32, address_width
=32, timeout
=1e6
, reserved_regions
={}):
112 self
.logger
= logging
.getLogger(name
)
113 self
.logger
.info("Creating Bus Handler...")
116 if standard
not in self
.supported_standard
:
117 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
118 colorer("Bus standard", color
="red"),
120 colorer(", ".join(self
.supported_standard
))))
124 if data_width
not in self
.supported_data_width
:
125 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
126 colorer("Data Width", color
="red"),
128 colorer(", ".join(str(x
) for x
in self
.supported_data_width
))))
131 # Check Address Width
132 if address_width
not in self
.supported_address_width
:
133 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
134 colorer("Address Width", color
="red"),
135 colorer(address_width
),
136 colorer(", ".join(str(x
) for x
in self
.supported_address_width
))))
140 self
.standard
= standard
141 self
.data_width
= data_width
142 self
.address_width
= address_width
147 self
.timeout
= timeout
148 self
.logger
.info("{}-bit {} Bus, {}GiB Address Space.".format(
149 colorer(data_width
), colorer(standard
), colorer(2**address_width
/2**30)))
151 # Adding reserved regions
152 self
.logger
.info("Adding {} Bus Regions...".format(colorer("reserved", color
="cyan")))
153 for name
, region
in reserved_regions
.items():
154 if isinstance(region
, int):
155 region
= SoCRegion(origin
=region
, size
=0x1000000)
156 self
.add_region(name
, region
)
158 self
.logger
.info("Bus Handler {}.".format(colorer("created", color
="green")))
160 # Add/Allog/Check Regions ----------------------------------------------------------------------
161 def add_region(self
, name
, region
):
163 if name
in self
.regions
.keys() or name
in self
.io_regions
.keys():
164 self
.logger
.error("{} already declared as Region:".format(colorer(name
, color
="red")))
165 self
.logger
.error(self
)
167 # Check if SoCIORegion
168 if isinstance(region
, SoCIORegion
):
169 self
.io_regions
[name
] = region
170 overlap
= self
.check_regions_overlap(self
.io_regions
)
171 if overlap
is not None:
172 self
.logger
.error("IO Region {} between {} and {}:".format(
173 colorer("overlap", color
="red"),
175 colorer(overlap
[1])))
176 self
.logger
.error(str(self
.io_regions
[overlap
[0]]))
177 self
.logger
.error(str(self
.io_regions
[overlap
[1]]))
179 self
.logger
.info("{} Region {} at {}.".format(
180 colorer(name
, color
="underline"),
181 colorer("added", color
="green"),
184 elif isinstance(region
, SoCRegion
):
185 # If no origin specified, allocate region.
186 if region
.origin
is None:
188 region
= self
.alloc_region(name
, region
.size
, region
.cached
)
189 self
.regions
[name
] = region
190 # Else add region and check for overlaps.
192 if not region
.cached
:
193 if not self
.check_region_is_io(region
):
194 self
.logger
.error("{} Region {}: {}.".format(
196 colorer("not in IO region", color
="red"),
198 self
.logger
.error(self
)
200 self
.regions
[name
] = region
201 overlap
= self
.check_regions_overlap(self
.regions
)
202 if overlap
is not None:
203 self
.logger
.error("Region {} between {} and {}:".format(
204 colorer("overlap", color
="red"),
206 colorer(overlap
[1])))
207 self
.logger
.error(str(self
.regions
[overlap
[0]]))
208 self
.logger
.error(str(self
.regions
[overlap
[1]]))
210 self
.logger
.info("{} Region {} at {}.".format(
211 colorer(name
, color
="underline"),
212 colorer("allocated" if allocated
else "added", color
="cyan" if allocated
else "green"),
215 self
.logger
.error("{} is not a supported Region.".format(colorer(name
, color
="red")))
218 def alloc_region(self
, name
, size
, cached
=True):
219 self
.logger
.info("Allocating {} Region of size {}...".format(
220 colorer("Cached" if cached
else "IO"),
221 colorer("0x{:08x}".format(size
))))
223 # Limit Search Regions
225 search_regions
= self
.io_regions
227 search_regions
= {"main": SoCRegion(origin
=0x00000000, size
=2**self
.address_width
-1)}
229 # Iterate on Search_Regions to find a Candidate
230 for _
, search_region
in search_regions
.items():
231 origin
= search_region
.origin
232 while (origin
+ size
) < (search_region
.origin
+ search_region
.size_pow2
):
233 # Create a Candicate.
234 candidate
= SoCRegion(origin
=origin
, size
=size
, cached
=cached
)
236 # Check Candidate does not overlap with allocated existing regions
237 for _
, allocated
in self
.regions
.items():
238 if self
.check_regions_overlap({"0": allocated
, "1": candidate
}) is not None:
239 origin
= allocated
.origin
+ allocated
.size_pow2
243 # If no overlap, the Candidate is selected
246 self
.logger
.error("Not enough Address Space to allocate Region.")
249 def check_regions_overlap(self
, regions
, check_linker
=False):
251 while i
< len(regions
):
252 n0
= list(regions
.keys())[i
]
254 for n1
in list(regions
.keys())[i
+1:]:
256 if r0
.linker
or r1
.linker
:
259 if r0
.origin
>= (r1
.origin
+ r1
.size_pow2
):
261 if r1
.origin
>= (r0
.origin
+ r0
.size_pow2
):
267 def check_region_is_in(self
, region
, container
):
269 if not (region
.origin
>= container
.origin
):
271 if not ((region
.origin
+ region
.size
) < (container
.origin
+ container
.size
)):
275 def check_region_is_io(self
, region
):
277 for _
, io_region
in self
.io_regions
.items():
278 if self
.check_region_is_in(region
, io_region
):
282 # Add Master/Slave -----------------------------------------------------------------------------
283 def add_adapter(self
, name
, interface
, direction
="m2s"):
284 assert direction
in ["m2s", "s2m"]
286 # Data width conversion
287 if interface
.data_width
!= self
.data_width
:
288 interface_cls
= type(interface
)
290 wishbone
.Interface
: wishbone
.Converter
,
291 axi
.AXILiteInterface
: axi
.AXILiteConverter
,
293 converted_interface
= interface_cls(data_width
=self
.data_width
)
294 if direction
== "m2s":
295 master
, slave
= interface
, converted_interface
296 elif direction
== "s2m":
297 master
, slave
= converted_interface
, interface
298 converter
= converter_cls(master
=master
, slave
=slave
)
299 self
.submodules
+= converter
301 converted_interface
= interface
303 # Wishbone <-> AXILite bridging
305 "wishbone": wishbone
.Interface
,
306 "axi-lite": axi
.AXILiteInterface
,
308 if isinstance(converted_interface
, main_bus_cls
):
309 bridged_interface
= converted_interface
311 bridged_interface
= main_bus_cls(data_width
=self
.data_width
)
312 if direction
== "m2s":
313 master
, slave
= converted_interface
, bridged_interface
314 elif direction
== "s2m":
315 master
, slave
= bridged_interface
, converted_interface
317 (wishbone
.Interface
, axi
.AXILiteInterface
): axi
.Wishbone2AXILite
,
318 (axi
.AXILiteInterface
, wishbone
.Interface
): axi
.AXILite2Wishbone
,
319 }[type(master
), type(slave
)]
320 bridge
= bridge_cls(master
, slave
)
321 self
.submodules
+= bridge
323 if type(interface
) != type(bridged_interface
) or interface
.data_width
!= bridged_interface
.data_width
:
324 fmt
= "{name} Bus {converted} from {frombus} {frombits}-bit to {tobus} {tobits}-bit."
326 wishbone
.Interface
: "Wishbone",
327 axi
.AXILiteInterface
: "AXI Lite",
329 self
.logger
.info(fmt
.format(
330 name
= colorer(name
),
331 converted
= colorer("converted", color
="cyan"),
332 frombus
= colorer(bus_names
[type(interface
)]),
333 frombits
= colorer(interface
.data_width
),
334 tobus
= colorer(bus_names
[type(bridged_interface
)]),
335 tobits
= colorer(bridged_interface
.data_width
)))
336 return bridged_interface
338 def add_master(self
, name
=None, master
=None):
340 name
= "master{:d}".format(len(self
.masters
))
341 if name
in self
.masters
.keys():
342 self
.logger
.error("{} {} as Bus Master:".format(
344 colorer("already declared", color
="red")))
345 self
.logger
.error(self
)
347 master
= self
.add_adapter(name
, master
, "m2s")
348 self
.masters
[name
] = master
349 self
.logger
.info("{} {} as Bus Master.".format(
350 colorer(name
, color
="underline"),
351 colorer("added", color
="green")))
353 def add_slave(self
, name
=None, slave
=None, region
=None):
354 no_name
= name
is None
355 no_region
= region
is None
356 if no_name
and no_region
:
357 self
.logger
.error("Please {} {} or/and {} of Bus Slave.".format(
358 colorer("specify", color
="red"),
363 name
= "slave{:d}".format(len(self
.slaves
))
365 region
= self
.regions
.get(name
, None)
367 self
.logger
.error("{} Region {}.".format(
369 colorer("not found", color
="red")))
372 self
.add_region(name
, region
)
373 if name
in self
.slaves
.keys():
374 self
.logger
.error("{} {} as Bus Slave:".format(
376 colorer("already declared", color
="red")))
377 self
.logger
.error(self
)
379 slave
= self
.add_adapter(name
, slave
, "s2m")
380 self
.slaves
[name
] = slave
381 self
.logger
.info("{} {} as Bus Slave.".format(
382 colorer(name
, color
="underline"),
383 colorer("added", color
="green")))
385 # Str ------------------------------------------------------------------------------------------
387 r
= "{}-bit {} Bus, {}GiB Address Space.\n".format(
388 colorer(self
.data_width
), colorer(self
.standard
), colorer(2**self
.address_width
/2**30))
389 r
+= "IO Regions: ({})\n".format(len(self
.io_regions
.keys())) if len(self
.io_regions
.keys()) else ""
390 io_regions
= {k
: v
for k
, v
in sorted(self
.io_regions
.items(), key
=lambda item
: item
[1].origin
)}
391 for name
, region
in io_regions
.items():
392 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
393 r
+= "Bus Regions: ({})\n".format(len(self
.regions
.keys())) if len(self
.regions
.keys()) else ""
394 regions
= {k
: v
for k
, v
in sorted(self
.regions
.items(), key
=lambda item
: item
[1].origin
)}
395 for name
, region
in regions
.items():
396 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
397 r
+= "Bus Masters: ({})\n".format(len(self
.masters
.keys())) if len(self
.masters
.keys()) else ""
398 for name
in self
.masters
.keys():
399 r
+= "- {}\n".format(colorer(name
, color
="underline"))
400 r
+= "Bus Slaves: ({})\n".format(len(self
.slaves
.keys())) if len(self
.slaves
.keys()) else ""
401 for name
in self
.slaves
.keys():
402 r
+= "- {}\n".format(colorer(name
, color
="underline"))
406 # SoCLocHandler --------------------------------------------------------------------------------------
408 class SoCLocHandler(Module
):
409 # Creation -------------------------------------------------------------------------------------
410 def __init__(self
, name
, n_locs
):
415 # Add ------------------------------------------------------------------------------------------
416 def add(self
, name
, n
=None, use_loc_if_exists
=False):
418 if not (use_loc_if_exists
and name
in self
.locs
.keys()):
419 if name
in self
.locs
.keys():
420 self
.logger
.error("{} {} name {}.".format(
421 colorer(name
), self
.name
, colorer("already used", color
="red")))
422 self
.logger
.error(self
)
424 if n
in self
.locs
.values():
425 self
.logger
.error("{} {} Location {}.".format(
426 colorer(n
), self
.name
, colorer("already used", color
="red")))
427 self
.logger
.error(self
)
434 self
.logger
.error("{} {} Location should be {}.".format(
437 colorer("positive", color
="red")))
440 self
.logger
.error("{} {} Location {} than maximum: {}.".format(
443 colorer("higher", color
="red"),
444 colorer(self
.n_locs
)))
449 self
.logger
.info("{} {} {} at Location {}.".format(
450 colorer(name
, color
="underline"),
452 colorer("allocated" if allocated
else "added", color
="cyan" if allocated
else "green"),
455 # Alloc ----------------------------------------------------------------------------------------
456 def alloc(self
, name
):
457 for n
in range(self
.n_locs
):
458 if n
not in self
.locs
.values():
460 self
.logger
.error("Not enough Locations.")
461 self
.logger
.error(self
)
464 # Str ------------------------------------------------------------------------------------------
466 r
= "{} Locations: ({})\n".format(self
.name
, len(self
.locs
.keys())) if len(self
.locs
.keys()) else ""
467 locs
= {k
: v
for k
, v
in sorted(self
.locs
.items(), key
=lambda item
: item
[1])}
469 for name
in locs
.keys():
470 if len(name
) > length
: length
= len(name
)
471 for name
in locs
.keys():
472 r
+= "- {}{}: {}\n".format(colorer(name
, color
="underline"), " "*(length
+ 1 - len(name
)), colorer(self
.locs
[name
]))
475 # SoCCSRHandler ------------------------------------------------------------------------------------
477 class SoCCSRHandler(SoCLocHandler
):
478 supported_data_width
= [8, 32]
479 supported_address_width
= [14+i
for i
in range(4)]
480 supported_alignment
= [32]
481 supported_paging
= [0x800*2**i
for i
in range(4)]
482 supported_ordering
= ["big", "little"]
484 # Creation -------------------------------------------------------------------------------------
485 def __init__(self
, data_width
=32, address_width
=14, alignment
=32, paging
=0x800, ordering
="big", reserved_csrs
={}):
486 SoCLocHandler
.__init
__(self
, "CSR", n_locs
=alignment
//8*(2**address_width
)//paging
)
487 self
.logger
= logging
.getLogger("SoCCSRHandler")
488 self
.logger
.info("Creating CSR Handler...")
491 if data_width
not in self
.supported_data_width
:
492 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
493 colorer("Data Width", color
="red"),
495 colorer(", ".join(str(x
) for x
in self
.supported_data_width
))))
498 # Check Address Width
499 if address_width
not in self
.supported_address_width
:
500 self
.logger
.error("Unsupported {} {} supporteds: {:s}".format(
501 colorer("Address Width", color
="red"),
502 colorer(address_width
),
503 colorer(", ".join(str(x
) for x
in self
.supported_address_width
))))
507 if alignment
not in self
.supported_alignment
:
508 self
.logger
.error("Unsupported {}: {} supporteds: {:s}".format(
509 colorer("Alignment", color
="red"),
511 colorer(", ".join(str(x
) for x
in self
.supported_alignment
))))
513 if data_width
> alignment
:
514 self
.logger
.error("Alignment ({}) {} Data Width ({})".format(
516 colorer("should be >=", color
="red"),
517 colorer(data_width
)))
521 if paging
not in self
.supported_paging
:
522 self
.logger
.error("Unsupported {} 0x{}, supporteds: {:s}".format(
523 colorer("Paging", color
="red"),
524 colorer("{:x}".format(paging
)),
525 colorer(", ".join("0x{:x}".format(x
) for x
in self
.supported_paging
))))
529 if ordering
not in self
.supported_ordering
:
530 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
531 colorer("Ordering", color
="red"),
532 colorer("{}".format(paging
)),
533 colorer(", ".join("{}".format(x
) for x
in self
.supported_ordering
))))
537 self
.data_width
= data_width
538 self
.address_width
= address_width
539 self
.alignment
= alignment
541 self
.ordering
= ordering
544 self
.logger
.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging, {} Ordering (Up to {} Locations).".format(
545 colorer(self
.data_width
),
546 colorer(self
.alignment
),
547 colorer(2**self
.address_width
/2**10),
548 colorer(self
.paging
),
549 colorer(self
.ordering
),
550 colorer(self
.n_locs
)))
552 # Adding reserved CSRs
553 self
.logger
.info("Adding {} CSRs...".format(colorer("reserved", color
="cyan")))
554 for name
, n
in reserved_csrs
.items():
557 self
.logger
.info("CSR Handler {}.".format(colorer("created", color
="green")))
559 # Add Master -----------------------------------------------------------------------------------
560 def add_master(self
, name
=None, master
=None):
562 name
= "master{:d}".format(len(self
.masters
))
563 if name
in self
.masters
.keys():
564 self
.logger
.error("{} {} as CSR Master:".format(
566 colorer("already declared", color
="red")))
567 self
.logger
.error(self
)
569 if master
.data_width
!= self
.data_width
:
570 self
.logger
.error("{} Master/Handler Data Width {} ({} vs {}).".format(
572 colorer("missmatch", color
="red"),
573 colorer(master
.data_width
),
574 colorer(self
.data_width
)))
576 self
.masters
[name
] = master
577 self
.logger
.info("{} {} as CSR Master.".format(
578 colorer(name
, color
="underline"),
579 colorer("added", color
="green")))
581 # Add Region -----------------------------------------------------------------------------------
582 def add_region(self
, name
, region
):
584 self
.regions
[name
] = region
586 # Address map ----------------------------------------------------------------------------------
587 def address_map(self
, name
, memory
):
588 if memory
is not None:
589 name
= name
+ "_" + memory
.name_override
590 if self
.locs
.get(name
, None) is None:
591 self
.logger
.error("CSR {} {}.".format(
593 colorer("not found", color
="red")))
594 self
.logger
.error(self
)
596 return self
.locs
[name
]
598 # Str ------------------------------------------------------------------------------------------
600 r
= "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging, {} Ordering (Up to {} Locations).\n".format(
601 colorer(self
.data_width
),
602 colorer(self
.alignment
),
603 colorer(2**self
.address_width
/2**10),
604 colorer(self
.paging
),
605 colorer(self
.ordering
),
606 colorer(self
.n_locs
))
607 r
+= SoCLocHandler
.__str
__(self
)
611 # SoCIRQHandler ------------------------------------------------------------------------------------
613 class SoCIRQHandler(SoCLocHandler
):
614 # Creation -------------------------------------------------------------------------------------
615 def __init__(self
, n_irqs
=32, reserved_irqs
={}):
616 SoCLocHandler
.__init
__(self
, "IRQ", n_locs
=n_irqs
)
617 self
.logger
= logging
.getLogger("SoCIRQHandler")
618 self
.logger
.info("Creating IRQ Handler...")
622 self
.logger
.error("Unsupported IRQs number: {} supporteds: {:s}".format(
623 colorer(n
, color
="red"), colorer("Up to 32", color
="green")))
627 self
.logger
.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs
)))
629 # Adding reserved IRQs
630 self
.logger
.info("Adding {} IRQs...".format(colorer("reserved", color
="cyan")))
631 for name
, n
in reserved_irqs
.items():
634 self
.logger
.info("IRQ Handler {}.".format(colorer("created", color
="green")))
636 # Str ------------------------------------------------------------------------------------------
638 r
="IRQ Handler (up to {} Locations).\n".format(colorer(self
.n_locs
))
639 r
+= SoCLocHandler
.__str
__(self
)
643 # SoCController ------------------------------------------------------------------------------------
645 class SoCController(Module
, AutoCSR
):
652 self
._reset
= CSRStorage(1, description
="""Write a ``1`` to this register to reset the SoC.""")
654 self
._scratch
= CSRStorage(32, reset
=0x12345678, description
="""
655 Use this register as a scratch space to verify that software read/write accesses
656 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
657 can be used to verify endianness.""")
659 self
._bus
_errors
= CSRStatus(32, description
="Total number of Wishbone bus errors (timeouts) since start.")
665 self
.reset
= Signal()
666 self
.comb
+= self
.reset
.eq(self
._reset
.re
)
670 self
.bus_error
= Signal()
671 bus_errors
= Signal(32)
673 If(bus_errors
!= (2**len(bus_errors
)-1),
674 If(self
.bus_error
, bus_errors
.eq(bus_errors
+ 1))
677 self
.comb
+= self
._bus
_errors
.status
.eq(bus_errors
)
679 # SoC ----------------------------------------------------------------------------------------------
683 def __init__(self
, platform
, sys_clk_freq
,
684 bus_standard
= "wishbone",
686 bus_address_width
= 32,
688 bus_reserved_regions
= {},
691 csr_address_width
= 14,
693 csr_ordering
= "big",
694 csr_reserved_csrs
= {},
697 irq_reserved_irqs
= {},
700 self
.logger
= logging
.getLogger("SoC")
701 self
.logger
.info(colorer(" __ _ __ _ __ ", color
="bright"))
702 self
.logger
.info(colorer(" / / (_) /____ | |/_/ ", color
="bright"))
703 self
.logger
.info(colorer(" / /__/ / __/ -_)> < ", color
="bright"))
704 self
.logger
.info(colorer(" /____/_/\\__/\\__/_/|_| ", color
="bright"))
705 self
.logger
.info(colorer(" Build your hardware, easily!", color
="bright"))
707 self
.logger
.info(colorer("-"*80, color
="bright"))
708 self
.logger
.info(colorer("Creating SoC... ({})".format(build_time())))
709 self
.logger
.info(colorer("-"*80, color
="bright"))
710 self
.logger
.info("FPGA device : {}.".format(platform
.device
))
711 self
.logger
.info("System clock: {:3.2f}MHz.".format(sys_clk_freq
/1e6
))
713 # SoC attributes ---------------------------------------------------------------------------
714 self
.platform
= platform
715 self
.sys_clk_freq
= sys_clk_freq
717 self
.csr_regions
= {}
719 # SoC Bus Handler --------------------------------------------------------------------------
720 self
.submodules
.bus
= SoCBusHandler(
721 standard
= bus_standard
,
722 data_width
= bus_data_width
,
723 address_width
= bus_address_width
,
724 timeout
= bus_timeout
,
725 reserved_regions
= bus_reserved_regions
,
728 # SoC Bus Handler --------------------------------------------------------------------------
729 self
.submodules
.csr
= SoCCSRHandler(
730 data_width
= csr_data_width
,
731 address_width
= csr_address_width
,
734 ordering
= csr_ordering
,
735 reserved_csrs
= csr_reserved_csrs
,
738 # SoC IRQ Handler --------------------------------------------------------------------------
739 self
.submodules
.irq
= SoCIRQHandler(
741 reserved_irqs
= irq_reserved_irqs
744 self
.logger
.info(colorer("-"*80, color
="bright"))
745 self
.logger
.info(colorer("Initial SoC:"))
746 self
.logger
.info(colorer("-"*80, color
="bright"))
747 self
.logger
.info(self
.bus
)
748 self
.logger
.info(self
.csr
)
749 self
.logger
.info(self
.irq
)
750 self
.logger
.info(colorer("-"*80, color
="bright"))
752 self
.add_config("CLOCK_FREQUENCY", int(sys_clk_freq
))
754 # SoC Helpers ----------------------------------------------------------------------------------
755 def check_if_exists(self
, name
):
756 if hasattr(self
, name
):
757 self
.logger
.error("{} SubModule already {}.".format(
759 colorer("declared", color
="red")))
762 def add_constant(self
, name
, value
=None):
764 if name
in self
.constants
.keys():
765 self
.logger
.error("{} Constant already {}.".format(
767 colorer("declared", color
="red")))
769 self
.constants
[name
] = SoCConstant(value
)
771 def add_config(self
, name
, value
=None):
772 name
= "CONFIG_" + name
773 if isinstance(value
, str):
774 self
.add_constant(name
+ "_" + value
)
776 self
.add_constant(name
, value
)
778 # SoC Main Components --------------------------------------------------------------------------
779 def add_controller(self
, name
="ctrl", **kwargs
):
780 self
.check_if_exists(name
)
781 setattr(self
.submodules
, name
, SoCController(**kwargs
))
782 self
.csr
.add(name
, use_loc_if_exists
=True)
784 def add_ram(self
, name
, origin
, size
, contents
=[], mode
="rw"):
786 "wishbone": wishbone
.SRAM
,
787 "axi-lite": axi
.AXILiteSRAM
,
790 "wishbone": wishbone
.Interface
,
791 "axi-lite": axi
.AXILiteInterface
,
793 ram_bus
= interface_cls(data_width
=self
.bus
.data_width
)
794 ram
= ram_cls(size
, bus
=ram_bus
, init
=contents
, read_only
=(mode
== "r"))
795 self
.bus
.add_slave(name
, ram
.bus
, SoCRegion(origin
=origin
, size
=size
, mode
=mode
))
796 self
.check_if_exists(name
)
797 self
.logger
.info("RAM {} {} {}.".format(
799 colorer("added", color
="green"),
800 self
.bus
.regions
[name
]))
801 setattr(self
.submodules
, name
, ram
)
803 def add_rom(self
, name
, origin
, size
, contents
=[]):
804 self
.add_ram(name
, origin
, size
, contents
, mode
="r")
806 def add_csr_bridge(self
, origin
, register
=False):
808 "wishbone": wishbone
.Wishbone2CSR
,
809 "axi-lite": axi
.AXILite2CSR
,
811 self
.submodules
.csr_bridge
= csr_bridge_cls(
812 bus_csr
= csr_bus
.Interface(
813 address_width
= self
.csr
.address_width
,
814 data_width
= self
.csr
.data_width
),
816 csr_size
= 2**(self
.csr
.address_width
+ 2)
817 csr_region
= SoCRegion(origin
=origin
, size
=csr_size
, cached
=False)
818 bus
= getattr(self
.csr_bridge
, self
.bus
.standard
.replace('-', '_'))
819 self
.bus
.add_slave("csr", bus
, csr_region
)
820 self
.csr
.add_master(name
="bridge", master
=self
.csr_bridge
.csr
)
821 self
.add_config("CSR_DATA_WIDTH", self
.csr
.data_width
)
822 self
.add_config("CSR_ALIGNMENT", self
.csr
.alignment
)
824 def add_cpu(self
, name
="vexriscv", variant
="standard", cls
=None, reset_address
=None):
825 if name
not in cpu
.CPUS
.keys():
826 self
.logger
.error("{} CPU {}, supporteds: {}.".format(
828 colorer("not supported", color
="red"),
829 colorer(", ".join(cpu
.CPUS
.keys()))))
832 if name
== "external" and cls
is None:
833 self
.logger
.error("{} CPU requires {} to be specified.".format(
835 colorer("cpu_cls", color
="red")))
837 cpu_cls
= cls
if cls
is not None else cpu
.CPUS
[name
]
838 if variant
not in cpu_cls
.variants
:
839 self
.logger
.error("{} CPU variant {}, supporteds: {}.".format(
841 colorer("not supported", color
="red"),
842 colorer(", ".join(cpu_cls
.variants
))))
844 self
.submodules
.cpu
= cpu_cls(self
.platform
, variant
)
845 # Update SoC with CPU constraints
846 for n
, (origin
, size
) in enumerate(self
.cpu
.io_regions
.items()):
847 self
.bus
.add_region("io{}".format(n
), SoCIORegion(origin
=origin
, size
=size
, cached
=False))
848 self
.mem_map
.update(self
.cpu
.mem_map
) # FIXME
849 # Add Bus Masters/CSR/IRQs
850 if not isinstance(self
.cpu
, (cpu
.CPUNone
, cpu
.Zynq7000
)):
851 if reset_address
is None:
852 reset_address
= self
.mem_map
["rom"]
853 self
.cpu
.set_reset_address(reset_address
)
854 for n
, cpu_bus
in enumerate(self
.cpu
.periph_buses
):
855 self
.bus
.add_master(name
="cpu_bus{}".format(n
), master
=cpu_bus
)
856 self
.csr
.add("cpu", use_loc_if_exists
=True)
857 if hasattr(self
.cpu
, "interrupt"):
858 for name
, loc
in self
.cpu
.interrupts
.items():
859 self
.irq
.add(name
, loc
)
860 self
.add_config("CPU_HAS_INTERRUPT")
862 # Create optional DMA Bus (for Cache Coherence)
863 if hasattr(self
.cpu
, "dma_bus"):
864 self
.submodules
.dma_bus
= SoCBusHandler(
865 name
= "SoCDMABusHandler",
866 standard
= "wishbone",
867 data_width
= self
.bus
.data_width
,
868 address_width
= self
.bus
.address_width
,
870 dma_bus
= wishbone
.Interface(data_width
=self
.bus
.data_width
)
871 self
.dma_bus
.add_slave("dma", slave
=dma_bus
, region
=SoCRegion(origin
=0x00000000, size
=0x100000000)) # FIXME: covers lower 4GB only
872 self
.submodules
+= wishbone
.Converter(dma_bus
, self
.cpu
.dma_bus
)
874 # Connect SoCController's reset to CPU reset
875 if hasattr(self
, "ctrl"):
876 if hasattr(self
.ctrl
, "reset"):
877 self
.comb
+= self
.cpu
.reset
.eq(self
.ctrl
.reset
)
878 self
.add_config("CPU_RESET_ADDR", reset_address
)
880 self
.add_config("CPU_TYPE", str(name
))
881 self
.add_config("CPU_VARIANT", str(variant
.split('+')[0]))
882 self
.add_constant("CONFIG_CPU_HUMAN_NAME", getattr(self
.cpu
, "human_name", "Unknown"))
883 if hasattr(self
.cpu
, "nop"):
884 self
.add_constant("CONFIG_CPU_NOP", self
.cpu
.nop
)
886 def add_timer(self
, name
="timer0"):
887 self
.check_if_exists(name
)
888 setattr(self
.submodules
, name
, Timer())
889 self
.csr
.add(name
, use_loc_if_exists
=True)
890 if hasattr(self
.cpu
, "interrupt"):
891 self
.irq
.add(name
, use_loc_if_exists
=True)
893 # SoC finalization -----------------------------------------------------------------------------
894 def do_finalize(self
):
895 self
.logger
.info(colorer("-"*80, color
="bright"))
896 self
.logger
.info(colorer("Finalized SoC:"))
897 self
.logger
.info(colorer("-"*80, color
="bright"))
898 self
.logger
.info(self
.bus
)
899 if hasattr(self
, "dma_bus"):
900 self
.logger
.info(self
.dma_bus
)
901 self
.logger
.info(self
.csr
)
902 self
.logger
.info(self
.irq
)
903 self
.logger
.info(colorer("-"*80, color
="bright"))
905 interconnect_p2p_cls
= {
906 "wishbone": wishbone
.InterconnectPointToPoint
,
907 "axi-lite": axi
.AXILiteInterconnectPointToPoint
,
909 interconnect_shared_cls
= {
910 "wishbone": wishbone
.InterconnectShared
,
911 "axi-lite": axi
.AXILiteInterconnectShared
,
914 # SoC CSR bridge ---------------------------------------------------------------------------
915 # FIXME: for now, use registered CSR bridge when SDRAM is present; find the best compromise.
916 self
.add_csr_bridge(self
.mem_map
["csr"], register
=hasattr(self
, "sdram"))
918 # SoC Bus Interconnect ---------------------------------------------------------------------
919 if len(self
.bus
.masters
) and len(self
.bus
.slaves
):
920 # If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
921 if ((len(self
.bus
.masters
) == 1) and
922 (len(self
.bus
.slaves
) == 1) and
923 (next(iter(self
.bus
.regions
.values())).origin
== 0)):
924 self
.submodules
.bus_interconnect
= interconnect_p2p_cls(
925 master
= next(iter(self
.bus
.masters
.values())),
926 slave
= next(iter(self
.bus
.slaves
.values())))
927 # Otherwise, use InterconnectShared.
929 self
.submodules
.bus_interconnect
= interconnect_shared_cls(
930 masters
= self
.bus
.masters
.values(),
931 slaves
= [(self
.bus
.regions
[n
].decoder(self
.bus
), s
) for n
, s
in self
.bus
.slaves
.items()],
933 timeout_cycles
= self
.bus
.timeout
)
934 if hasattr(self
, "ctrl") and self
.bus
.timeout
is not None:
935 if hasattr(self
.ctrl
, "bus_error"):
936 self
.comb
+= self
.ctrl
.bus_error
.eq(self
.bus_interconnect
.timeout
.error
)
937 self
.bus
.logger
.info("Interconnect: {} ({} <-> {}).".format(
938 colorer(self
.bus_interconnect
.__class
__.__name
__),
939 colorer(len(self
.bus
.masters
)),
940 colorer(len(self
.bus
.slaves
))))
941 self
.add_constant("CONFIG_BUS_STANDARD", self
.bus
.standard
.upper())
942 self
.add_constant("CONFIG_BUS_DATA_WIDTH", self
.bus
.data_width
)
943 self
.add_constant("CONFIG_BUS_ADDRESS_WIDTH", self
.bus
.address_width
)
945 # SoC DMA Bus Interconnect (Cache Coherence) -----------------------------------------------
946 if hasattr(self
, "dma_bus"):
947 if len(self
.dma_bus
.masters
) and len(self
.dma_bus
.slaves
):
948 # If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
949 if ((len(self
.dma_bus
.masters
) == 1) and
950 (len(self
.dma_bus
.slaves
) == 1) and
951 (next(iter(self
.dma_bus
.regions
.values())).origin
== 0)):
952 self
.submodules
.dma_bus_interconnect
= wishbone
.InterconnectPointToPoint(
953 master
= next(iter(self
.dma_bus
.masters
.values())),
954 slave
= next(iter(self
.dma_bus
.slaves
.values())))
955 # Otherwise, use InterconnectShared.
957 self
.submodules
.dma_bus_interconnect
= wishbone
.InterconnectShared(
958 masters
= self
.dma_bus
.masters
.values(),
959 slaves
= [(self
.dma_bus
.regions
[n
].decoder(self
.dma_bus
), s
) for n
, s
in self
.dma_bus
.slaves
.items()],
961 self
.bus
.logger
.info("DMA Interconnect: {} ({} <-> {}).".format(
962 colorer(self
.dma_bus_interconnect
.__class
__.__name
__),
963 colorer(len(self
.dma_bus
.masters
)),
964 colorer(len(self
.dma_bus
.slaves
))))
965 self
.add_constant("CONFIG_CPU_HAS_DMA_BUS")
967 # SoC CSR Interconnect ---------------------------------------------------------------------
968 self
.submodules
.csr_bankarray
= csr_bus
.CSRBankArray(self
,
969 address_map
= self
.csr
.address_map
,
970 data_width
= self
.csr
.data_width
,
971 address_width
= self
.csr
.address_width
,
972 alignment
= self
.csr
.alignment
,
973 paging
= self
.csr
.paging
,
974 ordering
= self
.csr
.ordering
,
975 soc_bus_data_width
= self
.bus
.data_width
)
976 if len(self
.csr
.masters
):
977 self
.submodules
.csr_interconnect
= csr_bus
.InterconnectShared(
978 masters
= list(self
.csr
.masters
.values()),
979 slaves
= self
.csr_bankarray
.get_buses())
982 for name
, csrs
, mapaddr
, rmap
in self
.csr_bankarray
.banks
:
983 self
.csr
.add_region(name
, SoCCSRRegion(
984 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
985 busword
= self
.csr
.data_width
,
989 for name
, memory
, mapaddr
, mmap
in self
.csr_bankarray
.srams
:
990 self
.csr
.add_region(name
+ "_" + memory
.name_override
, SoCCSRRegion(
991 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
992 busword
= self
.csr
.data_width
,
995 # Sort CSR regions by origin
996 self
.csr
.regions
= {k
: v
for k
, v
in sorted(self
.csr
.regions
.items(), key
=lambda item
: item
[1].origin
)}
998 # Add CSRs / Config items to constants
999 for name
, constant
in self
.csr_bankarray
.constants
:
1000 self
.add_constant(name
+ "_" + constant
.name
, constant
.value
.value
)
1002 # SoC CPU Check ----------------------------------------------------------------------------
1003 if not isinstance(self
.cpu
, (cpu
.CPUNone
, cpu
.Zynq7000
)):
1004 if "sram" not in self
.bus
.regions
.keys():
1005 self
.logger
.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
1007 colorer("defined", color
="red")))
1008 self
.logger
.error(self
.bus
)
1010 cpu_reset_address_valid
= False
1011 for name
, container
in self
.bus
.regions
.items():
1012 if self
.bus
.check_region_is_in(
1013 region
= SoCRegion(origin
=self
.cpu
.reset_address
, size
=self
.bus
.data_width
//8),
1014 container
= container
):
1015 cpu_reset_address_valid
= True
1017 self
.cpu
.use_rom
= True
1018 if not cpu_reset_address_valid
:
1019 self
.logger
.error("CPU needs {} to be in a {} Region.".format(
1020 colorer("reset address 0x{:08x}".format(self
.cpu
.reset_address
)),
1021 colorer("defined", color
="red")))
1022 self
.logger
.error(self
.bus
)
1025 # SoC IRQ Interconnect ---------------------------------------------------------------------
1026 if hasattr(self
, "cpu"):
1027 if hasattr(self
.cpu
, "interrupt"):
1028 for name
, loc
in sorted(self
.irq
.locs
.items()):
1029 if name
in self
.cpu
.interrupts
.keys():
1031 if hasattr(self
, name
):
1032 module
= getattr(self
, name
)
1033 if not hasattr(module
, "ev"):
1034 self
.logger
.error("EventManager {} in {} SubModule.".format(
1035 colorer("not found", color
="red"),
1038 self
.comb
+= self
.cpu
.interrupt
[loc
].eq(module
.ev
.irq
)
1039 self
.add_constant(name
+ "_INTERRUPT", loc
)
1041 # SoC build ------------------------------------------------------------------------------------
1042 def build(self
, *args
, **kwargs
):
1043 self
.build_name
= kwargs
.pop("build_name", self
.platform
.name
)
1044 kwargs
.update({"build_name": self
.build_name
})
1045 return self
.platform
.build(self
, *args
, **kwargs
)
1047 # LiteXSoC -----------------------------------------------------------------------------------------
1049 class LiteXSoC(SoC
):
1050 # Add Identifier -------------------------------------------------------------------------------
1051 def add_identifier(self
, name
="identifier", identifier
="LiteX SoC", with_build_time
=True):
1052 self
.check_if_exists(name
)
1054 identifier
+= " " + build_time()
1055 setattr(self
.submodules
, name
, Identifier(identifier
))
1056 self
.csr
.add(name
+ "_mem", use_loc_if_exists
=True)
1058 # Add UART -------------------------------------------------------------------------------------
1059 def add_uart(self
, name
, baudrate
=115200, fifo_depth
=16):
1060 from litex
.soc
.cores
import uart
1063 if name
in ["stub", "stream"]:
1064 self
.submodules
.uart
= uart
.UART(tx_fifo_depth
=0, rx_fifo_depth
=0)
1066 self
.comb
+= self
.uart
.sink
.ready
.eq(1)
1069 elif name
in ["uartbone", "bridge"]:
1070 self
.add_uartbone(baudrate
=baudrate
)
1073 elif name
in ["crossover"]:
1074 self
.submodules
.uart
= uart
.UARTCrossover()
1077 elif name
in ["model", "sim"]:
1078 self
.submodules
.uart_phy
= uart
.RS232PHYModel(self
.platform
.request("serial"))
1079 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
,
1080 tx_fifo_depth
= fifo_depth
,
1081 rx_fifo_depth
= fifo_depth
))
1084 elif name
in ["jtag_atlantic"]:
1085 from litex
.soc
.cores
.jtag
import JTAGAtlantic
1086 self
.submodules
.uart_phy
= JTAGAtlantic()
1087 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
,
1088 tx_fifo_depth
= fifo_depth
,
1089 rx_fifo_depth
= fifo_depth
))
1092 elif name
in ["jtag_uart"]:
1093 from litex
.soc
.cores
.jtag
import JTAGPHY
1094 self
.submodules
.uart_phy
= JTAGPHY(device
=self
.platform
.device
)
1095 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
,
1096 tx_fifo_depth
= fifo_depth
,
1097 rx_fifo_depth
= fifo_depth
))
1099 # USB ACM (with ValentyUSB core)
1100 elif name
in ["usb_acm"]:
1101 import valentyusb
.usbcore
.io
as usbio
1102 import valentyusb
.usbcore
.cpu
.cdc_eptri
as cdc_eptri
1103 usb_pads
= self
.platform
.request("usb")
1104 usb_iobuf
= usbio
.IoBuf(usb_pads
.d_p
, usb_pads
.d_n
, usb_pads
.pullup
)
1105 self
.submodules
.uart
= cdc_eptri
.CDCUsb(usb_iobuf
)
1109 self
.submodules
.uart_phy
= uart
.UARTPHY(
1110 pads
= self
.platform
.request(name
),
1111 clk_freq
= self
.sys_clk_freq
,
1112 baudrate
= baudrate
)
1113 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
,
1114 tx_fifo_depth
= fifo_depth
,
1115 rx_fifo_depth
= fifo_depth
))
1117 self
.csr
.add("uart_phy", use_loc_if_exists
=True)
1118 self
.csr
.add("uart", use_loc_if_exists
=True)
1119 if hasattr(self
.cpu
, "interrupt"):
1120 self
.irq
.add("uart", use_loc_if_exists
=True)
1122 self
.add_constant("UART_POLLING")
1124 # Add UARTbone ---------------------------------------------------------------------------------
1125 def add_uartbone(self
, name
="serial", baudrate
=115200):
1126 from litex
.soc
.cores
import uart
1127 self
.submodules
.uartbone
= uart
.UARTBone(
1128 pads
= self
.platform
.request(name
),
1129 clk_freq
= self
.sys_clk_freq
,
1130 baudrate
= baudrate
)
1131 self
.bus
.add_master(name
="uartbone", master
=self
.uartbone
.wishbone
)
1133 # Add SDRAM ------------------------------------------------------------------------------------
1134 def add_sdram(self
, name
, phy
, module
, origin
, size
=None, with_soc_interconnect
=True,
1135 l2_cache_size
= 8192,
1136 l2_cache_min_data_width
= 128,
1137 l2_cache_reverse
= True,
1138 l2_cache_full_memory_we
= True,
1142 from litedram
.common
import LiteDRAMNativePort
1143 from litedram
.core
import LiteDRAMCore
1144 from litedram
.frontend
.wishbone
import LiteDRAMWishbone2Native
1145 from litedram
.frontend
.axi
import LiteDRAMAXI2Native
1148 self
.submodules
.sdram
= LiteDRAMCore(
1150 geom_settings
= module
.geom_settings
,
1151 timing_settings
= module
.timing_settings
,
1152 clk_freq
= self
.sys_clk_freq
,
1154 self
.csr
.add("sdram")
1156 # Save SPD data to be able to verify it at runtime
1157 if hasattr(module
, "_spd_data"):
1158 # pack the data into words of bus width
1159 bytes_per_word
= self
.bus
.data_width
// 8
1160 mem
= [0] * ceil(len(module
._spd
_data
) / bytes_per_word
)
1161 for i
in range(len(mem
)):
1162 for offset
in range(bytes_per_word
):
1164 if self
.cpu
.endianness
== "little":
1165 offset
= bytes_per_word
- 1 - offset
1166 spd_byte
= i
* bytes_per_word
+ offset
1167 if spd_byte
< len(module
._spd
_data
):
1168 mem
[i
] |
= module
._spd
_data
[spd_byte
]
1171 origin
=self
.mem_map
.get("spd", None),
1172 size
=len(module
._spd
_data
),
1176 if not with_soc_interconnect
: return
1178 # Compute/Check SDRAM size
1179 sdram_size
= 2**(module
.geom_settings
.bankbits
+
1180 module
.geom_settings
.rowbits
+
1181 module
.geom_settings
.colbits
)*phy
.settings
.databits
//8
1182 if size
is not None:
1183 sdram_size
= min(sdram_size
, size
)
1186 self
.bus
.add_region("main_ram", SoCRegion(origin
=origin
, size
=sdram_size
))
1188 # Add CPU's direct memory buses (if not already declared) ----------------------------------
1189 if hasattr(self
.cpu
, "add_memory_buses"):
1190 self
.cpu
.add_memory_buses(
1192 data_width
= self
.sdram
.crossbar
.controller
.data_width
1195 # Connect CPU's direct memory buses to LiteDRAM --------------------------------------------
1196 if len(self
.cpu
.memory_buses
):
1197 # When CPU has at least a direct memory bus, connect them directly to LiteDRAM.
1198 for mem_bus
in self
.cpu
.memory_buses
:
1199 # Request a LiteDRAM native port.
1200 port
= self
.sdram
.crossbar
.get_port()
1201 port
.data_width
= 2**int(log2(port
.data_width
)) # Round to nearest power of 2.
1203 # Check if bus is an AXI bus and connect it.
1204 if isinstance(mem_bus
, axi
.AXIInterface
):
1205 # If same data_width, connect it directly.
1206 if port
.data_width
== mem_bus
.data_width
:
1207 self
.logger
.info("Matching AXI MEM data width ({})\n".format(port
.data_width
))
1208 self
.submodules
+= LiteDRAMAXI2Native(
1209 axi
= self
.cpu
.mem_axi
,
1211 base_address
= self
.bus
.regions
["main_ram"].origin
)
1212 # If different data_width, do the adaptation and connect it via Wishbone.
1214 self
.logger
.info("Converting MEM data width: {} to {} via Wishbone".format(
1216 self
.cpu
.mem_axi
.data_width
))
1217 # FIXME: replace WB data-width converter with native AXI converter!!!
1218 mem_wb
= wishbone
.Interface(
1219 data_width
= self
.cpu
.mem_axi
.data_width
,
1220 adr_width
= 32-log2_int(self
.cpu
.mem_axi
.data_width
//8))
1221 # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
1222 mem_a2w
= ResetInserter()(axi
.AXI2Wishbone(
1223 axi
= self
.cpu
.mem_axi
,
1226 self
.comb
+= mem_a2w
.reset
.eq(ResetSignal() | self
.cpu
.reset
)
1227 self
.submodules
+= mem_a2w
1228 litedram_wb
= wishbone
.Interface(port
.data_width
)
1229 self
.submodules
+= LiteDRAMWishbone2Native(
1230 wishbone
= litedram_wb
,
1232 base_address
= origin
)
1233 self
.submodules
+= wishbone
.Converter(mem_wb
, litedram_wb
)
1234 # Check if bus is a Native bus and connect it.
1235 if isinstance(mem_bus
, LiteDRAMNativePort
):
1236 # If same data_width, connect it directly.
1237 if port
.data_width
== mem_bus
.data_width
:
1238 self
.comb
+= mem_bus
.cmd
.connect(port
.cmd
)
1239 self
.comb
+= mem_bus
.wdata
.connect(port
.wdata
)
1240 self
.comb
+= port
.rdata
.connect(mem_bus
.rdata
)
1243 raise NotImplementedError
1245 # Connect Main bus to LiteDRAM (with optional L2 Cache) ------------------------------------
1246 connect_main_bus_to_dram
= (
1248 (not len(self
.cpu
.memory_buses
)) or
1249 # Memory buses but no DMA bus.
1250 (len(self
.cpu
.memory_buses
) and not hasattr(self
.cpu
, "dma_bus"))
1252 if connect_main_bus_to_dram
:
1253 # Request a LiteDRAM native port.
1254 port
= self
.sdram
.crossbar
.get_port()
1255 port
.data_width
= 2**int(log2(port
.data_width
)) # Round to nearest power of 2.
1257 # Create Wishbone Slave.
1258 wb_sdram
= wishbone
.Interface()
1259 self
.bus
.add_slave("main_ram", wb_sdram
)
1262 if l2_cache_size
!= 0:
1263 # Insert L2 cache inbetween Wishbone bus and LiteDRAM
1264 l2_cache_size
= max(l2_cache_size
, int(2*port
.data_width
/8)) # Use minimal size if lower
1265 l2_cache_size
= 2**int(log2(l2_cache_size
)) # Round to nearest power of 2
1266 l2_cache_data_width
= max(port
.data_width
, l2_cache_min_data_width
)
1267 l2_cache
= wishbone
.Cache(
1268 cachesize
= l2_cache_size
//4,
1270 slave
= wishbone
.Interface(l2_cache_data_width
),
1271 reverse
= l2_cache_reverse
)
1272 if l2_cache_full_memory_we
:
1273 l2_cache
= FullMemoryWE()(l2_cache
)
1274 self
.submodules
.l2_cache
= l2_cache
1275 litedram_wb
= self
.l2_cache
.slave
1277 litedram_wb
= wishbone
.Interface(port
.data_width
)
1278 self
.submodules
+= wishbone
.Converter(wb_sdram
, litedram_wb
)
1279 self
.add_config("L2_SIZE", l2_cache_size
)
1281 # Wishbone Slave <--> LiteDRAM bridge
1282 self
.submodules
.wishbone_bridge
= LiteDRAMWishbone2Native(
1283 wishbone
= litedram_wb
,
1285 base_address
= self
.bus
.regions
["main_ram"].origin
)
1287 # Add Ethernet ---------------------------------------------------------------------------------
1288 def add_ethernet(self
, name
="ethmac", phy
=None):
1290 from liteeth
.mac
import LiteEthMAC
1292 ethmac
= LiteEthMAC(
1295 interface
= "wishbone",
1296 endianness
= self
.cpu
.endianness
)
1297 setattr(self
.submodules
, name
, ethmac
)
1298 ethmac_region
= SoCRegion(origin
=self
.mem_map
.get(name
, None), size
=0x2000, cached
=False)
1299 self
.bus
.add_slave(name
=name
, slave
=ethmac
.bus
, region
=ethmac_region
)
1301 self
.add_interrupt(name
)
1302 # Timing constraints
1303 if hasattr(phy
, "crg"):
1304 eth_rx_clk
= phy
.crg
.cd_eth_rx
.clk
1305 eth_tx_clk
= phy
.crg
.cd_eth_tx
.clk
1307 eth_rx_clk
= phy
.cd_eth_rx
.clk
1308 eth_tx_clk
= phy
.cd_eth_tx
.clk
1309 self
.platform
.add_period_constraint(eth_rx_clk
, 1e9
/phy
.rx_clk_freq
)
1310 self
.platform
.add_period_constraint(eth_tx_clk
, 1e9
/phy
.tx_clk_freq
)
1311 self
.platform
.add_false_path_constraints(
1312 self
.crg
.cd_sys
.clk
,
1316 # Add Etherbone --------------------------------------------------------------------------------
1317 def add_etherbone(self
, name
="etherbone", phy
=None,
1318 mac_address
= 0x10e2d5000000,
1319 ip_address
= "192.168.1.50",
1322 from liteeth
.core
import LiteEthUDPIPCore
1323 from liteeth
.frontend
.etherbone
import LiteEthEtherbone
1325 ethcore
= LiteEthUDPIPCore(
1327 mac_address
= mac_address
,
1328 ip_address
= ip_address
,
1329 clk_freq
= self
.clk_freq
)
1330 ethcore
= ClockDomainsRenamer("eth_tx")(ethcore
)
1331 self
.submodules
.ethcore
= ethcore
1333 # Clock domain renaming
1334 self
.clock_domains
.cd_etherbone
= ClockDomain("etherbone")
1335 self
.comb
+= self
.cd_etherbone
.clk
.eq(ClockSignal("sys"))
1336 self
.comb
+= self
.cd_etherbone
.rst
.eq(ResetSignal("sys"))
1339 etherbone
= LiteEthEtherbone(ethcore
.udp
, udp_port
, cd
="etherbone")
1340 setattr(self
.submodules
, name
, etherbone
)
1341 self
.add_wb_master(etherbone
.wishbone
.bus
)
1342 # Timing constraints
1343 if hasattr(phy
, "crg"):
1344 eth_rx_clk
= phy
.crg
.cd_eth_rx
.clk
1345 eth_tx_clk
= phy
.crg
.cd_eth_tx
.clk
1347 eth_rx_clk
= phy
.cd_eth_rx
.clk
1348 eth_tx_clk
= phy
.cd_eth_tx
.clk
1349 self
.platform
.add_period_constraint(eth_rx_clk
, 1e9
/phy
.rx_clk_freq
)
1350 self
.platform
.add_period_constraint(eth_tx_clk
, 1e9
/phy
.tx_clk_freq
)
1351 self
.platform
.add_false_path_constraints(
1352 self
.crg
.cd_sys
.clk
,
1356 # Add SPI Flash --------------------------------------------------------------------------------
1357 def add_spi_flash(self
, name
="spiflash", mode
="4x", dummy_cycles
=None, clk_freq
=None):
1358 assert dummy_cycles
is not None # FIXME: Get dummy_cycles from SPI Flash
1359 assert mode
in ["1x", "4x"]
1360 if clk_freq
is None: clk_freq
= self
.clk_freq
/2 # FIXME: Get max clk_freq from SPI Flash
1361 spiflash
= SpiFlash(
1362 pads
= self
.platform
.request(name
if mode
== "1x" else name
+ mode
),
1363 dummy
= dummy_cycles
,
1364 div
= ceil(self
.clk_freq
/clk_freq
),
1365 with_bitbang
= True,
1366 endianness
= self
.cpu
.endianness
)
1367 spiflash
.add_clk_primitive(self
.platform
.device
)
1368 setattr(self
.submodules
, name
, spiflash
)
1369 self
.add_memory_region(name
, self
.mem_map
[name
], 0x1000000) # FIXME: Get size from SPI Flash
1370 self
.add_wb_slave(self
.mem_map
[name
], spiflash
.bus
)
1373 # Add SPI SDCard -------------------------------------------------------------------------------
1374 def add_spi_sdcard(self
, name
="spisdcard", spi_clk_freq
=400e3
):
1375 pads
= self
.platform
.request(name
)
1376 if hasattr(pads
, "rst"):
1377 self
.comb
+= pads
.rst
.eq(0)
1378 spisdcard
= SPIMaster(pads
, 8, self
.sys_clk_freq
, spi_clk_freq
)
1379 spisdcard
.add_clk_divider()
1380 setattr(self
.submodules
, name
, spisdcard
)
1383 # Add SDCard -----------------------------------------------------------------------------------
1384 def add_sdcard(self
, name
="sdcard", mode
="read+write", use_emulator
=False):
1385 assert mode
in ["read", "write", "read+write"]
1387 from litesdcard
.emulator
import SDEmulator
1388 from litesdcard
.phy
import SDPHY
1389 from litesdcard
.core
import SDCore
1390 from litesdcard
.frontend
.dma
import SDBlock2MemDMA
, SDMem2BlockDMA
1394 sdemulator
= SDEmulator(self
.platform
)
1395 self
.submodules
+= sdemulator
1396 sdcard_pads
= sdemulator
.pads
1398 sdcard_pads
= self
.platform
.request(name
)
1401 self
.submodules
.sdphy
= SDPHY(sdcard_pads
, self
.platform
.device
, self
.clk_freq
)
1402 self
.submodules
.sdcore
= SDCore(self
.sdphy
)
1403 self
.add_csr("sdphy")
1404 self
.add_csr("sdcore")
1408 bus
= wishbone
.Interface(data_width
=self
.bus
.data_width
, adr_width
=self
.bus
.address_width
)
1409 self
.submodules
.sdblock2mem
= SDBlock2MemDMA(bus
=bus
, endianness
=self
.cpu
.endianness
)
1410 self
.comb
+= self
.sdcore
.source
.connect(self
.sdblock2mem
.sink
)
1411 dma_bus
= self
.bus
if not hasattr(self
, "dma_bus") else self
.dma_bus
1412 dma_bus
.add_master("sdblock2mem", master
=bus
)
1413 self
.add_csr("sdblock2mem")
1417 bus
= wishbone
.Interface(data_width
=self
.bus
.data_width
, adr_width
=self
.bus
.address_width
)
1418 self
.submodules
.sdmem2block
= SDMem2BlockDMA(bus
=bus
, endianness
=self
.cpu
.endianness
)
1419 self
.comb
+= self
.sdmem2block
.source
.connect(self
.sdcore
.sink
)
1420 dma_bus
= self
.bus
if not hasattr(self
, "dma_bus") else self
.dma_bus
1421 dma_bus
.add_master("sdmem2block", master
=bus
)
1422 self
.add_csr("sdmem2block")