tinyriscv/tools/regtool/reggen/bits.py

88 lines
2.8 KiB
Python

# 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 .lib import check_str
from .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)