3 # This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
12 from litex
.soc
.interconnect
import wishbone
15 # - replace raise with exit on logging error.
16 # - add configurable CSR paging.
17 # - manage IO/Linker regions.
19 logging
.basicConfig(level
=logging
.INFO
)
21 # Helpers ------------------------------------------------------------------------------------------
22 def colorer(s
, color
="bright"):
29 "underline": "\x1b[4m"}[color
]
31 return header
+ str(s
) + trailer
33 def buildtime(with_time
=True):
34 fmt
= "%Y-%m-%d %H:%M:%S" if with_time
else "%Y-%m-%d"
35 return datetime
.datetime
.fromtimestamp(time
.time()).strftime("%Y-%m-%d %H:%M:%S")
37 # SoCRegion ----------------------------------------------------------------------------------------
40 def __init__(self
, origin
=None, size
=None, cached
=True):
41 self
.logger
= logging
.getLogger("SoCRegion")
50 size
= 2**log2_int(size
, False)
51 if (origin
& (size
- 1)) != 0:
52 self
.logger
.error("Origin needs to be aligned on size:")
53 self
.logger
.error(self
)
55 origin
>>= 2 # bytes to words aligned
56 size
>>= 2 # bytes to words aligned
57 return lambda a
: (a
[log2_int(size
):-1] == (origin
>> log2_int(size
)))
61 if self
.origin
is not None:
62 r
+= "Origin: {}, ".format(colorer("0x{:08x}".format(self
.origin
)))
63 if self
.size
is not None:
64 r
+= "Size: {}, ".format(colorer("0x{:08x}".format(self
.size
)))
65 r
+= "Cached: {}".format(colorer(self
.cached
))
69 class SoCLinkerRegion(SoCRegion
):
72 # SoCBusHandler ------------------------------------------------------------------------------------
75 supported_standard
= ["wishbone"]
76 supported_data_width
= [32, 64]
77 supported_address_width
= [32]
79 # Creation -------------------------------------------------------------------------------------
80 def __init__(self
, standard
, data_width
=32, address_width
=32, timeout
=1e6
, reserved_regions
={}):
81 self
.logger
= logging
.getLogger("SoCBusHandler")
82 self
.logger
.info(colorer("Creating new Bus Handler...", color
="cyan"))
85 if standard
not in self
.supported_standard
:
86 self
.logger
.error("Unsupported Standard: {} supporteds: {:s}".format(
87 colorer(standard
, color
="red"),
88 colorer(", ".join(self
.supported_standard
), color
="green")))
92 if data_width
not in self
.supported_data_width
:
93 self
.logger
.error("Unsupported Data_Width: {} supporteds: {:s}".format(
94 colorer(data_width
, color
="red"),
95 colorer(", ".join(str(x
) for x
in self
.supported_data_width
), color
="green")))
99 if address_width
not in self
.supported_address_width
:
100 self
.logger
.error("Unsupported Address Width: {} supporteds: {:s}".format(
101 colorer(data_width
, color
="red"),
102 colorer(", ".join(str(x
) for x
in self
.supported_address_width
), color
="green")))
106 self
.standard
= standard
107 self
.data_width
= data_width
108 self
.address_width
= address_width
112 self
.timeout
= timeout
113 self
.logger
.info("{}-bit {} Bus, {}GiB Address Space.".format(
114 colorer(data_width
), colorer(standard
), colorer(2**address_width
/2**30)))
116 # Adding reserved regions
117 self
.logger
.info("Adding {} Regions...".format(colorer("reserved")))
118 for name
, region
in reserved_regions
.items():
119 if isinstance(region
, int):
120 region
= SoCRegion(origin
=region
, size
=0x1000000)
121 self
.add_region(name
, region
)
123 self
.logger
.info(colorer("Bus Handler created.", color
="cyan"))
125 # Add/Allog/Check Regions ----------------------------------------------------------------------
126 def add_region(self
, name
, region
):
128 # Check if SoCLinkerRegion
129 if isinstance(region
, SoCLinkerRegion
):
130 self
.logger
.info("FIXME: SoCLinkerRegion")
132 elif isinstance(region
, SoCRegion
):
133 # If no origin specified, allocate region.
134 if region
.origin
is None:
136 region
= self
.alloc_region(region
.size
, region
.cached
)
137 self
.regions
[name
] = region
138 # Else add region and check for overlaps.
140 self
.regions
[name
] = region
141 overlap
= self
.check_region(self
.regions
)
142 if overlap
is not None:
143 self
.logger
.error("Region overlap between {} and {}:".format(
144 colorer(overlap
[0], color
="red"),
145 colorer(overlap
[1], color
="red")))
146 self
.logger
.error(str(self
.regions
[overlap
[0]]))
147 self
.logger
.error(str(self
.regions
[overlap
[1]]))
149 self
.logger
.info("{} Region {} {}.".format(
150 colorer(name
, color
="underline"),
151 colorer("allocated" if allocated
else "added", color
="yellow" if allocated
else "green"),
154 self
.logger
.error("{} is not a supported Region".format(colorer(name
, color
="red")))
157 def alloc_region(self
, size
, cached
=True):
158 self
.logger
.info("Allocating {} Region of size {}...".format(
159 colorer("Cached" if cached
else "IO"),
160 colorer("0x{:08x}".format(size
))))
162 # Limit Search Regions
163 uncached_regions
= {}
164 for _
, region
in self
.regions
.items():
165 if region
.cached
== False:
166 uncached_regions
[name
] = region
168 search_regions
= uncached_regions
170 search_regions
= {"main": SoCRegion(origin
=0x00000000, size
=2**self
.address_width
-1)}
172 # Iterate on Search_Regions to find a Candidate
173 for _
, search_region
in search_regions
.items():
174 origin
= search_region
.origin
175 while (origin
+ size
) < (search_region
.origin
+ search_region
.size
):
176 # Create a Candicate.
177 candidate
= SoCRegion(origin
=origin
, size
=size
, cached
=cached
)
179 # Check Candidate does not overlap with allocated existing regions
180 for _
, allocated
in self
.regions
.items():
181 if self
.check_region({"0": allocated
, "1": candidate
}) is not None:
182 origin
= allocated
.origin
+ allocated
.size
186 # If no overlap, the Candidate is selected
189 self
.logger
.error("Not enough Address Space to allocate Region")
192 def check_region(self
, regions
):
194 while i
< len(regions
):
195 n0
= list(regions
.keys())[i
]
197 for n1
in list(regions
.keys())[i
+1:]:
199 if isinstance(r0
, SoCLinkerRegion
) or isinstance(r1
, SoCLinkerRegion
):
201 if r0
.origin
>= (r1
.origin
+ r1
.size
):
203 if r1
.origin
>= (r0
.origin
+ r0
.size
):
209 # Add Master/Slave -----------------------------------------------------------------------------
210 def add_master(self
, name
=None, master
=None, io_regions
={}):
212 name
= "master{:d}".format(len(self
.masters
))
213 if name
in self
.masters
.keys():
214 self
.logger
.error("{} already declared as Bus Master:".format(colorer(name
, color
="red")))
215 self
.logger
.error(self
)
217 self
.masters
[name
] = master
218 self
.logger
.info("{} {} as Bus Master.".format(colorer(name
, color
="underline"), colorer("added", color
="green")))
219 # FIXME: handle IO regions
221 def add_slave(self
, name
=None, slave
=None, region
=None):
222 no_name
= name
is None
223 no_region
= region
is None
224 if no_name
and no_region
:
225 self
.logger
.error("Please specify at least {} or {} of Bus Slave".format(
226 colorer("name", color
="red"),
227 colorer("region", color
="red")))
230 name
= "slave{:d}".format(len(self
.slaves
))
232 region
= self
.regions
.get(name
, None)
234 self
.logger
.error("Unable to find Region {}".format(colorer(name
, color
="red")))
237 self
.add_region(name
, region
)
238 if name
in self
.slaves
.keys():
239 self
.logger
.error("{} already declared as Bus Slave:".format(colorer(name
, color
="red")))
240 self
.logger
.error(self
)
242 self
.slaves
[name
] = slave
243 self
.logger
.info("{} {} as Bus Slave.".format(
244 colorer(name
, color
="underline"),
245 colorer("added", color
="green")))
247 # Str ------------------------------------------------------------------------------------------
249 r
= "{}-bit {} Bus, {}GiB Address Space.\n".format(
250 colorer(self
.data_width
), colorer(self
.standard
), colorer(2**self
.address_width
/2**30))
251 r
+= "Bus Regions: ({})\n".format(len(self
.regions
.keys())) if len(self
.regions
.keys()) else ""
252 for name
, region
in self
.regions
.items():
253 r
+= colorer(name
, color
="underline") + " "*(20-len(name
)) + ": " + str(region
) + "\n"
254 r
+= "Bus Masters: ({})\n".format(len(self
.masters
.keys())) if len(self
.masters
.keys()) else ""
255 for name
in self
.masters
.keys():
256 r
+= "- {}\n".format(colorer(name
, color
="underline"))
257 r
+= "Bus Slaves: ({})\n".format(len(self
.slaves
.keys())) if len(self
.slaves
.keys()) else ""
258 for name
in self
.slaves
.keys():
259 r
+= "- {}\n".format(colorer(name
, color
="underline"))
263 # SoCLocHandler --------------------------------------------------------------------------------------
266 # Creation -------------------------------------------------------------------------------------
267 def __init__(self
, name
, n_locs
):
272 # Add ------------------------------------------------------------------------------------------
273 def add(self
, name
, n
=None, use_loc_if_exists
=False):
275 if not (use_loc_if_exists
and name
in self
.locs
.keys()):
276 if name
in self
.locs
.keys():
277 self
.logger
.error("{} {} name already used.".format(colorer(name
, "red"), self
.name
))
278 self
.logger
.error(self
)
280 if n
in self
.locs
.values():
281 self
.logger
.error("{} {} Location already used.".format(colorer(n
, "red"), self
.name
))
282 self
.logger
.error(self
)
289 self
.logger
.error("{} {} Location should be positive.".format(
290 colorer(n
, color
="red"),
294 self
.logger
.error("{} {} Location too high (Up to {}).".format(
295 colorer(n
, color
="red"),
297 colorer(self
.n_csrs
, color
="green")))
302 self
.logger
.info("{} {} {} at Location {}.".format(
303 colorer(name
, color
="underline"),
305 colorer("allocated" if allocated
else "added", color
="yellow" if allocated
else "green"),
308 # Alloc ----------------------------------------------------------------------------------------
309 def alloc(self
, name
):
310 for n
in range(self
.n_locs
):
311 if n
not in self
.locs
.values():
313 self
.logger
.error("Not enough Locations.")
314 self
.logger
.error(self
)
317 # Str ------------------------------------------------------------------------------------------
319 r
= "{} Locations: ({})\n".format(self
.name
, len(self
.locs
.keys())) if len(self
.locs
.keys()) else ""
320 for name
in self
.locs
.keys():
321 r
+= "- {}{}: {}\n".format(colorer(name
, color
="underline"), " "*(20-len(name
)), colorer(self
.locs
[name
]))
324 # SoCCSRHandler ------------------------------------------------------------------------------------
326 class SoCCSRHandler(SoCLocHandler
):
327 supported_data_width
= [8, 32]
328 supported_address_width
= [14, 15]
329 supported_alignment
= [32, 64]
330 supported_paging
= [0x800]
332 # Creation -------------------------------------------------------------------------------------
333 def __init__(self
, data_width
=32, address_width
=14, alignment
=32, paging
=0x800, reserved_csrs
={}):
334 SoCLocHandler
.__init
__(self
, "CSR", n_locs
=4*2**address_width
//paging
) # FIXME
335 self
.logger
= logging
.getLogger("SoCCSRHandler")
336 self
.logger
.info(colorer("Creating new CSR Handler...", color
="cyan"))
339 if data_width
not in self
.supported_data_width
:
340 self
.logger
.error("Unsupported data_width: {} supporteds: {:s}".format(
341 colorer(data_width
, color
="red"),
342 colorer(", ".join(str(x
) for x
in self
.supported_data_width
)), color
="green"))
345 # Check Address Width
346 if address_width
not in self
.supported_address_width
:
347 self
.logger
.error("Unsupported address_width: {} supporteds: {:s}".format(
348 colorer(address_width
, color
="red"),
349 colorer(", ".join(str(x
) for x
in self
.supported_address_width
), color
="green")))
353 if alignment
not in self
.supported_alignment
:
354 self
.logger
.error("Unsupported alignment: {} supporteds: {:s}".format(
355 colorer(alignment
, color
="red"),
356 colorer(", ".join(str(x
) for x
in self
.supported_alignment
), color
="green")))
360 if paging
not in self
.supported_paging
:
361 self
.logger
.error("Unsupported paging: {} supporteds: {:s}".format(
362 colorer(paging
, color
="red"),
363 colorer(", ".join(str(x
) for x
in self
.supported_paging
), color
="green")))
367 self
.data_width
= data_width
368 self
.address_width
= address_width
369 self
.alignment
= alignment
371 self
.logger
.info("{}-bit CSR Bus, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
372 colorer(self
.data_width
),
373 colorer(2**self
.address_width
/2**10),
374 colorer(self
.paging
),
375 colorer(self
.n_locs
)))
377 # Adding reserved CSRs
378 self
.logger
.info("Adding {} CSRs...".format(colorer("reserved")))
379 for name
, n
in reserved_csrs
.items():
382 self
.logger
.info(colorer("CSR Bus Handler created.", color
="cyan"))
384 # Str ------------------------------------------------------------------------------------------
386 r
= "{}-bit CSR Bus, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
387 colorer(self
.data_width
),
388 colorer(2**self
.address_width
/2**10),
389 colorer(self
.paging
),
390 colorer(self
.n_locs
))
391 r
+= SoCLocHandler
.__str
__(self
)
395 # SoCIRQHandler ------------------------------------------------------------------------------------
397 class SoCIRQHandler(SoCLocHandler
):
398 # Creation -------------------------------------------------------------------------------------
399 def __init__(self
, n_irqs
=32, reserved_irqs
={}):
400 SoCLocHandler
.__init
__(self
, "IRQ", n_locs
=n_irqs
)
401 self
.logger
= logging
.getLogger("SoCIRQHandler")
402 self
.logger
.info(colorer("Creating new SoC IRQ Handler...", color
="cyan"))
406 self
.logger
.error("Unsupported IRQs number: {} supporteds: {:s}".format(
407 colorer(n
, color
="red"), colorer("Up to 32", color
="green")))
411 self
.logger
.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs
)))
413 # Adding reserved IRQs
414 self
.logger
.info("Adding {} IRQs...".format(colorer("reserved")))
415 for name
, n
in reserved_irqs
.items():
418 self
.logger
.info(colorer("IRQ Handler created.", color
="cyan"))
420 # Str ------------------------------------------------------------------------------------------
422 r
="IRQ Handler (up to {} Locations).".format(colorer(self
.n_locs
))
423 r
+= SoCLocHandler
.__str
__(self
)
427 # SoC ----------------------------------------------------------------------------------------------
431 bus_standard
= "wishbone",
433 bus_address_width
= 32,
435 bus_reserved_regions
= {},
438 csr_address_width
= 14,
441 csr_reserved_csrs
= {},
444 irq_reserved_irqs
= {},
447 self
.logger
= logging
.getLogger("SoC")
448 self
.logger
.info(colorer(" __ _ __ _ __ ", color
="bright"))
449 self
.logger
.info(colorer(" / / (_) /____ | |/_/ ", color
="bright"))
450 self
.logger
.info(colorer(" / /__/ / __/ -_)> < ", color
="bright"))
451 self
.logger
.info(colorer(" /____/_/\\__/\\__/_/|_| ", color
="bright"))
452 self
.logger
.info(colorer(" Build your hardware, easily!", color
="bright"))
454 self
.logger
.info(colorer("-"*80, color
="bright"))
455 self
.logger
.info(colorer("Creating new SoC... ({})".format(buildtime()), color
="cyan"))
456 self
.logger
.info(colorer("-"*80, color
="bright"))
458 # SoC Bus Handler --------------------------------------------------------------------------
459 self
.bus
= SoCBusHandler(
460 standard
= bus_standard
,
461 data_width
= bus_data_width
,
462 address_width
= bus_address_width
,
463 timeout
= bus_timeout
,
464 reserved_regions
= bus_reserved_regions
,
467 # SoC Bus Handler --------------------------------------------------------------------------
468 self
.csr
= SoCCSRHandler(
469 data_width
= csr_data_width
,
470 address_width
= csr_address_width
,
471 alignment
= csr_alignment
,
473 reserved_csrs
= csr_reserved_csrs
,
476 # SoC IRQ Handler --------------------------------------------------------------------------
477 self
.irq
= SoCIRQHandler(
479 reserved_irqs
= irq_reserved_irqs
482 self
.logger
.info(colorer("-"*80, color
="bright"))
483 self
.logger
.info(colorer("Initial SoC:", color
="cyan"))
484 self
.logger
.info(colorer("-"*80, color
="bright"))
485 self
.logger
.info(self
.bus
)
486 self
.logger
.info(self
.csr
)
487 self
.logger
.info(self
.irq
)
488 self
.logger
.info(colorer("-"*80, color
="bright"))
491 def do_finalize(self
):
492 self
.logger
.info(colorer("-"*80, color
="bright"))
493 self
.logger
.info(colorer("Finalized SoC:", color
="cyan"))
494 self
.logger
.info(colorer("-"*80, color
="bright"))
495 self
.logger
.info(self
.bus
)
496 self
.logger
.info(self
.csr
)
497 self
.logger
.info(self
.irq
)
498 self
.logger
.info(colorer("-"*80, color
="bright"))
500 # SoC Bus Interconnect ---------------------------------------------------------------------
501 bus_masters
= self
.bus
.masters
.values()
502 bus_slaves
= [(self
.bus
.regions
[n
].decoder(), s
) for n
, s
in self
.bus
.slaves
.items()]
503 if len(bus_masters
) and len(bus_slaves
):
504 self
.submodules
.bus_interconnect
= wishbone
.InterconnectShared(
505 masters
= bus_masters
,
508 timeout_cycles
= self
.bus
.timeout
)
512 # Test (FIXME: move to litex/text and improve) -----------------------------------------------------
514 if __name__
== "__main__":
515 bus
= SoCBusHandler("wishbone", reserved_regions
={
516 "rom": SoCRegion(origin
=0x00000100, size
=1024),
517 "ram": SoCRegion(size
=512),
520 bus
.add_master("cpu", None)
521 bus
.add_slave("rom", None, SoCRegion(size
=1024))
522 bus
.add_slave("ram", None, SoCRegion(size
=1024))
525 csr
= SoCCSRHandler(reserved_csrs
={"ctrl": 0, "uart": 1})
533 irq
= SoCIRQHandler(reserved_irqs
={"uart": 1})