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>
13 from litex
.soc
.cores
import cpu
14 from litex
.soc
.cores
.identifier
import Identifier
15 from litex
.soc
.cores
.timer
import Timer
17 from litex
.soc
.interconnect
.csr
import *
18 from litex
.soc
.interconnect
import csr_bus
19 from litex
.soc
.interconnect
import wishbone
20 from litex
.soc
.interconnect
import wishbone2csr
21 from litex
.soc
.interconnect
import axi
23 from litedram
.core
import LiteDRAMCore
24 from litedram
.frontend
.wishbone
import LiteDRAMWishbone2Native
25 from litedram
.frontend
.axi
import LiteDRAMAXI2Native
28 # - replace raise with exit on logging error.
29 # - cleanup SoCCSRRegion
31 logging
.basicConfig(level
=logging
.INFO
)
33 # Helpers ------------------------------------------------------------------------------------------
34 def colorer(s
, color
="bright"):
41 "underline": "\x1b[4m"}[color
]
43 return header
+ str(s
) + trailer
45 def build_time(with_time
=True):
46 fmt
= "%Y-%m-%d %H:%M:%S" if with_time
else "%Y-%m-%d"
47 return datetime
.datetime
.fromtimestamp(time
.time()).strftime(fmt
)
49 # SoCConstant --------------------------------------------------------------------------------------
51 def SoCConstant(value
):
54 # SoCRegion ----------------------------------------------------------------------------------------
57 def __init__(self
, origin
=None, size
=None, mode
="rw", cached
=True, linker
=False):
58 self
.logger
= logging
.getLogger("SoCRegion")
65 def decoder(self
, bus
):
68 size
= 2**log2_int(size
, False)
69 if (origin
& (size
- 1)) != 0:
70 self
.logger
.error("Origin needs to be aligned on size:")
71 self
.logger
.error(self
)
73 origin
>>= int(log2(bus
.data_width
//8)) # bytes to words aligned
74 size
>>= int(log2(bus
.data_width
//8)) # bytes to words aligned
75 return lambda a
: (a
[log2_int(size
):] == (origin
>> log2_int(size
)))
79 if self
.origin
is not None:
80 r
+= "Origin: {}, ".format(colorer("0x{:08x}".format(self
.origin
)))
81 if self
.size
is not None:
82 r
+= "Size: {}, ".format(colorer("0x{:08x}".format(self
.size
)))
83 r
+= "Mode: {}, ".format(colorer(self
.mode
.upper()))
84 r
+= "Cached: {} ".format(colorer(self
.cached
))
85 r
+= "Linker: {}".format(colorer(self
.linker
))
88 class SoCIORegion(SoCRegion
): pass
90 # SoCCSRRegion -------------------------------------------------------------------------------------
93 def __init__(self
, origin
, busword
, obj
):
95 self
.busword
= busword
98 # SoCBusHandler ------------------------------------------------------------------------------------
100 class SoCBusHandler(Module
):
101 supported_standard
= ["wishbone"]
102 supported_data_width
= [32, 64]
103 supported_address_width
= [32]
105 # Creation -------------------------------------------------------------------------------------
106 def __init__(self
, standard
, data_width
=32, address_width
=32, timeout
=1e6
, reserved_regions
={}):
107 self
.logger
= logging
.getLogger("SoCBusHandler")
108 self
.logger
.info("Creating Bus Handler...")
111 if standard
not in self
.supported_standard
:
112 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
113 colorer("Bus standard", color
="red"),
115 colorer(", ".join(self
.supported_standard
))))
119 if data_width
not in self
.supported_data_width
:
120 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
121 colorer("Data Width", color
="red"),
123 colorer(", ".join(str(x
) for x
in self
.supported_data_width
))))
126 # Check Address Width
127 if address_width
not in self
.supported_address_width
:
128 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
129 colorer("Address Width", color
="red"),
131 colorer(", ".join(str(x
) for x
in self
.supported_address_width
))))
135 self
.standard
= standard
136 self
.data_width
= data_width
137 self
.address_width
= address_width
142 self
.timeout
= timeout
143 self
.logger
.info("{}-bit {} Bus, {}GiB Address Space.".format(
144 colorer(data_width
), colorer(standard
), colorer(2**address_width
/2**30)))
146 # Adding reserved regions
147 self
.logger
.info("Adding {} Bus Regions...".format(colorer("reserved", color
="cyan")))
148 for name
, region
in reserved_regions
.items():
149 if isinstance(region
, int):
150 region
= SoCRegion(origin
=region
, size
=0x1000000)
151 self
.add_region(name
, region
)
153 self
.logger
.info("Bus Handler {}.".format(colorer("created", color
="green")))
155 # Add/Allog/Check Regions ----------------------------------------------------------------------
156 def add_region(self
, name
, region
):
158 if name
in self
.regions
.keys() or name
in self
.io_regions
.keys():
159 self
.logger
.error("{} already declared as Region:".format(colorer(name
, color
="red")))
160 self
.logger
.error(self
)
162 # Check if SoCIORegion
163 if isinstance(region
, SoCIORegion
):
164 self
.io_regions
[name
] = region
165 overlap
= self
.check_regions_overlap(self
.io_regions
)
166 if overlap
is not None:
167 self
.logger
.error("IO Region {} between {} and {}:".format(
168 colorer("overlap", color
="red"),
170 colorer(overlap
[1])))
171 self
.logger
.error(str(self
.io_regions
[overlap
[0]]))
172 self
.logger
.error(str(self
.io_regions
[overlap
[1]]))
174 self
.logger
.info("{} Region {} at {}.".format(
175 colorer(name
, color
="underline"),
176 colorer("added", color
="green"),
179 elif isinstance(region
, SoCRegion
):
180 # If no origin specified, allocate region.
181 if region
.origin
is None:
183 region
= self
.alloc_region(name
, region
.size
, region
.cached
)
184 self
.regions
[name
] = region
185 # Else add region and check for overlaps.
187 if not region
.cached
:
188 if not self
.check_region_is_io(region
):
189 self
.logger
.error("{} Region {}: {}.".format(
191 colorer("not in IO region", color
="red"),
193 self
.logger
.error(self
)
195 self
.regions
[name
] = region
196 overlap
= self
.check_regions_overlap(self
.regions
)
197 if overlap
is not None:
198 self
.logger
.error("Region {} between {} and {}:".format(
199 colorer("overlap", color
="red"),
201 colorer(overlap
[1])))
202 self
.logger
.error(str(self
.regions
[overlap
[0]]))
203 self
.logger
.error(str(self
.regions
[overlap
[1]]))
205 self
.logger
.info("{} Region {} at {}.".format(
206 colorer(name
, color
="underline"),
207 colorer("allocated" if allocated
else "added", color
="cyan" if allocated
else "green"),
210 self
.logger
.error("{} is not a supported Region.".format(colorer(name
, color
="red")))
213 def alloc_region(self
, name
, size
, cached
=True):
214 self
.logger
.info("Allocating {} Region of size {}...".format(
215 colorer("Cached" if cached
else "IO"),
216 colorer("0x{:08x}".format(size
))))
218 # Limit Search Regions
220 search_regions
= self
.io_regions
222 search_regions
= {"main": SoCRegion(origin
=0x00000000, size
=2**self
.address_width
-1)}
224 # Iterate on Search_Regions to find a Candidate
225 for _
, search_region
in search_regions
.items():
226 origin
= search_region
.origin
227 while (origin
+ size
) < (search_region
.origin
+ search_region
.size
):
228 # Create a Candicate.
229 candidate
= SoCRegion(origin
=origin
, size
=size
, cached
=cached
)
231 # Check Candidate does not overlap with allocated existing regions
232 for _
, allocated
in self
.regions
.items():
233 if self
.check_regions_overlap({"0": allocated
, "1": candidate
}) is not None:
234 origin
= allocated
.origin
+ allocated
.size
238 # If no overlap, the Candidate is selected
241 self
.logger
.error("Not enough Address Space to allocate Region.")
244 def check_regions_overlap(self
, regions
, check_linker
=False):
246 while i
< len(regions
):
247 n0
= list(regions
.keys())[i
]
249 for n1
in list(regions
.keys())[i
+1:]:
251 if r0
.linker
or r1
.linker
:
254 if r0
.origin
>= (r1
.origin
+ r1
.size
):
256 if r1
.origin
>= (r0
.origin
+ r0
.size
):
262 def check_region_is_in(self
, region
, container
):
264 if not (region
.origin
>= container
.origin
):
266 if not ((region
.origin
+ region
.size
) < (container
.origin
+ container
.size
)):
270 def check_region_is_io(self
, region
):
272 for _
, io_region
in self
.io_regions
.items():
273 if self
.check_region_is_in(region
, io_region
):
277 # Add Master/Slave -----------------------------------------------------------------------------
278 def add_adapter(self
, name
, interface
):
279 if interface
.data_width
!= self
.data_width
:
280 self
.logger
.info("{} Bus {} from {}-bit to {}-bit.".format(
282 colorer("converted", color
="cyan"),
283 colorer(interface
.data_width
),
284 colorer(self
.data_width
)))
285 new_interface
= wishbone
.Interface(data_width
=self
.data_width
)
286 self
.submodules
+= wishbone
.Converter(interface
, new_interface
)
291 def add_master(self
, name
=None, master
=None):
293 name
= "master{:d}".format(len(self
.masters
))
294 if name
in self
.masters
.keys():
295 self
.logger
.error("{} {} as Bus Master:".format(
297 colorer("already declared", color
="red")))
298 self
.logger
.error(self
)
300 master
= self
.add_adapter(name
, master
)
301 self
.masters
[name
] = master
302 self
.logger
.info("{} {} as Bus Master.".format(
303 colorer(name
, color
="underline"),
304 colorer("added", color
="green")))
306 def add_slave(self
, name
=None, slave
=None, region
=None):
307 no_name
= name
is None
308 no_region
= region
is None
309 if no_name
and no_region
:
310 self
.logger
.error("Please {} {} or/and {} of Bus Slave.".format(
311 colorer("specify", color
="red"),
316 name
= "slave{:d}".format(len(self
.slaves
))
318 region
= self
.regions
.get(name
, None)
320 self
.logger
.error("{} Region {}.".format(
322 colorer("not found", color
="red")))
325 self
.add_region(name
, region
)
326 if name
in self
.slaves
.keys():
327 self
.logger
.error("{} {} as Bus Slave:".format(
329 colorer("already declared", color
="red")))
330 self
.logger
.error(self
)
332 slave
= self
.add_adapter(name
, slave
)
333 self
.slaves
[name
] = slave
334 self
.logger
.info("{} {} as Bus Slave.".format(
335 colorer(name
, color
="underline"),
336 colorer("added", color
="green")))
338 # Str ------------------------------------------------------------------------------------------
340 r
= "{}-bit {} Bus, {}GiB Address Space.\n".format(
341 colorer(self
.data_width
), colorer(self
.standard
), colorer(2**self
.address_width
/2**30))
342 r
+= "IO Regions: ({})\n".format(len(self
.io_regions
.keys())) if len(self
.io_regions
.keys()) else ""
343 io_regions
= {k
: v
for k
, v
in sorted(self
.io_regions
.items(), key
=lambda item
: item
[1].origin
)}
344 for name
, region
in io_regions
.items():
345 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
346 r
+= "Bus Regions: ({})\n".format(len(self
.regions
.keys())) if len(self
.regions
.keys()) else ""
347 regions
= {k
: v
for k
, v
in sorted(self
.regions
.items(), key
=lambda item
: item
[1].origin
)}
348 for name
, region
in regions
.items():
349 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
350 r
+= "Bus Masters: ({})\n".format(len(self
.masters
.keys())) if len(self
.masters
.keys()) else ""
351 for name
in self
.masters
.keys():
352 r
+= "- {}\n".format(colorer(name
, color
="underline"))
353 r
+= "Bus Slaves: ({})\n".format(len(self
.slaves
.keys())) if len(self
.slaves
.keys()) else ""
354 for name
in self
.slaves
.keys():
355 r
+= "- {}\n".format(colorer(name
, color
="underline"))
359 # SoCLocHandler --------------------------------------------------------------------------------------
361 class SoCLocHandler(Module
):
362 # Creation -------------------------------------------------------------------------------------
363 def __init__(self
, name
, n_locs
):
368 # Add ------------------------------------------------------------------------------------------
369 def add(self
, name
, n
=None, use_loc_if_exists
=False):
371 if not (use_loc_if_exists
and name
in self
.locs
.keys()):
372 if name
in self
.locs
.keys():
373 self
.logger
.error("{} {} name {}.".format(
374 colorer(name
), self
.name
, colorer("already used", color
="red")))
375 self
.logger
.error(self
)
377 if n
in self
.locs
.values():
378 self
.logger
.error("{} {} Location {}.".format(
379 colorer(n
), self
.name
, colorer("already used", color
="red")))
380 self
.logger
.error(self
)
387 self
.logger
.error("{} {} Location should be {}.".format(
390 colorer("positive", color
="red")))
393 self
.logger
.error("{} {} Location {} than maximum: {}.".format(
396 colorer("higher", color
="red"),
397 colorer(self
.n_locs
)))
402 self
.logger
.info("{} {} {} at Location {}.".format(
403 colorer(name
, color
="underline"),
405 colorer("allocated" if allocated
else "added", color
="cyan" if allocated
else "green"),
408 # Alloc ----------------------------------------------------------------------------------------
409 def alloc(self
, name
):
410 for n
in range(self
.n_locs
):
411 if n
not in self
.locs
.values():
413 self
.logger
.error("Not enough Locations.")
414 self
.logger
.error(self
)
417 # Str ------------------------------------------------------------------------------------------
419 r
= "{} Locations: ({})\n".format(self
.name
, len(self
.locs
.keys())) if len(self
.locs
.keys()) else ""
420 locs
= {k
: v
for k
, v
in sorted(self
.locs
.items(), key
=lambda item
: item
[1])}
422 for name
in locs
.keys():
423 if len(name
) > length
: length
= len(name
)
424 for name
in locs
.keys():
425 r
+= "- {}{}: {}\n".format(colorer(name
, color
="underline"), " "*(length
+ 1 - len(name
)), colorer(self
.locs
[name
]))
428 # SoCCSRHandler ------------------------------------------------------------------------------------
430 class SoCCSRHandler(SoCLocHandler
):
431 supported_data_width
= [8, 32]
432 supported_address_width
= [14+i
for i
in range(4)]
433 supported_alignment
= [32, 64]
434 supported_paging
= [0x800*2**i
for i
in range(4)]
436 # Creation -------------------------------------------------------------------------------------
437 def __init__(self
, data_width
=32, address_width
=14, alignment
=32, paging
=0x800, reserved_csrs
={}):
438 SoCLocHandler
.__init
__(self
, "CSR", n_locs
=alignment
//8*(2**address_width
)//paging
)
439 self
.logger
= logging
.getLogger("SoCCSRHandler")
440 self
.logger
.info("Creating CSR Handler...")
443 if data_width
not in self
.supported_data_width
:
444 self
.logger
.error("Unsupported {} {}, supporteds: {:s}".format(
445 colorer("Data Width", color
="red"),
447 colorer(", ".join(str(x
) for x
in self
.supported_data_width
))))
450 # Check Address Width
451 if address_width
not in self
.supported_address_width
:
452 self
.logger
.error("Unsupported {} {} supporteds: {:s}".format(
453 colorer("Address Width", color
="red"),
454 colorer(address_width
),
455 colorer(", ".join(str(x
) for x
in self
.supported_address_width
))))
459 if alignment
not in self
.supported_alignment
:
460 self
.logger
.error("Unsupported {}: {} supporteds: {:s}".format(
461 colorer("Alignment", color
="red"),
463 colorer(", ".join(str(x
) for x
in self
.supported_alignment
))))
465 if data_width
> alignment
:
466 self
.logger
.error("Alignment ({}) {} Data Width ({})".format(
468 colorer("should be >=", color
="red"),
469 colorer(data_width
)))
473 if paging
not in self
.supported_paging
:
474 self
.logger
.error("Unsupported {} 0x{}, supporteds: {:s}".format(
475 colorer("Paging", color
="red"),
476 colorer("{:x}".format(paging
)),
477 colorer(", ".join("0x{:x}".format(x
) for x
in self
.supported_paging
))))
481 self
.data_width
= data_width
482 self
.address_width
= address_width
483 self
.alignment
= alignment
487 self
.logger
.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).".format(
488 colorer(self
.data_width
),
489 colorer(self
.alignment
),
490 colorer(2**self
.address_width
/2**10),
491 colorer(self
.paging
),
492 colorer(self
.n_locs
)))
494 # Adding reserved CSRs
495 self
.logger
.info("Adding {} CSRs...".format(colorer("reserved", color
="cyan")))
496 for name
, n
in reserved_csrs
.items():
499 self
.logger
.info("CSR Handler {}.".format(colorer("created", color
="green")))
501 # Update CSR Alignment ----------------------------------------------------------------------------
502 def update_alignment(self
, alignment
):
504 if alignment
not in self
.supported_alignment
:
505 self
.logger
.error("Unsupported {}: {} supporteds: {:s}".format(
506 colorer("Alignment", color
="red"),
508 colorer(", ".join(str(x
) for x
in self
.supported_alignment
))))
510 self
.logger
.info("Alignment {} from {}-bit to {}-bit.".format(
511 colorer("updated", color
="cyan"),
512 colorer(self
.alignment
),
514 self
.alignment
= alignment
516 # Add Master -----------------------------------------------------------------------------------
517 def add_master(self
, name
=None, master
=None):
519 name
= "master{:d}".format(len(self
.masters
))
520 if name
in self
.masters
.keys():
521 self
.logger
.error("{} {} as CSR Master:".format(
523 colorer("already declared", color
="red")))
524 self
.logger
.error(self
)
526 if master
.data_width
!= self
.data_width
:
527 self
.logger
.error("{} Master/Handler Data Width {} ({} vs {}).".format(
529 colorer("missmatch", color
="red"),
530 colorer(master
.data_width
),
531 colorer(self
.data_width
)))
533 self
.masters
[name
] = master
534 self
.logger
.info("{} {} as CSR Master.".format(
535 colorer(name
, color
="underline"),
536 colorer("added", color
="green")))
538 # Add Region -----------------------------------------------------------------------------------
539 def add_region(self
, name
, region
):
541 self
.regions
[name
] = region
543 # Address map ----------------------------------------------------------------------------------
544 def address_map(self
, name
, memory
):
545 if memory
is not None:
546 name
= name
+ "_" + memory
.name_override
547 if self
.locs
.get(name
, None) is None:
548 self
.logger
.error("CSR {} {}.".format(
550 colorer("not found", color
="red")))
551 self
.logger
.error(self
)
553 return self
.locs
[name
]
555 # Str ------------------------------------------------------------------------------------------
557 r
= "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
558 colorer(self
.data_width
),
559 colorer(self
.alignment
),
560 colorer(2**self
.address_width
/2**10),
561 colorer(self
.paging
),
562 colorer(self
.n_locs
))
563 r
+= SoCLocHandler
.__str
__(self
)
567 # SoCIRQHandler ------------------------------------------------------------------------------------
569 class SoCIRQHandler(SoCLocHandler
):
570 # Creation -------------------------------------------------------------------------------------
571 def __init__(self
, n_irqs
=32, reserved_irqs
={}):
572 SoCLocHandler
.__init
__(self
, "IRQ", n_locs
=n_irqs
)
573 self
.logger
= logging
.getLogger("SoCIRQHandler")
574 self
.logger
.info("Creating IRQ Handler...")
578 self
.logger
.error("Unsupported IRQs number: {} supporteds: {:s}".format(
579 colorer(n
, color
="red"), colorer("Up to 32", color
="green")))
583 self
.logger
.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs
)))
585 # Adding reserved IRQs
586 self
.logger
.info("Adding {} IRQs...".format(colorer("reserved", color
="cyan")))
587 for name
, n
in reserved_irqs
.items():
590 self
.logger
.info("IRQ Handler {}.".format(colorer("created", color
="green")))
592 # Str ------------------------------------------------------------------------------------------
594 r
="IRQ Handler (up to {} Locations).\n".format(colorer(self
.n_locs
))
595 r
+= SoCLocHandler
.__str
__(self
)
599 # SoCController ------------------------------------------------------------------------------------
601 class SoCController(Module
, AutoCSR
):
603 self
._reset
= CSRStorage(1, description
="""
604 Write a ``1`` to this register to reset the SoC.""")
605 self
._scratch
= CSRStorage(32, reset
=0x12345678, description
="""
606 Use this register as a scratch space to verify that software read/write accesses
607 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
608 can be used to verify endianness.""")
609 self
._bus
_errors
= CSRStatus(32, description
="""
610 Total number of Wishbone bus errors (timeouts) since last reset.""")
615 self
.reset
= Signal()
616 self
.comb
+= self
.reset
.eq(self
._reset
.re
)
619 self
.bus_error
= Signal()
620 bus_errors
= Signal(32)
622 If(bus_errors
!= (2**len(bus_errors
)-1),
623 If(self
.bus_error
, bus_errors
.eq(bus_errors
+ 1))
625 self
.comb
+= self
._bus
_errors
.status
.eq(bus_errors
)
627 # SoC ----------------------------------------------------------------------------------------------
631 def __init__(self
, platform
, sys_clk_freq
,
633 bus_standard
= "wishbone",
635 bus_address_width
= 32,
637 bus_reserved_regions
= {},
640 csr_address_width
= 14,
643 csr_reserved_csrs
= {},
646 irq_reserved_irqs
= {},
649 self
.logger
= logging
.getLogger("SoC")
650 self
.logger
.info(colorer(" __ _ __ _ __ ", color
="bright"))
651 self
.logger
.info(colorer(" / / (_) /____ | |/_/ ", color
="bright"))
652 self
.logger
.info(colorer(" / /__/ / __/ -_)> < ", color
="bright"))
653 self
.logger
.info(colorer(" /____/_/\\__/\\__/_/|_| ", color
="bright"))
654 self
.logger
.info(colorer(" Build your hardware, easily!", color
="bright"))
656 self
.logger
.info(colorer("-"*80, color
="bright"))
657 self
.logger
.info(colorer("Creating SoC... ({})".format(build_time())))
658 self
.logger
.info(colorer("-"*80, color
="bright"))
660 # SoC attributes ---------------------------------------------------------------------------
661 self
.platform
= platform
662 self
.sys_clk_freq
= sys_clk_freq
664 self
.csr_regions
= {}
666 # SoC Bus Handler --------------------------------------------------------------------------
667 self
.submodules
.bus
= SoCBusHandler(
668 standard
= bus_standard
,
669 data_width
= bus_data_width
,
670 address_width
= bus_address_width
,
671 timeout
= bus_timeout
,
672 reserved_regions
= bus_reserved_regions
,
675 # SoC Bus Handler --------------------------------------------------------------------------
676 self
.submodules
.csr
= SoCCSRHandler(
677 data_width
= csr_data_width
,
678 address_width
= csr_address_width
,
679 alignment
= csr_alignment
,
681 reserved_csrs
= csr_reserved_csrs
,
684 # SoC IRQ Handler --------------------------------------------------------------------------
685 self
.submodules
.irq
= SoCIRQHandler(
687 reserved_irqs
= irq_reserved_irqs
690 self
.logger
.info(colorer("-"*80, color
="bright"))
691 self
.logger
.info(colorer("Initial SoC:"))
692 self
.logger
.info(colorer("-"*80, color
="bright"))
693 self
.logger
.info(self
.bus
)
694 self
.logger
.info(self
.csr
)
695 self
.logger
.info(self
.irq
)
696 self
.logger
.info(colorer("-"*80, color
="bright"))
698 self
.add_config("CLOCK_FREQUENCY", int(sys_clk_freq
))
700 # SoC Helpers ----------------------------------------------------------------------------------
701 def check_if_exists(self
, name
):
702 if hasattr(self
, name
):
703 self
.logger
.error("{} SubModule already {}.".format(
705 colorer("declared", color
="red")))
708 def add_constant(self
, name
, value
=None):
710 if name
in self
.constants
.keys():
711 self
.logger
.error("{} Constant already {}.".format(
713 colorer("declared", color
="red")))
715 self
.constants
[name
] = SoCConstant(value
)
717 def add_config(self
, name
, value
):
718 name
= "CONFIG_" + name
719 if isinstance(value
, str):
720 self
.add_constant(name
+ "_" + value
)
722 self
.add_constant(name
, value
)
724 # SoC Main Components --------------------------------------------------------------------------
725 def add_controller(self
, name
="ctrl"):
726 self
.check_if_exists(name
)
727 setattr(self
.submodules
, name
, SoCController())
728 self
.csr
.add(name
, use_loc_if_exists
=True)
730 def add_ram(self
, name
, origin
, size
, contents
=[], mode
="rw"):
731 ram_bus
= wishbone
.Interface(data_width
=self
.bus
.data_width
)
732 ram
= wishbone
.SRAM(size
, bus
=ram_bus
, init
=contents
, read_only
=(mode
== "r"))
733 self
.bus
.add_slave(name
, ram
.bus
, SoCRegion(origin
=origin
, size
=size
, mode
=mode
))
734 self
.check_if_exists(name
)
735 self
.logger
.info("RAM {} {} {}.".format(
737 colorer("added", color
="green"),
738 self
.bus
.regions
[name
]))
739 setattr(self
.submodules
, name
, ram
)
741 def add_rom(self
, name
, origin
, size
, contents
=[]):
742 self
.add_ram(name
, origin
, size
, contents
, mode
="r")
744 def add_csr_bridge(self
, origin
):
745 self
.submodules
.csr_bridge
= wishbone2csr
.WB2CSR(
746 bus_csr
= csr_bus
.Interface(
747 address_width
= self
.csr
.address_width
,
748 data_width
= self
.csr
.data_width
))
749 csr_size
= 2**(self
.csr
.address_width
+ 2)
750 csr_region
= SoCRegion(origin
=origin
, size
=csr_size
, cached
=False)
751 self
.bus
.add_slave("csr", self
.csr_bridge
.wishbone
, csr_region
)
752 self
.csr
.add_master(name
="bridge", master
=self
.csr_bridge
.csr
)
753 self
.add_config("CSR_DATA_WIDTH", self
.csr
.data_width
)
754 self
.add_config("CSR_ALIGNMENT", self
.csr
.alignment
)
756 def add_cpu(self
, name
="vexriscv", variant
="standard", reset_address
=None):
757 if name
not in cpu
.CPUS
.keys():
758 self
.logger
.error("{} CPU {}, supporteds: {}".format(
760 colorer("not supported", color
="red"),
761 colorer(", ".join(cpu
.CPUS
.keys()))))
764 self
.submodules
.cpu
= cpu
.CPUS
[name
](self
.platform
, variant
)
765 # Update SoC with CPU constraints
766 for n
, (origin
, size
) in enumerate(self
.cpu
.io_regions
.items()):
767 self
.bus
.add_region("io{}".format(n
), SoCIORegion(origin
=origin
, size
=size
, cached
=False))
768 self
.mem_map
.update(self
.cpu
.mem_map
) # FIXME
769 self
.csr
.update_alignment(self
.cpu
.data_width
)
770 # Add Bus Masters/CSR/IRQs
771 if not isinstance(self
.cpu
, cpu
.CPUNone
):
772 if reset_address
is None:
773 reset_address
= self
.mem_map
["rom"]
774 self
.cpu
.set_reset_address(reset_address
)
775 for n
, cpu_bus
in enumerate(self
.cpu
.buses
):
776 self
.bus
.add_master(name
="cpu_bus{}".format(n
), master
=cpu_bus
)
777 self
.csr
.add("cpu", use_loc_if_exists
=True)
778 for name
, loc
in self
.cpu
.interrupts
.items():
779 self
.irq
.add(name
, loc
)
780 if hasattr(self
, "ctrl"):
781 self
.comb
+= self
.cpu
.reset
.eq(self
.ctrl
.reset
)
782 self
.add_config("CPU_RESET_ADDR", reset_address
)
784 self
.add_config("CPU_TYPE", str(name
))
785 self
.add_config("CPU_VARIANT", str(variant
.split('+')[0]))
787 def add_timer(self
, name
="timer0"):
788 self
.check_if_exists(name
)
789 setattr(self
.submodules
, name
, Timer())
790 self
.csr
.add(name
, use_loc_if_exists
=True)
791 self
.irq
.add(name
, use_loc_if_exists
=True)
793 # SoC finalization -----------------------------------------------------------------------------
794 def do_finalize(self
):
795 self
.logger
.info(colorer("-"*80, color
="bright"))
796 self
.logger
.info(colorer("Finalized SoC:"))
797 self
.logger
.info(colorer("-"*80, color
="bright"))
798 self
.logger
.info(self
.bus
)
799 self
.logger
.info(self
.csr
)
800 self
.logger
.info(self
.irq
)
801 self
.logger
.info(colorer("-"*80, color
="bright"))
803 # SoC Bus Interconnect ---------------------------------------------------------------------
804 bus_masters
= self
.bus
.masters
.values()
805 bus_slaves
= [(self
.bus
.regions
[n
].decoder(self
.bus
), s
) for n
, s
in self
.bus
.slaves
.items()]
806 if len(bus_masters
) and len(bus_slaves
):
807 self
.submodules
.bus_interconnect
= wishbone
.InterconnectShared(
808 masters
= bus_masters
,
811 timeout_cycles
= self
.bus
.timeout
)
812 if hasattr(self
, "ctrl") and self
.bus
.timeout
is not None:
813 self
.comb
+= self
.ctrl
.bus_error
.eq(self
.bus_interconnect
.timeout
.error
)
815 # SoC CSR Interconnect ---------------------------------------------------------------------
816 self
.submodules
.csr_bankarray
= csr_bus
.CSRBankArray(self
,
817 address_map
= self
.csr
.address_map
,
818 data_width
= self
.csr
.data_width
,
819 address_width
= self
.csr
.address_width
,
820 alignment
= self
.csr
.alignment
,
821 paging
= self
.csr
.paging
,
822 soc_bus_data_width
= self
.bus
.data_width
)
823 if len(self
.csr
.masters
):
824 self
.submodules
.csr_interconnect
= csr_bus
.InterconnectShared(
825 masters
= list(self
.csr
.masters
.values()),
826 slaves
= self
.csr_bankarray
.get_buses())
829 for name
, csrs
, mapaddr
, rmap
in self
.csr_bankarray
.banks
:
830 self
.csr
.add_region(name
, SoCCSRRegion(
831 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
832 busword
= self
.csr
.data_width
,
836 for name
, memory
, mapaddr
, mmap
in self
.csr_bankarray
.srams
:
837 self
.csr
.add_region(name
+ "_" + memory
.name_override
, SoCCSRRegion(
838 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
839 busword
= self
.csr
.data_width
,
842 # Sort CSR regions by origin
843 self
.csr
.regions
= {k
: v
for k
, v
in sorted(self
.csr
.regions
.items(), key
=lambda item
: item
[1].origin
)}
845 # Add CSRs / Config items to constants
846 for name
, constant
in self
.csr_bankarray
.constants
:
847 self
.add_constant(name
+ "_" + constant
.name
, constant
.value
.value
)
849 # SoC CPU Check ----------------------------------------------------------------------------
850 if not isinstance(self
.cpu
, cpu
.CPUNone
):
851 for name
in ["rom", "sram"]:
852 if name
not in self
.bus
.regions
.keys():
853 self
.logger
.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
855 colorer("defined", color
="red")))
856 self
.logger
.error(self
.bus
)
859 # SoC IRQ Interconnect ---------------------------------------------------------------------
860 if hasattr(self
, "cpu"):
861 if hasattr(self
.cpu
, "interrupt"):
862 for name
, loc
in sorted(self
.irq
.locs
.items()):
863 if name
in self
.cpu
.interrupts
.keys():
865 if hasattr(self
, name
):
866 module
= getattr(self
, name
)
867 if not hasattr(module
, "ev"):
868 self
.logger
.error("EventManager {} in {} SubModule.".format(
869 colorer("not found", color
="red"),
872 self
.comb
+= self
.cpu
.interrupt
[loc
].eq(module
.ev
.irq
)
873 self
.add_constant(name
+ "_INTERRUPT", loc
)
875 # SoC build ------------------------------------------------------------------------------------
876 def build(self
, *args
, **kwargs
):
877 return self
.platform
.build(self
, *args
, **kwargs
)
879 # LiteXSoC -----------------------------------------------------------------------------------------
882 # Add Identifier -------------------------------------------------------------------------------
883 def add_identifier(self
, name
="identifier", identifier
="LiteX SoC", with_build_time
=True):
884 self
.check_if_exists(name
)
886 identifier
+= " " + build_time()
887 setattr(self
.submodules
, name
, Identifier(identifier
))
888 self
.csr
.add(name
+ "_mem", use_loc_if_exists
=True)
890 # Add UART -------------------------------------------------------------------------------------
891 def add_uart(self
, name
, baudrate
=115200):
892 from litex
.soc
.cores
import uart
893 if name
in ["stub", "stream"]:
894 self
.submodules
.uart
= uart
.UART()
896 self
.comb
+= self
.uart
.sink
.ready
.eq(1)
897 elif name
== "bridge":
898 self
.submodules
.uart
= uart
.UARTWishboneBridge(
899 pads
= self
.platform
.request("serial"),
900 clk_freq
= self
.sys_clk_freq
,
902 self
.bus
.add_master(name
="uart_bridge", master
=self
.uart
.wishbone
)
903 elif name
== "crossover":
904 self
.submodules
.uart
= uart
.UARTCrossover()
906 if name
== "jtag_atlantic":
907 from litex
.soc
.cores
.jtag
import JTAGAtlantic
908 self
.submodules
.uart_phy
= JTAGAtlantic()
909 elif name
== "jtag_uart":
910 from litex
.soc
.cores
.jtag
import JTAGPHY
911 self
.submodules
.uart_phy
= JTAGPHY(device
=self
.platform
.device
)
913 self
.submodules
.uart_phy
= uart
.UARTPHY(
914 pads
= self
.platform
.request(name
),
915 clk_freq
= self
.sys_clk_freq
,
917 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
))
918 self
.csr
.add("uart_phy", use_loc_if_exists
=True)
919 self
.csr
.add("uart", use_loc_if_exists
=True)
920 self
.irq
.add("uart", use_loc_if_exists
=True)
922 # Add SDRAM ------------------------------------------------------------------------------------
923 def add_sdram(self
, name
, phy
, module
, origin
, size
=None,
924 l2_cache_size
= 8192,
925 l2_cache_min_data_width
= 128,
926 l2_cache_reverse
= True,
927 l2_cache_full_memory_we
= True,
930 # LiteDRAM core ----------------------------------------------------------------------------
931 self
.submodules
.sdram
= LiteDRAMCore(
933 geom_settings
= module
.geom_settings
,
934 timing_settings
= module
.timing_settings
,
935 clk_freq
= self
.sys_clk_freq
,
937 self
.csr
.add("sdram")
939 # LiteDRAM port ----------------------------------------------------------------------------
940 port
= self
.sdram
.crossbar
.get_port()
941 port
.data_width
= 2**int(log2(port
.data_width
)) # Round to nearest power of 2
943 # SDRAM size -------------------------------------------------------------------------------
944 sdram_size
= 2**(module
.geom_settings
.bankbits
+
945 module
.geom_settings
.rowbits
+
946 module
.geom_settings
.colbits
)*phy
.settings
.databits
//8
948 sdram_size
= min(sdram_size
, size
)
949 self
.bus
.add_region("main_ram", SoCRegion(origin
=origin
, size
=sdram_size
))
951 # SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
952 if self
.cpu
.name
== "rocket":
953 # Rocket has its own I/D L1 cache: connect directly to LiteDRAM when possible.
954 if port
.data_width
== self
.cpu
.mem_axi
.data_width
:
955 self
.logger
.info("Matching AXI MEM data width ({})\n".format(port
.data_width
))
956 self
.submodules
+= LiteDRAMAXI2Native(
957 axi
= self
.cpu
.mem_axi
,
959 base_address
= self
.bus
.regions
["main_ram"].origin
)
961 self
.logger
.info("Converting MEM data width: {} to {} via Wishbone".format(
963 self
.cpu
.mem_axi
.data_width
))
964 # FIXME: replace WB data-width converter with native AXI converter!!!
965 mem_wb
= wishbone
.Interface(
966 data_width
= self
.cpu
.mem_axi
.data_width
,
967 adr_width
= 32-log2_int(self
.cpu
.mem_axi
.data_width
//8))
968 # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
969 mem_a2w
= ResetInserter()(axi
.AXI2Wishbone(
970 axi
= self
.cpu
.mem_axi
,
973 self
.comb
+= mem_a2w
.reset
.eq(ResetSignal() | self
.cpu
.reset
)
974 self
.submodules
+= mem_a2w
975 litedram_wb
= wishbone
.Interface(port
.data_width
)
976 self
.submodules
+= LiteDRAMWishbone2Native(
977 wishbone
= litedram_wb
,
979 base_address
= origin
)
980 self
.submodules
+= wishbone
.Converter(mem_wb
, litedram_wb
)
981 elif self
.with_wishbone
:
982 # Wishbone Slave SDRAM interface -------------------------------------------------------
983 wb_sdram
= wishbone
.Interface()
984 self
.bus
.add_slave("main_ram", wb_sdram
)
986 # L2 Cache -----------------------------------------------------------------------------
987 if l2_cache_size
!= 0:
988 # Insert L2 cache inbetween Wishbone bus and LiteDRAM
989 l2_cache_size
= max(l2_cache_size
, int(2*port
.data_width
/8)) # Use minimal size if lower
990 l2_cache_size
= 2**int(log2(l2_cache_size
)) # Round to nearest power of 2
991 l2_cache_data_width
= max(port
.data_width
, l2_cache_min_data_width
)
992 l2_cache
= wishbone
.Cache(
993 cachesize
= l2_cache_size
//4,
995 slave
= wishbone
.Interface(l2_cache_data_width
),
996 reverse
= l2_cache_reverse
)
997 if l2_cache_full_memory_we
:
998 l2_cache
= FullMemoryWE()(l2_cache
)
999 self
.submodules
.l2_cache
= l2_cache
1000 litedram_wb
= self
.l2_cache
.slave
1002 litedram_wb
= wishbone
.Interface(port
.data_width
)
1003 self
.submodules
+= wishbone
.Converter(wb_sdram
, litedram_wb
)
1004 self
.add_config("L2_SIZE", l2_cache_size
)
1006 # Wishbone Slave <--> LiteDRAM bridge --------------------------------------------------
1007 self
.submodules
.wishbone_bridge
= LiteDRAMWishbone2Native(litedram_wb
, port
,
1008 base_address
= self
.bus
.regions
["main_ram"].origin
)