1
0
mirror of https://github.com/myhdl/myhdl.git synced 2024-12-14 07:44:38 +08:00

Merge pull request #228 from qrqiuren/mep-111

[WIP] GSoC fixed-point compiler
This commit is contained in:
Keerthan Jaic 2018-01-09 22:57:25 -08:00 committed by GitHub
commit 50ebc9acbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1644 additions and 7 deletions

View File

@ -37,6 +37,7 @@ from myhdl._simulator import _futureEvents
from myhdl._simulator import _siglist
from myhdl._simulator import _signals
from myhdl._intbv import intbv
from myhdl._fixbv import fixbv
from myhdl._bin import bin
# from myhdl._enum import EnumItemType
@ -124,7 +125,7 @@ class _Signal(object):
'_setNextVal', '_copyVal2Next', '_printVcd',
'_driven', '_read', '_name', '_used', '_inList',
'_waiter', 'toVHDL', 'toVerilog', '_slicesigs',
'_numeric'
'_numeric', '_W'
)
def __init__(self, val=None):
@ -151,6 +152,17 @@ class _Signal(object):
elif isinstance(val, integer_types):
self._type = integer_types
self._setNextVal = self._setNextInt
elif isinstance(val, fixbv):
self._type = fixbv
if val._val is None:
raise ValueError("fixbv has not been initialized correctly")
self._W = val._W
self._nrbits = val._nrbits
self._setNextVal = self._setNextFixbv
if self._nrbits:
self._printVcd = self._printVcdVec
else:
self._printVcd = self._printVcdHex
elif isinstance(val, intbv):
self._type = intbv
self._min = val._min
@ -297,6 +309,12 @@ class _Signal(object):
raise TypeError("Expected int or intbv, got %s" % type(val))
self._next = val
def _setNextFixbv(self, val):
if not isinstance(val, fixbv):
raise TypeError("Expected fixbv, got %s" % type(val))
self._next[:] = val
self._next._handleBounds()
def _setNextIntbv(self, val):
if isinstance(val, intbv):
val = val._val
@ -509,22 +527,40 @@ class _Signal(object):
# comparisons
def __eq__(self, other):
return self.val == other
if isinstance(other, _Signal):
return self._val == other._val
else:
return self._val == other
def __ne__(self, other):
return self.val != other
if isinstance(other, _Signal):
return self._val != other._val
else:
return self._val != other
def __lt__(self, other):
return self.val < other
if isinstance(other, _Signal):
return self._val < other._val
else:
return self._val < other
def __le__(self, other):
return self.val <= other
if isinstance(other, _Signal):
return self._val <= other._val
else:
return self._val <= other
def __gt__(self, other):
return self.val > other
if isinstance(other, _Signal):
return self._val > other._val
else:
return self._val > other
def __ge__(self, other):
return self.val >= other
if isinstance(other, _Signal):
return self._val >= other._val
else:
return self._val >= other
# method lookup delegation
def __getattr__(self, attr):

View File

