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
27 # - replace raise with exit on logging error.
28 # - add configurable CSR paging.
29 # - manage SoCLinkerRegion
30 # - cleanup SoCCSRRegion
32 logging
.basicConfig(level
=logging
.INFO
)
34 # Helpers ------------------------------------------------------------------------------------------
35 def colorer(s
, color
="bright"):
42 "underline": "\x1b[4m"}[color
]
44 return header
+ str(s
) + trailer
46 def build_time(with_time
=True):
47 fmt
= "%Y-%m-%d %H:%M:%S" if with_time
else "%Y-%m-%d"
48 return datetime
.datetime
.fromtimestamp(time
.time()).strftime(fmt
)
50 # SoCConstant --------------------------------------------------------------------------------------
52 def SoCConstant(value
):
55 # SoCRegion ----------------------------------------------------------------------------------------
58 def __init__(self
, origin
=None, size
=None, mode
="rw", cached
=True):
59 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
))
87 class SoCIORegion(SoCRegion
): pass
89 class SoCLinkerRegion(SoCRegion
): pass
91 # SoCCSRRegion -------------------------------------------------------------------------------------
94 def __init__(self
, origin
, busword
, obj
):
96 self
.busword
= busword
99 # SoCBusHandler ------------------------------------------------------------------------------------
101 class SoCBusHandler(Module
):
102 supported_standard
= ["wishbone"]
103 supported_data_width
= [32, 64]
104 supported_address_width
= [32]
106 # Creation -------------------------------------------------------------------------------------
107 def __init__(self
, standard
, data_width
=32, address_width
=32, timeout
=1e6
, reserved_regions
={}):
108 self
.logger
= logging
.getLogger("SoCBusHandler")
109 self
.logger
.info(colorer("Creating new Bus Handler..."))
112 if standard
not in self
.supported_standard
:
113 self
.logger
.error("Unsupported Standard: {} supporteds: {:s}".format(
114 colorer(standard
, color
="red"),
115 colorer(", ".join(self
.supported_standard
), color
="green")))
119 if data_width
not in self
.supported_data_width
:
120 self
.logger
.error("Unsupported Data_Width: {} supporteds: {:s}".format(
121 colorer(data_width
, color
="red"),
122 colorer(", ".join(str(x
) for x
in self
.supported_data_width
), color
="green")))
125 # Check Address Width
126 if address_width
not in self
.supported_address_width
:
127 self
.logger
.error("Unsupported Address Width: {} supporteds: {:s}".format(
128 colorer(data_width
, color
="red"),
129 colorer(", ".join(str(x
) for x
in self
.supported_address_width
), color
="green")))
133 self
.standard
= standard
134 self
.data_width
= data_width
135 self
.address_width
= address_width
141 self
.timeout
= timeout
142 self
.logger
.info("{}-bit {} Bus, {}GiB Address Space.".format(
143 colorer(data_width
), colorer(standard
), colorer(2**address_width
/2**30)))
145 # Adding reserved regions
146 self
.logger
.info("Adding {} Regions...".format(colorer("reserved")))
147 for name
, region
in reserved_regions
.items():
148 if isinstance(region
, int):
149 region
= SoCRegion(origin
=region
, size
=0x1000000)
150 self
.add_region(name
, region
)
152 self
.logger
.info(colorer("Bus Handler created."))
154 # Add/Allog/Check Regions ----------------------------------------------------------------------
155 def add_region(self
, name
, region
):
157 # Check if SoCIORegion
158 if isinstance(region
, SoCIORegion
):
159 if name
in self
.masters
.keys():
160 self
.logger
.error("{} already declared as IO Region:".format(colorer(name
, color
="red")))
161 self
.logger
.error(self
)
163 self
.io_regions
[name
] = region
164 overlap
= self
.check_regions_overlap(self
.io_regions
)
165 if overlap
is not None:
166 self
.logger
.error("IO Region overlap between {} and {}:".format(
167 colorer(overlap
[0], color
="red"),
168 colorer(overlap
[1], color
="red")))
169 self
.logger
.error(str(self
.regions
[overlap
[0]]))
170 self
.logger
.error(str(self
.regions
[overlap
[1]]))
172 self
.logger
.info("{} Region {} {}.".format(
173 colorer(name
, color
="underline"),
174 colorer("added", color
="green"),
176 # Check if SoCLinkerRegion
177 elif isinstance(region
, SoCLinkerRegion
):
178 if name
in self
.masters
.keys():
179 self
.logger
.error("{} already declared as Linker Region:".format(colorer(name
, color
="red")))
180 self
.logger
.error(self
)
182 self
.ld_regions
[name
] = region
183 overlap
= self
.check_regions_overlap(self
.ld_regions
)
184 if overlap
is not None:
185 self
.logger
.error("Linker Region overlap between {} and {}:".format(
186 colorer(overlap
[0], color
="red"),
187 colorer(overlap
[1], color
="red")))
188 self
.logger
.error(str(self
.regions
[overlap
[0]]))
189 self
.logger
.error(str(self
.regions
[overlap
[1]]))
191 self
.logger
.info("{} Region {} {}.".format(
192 colorer(name
, color
="underline"),
193 colorer("added", color
="green"),
196 elif isinstance(region
, SoCRegion
):
197 # If no origin specified, allocate region.
198 if region
.origin
is None:
200 region
= self
.alloc_region(name
, region
.size
, region
.cached
)
201 self
.regions
[name
] = region
202 # Else add region and check for overlaps.
204 if not region
.cached
:
205 if not self
.check_region_is_io(region
):
206 self
.logger
.error("{} Region {}: {}".format(
207 colorer(name
, color
="red"),
208 colorer("not cached but not in IO region", color
="red"),
210 self
.logger
.error(self
)
212 self
.regions
[name
] = region
213 overlap
= self
.check_regions_overlap(self
.regions
)
214 if overlap
is not None:
215 self
.logger
.error("Region overlap between {} and {}:".format(
216 colorer(overlap
[0], color
="red"),
217 colorer(overlap
[1], color
="red")))
218 self
.logger
.error(str(self
.regions
[overlap
[0]]))
219 self
.logger
.error(str(self
.regions
[overlap
[1]]))
221 self
.logger
.info("{} Region {} {}.".format(
222 colorer(name
, color
="underline"),
223 colorer("allocated" if allocated
else "added", color
="cyan" if allocated
else "green"),
226 self
.logger
.error("{} is not a supported Region".format(colorer(name
, color
="red")))
229 def alloc_region(self
, name
, size
, cached
=True):
230 self
.logger
.info("Allocating {} Region of size {}...".format(
231 colorer("Cached" if cached
else "IO"),
232 colorer("0x{:08x}".format(size
))))
234 # Limit Search Regions
236 search_regions
= self
.io_regions
238 search_regions
= {"main": SoCRegion(origin
=0x00000000, size
=2**self
.address_width
-1)}
240 # Iterate on Search_Regions to find a Candidate
241 for _
, search_region
in search_regions
.items():
242 origin
= search_region
.origin
243 while (origin
+ size
) < (search_region
.origin
+ search_region
.size
):
244 # Create a Candicate.
245 candidate
= SoCRegion(origin
=origin
, size
=size
, cached
=cached
)
247 # Check Candidate does not overlap with allocated existing regions
248 for _
, allocated
in self
.regions
.items():
249 if self
.check_regions_overlap({"0": allocated
, "1": candidate
}) is not None:
250 origin
= allocated
.origin
+ allocated
.size
254 # If no overlap, the Candidate is selected
257 self
.logger
.error("Not enough Address Space to allocate Region")
260 def check_regions_overlap(self
, regions
):
262 while i
< len(regions
):
263 n0
= list(regions
.keys())[i
]
265 for n1
in list(regions
.keys())[i
+1:]:
267 if isinstance(r0
, SoCLinkerRegion
) or isinstance(r1
, SoCLinkerRegion
):
269 if r0
.origin
>= (r1
.origin
+ r1
.size
):
271 if r1
.origin
>= (r0
.origin
+ r0
.size
):
277 def check_region_is_in(self
, region
, container
):
279 if not (region
.origin
>= container
.origin
):
281 if not ((region
.origin
+ region
.size
) < (container
.origin
+ container
.size
)):
285 def check_region_is_io(self
, region
):
287 for _
, io_region
in self
.io_regions
.items():
288 if self
.check_region_is_in(region
, io_region
):
292 # Add Master/Slave -----------------------------------------------------------------------------
293 def add_adapter(self
, name
, interface
):
294 if interface
.data_width
!= self
.data_width
:
295 self
.logger
.info("{} Bus {} from {}-bit to {}-bit.".format(
297 colorer("converted", color
="cyan"),
298 colorer(interface
.data_width
),
299 colorer(self
.data_width
)))
300 new_interface
= wishbone
.Interface(data_width
=self
.data_width
)
301 self
.submodules
+= wishbone
.Converter(interface
, new_interface
)
306 def add_master(self
, name
=None, master
=None):
308 name
= "master{:d}".format(len(self
.masters
))
309 if name
in self
.masters
.keys():
310 self
.logger
.error("{} already declared as Bus Master:".format(colorer(name
, color
="red")))
311 self
.logger
.error(self
)
313 master
= self
.add_adapter(name
, master
)
314 self
.masters
[name
] = master
315 self
.logger
.info("{} {} as Bus Master.".format(
316 colorer(name
, color
="underline"),
317 colorer("added", color
="green")))
319 def add_slave(self
, name
=None, slave
=None, region
=None):
320 no_name
= name
is None
321 no_region
= region
is None
322 if no_name
and no_region
:
323 self
.logger
.error("Please specify at least {} or {} of Bus Slave".format(
324 colorer("name", color
="red"),
325 colorer("region", color
="red")))
328 name
= "slave{:d}".format(len(self
.slaves
))
330 region
= self
.regions
.get(name
, None)
332 self
.logger
.error("Unable to find Region {}".format(colorer(name
, color
="red")))
335 self
.add_region(name
, region
)
336 if name
in self
.slaves
.keys():
337 self
.logger
.error("{} already declared as Bus Slave:".format(colorer(name
, color
="red")))
338 self
.logger
.error(self
)
340 slave
= self
.add_adapter(name
, slave
)
341 self
.slaves
[name
] = slave
342 self
.logger
.info("{} {} as Bus Slave.".format(
343 colorer(name
, color
="underline"),
344 colorer("added", color
="green")))
346 # Str ------------------------------------------------------------------------------------------
348 r
= "{}-bit {} Bus, {}GiB Address Space.\n".format(
349 colorer(self
.data_width
), colorer(self
.standard
), colorer(2**self
.address_width
/2**30))
350 r
+= "IO Regions: ({})\n".format(len(self
.io_regions
.keys())) if len(self
.io_regions
.keys()) else ""
351 io_regions
= {k
: v
for k
, v
in sorted(self
.io_regions
.items(), key
=lambda item
: item
[1].origin
)}
352 for name
, region
in io_regions
.items():
353 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
354 r
+= "Linker Regions: ({})\n".format(len(self
.ld_regions
.keys())) if len(self
.ld_regions
.keys()) else ""
355 ld_regions
= {k
: v
for k
, v
in sorted(self
.ld_regions
.items(), key
=lambda item
: item
[1].origin
)}
356 for name
, region
in ld_regions
.items():
357 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
358 r
+= "Bus Regions: ({})\n".format(len(self
.regions
.keys())) if len(self
.regions
.keys()) else ""
359 regions
= {k
: v
for k
, v
in sorted(self
.regions
.items(), key
=lambda item
: item
[1].origin
)}
360 for name
, region
in regions
.items():
361 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
362 r
+= "Bus Masters: ({})\n".format(len(self
.masters
.keys())) if len(self
.masters
.keys()) else ""
363 for name
in self
.masters
.keys():
364 r
+= "- {}\n".format(colorer(name
, color
="underline"))
365 r
+= "Bus Slaves: ({})\n".format(len(self
.slaves
.keys())) if len(self
.slaves
.keys()) else ""
366 for name
in self
.slaves
.keys():
367 r
+= "- {}\n".format(colorer(name
, color
="underline"))
371 # SoCLocHandler --------------------------------------------------------------------------------------
373 class SoCLocHandler(Module
):
374 # Creation -------------------------------------------------------------------------------------
375 def __init__(self
, name
, n_locs
):
380 # Add ------------------------------------------------------------------------------------------
381 def add(self
, name
, n
=None, use_loc_if_exists
=False):
383 if not (use_loc_if_exists
and name
in self
.locs
.keys()):
384 if name
in self
.locs
.keys():
385 self
.logger
.error("{} {} name already used.".format(colorer(name
, "red"), self
.name
))
386 self
.logger
.error(self
)
388 if n
in self
.locs
.values():
389 self
.logger
.error("{} {} Location already used.".format(colorer(n
, "red"), self
.name
))
390 self
.logger
.error(self
)
397 self
.logger
.error("{} {} Location should be positive.".format(
398 colorer(n
, color
="red"),
402 self
.logger
.error("{} {} Location too high (Up to {}).".format(
403 colorer(n
, color
="red"),
405 colorer(self
.n_csrs
, color
="green")))
410 self
.logger
.info("{} {} {} at Location {}.".format(
411 colorer(name
, color
="underline"),
413 colorer("allocated" if allocated
else "added", color
="cyan" if allocated
else "green"),
416 # Alloc ----------------------------------------------------------------------------------------
417 def alloc(self
, name
):
418 for n
in range(self
.n_locs
):
419 if n
not in self
.locs
.values():
421 self
.logger
.error("Not enough Locations.")
422 self
.logger
.error(self
)
425 # Str ------------------------------------------------------------------------------------------
427 r
= "{} Locations: ({})\n".format(self
.name
, len(self
.locs
.keys())) if len(self
.locs
.keys()) else ""
428 locs
= {k
: v
for k
, v
in sorted(self
.locs
.items(), key
=lambda item
: item
[1])}
429 for name
in locs
.keys():
430 r
+= "- {}{}: {}\n".format(colorer(name
, color
="underline"), " "*(20-len(name
)), colorer(self
.locs
[name
]))
433 # SoCCSRHandler ------------------------------------------------------------------------------------
435 class SoCCSRHandler(SoCLocHandler
):
436 supported_data_width
= [8, 32]
437 supported_address_width
= [14, 15]
438 supported_alignment
= [32, 64]
439 supported_paging
= [0x800]
441 # Creation -------------------------------------------------------------------------------------
442 def __init__(self
, data_width
=32, address_width
=14, alignment
=32, paging
=0x800, reserved_csrs
={}):
443 SoCLocHandler
.__init
__(self
, "CSR", n_locs
=4*2**address_width
//paging
) # FIXME
444 self
.logger
= logging
.getLogger("SoCCSRHandler")
445 self
.logger
.info(colorer("Creating new CSR Handler..."))
448 if data_width
not in self
.supported_data_width
:
449 self
.logger
.error("Unsupported data_width: {} supporteds: {:s}".format(
450 colorer(data_width
, color
="red"),
451 colorer(", ".join(str(x
) for x
in self
.supported_data_width
)), color
="green"))
454 # Check Address Width
455 if address_width
not in self
.supported_address_width
:
456 self
.logger
.error("Unsupported address_width: {} supporteds: {:s}".format(
457 colorer(address_width
, color
="red"),
458 colorer(", ".join(str(x
) for x
in self
.supported_address_width
), color
="green")))
462 if alignment
not in self
.supported_alignment
:
463 self
.logger
.error("Unsupported alignment: {} supporteds: {:s}".format(
464 colorer(alignment
, color
="red"),
465 colorer(", ".join(str(x
) for x
in self
.supported_alignment
), color
="green")))
467 if data_width
> alignment
:
468 self
.logger
.error("Alignment ({}) should be >= data_width ({})".format(
469 colorer(alignment
, color
="red"),
470 colorer(data_width
, color
="red")))
474 if paging
not in self
.supported_paging
:
475 self
.logger
.error("Unsupported paging: {} supporteds: {:s}".format(
476 colorer(paging
, color
="red"),
477 colorer(", ".join(str(x
) for x
in self
.supported_paging
), color
="green")))
481 self
.data_width
= data_width
482 self
.address_width
= address_width
483 self
.alignment
= alignment
487 self
.logger
.info("{}-bit CSR Bus, {}KiB Address Space, {}B Paging (Up to {} Locations).".format(
488 colorer(self
.data_width
),
489 colorer(2**self
.address_width
/2**10),
490 colorer(self
.paging
),
491 colorer(self
.n_locs
)))
493 # Adding reserved CSRs
494 self
.logger
.info("Adding {} CSRs...".format(colorer("reserved")))
495 for name
, n
in reserved_csrs
.items():
498 self
.logger
.info(colorer("CSR Handler created."))
500 # Add Master -----------------------------------------------------------------------------------
501 def add_master(self
, name
=None, master
=None):
503 name
= "master{:d}".format(len(self
.masters
))
504 if name
in self
.masters
.keys():
505 self
.logger
.error("{} already declared as CSR Master:".format(colorer(name
, color
="red")))
506 self
.logger
.error(self
)
508 if master
.data_width
!= self
.data_width
:
509 self
.logger
.error("{} Master/Handler data_width {} ({} vs {}).".format(
511 colorer("missmatch"),
512 colorer(master
.data_width
, color
="red"),
513 colorer(self
.data_width
, color
="red")))
515 self
.masters
[name
] = master
516 self
.logger
.info("{} {} as CSR Master.".format(
517 colorer(name
, color
="underline"),
518 colorer("added", color
="green")))
520 # Add Region -----------------------------------------------------------------------------------
521 def add_region(self
, name
, region
):
523 self
.regions
[name
] = region
525 # Address map ----------------------------------------------------------------------------------
526 def address_map(self
, name
, memory
):
527 if memory
is not None:
528 name
= name
+ "_" + memory
.name_override
529 if self
.locs
.get(name
, None) is None:
530 self
.logger
.error("Undefined {} CSR.".format(colorer(name
, color
="red")))
532 return self
.locs
[name
]
534 # Str ------------------------------------------------------------------------------------------
536 r
= "{}-bit CSR Bus, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
537 colorer(self
.data_width
),
538 colorer(2**self
.address_width
/2**10),
539 colorer(self
.paging
),
540 colorer(self
.n_locs
))
541 r
+= SoCLocHandler
.__str
__(self
)
545 # SoCIRQHandler ------------------------------------------------------------------------------------
547 class SoCIRQHandler(SoCLocHandler
):
548 # Creation -------------------------------------------------------------------------------------
549 def __init__(self
, n_irqs
=32, reserved_irqs
={}):
550 SoCLocHandler
.__init
__(self
, "IRQ", n_locs
=n_irqs
)
551 self
.logger
= logging
.getLogger("SoCIRQHandler")
552 self
.logger
.info(colorer("Creating new SoC IRQ Handler..."))
556 self
.logger
.error("Unsupported IRQs number: {} supporteds: {:s}".format(
557 colorer(n
, color
="red"), colorer("Up to 32", color
="green")))
561 self
.logger
.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs
)))
563 # Adding reserved IRQs
564 self
.logger
.info("Adding {} IRQs...".format(colorer("reserved")))
565 for name
, n
in reserved_irqs
.items():
568 self
.logger
.info(colorer("IRQ Handler created."))
570 # Str ------------------------------------------------------------------------------------------
572 r
="IRQ Handler (up to {} Locations).\n".format(colorer(self
.n_locs
))
573 r
+= SoCLocHandler
.__str
__(self
)
577 # SoCController ------------------------------------------------------------------------------------
579 class SoCController(Module
, AutoCSR
):
581 self
._reset
= CSRStorage(1, description
="""
582 Write a ``1`` to this register to reset the SoC.""")
583 self
._scratch
= CSRStorage(32, reset
=0x12345678, description
="""
584 Use this register as a scratch space to verify that software read/write accesses
585 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
586 can be used to verify endianness.""")
587 self
._bus
_errors
= CSRStatus(32, description
="""
588 Total number of Wishbone bus errors (timeouts) since last reset.""")
593 self
.reset
= Signal()
594 self
.comb
+= self
.reset
.eq(self
._reset
.re
)
597 self
.bus_error
= Signal()
598 bus_errors
= Signal(32)
600 If(bus_errors
!= (2**len(bus_errors
)-1),
601 If(self
.bus_error
, bus_errors
.eq(bus_errors
+ 1))
603 self
.comb
+= self
._bus
_errors
.status
.eq(bus_errors
)
605 # SoC ----------------------------------------------------------------------------------------------
608 def __init__(self
, platform
, sys_clk_freq
,
610 bus_standard
= "wishbone",
612 bus_address_width
= 32,
614 bus_reserved_regions
= {},
617 csr_address_width
= 14,
620 csr_reserved_csrs
= {},
623 irq_reserved_irqs
= {},
626 self
.logger
= logging
.getLogger("SoC")
627 self
.logger
.info(colorer(" __ _ __ _ __ ", color
="bright"))
628 self
.logger
.info(colorer(" / / (_) /____ | |/_/ ", color
="bright"))
629 self
.logger
.info(colorer(" / /__/ / __/ -_)> < ", color
="bright"))
630 self
.logger
.info(colorer(" /____/_/\\__/\\__/_/|_| ", color
="bright"))
631 self
.logger
.info(colorer(" Build your hardware, easily!", color
="bright"))
633 self
.logger
.info(colorer("-"*80, color
="bright"))
634 self
.logger
.info(colorer("Creating new SoC... ({})".format(build_time())))
635 self
.logger
.info(colorer("-"*80, color
="bright"))
637 # SoC attributes ---------------------------------------------------------------------------
638 self
.platform
= platform
639 self
.sys_clk_freq
= sys_clk_freq
641 self
.csr_regions
= {}
643 # SoC Bus Handler --------------------------------------------------------------------------
644 self
.submodules
.bus
= SoCBusHandler(
645 standard
= bus_standard
,
646 data_width
= bus_data_width
,
647 address_width
= bus_address_width
,
648 timeout
= bus_timeout
,
649 reserved_regions
= bus_reserved_regions
,
652 # SoC Bus Handler --------------------------------------------------------------------------
653 self
.submodules
.csr
= SoCCSRHandler(
654 data_width
= csr_data_width
,
655 address_width
= csr_address_width
,
656 alignment
= csr_alignment
,
658 reserved_csrs
= csr_reserved_csrs
,
661 # SoC IRQ Handler --------------------------------------------------------------------------
662 self
.submodules
.irq
= SoCIRQHandler(
664 reserved_irqs
= irq_reserved_irqs
667 self
.logger
.info(colorer("-"*80, color
="bright"))
668 self
.logger
.info(colorer("Initial SoC:"))
669 self
.logger
.info(colorer("-"*80, color
="bright"))
670 self
.logger
.info(self
.bus
)
671 self
.logger
.info(self
.csr
)
672 self
.logger
.info(self
.irq
)
673 self
.logger
.info(colorer("-"*80, color
="bright"))
675 self
.add_config("CLOCK_FREQUENCY", int(sys_clk_freq
))
677 # SoC Helpers ----------------------------------------------------------------------------------
678 def check_if_exists(self
, name
):
679 if hasattr(self
, name
):
680 self
.logger
.error("{} SubModule already declared.".format(colorer(name
, "red")))
683 def add_constant(self
, name
, value
=None):
685 if name
in self
.constants
.keys():
686 self
.logger
.error("{} Constant already declared.".format(colorer(name
, "red")))
688 self
.constants
[name
] = SoCConstant(value
)
690 def add_config(self
, name
, value
):
691 name
= "CONFIG_" + name
692 if isinstance(value
, str):
693 self
.add_constant(name
+ "_" + value
)
695 self
.add_constant(name
, value
)
697 # SoC Main Components --------------------------------------------------------------------------
698 def add_controller(self
, name
="ctrl"):
699 self
.check_if_exists(name
)
700 setattr(self
.submodules
, name
, SoCController())
701 self
.csr
.add(name
, use_loc_if_exists
=True)
703 def add_ram(self
, name
, origin
, size
, contents
=[], mode
="rw"):
704 ram_bus
= wishbone
.Interface(data_width
=self
.bus
.data_width
)
705 ram
= wishbone
.SRAM(size
, bus
=ram_bus
, init
=contents
, read_only
=(mode
== "r"))
706 self
.bus
.add_slave(name
, ram
.bus
, SoCRegion(origin
=origin
, size
=size
, mode
=mode
))
707 self
.check_if_exists(name
)
708 self
.logger
.info("RAM {} {} {}.".format(
710 colorer("added", color
="green"),
711 self
.bus
.regions
[name
]))
712 setattr(self
.submodules
, name
, ram
)
714 def add_rom(self
, name
, origin
, size
, contents
=[]):
715 self
.add_ram(name
, origin
, size
, contents
, mode
="r")
717 def add_csr_bridge(self
, origin
):
718 self
.submodules
.csr_bridge
= wishbone2csr
.WB2CSR(
719 bus_csr
= csr_bus
.Interface(
720 address_width
= self
.csr
.address_width
,
721 data_width
= self
.csr
.data_width
))
722 csr_size
= 2**(self
.csr
.address_width
+ 2)
723 csr_region
= SoCRegion(origin
=origin
, size
=csr_size
, cached
=False)
724 self
.bus
.add_slave("csr", self
.csr_bridge
.wishbone
, csr_region
)
725 self
.csr
.add_master(name
="bridge", master
=self
.csr_bridge
.csr
)
726 self
.add_config("CSR_DATA_WIDTH", self
.csr
.data_width
)
727 self
.add_config("CSR_ALIGNMENT", self
.csr
.alignment
)
729 def add_cpu(self
, name
="vexriscv", variant
="standard", reset_address
=None):
730 if name
not in cpu
.CPUS
.keys():
731 self
.logger
.error("{} CPU not supported, supporteds: {}".format(
732 colorer(name
, color
="red"),
733 colorer(", ".join(cpu
.CPUS
.keys()), color
="green")))
736 self
.submodules
.cpu
= cpu
.CPUS
[name
](self
.platform
, variant
)
737 # Add Bus Masters/CSR/IRQs
738 if not isinstance(self
.cpu
, cpu
.CPUNone
):
739 self
.cpu
.set_reset_address(reset_address
)
740 for n
, cpu_bus
in enumerate(self
.cpu
.buses
):
741 self
.bus
.add_master(name
="cpu_bus{}".format(n
), master
=cpu_bus
)
742 self
.add_csr("cpu", use_loc_if_exists
=True)
743 for name
, loc
in self
.cpu
.interrupts
.items():
744 self
.irq
.add(name
, loc
)
745 if hasattr(self
, "ctrl"):
746 self
.comb
+= self
.cpu
.reset
.eq(self
.ctrl
.reset
)
747 self
.add_config("CPU_RESET_ADDR", reset_address
)
748 # Update SoC with CPU constraints
749 for n
, (origin
, size
) in enumerate(self
.cpu
.io_regions
.items()):
750 self
.bus
.add_region("io{}".format(n
), SoCIORegion(origin
=origin
, size
=size
, cached
=False))
751 self
.mem_map
.update(self
.cpu
.mem_map
) # FIXME
753 self
.add_config("CPU_TYPE", str(name
))
754 self
.add_config("CPU_VARIANT", str(variant
.split('+')[0]))
756 def add_timer(self
, name
="timer0"):
757 self
.check_if_exists(name
)
758 setattr(self
.submodules
, name
, Timer())
759 self
.csr
.add(name
, use_loc_if_exists
=True)
760 self
.irq
.add(name
, use_loc_if_exists
=True)
762 # SoC finalization -----------------------------------------------------------------------------
763 def do_finalize(self
):
764 self
.logger
.info(colorer("-"*80, color
="bright"))
765 self
.logger
.info(colorer("Finalized SoC:"))
766 self
.logger
.info(colorer("-"*80, color
="bright"))
767 self
.logger
.info(self
.bus
)
768 self
.logger
.info(self
.csr
)
769 self
.logger
.info(self
.irq
)
770 self
.logger
.info(colorer("-"*80, color
="bright"))
772 # SoC Bus Interconnect ---------------------------------------------------------------------
773 bus_masters
= self
.bus
.masters
.values()
774 bus_slaves
= [(self
.bus
.regions
[n
].decoder(self
.bus
), s
) for n
, s
in self
.bus
.slaves
.items()]
775 if len(bus_masters
) and len(bus_slaves
):
776 self
.submodules
.bus_interconnect
= wishbone
.InterconnectShared(
777 masters
= bus_masters
,
780 timeout_cycles
= self
.bus
.timeout
)
781 if hasattr(self
, "ctrl") and self
.bus
.timeout
is not None:
782 self
.comb
+= self
.ctrl
.bus_error
.eq(self
.bus_interconnect
.timeout
.error
)
784 # SoC CSR Interconnect ---------------------------------------------------------------------
785 self
.submodules
.csr_bankarray
= csr_bus
.CSRBankArray(self
,
786 address_map
= self
.csr
.address_map
,
787 data_width
= self
.csr
.data_width
,
788 address_width
= self
.csr
.address_width
,
789 alignment
= self
.csr
.alignment
791 if len(self
.csr
.masters
):
792 self
.submodules
.csr_interconnect
= csr_bus
.InterconnectShared(
793 masters
= list(self
.csr
.masters
.values()),
794 slaves
= self
.csr_bankarray
.get_buses())
797 for name
, csrs
, mapaddr
, rmap
in self
.csr_bankarray
.banks
:
798 self
.csr
.add_region(name
, SoCCSRRegion(
799 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
800 busword
= self
.csr
.data_width
,
804 for name
, memory
, mapaddr
, mmap
in self
.csr_bankarray
.srams
:
805 self
.csr
.add_region(name
+ "_" + memory
.name_override
, SoCCSRRegion(
806 origin
= (self
.bus
.regions
["csr"].origin
+ self
.csr
.paging
*mapaddr
),
807 busworkd
= self
.csr
.data_width
,
810 # Sort CSR regions by origin
811 self
.csr
.regions
= {k
: v
for k
, v
in sorted(self
.csr
.regions
.items(), key
=lambda item
: item
[1].origin
)}
813 # Add CSRs / Config items to constants
814 for name
, constant
in self
.csr_bankarray
.constants
:
815 self
.add_constant(name
+ "_" + constant
.name
, constant
.value
.value
)
817 # SoC CPU Check ----------------------------------------------------------------------------
818 if not isinstance(self
.cpu
, cpu
.CPUNone
):
819 for name
in ["rom", "sram"]:
820 if name
not in list(self
.bus
.regions
.keys()) + list(self
.bus
.ld_regions
.keys()):
821 self
.logger
.error("CPU needs {} Region to be defined as Bus or Linker Region.".format(
822 colorer(name
, color
="red")))
823 self
.logger
.error(self
.bus
)
826 # SoC IRQ Interconnect ---------------------------------------------------------------------
827 if hasattr(self
, "cpu"):
828 if hasattr(self
.cpu
, "interrupt"):
829 for name
, loc
in sorted(self
.irq
.locs
.items()):
830 if name
in self
.cpu
.interrupts
.keys():
832 if hasattr(self
, name
):
833 module
= getattr(self
, name
)
834 if not hasattr(module
, "ev"):
835 self
.logger
.error("No EventManager found on {} SubModule".format(
836 colorer(name
, color
="red")))
837 self
.comb
+= self
.cpu
.interrupt
[loc
].eq(module
.ev
.irq
)
838 self
.add_constant(name
+ "_INTERRUPT", loc
)
840 # SoC build ------------------------------------------------------------------------------------
841 def build(self
, *args
, **kwargs
):
842 return self
.platform
.build(self
, *args
, **kwargs
)
844 # LiteXSoC -----------------------------------------------------------------------------------------
847 # Add Identifier -------------------------------------------------------------------------------
848 def add_identifier(self
, name
="identifier", identifier
="LiteX SoC", with_build_time
=True):
849 self
.check_if_exists(name
)
851 identifier
+= " " + build_time()
852 setattr(self
.submodules
, name
, Identifier(ident
))
853 self
.csr
.add(name
+ "_mem", use_loc_if_exists
=True)
855 # Add UART -------------------------------------------------------------------------------------
856 def add_uart(self
, name
, baudrate
=115200):
857 from litex
.soc
.cores
import uart
858 if name
in ["stub", "stream"]:
859 self
.submodules
.uart
= uart
.UART()
861 self
.comb
+= self
.uart
.sink
.ready
.eq(1)
862 elif name
== "bridge":
863 self
.submodules
.uart
= uart
.UARTWishboneBridge(
864 pads
= self
.platform
.request("serial"),
865 clk_freq
= self
.sys_clk_freq
,
867 self
.bus
.master(name
="uart_bridge", master
=self
.uart
.wishbone
)
868 elif name
== "crossover":
869 self
.submodules
.uart
= uart
.UARTCrossover()
871 if name
== "jtag_atlantic":
872 from litex
.soc
.cores
.jtag
import JTAGAtlantic
873 self
.submodules
.uart_phy
= JTAGAtlantic()
874 elif name
== "jtag_uart":
875 from litex
.soc
.cores
.jtag
import JTAGPHY
876 self
.submodules
.uart_phy
= JTAGPHY(device
=self
.platform
.device
)
878 self
.submodules
.uart_phy
= uart
.UARTPHY(
879 pads
= self
.platform
.request(name
),
880 clk_freq
= self
.sys_clk_freq
,
882 self
.submodules
.uart
= ResetInserter()(uart
.UART(self
.uart_phy
))
883 self
.csr
.add("uart_phy", use_loc_if_exists
=True)
884 self
.csr
.add("uart", use_loc_if_exists
=True)
885 self
.irq
.add("uart", use_loc_if_exists
=True)
887 # Add SDRAM ------------------------------------------------------------------------------------
888 def add_sdram(self
, name
, phy
, module
, origin
, size
=None,
889 l2_cache_size
= 8192,
890 l2_cache_min_data_width
= 128,
891 l2_cache_reverse
= True,
892 l2_cache_full_memory_we
= True,
895 # LiteDRAM core ----------------------------------------------------------------------------
896 self
.submodules
.sdram
= LiteDRAMCore(
898 geom_settings
= module
.geom_settings
,
899 timing_settings
= module
.timing_settings
,
900 clk_freq
= self
.sys_clk_freq
,
902 self
.csr
.add("sdram")
904 # LiteDRAM port ----------------------------------------------------------------------------
905 port
= self
.sdram
.crossbar
.get_port()
906 port
.data_width
= 2**int(log2(port
.data_width
)) # Round to nearest power of 2
908 # SDRAM size -------------------------------------------------------------------------------
909 sdram_size
= 2**(module
.geom_settings
.bankbits
+
910 module
.geom_settings
.rowbits
+
911 module
.geom_settings
.colbits
)*phy
.settings
.databits
//8
913 sdram_size
= min(sdram_size
, size
)
914 self
.bus
.add_region("main_ram", SoCRegion(origin
=origin
, size
=sdram_size
))
916 # SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
917 if self
.cpu
.name
== "rocket":
918 # Rocket has its own I/D L1 cache: connect directly to LiteDRAM when possible.
919 if port
.data_width
== self
.cpu
.mem_axi
.data_width
:
920 self
.logger
.info("Matching AXI MEM data width ({})\n".format(port
.data_width
))
921 self
.submodules
+= LiteDRAMAXI2Native(
922 axi
= self
.cpu
.mem_axi
,
924 base_address
= self
.bus
.regions
["main_ram"].origin
)
926 self
.logger
.info("Converting MEM data width: {} to {} via Wishbone".format(
928 self
.cpu
.mem_axi
.data_width
))
929 # FIXME: replace WB data-width converter with native AXI converter!!!
930 mem_wb
= wishbone
.Interface(
931 data_width
= self
.cpu
.mem_axi
.data_width
,
932 adr_width
= 32-log2_int(self
.cpu
.mem_axi
.data_width
//8))
933 # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
934 mem_a2w
= ResetInserter()(axi
.AXI2Wishbone(
935 axi
= self
.cpu
.mem_axi
,
938 self
.comb
+= mem_a2w
.reset
.eq(ResetSignal() | self
.cpu
.reset
)
939 self
.submodules
+= mem_a2w
940 litedram_wb
= wishbone
.Interface(port
.data_width
)
941 self
.submodules
+= LiteDRAMWishbone2Native(
942 wishbone
= litedram_wb
,
944 base_address
= origin
)
945 self
.submodules
+= wishbone
.Converter(mem_wb
, litedram_wb
)
946 elif self
.with_wishbone
:
947 # Wishbone Slave SDRAM interface -------------------------------------------------------
948 wb_sdram
= wishbone
.Interface()
949 self
.bus
.add_slave("main_ram", wb_sdram
, SoCRegion(origin
=origin
, size
=sdram_size
))
951 # L2 Cache -----------------------------------------------------------------------------
952 if l2_cache_size
!= 0:
953 # Insert L2 cache inbetween Wishbone bus and LiteDRAM
954 l2_cache_size
= max(l2_cache_size
, int(2*port
.data_width
/8)) # Use minimal size if lower
955 l2_cache_size
= 2**int(log2(l2_cache_size
)) # Round to nearest power of 2
956 l2_cache_data_width
= max(port
.data_width
, l2_cache_min_data_width
)
957 l2_cache
= wishbone
.Cache(
958 cachesize
= l2_cache_size
//4,
960 slave
= wishbone
.Interface(l2_cache_data_width
),
961 reverse
= l2_cache_reverse
)
962 if l2_cache_full_memory_we
:
963 l2_cache
= FullMemoryWE()(l2_cache
)
964 self
.submodules
.l2_cache
= l2_cache
965 litedram_wb
= self
.l2_cache
.slave
967 litedram_wb
= wishbone
.Interface(port
.data_width
)
968 self
.submodules
+= wishbone
.Converter(wb_sdram
, litedram_wb
)
969 self
.add_config("L2_SIZE", l2_cache_size
)
971 # Wishbone Slave <--> LiteDRAM bridge --------------------------------------------------
972 self
.submodules
.wishbone_bridge
= LiteDRAMWishbone2Native(litedram_wb
, port
)