soc.py 58.2 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 285 286 287 288 289 290 291 292 293 294 295 296 297

        if isinstance(interface, axi.AXILiteInterface):
            self.logger.info("{} Bus {} from {} to {}.".format(
                colorer(name),
                colorer("converted", color="cyan"),
                colorer("AXILite"),
                colorer("Wishbone")))
            new_interface = wishbone.Interface(data_width=interface.data_width)
            if direction == "m2s":
                converter = axi.AXILite2Wishbone(axi_lite=interface, wishbone=new_interface)
            elif direction == "s2m":
                converter = axi.Wishbone2AXILite(wishbone=new_interface, axi_lite=interface)
            self.submodules += converter
            interface = new_interface

298 299 300 301 302 303 304
        if interface.data_width != self.data_width:
            self.logger.info("{} Bus {} from {}-bit to {}-bit.".format(
                colorer(name),
                colorer("converted", color="cyan"),
                colorer(interface.data_width),
                colorer(self.data_width)))
            new_interface = wishbone.Interface(data_width=self.data_width)
305 306 307 308 309
            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
310 311 312 313
            return new_interface
        else:
            return interface

314
    def add_master(self, name=None, master=None):
315 316 317
        if name is None:
            name = "master{:d}".format(len(self.masters))
        if name in self.masters.keys():
318 319 320
            self.logger.error("{} {} as Bus Master:".format(
                colorer(name),
                colorer("already declared", color="red")))
321 322
            self.logger.error(self)
            raise
323
        master = self.add_adapter(name, master, "m2s")
324
        self.masters[name] = master
325 326 327
        self.logger.info("{} {} as Bus Master.".format(
            colorer(name,    color="underline"),
            colorer("added", color="green")))
328 329 330 331 332

    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:
333 334 335 336
            self.logger.error("Please {} {} or/and {} of Bus Slave.".format(
                colorer("specify", color="red"),
                colorer("name"),
                colorer("region")))
337 338 339 340 341 342
            raise
        if no_name:
            name = "slave{:d}".format(len(self.slaves))
        if no_region:
            region = self.regions.get(name, None)
            if region is None:
343 344 345
                self.logger.error("{} Region {}.".format(
                    colorer(name),
                    colorer("not found", color="red")))
346 347 348 349
                raise
        else:
             self.add_region(name, region)
        if name in self.slaves.keys():
350 351 352
            self.logger.error("{} {} as Bus Slave:".format(
                colorer(name),
                colorer("already declared", color="red")))
353 354
            self.logger.error(self)
            raise
355
        slave = self.add_adapter(name, slave, "s2m")
356 357 358 359 360 361 362 363 364
        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))
365 366 367 368
        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"
369
        r += "Bus Regions: ({})\n".format(len(self.regions.keys())) if len(self.regions.keys()) else ""
370 371
        regions = {k: v for k, v in sorted(self.regions.items(), key=lambda item: item[1].origin)}
        for name, region in regions.items():
372 373 374 375 376 377 378 379 380 381
           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

382
# SoCLocHandler --------------------------------------------------------------------------------------
383

384
class SoCLocHandler(Module):
385 386 387 388 389 390 391 392 393 394 395
    # 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():
396 397
                self.logger.error("{} {} name {}.".format(
                    colorer(name), self.name, colorer("already used", color="red")))
398 399 400
                self.logger.error(self)
                raise
            if n in self.locs.values():
401 402
                self.logger.error("{} {} Location {}.".format(
                    colorer(n), self.name, colorer("already used", color="red")))
403 404 405 406 407 408 409
                self.logger.error(self)
                raise
            if n is None:
                allocated = True
                n = self.alloc(name)
            else:
                if n < 0:
410 411 412 413
                    self.logger.error("{} {} Location should be {}.".format(
                        colorer(n),
                        self.name,
                        colorer("positive", color="red")))
414 415
                    raise
                if n > self.n_locs:
416 417
                    self.logger.error("{} {} Location {} than maximum: {}.".format(
                        colorer(n),
418
                        self.name,
419 420
                        colorer("higher", color="red"),
                        colorer(self.n_locs)))
421 422 423 424 425 426 427
                    raise
            self.locs[name] = n
        else:
            n = self.locs[name]
        self.logger.info("{} {} {} at Location {}.".format(
            colorer(name, color="underline"),
            self.name,
428
            colorer("allocated" if allocated else "added", color="cyan" if allocated else "green"),
429 430 431 432 433 434 435 436 437 438 439 440 441 442
            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 ""
443
        locs = {k: v for k, v in sorted(self.locs.items(), key=lambda item: item[1])}
444
        length = 0
445
        for name in locs.keys():
446 447 448
            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]))