@ -149,6 +149,8 @@ from ._bin import bin
from ._concat import concat
from ._intbv import intbv
from ._modbv import modbv
from ._fixbv import fixbv
from ._resize import resize
from ._join import join
from ._Signal import posedge, negedge, Signal, SignalType
from ._ShadowSignal import ConcatSignal
@ -177,6 +179,8 @@ __all__ = ["bin",
"concat",
"intbv",
"modbv",
"fixbv",
"resize",
"join",
"posedge",
"negedge",

711
myhdl/_fixbv.py Normal file
View File

@ -0,0 +1,711 @@
# This file is part of the myhdl library, a Python package for using
# Python as a Hardware Description Language.
#
# Copyright (C) 2013 Christopher L. Felton
#
# The myhdl library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the
# License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
""" Module with the fixbv class """
from __future__ import print_function, absolute_import
import sys
import math
from myhdl._intbv import intbv
from myhdl._modbv import modbv
from myhdl._compat import integer_types
class FixedPointFormat(object):
def __init__(self, wl, iwl, fwl=None):
if fwl is None:
fwl = wl-iwl-1
assert wl == iwl+fwl+1, "Invalid %d %d %d" % (wl, iwl, fwl)
self._wl, self._iwl, self._fwl = wl, iwl, fwl
def __len__(self):
return 3
def __repr__(self):
s = "FixedPointFormat(%d, %d, [%d])" % (self._wl, self._iwl, self._fwl)
return s
def __str__(self):
s = "(%d, %d, [%d])" % (self._wl, self._iwl, self._fwl)
return s
def __getitem__(self, key):
val = [self._wl, self._iwl, self._fwl][key]
return val
def __setitem__(self, key, val):
if isinstance(key, (int)):
keys, vals = [key], [val]
elif isinstance(key, slice):
keys = range(*key.indices(3))
vals = val
for k, v in zip(keys, vals):
if k == 0:
self._wl = v
elif k == 1:
self._iwl = v
elif k == 2:
self._fwl = v
else:
raise AssertionError("Invalid index %d %s" % (key, type(key)))
def __eq__(self, other):
cmp = True
if isinstance(other, FixedPointFormat):
for s, o in zip(self[:], other[:]):
if s != o:
cmp = False
else:
cmp = False
return cmp
def __addsub__(self, other):
""" Addition or Subtraction grows the format by one
"""
assert isinstance(other, FixedPointFormat), \
"Invalid type for other %s" % (type(other))
iwl = max(self._iwl, other._iwl)+1
fwl = max(self._fwl, other._fwl)
wl = iwl+fwl+1
return FixedPointFormat(wl, iwl, fwl)
def __muldiv__(self, other):
""" Multiplication and Division double the word size
"""
assert isinstance(other, FixedPointFormat), \
"Invalid type for other %s" % (type(other))
wl = self._wl+other._wl
fwl = self._fwl+other._fwl
iwl = wl-fwl-1
return FixedPointFormat(wl, iwl, fwl)
def __add__(self, other):
return self.__addsub__(other)
def __sub__(self, other):
return self.__addsub__(other)
def __mul__(self, other):
return self.__muldiv__(other)
def __div__(self, other):
return self.__muldiv__(other)
class fixbv(modbv):
def __init__(self, val, min=None, max=None, res=None,
round_mode=None, overflow_mode=None):
# round mode and overflow mode
if round_mode is None:
self._round_mode = 'floor'
else:
self._round_mode = round_mode
if overflow_mode is None:
self._overflow_mode = 'saturate'
else:
self._overflow_mode = overflow_mode
format = None
val = float(val)
self._ifval = val # save the initial value
if None in (min, max, res):
# @todo: this is not working correctly
# min,max,res = self._calc_min_max_res(val)
self._val = None
return
# validate the range and resolution
if max < 1 or abs(min) < 1:
raise ValueError("Maximum and Minimum has to be 1 or greater")
if max is None or not isinstance(max, integer_types + (float,)):
raise ValueError("Maximum has to be provided, max=%s" % (str(max)))
if min is None or not isinstance(min, integer_types + (float,)):
raise ValueError("Minimum has to be provided, min=%s" % (str(min)))
if res is None:
raise ValueError("Resolution has to be provided, res=%s"
% (str(res)))
elif res <= 0 or res > 1:
raise ValueError("Resolution must be in range (0, 1], res=%s"
% (str(res)))
# calculate the integer and fractional widths
ival = abs(min) if abs(min) > max else max
niwl, nfwl = self._calc_width(ival, res)
nwl = niwl+nfwl+1
self._W = FixedPointFormat(nwl, niwl, nfwl)
self.__min, self.__max = min, max
# We want a signed number but we don't want to force any
# notion of a fixed point value to the lower levels. From
# the intbv point of view it only knows that it is a signed
# integer, this is enough information to enforce the rules.
# But intbv the min and max are the min/max for the number
# of bits we are creating.
nrbits = nwl # self._iwl + self._fwl + 1
# these will be overwritten by intbv
self._min = -1 * 2**(nrbits-1)
self._max = 2**(nrbits-1)
fxval = self._from_float(val)
intbv.__init__(self, fxval, min=self._min, max=self._max)
# self._fval = val
if self._nrbits != nrbits:
errstr = "ERROR: intbv num of bits != fixbv num of bits %d,%d" \
% (self._nrbits, nrbits)
raise ValueError(errstr)
# make sure things were setup ok
self._handleBounds()
def _handleBounds(self):
""" check bounds """
intbv._handleBounds(self)
######################################################################
# properties
######################################################################
@property
def format(self):
return tuple(self._W[:])
@property
def res(self):
_res = 2.0 ** (-1*self._W._fwl)
return _res
@property
def max(self):
return self.__max
@property
def min(self):
return self.__min
######################################################################
# overloaded functions
######################################################################
def __copy__(self):
min, max, res = self._minmaxres()
retval = fixbv(self._to_float(), min, max, res,
round_mode=self._round_mode,
overflow_mode=self._overflow_mode)
return retval
def __deepcopy__(self, visit):
min, max, res = self._minmaxres()
retval = fixbv(self._to_float(), min, max, res,
round_mode=self._round_mode,
overflow_mode=self._overflow_mode)
return retval
def __getitem__(self, key):
if isinstance(key, (tuple, list)):
fwl = key[2] if len(key) == 3 else key[0]-key[1]-1
res = 2**-(fwl)
nmax = 2**(key[1])
nmin = -nmax
if self._val is None:
fval = self._ifval
else:
fval = self._to_float()
return fixbv(fval, min=nmin, max=nmax, res=res,
round_mode=self._round_mode, overflow_mode=self._overflow_mode)
else:
# @todo: check for negative index and convert
# to the underlying intbv indexes
slc = intbv(self._val, _nrbits=self._nrbits)
return slc.__getitem__(key)
def __setitem__(self, key, val):
if isinstance(val, fixbv):
assert key == slice(None, None, None)
v = fixbv._round(val, self._W, self._round_mode)
v = fixbv._overflow(v, self._W, self._overflow_mode)
else:
v = val
# @todo: convert negative keys to the correct bit index
intbv.__setitem__(self, key, v)
def __repr__(self):
# fixbv(_fval, format=(%d,%d,%d))
rs = "fixbv(%f, " % (self._to_float())
wl, iwl, fwl = self._W[:]
fwl = wl-iwl-1
rs += " format=(%d,%d,%d), " % (wl, iwl, fwl)
rs += ")"
# @todo: ? add integer value somewhere?
return rs
def __str__(self):
# For very large bit widths the resolution of the fixbv
# will exceed those of a 64 bit float value. Need to use
# something more power when "displaying" the values, use the
# Decimal object to create a more accurate version of the
# underlying value.
# @todo: use *Decimal* and determine the number of of
# 10s digits required.
# intp = Decimal(self._iival) + 2**Decimal(-self._ifval)
fstr = "%f" % (self._to_float())
return fstr
def __hex__(self):
return hex(self._val)
def __float__(self):
return self._to_float() # self._fval
def __ord__(self):
return self._val
def __add__(self, other):
if isinstance(other, fixbv):
iW = self._W + other._W
else:
# Solve the case if `type(other) is Signal`
return other.__radd__(self) # TODO: Write test for it
# raise TypeError("other must be fixbv not %s" % (type(other)))
retval = fixbv(0)[iW[:]]
if self._W._fwl < other._W._fwl:
a = (self._val << (other._W._fwl - self._W._fwl))
b = other._val
else:
a = self._val
b = (other._val << (self._W._fwl - other._W._fwl))
retval._val = a + b
retval._handleBounds()
return retval
def __sub__(self, other):
if isinstance(other, fixbv):
iW = self._W + other._W
else:
# Solve the case if `type(other) is Signal`
return other.__rsub__(self) # TODO: Write test for it
# raise TypeError("other must be fixbv not %s" % (type(other)))
retval = fixbv(0)[iW[:]]
if self._W._fwl < other._W._fwl:
a = (self._val << (other._W._fwl - self._W._fwl))
b = other._val
else:
a = self._val
b = (other._val << (self._W._fwl - other._W._fwl))
retval._val = a - b
retval._handleBounds()
return retval
def __mul__(self, other):
if isinstance(other, fixbv):
iW = self._W * other._W
else:
# Solve the case if `type(other) is Signal`
return other.__rmul__(self) # TODO: Write test for it
# raise TypeError("other must be fixbv not %s" % (type(other)))
retval = fixbv(0)[iW[:]]
retval._val = self._val * other._val
retval._handleBounds()
return retval
def __pow__(self, other):
# @todo: a better way to do this, add __pow__ to FixedPointFormat?
# TODO: Deal with the case if other < 0 or other == 0
if not isinstance(other, integer_types):
raise TypeError("other must be integer type not %s" %
(type(other)))
if other < 2:
iW = self._W
else:
iW = self._W * self._W
for ii in range(2, other):
iW = iW * self._W
retval = fixbv(0)[iW[:]]
retval._val = self._val ** other
retval._handleBounds()
return retval
# all comparisons must be on aligned types
def __eq__(self, other):
if isinstance(other, fixbv):
pass
else:
# Solve the case if `type(other) is Signal`
return other.__eq__(self) # TODO: Write test for it
# raise TypeError("other must be fixbv not %s" % (type(other)))
if self._W._fwl < other._W._fwl:
a = (self._val << (other._W._fwl - self._W._fwl))
b = other._val
else:
a = self._val
b = (other._val << (self._W._fwl - other._W._fwl))
return a == b
def __ne__(self, other):
if isinstance(other, fixbv):
pass
else:
# Solve the case if `type(other) is Signal`
return other.__ne__(self) # TODO: Write test for it
# raise TypeError("other must be fixbv not %s" % (type(other)))
if self._W._fwl < other._W._fwl:
a = (self._val << (other._W._fwl - self._W._fwl))
b = other._val
else:
a = self._val
b = (other._val << (self._W._fwl - other._W._fwl))
return a != b
def __gt__(self, other):
if isinstance(other, fixbv):
pass
else:
# Solve the case if `type(other) is Signal`
return other.__le__(self) # TODO: Write test for it
# raise TypeError("other must be fixbv not %s" % (type(other)))
if self._W._fwl < other._W._fwl:
a = (self._val << (other._W._fwl - self._W._fwl))
b = other._val
else:
a = self._val
b = (other._val << (self._W._fwl - other._W._fwl))
return a > b
def __ge__(self, other):
if isinstance(other, fixbv):
pass
else:
# Solve the case if `type(other) is Signal`
return other.__lt__(self) # TODO: Write test for it
# raise TypeError("other must be fixbv not %s" % (type(other)))
if self._W._fwl < other._W._fwl:
a = (self._val << (other._W._fwl - self._W._fwl))
b = other._val
else:
a = self._val
b = (other._val << (self._W._fwl - other._W._fwl))
return a >= b
def __lt__(self, other):
if isinstance(other, fixbv):
pass
else:
# Solve the case if `type(other) is Signal`
return other.__ge__(self) # TODO: Write test for it
# raise TypeError("other must be fixbv not %s" % (type(other)))
if self._W._fwl < other._W._fwl:
a = (self._val << (other._W._fwl - self._W._fwl))
b = other._val
else:
a = self._val
b = (other._val << (self._W._fwl - other._W._fwl))
return a < b
def __le__(self, other):
if isinstance(other, fixbv):
pass
else:
# Solve the case if `type(other) is Signal`
return other.__gt__(self) # TODO: Write test for it
# raise TypeError("other must be fixbv no t%s" % (type(other)))
if self._W._fwl < other._W._fwl:
a = (self._val << (other._W._fwl - self._W._fwl))
b = other._val
else:
a = self._val
b = (other._val << (self._W._fwl - other._W._fwl))
return a <= b
######################################################################
# private methods
######################################################################
def _minmaxres(self):
""" get the min, max, res """
wl, iwl, fwl = self.format
max, min, res = 2**(iwl), -2**(iwl), 2**(-fwl)
return min, max, res
def _calc_width(self, val, res=0):
"""Caclulate the iwl and fwl required for the value
@todo: this function is not working!
"""
frac, integer = math.modf(val)
if res < frac or frac == 0:
frac = res
if abs(integer) == 0:
iwl = 0
else:
iwl = math.ceil(math.log(abs(integer), 2))
if frac == 0:
fwl = 0
else:
fwl = math.ceil(math.log(frac**-1, 2))
return (int(iwl), int(fwl))
def _calc_min_max_res(self, fval):
"""Given floating point number calculate min, max and resolution
Given a floating point number calculate the resolution required to
represent the floating-point in a fixed-point number.
"""
if fval == 0:
inbits = 1
fnbits = 1
else:
frac, integer = math.modf(fval)
frac = abs(frac)
integer = abs(integer)
try:
# adds an extra bit
if integer == 0:
inbits = 1
else:
inbits = int(abs(math.floor(math.log(integer, 2)))) + 1
# adds an extra bit
if frac == 0:
fnbits = 1
else:
fnbits = int(abs(math.floor(math.log(frac, 2)))) + 1
except:
print("Fractional %s Integer %s" % (frac, integer))
print("Unexpected error:", sys.exc_info()[0])
raise
fnbits = 1 if fnbits == 0 else fnbits
inbits = 1 if inbits == 0 else inbits
max = 2**(inbits-1)
min = -2**(inbits-1)
res = 2**(-fnbits)
# make sure limits are still applicable for the rounded
# version of fval if the value doesn't fit need an extra
# integer bit. This functions is mainly used if bit
# constraints are not give (determine bit contraints
# from value). Adding extra bit (case of round_mode=truncate)
# is ok.
if round(fval) >= max or round(fval) <= min:
max = 2**(inbits+1)
min = -2**(inbits+1)
return min, max, res
def _from_float(self, val):
"""Convert float value to fixed point"""
retval = fixbv._round(val, self._W, self._round_mode)
# retval = self._overflow(retval)
return int(retval)
def _to_float(self):
"""Convert fixed point value to floating point number"""
return float(self._val) / (2.0 ** self._W._fwl)
######################################################################
# private static methods
######################################################################
@staticmethod
def _round(val, fmt, round_mode):
"""Round the value into a new format"""
if isinstance(val, float):
val *= 2.0**fmt._fwl
if round_mode == 'ceil':
retval = math.ceil(val)
elif round_mode == 'fix':
if val > 0:
retval = math.floor(val)
else:
retval = math.ceil(val)
elif round_mode == 'floor':
retval = math.floor(val)
elif round_mode == 'nearest':
fval,ival = math.modf(val)
if fval == .5 or fval == -.5:
retval = int(val+1) if val > 0 else int(val-1)
else:
retval = round(val)
elif round_mode == 'round':
retval = round(val)
elif round_mode == 'round_even' or round_mode == 'convergent':
fval,ival = math.modf(val)
abs_ival = int(abs(ival))
sign = -1 if ival < 0 else 1
if (abs(fval) - 0.5) == 0.0:
if abs_ival%2 == 0:
retval = abs_ival * sign
else:
retval = (abs_ival + 1) * sign
else:
retval = round(val)
else:
raise ValueError("Invalid round mode %s" % round_mode)
return int(retval)
elif isinstance(val, fixbv):
if val._W._fwl <= fmt._fwl:
# Case when no rounding should apply
retval = (val._val << (fmt._fwl - val._W._fwl))
return retval
# Explanation of the variables
#
# flag A BC D
# val siiiii.ffffff
# fmt siiiii.ff
#
# A: sign_bit
# B: least_reserve_bit
# C: greatest_tail_bit
# C-D: tail_bits
# A-B: retval
# |C-D|: round_bits
round_bits = val._W._fwl - fmt._fwl
sign_bit = (val._val >> (val._W._wl - 1)) & 1
least_reserve_bit = (val._val >> round_bits) & 1
greatest_tail_bit = (val._val >> (round_bits - 1)) & 1
tail_bits = val._val & ((1 << round_bits) - 1)
retval = (val._val >> round_bits)
if round_mode == 'ceil':
if tail_bits != 0:
retval += 1
elif round_mode == 'floor':
pass
elif round_mode == 'fix':
# Ceil for negatives and floor for non-negatives
if sign_bit == 1 and tail_bits != 0:
retval += 1
elif round_mode == 'nearest':
if sign_bit == 0:
# positive
if greatest_tail_bit == 1:
retval += 1
else:
# negative
middle = (1 << (round_bits - 1))
if tail_bits > middle:
retval += 1
elif round_mode in ('round', 'round_even', 'convergent'):
middle = (1 << (round_bits - 1))
if tail_bits == middle:
# halfway
if least_reserve_bit == 1:
# odd number
retval += 1
elif tail_bits > middle:
retval += 1
else:
raise ValueError("Invalid round mode %s" % round_mode)
return retval
else:
raise TypeError("%s is neither float nor fixbv" % type(val))
@staticmethod
def _overflow(val, fmt, overflow_mode):
wl, iwl, fwl = fmt
if isinstance(val, fixbv):
val = val._val
mm = 2 ** (wl - 1)
elif isinstance(val, integer_types):
mm = 2 ** (wl - 1)
elif isinstance(val, float):
mm = 2 ** iwl
else:
raise TypeError("%s is neither float nor fixbv" % type(val))
mmin, mmax = -mm, mm
if overflow_mode == 'saturate':
if val >= mmax:
retval = mmax-1
elif val <= mmin:
retval = mmin
else:
retval = val
elif overflow_mode == 'ring' or overflow_mode == 'wrap':
if isinstance(val, float):
retval = math.fmod(val - mmin, mmax - mmin) + mmin
else:
retval = (val - mmin) % (mmax - mmin) + mmin
else:
raise ValueError("Invalid overflow mode %s" % overflow_mode)
return retval
######################################################################
# public methods
######################################################################
def int(self):
""" Retrieve the integer portion of the fixed-point
This function is convertible to V*. This will return
the integer portion as an integer.
"""
return self._val >> self._W._fwl
def frac(self):
""" Retrieve the fractional portion of hte fixed-point
This funciton is convertible to V*. This will return the
fraction portion as an integer.
"""
return self._val & ((1 << self._W._fwl) - 1)

174
myhdl/_resize.py Normal file
View File

@ -0,0 +1,174 @@
# This file is part of the myhdl library, a Python package for using
# Python as a Hardware Description Language.
#
# Copyright (C) 2013 Christopher L. Felton
#
# The myhdl library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation; either version 2.1 of the
# License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
""" Module with the fixbv resize function """
from __future__ import absolute_import
import math
from myhdl._fixbv import fixbv
from myhdl._fixbv import FixedPointFormat
# round :
ROUND_MODES = ( # towards :
'ceil', # +infinity: always round up
'fix', # 0 : always down
'floor', # -infinity: truncate, always round down
'nearest', # nearest : tie towards largest absolute value
'round', # nearest : ties to +infinity
'convergent', # nearest : tie to closest even (round_even)
'round_even', # nearest : tie to closest even (convergent)
)
OVERFLOW_MODES = (
'saturate',
'ring',
'wrap',
)
def is_round_mode(mode):
if mode.lower() in ROUND_MODES:
found = True
else:
# @todo: is there a close match?
found = False
return found
def is_overflow_mode(mode):
if mode.lower() in OVERFLOW_MODES:
found = True
else:
# @todo: is there a close match?
found = False
return found
def _overflow(val, fmt, overflow_mode):
"""handle overflow"""
assert is_overflow_mode(overflow_mode)
wl,iwl,fwl = fmt
mm = 2**(wl-1)
mmin,mmax = -mm,mm
#print(" [rsz][ovl]: %f %d %d, %s" % (val, mmin, mmax, fmt))
if overflow_mode == 'saturate':
if val >= mmax:
retval = mmax-1
elif val <= mmin:
retval = mmin
else:
retval = val
elif overflow_mode == 'ring' or overflow_mode == 'wrap':
retval = (val - mmin) % (mmax - mmin) + mmin
else:
raise ValueError
return retval
def _round(val, fmt, round_mode):
"""Round the initial value if needed"""
# Scale the value to the integer range (the underlying representation)
assert is_round_mode(round_mode)
assert isinstance(fmt, tuple)
wl,iwl,fwl = fmt
_val = val
val = val * 2.0**fwl
#print(" [rsz][rnd]: %f %f, %s" % (val, _val, fmt))
if round_mode == 'ceil':
retval = math.ceil(val)
elif round_mode == 'fix':
if val > 0:
retval = math.floor(val)
else:
retval = math.ceil(val)
elif round_mode == 'floor':
retval = math.floor(val)
elif round_mode == 'nearest':
fval,ival = math.modf(val)
if fval == .5:
retval = int(val+1) if val > 0 else int(val-1)
else:
retval = round(val)
elif round_mode == 'round':
retval = round(val)
elif round_mode == 'round_even' or round_mode == 'convergent':
fval,ival = math.modf(val)
abs_ival = int(abs(ival))
sign = -1 if ival < 0 else 1
if (abs(fval) - 0.5) == 0.0:
if abs_ival%2 == 0:
retval = abs_ival * sign
else:
retval = (abs_ival + 1) * sign
else:
retval = round(val)
else:
raise TypeError("invalid round mode!" % self.round_mode)
return int(retval)
def resize(val, fmt, round_mode='convergent', overflow_mode='saturate'):
"""
"""
if isinstance(fmt, fixbv):
fmt = fmt.format
elif isinstance(fmt, FixedPointFormat):
fmt = tuple(fmt[:])
elif isinstance(fmt, tuple):
fmt = fmt
else:
pass
if isinstance(val, fixbv):
fval = float(val)
elif isinstance(val, float):
fval = val
else:
pass
wl,iwl,fwl = fmt
mm = 2**iwl
res = 2**-fwl
rfx = fixbv(0, min=-mm, max=mm, res=res)
assert (wl,iwl,fwl,) == rfx.format, "%d,%d,%d != %s" % (wl,iwl,fwl, repr(rfx))
ival = _round(fval, fmt, round_mode=round_mode)
ival = _overflow(ival, fmt, overflow_mode=overflow_mode)
rfx._val = ival
rfx._handleBounds()
return rfx

View File

@ -0,0 +1,68 @@
from __future__ import absolute_import
from unittest import TestCase
from myhdl import fixbv, Signal, block, instance, always_comb, delay, StopSimulation, toVerilog
@block
def fixop1(x, y, z, w):
@always_comb
def inst():
if x + y > z:
w.next = z - y
else:
w.next = y + x * z
return inst
@block
def fixop2(x, y, z, w):
@instance
def inst():
while 1:
yield x, y, z
if x + y > z:
w.next = z - y
else:
w.next = y + x * z
return inst
class FixbvTest(TestCase):
@block
def bench(self, fixop):
x = Signal(fixbv(0.125, min=-8, max=8, res=2**-5,
round_mode='round', overflow_mode='saturate'))
y = Signal(fixbv(-2.25, min=-8, max=8, res=2**-6,
round_mode='round', overflow_mode='saturate'))
z = Signal(fixbv(1.125, min=-8, max=8, res=2**-7,
round_mode='round', overflow_mode='saturate'))
w = Signal(fixbv(0, min=-8, max=8, res=2**-4,
round_mode='round', overflow_mode='saturate'))
w_v = Signal(fixbv(0, round_mode='round', overflow_mode='saturate')[8, 3, 4])
fixop_inst = fixop(x, y, z, w).convert(hdl='verilog')
fixop_v_inst = fixop(x, y, z, w)
@instance
def stimulus():
print(w, w_v)
yield delay(10)
print(w, w_v)
assert w == w_v
assert float(w) == -2.125
return stimulus
def test_fixop1(self):
sim = self.bench(fixop1)
sim.run_sim()
def test_fixop2(self):
sim = self.bench(fixop2)
sim.run_sim()
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,64 @@
from __future__ import absolute_import
from unittest import TestCase
from myhdl import fixbv, Signal, block, instance, always_comb, delay, StopSimulation, toVerilog
@block
def fixop1(x, y, z, w):
@always_comb
def inst():
if x + y > z:
w.next = z - y
else:
w.next = y + x - z
return inst
@block
def fixop2(x, y, z, w):
@instance
def inst():
while 1:
yield x, y, z
if x + y > z:
w.next = z - y
else:
w.next = y + x - z
return inst
class FixbvTest(TestCase):
@block
def bench(self, fixop):
x = Signal(fixbv(0.125, min=-8, max=8, res=2**-4))
y = Signal(fixbv(-2.25, min=-8, max=8, res=2**-4))
z = Signal(fixbv(1.125, min=-8, max=8, res=2**-4))
w = Signal(fixbv(0, min=-8, max=8, res=2**-4))
w_v = Signal(fixbv(0)[8, 3, 4])
fixop_inst = fixop(x, y, z, w).convert(hdl='verilog')
fixop_v_inst = fixop(x, y, z, w)
@instance
def stimulus():
print(w, w_v)
yield delay(10)
print(w, w_v)
assert w == w_v
assert float(w) == -3.25
return stimulus
def test_fixop1(self):
sim = self.bench(fixop1)
sim.run_sim()
def test_fixop2(self):
sim = self.bench(fixop2)
sim.run_sim()
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,580 @@
from __future__ import division
import math
from random import randint
import pytest
import myhdl
from myhdl import *
from myhdl import fixbv
from myhdl._fixbv import FixedPointFormat as fpf
def test_fpf_inst():
ewl,eiwl,efwl = (8,4,3)
W1 = fpf(8,4,3)
W2 = fpf(8,4)
W3 = fpf(wl=8,iwl=4,fwl=3)
W4 = fpf(*(8,4,3,))
for ww in (W1,W2,W3,W4):
wl,iwl,fwl = ww[:]
assert wl == ewl
assert iwl == eiwl
assert fwl == efwl
def test_fpf_add():
W1 = fpf(8,4)
W2 = fpf(8,4)
Wa = W1+W2
wl,iwl,fwl = Wa[:]
assert iwl == 5, '%s+%s==%s' % (W1,W2,Wa)
assert fwl == 3, '%s+%s==%s' % (W1,W2,Wa)
assert wl == 9, '%s+%s==%s' % (W1,W2,Wa)
W1 = fpf(8,4) # W8.4.3
W2 = fpf(8,3) # W8.3.4
Wa = W1+W2
wl,iwl,fwl = Wa[:]
assert iwl == 5, '%s+%s==%s' % (W1,W2,Wa)
assert fwl == 4, '%s+%s==%s' % (W1,W2,Wa)
assert wl == 10, '%s+%s==%s' % (W1,W2,Wa)
W1 = fpf(16,0) # W16.0.15
W2 = fpf(16,0) # W16.0.15
Wa = W1+W2
wl,iwl,fwl = Wa[:]
assert iwl == 1, '%s+%s==%s' % (W1,W2,Wa)
assert fwl == 15, '%s+%s==%s' % (W1,W2,Wa)
assert wl == 17, '%s+%s==%s' % (W1,W2,Wa)
W1 = fpf(16,1) # W16.1.14
W2 = fpf(16,3) # W16.3.12
Wa = W1+W2
wl,iwl,fwl = Wa[:]
assert iwl == 4, '%s+%s==%s' % (W1,W2,Wa)
assert fwl == 14, '%s+%s==%s' % (W1,W2,Wa)
assert wl == 19, '%s+%s==%s' % (W1,W2,Wa)
# negative integer widths, fwl larger than the iwl
# wl(4) = iwl(-4) + fwl + 1
# fwl = wl(4) - iwl(-4) -1
# fwl = 7
# when adding the total word-length (wl) should increase
# the number of fractions should increase (until iwl >= 0)
# and the iwl should increase.
W1 = fpf(4,-4)
W2 = fpf(4,-4)
Wa = W1+W2
wl,iwl,fwl = Wa[:]
assert iwl == -3, '%s+%s==%s' % (W1,W2,afmt)
assert fwl == 7, '%s+%s==%s' % (W1,W2,Wa)
assert wl == 5, '%s+%s==%s' % (W1,W2,Wa)
# The following test case is for fixbv addition. Each
# add operation would assume the worst case, that is,
# each addition would cause an overflow. So that the
# result always adds one more extra integer bit during
# addition.
# However, if several fixbv instances add together, the
# result might be huge and odd if the order of
# addition changes. In this case, fxsum would be
# recommended for optimal results.
W1_iwl = randint(3, 16)
W2_iwl = randint(3, 16)
W3_iwl = randint(3, 8)
W4_iwl = randint(3, 8)
Wa_iwl = max(W1_iwl, W2_iwl) + 1
Wa_iwl = max(Wa_iwl, W3_iwl) + 1
Wa_iwl = max(Wa_iwl, W4_iwl) + 1
W1 = fpf(16, W1_iwl)
W2 = fpf(16, W2_iwl)
W3 = fpf(8, W3_iwl)
W4 = fpf(8, W4_iwl)
Wa = W1+W2+W3+W4
wl, iwl, fwl = Wa[:]
assert iwl == Wa_iwl, '%s+%s+%s+%s==%s' % (W1, W2, W3, W4, Wa)
# cross-over points when negative iwl becomes
# positive and vise-versa
W1 = fpf(8, -3)
W2 = fpf(8, -4)
niwl = -2
for ii in range(6):
W1 = W1 + W2
assert W1._iwl == niwl
niwl += 1
# negative fraction widths
W1 = fpf(4, 8)
assert W1._fwl == -5
# cross-over points when negative fwl becomes
# positive and vise-versa
W1 = fpf(4, 8)
W2 = fpf(10, 8, 1)
nfwl = -5
for ii in range(6):
W1 = W1 * W2
nfwl += 1
assert W1._fwl == nfwl
def test_fpf_sub():
# some basic adds
W1 = fpf(8,4) # W8.4.3
W2 = fpf(8,4) # W8.4.3
Wa = W1-W2
wl,iwl,fwl = Wa[:]
assert iwl == 5, '%s+%s==%s' % (W1,W2,Wa)
assert fwl == 3, '%s+%s==%s' % (W1,W2,Wa)
assert wl == 9, '%s+%s==%s' % (W1,W2,Wa)
W1 = fpf(8,4) # W8.4.3
W2 = fpf(8,3) # W8.3.4
Wa = W1-W2
wl,iwl,fwl = Wa[:]
assert iwl == 5, '%s+%s==%s' % (W1,W2,Wa)
assert fwl == 4, '%s+%s==%s' % (W1,W2,Wa)
assert wl == 10, '%s+%s==%s' % (W1,W2,Wa)
W1 = fpf(16,0) # W16.0.15
W2 = fpf(16,0) # W16.0.15
Wa = W1-W2
wl,iwl,fwl = Wa[:]
assert iwl == 1, '%s+%s==%s' % (W1,W2,Wa)
assert fwl == 15, '%s+%s==%s' % (W1,W2,Wa)
assert wl == 17, '%s+%s==%s' % (W1,W2,Wa)
def test_fpf_mul():
W1 = fpf(8,4) # W8.4.3
W2 = fpf(8,4) # W8.4.3
Wa = W1*W2
wl,iwl,fwl = Wa[:]
assert iwl == 9, '%s+%s==%s' % (W1,W2,Wa)
assert fwl == 6, '%s+%s==%s' % (W1,W2,Wa)
assert wl == 16, '%s+%s==%s' % (W1,W2,Wa)
# @todo: negative iwl and fwl
def test_basic():
# test all exact single bit values for (16,0,15)
for f in range(1,16):
x = fixbv(2**-f)[16,0,15]
y = fixbv(-2**-f)[16,0,15]
assert float(x) == 2**-f, \
"%f != %f, %04x != %04x" % (2.**-f, float(x),
x,
0x8000 >> f)
assert bin(x._val,16) == bin(0x8000 >> f, 16), \
"%s != %s for f == %d" % (bin(x._val, 16),
bin(0x8000 >> f, 16), f)
assert float(y) == -2**-f
assert bin(y._val,16) == bin(-0x8000 >> f, 16), \
"%s" % (bin(y._val, 16))
# Test all exact single bit values for W128.0
for f in range(1,128):
x = fixbv(2**-f, min=-1, max=1, res=2**-127)
y = fixbv(-2**-f, min=-1, max=1, res=2**-127)
assert float(x) == 2**-f
assert bin(x,128) == bin(0x80000000000000000000000000000000 >> f, 128)
assert float(y) == -2**-f
assert bin(y,128) == bin(-0x80000000000000000000000000000000 >> f, 128)
assert x > y
assert y < x
assert min(x,y) == min(y,x) == y
assert max(x,y) == max(y,x) == x
assert x != y
x = fixbv(3.14159)[18,3]
y = fixbv(-1.4142 - 1.161802 - 2.71828)[18,3]
assert x != y
#assert --x == x
assert abs(y) > abs(x)
assert abs(x) < abs(y)
assert x == x and y == y
# Create a (8,3) fixed-point object value == 2.5
x = fixbv(2.5, min=-8, max=8, res=1./32)
assert float(x) == 2.5
assert x._val == 0x50
def test_round_overflow():
# round mode: round
x = fixbv(10.1875, min=-16, max=16, res=2**-5,
round_mode='round', overflow_mode='ring')
y = fixbv(-3.14, min=-16, max=16, res=2**-6,
round_mode='round', overflow_mode='ring')
z = fixbv(-2.125, min=-16, max=16, res=2**-6,
round_mode='round', overflow_mode='ring')
w = fixbv(0, min=-16, max=16, res=2**-2,
round_mode='round', overflow_mode='ring')
w[:] = x
assert float(w) == 10.25
w[:] = y
assert float(w) == -3.25
w[:] = z
assert float(w) == -2
# round mode: nearest
x = fixbv(10.625, min=-16, max=16, res=2**-5,
round_mode='nearest', overflow_mode='ring')
y = fixbv(-3.14, min=-16, max=16, res=2**-6,
round_mode='nearest', overflow_mode='ring')
z = fixbv(-2.125, min=-16, max=16, res=2**-6,
round_mode='nearest', overflow_mode='ring')
w = fixbv(0, min=-16, max=16, res=2**-2,
round_mode='nearest', overflow_mode='ring')
w[:] = x
assert float(w) == 10.75
w[:] = y
assert float(w) == -3.25
w[:] = z
assert float(w) == -2.25
# round mode: floor
x = fixbv(10.625, min=-16, max=16, res=2**-5,
round_mode='floor', overflow_mode='ring')
y = fixbv(-3.14, min=-16, max=16, res=2**-6,
round_mode='floor', overflow_mode='ring')
z = fixbv(-2.125, min=-16, max=16, res=2**-6,
round_mode='floor', overflow_mode='ring')
w = fixbv(0, min=-16, max=16, res=2**-2,
round_mode='floor', overflow_mode='ring')
w[:] = x
assert float(w) == 10.5
w[:] = y
assert float(w) == -3.25
w[:] = z
assert float(w) == -2.25
# overflow mode: ring
x = fixbv(10.1875, min=-16, max=16, res=2**-5,
round_mode='round', overflow_mode='ring')
y = fixbv(-2., min=-16, max=16, res=2**-6,
round_mode='round', overflow_mode='ring')
z = fixbv(-6.125, min=-16, max=16, res=2**-6,
round_mode='round', overflow_mode='ring')
w = fixbv(0, min=-4, max=4, res=2**-8,
round_mode='round', overflow_mode='ring')
w[:] = x
assert float(w) == 2.1875
w[:] = y
assert float(w) == -2.
w[:] = z
assert float(w) == 1.875
# overflow mode: saturate
x = fixbv(10.1875, min=-16, max=16, res=2**-5,
round_mode='round', overflow_mode='saturate')
y = fixbv(-2., min=-16, max=16, res=2**-6,
round_mode='round', overflow_mode='saturate')
z = fixbv(-6.125, min=-16, max=16, res=2**-6,
round_mode='round', overflow_mode='saturate')
w = fixbv(0, min=-4, max=4, res=2**-8,
round_mode='round', overflow_mode='saturate')
w[:] = x
assert float(w) == 4 - 2**-8
w[:] = y
assert float(w) == -2.
w[:] = z
assert float(w) == -4
def m_round_overflow(x, y):
@always_comb
def rtl():
y.next = x
return rtl
def test_module_round_overflow():
x = Signal(fixbv(10.1875, min=-16, max=16, res=2**-5,
round_mode='round', overflow_mode='ring'))
y = Signal(fixbv(0, min=-8, max=8, res=2**-2,
round_mode='round', overflow_mode='ring'))
def _test():
tbdut = m_round_overflow(x, y)
@instance
def tbstim():
print(x, y)
yield delay(10)
print(x, y)
assert float(y) == -5.75
return tbdut, tbstim
Simulation(_test()).run()
def test_math():
x = fixbv(0.5)[16,0]
y = fixbv(0.25)[16,0]
z = fixbv(0)[16,0]
#print(x, y, z)
#w = x + y
#print(w, type(w))
z[:] = x + y
print(z, type(z), x+y)
assert float(z) == 0.75
x = fixbv(3.5, min=-8, max=8, res=2**-5)
y = fixbv(-5.25, min=-8, max=8, res=2**-5)
iW = x._W + y._W
print(iW)
z = fixbv(0)[iW[:]]
z[:] = x + y
assert float(z) == -1.75
z[:] = y - x
assert float(z) == -8.75
z[:] = x - y
assert float(z) == 8.75
x = fixbv(3.141592)[19,4]
y = fixbv(1.618033)[19,4]
print(float(x), int(x), repr(x))
print(float(y), int(y), repr(y))
iW = x._W * y._W
print(iW)
z = fixbv(0)[iW[:]]
wl,iwl,fwl = z._W[:]
print(repr(z), z._max, z._min, z._nrbits, "iwl, fwl", iwl, fwl)
z[:] = x * y
print(repr(x), repr(y))
print(float(z), int(z), repr(z))
assert float(z) > 5.
x = fixbv(3.5, min=-8, max=8, res=2**-5)
z = fixbv(0)[(x*x).format]
print(x, z)
z[:] = x * x
assert float(z) == 12.25
z[:] = x**2
assert float(z) == 12.25
z = fixbv(0)[(x*x*x).format]
z[:] = x * x * x
assert float(z) == 42.875
z[:] = x**3
assert float(z) == 42.875
# Point alignment
x = fixbv(2.25, min=-4, max=4, res=2**-5)
y = fixbv(1.5, min=-2, max=2, res=2**-8)
z = fixbv(0)[12, 4, 7]
z[:] = x + y
assert float(z) == 3.75
z[:] = x - y
assert float(z) == 0.75
z[:] = x * y
assert float(z) == 3.375
x = fixbv(9.5, min=-16, max=16, res=0.25)
y = fixbv(-3.25, min=-4, max=4, res=2**-4)
z = fixbv(0)[11, 6, 4]
z[:] = x + y
assert float(z) == 6.25
z[:] = x - y
assert float(z) == 12.75
z[:] = y - x
assert float(z) == -12.75
z[:] = x * y
assert float(z) == -30.875
def m_add(x, y, z):
@always_comb
def rtl():
z.next = x + y
return rtl
def test_module_add():
x = Signal(fixbv(3.14159, min=-8, max=8, res=1e-5))
y = Signal(fixbv(3.14159, min=-8, max=8, res=1e-5))
z = Signal(fixbv(0, min=-8, max=8, res=1e-5))
def _test():
tbdut = m_add(x, y, z)
@instance
def tbstim():
print(x,y,z)
yield delay(10)
print(x,y,z)
assert float(z) > 6
assert float(z) < 7
err = abs(2*math.pi - float(z))
# @todo: need to quantify what the expected error is
assert err < 1e-4
return tbdut, tbstim
Simulation(_test()).run()
def m_align(x, y, z):
@always_comb
def rtl():
z.next = y - x * y + x
return rtl
def test_module_align():
x = Signal(fixbv(0.125, min=-32, max=32, res=1/2**8))
y = Signal(fixbv(0.25, min=-128, max=128, res=1/2**32))
z = Signal(fixbv(0, min=-16, max=16, res=1/2**16))
def _test():
tbdut = m_align(x, y, z)
@instance
def tbstim():
print(x,y,z)
yield delay(10)
print(x,y,z)
assert float(z) == 0.34375
return tbdut, tbstim
Simulation(_test()).run()
def m_more(w, x, y, z):
@always_comb
def rtl():
if (x + y) > w:
z.next = x + y - w
else:
z.next = (x * w**2) - y**2
return rtl
def test_module_more():
w = Signal(fixbv(0.5, min=-128, max=128, res=1/2**32))
x = Signal(fixbv(0.125, min=-128, max=128, res=1/2**32))
y = Signal(fixbv(0.125, min=-128, max=128, res=1/2**32))
z = Signal(fixbv(0, min=-128, max=128, res=1/2**32))
def _test():
tbdut = m_more(w, x, y, z)
@instance
def tbstim():
print(w,x,y,z)
yield delay(10)
print(w,x,y,z)
assert float(z) == 0.015625
return tbdut, tbstim
Simulation(_test()).run()
def test_equalities():
x = fixbv(3.14159, min=-8, max=8, res=1e-5)
y = fixbv(3.14159, min=-8, max=8, res=1e-5)
z = fixbv(0, min=-8, max=8, res=1e-5)
w = fixbv(0, min=-16, max=16, res=2**-16)
u = fixbv(-2.7183, min=-8, max=8, res=1e-5)
v = fixbv(-2.7183, min=-16, max=16, res=1e-7)
assert x == y
assert x >= y
assert y <= x
assert z < x
assert x > z
assert x != z
assert x > w
assert y >= w
assert z == w
assert z >= w
assert z <= w
assert x > u
assert u <= y
assert w >= v
assert v < x
# with pytest.raises(AssertionError) as excinfo:
# if x == w:
# print("nope, this shouldn't work")
# @todo: now this is an issue, when intbv is in a Signal and
# pass the operators down it will be intbv == Signal
x = Signal(fixbv(3.14159, min=-8, max=8, res=1e-5))
y = Signal(fixbv(3.14159, min=-8, max=8, res=1e-5))
z = Signal(fixbv(0, min=-8, max=8, res=1e-5))
w = Signal(fixbv(0, min=-16, max=16, res=2**-16))
u = Signal(fixbv(-2.7183, min=-8, max=8, res=1e-5))
v = Signal(fixbv(-2.7183, min=-16, max=16, res=1e-7))
# these tests currrently fail, need to usderstand why
assert x == y
assert x >= y
assert y <= x
assert z < x
assert x > z
assert x != z
assert x > w
assert y >= w
assert z == w
assert z >= w
assert z <= w
assert x > u
assert u <= y
assert w >= v
assert v < x
# none of the following should work because 'x' and 'w' are
# different types. They need to be the same widths before
# the comparisons.
#
# I added the comparison for different widths. - qrqiuren
# with pytest.raises(AssertionError) as excinfo:
# if x == w: print("nope, this shoudln't work")
# with pytest.raises(AssertionError) as excinfo:
# if x < w: print("nope, this shoudln't work")
# with pytest.raises(AssertionError) as excinfo:
# if w > x: print("nope, this shoudln't work")
# with pytest.raises(AssertionError) as excinfo:
# if x <= w: print("nope, this shoudln't work")
# with pytest.raises(AssertionError) as excinfo:
# if w >= x: print("nope, this shoudln't work")
# with pytest.raises(AssertionError) as excinfo:
# if x != w: print("nope, this shoudln't work")
def test_int_frac():
x = fixbv(3.75, min=-4, max=4, res=0.125)
print(bin(x._val))
assert x.int() == 3
assert x.frac() == 6
x = fixbv(-4.625, min=-8, max=8, res=0.125)
print(bin(x._val))
assert x.int() == -5
assert x.frac() == 3
if __name__ == '__main__':
#test_fpf_add()
#test_basic()
test_math()
#test_equalities()
test_module_add()
test_module_more()