soc/integration: initial adaptation to new SoC class
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Thu, 6 Feb 2020 10:07:50 +0000 (11:07 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Thu, 6 Feb 2020 17:21:13 +0000 (18:21 +0100)
litex/soc/integration/soc_core.py

index 496435c9ec94803e4934ac88966ccbae2735ca3f..943c5c46d6334c728570cd5ac0ab18c008132d12 100644 (file)
@@ -25,6 +25,7 @@ from litex.soc.cores import cpu
 from litex.soc.interconnect.csr import *
 from litex.soc.interconnect import wishbone, csr_bus, wishbone2csr
 from litex.soc.integration.common import *
+from litex.soc.integration.soc import SoCRegion, SoC
 
 __all__ = [
     "mem_decoder",
@@ -69,7 +70,7 @@ class SoCController(Module, AutoCSR):
 
 # SoCCore ------------------------------------------------------------------------------------------
 
-class SoCCore(Module):
+class SoCCore(SoC):
     # default register/interrupt/memory mappings (can be redefined by user)
     csr_map       = {}
     interrupt_map = {}
@@ -106,6 +107,24 @@ class SoCCore(Module):
         self.platform = platform
         self.clk_freq = clk_freq
 
+        SoC.__init__(self,
+            bus_standard         = "wishbone",
+            bus_data_width       = 32,
+            bus_address_width    = 32,
+            bus_timeout          = wishbone_timeout_cycles,
+            bus_reserved_regions = {},
+
+            csr_data_width       = csr_data_width,
+            csr_address_width    = csr_address_width,
+            csr_alignment        = csr_alignment,
+            csr_paging           = 0x800,
+            csr_reserved_csrs    = self.csr_map,
+
+            irq_n_irqs           = 32,
+            irq_reserved_irqs    = {},
+        )
+        self.mem_regions = self.bus.regions
+
         # SoC's CSR/Mem/Interrupt mapping (default or user defined + dynamically allocateds)
         self.soc_csr_map       = {}
         self.soc_interrupt_map = {}
@@ -115,18 +134,11 @@ class SoCCore(Module):
         # SoC's Config/Constants/Regions
         self.config      = {}
         self.constants   = {}
-        self.mem_regions = {}
         self.csr_regions = {}
 
-        # Wishbone masters/slaves lists
-        self._wb_masters = []
-        self._wb_slaves  = []
-
         # CSR masters list
         self._csr_masters = []
 
-        self.add_retro_compat(kwargs)
-
         # Parameters managment ---------------------------------------------------------------------
         if cpu_type == "None":
             cpu_type = None
@@ -134,38 +146,31 @@ class SoCCore(Module):
         if not with_wishbone:
             self.soc_mem_map["csr"]  = 0x00000000
 
-        self.cpu_type    = cpu_type
-        self.cpu_variant = cpu.check_format_cpu_variant(cpu_variant)
+        self.cpu_type                   = cpu_type
+        self.cpu_variant                = cpu.check_format_cpu_variant(cpu_variant)
 
         self.integrated_rom_size        = integrated_rom_size
         self.integrated_rom_initialized = integrated_rom_init != []
         self.integrated_sram_size       = integrated_sram_size
         self.integrated_main_ram_size   = integrated_main_ram_size
 
-        assert csr_data_width in [8, 16, 32]
-        self.csr_data_width    = csr_data_width
-        self.csr_address_width = csr_address_width
-
-        assert csr_alignment in [32, 64]
+        self.csr_data_width             = csr_data_width
+        self.csr_address_width          = csr_address_width
 
-        self.with_ctrl = with_ctrl
+        self.with_ctrl                  = with_ctrl
 
-        self.with_uart     = with_uart
-        self.uart_baudrate = uart_baudrate
+        self.with_uart                  = with_uart
+        self.uart_baudrate              = uart_baudrate
 
-        self.with_wishbone           = with_wishbone
-        self.wishbone_timeout_cycles = wishbone_timeout_cycles
+        self.with_wishbone              = with_wishbone
+        self.wishbone_timeout_cycles    = wishbone_timeout_cycles
 
         # Modules instances ------------------------------------------------------------------------
 
-        # Add user's CSRs (needs to be done before the first dynamic allocation)
-        for _name, _id in self.csr_map.items():
-            self.add_csr(_name, _id)
-
         # Add SoCController
         if with_ctrl:
             self.submodules.ctrl = SoCController()
-            self.add_csr("ctrl", allow_user_defined=True)
+            self.add_csr("ctrl")
 
         # Add CPU
         self.config["CPU_TYPE"] = str(cpu_type).upper()
@@ -197,12 +202,12 @@ class SoCCore(Module):
             # Add CPU buses as 32-bit Wishbone masters
             for cpu_bus in self.cpu.buses:
                 assert cpu_bus.data_width in [32, 64, 128]
-                soc_bus = wishbone.Interface(data_width=32)
+                soc_bus = wishbone.Interface(data_width=self.bus.data_width)
                 self.submodules += wishbone.Converter(cpu_bus, soc_bus)
                 self.add_wb_master(soc_bus)
 
             # Add CPU CSR (dynamic)
-            self.add_csr("cpu", allow_user_defined=True)
+            self.add_csr("cpu")
 
             # Add CPU interrupts
             for _name, _id in self.cpu.interrupts.items():
@@ -258,23 +263,23 @@ class SoCCore(Module):
                 else:
                     self.submodules.uart_phy = uart.UARTPHY(platform.request(uart_name), clk_freq, uart_baudrate)
                 self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy))
