soc: add add_ram/add_rom methods
[litex.git] / litex / soc / integration / soc.py
1 #!/usr/bin/env python3
2
3 # This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
4 # License: BSD
5
6 import logging
7 import time
8 import datetime
9
10 from migen import *
11
12 from litex.soc.interconnect.csr import *
13 from litex.soc.interconnect import wishbone
14
15 # TODO:
16 # - replace raise with exit on logging error.
17 # - add configurable CSR paging.
18 # - manage IO/Linker regions.
19
20 logging.basicConfig(level=logging.INFO)
21
22 # Helpers ------------------------------------------------------------------------------------------
23 def colorer(s, color="bright"):
24 header = {
25 "bright": "\x1b[1m",
26 "green": "\x1b[32m",
27 "cyan": "\x1b[36m",
28 "red": "\x1b[31m",
29 "yellow": "\x1b[33m",
30 "underline": "\x1b[4m"}[color]
31 trailer = "\x1b[0m"
32 return header + str(s) + trailer
33
34 def buildtime(with_time=True):
35 fmt = "%Y-%m-%d %H:%M:%S" if with_time else "%Y-%m-%d"
36 return datetime.datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d %H:%M:%S")
37
38 # SoCRegion ----------------------------------------------------------------------------------------
39
40 class SoCRegion:
41 def __init__(self, origin=None, size=None, cached=True, read_only=False):
42 self.logger = logging.getLogger("SoCRegion")
43 self.origin = origin
44 self.size = size
45 self.cached = cached
46 self.read_only = read_only
47
48 def decoder(self):
49 origin = self.origin
50 size = self.size
51 origin &= ~0x80000000
52 size = 2**log2_int(size, False)
53 if (origin & (size - 1)) != 0:
54 self.logger.error("Origin needs to be aligned on size:")
55 self.logger.error(self)
56 raise
57 origin >>= 2 # bytes to words aligned
58 size >>= 2 # bytes to words aligned
59 return lambda a: (a[log2_int(size):-1] == (origin >> log2_int(size)))
60
61 def __str__(self):
62 r = ""
63 if self.origin is not None:
64 r += "Origin: {}, ".format(colorer("0x{:08x}".format(self.origin)))
65 if self.size is not None:
66 r += "Size: {}, ".format(colorer("0x{:08x}".format(self.size)))
67 r += "Cached: {}".format(colorer(self.cached))
68 if self.read_only:
69 r += ", Read Only"
70 return r
71
72
73 class SoCLinkerRegion(SoCRegion):
74 pass
75
76 # SoCBusHandler ------------------------------------------------------------------------------------
77
78 class SoCBusHandler(Module):
79 supported_standard = ["wishbone"]
80 supported_data_width = [32, 64]
81 supported_address_width = [32]
82
83 # Creation -------------------------------------------------------------------------------------
84 def __init__(self, standard, data_width=32, address_width=32, timeout=1e6, reserved_regions={}):
85 self.logger = logging.getLogger("SoCBusHandler")
86 self.logger.info(colorer("Creating new Bus Handler...", color="cyan"))
87
88 # Check Standard
89 if standard not in self.supported_standard:
90 self.logger.error("Unsupported Standard: {} supporteds: {:s}".format(
91 colorer(standard, color="red"),
92 colorer(", ".join(self.supported_standard), color="green")))
93 raise
94
95 # Check Data Width
96 if data_width not in self.supported_data_width:
97 self.logger.error("Unsupported Data_Width: {} supporteds: {:s}".format(
98 colorer(data_width, color="red"),
99 colorer(", ".join(str(x) for x in self.supported_data_width), color="green")))
100 raise
101
102 # Check Address Width
103 if address_width not in self.supported_address_width:
104 self.logger.error("Unsupported Address Width: {} supporteds: {:s}".format(
105 colorer(data_width, color="red"),
106 colorer(", ".join(str(x) for x in self.supported_address_width), color="green")))
107 raise
108
109 # Create Bus
110 self.standard = standard
111 self.data_width = data_width
112 self.address_width = address_width
113 self.masters = {}
114 self.slaves = {}
115 self.regions = {}
116 self.timeout = timeout
117 self.logger.info("{}-bit {} Bus, {}GiB Address Space.".format(
118 colorer(data_width), colorer(standard), colorer(2**address_width/2**30)))
119
120 # Adding reserved regions
121 self.logger.info("Adding {} Regions...".format(colorer("reserved")))
122 for name, region in reserved_regions.items():
123 if isinstance(region, int):
124 region = SoCRegion(origin=region, size=0x1000000)
125 self.add_region(name, region)
126
127 self.logger.info(colorer("Bus Handler created.", color="cyan"))
128
129 # Add/Allog/Check Regions ----------------------------------------------------------------------
130 def add_region(self, name, region):
131 allocated = False
132 # Check if SoCLinkerRegion
133 if isinstance(region, SoCLinkerRegion):
134 self.logger.info("FIXME: SoCLinkerRegion")
135 # Check if SoCRegion
136 elif isinstance(region, SoCRegion):
137 # If no origin specified, allocate region.
138 if region.origin is None:
139 allocated = True
140 region = self.alloc_region(region.size, region.cached)
141 self.regions[name] = region
142 # Else add region and check for overlaps.
143 else:
144 self.regions[name] = region
145 overlap = self.check_region(self.regions)
146 if overlap is not None:
147 self.logger.error("Region overlap between {} and {}:".format(
148 colorer(overlap[0], color="red"),
149 colorer(overlap[1], color="red")))
150 self.logger.error(str(self.regions[overlap[0]]))
151 self.logger.error(str(self.regions[overlap[1]]))
152 raise
153 self.logger.info("{} Region {} {}.".format(
154 colorer(name, color="underline"),
155 colorer("allocated" if allocated else "added", color="yellow" if allocated else "green"),
156 str(region)))
157 else:
158 self.logger.error("{} is not a supported Region".format(colorer(name, color="red")))
159 raise
160
161 def alloc_region(self, size, cached=True):
162 self.logger.info("Allocating {} Region of size {}...".format(
163 colorer("Cached" if cached else "IO"),
164 colorer("0x{:08x}".format(size))))
165
166 # Limit Search Regions
167 uncached_regions = {}
168 for _, region in self.regions.items():
169 if region.cached == False:
170 uncached_regions[name] = region
171 if cached == False:
172 search_regions = uncached_regions
173 else:
174 search_regions = {"main": SoCRegion(origin=0x00000000, size=2**self.address_width-1)}
175
176 # Iterate on Search_Regions to find a Candidate
177 for _, search_region in search_regions.items():
178 origin = search_region.origin
179 while (origin + size) < (search_region.origin + search_region.size):
180 # Create a Candicate.
181 candidate = SoCRegion(origin=origin, size=size, cached=cached)
182 overlap = False
183 # Check Candidate does not overlap with allocated existing regions
184 for _, allocated in self.regions.items():
185 if self.check_region({"0": allocated, "1": candidate}) is not None:
186 origin = allocated.origin + allocated.size
187 overlap = True
188 break
189 if not overlap:
190 # If no overlap, the Candidate is selected
191 return candidate
192
193 self.logger.error("Not enough Address Space to allocate Region")
194 raise
195
196 def check_region(self, regions):
197 i = 0
198 while i < len(regions):
199 n0 = list(regions.keys())[i]
200 r0 = regions[n0]
201 for n1 in list(regions.keys())[i+1:]:
202 r1 = regions[n1]
203 if isinstance(r0, SoCLinkerRegion) or isinstance(r1, SoCLinkerRegion):
204 continue
205 if r0.origin >= (r1.origin + r1.size):
206 continue
207 if r1.origin >= (r0.origin + r0.size):
208 continue
209 return (n0, n1)
210 i += 1
211 return None
212
213 # Add Master/Slave -----------------------------------------------------------------------------
214 def add_master(self, name=None, master=None, io_regions={}):
215 if name is None:
216 name = "master{:d}".format(len(self.masters))
217 if name in self.masters.keys():
218 self.logger.error("{} already declared as Bus Master:".format(colorer(name, color="red")))
219 self.logger.error(self)
220 raise
221 if master.data_width != self.data_width:
222 self.logger.error("{} Bus Master {} from {}-bit to {}-bit.".format(
223 colorer(name),
224 colorer("converted", color="yellow"),
225 colorer(master.data_width),
226 colorer(self.data_width)))
227 new_master = wishbone.Interface(data_width=self.data_width)
228 self.submodules += wishbone.Converter(master, new_master)
229 master = new_master
230 self.masters[name] = master
231 self.logger.info("{} {} as Bus Master.".format(colorer(name, color="underline"), colorer("added", color="green")))
232 # FIXME: handle IO regions
233
234 def add_slave(self, name=None, slave=None, region=None):
235 no_name = name is None
236 no_region = region is None
237 if no_name and no_region:
238 self.logger.error("Please specify at least {} or {} of Bus Slave".format(
239 colorer("name", color="red"),
240 colorer("region", color="red")))
241 raise
242 if no_name:
243 name = "slave{:d}".format(len(self.slaves))
244 if no_region:
245 region = self.regions.get(name, None)
246 if region is None:
247 self.logger.error("Unable to find Region {}".format(colorer(name, color="red")))
248 raise
249 else:
250 self.add_region(name, region)
251 if name in self.slaves.keys():
252 self.logger.error("{} already declared as Bus Slave:".format(colorer(name, color="red")))
253 self.logger.error(self)
254 raise
255 if slave.data_width != self.data_width:
256 self.logger.error("{} Bus Slave {} from {}-bit to {}-bit.".format(
257 colorer(name),
258 colorer("converted", color="yellow"),
259 colorer(slave.data_width),
260 colorer(self.data_width)))
261 new_slave = wishbone.Interface(data_width=self.data_width)
262 self.submodules += wishbone.Converter(slave, new_slave)
263 slave = new_slave
264 self.slaves[name] = slave
265 self.logger.info("{} {} as Bus Slave.".format(
266 colorer(name, color="underline"),
267 colorer("added", color="green")))
268
269 # Str ------------------------------------------------------------------------------------------
270 def __str__(self):
271 r = "{}-bit {} Bus, {}GiB Address Space.\n".format(
272 colorer(self.data_width), colorer(self.standard), colorer(2**self.address_width/2**30))
273 r += "Bus Regions: ({})\n".format(len(self.regions.keys())) if len(self.regions.keys()) else ""
274 for name, region in self.regions.items():
275 r += colorer(name, color="underline") + " "*(20-len(name)) + ": " + str(region) + "\n"
276 r += "Bus Masters: ({})\n".format(len(self.masters.keys())) if len(self.masters.keys()) else ""
277 for name in self.masters.keys():
278 r += "- {}\n".format(colorer(name, color="underline"))
279 r += "Bus Slaves: ({})\n".format(len(self.slaves.keys())) if len(self.slaves.keys()) else ""
280 for name in self.slaves.keys():
281 r += "- {}\n".format(colorer(name, color="underline"))
282 r = r[:-1]
283 return r
284
285 # SoCLocHandler --------------------------------------------------------------------------------------
286
287 class SoCLocHandler(Module):
288 # Creation -------------------------------------------------------------------------------------
289 def __init__(self, name, n_locs):
290 self.name = name
291 self.locs = {}
292 self.n_locs = n_locs
293
294 # Add ------------------------------------------------------------------------------------------
295 def add(self, name, n=None, use_loc_if_exists=False):
296 allocated = False
297 if not (use_loc_if_exists and name in self.locs.keys()):
298 if name in self.locs.keys():
299 self.logger.error("{} {} name already used.".format(colorer(name, "red"), self.name))
300 self.logger.error(self)
301 raise
302 if n in self.locs.values():
303 self.logger.error("{} {} Location already used.".format(colorer(n, "red"), self.name))
304 self.logger.error(self)
305 raise
306 if n is None:
307 allocated = True
308 n = self.alloc(name)
309 else:
310 if n < 0:
311 self.logger.error("{} {} Location should be positive.".format(
312 colorer(n, color="red"),
313 self.name))
314 raise
315 if n > self.n_locs:
316 self.logger.error("{} {} Location too high (Up to {}).".format(
317 colorer(n, color="red"),
318 self.name,
319 colorer(self.n_csrs, color="green")))
320 raise
321 self.locs[name] = n
322 else:
323 n = self.locs[name]
324 self.logger.info("{} {} {} at Location {}.".format(
325 colorer(name, color="underline"),
326 self.name,
327 colorer("allocated" if allocated else "added", color="yellow" if allocated else "green"),
328 colorer(n)))
329
330 # Alloc ----------------------------------------------------------------------------------------
331 def alloc(self, name):
332 for n in range(self.n_locs):
333 if n not in self.locs.values():
334 return n
335 self.logger.error("Not enough Locations.")
336 self.logger.error(self)
337 raise
338
339 # Str ------------------------------------------------------------------------------------------
340 def __str__(self):
341 r = "{} Locations: ({})\n".format(self.name, len(self.locs.keys())) if len(self.locs.keys()) else ""
342 for name in self.locs.keys():
343 r += "- {}{}: {}\n".format(colorer(name, color="underline"), " "*(20-len(name)), colorer(self.locs[name]))
344 return r
345
346 # SoCCSRHandler ------------------------------------------------------------------------------------
347
348 class SoCCSRHandler(SoCLocHandler):
349 supported_data_width = [8, 32]
350 supported_address_width = [14, 15]
351 supported_alignment = [32, 64]
352 supported_paging = [0x800]
353
354 # Creation -------------------------------------------------------------------------------------
355 def __init__(self, data_width=32, address_width=14, alignment=32, paging=0x800, reserved_csrs={}):
356 SoCLocHandler.__init__(self, "CSR", n_locs=4*2**address_width//paging) # FIXME
357 self.logger = logging.getLogger("SoCCSRHandler")
358 self.logger.info(colorer("Creating new CSR Handler...", color="cyan"))
359
360 # Check Data Width
361 if data_width not in self.supported_data_width:
362 self.logger.error("Unsupported data_width: {} supporteds: {:s}".format(
363 colorer(data_width, color="red"),
364 colorer(", ".join(str(x) for x in self.supported_data_width)), color="green"))
365 raise
366
367 # Check Address Width
368 if address_width not in self.supported_address_width:
369 self.logger.error("Unsupported address_width: {} supporteds: {:s}".format(
370 colorer(address_width, color="red"),
371 colorer(", ".join(str(x) for x in self.supported_address_width), color="green")))
372 raise
373
374 # Check Alignment
375 if alignment not in self.supported_alignment:
376 self.logger.error("Unsupported alignment: {} supporteds: {:s}".format(
377 colorer(alignment, color="red"),
378 colorer(", ".join(str(x) for x in self.supported_alignment), color="green")))
379 raise
380
381 # Check Paging
382 if paging not in self.supported_paging:
383 self.logger.error("Unsupported paging: {} supporteds: {:s}".format(
384 colorer(paging, color="red"),
385 colorer(", ".join(str(x) for x in self.supported_paging), color="green")))
386 raise
387
388 # Create CSR Handler
389 self.data_width = data_width
390 self.address_width = address_width
391 self.alignment = alignment
392 self.paging = paging
393 self.logger.info("{}-bit CSR Bus, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
394 colorer(self.data_width),
395 colorer(2**self.address_width/2**10),
396 colorer(self.paging),
397 colorer(self.n_locs)))
398
399 # Adding reserved CSRs
400 self.logger.info("Adding {} CSRs...".format(colorer("reserved")))
401 for name, n in reserved_csrs.items():
402 self.add(name, n)
403
404 self.logger.info(colorer("CSR Bus Handler created.", color="cyan"))
405
406 # Str ------------------------------------------------------------------------------------------
407 def __str__(self):
408 r = "{}-bit CSR Bus, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
409 colorer(self.data_width),
410 colorer(2**self.address_width/2**10),
411 colorer(self.paging),
412 colorer(self.n_locs))
413 r += SoCLocHandler.__str__(self)
414 r = r[:-1]
415 return r
416
417 # SoCIRQHandler ------------------------------------------------------------------------------------
418
419 class SoCIRQHandler(SoCLocHandler):
420 # Creation -------------------------------------------------------------------------------------
421 def __init__(self, n_irqs=32, reserved_irqs={}):
422 SoCLocHandler.__init__(self, "IRQ", n_locs=n_irqs)
423 self.logger = logging.getLogger("SoCIRQHandler")
424 self.logger.info(colorer("Creating new SoC IRQ Handler...", color="cyan"))
425
426 # Check IRQ Number
427 if n_irqs > 32:
428 self.logger.error("Unsupported IRQs number: {} supporteds: {:s}".format(
429 colorer(n, color="red"), colorer("Up to 32", color="green")))
430 raise
431
432 # Create IRQ Handler
433 self.logger.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs)))
434
435 # Adding reserved IRQs
436 self.logger.info("Adding {} IRQs...".format(colorer("reserved")))
437 for name, n in reserved_irqs.items():
438 self.add(name, n)
439
440 self.logger.info(colorer("IRQ Handler created.", color="cyan"))
441
442 # Str ------------------------------------------------------------------------------------------
443 def __str__(self):
444 r ="IRQ Handler (up to {} Locations).\n".format(colorer(self.n_locs))
445 r += SoCLocHandler.__str__(self)
446 r = r[:-1]
447 return r
448
449 # SoCController ------------------------------------------------------------------------------------
450
451 class SoCController(Module, AutoCSR):
452 def __init__(self):
453 self._reset = CSRStorage(1, description="""
454 Write a ``1`` to this register to reset the SoC.""")
455 self._scratch = CSRStorage(32, reset=0x12345678, description="""
456 Use this register as a scratch space to verify that software read/write accesses
457 to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
458 can be used to verify endianness.""")
459 self._bus_errors = CSRStatus(32, description="""
460 Total number of Wishbone bus errors (timeouts) since last reset.""")
461
462 # # #
463
464 # Reset
465 self.reset = Signal()
466 self.comb += self.reset.eq(self._reset.re)
467
468 # Bus errors
469 self.bus_error = Signal()
470 bus_errors = Signal(32)
471 self.sync += \
472 If(bus_errors != (2**len(bus_errors)-1),
473 If(self.bus_error, bus_errors.eq(bus_errors + 1))
474 )
475 self.comb += self._bus_errors.status.eq(bus_errors)
476
477 # SoC ----------------------------------------------------------------------------------------------
478
479 class SoC(Module):
480 def __init__(self,
481 bus_standard = "wishbone",
482 bus_data_width = 32,
483 bus_address_width = 32,
484 bus_timeout = 1e6,
485 bus_reserved_regions = {},
486
487 csr_data_width = 32,
488 csr_address_width = 14,
489 csr_alignment = 32,
490 csr_paging = 0x800,
491 csr_reserved_csrs = {},
492
493 irq_n_irqs = 32,
494 irq_reserved_irqs = {},
495 ):
496
497 self.logger = logging.getLogger("SoC")
498 self.logger.info(colorer(" __ _ __ _ __ ", color="bright"))
499 self.logger.info(colorer(" / / (_) /____ | |/_/ ", color="bright"))
500 self.logger.info(colorer(" / /__/ / __/ -_)> < ", color="bright"))
501 self.logger.info(colorer(" /____/_/\\__/\\__/_/|_| ", color="bright"))
502 self.logger.info(colorer(" Build your hardware, easily!", color="bright"))
503
504 self.logger.info(colorer("-"*80, color="bright"))
505 self.logger.info(colorer("Creating new SoC... ({})".format(buildtime()), color="cyan"))
506 self.logger.info(colorer("-"*80, color="bright"))
507
508 # SoC Bus Handler --------------------------------------------------------------------------
509 self.submodules.bus = SoCBusHandler(
510 standard = bus_standard,
511 data_width = bus_data_width,
512 address_width = bus_address_width,
513 timeout = bus_timeout,
514 reserved_regions = bus_reserved_regions,
515 )
516
517 # SoC Bus Handler --------------------------------------------------------------------------
518 self.submodules.csr = SoCCSRHandler(
519 data_width = csr_data_width,
520 address_width = csr_address_width,
521 alignment = csr_alignment,
522 paging = csr_paging,
523 reserved_csrs = csr_reserved_csrs,
524 )
525
526 # SoC IRQ Handler --------------------------------------------------------------------------
527 self.submodules.irq = SoCIRQHandler(
528 n_irqs = irq_n_irqs,
529 reserved_irqs = irq_reserved_irqs
530 )
531
532 self.logger.info(colorer("-"*80, color="bright"))
533 self.logger.info(colorer("Initial SoC:", color="cyan"))
534 self.logger.info(colorer("-"*80, color="bright"))
535 self.logger.info(self.bus)
536 self.logger.info(self.csr)
537 self.logger.info(self.irq)
538 self.logger.info(colorer("-"*80, color="bright"))
539
540
541 # SoC main components --------------------------------------------------------------------------
542 def add_ram(self, name, origin, size, contents=[], read_only=False):
543 ram_bus = wishbone.Interface(data_width=self.bus.data_width)
544 ram = wishbone.SRAM(size, bus=ram_bus, init=contents, read_only=read_only)
545 self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, read_only=read_only))
546 if hasattr(self, name):
547 self.logger.error("{} name already used.".format(colorer(name, "red")))
548 raise
549 self.logger.info("RAM {} {} {}.".format(
550 colorer(name),
551 colorer("added", "green"),
552 self.bus.regions[name]))
553 setattr(self.submodules, name, ram)
554
555 def add_rom(self, name, origin, size, contents=[]):
556 self.add_ram(name, origin, size, contents, read_only=True)
557
558 # SoC finalization -----------------------------------------------------------------------------
559 def do_finalize(self):
560 self.logger.info(colorer("-"*80, color="bright"))
561 self.logger.info(colorer("Finalized SoC:", color="cyan"))
562 self.logger.info(colorer("-"*80, color="bright"))
563 self.logger.info(self.bus)
564 self.logger.info(self.csr)
565 self.logger.info(self.irq)
566 self.logger.info(colorer("-"*80, color="bright"))
567
568 # SoC Bus Interconnect ---------------------------------------------------------------------
569 bus_masters = self.bus.masters.values()
570 bus_slaves = [(self.bus.regions[n].decoder(), s) for n, s in self.bus.slaves.items()]
571 if len(bus_masters) and len(bus_slaves):
572 self.submodules.bus_interconnect = wishbone.InterconnectShared(
573 masters = bus_masters,
574 slaves = bus_slaves,
575 register = True,
576 timeout_cycles = self.bus.timeout)
577
578 #exit()
579
580 # Test (FIXME: move to litex/text and improve) -----------------------------------------------------
581
582 if __name__ == "__main__":
583 bus = SoCBusHandler("wishbone", reserved_regions={
584 "rom": SoCRegion(origin=0x00000100, size=1024),
585 "ram": SoCRegion(size=512),
586 }
587 )
588 bus.add_master("cpu", None)
589 bus.add_slave("rom", None, SoCRegion(size=1024))
590 bus.add_slave("ram", None, SoCRegion(size=1024))
591
592
593 csr = SoCCSRHandler(reserved_csrs={"ctrl": 0, "uart": 1})
594 csr.add("csr0")
595 csr.add("csr1", 0)
596 #csr.add("csr2", 46)
597 csr.add("csr3", -1)
598 print(bus)
599 print(csr)
600
601 irq = SoCIRQHandler(reserved_irqs={"uart": 1})
602
603 soc = SoC()