1# 2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 3# 4# SPDX-License-Identifier: GPL-2.0-only 5# 6 7import functools 8import hardware 9 10 11@functools.total_ordering 12class Region: 13 ''' Represents a region of memory. ''' 14 15 def __init__(self, base: int, size: int, owner: 'WrappedNode' = None): 16 self.base = base 17 self.size = size 18 self.owner = owner 19 20 @staticmethod 21 def clone(other): 22 return Region(other.base, other.size) 23 24 def __repr__(self): 25 ''' Returns a string representation that is a valid Python expression 26 that eval() can parse. ''' 27 return 'Region(base=0x{:x},size=0x{:x})'.format(self.base, self.size) 28 29 def __str__(self): 30 ''' Returns a string representation. ''' 31 return 'Region [0x{:x}..0x{:x}] ({:d} bytes)'.format( 32 self.base, 33 self.base + self.size - (1 if self.size > 0 else 0), 34 self.size) 35 36 def __eq__(self, other): 37 return self.base == other.base and self.size == other.size 38 39 def __ne__(self, other): 40 # Needed only for py2. 41 return not self.__eq__(other) 42 43 def __gt__(self, other): 44 return self.base > other.base 45 46 def __hash__(self): 47 return hash((self.base, self.size)) 48 49 @staticmethod 50 def from_range(start, end, owner=None): 51 ''' create a region from a start/end rather than start/size ''' 52 if start > end: 53 raise ValueError( 54 'invalid rage start (0x{:x}) > end (0x{:x})'.format(start > end)) 55 return Region(start, end - start, owner) 56 57 def overlaps(self, other): 58 ''' returns True if this region overlaps the given region ''' 59 # Either our base is first, and to overlap our end must be > other.base 60 # or other.base is first, and to overlap other's end must be > self.base 61 return (self.base <= other.base and (self.base + self.size) > other.base) \ 62 or (other.base <= self.base and (other.base + other.size) > self.base) 63 64 def reserve(self, excluded): 65 ''' returns an array of regions that represent this region 66 minus the excluded range ''' 67 if not self.overlaps(excluded): 68 return [Region(self.base, self.size, self.owner)] 69 70 ret = [] 71 if self.base < excluded.base: 72 # the first region is from our base to excluded.base 73 ret.append(Region.from_range(self.base, excluded.base, self.owner)) 74 # skip the region from excluded.base - excluded.base + excluded.size 75 # if there's anything left, add it. 76 if (excluded.base + excluded.size) < (self.base + self.size): 77 ret.append(Region.from_range(excluded.base + excluded.size, 78 self.base + self.size, self.owner)) 79 else: # self.base >= excluded.base 80 # we skip the first chunk 81 # we add what's left after the current chunk. 82 if (self.base + self.size) > (excluded.base + excluded.size): 83 ret.append(Region.from_range(excluded.base + excluded.size, 84 self.base + self.size, self.owner)) 85 return ret 86 87 def align_base(self, align_bits): 88 ''' align this region up to a given number of bits ''' 89 new_base = hardware.utils.align_up(self.base, align_bits) 90 diff = new_base - self.base 91 if self.size < diff: 92 raise ValueError( 93 'can''t align region base to {} bits, {} too small'.format( 94 align_bits, self)) 95 # This could become an empty region now. We don't care, the caller has 96 # to check if this region still fits its needs. 97 return Region(new_base, self.size - diff, self.owner) 98 99 def align_size(self, align_bits): 100 ''' align this region's size to a given number of bits. 101 will move the base address down and the region's size 102 up ''' 103 new_base = hardware.utils.align_down(self.base, align_bits) 104 new_size = hardware.utils.align_up(self.size, align_bits) 105 return Region(new_base, new_size, self.owner) 106 107 def make_chunks(self, chunksz): 108 base = self.base 109 size = self.size 110 ret = [] 111 while size > 0: 112 ret.append(Region(base, min(size, chunksz), self.owner)) 113 base += chunksz 114 size -= chunksz 115 return ret 116