-            self.add_csr("uart_phy", allow_user_defined=True)
-            self.add_csr("uart", allow_user_defined=True)
-            self.add_interrupt("uart", allow_user_defined=True)
+            self.add_csr("uart_phy")
+            self.add_csr("uart")
+            self.add_interrupt("uart")
 
         # Add Identifier
         if ident:
             if ident_version:
                 ident = ident + " " + get_version()
             self.submodules.identifier = identifier.Identifier(ident)
-            self.add_csr("identifier_mem", allow_user_defined=True)
+            self.add_csr("identifier_mem")
         self.config["CLOCK_FREQUENCY"] = int(clk_freq)
 
         # Add Timer
         if with_timer:
             self.submodules.timer0 = timer.Timer()
-            self.add_csr("timer0", allow_user_defined=True)
-            self.add_interrupt("timer0", allow_user_defined=True)
+            self.add_csr("timer0")
+            self.add_interrupt("timer0")
 
         # Add Wishbone to CSR bridge
         self.config["CSR_DATA_WIDTH"] = csr_data_width
@@ -293,77 +298,24 @@ class SoCCore(Module):
     # Methods --------------------------------------------------------------------------------------
 
     def add_interrupt(self, interrupt_name, interrupt_id=None, allow_user_defined=False):
-        # Check that interrupt_name is not already used
-        if interrupt_name in self.soc_interrupt_map.keys():
-            if allow_user_defined:
-                return
-            else:
-                raise ValueError("Interrupt conflict, {} name already used".format(interrupt_name))
-
-        # Check that interrupt_id is in range
-        if interrupt_id is not None and interrupt_id >= 32:
-            raise ValueError("{} Interrupt ID out of range ({}, max=31)".format(
-                interrupt_name, interrupt_id))
-
-        # Interrupt_id not provided: allocate interrupt to the first available id
-        if interrupt_id is None:
-            for n in range(32):
-                if n not in self.soc_interrupt_map.values():
-                    self.soc_interrupt_map.update({interrupt_name: n})
-                    return
-            raise ValueError("No more space to allocate {} interrupt".format(interrupt_name))
-        # Interrupt_id provided: check that interrupt_id is not already used and add interrupt
-        else:
-            for _name, _id in self.soc_interrupt_map.items():
-                if interrupt_id == _id:
-                    raise ValueError("Interrupt conflict, {} already used by {} interrupt".format(
-                        interrupt_id, _name))
-            self.soc_interrupt_map.update({interrupt_name: interrupt_id})
+        self.irq.add(interrupt_name, interrupt_id)
 
     def add_csr(self, csr_name, csr_id=None, allow_user_defined=False):
