fhdl.ast.Signal: implement width derivation from min/max.

This commit is contained in:
whitequark 2018-12-12 10:43:09 +00:00
parent bc60631d68
commit 4eadc1629a
2 changed files with 49 additions and 6 deletions

View file

@ -1,3 +1,4 @@
import builtins
from collections import OrderedDict from collections import OrderedDict
from collections.abc import Iterable, MutableMapping, MutableSet from collections.abc import Iterable, MutableMapping, MutableSet
@ -496,6 +497,11 @@ class Signal(Value, DUID):
If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements. If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements.
The ``reset`` value is only used as a combinatorial default or as the initial value. The ``reset`` value is only used as a combinatorial default or as the initial value.
Defaults to ``False``. Defaults to ``False``.
min : int or None
max : int or None
If `bits_sign` is `None`, the signal bit width and signedness are
determined by the integer range given by `min` (inclusive,
defaults to 0) and `max` (exclusive, defaults to 2).
Attributes Attributes
---------- ----------
@ -505,7 +511,7 @@ class Signal(Value, DUID):
reset : int reset : int
""" """
def __init__(self, bits_sign=1, name=None, reset=0, reset_less=False): def __init__(self, bits_sign=None, name=None, reset=0, reset_less=False, min=None, max=None):
super().__init__() super().__init__()
if name is None: if name is None:
@ -515,11 +521,28 @@ class Signal(Value, DUID):
name = "$signal" name = "$signal"
self.name = name self.name = name
if isinstance(bits_sign, int): if bits_sign is None:
bits_sign = bits_sign, False if min is None:
self.nbits, self.signed = bits_sign min = 0
if max is None:
max = 2
max -= 1 # make both bounds inclusive
if not min < max:
raise ValueError("Lower bound {!r} should be less than higher bound {!r}"
.format(min, max))
self.signed = min < 0 or max < 0
self.nbits = builtins.max(bits_for(min, self.signed), bits_for(max, self.signed))
elif isinstance(bits_sign, int):
if not (min is None or max is None):
raise ValueError("Only one of bits/signedness or bounds may be specified")
self.nbits, self.signed = 1, False
else:
self.nbits, self.signed = bits_sign
if not isinstance(self.nbits, int) or self.nbits < 0: if not isinstance(self.nbits, int) or self.nbits < 0:
raise TypeError("Width must be a positive integer") raise TypeError("Width must be a positive integer, not {!r}".format(self.nbits))
self.reset = reset self.reset = reset
self.reset_less = reset_less self.reset_less = reset_less

View file

@ -1,7 +1,7 @@
from collections import Iterable from collections import Iterable
__all__ = ["flatten", "union"] __all__ = ["flatten", "union", "log2_int", "bits_for"]
def flatten(i): def flatten(i):
@ -20,3 +20,23 @@ def union(i):
else: else:
r |= e r |= e
return r return r
def log2_int(n, need_pow2=True):
if n == 0:
return 0
r = (n - 1).bit_length()
if need_pow2 and (1 << r) != n:
raise ValueError("{} is not a power of 2".format(n))
return r
def bits_for(n, require_sign_bit=False):
if n > 0:
r = log2_int(n + 1, False)
else:
require_sign_bit = True
r = log2_int(-n, False)
if require_sign_bit:
r += 1
return r