449 450 451 452 453
        return r

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

class SoCCSRHandler(SoCLocHandler):
454
    supported_data_width    = [8, 32]
455
    supported_address_width = [14+i for i in range(4)]
456
    supported_alignment     = [32]
457
    supported_paging        = [0x800*2**i for i in range(4)]
458 459 460

    # Creation -------------------------------------------------------------------------------------
    def __init__(self, data_width=32, address_width=14, alignment=32, paging=0x800, reserved_csrs={}):
461
        SoCLocHandler.__init__(self, "CSR", n_locs=alignment//8*(2**address_width)//paging)
462
        self.logger = logging.getLogger("SoCCSRHandler")
463
        self.logger.info("Creating CSR Handler...")
464 465 466

        # Check Data Width
        if data_width not in self.supported_data_width:
467 468 469 470
            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))))
471 472 473 474
            raise

        # Check Address Width
        if address_width not in self.supported_address_width:
475 476 477 478
            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))))
479 480 481 482
            raise

        # Check Alignment
        if alignment not in self.supported_alignment:
483 484 485 486
            self.logger.error("Unsupported {}: {} supporteds: {:s}".format(
                colorer("Alignment", color="red"),
                colorer(alignment),
                colorer(", ".join(str(x) for x in self.supported_alignment))))
487
            raise
488
        if data_width > alignment:
489 490 491 492
            self.logger.error("Alignment ({}) {} Data Width ({})".format(
                colorer(alignment),
                colorer("should be >=", color="red"),
                colorer(data_width)))
493
            raise
494 495 496

        # Check Paging
        if paging not in self.supported_paging:
497
            self.logger.error("Unsupported {} 0x{}, supporteds: {:s}".format(
498
                colorer("Paging", color="red"),
499 500
                colorer("{:x}".format(paging)),
                colorer(", ".join("0x{:x}".format(x) for x in self.supported_paging))))
501 502 503 504 505 506 507
            raise

        # Create CSR Handler
        self.data_width    = data_width
        self.address_width = address_width
        self.alignment     = alignment
        self.paging        = paging
508
        self.masters       = {}
509
        self.regions       = {}
510
        self.logger.info("{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).".format(
511
            colorer(self.data_width),
512
            colorer(self.alignment),
513 514
            colorer(2**self.address_width/2**10),
            colorer(self.paging),
515
            colorer(self.n_locs)))
516 517

        # Adding reserved CSRs
518
        self.logger.info("Adding {} CSRs...".format(colorer("reserved", color="cyan")))
519 520 521
        for name, n in reserved_csrs.items():
            self.add(name, n)

522
        self.logger.info("CSR Handler {}.".format(colorer("created", color="green")))
523 524 525 526 527 528

    # 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():
529 530 531
            self.logger.error("{} {} as CSR Master:".format(
                colorer(name),
                colorer("already declared", color="red")))
532 533 534
            self.logger.error(self)
            raise
        if master.data_width != self.data_width:
535
            self.logger.error("{} Master/Handler Data Width {} ({} vs {}).".format(
536
                colorer(name),
537 538 539
                colorer("missmatch", color="red"),
                colorer(master.data_width),
                colorer(self.data_width)))
540 541 542 543 544 545
            raise
        self.masters[name] = master
        self.logger.info("{} {} as CSR Master.".format(
            colorer(name,    color="underline"),
            colorer("added", color="green")))

546 547 548 549 550
    # Add Region -----------------------------------------------------------------------------------
    def add_region(self, name, region):
        # FIXME: add checks
        self.regions[name] = region

551 552 553 554 555
    # 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:
556 557 558 559
            self.logger.error("CSR {} {}.".format(
                colorer(name),
                colorer("not found", color="red")))
            self.logger.error(self)
560 561
            raise
        return self.locs[name]
562 563 564

    # Str ------------------------------------------------------------------------------------------
    def __str__(self):
565
        r = "{}-bit CSR Bus, {}-bit Aligned, {}KiB Address Space, {}B Paging (Up to {} Locations).\n".format(
566
            colorer(self.data_width),
567
            colorer(self.alignment),
568 569
            colorer(2**self.address_width/2**10),
            colorer(self.paging),
570 571
            colorer(self.n_locs))
        r += SoCLocHandler.__str__(self)
