soc.py 58.8 KB
Newer Older
1 2
# This file is Copyright (c) 2014-2020 Florent Kermarrec <florent@enjoy-digital.fr>
# This file is Copyright (c) 2013-2014 Sebastien Bourdeauducq <sb@m-labs.hk>
Florent Kermarrec's avatar
Florent Kermarrec committed
3
# This file is Copyright (c) 2019 Gabriel L. Somlo <somlo@cmu.edu>
4 5 6 7 8
# License: BSD

import logging
import time
import datetime
9
from math import log2, ceil
10 11 12

from migen import *

Florent Kermarrec's avatar
Florent Kermarrec committed
13
from litex.soc.cores import cpu
14 15
from litex.soc.cores.identifier import Identifier
from litex.soc.cores.timer import Timer
16
from litex.soc.cores.spi_flash import SpiFlash
17
from litex.soc.cores.spi import SPIMaster
18

19
from litex.soc.interconnect.csr import *
20
from litex.soc.interconnect import csr_bus
21
from litex.soc.interconnect import stream
22
from litex.soc.interconnect import wishbone
23
from litex.soc.interconnect import axi
24 25 26 27

logging.basicConfig(level=logging.INFO)

# Helpers ------------------------------------------------------------------------------------------
28 29 30 31

def auto_int(x):
    return int(x, 0)

32 33 34 35 36 37 38 39 40 41 42
def colorer(s, color="bright"):
    header  = {
        "bright": "\x1b[1m",
        "green":  "\x1b[32m",
        "cyan":   "\x1b[36m",
        "red":    "\x1b[31m",
        "yellow": "\x1b[33m",
        "underline": "\x1b[4m"}[color]
    trailer = "\x1b[0m"
    return header + str(s) + trailer

43
def build_time(with_time=True):
44
    fmt = "%Y-%m-%d %H:%M:%S" if with_time else "%Y-%m-%d"
45
    return datetime.datetime.fromtimestamp(time.time()).strftime(fmt)
46

47 48 49 50 51
# SoCConstant --------------------------------------------------------------------------------------

def SoCConstant(value):
    return value

52 53 54
# SoCRegion ----------------------------------------------------------------------------------------

class SoCRegion:
55
    def __init__(self, origin=None, size=None, mode="rw", cached=True, linker=False):
56 57 58
        self.logger    = logging.getLogger("SoCRegion")
        self.origin    = origin
        self.size      = size
59 60 61 62 63 64
        if size != 2**log2_int(size, False):
            self.logger.info("Region size {} internally from {} to {}.".format(
                colorer("rounded", color="cyan"),
                colorer("0x{:08x}".format(size)),
                colorer("0x{:08x}".format(2**log2_int(size, False)))))
        self.size_pow2 = 2**log2_int(size, False)
65
        self.mode      = mode
66
        self.cached    = cached
67
        self.linker    = linker
68

69
    def decoder(self, bus):
70
        origin = self.origin
71
        size   = self.size_pow2
72 73 74 75
        if (origin & (size - 1)) != 0:
            self.logger.error("Origin needs to be aligned on size:")
            self.logger.error(self)
            raise
76 77 78
        origin >>= int(log2(bus.data_width//8)) # bytes to words aligned
        size   >>= int(log2(bus.data_width//8)) # bytes to words aligned
        return lambda a: (a[log2_int(size):] == (origin >> log2_int(size)))
79 80 81 82 83 84 85

    def __str__(self):
        r = ""
        if self.origin is not None:
            r += "Origin: {}, ".format(colorer("0x{:08x}".format(self.origin)))
        if self.size is not None:
            r += "Size: {}, ".format(colorer("0x{:08x}".format(self.size)))
86
        r += "Mode: {}, ".format(colorer(self.mode.upper()))
87 88
        r += "Cached: {} ".format(colorer(self.cached))
        r += "Linker: {}".format(colorer(self.linker))
89 90
        return r

91
class SoCIORegion(SoCRegion): pass
92

93 94 95 96 97 98 99 100
# SoCCSRRegion -------------------------------------------------------------------------------------

class SoCCSRRegion:
    def __init__(self, origin, busword, obj):
        self.origin  = origin
        self.busword = busword
        self.obj     = obj

101
# SoCBusHandler ------------------------------------------------------------------------------------
102

103
class SoCBusHandler(Module):
104 105 106 107 108 109
    supported_standard      = ["wishbone"]
    supported_data_width    = [32, 64]
    supported_address_width = [32]

    # Creation -------------------------------------------------------------------------------------
    def __init__(self, standard, data_width=32, address_width=32, timeout=1e6, reserved_regions={}):
110
        self.logger = logging.getLogger("SoCBusHandler")
111
        self.logger.info("Creating Bus Handler...")
112 113 114

        # Check Standard
        if standard not in self.supported_standard:
115 116 117 118
            self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
                colorer("Bus standard", color="red"),
                colorer(standard),
                colorer(", ".join(self.supported_standard))))
119 120 121 122
            raise

        # Check Data Width
        if data_width not in self.supported_data_width:
123 124 125 126
            self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
                colorer("Data Width", color="red"),
                colorer(data_width),
                colorer(", ".join(str(x) for x in self.supported_data_width))))
127 128 129 130
            raise

        # Check Address Width
        if address_width not in self.supported_address_width:
131 132 133 134
            self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
                colorer("Address Width", color="red"),
                colorer(data_width),
                colorer(", ".join(str(x) for x in self.supported_address_width))))
135 136 137 138 139 140 141 142 143
            raise

        # Create Bus
        self.standard      = standard
        self.data_width    = data_width
        self.address_width = address_width
        self.masters       = {}
        self.slaves        = {}
        self.regions       = {}
144
        self.io_regions    = {}
145 146 147 148 149
        self.timeout       = timeout
        self.logger.info("{}-bit {} Bus, {}GiB Address Space.".format(
            colorer(data_width), colorer(standard), colorer(2**address_width/2**30)))

        # Adding reserved regions
150
        self.logger.info("Adding {} Bus Regions...".format(colorer("reserved", color="cyan")))
