| # Copyright lowRISC contributors. | 
 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | # SPDX-License-Identifier: Apache-2.0 | 
 |  | 
 | '''Support code for bit ranges in reggen''' | 
 |  | 
 | from typing import Tuple | 
 |  | 
 | from reggen.lib import check_str | 
 | from reggen.params import ReggenParams | 
 |  | 
 |  | 
 | class Bits: | 
 |     def __init__(self, msb: int, lsb: int): | 
 |         assert 0 <= lsb <= msb | 
 |         self.msb = msb | 
 |         self.lsb = lsb | 
 |  | 
 |     def bitmask(self) -> int: | 
 |         return (1 << (self.msb + 1)) - (1 << self.lsb) | 
 |  | 
 |     def width(self) -> int: | 
 |         return 1 + self.msb - self.lsb | 
 |  | 
 |     def max_value(self) -> int: | 
 |         return (1 << self.width()) - 1 | 
 |  | 
 |     def extract_field(self, reg_val: int) -> int: | 
 |         return (reg_val & self.bitmask()) >> self.lsb | 
 |  | 
 |     @staticmethod | 
 |     def from_raw(where: str, | 
 |                  reg_width: int, | 
 |                  params: ReggenParams, | 
 |                  raw: object) -> 'Bits': | 
 |         # Bits should be specified as msb:lsb or as just a single bit index. | 
 |         if isinstance(raw, int): | 
 |             msb = raw | 
 |             lsb = raw | 
 |         else: | 
 |             str_val = check_str(raw, 'bits field for {}'.format(where)) | 
 |             msb, lsb = Bits._parse_str(where, params, str_val) | 
 |  | 
 |         # Check that the bit indices look sensible | 
 |         if msb < lsb: | 
 |             raise ValueError('msb for {} is {}: less than {}, the msb.' | 
 |                              .format(where, msb, lsb)) | 
 |         if lsb < 0: | 
 |             raise ValueError('lsb for {} is {}, which is negative.' | 
 |                              .format(where, lsb)) | 
 |         if msb >= reg_width: | 
 |             raise ValueError("msb for {} is {}, which doesn't fit in {} bits." | 
 |                              .format(where, msb, reg_width)) | 
 |  | 
 |         return Bits(msb, lsb) | 
 |  | 
 |     @staticmethod | 
 |     def _parse_str(where: str, | 
 |                    params: ReggenParams, | 
 |                    str_val: str) -> Tuple[int, int]: | 
 |         try: | 
 |             idx = int(str_val) | 
 |             return (idx, idx) | 
 |         except ValueError: | 
 |             # Doesn't look like an integer. Never mind: try msb:lsb | 
 |             pass | 
 |  | 
 |         parts = str_val.split(':') | 
 |         if len(parts) != 2: | 
 |             raise ValueError('bits field for {} is not an ' | 
 |                              'integer or of the form msb:lsb. Saw {!r}.' | 
 |                              .format(where, str_val)) | 
 |         return (params.expand(parts[0], | 
 |                               'msb of bits field for {}'.format(where)), | 
 |                 params.expand(parts[1], | 
 |                               'lsb of bits field for {}'.format(where))) | 
 |  | 
 |     def make_translated(self, bit_offset: int) -> 'Bits': | 
 |         assert 0 <= bit_offset | 
 |         return Bits(self.msb + bit_offset, self.lsb + bit_offset) | 
 |  | 
 |     def as_str(self) -> str: | 
 |         if self.lsb == self.msb: | 
 |             return str(self.lsb) | 
 |         else: | 
 |             assert self.lsb < self.msb | 
 |             return '{}:{}'.format(self.msb, self.lsb) | 
 |  | 
 |     def __eq__(self, other: object) -> bool: | 
 |         if not isinstance(other, Bits): | 
 |             return NotImplemented | 
 |         return (self.lsb == other.lsb) and (self.msb == other.msb) | 
 |  | 
 |     def __str__(self) -> str: | 
 |         return '[{}:{}]'.format(self.msb, self.lsb) |