572 573 574
        r = r[:-1]
        return r

575
# SoCIRQHandler ------------------------------------------------------------------------------------
576

577
class SoCIRQHandler(SoCLocHandler):
578 579
    # Creation -------------------------------------------------------------------------------------
    def __init__(self, n_irqs=32, reserved_irqs={}):
580 581
        SoCLocHandler.__init__(self, "IRQ", n_locs=n_irqs)
        self.logger = logging.getLogger("SoCIRQHandler")
582
        self.logger.info("Creating IRQ Handler...")
583 584 585 586 587 588 589 590 591 592 593

        # 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
594
        self.logger.info("Adding {} IRQs...".format(colorer("reserved", color="cyan")))
595 596 597
        for name, n in reserved_irqs.items():
            self.add(name, n)

598
        self.logger.info("IRQ Handler {}.".format(colorer("created", color="green")))
599 600 601

    # Str ------------------------------------------------------------------------------------------
    def __str__(self):
602
        r ="IRQ Handler (up to {} Locations).\n".format(colorer(self.n_locs))
603
        r += SoCLocHandler.__str__(self)
604 605 606
        r = r[:-1]
        return r

607 608 609
# SoCController ------------------------------------------------------------------------------------

class SoCController(Module, AutoCSR):
610 611 612
    def __init__(self,
        with_reset    = True,
        with_scratch  = True,
613
        with_errors   = True):
614 615 616 617 618 619 620 621 622 623 624

        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.")

625 626 627
        # # #

        # Reset
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
        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)

643 644 645
# SoC ----------------------------------------------------------------------------------------------

class SoC(Module):
646
    mem_map = {}
Florent Kermarrec's avatar
Florent Kermarrec committed
647
    def __init__(self, platform, sys_clk_freq,
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
        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"))
671
        self.logger.info(colorer("Creating SoC... ({})".format(build_time())))
672
        self.logger.info(colorer("-"*80, color="bright"))
673 674
        self.logger.info("FPGA device : {}.".format(platform.device))
        self.logger.info("System clock: {:3.2f}MHz.".format(sys_clk_freq/1e6))
675

676
        # SoC attributes ---------------------------------------------------------------------------
Florent Kermarrec's avatar
Florent Kermarrec committed
677 678 679
        self.platform     = platform
        self.sys_clk_freq = sys_clk_freq
        self.constants    = {}
680
        self.csr_regions  = {}
681

682
        # SoC Bus Handler --------------------------------------------------------------------------
683
        self.submodules.bus = SoCBusHandler(
684 685 686 687 688 689 690 691
            standard         = bus_standard,
            data_width       = bus_data_width,
            address_width    = bus_address_width,
            timeout          = bus_timeout,
            reserved_regions = bus_reserved_regions,
           )

        # SoC Bus Handler --------------------------------------------------------------------------
692
        self.submodules.csr = SoCCSRHandler(
693 694
            data_width    = csr_data_width,
            address_width = csr_address_width,
695
            alignment     = 32,
696 697 698 699 700
            paging        = csr_paging,
            reserved_csrs = csr_reserved_csrs,
        )

        # SoC IRQ Handler --------------------------------------------------------------------------
701
        self.submodules.irq = SoCIRQHandler(
702 703 704 705 706
            n_irqs        = irq_n_irqs,
            reserved_irqs = irq_reserved_irqs
        )

        self.logger.info(colorer("-"*80, color="bright"))
707
        self.logger.info(colorer("Initial SoC:"))
708 709 710 711 712 713
        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"))

714
        self.add_config("CLOCK_FREQUENCY", int(sys_clk_freq))
715

716 717
    # SoC Helpers ----------------------------------------------------------------------------------
    def check_if_exists(self, name):
718
        if hasattr(self, name):
719 720 721
            self.logger.error("{} SubModule already {}.".format(
                colorer(name),
                colorer("declared", color="red")))
722
            raise
723

724 725 726
    def add_constant(self, name, value=None):
        name = name.upper()
        if name in self.constants.keys():
727 728 729
            self.logger.error("{} Constant already {}.".format(
                colorer(name),
                colorer("declared", color="red")))
730 731 732
            raise
        self.constants[name] = SoCConstant(value)

733
    def add_config(self, name, value=None):
734 735 736 737 738 739
        name = "CONFIG_" + name
        if isinstance(value, str):
            self.add_constant(name + "_" + value)
        else:
            self.add_constant(name, value)

740
    # SoC Main Components --------------------------------------------------------------------------
741
    def add_controller(self, name="ctrl", **kwargs):
742
        self.check_if_exists(name)
743
        setattr(self.submodules, name, SoCController(**kwargs))
744 745
        self.csr.add(name, use_loc_if_exists=True)

746 747 748 749 750 751 752 753 754 755 756
    def add_ram(self, name, origin, size, contents=[], mode="rw", bus=None):
        if bus is None:
            bus = wishbone.Interface(data_width=self.bus.data_width)

        if isinstance(bus, wishbone.Interface):
            ram = wishbone.SRAM(size, bus=bus, init=contents, read_only=(mode == "r"))
        elif isinstance(bus, axi.AXILiteInterface):
            ram = axi.AXILiteSRAM(size, bus=bus, init=contents, read_only=(mode == "r"))
        else:
            raise TypeError(bus)

757 758
        self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
        self.check_if_exists(name)
759 760
        self.logger.info("{} RAM {} {} {}.".format(
            colorer("Wishbone" if isinstance(bus, wishbone.Interface) else "AXILite"),
761
            colorer(name),
762
            colorer("added", color="green"),
763 764 765
            self.bus.regions[name]))
        setattr(self.submodules, name, ram)

766 767
    def add_rom(self, name, origin, size, contents=[], bus=None):
        self.add_ram(name, origin, size, contents, mode="r", bus=bus)
768

769
    def add_csr_bridge(self, origin):
770
        self.submodules.csr_bridge = wishbone.Wishbone2CSR(
771
            bus_csr       = csr_bus.Interface(
772 773
            address_width = self.csr.address_width,
            data_width    = self.csr.data_width))
774 775 776
        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)
