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 wishbone
22 from litex
.soc
.interconnect
import wishbone2csr
23 from litex
.soc
.interconnect
import axi
26 # - replace raise with exit on logging error.
27 # - cleanup SoCCSRRegion
29 logging
.basicConfig(level
=logging
.INFO
)
31 # Helpers ------------------------------------------------------------------------------------------
36 def colorer(s
, color
="bright"):
43 "underline": "\x1b[4m"}[color
]
45 return header
+ str(s
) + trailer
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
)
51 # SoCConstant --------------------------------------------------------------------------------------
53 def SoCConstant(value
):
56 # SoCRegion ----------------------------------------------------------------------------------------
59 def __init__(self
, origin
=None, size
=None, mode
="rw", cached
=True, linker
=False):
60 self
.logger
= logging
.getLogger("SoCRegion")
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)
73 def decoder(self
, bus
):
76 if (origin
& (size
- 1)) != 0:
77 self
.logger
.error("Origin needs to be aligned on size:")
78 self
.logger
.error(self
)
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
)))
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
))
95 class SoCIORegion(SoCRegion
): pass
97 # SoCCSRRegion -------------------------------------------------------------------------------------
100 def __init__(self
, origin
, busword
, obj
):
102 self
.busword
= busword
105 # SoCBusHandler ------------------------------------------------------------------------------------
107 class SoCBusHandler(Module
):
108 supported_standard
= ["wishbone"]
109 supported_data_width
= [32, 64]
110 supported_address_width
= [32]
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...")
118 if standard
not in self
.supported_standard
:
119 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
120 colorer("Bus standard", color
="red"),
122 colorer(", ".join(self
.supported_standard
))))
126 if data_width
not in self
.supported_data_width
:
127 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
128 colorer("Data Width", color
="red"),
130 colorer(", ".join(str(x
) for x
in self
.supported_data_width
))))
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"),
138 colorer(", ".join(str(x
) for x
in self
.supported_address_width
))))
142 self
.standard
= standard
143 self
.data_width
= data_width
144 self
.address_width
= address_width
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)))
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
)
160 self
.logger
.info("Bus Handler {}.".format(colorer("created", color
="green")))
162 # Add/Allog/Check Regions ----------------------------------------------------------------------
163 def add_region(self
, name
, region
):
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
)
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"),
177 colorer(overlap
[1])))
178 self
.logger
.error(str(self
.io_regions
[overlap
[0]]))
179 self
.logger
.error(str(self
.io_regions
[overlap
[1]]))
181 self
.logger
.info("{} Region {} at {}.".format(
182 colorer(name
, color
="underline"),
183 colorer("added", color
="green"),
186 elif isinstance(region
, SoCRegion
):
187 # If no origin specified, allocate region.
188 if region
.origin
is None:
190 region
= self
.alloc_region(name
, region
.size
, region
.cached
)
191 self
.regions
[name
] = region
192 # Else add region and check for overlaps.
194 if not region
.cached
:
195 if not self
.check_region_is_io(region
):
196 self
.logger
.error("{} Region {}: {}.".format(
198 colorer("not in IO region", color
="red"),
200 self
.logger
.error(self
)
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"),
208 colorer(overlap
[1])))
209 self
.logger
.error(str(self
.regions
[overlap
[0]]))
210 self
.logger
.error(str(self
.regions
[overlap
[1]]))
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"),
217 self
.logger
.error("{} is not a supported Region.".format(colorer(name
, color
="red")))
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
))))
225 # Limit Search Regions
227 search_regions
= self
.io_regions
229 search_regions
= {"main": SoCRegion(origin
=0x00000000, size
=2**self
.address_width
-1)}
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
)
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
245 # If no overlap, the Candidate is selected
248 self
.logger
.error("Not enough Address Space to allocate Region.")
251 def check_regions_overlap(self
, regions
, check_linker
=False):
253 while i
< len(regions
):
254 n0
= list(regions
.keys())[i
]
256 for n1
in list(regions
.keys())[i
+1:]:
258 if r0
.linker
or r1
.linker
:
261 if r0
.origin
>= (r1
.origin
+ r1
.size_pow2
):
263 if r1
.origin
>= (r0
.origin
+ r0
.size_pow2
):
269 def check_region_is_in(self
, region
, container
):
271 if not (region
.origin
>= container
.origin
):
273 if not ((region
.origin
+ region
.size
) < (container
.origin
+ container
.size
)):
277 def check_region_is_io(self
, region
):
279 for _
, io_region
in self
.io_regions
.items():
280 if self
.check_region_is_in(region
, io_region
):
284 # Add Master/Slave -----------------------------------------------------------------------------
285 def add_adapter(self
, name
, interface
):
286 if interface
.data_width
!= self
.data_width
:
287 self
.logger
.info("{} Bus {} from {}-bit to {}-bit.".format(
289 colorer("converted", color
="cyan"),
290 colorer(interface
.data_width
),
291 colorer(self
.data_width
)))
292 new_interface
= wishbone
.Interface(data_width
=self
.data_width
)
293 self
.submodules
+= wishbone
.Converter(interface
, new_interface
)
298 def add_master(self
, name
=None, master
=None):
300 name
= "master{:d}".format(len(self
.masters
))
301 if name
in self
.masters
.keys():
302 self
.logger
.error("{} {} as Bus Master:".format(
304 colorer("already declared", color
="red")))
305 self
.logger
.error(self
)
307 master
= self
.add_adapter(name
, master
)
308 self
.masters
[name
] = master
309 self
.logger
.info("{} {} as Bus Master.".format(
310 colorer(name
, color
="underline"),
311 colorer("added", color
="green")))
313 def add_slave(self
, name
=None, slave
=None, region
=None):
314 no_name
= name
is None
315 no_region
= region
is None
316 if no_name
and no_region
:
317 self
.logger
.error("Please {} {} or/and {} of Bus Slave.".format(
318 colorer("specify", color
="red"),
323 name
= "slave{:d}".format(len(self
.slaves
))
325 region
= self
.regions
.get(name
, None)
327 self
.logger
.error("{} Region {}.".format(
329 colorer("not found", color
="red")))
332 self
.add_region(name
, region
)
333 if name
in self
.slaves
.keys():
334 self
.logger
.error("{} {} as Bus Slave:".format(
336 colorer("already declared", color
="red")))
337 self
.logger
.error(self
)
339 slave
= self
.add_adapter(name
, slave
)
340 self
.slaves
[name
] = slave
341 self
.logger
.info("{} {} as Bus Slave.".format(
342 colorer(name
, color
="underline"),
343 colorer("added", color
="green")))
345 # Str ------------------------------------------------------------------------------------------
347 r
= "{}-bit {} Bus, {}GiB Address Space.\n".format(
348 colorer(self
.data_width
), colorer(self
.standard
), colorer(2**self
.address_width
/2**30))
349 r
+= "IO Regions: ({})\n".format(len(self
.io_regions
.keys())) if len(self
.io_regions
.keys()) else ""
350 io_regions
= {k
: v
for k
, v
in sorted(self
.io_regions
.items(), key
=lambda item
: item
[1].origin
)}
351 for name
, region
in io_regions
.items():
352 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
353 r
+= "Bus Regions: ({})\n".format(len(self
.regions
.keys())) if len(self
.regions
.keys()) else ""
354 regions
= {k
: v
for k
, v
in sorted(self
.regions
.items(), key
=lambda item
: item
[1].origin
)}
355 for name
, region
in regions
.items():
356 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
357 r
+= "Bus Masters: ({})\n".format(len(self
.masters
.keys())) if len(self
.masters
.keys()) else ""
358 for name
in self
.masters
.keys():
359 r
+= "- {}\n".format(colorer(name
, color
="underline"))
360 r
+= "Bus Slaves: ({})\n".format(len(self
.slaves
.keys())) if len(self
.slaves
.keys()) else ""
361 for name
in self
.slaves
.keys():
362 r
+= "- {}\n".format(colorer(name
, color
="underline"))
366 # SoCLocHandler --------------------------------------------------------------------------------------
368 class SoCLocHandler(Module
):
369 # Creation -------------------------------------------------------------------------------------
370 def __init__(self
, name
, n_locs
):
375 # Add ------------------------------------------------------------------------------------------
376 def add(self
, name
, n
=None, use_loc_if_exists
=False):
378 if not (use_loc_if_exists
and name
in self
.locs
.keys()):
379 if name
in self
.locs
.keys():
380 self
.logger
.error("{} {} name {}.".format(
381 colorer(name
), self
.name
, colorer("already used", color
="red")))
382 self
.logger
.error(self
)
384 if n
in self
.locs
.values():
385 self
.logger
.error("{} {} Location {}.".format(
386 colorer(n
), self
.name
, colorer("already used", color
="red")))
387 self
.logger
.error(self
)
394 self
.logger
.error("{} {} Location should be {}.".format(
397 colorer("positive", color
="red")))
400 self
.logger
.error("{} {} Location {} than maximum: {}.".format(
403 colorer("higher", color
="red"),
404 colorer(self
.n_locs
)))
409 self
.logger
.info("{} {} {} at Location {}.".format(
410 colorer(name
, color
="underline"),
412 colorer("allocated" if allocated
else "added", color
="cyan" if allocated
else "green"),
415 # Alloc ----------------------------------------------------------------------------------------
416 def alloc(self
, name
):
417 for n
in range(self
.n_locs
):
418 if n
not in self
.locs
.values():
420 self
.logger
.error("Not enough Locations.")
421 self
.logger
.error(self
)
424 # Str ------------------------------------------------------------------------------------------
426 r
= "{} Locations: ({})\n".format(self
.name
, len(self
.locs
.keys())) if len(self
.locs
.keys()) else ""
427 locs
= {k
: v
for k
, v
in sorted(self
.locs
.items(), key
=lambda item
: item
[1])}
429 for name
in locs
.keys():
430 if len(name
) > length
: length
= len(name
)
431 for name
in locs
.keys():
432 r
+= "- {}{}: {}\n".format(colorer(name
, color
="underline"), " "*(length
+ 1 - len(name
)), colorer(self
.locs
[name
]))
435 # SoCCSRHandler ------------------------------------------------------------------------------------
437 class SoCCSRHandler(SoCLocHandler
):
438 supported_data_width
= [8, 32]
439 supported_address_width
= [14+i
for i
in range(4)]
440 supported_alignment
= [32, 64]
441 supported_paging
= [0x800*2**i
for i
in range(4)]
443 # Creation -------------------------------------------------------------------------------------
444 def __init__(self
, data_width
=32, address_width
=14, alignment
=32, paging
=0x800, reserved_csrs
={}):
445 SoCLocHandler
.__init
__(self
, "CSR", n_locs
=alignment
//8*(2**address_width
)//paging
)
446 self
.logger
= logging
.getLogger("SoCCSRHandler")
447 self
.logger
.info("Creating CSR Handler...")
450 if data_width
not in self
.supported_data_width
:
451 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
452 colorer("Data Width", color
="red"),
454 colorer(", ".join(str(x
) for x
in self
.supported_data_width
))))
457 # Check Address Width
458 if address_width
not in self
.supported_address_width
:
459 self
.logger
.error("Unsupported {} {} supporteds: {:s}".format(
460 colorer("Address Width", color
="red"),
461 colorer(address_width
),
462 colorer(", ".join(str(x
) for x
in self
.supported_address_width
))))
466 if alignment
not in self
.supported_alignment
:
467 self
.logger
.error("Unsupported {}: {} supporteds: {:s}".format(
468 colorer("Alignment", color
="red"),
470 colorer(", ".join(str(x
) for x
in self
.supported_alignment
))))
472 if data_width
> alignment
:
473 self
.logger
.error("Alignment ({}) {} Data Width ({})".format(
475 colorer("should be >=", color
="red"),
476 colorer(data_width
)))
480 if paging
not in self
.supported_paging
:
481 self
.logger
.error("Unsupported {} 0x{}, supporteds: {:s}".format(
482 colorer("Paging", color
="red"),
483 colorer("{:x}".format(paging
)),
484 colorer(", ".join("0x{:x}".format(x
) for x
in self
.supported_paging
))))
488 self
.data_width
= data_width
489 self
.address_width
= address_width
490 self
.alignment
= alignment
494 self
.logger
.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).".format(
495 colorer(self
.data_width
),
496 colorer(self
.alignment
),
497 colorer(2**self
.address_width
/2**10),
498 colorer(self
.paging
),
499 colorer(self
.n_locs
)))
501 # Adding reserved CSRs
502 self
.logger
.info("Adding {} CSRs...".format(colorer("reserved", color
="cyan")))
503 for name
, n
in reserved_csrs
.items():
506 self
.logger
.info("CSR Handler {}.".format(colorer("created", color
="green")))
508 # Update CSR Alignment ----------------------------------------------------------------------------
509 def update_alignment(self
, alignment
):
511 if alignment
not in self
.supported_alignment
:
512 self
.logger
.error("Unsupported {}: {} supporteds: {:s}".format(
513 colorer("Alignment", color
="red"),
515 colorer(", ".join(str(x
) for x
in self
.supported_alignment
))))
517 self
.logger
.info("Alignment {} from {}-bit to {}-bit.".format(
518 colorer("updated", color
="cyan"),
519 colorer(self
.alignment
),
521 self
.alignment
= alignment
523 # Add Master -----------------------------------------------------------------------------------
524 def add_master(self
, name
=None, master
=None):
526 name
= "master{:d}".format(len(self
.masters
))
527 if name
in self
.masters
.keys():
528 self
.logger
.error("{} {} as CSR Master:".format(
530 colorer("already declared", color
="red")))
531 self
.logger
.error(self
)
533 if master
.data_width
!= self
.data_width
:
534 self
.logger
.error("{} Master/Handler Data Width {} ({} vs {}).".format(
536 colorer("missmatch", color
="red"),
537 colorer(master
.data_width
),
538 colorer(self
.data_width
)))
540 self
.masters
[name
] = master
541 self
.logger
.info("{} {} as CSR Master.".format(
542 colorer(name
, color
="underline"),
543 colorer("added", color
="green")))
545 # Add Region -----------------------------------------------------------------------------------
546 def add_region(self
, name
, region
):
548 self
.regions
[name
] = region
550 # Address map ----------------------------------------------------------------------------------
551 def address_map(self
, name
, memory
):
552 if memory
is not None:
553 name
= name
+ "_" + memory
.name_override
554 if self
.locs
.get(name
, None) is None:
555 self
.logger
.error("CSR {} {}.".format(
557 colorer("not found", color
="red")))
558 self
.logger
.error(self
)
560 return self
.locs
[name
]
562 # Str ------------------------------------------------------------------------------------------
564 r
= "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
565 colorer(self
.data_width
),
566 colorer(self
.alignment
),
567 colorer(2**self
.address_width
/2**10),
568 colorer(self
.paging
),
569 colorer(self
.n_locs
))
570 r
+= SoCLocHandler
.__str
__(self
)
574 # SoCIRQHandler ------------------------------------------------------------------------------------
576 class SoCIRQHandler(SoCLocHandler
):
577 # Creation -------------------------------------------------------------------------------------
578 def __init__(self
, n_irqs
=32, reserved_irqs
={}):
579 SoCLocHandler
.__init
__(self
, "IRQ", n_locs
=n_irqs
)
580 self
.logger
= logging
.getLogger("SoCIRQHandler")
581 self
.logger
.info("Creating IRQ Handler...")
585 self
.logger
.error("Unsupported IRQs number: {} supporteds: {:s}".format(
586 colorer(n
, color
="red"), colorer("Up to 32", color
="green")))
590 self
.logger
.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs
)))
592 # Adding reserved IRQs
593 self
.logger
.info("Adding {} IRQs...".format(colorer("reserved", color
="cyan")))
594 for name
, n
in reserved_irqs
.items():
597 self
.logger
.info("IRQ Handler {}.".format(colorer("created", color
="green")))
599 # Str ------------------------------------------------------------------------------------------
601 r
="IRQ Handler (up to {} Locations).\n".format(colorer(self
.n_locs
))
602 r
+= SoCLocHandler
.__str
__(self
)
606 # SoCController ------------------------------------------------------------------------------------
608 class SoCController(Module
, AutoCSR
):
610 self
._reset
= CSRStorage(1, description
="""
611 Write a ``1`` to this register to reset the SoC.""")
612 self
._scratch
= CSRStorage(32, reset
=0x12345678, description
="""
613 Use this register as a scratch space to verify that software read/write accesses
614 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
615 can be used to verify endianness.""")
616 self
._bus
_errors
= CSRStatus(32, description
="""
617 Total number of Wishbone bus errors (timeouts) since last reset.""")
622 self
.reset
= Signal()
623 self
.comb
+= self
.reset
.eq(self
._reset
.re
)
626 self
.bus_error
= Signal()
627 bus_errors
= Signal(32)
629 If(bus_errors
!= (2**len(bus_errors
)-1),
630 If(self
.bus_error
, bus_errors
.eq(bus_errors
+ 1))
632 self
.comb
+= self
._bus
_errors
.status
.eq(bus_errors
)
634 # SoC ----------------------------------------------------------------------------------------------
638 def __init__(self
, platform
, sys_clk_freq
,
640 bus_standard
= "wishbone",
642 bus_address_width
= 32,
644 bus_reserved_regions
= {},
647 csr_address_width
= 14,
650 csr_reserved_csrs
= {},
653 irq_reserved_irqs
= {},
656 self
.logger
= logging
.getLogger("SoC")
657 self
.logger
.info(colorer(" __ _ __ _ __ ", color
="bright"))
658 self
.logger
.info(colorer(" / / (_) /____ | |/_/ ", color
="bright"))
659 self
.logger
.info(colorer(" / /__/ / __/ -_)> < ", color
="bright"))
660 self
.logger
.info(colorer(" /____/_/\\__/\\__/_/|_| ", color
="bright"))
661 self
.logger
.info(colorer(" Build your hardware, easily!", color
="bright"))
663 self
.logger
.info(colorer("-"*80, color
="bright"))
664 self
.logger
.info(colorer("Creating SoC... ({})".format(build_time())))
665 self
.logger
.info(colorer("-"*80, color
="bright"))
666 self
.logger
.info("FPGA device : {}.".format(platform
.device
))
667 self
.logger
.info("System clock: {:3.2f}MHz.".format(sys_clk_freq
/1e6
))
669 # SoC attributes ---------------------------------------------------------------------------
670 self
.platform
= platform
671 self
.sys_clk_freq
= sys_clk_freq
673 self
.csr_regions
= {}
675 # SoC Bus Handler --------------------------------------------------------------------------
676 self
.submodules
.bus
= SoCBusHandler(
677 standard
= bus_standard
,
678 data_width
= bus_data_width
,
679 address_width
= bus_address_width
,
680 timeout
= bus_timeout
,
681 reserved_regions
= bus_reserved_regions
,
684 # SoC Bus Handler --------------------------------------------------------------------------
685 self
.submodules
.csr
= SoCCSRHandler(
686 data_width
= csr_data_width
,
687 address_width
= csr_address_width
,
688 alignment
= csr_alignment
,
690 reserved_csrs
= csr_reserved_csrs
,
693 # SoC IRQ Handler --------------------------------------------------------------------------
694 self
.submodules
.irq
= SoCIRQHandler(
696 reserved_irqs
= irq_reserved_irqs
699 self
.logger
.info(colorer("-"*80, color
="bright"))
700 self
.logger
.info(colorer("Initial SoC:"))
701 self
.logger
.info(colorer("-"*80, color
="bright"))
702 self
.logger
.info(self
.bus
)
703 self
.logger
.info(self
.csr
)
704 self
.logger
.info(self
.irq
)
705 self
.logger
.info(colorer("-"*80, color
="bright"))
707 self
.add_config("CLOCK_FREQUENCY", int(sys_clk_freq
))
709 # SoC Helpers ----------------------------------------------------------------------------------
710 def check_if_exists(self
, name
):
711 if hasattr(self
, name
):
712 self
.logger
.error("{} SubModule already {}.".format(
714 colorer("declared", color
="red")))
717 def add_constant(self
, name
, value
=None):
719 if name
in self
.constants
.keys():
720 self
.logger
.error("{} Constant already {}.".format(
722 colorer("declared", color
="red")))
724 self
.constants
[name
] = SoCConstant(value
)
726 def add_config(self
, name
, value
):
727 name
= "CONFIG_" + name
728 if isinstance(value
, str):
729 self
.add_constant(name
+ "_" + value
)
731 self
.add_constant(name
, value
)
733 # SoC Main Components --------------------------------------------------------------------------
734 def add_controller(self
, name
="ctrl"):
735 self
.check_if_exists(name
)
736 setattr(self
.submodules
, name
, SoCController())
737 self
.csr
.add(name
, use_loc_if_exists
=True)
739 def add_ram(self
, name
, origin
, size
, contents
=[], mode
="rw"):
740 ram_bus
= wishbone
.Interface(data_width
=self
.bus
.data_width
)
741 ram
= wishbone
.SRAM(size
, bus
=ram_bus
, init
=contents
, read_only
=(mode
== "r"))
742 self
.bus
.add_slave(name
, ram
.bus
, SoCRegion(origin
=origin
, size
=size
, mode
=mode
))
743 self
.check_if_exists(name
)
744 self
.logger
.info("RAM {} {} {}.".format(
746 colorer("added", color
="green"),
747 self
.bus
.regions
[name
]))
748 setattr(self
.submodules
, name
, ram
)
750 def add_rom(self
, name
, origin
, size
, contents
=[]):
751 self
.add_ram(name
, origin
, size
, contents
, mode
="r")
753 def add_csr_bridge(self
, origin
):
754 self
.submodules
.csr_bridge
= wishbone2csr
.WB2CSR(
755 bus_csr
= csr_bus
.Interface(
756 address_width
= self
.csr
.address_width
,
757 data_width
= self
.csr
.data_width
))
758 csr_size
= 2**(self
.csr
.address_width
+ 2)
759 csr_region
= SoCRegion(origin
=origin
, size
=csr_size
, cached
=False)
760 self
.bus
.add_slave("csr", self
.csr_bridge
.wishbone
, csr_region
)
761 self
.csr
.add_master(name
="bridge", master
=self
.csr_bridge
.csr
)
762 self
.add_config("CSR_DATA_WIDTH", self
.csr
.data_width
)
763 self
.add_config("CSR_ALIGNMENT", self
.csr
.alignment
)
765 def add_cpu(self
, name
="vexriscv", variant
="standard", reset_address
=None):
766 if name
not in cpu
.CPUS
.keys():
767 self
.logger
.error("{} CPU {}, supporteds: {}".format(
769 colorer("not supported", color
="red"),
770 colorer(", ".join(cpu
.CPUS
.keys()))))
773 self
.submodules
.cpu
= cpu
.CPUS
[name
](self
.platform
, variant
)
774 # Update SoC with CPU constraints
775 for n
, (origin
, size
) in enumerate(self
.cpu
.io_regions
.items()):
776 self
.bus
.add_region("io{}".format(n
), SoCIORegion(origin
=origin
, size
=size
, cached
=False))
777 self
.mem_map
.update(self
.cpu
.mem_map
) # FIXME
778 self
.csr
.update_alignment(self
.cpu
.data_width
)
779 # Add Bus Masters/CSR/IRQs
780 if not isinstance(self
.cpu
, cpu
.CPUNone
):
781 if reset_address
is None:
782 reset_address
= self
.mem_map
["rom"]
783 self
.cpu
.set_reset_address(reset_address
)
784 for n
, cpu_bus
in enumerate(self
.cpu
.buses
):
785 self
.bus
.add_master(name
="cpu_bus{}".format(n
), master
=cpu_bus
)
786 self
.csr
.add("cpu", use_loc_if_exists
=True)
787 for name
, loc
in self
.cpu
.interrupts
.items():
788 self
.irq
.add(name
, loc
)
789 if hasattr(self
, "ctrl"):
790 self
.comb
+= self
.cpu
.reset
.eq(self
.ctrl
.reset
)
791 self
.add_config("CPU_RESET_ADDR", reset_address
)
793 self
.add_config("CPU_TYPE", str(name
))
794 self
.add_config("CPU_VARIANT", str(variant
.split('+')[0]))
796 def add_timer(self
, name
="timer0"):
797 self
.check_if_exists(name
)
798 setattr(self
.submodules
, name
, Timer())
799 self
.csr
.add(name
, use_loc_if_exists
=True)
800 self
.irq
.add(name
, use_loc_if_exists
=True)
802 # SoC finalization -----------------------------------------------------------------------------
803 def do_finalize(self
):
804 self
.logger
.info(colorer("-"*80, color
="bright"))
805 self
.logger
.info(colorer("Finalized SoC:"))
806 self
.logger
.info(colorer("-"*80, color
="bright"))
807 self
.logger
.info(self
.bus
)
808 self
.logger
.info(self
.csr
)
809 self
.logger
.info(self
.irq
)
810 self
.logger
.info(colorer("-"*80, color
="bright"))
812 # SoC Bus Interconnect ---------------------------------------------------------------------
813 bus_masters
= self
.bus
.masters
.values()
814 bus_slaves
= [(self
.bus
.regions
[n
].decoder(self
.bus
), s
) for n
, s
in self
.bus
.slaves
.items()]
815 if len(bus_masters
) and len(bus_slaves
):
816 self
.submodules
.bus_interconnect
= wishbone
.InterconnectShared(
817 masters
= bus_masters
,
820 timeout_cycles
= self
.bus
.timeout
)
821 if hasattr(self
, "ctrl") and self
.bus
.timeout
is not None:
822 self
.comb
+= self
.ctrl
.bus_error
.eq(self
.bus_interconnect
.timeout
.error
)
824 # SoC CSR Interconnect ---------------------------------------------------------------------
825 self
.submodules
.csr_bankarray
= csr_bus
.CSRBankArray(self
,
826 address_map
= self
.csr
.address_map
,
827 data_width
= self
.csr
.data_width
,
828 address_width
= self
.csr
.address_width
,
829 alignment
= self
.csr
.alignment
,
830 paging
= self
.csr
.paging
,
831 soc_bus_data_width
= self
.bus
.data_width
)
832 if len(self
.csr
.masters
):
833 self
.submodules
.csr_interconnect
= csr_bus
.InterconnectShared(
834 masters
= list(self
.csr
.masters
.values()),
835 slaves
= self
.csr_bankarray
.get_buses())
838 for name
, csrs
, mapaddr
, rmap
in self
.csr_bankarray
.banks
:
839 self
.csr
.add_region(name
, SoCCSRRegion(
840 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
841 busword
= self
.csr
.data_width
,
845 for name
, memory
, mapaddr
, mmap
in self
.csr_bankarray
.srams
:
846 self
.csr
.add_region(name
+ "_" + memory
.name_override
, SoCCSRRegion(
847 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
848 busword
= self
.csr
.data_width
,
851 # Sort CSR regions by origin
852 self
.csr
.regions
= {k
: v
for k
, v
in sorted(self
.csr
.regions
.items(), key
=lambda item
: item
[1].origin
)}
854 # Add CSRs / Config items to constants
855 for name
, constant
in self
.csr_bankarray
.constants
:
856 self
.add_constant(name
+ "_" + constant
.name
, constant
.value
.value
)
858 # SoC CPU Check ----------------------------------------------------------------------------
859 if not isinstance(self
.cpu
, cpu
.CPUNone
):
860 if "sram" not in self
.bus
.regions
.keys():
861 self
.logger
.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
863 colorer("defined", color
="red")))
864 self
.logger
.error(self
.bus
)
866 cpu_reset_address_valid
= False
867 for name
, container
in self
.bus
.regions
.items():
868 if self
.bus
.check_region_is_in(
869 region
= SoCRegion(origin
=self
.cpu
.reset_address
, size
=self
.bus
.data_width
//8),
870 container
= container
):
871 cpu_reset_address_valid
= True
873 self
.cpu
.use_rom
= True
874 if not cpu_reset_address_valid
:
875 self
.logger
.error("CPU needs {} to be in a {} Region.".format(
876 colorer("reset address 0x{:08x}".format(self
.cpu
.reset_address
)),
877 colorer("defined", color
="red")))
878 self
.logger
.error(self
.bus
)
881 # SoC IRQ Interconnect ---------------------------------------------------------------------
882 if hasattr(self
, "cpu"):
883 if hasattr(self
.cpu
, "interrupt"):
884 for name
, loc
in sorted(self
.irq
.locs
.items()):
885 if name
in self
.cpu
.interrupts
.keys():
887 if hasattr(self
, name
):
888 module
= getattr(self
, name
)
889 if not hasattr(module
, "ev"):
890 self
.logger
.error("EventManager {} in {} SubModule.".format(
891 colorer("not found", color
="red"),
894 self
.comb
+= self
.cpu
.interrupt
[loc
].eq(module
.ev
.irq
)
895 self
.add_constant(name
+ "_INTERRUPT", loc
)
897 # SoC build ------------------------------------------------------------------------------------
898 def build(self
, *args
, **kwargs
):
899 return self
.platform
.build(self
, *args
, **kwargs
)
901 # LiteXSoC -----------------------------------------------------------------------------------------
904 # Add Identifier -------------------------------------------------------------------------------
905 def add_identifier(self
, name
="identifier", identifier
="LiteX SoC", with_build_time
=True):
906 self
.check_if_exists(name
)
908 identifier
+= " " + build_time()
909 setattr(self
.submodules
, name
, Identifier(identifier
))
910 self
.csr
.add(name
+ "_mem", use_loc_if_exists
=True)
912 # Add UART -------------------------------------------------------------------------------------
913 def add_uart(self
, name
, baudrate
=115200, fifo_depth
=16):
914 from litex
.soc
.cores
import uart
915 if name
in ["stub", "stream"]:
916 self
.submodules
.uart
= uart
.UART(tx_fifo_depth
=0, rx_fifo_depth
=0)
918 self
.comb
+= self
.uart
.sink
.ready
.eq(1)
919 elif name
== "bridge":
920 self
.submodules
.uart
= uart
.UARTWishboneBridge(
921 pads
= self
.platform
.request("serial"),
922 clk_freq
= self
.sys_clk_freq
,
924 self
.bus
.add_master(name
="uart_bridge", master
=self
.uart
.wishbone
)
925 elif name
== "crossover":
926 self
.submodules
.uart
= uart
.UARTCrossover()
928 if name
== "jtag_atlantic":
929 from litex
.soc
.cores
.jtag
import JTAGAtlantic
930 self
.submodules
.uart_phy
= JTAGAtlantic()
931 elif name
== "jtag_uart":
932 from litex
.soc
.cores
.jtag
import JTAGPHY
933 self
.submodules
.uart_phy
= JTAGPHY(device
=self
.platform
.device
)
935 self
.submodules
.uart_phy
= uart
.UARTPHY(
936 pads
= self
.platform
.request(name
),
937 clk_freq
= self
.sys_clk_freq
,
939 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
,
940 tx_fifo_depth
= fifo_depth
,
941 rx_fifo_depth
= fifo_depth
))
942 self
.csr
.add("uart_phy", use_loc_if_exists
=True)
943 self
.csr
.add("uart", use_loc_if_exists
=True)
944 self
.irq
.add("uart", use_loc_if_exists
=True)
946 # Add SDRAM ------------------------------------------------------------------------------------
947 def add_sdram(self
, name
, phy
, module
, origin
, size
=None,
948 l2_cache_size
= 8192,
949 l2_cache_min_data_width
= 128,
950 l2_cache_reverse
= True,
951 l2_cache_full_memory_we
= True,
955 from litedram
.core
import LiteDRAMCore
956 from litedram
.frontend
.wishbone
import LiteDRAMWishbone2Native
957 from litedram
.frontend
.axi
import LiteDRAMAXI2Native
960 self
.submodules
.sdram
= LiteDRAMCore(
962 geom_settings
= module
.geom_settings
,
963 timing_settings
= module
.timing_settings
,
964 clk_freq
= self
.sys_clk_freq
,
966 self
.csr
.add("sdram")
969 port
= self
.sdram
.crossbar
.get_port()
970 port
.data_width
= 2**int(log2(port
.data_width
)) # Round to nearest power of 2
973 sdram_size
= 2**(module
.geom_settings
.bankbits
+
974 module
.geom_settings
.rowbits
+
975 module
.geom_settings
.colbits
)*phy
.settings
.databits
//8
977 sdram_size
= min(sdram_size
, size
)
978 self
.bus
.add_region("main_ram", SoCRegion(origin
=origin
, size
=sdram_size
))
980 # SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
981 if self
.cpu
.name
== "rocket":
982 # Rocket has its own I/D L1 cache: connect directly to LiteDRAM when possible.
983 if port
.data_width
== self
.cpu
.mem_axi
.data_width
:
984 self
.logger
.info("Matching AXI MEM data width ({})\n".format(port
.data_width
))
985 self
.submodules
+= LiteDRAMAXI2Native(
986 axi
= self
.cpu
.mem_axi
,
988 base_address
= self
.bus
.regions
["main_ram"].origin
)
990 self
.logger
.info("Converting MEM data width: {} to {} via Wishbone".format(
992 self
.cpu
.mem_axi
.data_width
))
993 # FIXME: replace WB data-width converter with native AXI converter!!!
994 mem_wb
= wishbone
.Interface(
995 data_width
= self
.cpu
.mem_axi
.data_width
,
996 adr_width
= 32-log2_int(self
.cpu
.mem_axi
.data_width
//8))
997 # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
998 mem_a2w
= ResetInserter()(axi
.AXI2Wishbone(
999 axi
= self
.cpu
.mem_axi
,
1002 self
.comb
+= mem_a2w
.reset
.eq(ResetSignal() | self
.cpu
.reset
)
1003 self
.submodules
+= mem_a2w
1004 litedram_wb
= wishbone
.Interface(port
.data_width
)
1005 self
.submodules
+= LiteDRAMWishbone2Native(
1006 wishbone
= litedram_wb
,
1008 base_address
= origin
)
1009 self
.submodules
+= wishbone
.Converter(mem_wb
, litedram_wb
)
1010 elif self
.with_wishbone
:
1011 # Wishbone Slave SDRAM interface
1012 wb_sdram
= wishbone
.Interface()
1013 self
.bus
.add_slave("main_ram", wb_sdram
)
1016 if l2_cache_size
!= 0:
1017 # Insert L2 cache inbetween Wishbone bus and LiteDRAM
1018 l2_cache_size
= max(l2_cache_size
, int(2*port
.data_width
/8)) # Use minimal size if lower
1019 l2_cache_size
= 2**int(log2(l2_cache_size
)) # Round to nearest power of 2
1020 l2_cache_data_width
= max(port
.data_width
, l2_cache_min_data_width
)
1021 l2_cache
= wishbone
.Cache(
1022 cachesize
= l2_cache_size
//4,
1024 slave
= wishbone
.Interface(l2_cache_data_width
),
1025 reverse
= l2_cache_reverse
)
1026 if l2_cache_full_memory_we
:
1027 l2_cache
= FullMemoryWE()(l2_cache
)
1028 self
.submodules
.l2_cache
= l2_cache
1029 litedram_wb
= self
.l2_cache
.slave
1031 litedram_wb
= wishbone
.Interface(port
.data_width
)
1032 self
.submodules
+= wishbone
.Converter(wb_sdram
, litedram_wb
)
1033 self
.add_config("L2_SIZE", l2_cache_size
)
1035 # Wishbone Slave <--> LiteDRAM bridge
1036 self
.submodules
.wishbone_bridge
= LiteDRAMWishbone2Native(litedram_wb
, port
,
1037 base_address
= self
.bus
.regions
["main_ram"].origin
)
1039 # Add Ethernet ---------------------------------------------------------------------------------
1040 def add_ethernet(self
, phy
):
1042 from liteeth
.mac
import LiteEthMAC
1044 self
.submodules
.ethmac
= LiteEthMAC(
1047 interface
= "wishbone",
1048 endianness
= self
.cpu
.endianness
)
1049 ethmac_region
= SoCRegion(origin
=self
.mem_map
.get("ethmac", None), size
=0x2000, cached
=False)
1050 self
.bus
.add_slave(name
="ethmac", slave
=self
.ethmac
.bus
, region
=ethmac_region
)
1051 self
.add_csr("ethmac")
1052 self
.add_interrupt("ethmac")
1053 # Timing constraints
1054 if hasattr(phy
, "crg"):
1055 eth_rx_clk
= phy
.crg
.cd_eth_rx
.clk
1056 eth_tx_clk
= phy
.crg
.cd_eth_tx
.clk
1058 eth_rx_clk
= phy
.cd_eth_rx
.clk
1059 eth_tx_clk
= phy
.cd_eth_tx
.clk
1060 self
.platform
.add_period_constraint(eth_rx_clk
, 1e9
/phy
.rx_clk_freq
)
1061 self
.platform
.add_period_constraint(eth_tx_clk
, 1e9
/phy
.tx_clk_freq
)
1062 self
.platform
.add_false_path_constraints(
1063 self
.crg
.cd_sys
.clk
,
1067 # Add SPI Flash --------------------------------------------------------------------------------
1068 def add_spi_flash(self
, name
="spiflash", mode
="4x", dummy_cycles
=None, clk_freq
=None):
1069 assert dummy_cycles
is not None # FIXME: Get dummy_cycles from SPI Flash
1070 assert mode
in ["4x"] # FIXME: Add 1x support.
1071 if clk_freq
is None: clk_freq
= self
.clk_freq
/2 # FIXME: Get max clk_freq from SPI Flash
1072 spiflash
= SpiFlash(
1073 pads
= self
.platform
.request(name
+ mode
),
1074 dummy
= dummy_cycles
,
1075 div
= ceil(self
.clk_freq
/clk_freq
),
1076 with_bitbang
= True,
1077 endianness
= self
.cpu
.endianness
)
1078 spiflash
.add_clk_primitive(self
.platform
.device
)
1079 setattr(self
.submodules
, name
, spiflash
)
1080 self
.add_memory_region(name
, self
.mem_map
[name
], 0x1000000) # FIXME: Get size from SPI Flash
1081 self
.add_wb_slave(self
.mem_map
[name
], spiflash
.bus
)
1084 # Add SPI SDCard -------------------------------------------------------------------------------
1085 def add_spi_sdcard(self
, name
="spisdcard", clk_freq
=400e3
):
1086 pads
= self
.platform
.request(name
)
1087 if hasattr(pads
, "rst"):
1088 self
.comb
+= pads
.rst
.eq(0)
1089 spisdcard
= SPIMaster(pads
, 8, self
.sys_clk_freq
, 400e3
)
1090 setattr(self
.submodules
, name
, spisdcard
)