-        # Check that csr_name is not already used
-        if csr_name in self.soc_csr_map.keys():
-            if allow_user_defined:
-                return
-            else:
-                raise ValueError("CSR conflict, {} name already used".format(csr_name))
-
-        # Check that csr_id is in range
-        if csr_id is not None and csr_id >= 2**self.csr_address_width:
-            raise ValueError("{} CSR ID out of range ({}, max=31)".format(
-                csr_name, csr_id))
-
-        # csr_id not provided: allocate csr to the first available id
-        if csr_id is None:
-            for n in range(2**self.csr_address_width):
-                if n not in self.soc_csr_map.values():
-                    self.soc_csr_map.update({csr_name: n})
-                    return
-            raise ValueError("No more space to allocate {} csr".format(csr_name))
-        # csr_id provided: check that csr_id is not already used and add csr
-        else:
-            for _name, _id in self.soc_csr_map.items():
-                if csr_id == _id:
-                    raise ValueError("CSR conflict, {} already used by {} csr".format(
-                        csr_id, _name))
-            self.soc_csr_map.update({csr_name: csr_id})
+        self.csr.add(csr_name, csr_id)
 
     def initialize_rom(self, data):
         self.rom.mem.init = data
 
     def add_wb_master(self, wbm):
-        if self.finalized:
-            raise FinalizeError
-        self._wb_masters.append(wbm)
+        self.bus.add_master(master=wbm)
 
-    def add_wb_slave(self, address_or_address_decoder, interface, size=None):
-        if self.finalized:
-            raise FinalizeError
-        if size is not None:
-            address_decoder = mem_decoder(address_or_address_decoder, size)
-        else:
-            address_decoder = address_or_address_decoder
-        self._wb_slaves.append((address_decoder, interface))
+    def add_wb_slave(self, address, interface, size=None):
+        wb_name = None
+        for name, region in self.bus.regions.items():
+            if address == region.origin:
+                wb_name = name
+                break
+        self.bus.add_slave(name=wb_name, slave=interface)
 
     def add_csr_master(self, csrm):
         # CSR masters are not arbitrated, use this with precaution.
@@ -386,107 +338,17 @@ class SoCCore(Module):
                 msg += "- 0x{:08x}-0x{:08x}\n".format(region_origin, region_origin + region_length - 1)
         raise ValueError(msg)
 