151 152 153 154 155
        for name, region in reserved_regions.items():
            if isinstance(region, int):
                region = SoCRegion(origin=region, size=0x1000000)
            self.add_region(name, region)

156
        self.logger.info("Bus Handler {}.".format(colorer("created", color="green")))
157 158 159 160

    # Add/Allog/Check Regions ----------------------------------------------------------------------
    def add_region(self, name, region):
        allocated = False
161 162 163 164
        if name in self.regions.keys() or name in self.io_regions.keys():
            self.logger.error("{} already declared as Region:".format(colorer(name, color="red")))
            self.logger.error(self)
            raise
165 166 167 168 169
        # Check if SoCIORegion
        if isinstance(region, SoCIORegion):
            self.io_regions[name] = region
            overlap = self.check_regions_overlap(self.io_regions)
            if overlap is not None:
170 171 172 173 174 175
                self.logger.error("IO Region {} between {} and {}:".format(
                    colorer("overlap", color="red"),
                    colorer(overlap[0]),
                    colorer(overlap[1])))
                self.logger.error(str(self.io_regions[overlap[0]]))
                self.logger.error(str(self.io_regions[overlap[1]]))
176
                raise
177
            self.logger.info("{} Region {} at {}.".format(
178 179 180
                colorer(name,    color="underline"),
                colorer("added", color="green"),
                str(region)))
181 182 183 184 185
        # Check if SoCRegion
        elif isinstance(region, SoCRegion):
            # If no origin specified, allocate region.
            if region.origin is None:
                allocated = True
186
                region    = self.alloc_region(name, region.size, region.cached)
187 188 189
                self.regions[name] = region
            # Else add region and check for overlaps.
            else:
190 191
                if not region.cached:
                    if not self.check_region_is_io(region):
192 193 194
                        self.logger.error("{} Region {}: {}.".format(
                            colorer(name),
                            colorer("not in IO region", color="red"),
195 196 197
                            str(region)))
                        self.logger.error(self)
                        raise
198
                self.regions[name] = region
199
                overlap = self.check_regions_overlap(self.regions)
200
                if overlap is not None:
201 202 203 204
                    self.logger.error("Region {} between {} and {}:".format(
                        colorer("overlap", color="red"),
                        colorer(overlap[0]),
                        colorer(overlap[1])))
205 206 207
                    self.logger.error(str(self.regions[overlap[0]]))
                    self.logger.error(str(self.regions[overlap[1]]))
                    raise
208
            self.logger.info("{} Region {} at {}.".format(
209
                colorer(name, color="underline"),
210
                colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
211 212
                str(region)))
        else:
213
            self.logger.error("{} is not a supported Region.".format(colorer(name, color="red")))
214 215
            raise

216
    def alloc_region(self, name, size, cached=True):
217 218 219 220 221 222
        self.logger.info("Allocating {} Region of size {}...".format(
            colorer("Cached" if cached else "IO"),
            colorer("0x{:08x}".format(size))))

        # Limit Search Regions
        if cached == False:
223
            search_regions = self.io_regions
224 225 226 227 228 229
        else:
            search_regions = {"main": SoCRegion(origin=0x00000000, size=2**self.address_width-1)}

        # Iterate on Search_Regions to find a Candidate
        for _, search_region in search_regions.items():
            origin = search_region.origin
230
            while (origin + size) < (search_region.origin + search_region.size_pow2):
231 232 233 234 235
                # Create a Candicate.
                candidate = SoCRegion(origin=origin, size=size, cached=cached)
                overlap   = False
                # Check Candidate does not overlap with allocated existing regions
                for _, allocated in self.regions.items():
236
                    if self.check_regions_overlap({"0": allocated, "1": candidate}) is not None:
237
                        origin  = allocated.origin + allocated.size_pow2
238 239 240 241 242 243
                        overlap = True
                        break
                if not overlap:
                    # If no overlap, the Candidate is selected
                    return candidate

244
        self.logger.error("Not enough Address Space to allocate Region.")
245 246
        raise

247
    def check_regions_overlap(self, regions, check_linker=False):
248 249 250 251 252 253
        i = 0
        while i < len(regions):
            n0 =  list(regions.keys())[i]
            r0 = regions[n0]
            for n1 in list(regions.keys())[i+1:]:
                r1 = regions[n1]
254 255 256
                if r0.linker or r1.linker:
                    if not check_linker:
                        continue
257
                if r0.origin >= (r1.origin + r1.size_pow2):
258
                    continue
259
                if r1.origin >= (r0.origin + r0.size_pow2):
260 261 262 263 264
                    continue
                return (n0, n1)
            i += 1
        return None

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
    def check_region_is_in(self, region, container):
        is_in = True
        if not (region.origin >= container.origin):
            is_in = False
        if not ((region.origin + region.size) < (container.origin + container.size)):
            is_in = False
        return is_in

    def check_region_is_io(self, region):
        is_io = False
        for _, io_region in self.io_regions.items():
            if self.check_region_is_in(region, io_region):
                is_io = True
        return is_io

280
    # Add Master/Slave -----------------------------------------------------------------------------
281 282
    def add_adapter(self, name, interface, direction="m2s"):
        assert direction in ["m2s", "s2m"]
283

284
        if isinstance(interface, wishbone.Interface):
285 286 287 288 289 290 291 292 293
            if interface.data_width != self.data_width:
                new_interface = wishbone.Interface(data_width=self.data_width)
                if direction == "m2s":
                    converter = wishbone.Converter(master=interface, slave=new_interface)
                if direction == "s2m":
                    converter = wishbone.Converter(master=new_interface, slave=interface)
                self.submodules += converter
            else:
                new_interface = interface
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
        elif isinstance(interface, axi.AXILiteInterface):
            # Data width conversion
            intermediate = axi.AXILiteInterface(data_width=self.data_width)
            if direction == "m2s":
                converter = axi.AXILiteConverter(master=interface, slave=intermediate)
            if direction == "s2m":
                converter = axi.AXILiteConverter(master=intermediate, slave=interface)
            self.submodules += converter
            # Bus type conversion
            new_interface = wishbone.Interface(data_width=self.data_width)
            if direction == "m2s":
                converter = axi.AXILite2Wishbone(axi_lite=intermediate, wishbone=new_interface)
            elif direction == "s2m":
                converter = axi.Wishbone2AXILite(wishbone=new_interface, axi_lite=intermediate)
            self.submodules += converter