777 778 779 780
        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)

781
    def add_cpu(self, name="vexriscv", variant="standard", cls=None, reset_address=None):
782
        if name not in cpu.CPUS.keys():
783
            self.logger.error("{} CPU {}, supporteds: {}.".format(
784 785 786
                colorer(name),
                colorer("not supported", color="red"),
                colorer(", ".join(cpu.CPUS.keys()))))
787
            raise
788
        # Add CPU
789
        cpu_cls = cls if cls is not None else cpu.CPUS[name]
790 791 792 793 794 795
        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
796
        self.submodules.cpu = cpu_cls(self.platform, variant)
797 798 799 800
        # 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
801 802
        # Add Bus Masters/CSR/IRQs
        if not isinstance(self.cpu, cpu.CPUNone):
803 804
            if reset_address is None:
                reset_address = self.mem_map["rom"]
805
            self.cpu.set_reset_address(reset_address)
806
            for n, cpu_bus in enumerate(self.cpu.periph_buses):
807
                self.bus.add_master(name="cpu_bus{}".format(n), master=cpu_bus)
808
            self.csr.add("cpu", use_loc_if_exists=True)
809 810 811 812
            if hasattr(self.cpu, "interrupt"):
                for name, loc in self.cpu.interrupts.items():
                    self.irq.add(name, loc)
                self.add_config("CPU_HAS_INTERRUPT")
813 814


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

826 827 828 829
    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)
830 831
        if hasattr(self.cpu, "interrupt"):
            self.irq.add(name, use_loc_if_exists=True)
832 833 834 835 836 837 838 839 840 841 842 843

    # 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 ---------------------------------------------------------------------
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
        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:
860 861
                    if hasattr(self.ctrl, "bus_error"):
                        self.comb += self.ctrl.bus_error.eq(self.bus_interconnect.timeout.error)
862 863 864 865
            self.bus.logger.info("Interconnect: {} ({} <-> {}).".format(
                colorer(self.bus_interconnect.__class__.__name__),
                colorer(len(self.bus.masters)),
                colorer(len(self.bus.slaves))))
866 867 868
        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)
869 870 871

        # SoC CSR Interconnect ---------------------------------------------------------------------
        self.submodules.csr_bankarray = csr_bus.CSRBankArray(self,
872 873 874 875 876 877
            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)
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
        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
893 894 895
                origin  = (self.bus.regions["csr"].origin + self.csr.paging*mapaddr),
                busword = self.csr.data_width,
                obj     = memory))
896 897 898 899 900 901 902 903 904 905

        # 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):
906 907 908 909 910 911
            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
912
            cpu_reset_address_valid = False