-    @staticmethod
-    def check_regions_overlap(regions):
-        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]
-                if ("linker" in r0.type) or ("linker" in r1.type):
-                    continue
-                if r0.origin >= (r1.origin + r1.length):
-                    continue
-                if r1.origin >= (r0.origin + r0.length):
-                    continue
-                return (n0, n1)
-            i += 1
-        return None
-
-    def alloc_mem_region(self, name, length, type):
-        # Use type to limit search regions
-        search_regions = []
-        if "io" in type:
-            for _origin, _length in self.soc_io_regions.items():
-                search_regions.append(SoCMemRegion(origin=_origin, length=_length, type=type))
-        else:
-            search_regions.append(SoCMemRegion(origin=0x00000000, length=0x80000000, type=type))
-        # Iterate over search_regions and origin to find a candidate region
-        for search_region in search_regions:
-            origin = search_region.origin
-            while (origin + length) < (search_region.origin + search_region.length):
-                # Create a candidate mem region.
-                candidate_region  = SoCMemRegion(origin=origin, length=length, type=type)
-                candidate_overlap = False
-                # Check candidate does not overlap with allocated mem regions.
-                for _, allocated_region in self.mem_regions.items():
-                    if self.check_regions_overlap({"0": allocated_region, "1": candidate_region}) is not None:
-                        origin = allocated_region.origin + allocated_region.length
-                        candidate_overlap = True
-                        break
-                if not candidate_overlap:
-                    # If no overlap, the candidate is selected.
-                    #print("{} region allocated at: {:08x}".format(name, candidate_region.origin))
-                    return candidate_region
-        msg = "Not enough addressable memory space to allocate mem region {} of length 0x{:0x}".format(
-            name, length)
-        raise ValueError(msg)
-
-    def add_mem_region(self, name, length, type="cached"):
-        # Check name conflicts.
-        if name in self.mem_regions.keys():
-            raise ValueError("Memory region conflict, {} name already used".format(name))
-        # Round length to next power of 2.
-        length = 2**log2_int(length, False)
-        # Get mem origin; if not defined in mem_map, allocate a new mem region, else use mem_map
-        # mapping and check for type/overlap conflicts.
-        origin = self.mem_map.get(name, None)
-        if origin is None:
-            self.mem_regions[name] = self.alloc_mem_region(name, length, type)
-        else:
-            # Check type
-            if "io" in type:
-                self.check_io_region(name,origin, length)
-            # Add region
-            self.mem_regions[name] = SoCMemRegion(origin, length, type)
-            # Check overlap
-            overlap = self.check_regions_overlap(self.mem_regions)
-            if overlap is not None:
-                o0, o1 = overlap[0], overlap[1]
-                raise ValueError("Memory region conflict between {} ({}) and {} ({})".format(
-                    o0, self.mem_regions[o0],
-                    o1, self.mem_regions[o1],
-                ))
-
-    # FIXME: add deprecated warning?
-    def add_memory_region(self, name, origin, length, type="cached", io_region=False):
-        if io_region: # 2019-10-30: io_region retro-compatibility
-            deprecated_warning(": io_region replaced by type=\"io\".")
-            type = "io"
-        assert name in self.mem_map.keys()
-        self.add_mem_region(name, length, type)
+    def add_memory_region(self, name, origin, length, type="cached"):
+        self.bus.add_region(name, SoCRegion(origin=origin, size=length, cached="cached" in type))
 
     def register_mem(self, name, address, interface, size=0x10000000):
-        self.add_wb_slave(address, interface, size)
-        self.add_memory_region(name, address, size)
+        self.bus.add_slave(name, interface, SoCRegion(origin=address, size=size))
 
     def register_rom(self, interface, rom_size=0xa000):
-        self.add_wb_slave(self.soc_mem_map["rom"], interface, rom_size)
-        self.add_memory_region("rom", self.cpu.reset_address, rom_size)
-
-    def check_csr_range(self, name, addr):
-        if addr >= 1<<(self.csr_address_width+2):
-            raise ValueError("{} CSR out of range, increase csr_address_width".format(name))
-
-    def check_csr_region(self, name, origin):
-        for n, r in self.csr_regions.items():
-            if n == name or r.origin == origin:
-                raise ValueError("CSR region conflict between {} and {}".format(n, name))
+        self.bus.add_slave("rom", interface, SoCRegion(origin=self.cpu.reset_address, size=rom_size))
 
     def add_csr_region(self, name, origin, busword, obj):
         self.check_io_region(name, origin, 0x800)
-        self.check_csr_region(name, origin)
         self.csr_regions[name] = SoCCSRRegion(origin, busword, obj)
 
     def add_constant(self, name, value=None):
@@ -498,12 +360,12 @@ class SoCCore(Module):
         if memory is not None:
             name = name + "_" + memory.name_override
         try:
-            return self.soc_csr_map[name]
+            return self.csr.csrs[name]
         except KeyError as e:
             msg = "Undefined \"{}\" CSR.\n".format(name)
             msg += "Avalaible CSRs in {} ({}):\n".format(
                 self.__class__.__name__, inspect.getfile(self.__class__))
-            for k in sorted(self.soc_csr_map.keys()):
+            for k in sorted(self.csr.csrs.keys()):
                 msg += "- {}\n".format(k)
             raise RuntimeError(msg)
         except ValueError:
@@ -515,18 +377,22 @@ class SoCCore(Module):
     # Finalization ---------------------------------------------------------------------------------
 
     def do_finalize(self):
