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:
commit
50ebc9acbb
@ -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):
|
||||
|
@ -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
711
myhdl/_fixbv.py
Normal 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
174
myhdl/_resize.py
Normal 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
|
||||
|
||||
|
||||
|
68
myhdl/test/conversion/toVerilog/test_fixalign.py
Normal file
68
myhdl/test/conversion/toVerilog/test_fixalign.py
Normal 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()
|
64
myhdl/test/conversion/toVerilog/test_fixop.py
Normal file
64
myhdl/test/conversion/toVerilog/test_fixop.py
Normal 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()
|
580
myhdl/test/core/test_fixbv.py
Normal file
580
myhdl/test/core/test_fixbv.py
Normal 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()
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user