309
        else:
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
            raise TypeError(interface)

        fmt = "{name} Bus {converted} from {frombus} {frombits}-bit to {tobus} {tobits}-bit."
        frombus  = "Wishbone" if isinstance(interface, wishbone.Interface) else "AXILite"
        tobus    = "Wishbone" if isinstance(new_interface, wishbone.Interface) else "AXILite"
        frombits = interface.data_width
        tobits   = new_interface.data_width
        if frombus != tobus or frombits != tobits:
            self.logger.info(fmt.format(
                name      = colorer(name),
                converted = colorer("converted", color="cyan"),
                frombus   = colorer("Wishbone" if isinstance(interface, wishbone.Interface) else "AXILite"),
                frombits  = colorer(interface.data_width),
                tobus     = colorer("Wishbone" if isinstance(new_interface, wishbone.Interface) else "AXILite"),
                tobits    = colorer(new_interface.data_width)))
        return new_interface
326

327
    def add_master(self, name=None, master=None):
328 329 330
        if name is None:
            name = "master{:d}".format(len(self.masters))
        if name in self.masters.keys():
331 332 333
            self.logger.error("{} {} as Bus Master:".format(
                colorer(name),
                colorer("already declared", color="red")))
334 335
            self.logger.error(self)
            raise
336
        master = self.add_adapter(name, master, "m2s")
337
        self.masters[name] = master
338 339 340
        self.logger.info("{} {} as Bus Master.".format(
            colorer(name,    color="underline"),
            colorer("added", color="green")))
341 342 343 344 345

    def add_slave(self, name=None, slave=None, region=None):
        no_name   = name is None
        no_region = region is None
        if no_name and no_region:
346 347 348 349
            self.logger.error("Please {} {} or/and {} of Bus Slave.".format(
                colorer("specify", color="red"),
                colorer("name"),
                colorer("region")))
350 351 352 353 354 355
            raise
        if no_name:
            name = "slave{:d}".format(len(self.slaves))
        if no_region:
            region = self.regions.get(name, None)
            if region is None:
356 357 358
                self.logger.error("{} Region {}.".format(
                    colorer(name),
                    colorer("not found", color="red")))
359 360 361 362
                raise
        else:
             self.add_region(name, region)
        if name in self.slaves.keys():
363 364 365
            self.logger.error("{} {} as Bus Slave:".format(
                colorer(name),
                colorer("already declared", color="red")))
366 367
            self.logger.error(self)
            raise
368
        slave = self.add_adapter(name, slave, "s2m")
369 370 371 372 373 374 375 376 377
        self.slaves[name] = slave
        self.logger.info("{} {} as Bus Slave.".format(
            colorer(name, color="underline"),
            colorer("added", color="green")))

    # Str ------------------------------------------------------------------------------------------
    def __str__(self):
        r = "{}-bit {} Bus, {}GiB Address Space.\n".format(
            colorer(self.data_width), colorer(self.standard), colorer(2**self.address_width/2**30))
378 379 380 381
        r += "IO Regions: ({})\n".format(len(self.io_regions.keys())) if len(self.io_regions.keys()) else ""
        io_regions = {k: v for k, v in sorted(self.io_regions.items(), key=lambda item: item[1].origin)}
        for name, region in io_regions.items():
           r += colorer(name, color="underline") + " "*(20-len(name)) + ": " + str(region) + "\n"
382
        r += "Bus Regions: ({})\n".format(len(self.regions.keys())) if len(self.regions.keys()) else ""
383 384
        regions = {k: v for k, v in sorted(self.regions.items(), key=lambda item: item[1].origin)}
        for name, region in regions.items():
385 386 387 388 389 390 391 392 393 394
           r += colorer(name, color="underline") + " "*(20-len(name)) + ": " + str(region) + "\n"
        r += "Bus Masters: ({})\n".format(len(self.masters.keys())) if len(self.masters.keys()) else ""
        for name in self.masters.keys():
           r += "- {}\n".format(colorer(name, color="underline"))
        r += "Bus Slaves: ({})\n".format(len(self.slaves.keys())) if len(self.slaves.keys()) else ""
        for name in self.slaves.keys():
           r += "- {}\n".format(colorer(name, color="underline"))
        r = r[:-1]
        return r

395
# SoCLocHandler --------------------------------------------------------------------------------------
396

397
class SoCLocHandler(Module):
398 399 400 401 402 403 404 405 406 407 408
    # Creation -------------------------------------------------------------------------------------
    def __init__(self, name, n_locs):
        self.name   = name
        self.locs   = {}
        self.n_locs = n_locs

    # Add ------------------------------------------------------------------------------------------
    def add(self, name, n=None, use_loc_if_exists=False):
        allocated = False
        if not (use_loc_if_exists and name in self.locs.keys()):
            if name in self.locs.keys():
409 410
                self.logger.error("{} {} name {}.".format(
                    colorer(name), self.name, colorer("already used", color="red")))
411 412 413
                self.logger.error(self)
                raise
            if n in self.locs.values():
414 415
                self.logger.error("{} {} Location {}.".format(
                    colorer(n), self.name, colorer("already used", color="red")))
416 417 418 419 420 421 422
                self.logger.error(self)
                raise
            if n is None:
                allocated = True
                n = self.alloc(name)
            else:
                if n < 0:
423 424 425 426
                    self.logger.error("{} {} Location should be {}.".format(
                        colorer(n),
                        self.name,
                        colorer("positive", color="red")))
427 428
                    raise
                if n > self.n_locs:
429 430
                    self.logger.error("{} {} Location {} than maximum: {}.".format(
                        colorer(n),
431
                        self.name,
432 433
                        colorer("higher", color="red"),
                        colorer(self.n_locs)))
