soc_core: add new alloc_mem/add_mem_region to allow automatic allocation of memory...
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 20 Jan 2020 11:05:08 +0000 (12:05 +0100)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 20 Jan 2020 11:05:08 +0000 (12:05 +0100)
With add_memory_region, user needs to provide the memory origin, which should not be needed since
could be retrieved from mem_map and prevent automatic allocation which is already possible for csr
and interrupts.

New add_mem_region method now allows both: defining the memory origin in mem_map (which will then
be used) or let the SoC builder automatically find and allocate a memory region.

litex/soc/integration/soc_core.py

index a85dd0d6c29a0a0323971556fc1c9c8b32129b4b..05e60bf1467b1563683a94d3f8c47d80bfe21019 100644 (file)
@@ -403,23 +403,68 @@ class SoCCore(Module):
             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"
-        length = 2**log2_int(length, False)
-        if "io" in type:
-            self.check_io_region(name, origin, length)
-        if name in self.mem_regions.keys():
-            raise ValueError("Memory region conflict, {} name already used".format(name))
-        self.mem_regions[name] = SoCMemRegion(origin, length, type)
-        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],
-            ))
+        assert name in self.mem_map.keys()
+        self.add_mem_region(name, length, type)
 
     def register_mem(self, name, address, interface, size=0x10000000):
         self.add_wb_slave(address, interface, size)