3 # This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
12 from litex
.soc
.cores
.identifier
import Identifier
13 from litex
.soc
.cores
.timer
import Timer
15 from litex
.soc
.interconnect
.csr
import *
16 from litex
.soc
.interconnect
import csr_bus
17 from litex
.soc
.interconnect
import wishbone
18 from litex
.soc
.interconnect
import wishbone2csr
21 # - replace raise with exit on logging error.
22 # - add configurable CSR paging.
23 # - manage IO/Linker regions.
25 logging
.basicConfig(level
=logging
.INFO
)
27 # Helpers ------------------------------------------------------------------------------------------
28 def colorer(s
, color
="bright"):
35 "underline": "\x1b[4m"}[color
]
37 return header
+ str(s
) + trailer
39 def build_time(with_time
=True):
40 fmt
= "%Y-%m-%d %H:%M:%S" if with_time
else "%Y-%m-%d"
41 return datetime
.datetime
.fromtimestamp(time
.time()).strftime("%Y-%m-%d %H:%M:%S")
43 # SoCRegion ----------------------------------------------------------------------------------------
46 def __init__(self
, origin
=None, size
=None, mode
="rw", cached
=True):
47 self
.logger
= logging
.getLogger("SoCRegion")
57 size
= 2**log2_int(size
, False)
58 if (origin
& (size
- 1)) != 0:
59 self
.logger
.error("Origin needs to be aligned on size:")
60 self
.logger
.error(self
)
62 origin
>>= 2 # bytes to words aligned
63 size
>>= 2 # bytes to words aligned
64 return lambda a
: (a
[log2_int(size
):-1] == (origin
>> log2_int(size
)))
68 if self
.origin
is not None:
69 r
+= "Origin: {}, ".format(colorer("0x{:08x}".format(self
.origin
)))
70 if self
.size
is not None:
71 r
+= "Size: {}, ".format(colorer("0x{:08x}".format(self
.size
)))
72 r
+= "Mode: {}, ".format(colorer(self
.mode
.upper()))
73 r
+= "Cached: {}".format(colorer(self
.cached
))
77 class SoCLinkerRegion(SoCRegion
):
80 # SoCBusHandler ------------------------------------------------------------------------------------
82 class SoCBusHandler(Module
):
83 supported_standard
= ["wishbone"]
84 supported_data_width
= [32, 64]
85 supported_address_width
= [32]
87 # Creation -------------------------------------------------------------------------------------
88 def __init__(self
, standard
, data_width
=32, address_width
=32, timeout
=1e6
, reserved_regions
={}):
89 self
.logger
= logging
.getLogger("SoCBusHandler")
90 self
.logger
.info(colorer("Creating new Bus Handler...", color
="cyan"))
93 if standard
not in self
.supported_standard
:
94 self
.logger
.error("Unsupported Standard: {} supporteds: {:s}".format(
95 colorer(standard
, color
="red"),
96 colorer(", ".join(self
.supported_standard
), color
="green")))
100 if data_width
not in self
.supported_data_width
:
101 self
.logger
.error("Unsupported Data_Width: {} supporteds: {:s}".format(
102 colorer(data_width
, color
="red"),
103 colorer(", ".join(str(x
) for x
in self
.supported_data_width
), color
="green")))
106 # Check Address Width
107 if address_width
not in self
.supported_address_width
:
108 self
.logger
.error("Unsupported Address Width: {} supporteds: {:s}".format(
109 colorer(data_width
, color
="red"),
110 colorer(", ".join(str(x
) for x
in self
.supported_address_width
), color
="green")))
114 self
.standard
= standard
115 self
.data_width
= data_width
116 self
.address_width
= address_width
120 self
.timeout
= timeout
121 self
.logger
.info("{}-bit {} Bus, {}GiB Address Space.".format(
122 colorer(data_width
), colorer(standard
), colorer(2**address_width
/2**30)))
124 # Adding reserved regions
125 self
.logger
.info("Adding {} Regions...".format(colorer("reserved")))
126 for name
, region
in reserved_regions
.items():
127 if isinstance(region
, int):
128 region
= SoCRegion(origin
=region
, size
=0x1000000)
129 self
.add_region(name
, region
)
131 self
.logger
.info(colorer("Bus Handler created.", color
="cyan"))
133 # Add/Allog/Check Regions ----------------------------------------------------------------------
134 def add_region(self
, name
, region
):
136 # Check if SoCLinkerRegion
137 if isinstance(region
, SoCLinkerRegion
):
138 self
.logger
.info("FIXME: SoCLinkerRegion")
140 elif isinstance(region
, SoCRegion
):
141 # If no origin specified, allocate region.
142 if region
.origin
is None:
144 region
= self
.alloc_region(region
.size
, region
.cached
)
145 self
.regions
[name
] = region
146 # Else add region and check for overlaps.
148 self
.regions
[name
] = region
149 overlap
= self
.check_region(self
.regions
)
150 if overlap
is not None:
151 self
.logger
.error("Region overlap between {} and {}:".format(
152 colorer(overlap
[0], color
="red"),
153 colorer(overlap
[1], color
="red")))
154 self
.logger
.error(str(self
.regions
[overlap
[0]]))
155 self
.logger
.error(str(self
.regions
[overlap
[1]]))
157 self
.logger
.info("{} Region {} {}.".format(
158 colorer(name
, color
="underline"),
159 colorer("allocated" if allocated
else "added", color
="yellow" if allocated
else "green"),
162 self
.logger
.error("{} is not a supported Region".format(colorer(name
, color
="red")))
165 def alloc_region(self
, size
, cached
=True):
166 self
.logger
.info("Allocating {} Region of size {}...".format(
167 colorer("Cached" if cached
else "IO"),
168 colorer("0x{:08x}".format(size
))))
170 # Limit Search Regions
171 uncached_regions
= {}
172 for _
, region
in self
.regions
.items():
173 if region
.cached
== False:
174 uncached_regions
[name
] = region
176 search_regions
= uncached_regions
178 search_regions
= {"main": SoCRegion(origin
=0x00000000, size
=2**self
.address_width
-1)}
180 # Iterate on Search_Regions to find a Candidate
181 for _
, search_region
in search_regions
.items():
182 origin
= search_region
.origin
183 while (origin
+ size
) < (search_region
.origin
+ search_region
.size
):
184 # Create a Candicate.
185 candidate
= SoCRegion(origin
=origin
, size
=size
, cached
=cached
)
187 # Check Candidate does not overlap with allocated existing regions
188 for _
, allocated
in self
.regions
.items():
189 if self
.check_region({"0": allocated
, "1": candidate
}) is not None:
190 origin
= allocated
.origin
+ allocated
.size
194 # If no overlap, the Candidate is selected
197 self
.logger
.error("Not enough Address Space to allocate Region")
200 def check_region(self
, regions
):
202 while i
< len(regions
):
203 n0
= list(regions
.keys())[i
]
205 for n1
in list(regions
.keys())[i
+1:]:
207 if isinstance(r0
, SoCLinkerRegion
) or isinstance(r1
, SoCLinkerRegion
):
209 if r0
.origin
>= (r1
.origin
+ r1
.size
):
211 if r1
.origin
>= (r0
.origin
+ r0
.size
):
217 # Add Master/Slave -----------------------------------------------------------------------------
218 def add_master(self
, name
=None, master
=None, io_regions
={}):
220 name
= "master{:d}".format(len(self
.masters
))
221 if name
in self
.masters
.keys():
222 self
.logger
.error("{} already declared as Bus Master:".format(colorer(name
, color
="red")))
223 self
.logger
.error(self
)
225 if master
.data_width
!= self
.data_width
:
226 self
.logger
.error("{} Bus Master {} from {}-bit to {}-bit.".format(
228 colorer("converted", color
="yellow"),
229 colorer(master
.data_width
),
230 colorer(self
.data_width
)))
231 new_master
= wishbone
.Interface(data_width
=self
.data_width
)
232 self
.submodules
+= wishbone
.Converter(master
, new_master
)
234 self
.masters
[name
] = master
235 self
.logger
.info("{} {} as Bus Master.".format(colorer(name
, color
="underline"), colorer("added", color
="green")))
236 # FIXME: handle IO regions
238 def add_slave(self
, name
=None, slave
=None, region
=None):
239 no_name
= name
is None
240 no_region
= region
is None
241 if no_name
and no_region
:
242 self
.logger
.error("Please specify at least {} or {} of Bus Slave".format(
243 colorer("name", color
="red"),
244 colorer("region", color
="red")))
247 name
= "slave{:d}".format(len(self
.slaves
))
249 region
= self
.regions
.get(name
, None)
251 self
.logger
.error("Unable to find Region {}".format(colorer(name
, color
="red")))
254 self
.add_region(name
, region
)
255 if name
in self
.slaves
.keys():
256 self
.logger
.error("{} already declared as Bus Slave:".format(colorer(name
, color
="red")))
257 self
.logger
.error(self
)
259 if slave
.data_width
!= self
.data_width
:
260 self
.logger
.error("{} Bus Slave {} from {}-bit to {}-bit.".format(
262 colorer("converted", color
="yellow"),
263 colorer(slave
.data_width
),
264 colorer(self
.data_width
)))
265 new_slave
= wishbone
.Interface(data_width
=self
.data_width
)
266 self
.submodules
+= wishbone
.Converter(slave
, new_slave
)
268 self
.slaves
[name
] = slave
269 self
.logger
.info("{} {} as Bus Slave.".format(
270 colorer(name
, color
="underline"),
271 colorer("added", color
="green")))
273 # Str ------------------------------------------------------------------------------------------
275 r
= "{}-bit {} Bus, {}GiB Address Space.\n".format(
276 colorer(self
.data_width
), colorer(self
.standard
), colorer(2**self
.address_width
/2**30))
277 r
+= "Bus Regions: ({})\n".format(len(self
.regions
.keys())) if len(self
.regions
.keys()) else ""
278 for name
, region
in self
.regions
.items():
279 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
280 r
+= "Bus Masters: ({})\n".format(len(self
.masters
.keys())) if len(self
.masters
.keys()) else ""
281 for name
in self
.masters
.keys():
282 r
+= "- {}\n".format(colorer(name
, color
="underline"))
283 r
+= "Bus Slaves: ({})\n".format(len(self
.slaves
.keys())) if len(self
.slaves
.keys()) else ""
284 for name
in self
.slaves
.keys():
285 r
+= "- {}\n".format(colorer(name
, color
="underline"))
289 # SoCLocHandler --------------------------------------------------------------------------------------
291 class SoCLocHandler(Module
):
292 # Creation -------------------------------------------------------------------------------------
293 def __init__(self
, name
, n_locs
):
298 # Add ------------------------------------------------------------------------------------------
299 def add(self
, name
, n
=None, use_loc_if_exists
=False):
301 if not (use_loc_if_exists
and name
in self
.locs
.keys()):
302 if name
in self
.locs
.keys():
303 self
.logger
.error("{} {} name already used.".format(colorer(name
, "red"), self
.name
))
304 self
.logger
.error(self
)
306 if n
in self
.locs
.values():
307 self
.logger
.error("{} {} Location already used.".format(colorer(n
, "red"), self
.name
))
308 self
.logger
.error(self
)
315 self
.logger
.error("{} {} Location should be positive.".format(
316 colorer(n
, color
="red"),
320 self
.logger
.error("{} {} Location too high (Up to {}).".format(
321 colorer(n
, color
="red"),
323 colorer(self
.n_csrs
, color
="green")))
328 self
.logger
.info("{} {} {} at Location {}.".format(
329 colorer(name
, color
="underline"),
331 colorer("allocated" if allocated
else "added", color
="yellow" if allocated
else "green"),
334 # Alloc ----------------------------------------------------------------------------------------
335 def alloc(self
, name
):
336 for n
in range(self
.n_locs
):
337 if n
not in self
.locs
.values():
339 self
.logger
.error("Not enough Locations.")
340 self
.logger
.error(self
)
343 # Str ------------------------------------------------------------------------------------------
345 r
= "{} Locations: ({})\n".format(self
.name
, len(self
.locs
.keys())) if len(self
.locs
.keys()) else ""
346 for name
in self
.locs
.keys():
347 r
+= "- {}{}: {}\n".format(colorer(name
, color
="underline"), " "*(20-len(name
)), colorer(self
.locs
[name
]))
350 # SoCCSRHandler ------------------------------------------------------------------------------------
352 class SoCCSRHandler(SoCLocHandler
):
353 supported_data_width
= [8, 32]
354 supported_address_width
= [14, 15]
355 supported_alignment
= [32, 64]
356 supported_paging
= [0x800]
358 # Creation -------------------------------------------------------------------------------------
359 def __init__(self
, data_width
=32, address_width
=14, alignment
=32, paging
=0x800, reserved_csrs
={}):
360 SoCLocHandler
.__init
__(self
, "CSR", n_locs
=4*2**address_width
//paging
) # FIXME
361 self
.logger
= logging
.getLogger("SoCCSRHandler")
362 self
.logger
.info(colorer("Creating new CSR Handler...", color
="cyan"))
365 if data_width
not in self
.supported_data_width
:
366 self
.logger
.error("Unsupported data_width: {} supporteds: {:s}".format(
367 colorer(data_width
, color
="red"),
368 colorer(", ".join(str(x
) for x
in self
.supported_data_width
)), color
="green"))
371 # Check Address Width
372 if address_width
not in self
.supported_address_width
:
373 self
.logger
.error("Unsupported address_width: {} supporteds: {:s}".format(
374 colorer(address_width
, color
="red"),
375 colorer(", ".join(str(x
) for x
in self
.supported_address_width
), color
="green")))
379 if alignment
not in self
.supported_alignment
:
380 self
.logger
.error("Unsupported alignment: {} supporteds: {:s}".format(
381 colorer(alignment
, color
="red"),
382 colorer(", ".join(str(x
) for x
in self
.supported_alignment
), color
="green")))
384 if data_width
> alignment
:
385 self
.logger
.error("Alignment ({}) should be >= data_width ({})".format(
386 colorer(alignment
, color
="red"),
387 colorer(data_width
, color
="red")))
391 if paging
not in self
.supported_paging
:
392 self
.logger
.error("Unsupported paging: {} supporteds: {:s}".format(
393 colorer(paging
, color
="red"),
394 colorer(", ".join(str(x
) for x
in self
.supported_paging
), color
="green")))
398 self
.data_width
= data_width
399 self
.address_width
= address_width
400 self
.alignment
= alignment
402 self
.logger
.info("{}-bit CSR Bus, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
403 colorer(self
.data_width
),
404 colorer(2**self
.address_width
/2**10),
405 colorer(self
.paging
),
406 colorer(self
.n_locs
)))
408 # Adding reserved CSRs
409 self
.logger
.info("Adding {} CSRs...".format(colorer("reserved")))
410 for name
, n
in reserved_csrs
.items():
413 self
.logger
.info(colorer("CSR Bus Handler created.", color
="cyan"))
415 # Str ------------------------------------------------------------------------------------------
417 r
= "{}-bit CSR Bus, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
418 colorer(self
.data_width
),
419 colorer(2**self
.address_width
/2**10),
420 colorer(self
.paging
),
421 colorer(self
.n_locs
))
422 r
+= SoCLocHandler
.__str
__(self
)
426 # SoCIRQHandler ------------------------------------------------------------------------------------
428 class SoCIRQHandler(SoCLocHandler
):
429 # Creation -------------------------------------------------------------------------------------
430 def __init__(self
, n_irqs
=32, reserved_irqs
={}):
431 SoCLocHandler
.__init
__(self
, "IRQ", n_locs
=n_irqs
)
432 self
.logger
= logging
.getLogger("SoCIRQHandler")
433 self
.logger
.info(colorer("Creating new SoC IRQ Handler...", color
="cyan"))
437 self
.logger
.error("Unsupported IRQs number: {} supporteds: {:s}".format(
438 colorer(n
, color
="red"), colorer("Up to 32", color
="green")))
442 self
.logger
.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs
)))
444 # Adding reserved IRQs
445 self
.logger
.info("Adding {} IRQs...".format(colorer("reserved")))
446 for name
, n
in reserved_irqs
.items():
449 self
.logger
.info(colorer("IRQ Handler created.", color
="cyan"))
451 # Str ------------------------------------------------------------------------------------------
453 r
="IRQ Handler (up to {} Locations).\n".format(colorer(self
.n_locs
))
454 r
+= SoCLocHandler
.__str
__(self
)
458 # SoCController ------------------------------------------------------------------------------------
460 class SoCController(Module
, AutoCSR
):
462 self
._reset
= CSRStorage(1, description
="""
463 Write a ``1`` to this register to reset the SoC.""")
464 self
._scratch
= CSRStorage(32, reset
=0x12345678, description
="""
465 Use this register as a scratch space to verify that software read/write accesses
466 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
467 can be used to verify endianness.""")
468 self
._bus
_errors
= CSRStatus(32, description
="""
469 Total number of Wishbone bus errors (timeouts) since last reset.""")
474 self
.reset
= Signal()
475 self
.comb
+= self
.reset
.eq(self
._reset
.re
)
478 self
.bus_error
= Signal()
479 bus_errors
= Signal(32)
481 If(bus_errors
!= (2**len(bus_errors
)-1),
482 If(self
.bus_error
, bus_errors
.eq(bus_errors
+ 1))
484 self
.comb
+= self
._bus
_errors
.status
.eq(bus_errors
)
486 # SoC ----------------------------------------------------------------------------------------------
490 bus_standard
= "wishbone",
492 bus_address_width
= 32,
494 bus_reserved_regions
= {},
497 csr_address_width
= 14,
500 csr_reserved_csrs
= {},
503 irq_reserved_irqs
= {},
506 self
.logger
= logging
.getLogger("SoC")
507 self
.logger
.info(colorer(" __ _ __ _ __ ", color
="bright"))
508 self
.logger
.info(colorer(" / / (_) /____ | |/_/ ", color
="bright"))
509 self
.logger
.info(colorer(" / /__/ / __/ -_)> < ", color
="bright"))
510 self
.logger
.info(colorer(" /____/_/\\__/\\__/_/|_| ", color
="bright"))
511 self
.logger
.info(colorer(" Build your hardware, easily!", color
="bright"))
513 self
.logger
.info(colorer("-"*80, color
="bright"))
514 self
.logger
.info(colorer("Creating new SoC... ({})".format(build_time()), color
="cyan"))
515 self
.logger
.info(colorer("-"*80, color
="bright"))
517 # SoC Bus Handler --------------------------------------------------------------------------
518 self
.submodules
.bus
= SoCBusHandler(
519 standard
= bus_standard
,
520 data_width
= bus_data_width
,
521 address_width
= bus_address_width
,
522 timeout
= bus_timeout
,
523 reserved_regions
= bus_reserved_regions
,
526 # SoC Bus Handler --------------------------------------------------------------------------
527 self
.submodules
.csr
= SoCCSRHandler(
528 data_width
= csr_data_width
,
529 address_width
= csr_address_width
,
530 alignment
= csr_alignment
,
532 reserved_csrs
= csr_reserved_csrs
,
535 # SoC IRQ Handler --------------------------------------------------------------------------
536 self
.submodules
.irq
= SoCIRQHandler(
538 reserved_irqs
= irq_reserved_irqs
541 self
.logger
.info(colorer("-"*80, color
="bright"))
542 self
.logger
.info(colorer("Initial SoC:", color
="cyan"))
543 self
.logger
.info(colorer("-"*80, color
="bright"))
544 self
.logger
.info(self
.bus
)
545 self
.logger
.info(self
.csr
)
546 self
.logger
.info(self
.irq
)
547 self
.logger
.info(colorer("-"*80, color
="bright"))
550 # SoC Helpers ----------------------------------------------------------------------------------
551 def check_if_exists(self
, name
):
552 if hasattr(self
, name
):
553 self
.logger
.error("{} SubModule already declared.".format(colorer(name
, "red")))
556 # SoC Main components --------------------------------------------------------------------------
557 def add_ram(self
, name
, origin
, size
, contents
=[], mode
="rw"):
558 ram_bus
= wishbone
.Interface(data_width
=self
.bus
.data_width
)
559 ram
= wishbone
.SRAM(size
, bus
=ram_bus
, init
=contents
, read_only
=(mode
== "r"))
560 self
.bus
.add_slave(name
, ram
.bus
, SoCRegion(origin
=origin
, size
=size
, mode
=mode
))
561 self
.check_if_exists(name
)
562 self
.logger
.info("RAM {} {} {}.".format(
564 colorer("added", "green"),
565 self
.bus
.regions
[name
]))
566 setattr(self
.submodules
, name
, ram
)
568 def add_rom(self
, name
, origin
, size
, contents
=[]):
569 self
.add_ram(name
, origin
, size
, contents
, mode
="r")
571 def add_controller(self
, name
="ctrl"):
572 self
.check_if_exists(name
)
573 setattr(self
.submodules
, name
, SoCController())
574 self
.csr
.add(name
, use_loc_if_exists
=True)
576 def add_identifier(self
, name
="identifier", identifier
="LiteX SoC", with_build_time
=True):
577 self
.check_if_exists(name
)
579 identifier
+= " " + build_time()
580 setattr(self
.submodules
, name
, Identifier(ident
))
581 self
.csr
.add(name
+ "_mem", use_loc_if_exists
=True)
583 def add_timer(self
, name
="timer0"):
584 self
.check_if_exists(name
)
585 setattr(self
.submodules
, name
, Timer())
586 self
.csr
.add(name
, use_loc_if_exists
=True)
587 self
.irq
.add(name
, use_loc_if_exists
=True)
589 def add_csr_bridge(self
, origin
):
590 self
.submodules
.csr_bridge
= wishbone2csr
.WB2CSR(
591 bus_csr
= csr_bus
.Interface(
592 address_width
= self
.csr
.address_width
,
593 data_width
= self
.csr
.data_width
))
594 csr_size
= 2**(self
.csr
.address_width
+ 2)
595 self
.bus
.add_slave("csr", self
.csr_bridge
.wishbone
, SoCRegion(origin
=origin
, size
=csr_size
))
597 # SoC finalization -----------------------------------------------------------------------------
598 def do_finalize(self
):
599 self
.logger
.info(colorer("-"*80, color
="bright"))
600 self
.logger
.info(colorer("Finalized SoC:", color
="cyan"))
601 self
.logger
.info(colorer("-"*80, color
="bright"))
602 self
.logger
.info(self
.bus
)
603 self
.logger
.info(self
.csr
)
604 self
.logger
.info(self
.irq
)
605 self
.logger
.info(colorer("-"*80, color
="bright"))
607 # SoC Bus Interconnect ---------------------------------------------------------------------
608 bus_masters
= self
.bus
.masters
.values()
609 bus_slaves
= [(self
.bus
.regions
[n
].decoder(), s
) for n
, s
in self
.bus
.slaves
.items()]
610 if len(bus_masters
) and len(bus_slaves
):
611 self
.submodules
.bus_interconnect
= wishbone
.InterconnectShared(
612 masters
= bus_masters
,
615 timeout_cycles
= self
.bus
.timeout
)
619 # Test (FIXME: move to litex/text and improve) -----------------------------------------------------
621 if __name__
== "__main__":
622 bus
= SoCBusHandler("wishbone", reserved_regions
={
623 "rom": SoCRegion(origin
=0x00000100, size
=1024),
624 "ram": SoCRegion(size
=512),
627 bus
.add_master("cpu", None)
628 bus
.add_slave("rom", None, SoCRegion(size
=1024))
629 bus
.add_slave("ram", None, SoCRegion(size
=1024))
632 csr
= SoCCSRHandler(reserved_csrs
={"ctrl": 0, "uart": 1})
640 irq
= SoCIRQHandler(reserved_irqs
={"uart": 1})