434 435 436 437 438 439 440
                    raise
            self.locs[name] = n
        else:
            n = self.locs[name]
        self.logger.info("{} {} {} at Location {}.".format(
            colorer(name, color="underline"),
            self.name,
441
            colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
442 443 444 445 446 447 448 449 450 451 452 453 454 455
            colorer(n)))

    # Alloc ----------------------------------------------------------------------------------------
    def alloc(self, name):
        for n in range(self.n_locs):
            if n not in self.locs.values():
                return n
        self.logger.error("Not enough Locations.")
        self.logger.error(self)
        raise

    # Str ------------------------------------------------------------------------------------------
    def __str__(self):
        r = "{} Locations: ({})\n".format(self.name, len(self.locs.keys())) if len(self.locs.keys()) else ""
456
        locs = {k: v for k, v in sorted(self.locs.items(), key=lambda item: item[1])}
457
        length = 0
458
        for name in locs.keys():
459 460 461
            if len(name) > length: length = len(name)
        for name in locs.keys():
           r += "- {}{}: {}\n".format(colorer(name, color="underline"), " "*(length + 1 - len(name)), colorer(self.locs[name]))
462 463 464 465 466
        return r

# SoCCSRHandler ------------------------------------------------------------------------------------

class SoCCSRHandler(SoCLocHandler):
467
    supported_data_width    = [8, 32]
468
    supported_address_width = [14+i for i in range(4)]
469
    supported_alignment     = [32]
470
    supported_paging        = [0x800*2**i for i in range(4)]
471 472 473

    # Creation -------------------------------------------------------------------------------------
    def __init__(self, data_width=32, address_width=14, alignment=32, paging=0x800, reserved_csrs={}):
474
        SoCLocHandler.__init__(self, "CSR", n_locs=alignment//8*(2**address_width)//paging)
475
        self.logger = logging.getLogger("SoCCSRHandler")
476
        self.logger.info("Creating CSR Handler...")
477 478 479

        # Check Data Width
        if data_width not in self.supported_data_width:
480 481 482 483
            self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
                colorer("Data Width", color="red"),
                colorer(data_width),
                colorer(", ".join(str(x) for x in self.supported_data_width))))
484 485 486 487
            raise

        # Check Address Width
        if address_width not in self.supported_address_width:
488 489 490 491
            self.logger.error("Unsupported {} {} supporteds: {:s}".format(
                colorer("Address Width", color="red"),
                colorer(address_width),
                colorer(", ".join(str(x) for x in self.supported_address_width))))
492 493 494 495
            raise

        # Check Alignment
        if alignment not in self.supported_alignment:
496 497 498 499
            self.logger.error("Unsupported {}: {} supporteds: {:s}".format(
                colorer("Alignment", color="red"),
                colorer(alignment),
                colorer(", ".join(str(x) for x in self.supported_alignment))))
500
            raise
501
        if data_width > alignment:
502 503 504 505
            self.logger.error("Alignment ({}) {} Data Width ({})".format(
                colorer(alignment),
                colorer("should be >=", color="red"),
                colorer(data_width)))
506
            raise
507 508 509

        # Check Paging
        if paging not in self.supported_paging:
510
            self.logger.error("Unsupported {} 0x{}, supporteds: {:s}".format(
511
                colorer("Paging", color="red"),
512 513
                colorer("{:x}".format(paging)),
                colorer(", ".join("0x{:x}".format(x) for x in self.supported_paging))))
514 515 516 517 518 519 520
            raise

        # Create CSR Handler
        self.data_width    = data_width
        self.address_width = address_width
        self.alignment     = alignment
        self.paging        = paging
521
        self.masters       = {}
522
        self.regions       = {}
523
        self.logger.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).".format(
524
            colorer(self.data_width),
525
            colorer(self.alignment),
526 527
            colorer(2**self.address_width/2**10),
            colorer(self.paging),
528
            colorer(self.n_locs)))
529 530

        # Adding reserved CSRs
531
        self.logger.info("Adding {} CSRs...".format(colorer("reserved", color="cyan")))
532 533 534
        for name, n in reserved_csrs.items():
            self.add(name, n)

535
        self.logger.info("CSR Handler {}.".format(colorer("created", color="green")))
536 537 538 539 540 541

    # Add Master -----------------------------------------------------------------------------------
    def add_master(self, name=None, master=None):
        if name is None:
            name = "master{:d}".format(len(self.masters))
        if name in self.masters.keys():
542 543 544
            self.logger.error("{} {} as CSR Master:".format(
                colorer(name),
                colorer("already declared", color="red")))
545 546 547
            self.logger.error(self)
            raise
        if master.data_width != self.data_width:
548
            self.logger.error("{} Master/Handler Data Width {} ({} vs {}).".format(
549
                colorer(name),
550 551 552
                colorer("missmatch", color="red"),
                colorer(master.data_width),
                colorer(self.data_width)))
553 554 555 556 557 558
            raise
        self.masters[name] = master
        self.logger.info("{} {} as CSR Master.".format(
            colorer(name,    color="underline"),
            colorer("added", color="green")))

559 560 561 562 563
    # Add Region -----------------------------------------------------------------------------------
    def add_region(self, name, region):
        # FIXME: add checks
        self.regions[name] = region

564 565 566 567 568
    # Address map ----------------------------------------------------------------------------------
    def address_map(self, name, memory):
        if memory is not None:
            name = name + "_" + memory.name_override
        if self.locs.get(name, None) is None:
569 570 571 572
            self.logger.error("CSR {} {}.".format(
                colorer(name),
                colorer("not found", color="red")))
            self.logger.error(self)
573 574
            raise
        return self.locs[name]
575 576 577

    # Str ------------------------------------------------------------------------------------------
    def __str__(self):
578
        r = "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
579
            colorer(self.data_width),
580
            colorer(self.alignment),
581 582
            colorer(2**self.address_width/2**10),
            colorer(self.paging),
583 584
            colorer(self.n_locs))
        r += SoCLocHandler.__str__(self)
585 586 587
        r = r[:-1]
        return r

588
# SoCIRQHandler ------------------------------------------------------------------------------------
589