+        # retro-compat
+        for region in self.bus.regions.values():
+            region.length = region.size
+            region.type   = "cached" if region.cached else "io"
+
         # Verify CPU has required memories
         if not isinstance(self.cpu, cpu.CPUNone):
             for name in ["rom", "sram"]:
-                if name not in self.mem_regions.keys():
+                if name not in self.bus.regions.keys():
                     raise FinalizeError("CPU needs \"{}\" to be defined as memory or linker region".format(name))
 
+        SoC.do_finalize(self)
+
         # Add the Wishbone Masters/Slaves interconnect
-        if len(self._wb_masters):
-            self.submodules.wishbonecon = wishbone.InterconnectShared(self._wb_masters,
-                self._wb_slaves, register=True, timeout_cycles=self.wishbone_timeout_cycles)
-            if self.with_ctrl and (self.wishbone_timeout_cycles is not None):
-                self.comb += self.ctrl.bus_error.eq(self.wishbonecon.timeout.error)
+        if self.with_ctrl and (self.wishbone_timeout_cycles is not None):
+            self.comb += self.ctrl.bus_error.eq(self.bus_interconnect.timeout.error)
 
         # Collect and create CSRs
         self.submodules.csrbankarray = csr_bus.CSRBankArray(self,
@@ -543,13 +409,11 @@ class SoCCore(Module):
 
         # Check and add CSRs regions
         for name, csrs, mapaddr, rmap in self.csrbankarray.banks:
-            self.check_csr_range(name, 0x800*mapaddr)
             self.add_csr_region(name, (self.soc_mem_map["csr"] + 0x800*mapaddr),
                 self.csr_data_width, csrs)
 
         # Check and add Memory regions
         for name, memory, mapaddr, mmap in self.csrbankarray.srams:
-            self.check_csr_range(name, 0x800*mapaddr)
             self.add_csr_region(name + "_" + memory.name_override,
                 (self.soc_mem_map["csr"] + 0x800*mapaddr),
                 self.csr_data_width, memory)
@@ -559,8 +423,7 @@ class SoCCore(Module):
 
         # Add CSRs / Config items to constants
         for name, constant in self.csrbankarray.constants:
-            self.add_constant(name.upper() + "_" + constant.name.upper(),
-                              constant.value.value)
+            self.add_constant(name.upper() + "_" + constant.name.upper(), constant.value.value)
         for name, value in sorted(self.config.items(), key=itemgetter(0)):
             self.add_constant("CONFIG_" + name.upper(), value)
             if isinstance(value, str):
@@ -568,7 +431,7 @@ class SoCCore(Module):
 
         # Connect interrupts
         if hasattr(self.cpu, "interrupt"):
-            for _name, _id in sorted(self.soc_interrupt_map.items()):
+            for _name, _id in sorted(self.irq.irqs.items()):
                 if _name in self.cpu.interrupts.keys():
                     continue
                 if hasattr(self, _name):
@@ -577,26 +440,6 @@ class SoCCore(Module):
                     self.comb += self.cpu.interrupt[_id].eq(module.ev.irq)
                 self.constants[_name.upper() + "_INTERRUPT"] = _id
 
-
-    # API retro-compatibility layer ----------------------------------------------------------------
-    # Allow user to build the design the old API for ~3 months after the API change is introduced.
-    # Adds warning and artificical delay to encourage user to update.
-
-    def add_retro_compat(self, kwargs):
-        # 2019-10-09 : deprecate shadow_base, introduce io_regions
-        if "shadow_base" in kwargs.keys():
-            deprecated_warning(": shadow_base replaced by IO regions.")
-        self.retro_compat_shadow_base = kwargs.get("shadow_base", 0x80000000)
-        self.config["SHADOW_BASE"] = self.retro_compat_shadow_base
-
-    def __getattr__(self, name):
-        # 2019-10-09: deprecate shadow_base, introduce io_regions
-        if name == "shadow_base":
-            deprecated_warning(": shadow_base replaced by IO regions.")
-            return self.retro_compat_shadow_base
-        else:
-            return Module.__getattr__(self, name)
-
 # SoCCore arguments --------------------------------------------------------------------------------
 
 def soc_core_args(parser):