913
            for name, container in self.bus.regions.items():
914 915 916 917
                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
918 919
                    if name == "rom":
                        self.cpu.use_rom = True
920 921 922 923 924 925 926
            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

927 928 929 930 931 932 933 934 935
        # 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"):
936 937 938 939
                            self.logger.error("EventManager {} in {} SubModule.".format(
                                colorer("not found", color="red"),
                                colorer(name)))
                            raise
940 941 942 943 944
                        self.comb += self.cpu.interrupt[loc].eq(module.ev.irq)
                    self.add_constant(name + "_INTERRUPT", loc)

    # SoC build ------------------------------------------------------------------------------------
    def build(self, *args, **kwargs):
945 946
        self.build_name = kwargs.pop("build_name", self.platform.name)
        kwargs.update({"build_name": self.build_name})
947 948 949 950 951 952
        return self.platform.build(self, *args, **kwargs)

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

class LiteXSoC(SoC):
    # Add Identifier -------------------------------------------------------------------------------
953 954 955 956
    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()
957
        setattr(self.submodules, name, Identifier(identifier))
958 959
        self.csr.add(name + "_mem", use_loc_if_exists=True)

960
    # Add UART -------------------------------------------------------------------------------------
961
    def add_uart(self, name, baudrate=115200, fifo_depth=16):
Florent Kermarrec's avatar
Florent Kermarrec committed
962
        from litex.soc.cores import uart
963 964

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

970 971 972
        # UARTBone / Bridge
        elif name in ["uartbone", "bridge"]:
            self.add_uartbone(baudrate=baudrate)
973 974 975

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

978 979 980 981 982 983 984
        # 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))

985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
        # 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))

1001 1002
        # USB ACM (with ValentyUSB core)
        elif name in ["usb_acm"]:
1003 1004 1005 1006 1007 1008
            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)

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

Florent Kermarrec's avatar
Florent Kermarrec committed
1019 1020
        self.csr.add("uart_phy", use_loc_if_exists=True)
        self.csr.add("uart", use_loc_if_exists=True)
1021 1022 1023 1024
        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
1025

1026 1027 1028 1029 1030 1031 1032 1033 1034
    # 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)

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

1043
        # Imports
1044
        from litedram.common import LiteDRAMNativePort
1045 1046 1047 1048 1049
        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
1050 1051
        self.submodules.sdram = LiteDRAMCore(
            phy             = phy,
1052 1053
            geom_settings   = module.geom_settings,
            timing_settings = module.timing_settings,
Florent Kermarrec's avatar
Florent Kermarrec committed
1054 1055
            clk_freq        = self.sys_clk_freq,
            **kwargs)
1056
        self.csr.add("sdram")
Florent Kermarrec's avatar
Florent Kermarrec committed
1057

1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
        # 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,
            )

1078 1079
        if not with_soc_interconnect: return

1080
        # Compute/Check SDRAM size
1081 1082 1083 1084 1085
        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)
1086 1087

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

        # SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
1091 1092 1093 1094 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
        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.data_width)) # Round to nearest power of 2.

                # Check if bus is an AXI bus and connect it.
                if isinstance(mem_bus, axi.AXIInterface):
                    # If same data_width, connect it directly.
                    if port.data_width == mem_bus.data_width:
                        self.logger.info("Matching AXI MEM data width ({})\n".format(port.data_width))
                        self.submodules += LiteDRAMAXI2Native(
                            axi          = self.cpu.mem_axi,
                            port         = port,
                            base_address = self.bus.regions["main_ram"].origin)
                    # If different data_width, do the adaptation and connect it via Wishbone.
                    else:
                        self.logger.info("Converting MEM data width: {} to {} via Wishbone".format(
                            port.data_width,
                            self.cpu.mem_axi.data_width))
                        # FIXME: replace WB data-width converter with native AXI converter!!!
                        mem_wb  = wishbone.Interface(
                            data_width = self.cpu.mem_axi.data_width,
                            adr_width  = 32-log2_int(self.cpu.mem_axi.data_width//8))
                        # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
                        mem_a2w = ResetInserter()(axi.AXI2Wishbone(
                            axi          = self.cpu.mem_axi,
                            wishbone     = mem_wb,
                            base_address = 0))
                        self.comb += mem_a2w.reset.eq(ResetSignal() | self.cpu.reset)
                        self.submodules += mem_a2w
                        litedram_wb = wishbone.Interface(