590
class SoCIRQHandler(SoCLocHandler):
591 592
    # Creation -------------------------------------------------------------------------------------
    def __init__(self, n_irqs=32, reserved_irqs={}):
593 594
        SoCLocHandler.__init__(self, "IRQ", n_locs=n_irqs)
        self.logger = logging.getLogger("SoCIRQHandler")
595
        self.logger.info("Creating IRQ Handler...")
596 597 598 599 600 601 602 603 604 605 606

        # Check IRQ Number
        if n_irqs > 32:
            self.logger.error("Unsupported IRQs number: {} supporteds: {:s}".format(
                colorer(n, color="red"), colorer("Up to 32", color="green")))
            raise

        # Create IRQ Handler
        self.logger.info("IRQ Handler (up to {} Locations).".format(colorer(n_irqs)))

        # Adding reserved IRQs
607
        self.logger.info("Adding {} IRQs...".format(colorer("reserved", color="cyan")))
608 609 610
        for name, n in reserved_irqs.items():
            self.add(name, n)

611
        self.logger.info("IRQ Handler {}.".format(colorer("created", color="green")))
612 613 614

    # Str ------------------------------------------------------------------------------------------
    def __str__(self):
615
        r ="IRQ Handler (up to {} Locations).\n".format(colorer(self.n_locs))
616
        r += SoCLocHandler.__str__(self)
617 618 619
        r = r[:-1]
        return r

620 621 622
# SoCController ------------------------------------------------------------------------------------

class SoCController(Module, AutoCSR):
623 624 625
    def __init__(self,
        with_reset    = True,
        with_scratch  = True,
626
        with_errors   = True):
627 628 629 630 631 632 633 634 635 636 637

        if with_reset:
            self._reset = CSRStorage(1, description="""Write a ``1`` to this register to reset the SoC.""")
        if with_scratch:
            self._scratch = CSRStorage(32, reset=0x12345678, description="""
                Use this register as a scratch space to verify that software read/write accesses
                to the Wishbone/CSR bus are working correctly. The initial reset value of 0x1234578
                can be used to verify endianness.""")
        if with_errors:
            self._bus_errors = CSRStatus(32, description="Total number of Wishbone bus errors (timeouts) since start.")

638 639 640
        # # #

        # Reset
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
        if with_reset:
            self.reset = Signal()
            self.comb += self.reset.eq(self._reset.re)

        # Errors
        if with_errors:
            self.bus_error = Signal()
            bus_errors     = Signal(32)
            self.sync += [
                If(bus_errors != (2**len(bus_errors)-1),
                    If(self.bus_error, bus_errors.eq(bus_errors + 1))
                )
            ]
            self.comb += self._bus_errors.status.eq(bus_errors)

656 657 658
# SoC ----------------------------------------------------------------------------------------------

class SoC(Module):
659
    mem_map = {}
Florent Kermarrec's avatar
Florent Kermarrec committed
660
    def __init__(self, platform, sys_clk_freq,
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
        bus_standard         = "wishbone",
        bus_data_width       = 32,
        bus_address_width    = 32,
        bus_timeout          = 1e6,
        bus_reserved_regions = {},

        csr_data_width       = 32,
        csr_address_width    = 14,
        csr_paging           = 0x800,
        csr_reserved_csrs    = {},

        irq_n_irqs           = 32,
        irq_reserved_irqs    = {},
        ):

        self.logger = logging.getLogger("SoC")
        self.logger.info(colorer("        __   _ __      _  __  ", color="bright"))
        self.logger.info(colorer("       / /  (_) /____ | |/_/  ", color="bright"))
        self.logger.info(colorer("      / /__/ / __/ -_)>  <    ", color="bright"))
        self.logger.info(colorer("     /____/_/\\__/\\__/_/|_|  ", color="bright"))
        self.logger.info(colorer("  Build your hardware, easily!", color="bright"))

        self.logger.info(colorer("-"*80, color="bright"))
684
        self.logger.info(colorer("Creating SoC... ({})".format(build_time())))
685
        self.logger.info(colorer("-"*80, color="bright"))
686 687
        self.logger.info("FPGA device : {}.".format(platform.device))
        self.logger.info("System clock: {:3.2f}MHz.".format(sys_clk_freq/1e6))
688

689
        # SoC attributes ---------------------------------------------------------------------------
Florent Kermarrec's avatar
Florent Kermarrec committed
690 691 692
        self.platform     = platform
        self.sys_clk_freq = sys_clk_freq
        self.constants    = {}
693
        self.csr_regions  = {}
694

695
        # SoC Bus Handler --------------------------------------------------------------------------
696
        self.submodules.bus = SoCBusHandler(
697 698 699 700 701 702 703 704
            standard         = bus_standard,
            data_width       = bus_data_width,
            address_width    = bus_address_width,
            timeout          = bus_timeout,
            reserved_regions = bus_reserved_regions,
           )

        # SoC Bus Handler --------------------------------------------------------------------------
705
        self.submodules.csr = SoCCSRHandler(
706 707
            data_width    = csr_data_width,
            address_width = csr_address_width,
708
            alignment     = 32,
709 710 711 712 713
            paging        = csr_paging,
            reserved_csrs = csr_reserved_csrs,
        )

        # SoC IRQ Handler --------------------------------------------------------------------------
714
        self.submodules.irq = SoCIRQHandler(
715 716 717 718 719
            n_irqs        = irq_n_irqs,
            reserved_irqs = irq_reserved_irqs
        )

        self.logger.info(colorer("-"*80, color="bright"))
720
        self.logger.info(colorer("Initial SoC:"))
721 722 723 724 725 726
        self.logger.info(colorer("-"*80, color="bright"))
        self.logger.info(self.bus)
        self.logger.info(self.csr)
        self.logger.info(self.irq)
        self.logger.info(colorer("-"*80, color="bright"))

727
        self.add_config("CLOCK_FREQUENCY", int(sys_clk_freq))
728

729 730
    # SoC Helpers ----------------------------------------------------------------------------------
    def check_if_exists(self, name):
731
        if hasattr(self, name):
732 733 734
            self.logger.error("{} SubModule already {}.".format(
                colorer(name),
                colorer("declared", color="red")))
