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