| # Copyright lowRISC contributors. |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import re |
| from typing import List, Tuple |
| |
| |
| class BitRanges: |
| '''Represents the bit ranges used for a field in an encoding scheme''' |
| def __init__(self, |
| mask: int, |
| ranges: List[Tuple[int, int]], |
| width: int) -> None: |
| self.mask = mask |
| self.ranges = ranges |
| self.width = width |
| |
| @staticmethod |
| def from_list(ranges: List[Tuple[int, int]]) -> 'BitRanges': |
| mask = 0 |
| width = 0 |
| for msb, lsb in ranges: |
| assert 0 <= lsb <= msb <= 31 |
| rng_mask = (1 << (msb + 1)) - (1 << lsb) |
| assert not (rng_mask & mask) |
| mask |= rng_mask |
| width += msb - lsb + 1 |
| |
| return BitRanges(mask, ranges, width) |
| |
| @staticmethod |
| def from_yaml(as_string: str, what: str) -> 'BitRanges': |
| # ranges ::= range |
| # | range ',' ranges |
| # |
| # range ::= num |
| # | num ':' num |
| # |
| # Ranges are assumed to be msb:lsb (with msb >= lsb). Bit indices are |
| # at most 31 and ranges are disjoint. |
| |
| if not as_string: |
| raise ValueError('Empty string as bits for {}'.format(what)) |
| |
| overlaps = 0 |
| |
| mask = 0 |
| ranges = [] |
| width = 0 |
| |
| for rng in as_string.split(','): |
| match = re.match(r'([0-9]+)(?:-([0-9]+))?$', rng) |
| if match is None: |
| raise ValueError('Range {!r} in bits for {} is malformed.' |
| .format(rng, what)) |
| |
| msb = int(match.group(1)) |
| maybe_lsb = match.group(2) |
| lsb = msb if maybe_lsb is None else int(maybe_lsb) |
| |
| if msb < lsb: |
| raise ValueError('Range {!r} in bits for {} has msb < lsb.' |
| .format(rng, what)) |
| |
| if msb >= 32: |
| raise ValueError('Range {!r} in bits for {} has msb >= 32.' |
| .format(rng, what)) |
| |
| rng_mask = (1 << (msb + 1)) - (1 << lsb) |
| overlaps |= rng_mask & mask |
| mask |= rng_mask |
| |
| ranges.append((msb, lsb)) |
| width += msb - lsb + 1 |
| |
| if overlaps: |
| raise ValueError('Bits for {} have overlapping ranges ' |
| '(mask: {:#08x})' |
| .format(what, overlaps)) |
| |
| return BitRanges(mask, ranges, width) |
| |
| def __eq__(self, other: object) -> bool: |
| return isinstance(other, BitRanges) and self.ranges == other.ranges |
| |
| def encode(self, value: int) -> int: |
| '''Encode the given value as bit fields''' |
| ret = 0 |
| bits_taken = 0 |
| for msb, lsb in self.ranges: |
| rng_width = msb - lsb + 1 |
| value_msb = self.width - 1 - bits_taken |
| value_lsb = value_msb - rng_width + 1 |
| |
| rng_mask = (1 << rng_width) - 1 |
| rng_value = (value >> value_lsb) & rng_mask |
| ret |= rng_value << lsb |
| bits_taken += rng_width |
| |
| assert bits_taken == self.width |
| return ret |
| |
| def decode(self, raw: int) -> int: |
| '''Extract the bit fields from the given value''' |
| ret = 0 |
| for msb, lsb in self.ranges: |
| width = msb - lsb + 1 |
| mask = (1 << width) - 1 |
| |
| ret <<= width |
| ret |= (raw >> lsb) & mask |
| return ret |