735
            raise
736

737 738 739
    def add_constant(self, name, value=None):
        name = name.upper()
        if name in self.constants.keys():
740 741 742
            self.logger.error("{} Constant already {}.".format(
                colorer(name),
                colorer("declared", color="red")))
743 744 745
            raise
        self.constants[name] = SoCConstant(value)

746
    def add_config(self, name, value=None):
747 748 749 750 751 752
        name = "CONFIG_" + name
        if isinstance(value, str):
            self.add_constant(name + "_" + value)
        else:
            self.add_constant(name, value)

753
    # SoC Main Components --------------------------------------------------------------------------
754
    def add_controller(self, name="ctrl", **kwargs):
755
        self.check_if_exists(name)
756
        setattr(self.submodules, name, SoCController(**kwargs))
757 758
        self.csr.add(name, use_loc_if_exists=True)

759 760 761
    def add_ram(self, name, origin, size, contents=[], mode="rw"):
        ram_bus = wishbone.Interface(data_width=self.bus.data_width)
        ram     = wishbone.SRAM(size, bus=ram_bus, init=contents, read_only=(mode == "r"))
762 763
        self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
        self.check_if_exists(name)
764
        self.logger.info("RAM {} {} {}.".format(
765
            colorer(name),
766
            colorer("added", color="green"),
767 768 769
            self.bus.regions[name]))
        setattr(self.submodules, name, ram)

770 771
    def add_rom(self, name, origin, size, contents=[]):
        self.add_ram(name, origin, size, contents, mode="r")
772

773
    def add_csr_bridge(self, origin):
774
        self.submodules.csr_bridge = wishbone.Wishbone2CSR(
775
            bus_csr       = csr_bus.Interface(
776 777
            address_width = self.csr.address_width,
            data_width    = self.csr.data_width))
778 779 780
        csr_size   = 2**(self.csr.address_width + 2)
        csr_region = SoCRegion(origin=origin, size=csr_size, cached=False)
        self.bus.add_slave("csr", self.csr_bridge.wishbone, csr_region)
781 782 783 784
        self.csr.add_master(name="bridge", master=self.csr_bridge.csr)
        self.add_config("CSR_DATA_WIDTH", self.csr.data_width)
        self.add_config("CSR_ALIGNMENT",  self.csr.alignment)

785
    def add_cpu(self, name="vexriscv", variant="standard", cls=None, reset_address=None):
786
        if name not in cpu.CPUS.keys():
787
            self.logger.error("{} CPU {}, supporteds: {}.".format(
788 789 790
                colorer(name),
                colorer("not supported", color="red"),
                colorer(", ".join(cpu.CPUS.keys()))))
791
            raise
792
        # Add CPU
793
        cpu_cls = cls if cls is not None else cpu.CPUS[name]
794 795 796 797 798 799
        if variant not in cpu_cls.variants:
            self.logger.error("{} CPU variant {}, supporteds: {}.".format(
                colorer(variant),
                colorer("not supported", color="red"),
                colorer(", ".join(cpu_cls.variants))))
            raise
800
        self.submodules.cpu = cpu_cls(self.platform, variant)
801 802 803 804
        # Update SoC with CPU constraints
        for n, (origin, size) in enumerate(self.cpu.io_regions.items()):
            self.bus.add_region("io{}".format(n), SoCIORegion(origin=origin, size=size, cached=False))
        self.mem_map.update(self.cpu.mem_map) # FIXME
805 806
        # Add Bus Masters/CSR/IRQs
        if not isinstance(self.cpu, cpu.CPUNone):
807 808
            if reset_address is None:
                reset_address = self.mem_map["rom"]
809
            self.cpu.set_reset_address(reset_address)
810
            for n, cpu_bus in enumerate(self.cpu.periph_buses):
811
                self.bus.add_master(name="cpu_bus{}".format(n), master=cpu_bus)
812
            self.csr.add("cpu", use_loc_if_exists=True)
813 814 815 816
            if hasattr(self.cpu, "interrupt"):
                for name, loc in self.cpu.interrupts.items():
                    self.irq.add(name, loc)
                self.add_config("CPU_HAS_INTERRUPT")
817 818


819
            if hasattr(self, "ctrl"):
820 821
                if hasattr(self.ctrl, "reset"):
                    self.comb += self.cpu.reset.eq(self.ctrl.reset)
822 823
            self.add_config("CPU_RESET_ADDR", reset_address)
        # Add constants
824 825
        self.add_config("CPU_TYPE",    str(name))
        self.add_config("CPU_VARIANT", str(variant.split('+')[0]))
826
        self.add_constant("CONFIG_CPU_HUMAN_NAME", getattr(self.cpu, "human_name", "Unknown"))
827 828
        if hasattr(self.cpu, "nop"):
            self.add_constant("CONFIG_CPU_NOP", self.cpu.nop)
829

830 831 832 833
    def add_timer(self, name="timer0"):
        self.check_if_exists(name)
        setattr(self.submodules, name, Timer())
        self.csr.add(name, use_loc_if_exists=True)
834 835
        if hasattr(self.cpu, "interrupt"):
            self.irq.add(name, use_loc_if_exists=True)
836 837 838 839 840 841 842 843 844 845 846 847

    # SoC finalization -----------------------------------------------------------------------------
    def do_finalize(self):
        self.logger.info(colorer("-"*80, color="bright"))
        self.logger.info(colorer("Finalized SoC:"))
        self.logger.info(colorer("-"*80, color="bright"))
        self.logger.info(self.bus)
        self.logger.info(self.csr)
        self.logger.info(self.irq)
        self.logger.info(colorer("-"*80, color="bright"))

        # SoC Bus Interconnect ---------------------------------------------------------------------
848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
        if len(self.bus.masters) and len(self.bus.slaves):
            # If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
            if ((len(self.bus.masters) == 1)  and
                (len(self.bus.slaves)  == 1)  and
                (next(iter(self.bus.regions.values())).origin == 0)):
                self.submodules.bus_interconnect = wishbone.InterconnectPointToPoint(
                    master = next(iter(self.bus.masters.values())),
                    slave  = next(iter(self.bus.slaves.values())))
            # Otherwise, use InterconnectShared.
            else:
                self.submodules.bus_interconnect = wishbone.InterconnectShared(
                    masters        = self.bus.masters.values(),
                    slaves         = [(self.bus.regions[n].decoder(self.bus), s) for n, s in self.bus.slaves.items()],
                    register       = True,
                    timeout_cycles = self.bus.timeout)
                if hasattr(self, "ctrl") and self.bus.timeout is not None:
864 865
                    if hasattr(self.ctrl, "bus_error"):
                        self.comb += self.ctrl.bus_error.eq(self.bus_interconnect.timeout.error)
866 867 868 869
            self.bus.logger.info("Interconnect: {} ({} <-> {}).".format(
                colorer(self.bus_interconnect.__class__.__name__),
                colorer(len(self.bus.masters)),
                colorer(len(self.bus.slaves))))
870 871 872
        self.add_constant("CONFIG_BUS_STANDARD",      self.bus.standard.upper())
        self.add_constant("CONFIG_BUS_DATA_WIDTH",    self.bus.data_width)
        self.add_constant("CONFIG_BUS_ADDRESS_WIDTH", self.bus.address_width)
873 874 875

        # SoC CSR Interconnect ---------------------------------------------------------------------
        self.submodules.csr_bankarray = csr_bus.CSRBankArray(self,
876 877 878 879 880 881
            address_map        = self.csr.address_map,
            data_width         = self.csr.data_width,
            address_width      = self.csr.address_width,
            alignment          = self.csr.alignment,
            paging             = self.csr.paging,
            soc_bus_data_width = self.bus.data_width)
882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
        if len(self.csr.masters):
            self.submodules.csr_interconnect = csr_bus.InterconnectShared(
                masters = list(self.csr.masters.values()),
                slaves  = self.csr_bankarray.get_buses())

        # Add CSRs regions
        for name, csrs, mapaddr, rmap in self.csr_bankarray.banks:
            self.csr.add_region(name, SoCCSRRegion(
                origin   = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
                busword  = self.csr.data_width,
                obj      = csrs))

        # Add Memory regions
        for name, memory, mapaddr, mmap in self.csr_bankarray.srams:
            self.csr.add_region(name + "_" + memory.name_override, SoCCSRRegion(
Florent Kermarrec's avatar
Florent Kermarrec committed
897 898 899
                origin  = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
                busword = self.csr.data_width,
                obj     = memory))
900 901 902 903 904 905 906 907 908 909

        # Sort CSR regions by origin
        self.csr.regions = {k: v for k, v in sorted(self.csr.regions.items(), key=lambda item: item[1].origin)}

        # Add CSRs / Config items to constants
        for name, constant in self.csr_bankarray.constants:
            self.add_constant(name + "_" + constant.name, constant.value.value)

        # SoC CPU Check ----------------------------------------------------------------------------
        if not isinstance(self.cpu, cpu.CPUNone):
910 911 912 913 914 915
            if "sram" not in self.bus.regions.keys():
                self.logger.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
                    colorer("sram"),
                    colorer("defined", color="red")))
                self.logger.error(self.bus)
                raise
916
            cpu_reset_address_valid = False
917
            for name, container in self.bus.regions.items():
918 919 920 921
                if self.bus.check_region_is_in(
                    region    = SoCRegion(origin=self.cpu.reset_address, size=self.bus.data_width//8),
                    container = container):
                    cpu_reset_address_valid = True
922 923
                    if name == "rom":
                        self.cpu.use_rom = True
924 925 926 927 928 929 930
            if not cpu_reset_address_valid:
                self.logger.error("CPU needs {} to be in a {} Region.".format(
                    colorer("reset address 0x{:08x}".format(self.cpu.reset_address)),
                    colorer("defined", color="red")))
                self.logger.error(self.bus)
                raise

931 932 933 934 935 936 937 938 939
        # SoC IRQ Interconnect ---------------------------------------------------------------------
        if hasattr(self, "cpu"):
            if hasattr(self.cpu, "interrupt"):
                for name, loc in sorted(self.irq.locs.items()):
                    if name in self.cpu.interrupts.keys():
                        continue
                    if hasattr(self, name):
                        module = getattr(self, name)
                        if not hasattr(module, "ev"):
940 941 942 943
                            self.logger.error("EventManager {} in {} SubModule.".format(
                                colorer("not found", color="red"),
                                colorer(name)))
                            raise
944 945 946 947 948
                        self.comb += self.cpu.interrupt[loc].eq(module.ev.irq)
                    self.add_constant(name + "_INTERRUPT", loc)

    # SoC build ------------------------------------------------------------------------------------
    def build(self, *args, **kwargs):
949 950
        self.build_name = kwargs.pop("build_name", self.platform.name)
        kwargs.update({"build_name": self.build_name})
951 952 953 954 955 956
        return self.platform.build(self, *args, **kwargs)

# LiteXSoC -----------------------------------------------------------------------------------------

class LiteXSoC(SoC):
    # Add Identifier -------------------------------------------------------------------------------
957 958 959 960
    def add_identifier(self, name="identifier", identifier="LiteX SoC", with_build_time=True):
        self.check_if_exists(name)
        if with_build_time:
            identifier += " " + build_time()
961
        setattr(self.submodules, name, Identifier(identifier))
962 963
        self.csr.add(name + "_mem", use_loc_if_exists=True)

964
    # Add UART -------------------------------------------------------------------------------------
965
    def add_uart(self, name, baudrate=115200, fifo_depth=16):
Florent Kermarrec's avatar
Florent Kermarrec committed
966
        from litex.soc.cores import uart
967 968

        # Stub / Stream
Florent Kermarrec's avatar
Florent Kermarrec committed
969
        if name in ["stub", "stream"]:
970
            self.submodules.uart = uart.UART(tx_fifo_depth=0, rx_fifo_depth=0)
Florent Kermarrec's avatar
Florent Kermarrec committed
971 972
            if name == "stub":
                self.comb += self.uart.sink.ready.eq(1)
973

974 975 976
        # UARTBone / Bridge
        elif name in ["uartbone", "bridge"]:
            self.add_uartbone(baudrate=baudrate)
977 978 979

        # Crossover
        elif name in ["crossover"]:
Florent Kermarrec's avatar
Florent Kermarrec committed
980
            self.submodules.uart = uart.UARTCrossover()
981

982 983 984 985 986 987 988
        # Model/Sim
        elif name in ["model", "sim"]:
            self.submodules.uart_phy = uart.RS232PHYModel(self.platform.request("serial"))
            self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
                tx_fifo_depth = fifo_depth,
                rx_fifo_depth = fifo_depth))

989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004
        # JTAG Atlantic
        elif name in ["jtag_atlantic"]:
            from litex.soc.cores.jtag import JTAGAtlantic
            self.submodules.uart_phy = JTAGAtlantic()
            self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
                tx_fifo_depth = fifo_depth,
                rx_fifo_depth = fifo_depth))

        # JTAG UART
        elif name in ["jtag_uart"]:
            from litex.soc.cores.jtag import JTAGPHY
            self.submodules.uart_phy = JTAGPHY(device=self.platform.device)
            self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
                tx_fifo_depth = fifo_depth,
                rx_fifo_depth = fifo_depth))

1005 1006
        # USB ACM (with ValentyUSB core)
        elif name in ["usb_acm"]:
1007 1008 1009 1010 1011 1012
            import valentyusb.usbcore.io as usbio
            import valentyusb.usbcore.cpu.cdc_eptri as cdc_eptri
            usb_pads = self.platform.request("usb")
            usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup)
            self.submodules.uart = cdc_eptri.CDCUsb(usb_iobuf)

1013
        # Classic UART
Florent Kermarrec's avatar
Florent Kermarrec committed
1014
        else:
1015 1016 1017 1018
            self.submodules.uart_phy = uart.UARTPHY(
                pads     = self.platform.request(name),
                clk_freq = self.sys_clk_freq,
                baudrate = baudrate)
1019 1020 1021
            self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
                tx_fifo_depth = fifo_depth,
                rx_fifo_depth = fifo_depth))
1022

Florent Kermarrec's avatar
Florent Kermarrec committed
1023 1024
        self.csr.add("uart_phy", use_loc_if_exists=True)
        self.csr.add("uart", use_loc_if_exists=True)
1025 1026 1027 1028
        if hasattr(self.cpu, "interrupt"):
            self.irq.add("uart", use_loc_if_exists=True)
        else:
            self.add_constant("UART_POLLING")
Florent Kermarrec's avatar
Florent Kermarrec committed
1029

1030 1031 1032 1033 1034 1035 1036 1037 1038
    # Add UARTbone ---------------------------------------------------------------------------------
    def add_uartbone(self, name="serial", baudrate=115200):
        from litex.soc.cores import uart
        self.submodules.uartbone = uart.UARTBone(
            pads     = self.platform.request(name),
            clk_freq = self.sys_clk_freq,
            baudrate = baudrate)
        self.bus.add_master(name="uartbone", master=self.uartbone.wishbone)

1039
    # Add SDRAM ------------------------------------------------------------------------------------
1040
    def add_sdram(self, name, phy, module, origin, size=None, with_soc_interconnect=True,
Florent Kermarrec's avatar
Florent Kermarrec committed
1041 1042 1043
        l2_cache_size           = 8192,
        l2_cache_min_data_width = 128,
        l2_cache_reverse        = True,
1044
        l2_cache_full_memory_we = True,
Florent Kermarrec's avatar
Florent Kermarrec committed
1045 1046
        **kwargs):

1047
        # Imports
1048
        from litedram.common import LiteDRAMNativePort
1049 1050 1051 1052 1053
        from litedram.core import LiteDRAMCore
        from litedram.frontend.wishbone import LiteDRAMWishbone2Native
        from litedram.frontend.axi import LiteDRAMAXI2Native

        # LiteDRAM core
Florent Kermarrec's avatar
Florent Kermarrec committed
1054 1055
        self.submodules.sdram = LiteDRAMCore(
            phy             = phy,
1056 1057
            geom_settings   = module.geom_settings,
            timing_settings = module.timing_settings,
Florent Kermarrec's avatar
Florent Kermarrec committed
1058 1059
            clk_freq        = self.sys_clk_freq,
            **kwargs)
1060
        self.csr.add("sdram")
Florent Kermarrec's avatar
Florent Kermarrec committed
1061

1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
        # Save SPD data to be able to verify it at runtime
        if hasattr(module, "_spd_data"):
            # pack the data into words of bus width
            bytes_per_word = self.bus.data_width // 8
            mem = [0] * ceil(len(module._spd_data) / bytes_per_word)
            for i in range(len(mem)):
                for offset in range(bytes_per_word):
                    mem[i] <<= 8
                    if self.cpu.endianness == "little":
                        offset = bytes_per_word - 1 - offset
                    spd_byte = i * bytes_per_word + offset
                    if spd_byte < len(module._spd_data):
                        mem[i] |= module._spd_data[spd_byte]
            self.add_rom(
                name="spd",
                origin=self.mem_map.get("spd", None),
                size=len(module._spd_data),
                contents=mem,
            )

1082 1083
        if not with_soc_interconnect: return

1084
        # Compute/Check SDRAM size
1085 1086 1087 1088 1089
        sdram_size = 2**(module.geom_settings.bankbits +
                         module.geom_settings.rowbits +
                         module.geom_settings.colbits)*phy.settings.databits//8
        if size is not None:
            sdram_size = min(sdram_size, size)
1090 1091

        # Add SDRAM region
1092
        self.bus.add_region("main_ram", SoCRegion(origin=origin, size=sdram_size))
Florent Kermarrec's avatar
Florent Kermarrec committed
1093 1094

        # SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
        if len(self.cpu.memory_buses):
            # When CPU has at least a direct memory bus, connect them directly to LiteDRAM.
            for mem_bus in self.cpu.memory_buses:
                # Request a LiteDRAM native port.
                port = self.sdram.crossbar.get_port()
                port.data_width = 2**int(log2(port.