/* * Copyright 2011-2019 Xilinx, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __AP_FIXED_BASE_H__ #define __AP_FIXED_BASE_H__ #ifndef __AP_FIXED_H__ #error "Only ap_fixed.h and ap_int.h can be included directly in user code." #endif // for ap_int_base and its reference types. #include #ifndef __SYNTHESIS__ #if _AP_ENABLE_HALF_ == 1 // for half type #include #endif // for std io #include #endif #ifndef __cplusplus #error "C++ is required to include this header file" #else // __cplusplus // for warning on unsupported rounding mode in conversion to float/double. #if !defined(__SYNTHESIS__) && __cplusplus >= 201103L && \ (defined(__gnu_linux__) || defined(_WIN32)) #define AP_FIXED_ENABLE_CPP_FENV 1 #include #endif // ---------------------------------------------------------------------- /* Major TODO long double support: constructor, assign and other operators. binary operators with ap_fixed_base and const char*. return ap_fixed/ap_ufixed when result signedness is known. */ // Helper function in conversion to floating point types. #ifdef __SYNTHESIS__ #define _AP_ctype_op_get_bit(var, index) _AP_ROOT_op_get_bit(var, index) #define _AP_ctype_op_set_bit(var, index, x) _AP_ROOT_op_set_bit(var, index, x) #define _AP_ctype_op_get_range(var, low, high) \ _AP_ROOT_op_get_range(var, low, high) #define _AP_ctype_op_set_range(var, low, high, x) \ _AP_ROOT_op_set_range(var, low, high, x) #else // ifdef __SYNTHESIS__ template inline bool _AP_ctype_op_get_bit(_Tp1& var, const _Tp2& index) { return !!(var & (1ull << (index))); } template inline _Tp1 _AP_ctype_op_set_bit(_Tp1& var, const _Tp2& index, const _Tp3& x) { var |= (((x) ? 1ull : 0ull) << (index)); return var; } template inline _Tp1 _AP_ctype_op_get_range(_Tp1& var, const _Tp2& low, const _Tp3& high) { _Tp1 r = var; ap_ulong mask = -1ll; mask >>= (sizeof(_Tp1) * 8 - ((high) - (low) + 1)); r >>= (low); r &= mask; return r; } template inline _Tp1 _AP_ctype_op_set_range(_Tp1& var, const _Tp2& low, const _Tp3& high, const _Tp4& x) { ap_ulong mask = -1ll; mask >>= (_AP_SIZE_ap_slong - ((high) - (low) + 1)); var &= ~(mask << (low)); var |= ((mask & x) << (low)); return var; } #endif // ifdef __SYNTHESIS__ // trait for letting base class to return derived class. // Notice that derived class template is incomplete, and we cannot use // the member of the derived class. template struct _ap_fixed_factory; template struct _ap_fixed_factory<_AP_W2, _AP_I2, true> { typedef ap_fixed<_AP_W2, _AP_I2> type; }; template struct _ap_fixed_factory<_AP_W2, _AP_I2, false> { typedef ap_ufixed<_AP_W2, _AP_I2> type; }; /// ap_fixed_base: AutoPilot fixed point. /** partial specialization of signed. @tparam _AP_W width. @tparam _AP_I integral part width. @tparam _AP_S signed. @tparam _AP_Q quantization mode. Default is AP_TRN. @tparam _AP_O saturation mode. Default is AP_WRAP. @tparam _AP_N saturation wrap value. Default is 0. */ // default for _AP_Q, _AP_O and _AP_N set in ap_decl.h template struct ap_fixed_base : _AP_ROOT_TYPE<_AP_W, _AP_S> { public: typedef _AP_ROOT_TYPE<_AP_W, _AP_S> Base; static const int width = _AP_W; static const int iwidth = _AP_I; static const ap_q_mode qmode = _AP_Q; static const ap_o_mode omode = _AP_O; /// Return type trait. template struct RType { enum { _AP_F = _AP_W - _AP_I, F2 = _AP_W2 - _AP_I2, mult_w = _AP_W + _AP_W2, mult_i = _AP_I + _AP_I2, mult_s = _AP_S || _AP_S2, plus_w = AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) + 1 + AP_MAX(_AP_F, F2), plus_i = AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) + 1, plus_s = _AP_S || _AP_S2, minus_w = AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) + 1 + AP_MAX(_AP_F, F2), minus_i = AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) + 1, minus_s = true, #ifndef __SC_COMPATIBLE__ div_w = _AP_S2 + _AP_W + AP_MAX(F2, 0), #else div_w = _AP_S2 + _AP_W + AP_MAX(F2, 0) + AP_MAX(_AP_I2, 0), #endif div_i = _AP_S2 + _AP_I + F2, div_s = _AP_S || _AP_S2, logic_w = AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)) + AP_MAX(_AP_F, F2), logic_i = AP_MAX(_AP_I + (_AP_S2 && !_AP_S), _AP_I2 + (_AP_S && !_AP_S2)), logic_s = _AP_S || _AP_S2 }; typedef ap_fixed_base<_AP_W, _AP_I, _AP_S> lhs; typedef ap_fixed_base<_AP_W2, _AP_I2, _AP_S2> rhs; typedef ap_fixed_base mult_base; typedef ap_fixed_base plus_base; typedef ap_fixed_base minus_base; typedef ap_fixed_base logic_base; typedef ap_fixed_base div_base; typedef ap_fixed_base<_AP_W, _AP_I, _AP_S> arg1_base; typedef typename _ap_fixed_factory::type mult; typedef typename _ap_fixed_factory::type plus; typedef typename _ap_fixed_factory::type minus; typedef typename _ap_fixed_factory::type logic; typedef typename _ap_fixed_factory::type div; typedef typename _ap_fixed_factory<_AP_W, _AP_I, _AP_S>::type arg1; }; private: #ifndef __SYNTHESIS__ // This cannot handle hex float format string. void fromString(const std::string& val, unsigned char radix) { _AP_ERROR(!(radix == 2 || radix == 8 || radix == 10 || radix == 16), "ap_fixed_base::fromString(%s, %d)", val.c_str(), radix); Base::V = 0; int startPos = 0; int endPos = val.length(); int decPos = val.find("."); if (decPos == -1) decPos = endPos; // handle sign bool isNegative = false; if (val[0] == '-') { isNegative = true; ++startPos; } else if (val[0] == '+') ++startPos; // If there are no integer bits, e.g.: // .0000XXXX, then keep at least one bit. // If the width is greater than the number of integer bits, e.g.: // XXXX.XXXX, then we keep the integer bits // if the number of integer bits is greater than the width, e.g.: // XXX000 then we keep the integer bits. // Always keep one bit. ap_fixed_base integer_bits = 0; // Figure out if we can shift instead of multiply unsigned shift = (radix == 16 ? 4 : radix == 8 ? 3 : radix == 2 ? 1 : 0); //std::cout << "\n\n" << val << "\n"; //std::cout << startPos << " " << decPos << " " << endPos << "\n"; bool sticky_int = false; // Traverse the integer digits from the MSD, multiplying by radix as we go. for (int i = startPos; i < decPos; i++) { // Get a digit char cdigit = val[i]; if (cdigit == '\0') continue; unsigned digit = ap_private_ops::decode_digit(cdigit, radix); sticky_int |= integer_bits[AP_MAX(_AP_I, 4) + 4 - 1] | integer_bits[AP_MAX(_AP_I, 4) + 4 - 2] | integer_bits[AP_MAX(_AP_I, 4) + 4 - 3] | integer_bits[AP_MAX(_AP_I, 4) + 4 - 4]; // Shift or multiply the value by the radix if (shift) integer_bits <<= shift; else integer_bits *= radix; // Add in the digit we just interpreted integer_bits += digit; //std::cout << "idigit = " << digit << " " << integer_bits.to_string() // << " " << sticky_int << "\n"; } integer_bits[AP_MAX(_AP_I, 4) + 4 - 3] = integer_bits[AP_MAX(_AP_I, 4) + 4 - 3] | sticky_int; ap_fixed_base fractional_bits = 0; bool sticky = false; // Traverse the fractional digits from the LSD, dividing by radix as we go. for (int i = endPos - 1; i >= decPos + 1; i--) { // Get a digit char cdigit = val[i]; if (cdigit == '\0') continue; unsigned digit = ap_private_ops::decode_digit(cdigit, radix); // Add in the digit we just interpreted fractional_bits += digit; sticky |= fractional_bits[0] | fractional_bits[1] | fractional_bits[2] | fractional_bits[3]; // Shift or divide the value by the radix if (shift) fractional_bits >>= shift; else fractional_bits /= radix; //std::cout << "fdigit = " << digit << " " << fractional_bits.to_string() // << " " << sticky << "\n"; } //std::cout << "Int =" << integer_bits.to_string() << " " << // fractional_bits.to_string() << "\n"; fractional_bits[0] = fractional_bits[0] | sticky; if (isNegative) *this = -(integer_bits + fractional_bits); else *this = integer_bits + fractional_bits; //std::cout << "end = " << this->to_string(16) << "\n"; } /// report invalid constrction of ap_fixed_base INLINE void report() { if (!_AP_S && _AP_O == AP_WRAP_SM) { fprintf(stderr, "ap_ufxied<...> cannot support AP_WRAP_SM.\n"); exit(1); } if (_AP_W > MAX_MODE(AP_INT_MAX_W) * 1024) { fprintf(stderr, "[E] ap_%sfixed<%d, ...>: Bitwidth exceeds the " "default max value %d. Please use macro " "AP_INT_MAX_W to set a larger max value.\n", _AP_S ? "" : "u", _AP_W, MAX_MODE(AP_INT_MAX_W) * 1024); exit(1); } } #else INLINE void report() {} #endif // ifdef __SYNTHESIS__ /// @name helper functions. // @{ INLINE void overflow_adjust(bool underflow, bool overflow, bool lD, bool sign) { if (!underflow && !overflow) return; if (_AP_O == AP_WRAP) { if (_AP_N == 0) return; if (_AP_S) { // signed AP_WRAP // n_bits == 1 Base::V = _AP_ROOT_op_set_bit(Base::V, _AP_W - 1, sign); if (_AP_N > 1) { // n_bits > 1 ap_int_base<_AP_W, false> mask(-1); if (sign) mask.V = 0; Base::V = _AP_ROOT_op_set_range(Base::V, _AP_W - _AP_N, _AP_W - 2, mask.V); } } else { // unsigned AP_WRAP ap_int_base<_AP_W, false> mask(-1); Base::V = _AP_ROOT_op_set_range(Base::V, _AP_W - _AP_N, _AP_W - 1, mask.V); } } else if (_AP_O == AP_SAT_ZERO) { Base::V = 0; } else if (_AP_O == AP_WRAP_SM && _AP_S) { bool Ro = _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); if (_AP_N == 0) { if (lD != Ro) { Base::V = ~Base::V; Base::V = _AP_ROOT_op_set_bit(Base::V, _AP_W - 1, lD); } } else { if (_AP_N == 1 && sign != Ro) { Base::V = ~Base::V; } else if (_AP_N > 1) { bool lNo = _AP_ROOT_op_get_bit(Base::V, _AP_W - _AP_N); if (lNo == sign) Base::V = ~Base::V; ap_int_base<_AP_W, false> mask(-1); if (sign) mask.V = 0; Base::V = _AP_ROOT_op_set_range(Base::V, _AP_W - _AP_N, _AP_W - 2, mask.V); } Base::V = _AP_ROOT_op_set_bit(Base::V, _AP_W - 1, sign); } } else { if (_AP_S) { if (overflow) { Base::V = 1; Base::V <<= _AP_W - 1; Base::V = ~Base::V; } else if (underflow) { Base::V = 1; Base::V <<= _AP_W - 1; if (_AP_O == AP_SAT_SYM) Base::V |= 1; } } else { if (overflow) Base::V = ~(ap_int_base<_AP_W, false>(0).V); else if (underflow) Base::V = 0; } } } INLINE bool quantization_adjust(bool qb, bool r, bool s) { bool carry = (bool)_AP_ROOT_op_get_bit(Base::V, _AP_W - 1); if (_AP_Q == AP_TRN) return false; if (_AP_Q == AP_RND_ZERO) qb &= s || r; else if (_AP_Q == AP_RND_MIN_INF) qb &= r; else if (_AP_Q == AP_RND_INF) qb &= !s || r; else if (_AP_Q == AP_RND_CONV) qb &= _AP_ROOT_op_get_bit(Base::V, 0) || r; else if (_AP_Q == AP_TRN_ZERO) qb = s && (qb || r); Base::V += qb; return carry && (!(bool)_AP_ROOT_op_get_bit(Base::V, _AP_W - 1)); } // @} public: /// @name constructors. // @{ /// default ctor. INLINE ap_fixed_base() {} /// copy ctor. template INLINE ap_fixed_base( const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) { operator=(op); report(); } template INLINE ap_fixed_base( const volatile ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) { operator=(op); report(); } template INLINE ap_fixed_base(const ap_int_base<_AP_W2, _AP_S2>& op) { ap_fixed_base<_AP_W2, _AP_W2, _AP_S2> tmp; tmp.V = op.V; operator=(tmp); report(); } template INLINE ap_fixed_base(const volatile ap_int_base<_AP_W2, _AP_S2>& op) { ap_fixed_base<_AP_W2, _AP_W2, _AP_S2> tmp; tmp.V = op.V; operator=(tmp); report(); } #ifndef __SYNTHESIS__ #ifndef NON_C99STRING INLINE ap_fixed_base(const char* s, signed char rd = 0) { unsigned char radix = rd; std::string str = ap_private_ops::parseString(s, radix); // will guess rd, default 10 _AP_ERROR(radix == 0, "ap_fixed_base(const char* \"%s\", %d), str=%s, radix = %d", s, rd, str.c_str(), radix); // TODO remove this check fromString(str, radix); } #else INLINE ap_fixed_base(const char* s, signed char rd = 10) { ap_int_base<_AP_W, _AP_S> t(s, rd); Base::V = t.V; } #endif // ifndef NON_C99STRING #else // ifndef __SYNTHESIS__ // XXX _ssdm_string2bits only takes const string and const radix. // It seems XFORM will do compile time processing of the string. INLINE ap_fixed_base(const char* s) { typeof(Base::V) t; _ssdm_string2bits((void*)(&t), (const char*)(s), 10, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N, _AP_C99); Base::V = t; } INLINE ap_fixed_base(const char* s, signed char rd) { typeof(Base::V) t; _ssdm_string2bits((void*)(&t), (const char*)(s), rd, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N, _AP_C99); Base::V = t; } #endif // ifndef __SYNTHESIS__ else template INLINE ap_fixed_base(const ap_bit_ref<_AP_W2, _AP_S2>& op) { *this = ((bool)op); report(); } template INLINE ap_fixed_base(const ap_range_ref<_AP_W2, _AP_S2>& op) { *this = (ap_int_base<_AP_W2, false>(op)); report(); } template INLINE ap_fixed_base( const ap_concat_ref<_AP_W2, _AP_T2, _AP_W3, _AP_T3>& op) { *this = (ap_int_base<_AP_W2 + _AP_W3, false>(op)); report(); } template INLINE ap_fixed_base( const af_bit_ref<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) { *this = (bool(op)); report(); } template INLINE ap_fixed_base( const af_range_ref<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) { *this = (ap_int_base<_AP_W2, false>(op)); report(); } // ctors from c types. // make a temp ap_fixed_base first, and use ap_fixed_base.operator= #define CTOR_FROM_INT(C_TYPE, _AP_W2, _AP_S2) \ INLINE ap_fixed_base(const C_TYPE x) { \ ap_fixed_base<(_AP_W2), (_AP_W2), (_AP_S2)> tmp; \ tmp.V = x; \ *this = tmp; \ } CTOR_FROM_INT(bool, 1, false) CTOR_FROM_INT(char, 8, CHAR_IS_SIGNED) CTOR_FROM_INT(signed char, 8, true) CTOR_FROM_INT(unsigned char, 8, false) CTOR_FROM_INT(short, _AP_SIZE_short, true) CTOR_FROM_INT(unsigned short, _AP_SIZE_short, false) CTOR_FROM_INT(int, _AP_SIZE_int, true) CTOR_FROM_INT(unsigned int, _AP_SIZE_int, false) CTOR_FROM_INT(long, _AP_SIZE_long, true) CTOR_FROM_INT(unsigned long, _AP_SIZE_long, false) CTOR_FROM_INT(ap_slong, _AP_SIZE_ap_slong, true) CTOR_FROM_INT(ap_ulong, _AP_SIZE_ap_slong, false) #undef CTOR_FROM_INT /* * TODO: *Theere used to be several funtions which were AP_WEAK. *Now they're all INLINE expect ap_fixed_base(double d) *Maybe we can use '#pragma HLS inline' instead of INLINE. */ AP_WEAK ap_fixed_base(double d) { ap_int_base<64, false> ireg; ireg.V = doubleToRawBits(d); bool isneg = _AP_ROOT_op_get_bit(ireg.V, 63); ap_int_base exp; ap_int_base exp_tmp; exp_tmp.V = _AP_ROOT_op_get_range(ireg.V, DOUBLE_MAN, DOUBLE_MAN + DOUBLE_EXP - 1); exp = exp_tmp - DOUBLE_BIAS; ap_int_base man; man.V = _AP_ROOT_op_get_range(ireg.V, 0, DOUBLE_MAN - 1); // do not support NaN _AP_WARNING(exp == APFX_IEEE_DOUBLE_E_MAX + 1 && man.V != 0, "assign NaN to fixed point value"); man.V = _AP_ROOT_op_set_bit(man.V, DOUBLE_MAN, 1); if (isneg) man = -man; if ((ireg.V & 0x7fffffffffffffffLL) == 0) { Base::V = 0; } else { int _AP_W2 = DOUBLE_MAN + 2, _AP_I2 = exp.V + 2, _AP_F = _AP_W - _AP_I, F2 = _AP_W2 - _AP_I2; bool _AP_S2 = true, QUAN_INC = F2 > _AP_F && !(_AP_Q == AP_TRN || (_AP_Q == AP_TRN_ZERO && !_AP_S2)); bool carry = false; // handle quantization unsigned sh_amt = (F2 > _AP_F) ? F2 - _AP_F : _AP_F - F2; if (F2 == _AP_F) Base::V = man.V; else if (F2 > _AP_F) { if (sh_amt < DOUBLE_MAN + 2) Base::V = man.V >> sh_amt; else { Base::V = isneg ? -1 : 0; } if ((_AP_Q != AP_TRN) && !((_AP_Q == AP_TRN_ZERO) && !_AP_S2)) { bool qb = (F2 - _AP_F > _AP_W2) ? isneg : (bool)_AP_ROOT_op_get_bit( man.V, F2 - _AP_F - 1); bool r = (F2 > _AP_F + 1) ? _AP_ROOT_op_get_range(man.V, 0, (F2 - _AP_F - 2 < _AP_W2) ? (F2 - _AP_F - 2) : (_AP_W2 - 1)) != 0 : false; carry = quantization_adjust(qb, r, isneg); } } else { // no quantization Base::V = man.V; if (sh_amt < _AP_W) Base::V = Base::V << sh_amt; else Base::V = 0; } // handle overflow/underflow if ((_AP_O != AP_WRAP || _AP_N != 0) && ((!_AP_S && _AP_S2) || _AP_I - _AP_S < _AP_I2 - _AP_S2 + (QUAN_INC || (_AP_S2 && (_AP_O == AP_SAT_SYM))))) { // saturation bool deleted_zeros = _AP_S2 ? true : !carry, deleted_ones = true; bool neg_src = isneg; bool lD = false; int pos1 = F2 - _AP_F + _AP_W; int pos2 = F2 - _AP_F + _AP_W + 1; bool newsignbit = _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); if (pos1 < _AP_W2 && pos1 >= 0) // lD = _AP_ROOT_op_get_bit(man.V, pos1); lD = (man.V >> pos1) & 1; if (pos1 < _AP_W2) { bool Range1_all_ones = true; bool Range1_all_zeros = true; bool Range2_all_ones = true; ap_int_base Range2; ap_int_base all_ones(-1); if (pos2 >= 0 && pos2 < _AP_W2) { // Range2.V = _AP_ROOT_op_get_range(man.V, // pos2, _AP_W2 - 1); Range2.V = man.V; Range2.V >>= pos2; Range2_all_ones = Range2 == (all_ones >> pos2); } else if (pos2 < 0) Range2_all_ones = false; if (pos1 >= 0 && pos2 < _AP_W2) { Range1_all_ones = Range2_all_ones && lD; Range1_all_zeros = !Range2.V && !lD; } else if (pos2 == _AP_W2) { Range1_all_ones = lD; Range1_all_zeros = !lD; } else if (pos1 < 0) { Range1_all_zeros = !man.V; Range1_all_ones = false; } deleted_zeros = deleted_zeros && (carry ? Range1_all_ones : Range1_all_zeros); deleted_ones = carry ? Range2_all_ones && (pos1 < 0 || !lD) : Range1_all_ones; neg_src = isneg && !(carry && Range1_all_ones); } else neg_src = isneg && newsignbit; bool neg_trg = _AP_S && newsignbit; bool overflow = (neg_trg || !deleted_zeros) && !isneg; bool underflow = (!neg_trg || !deleted_ones) && neg_src; if ((_AP_O == AP_SAT_SYM) && _AP_S2 && _AP_S) underflow |= neg_src && (_AP_W > 1 ? _AP_ROOT_op_get_range(Base::V, 0, _AP_W - 2) == 0 : true); overflow_adjust(underflow, overflow, lD, neg_src); } } report(); } // TODO more optimized implementation. INLINE ap_fixed_base(float d) { *this = ap_fixed_base(double(d)); } #if _AP_ENABLE_HALF_ == 1 // TODO more optimized implementation. INLINE ap_fixed_base(half d) { *this = ap_fixed_base(double(d)); } #endif // @} /// @name assign operator /// assign, using another ap_fixed_base of same template parameters. /* INLINE ap_fixed_base& operator=( const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) { Base::V = op.V; return *this; } */ template INLINE ap_fixed_base& operator=( const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) { const int _AP_F = _AP_W - _AP_I; const int F2 = _AP_W2 - _AP_I2; const int QUAN_INC = F2 > _AP_F && !(_AP_Q == AP_TRN || (_AP_Q == AP_TRN_ZERO && !_AP_S2)); if (!op) Base::V = 0; bool carry = false; bool signbit = _AP_ROOT_op_get_bit(op.V, _AP_W2 - 1); bool isneg = signbit && _AP_S2; if (F2 == _AP_F) Base::V = op.V; else if (F2 > _AP_F) { unsigned int sh_amt = F2 - _AP_F; // moves bits right, handle quantization. if (sh_amt < _AP_W2) { Base::V = op.V >> sh_amt; } else { Base::V = isneg ? -1 : 0; } if (_AP_Q != AP_TRN && !(_AP_Q == AP_TRN_ZERO && !_AP_S2)) { bool qbit = _AP_ROOT_op_get_bit(op.V, F2 - _AP_F - 1); // bit after LSB. bool qb = (F2 - _AP_F > _AP_W2) ? _AP_S2 && signbit : qbit; enum { hi = ((F2 - _AP_F - 2) < _AP_W2) ? (F2 - _AP_F - 2) : (_AP_W2 - 1) }; // bits after qb. bool r = (F2 > _AP_F + 1) ? (_AP_ROOT_op_get_range(op.V, 0, hi) != 0) : false; carry = quantization_adjust(qb, r, isneg); } } else { unsigned sh_amt = _AP_F - F2; // moves bits left, no quantization if (sh_amt < _AP_W) { if (_AP_W > _AP_W2) { // extend and then shift, avoid losing bits. Base::V = op.V; Base::V <<= sh_amt; } else { // shift and truncate. Base::V = op.V << sh_amt; } } else { Base::V = 0; } } // handle overflow/underflow if ((_AP_O != AP_WRAP || _AP_N != 0) && ((!_AP_S && _AP_S2) || _AP_I - _AP_S < _AP_I2 - _AP_S2 + (QUAN_INC || (_AP_S2 && _AP_O == AP_SAT_SYM)))) { // saturation bool deleted_zeros = _AP_S2 ? true : !carry; bool deleted_ones = true; bool neg_src = isneg; bool newsignbit = _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); enum { pos1 = F2 - _AP_F + _AP_W, pos2 = F2 - _AP_F + _AP_W + 1 }; bool lD = (pos1 < _AP_W2 && pos1 >= 0) ? _AP_ROOT_op_get_bit(op.V, pos1) : false; if (pos1 < _AP_W2) { bool Range1_all_ones = true; bool Range1_all_zeros = true; bool Range2_all_ones = true; ap_int_base<_AP_W2, false> all_ones(-1); if (pos2 < _AP_W2 && pos2 >= 0) { ap_int_base<_AP_W2, false> Range2; Range2.V = _AP_ROOT_op_get_range(op.V, pos2, _AP_W2 - 1); Range2_all_ones = Range2 == (all_ones >> pos2); } else if (pos2 < 0) { Range2_all_ones = false; } if (pos1 >= 0 && pos2 < _AP_W2) { ap_int_base<_AP_W2, false> Range1; Range1.V = _AP_ROOT_op_get_range(op.V, pos1, _AP_W2 - 1); Range1_all_ones = Range1 == (all_ones >> pos1); Range1_all_zeros = !Range1.V; } else if (pos2 == _AP_W2) { Range1_all_ones = lD; Range1_all_zeros = !lD; } else if (pos1 < 0) { Range1_all_zeros = !op.V; Range1_all_ones = false; } deleted_zeros = deleted_zeros && (carry ? Range1_all_ones : Range1_all_zeros); deleted_ones = carry ? Range2_all_ones && (pos1 < 0 || !lD) : Range1_all_ones; neg_src = isneg && !(carry && Range1_all_ones); } else neg_src = isneg && newsignbit; bool neg_trg = _AP_S && newsignbit; bool overflow = (neg_trg || !deleted_zeros) && !isneg; bool underflow = (!neg_trg || !deleted_ones) && neg_src; if ((_AP_O == AP_SAT_SYM) && _AP_S2 && _AP_S) underflow |= neg_src && (_AP_W > 1 ? _AP_ROOT_op_get_range(Base::V, 0, _AP_W - 2) == 0 : true); overflow_adjust(underflow, overflow, lD, neg_src); } return *this; } // operator= template INLINE ap_fixed_base& operator=( const volatile ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) { operator=(const_cast&>(op)); return *this; } /// Set this ap_fixed_base with ULL. INLINE ap_fixed_base& setBits(ap_ulong bv) { // TODO when ull is not be long enough... Base::V = bv; return *this; } /// Return a ap_fixed_base object whose this->V is assigned by bv. static INLINE ap_fixed_base bitsToFixed(ap_ulong bv) { // TODO fix when ull is not be long enough... ap_fixed_base t; #ifdef __SYNTHESIS__ t.V = bv; #else t.V.set_bits(bv); #endif return t; } // Explicit conversion functions to ap_int_base. /** Captures all integer bits, in truncate mode. * @param[in] Cnative follow conversion from double to int. */ INLINE ap_int_base to_ap_int_base( bool Cnative = true) const { ap_int_base ret; if (_AP_I == 0) { ret.V = 0; } else if (_AP_I > 0 && _AP_I <= _AP_W) { ret.V = _AP_ROOT_op_get_range(Base::V, _AP_W - _AP_I, _AP_W - 1); } else if (_AP_I > _AP_W) { ret.V = _AP_ROOT_op_get_range(Base::V, 0, _AP_W - 1); ret.V <<= (_AP_I - _AP_W); } /* Consider the following case * float f = -7.5f; * ap_fixed<8,4> t = f; // -8 0 0 0 . 0.5 * int i = t.to_int(); * the result should be -7 instead of -8. * Therefore, after truncation, the value should be increated by 1. * For (-1, 0), carry to MSB will happen, but result 0 is still correct. */ if (Cnative && _AP_I < _AP_W) { // Follow C native data type, conversion from double to int if (_AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1) && (_AP_I < _AP_W) && (_AP_ROOT_op_get_range( Base::V, 0, _AP_I < 0 ? _AP_W - 1 : _AP_W - _AP_I - 1) != 0)) ++ret; } else { // Follow OSCI library, conversion from sc_fixed to sc_int } return ret; }; public: template INLINE operator ap_int_base<_AP_W2, _AP_S2>() const { return ap_int_base<_AP_W2, _AP_S2>(to_ap_int_base()); } // Explicit conversion function to C built-in integral type. INLINE char to_char() const { return to_ap_int_base().to_char(); } INLINE int to_int() const { return to_ap_int_base().to_int(); } INLINE unsigned to_uint() const { return to_ap_int_base().to_uint(); } INLINE ap_slong to_int64() const { return to_ap_int_base().to_int64(); } INLINE ap_ulong to_uint64() const { return to_ap_int_base().to_uint64(); } /// covert function to double. /** only round-half-to-even mode supported, does not obey FE env. */ INLINE double to_double() const { #if defined(AP_FIXED_ENABLE_CPP_FENV) _AP_WARNING(std::fegetround() != FE_TONEAREST, "Only FE_TONEAREST is supported"); #endif enum { BITS = DOUBLE_MAN + DOUBLE_EXP + 1 }; if (!Base::V) return 0.0f; bool s = _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); ///< sign. ap_int_base<_AP_W, false> tmp; if (s) tmp.V = -Base::V; // may truncate one bit extra from neg in sim. else tmp.V = Base::V; int l = tmp.countLeadingZeros(); ///< number of leading zeros. int e = _AP_I - l - 1 + DOUBLE_BIAS; ///< exponent int lsb_index = _AP_W - l - 1 - DOUBLE_MAN; // more than 0.5? bool a = (lsb_index >=2) ? (_AP_ROOT_op_get_range(tmp.V, 0, lsb_index - 2) != 0) : 0; // round to even a |= (lsb_index >=0) ? _AP_ROOT_op_get_bit(tmp.V, lsb_index) : 0; // ull is at least 64-bit ap_ulong m; // may actually left shift, ensure buffer is wide enough. if (_AP_W > BITS) { m = (lsb_index >= 1) ? (ap_ulong)(tmp.V >> (lsb_index - 1)) : (ap_ulong)(tmp.V << (1 - lsb_index)); } else { m = (ap_ulong)tmp.V; m = (lsb_index >= 1) ? (m >> (lsb_index - 1)) : (m << (1 - lsb_index)); } m += a; m >>= 1; //std::cout << '\n' << std::hex << m << '\n'; // TODO delete this // carry to MSB, increase exponent if (_AP_ctype_op_get_bit(m, DOUBLE_MAN + 1)) { e += 1; } // set sign and exponent m = _AP_ctype_op_set_bit(m, BITS - 1, s); //std::cout << m << '\n'; // TODO delete this m = _AP_ctype_op_set_range(m, DOUBLE_MAN, DOUBLE_MAN + DOUBLE_EXP - 1, e); //std::cout << std::hex << m << std::dec << std::endl; // TODO delete this // cast to fp return rawBitsToDouble(m); } /// convert function to float. /** only round-half-to-even mode supported, does not obey FE env. */ INLINE float to_float() const { #if defined(AP_FIXED_ENABLE_CPP_FENV) _AP_WARNING(std::fegetround() != FE_TONEAREST, "Only FE_TONEAREST is supported"); #endif enum { BITS = FLOAT_MAN + FLOAT_EXP + 1 }; if (!Base::V) return 0.0f; bool s = _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); ///< sign. ap_int_base<_AP_W, false> tmp; if (s) tmp.V = -Base::V; // may truncate one bit extra from neg in sim. else tmp.V = Base::V; int l = tmp.countLeadingZeros(); ///< number of leading zeros. int e = _AP_I - l - 1 + FLOAT_BIAS; ///< exponent int lsb_index = _AP_W - l - 1 - FLOAT_MAN; // more than 0.5? bool a = (lsb_index >=2) ? (_AP_ROOT_op_get_range(tmp.V, 0, lsb_index - 2) != 0) : 0; // round to even a |= (lsb_index >=0) ? _AP_ROOT_op_get_bit(tmp.V, lsb_index) : 0; // ul is at least 32-bit unsigned long m; // may actually left shift, ensure buffer is wide enough. if (_AP_W > BITS) { m = (lsb_index >= 1) ? (unsigned long)(tmp.V >> (lsb_index - 1)) : (unsigned long)(tmp.V << (1 - lsb_index)); } else { m = (unsigned long)tmp.V; m = (lsb_index >= 1) ? (m >> (lsb_index - 1)) : (m << (1 - lsb_index)); } m += a; m >>= 1; // carry to MSB, increase exponent if (_AP_ctype_op_get_bit(m, FLOAT_MAN + 1)) { e += 1; } // set sign and exponent m = _AP_ctype_op_set_bit(m, BITS - 1, s); m = _AP_ctype_op_set_range(m, FLOAT_MAN, FLOAT_MAN + FLOAT_EXP - 1, e); // cast to fp return rawBitsToFloat(m); } #if _AP_ENABLE_HALF_ == 1 /// convert function to half. /** only round-half-to-even mode supported, does not obey FE env. */ INLINE half to_half() const { #if defined(AP_FIXED_ENABLE_CPP_FENV) _AP_WARNING(std::fegetround() != FE_TONEAREST, "Only FE_TONEAREST is supported"); #endif enum { BITS = HALF_MAN + HALF_EXP + 1 }; if (!Base::V) return 0.0f; bool s = _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); ///< sign. ap_int_base<_AP_W, false> tmp; if (s) tmp.V = -Base::V; // may truncate one bit extra from neg in sim. else tmp.V = Base::V; int l = tmp.countLeadingZeros(); ///< number of leading zeros. int e = _AP_I - l - 1 + HALF_BIAS; ///< exponent int lsb_index = _AP_W - l - 1 - HALF_MAN; // more than 0.5? bool a = (lsb_index >=2) ? (_AP_ROOT_op_get_range(tmp.V, 0, lsb_index - 2) != 0) : 0; // round to even a |= (lsb_index >=0) ? _AP_ROOT_op_get_bit(tmp.V, lsb_index) : 0; // short is at least 16-bit unsigned short m; // may actually left shift, ensure buffer is wide enough. if (_AP_W > BITS) { m = (lsb_index >= 1) ? (unsigned short)(tmp.V >> (lsb_index - 1)) : (unsigned short)(tmp.V << (1 - lsb_index)); } else { m = (unsigned short)tmp.V; m = (lsb_index >= 1) ? (m >> (lsb_index - 1)) : (m << (1 - lsb_index)); } m += a; m >>= 1; // carry to MSB, increase exponent if (_AP_ctype_op_get_bit(m, HALF_MAN + 1)) { e += 1; } // set sign and exponent m = _AP_ctype_op_set_bit(m, BITS - 1, s); m = _AP_ctype_op_set_range(m, HALF_MAN, HALF_MAN + HALF_EXP - 1, e); // cast to fp return rawBitsToHalf(m); } #endif // FIXME inherited from old code, this may loose precision! INLINE operator long double() const { return (long double)to_double(); } INLINE operator double() const { return to_double(); } INLINE operator float() const { return to_float(); } #if _AP_ENABLE_HALF_ == 1 INLINE operator half() const { return to_half(); } #endif INLINE operator bool() const { return (bool)Base::V != 0; } INLINE operator char() const { return (char)to_int(); } INLINE operator signed char() const { return (signed char)to_int(); } INLINE operator unsigned char() const { return (unsigned char)to_uint(); } INLINE operator short() const { return (short)to_int(); } INLINE operator unsigned short() const { return (unsigned short)to_uint(); } INLINE operator int() const { return to_int(); } INLINE operator unsigned int() const { return to_uint(); } // FIXME don't assume data width... #ifdef __x86_64__ INLINE operator long() const { return (long)to_int64(); } INLINE operator unsigned long() const { return (unsigned long)to_uint64(); } #else INLINE operator long() const { return (long)to_int(); } INLINE operator unsigned long() const { return (unsigned long)to_uint(); } #endif // ifdef __x86_64__ else INLINE operator ap_ulong() const { return to_uint64(); } INLINE operator ap_slong() const { return to_int64(); } INLINE int length() const { return _AP_W; }; // bits_to_int64 deleted. #ifndef __SYNTHESIS__ // Used in autowrap, when _AP_W < 64. INLINE ap_ulong bits_to_uint64() const { return (Base::V).to_uint64(); } #endif // Count the number of zeros from the most significant bit // to the first one bit. Note this is only for ap_fixed_base whose // _AP_W <= 64, otherwise will incur assertion. INLINE int countLeadingZeros() { #ifdef __SYNTHESIS__ // TODO: used llvm.ctlz intrinsic ? if (_AP_W <= 32) { ap_int_base<32, false> t(-1ULL); t.range(_AP_W - 1, 0) = this->range(0, _AP_W - 1); return __builtin_ctz(t.V); } else if (_AP_W <= 64) { ap_int_base<64, false> t(-1ULL); t.range(_AP_W - 1, 0) = this->range(0, _AP_W - 1); return __builtin_ctzll(t.V); } else { enum {__N = (_AP_W + 63) / 64}; int NZeros = 0; int i = 0; bool hitNonZero = false; for (i = 0; i < __N - 1; ++i) { ap_int_base<64, false> t; t.range(0, 63) = this->range(_AP_W - i * 64 - 64, _AP_W - i * 64 - 1); NZeros += hitNonZero ? 0 : __builtin_clzll(t.V); hitNonZero |= (t != 0); } if (!hitNonZero) { ap_int_base<64, false> t(-1ULL); t.range(63 - (_AP_W - 1) % 64, 63) = this->range(0, (_AP_W - 1) % 64); NZeros += __builtin_clzll(t.V); } return NZeros; } #else return Base::V.countLeadingZeros(); #endif } // Arithmetic : Binary // ------------------------------------------------------------------------- template INLINE typename RType<_AP_W2, _AP_I2, _AP_S2>::mult operator*( const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op2) const { typename RType<_AP_W2, _AP_I2, _AP_S2>::mult_base r, t; r.V = Base::V; t.V = op2.V; r.V *= op2.V; return r; } // multiply function deleted. template INLINE typename RType<_AP_W2, _AP_I2, _AP_S2>::div operator/( const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op2) const { typename RType<_AP_W2, _AP_I2, _AP_S2>::div_base r; #ifndef __SYNTHESIS__ enum {F2 = _AP_W2-_AP_I2, _W1=AP_MAX(_AP_W + AP_MAX(F2, 0) + ((_AP_S2 && !_AP_S) ? 1 : 0), _AP_W2 + ((_AP_S && !_AP_S2) ? 1 : 0))}; ap_int_base<_W1,_AP_S||_AP_S2> dividend,divisior; ap_int_base<_W1,_AP_S> tmp1; ap_int_base<_W1,_AP_S2> tmp2; tmp1.V = Base::V; tmp1.V <<= AP_MAX(F2,0); tmp2.V = op2.V; dividend = tmp1; divisior = tmp2; r.V = ((_AP_S||_AP_S2) ? dividend.V.sdiv(divisior.V): dividend.V.udiv(divisior.V)); #else #ifndef __SC_COMPATIBLE__ ap_fixed_base<_AP_W + AP_MAX(_AP_W2 - _AP_I2, 0),_AP_I, _AP_S> t(*this); #else ap_fixed_base<_AP_W + AP_MAX(_AP_W2 - _AP_I2, 0) + AP_MAX(_AP_I2, 0),_AP_I, _AP_S> t(*this); #endif r.V = t.V / op2.V; #endif /* enum { F2 = _AP_W2 - _AP_I2, shl = AP_MAX(F2, 0) + AP_MAX(_AP_I2, 0), #ifndef __SC_COMPATIBLE__ shr = AP_MAX(_AP_I2, 0), #else shr = 0, #endif W3 = _AP_S2 + _AP_W + shl, S3 = _AP_S || _AP_S2, }; ap_int_base dividend, t; dividend.V = Base::V; // multiply both by (1 << F2), and than do integer division. dividend.V <<= (int) shl; #ifdef __SYNTHESIS__ // .V's have right signedness, and will have right extending. t.V = dividend.V / op2.V; #else // XXX op2 may be wider than dividend, and sdiv and udiv takes the same with // as left hand operand, so data might be truncated by mistake if not // handled here. t.V = S3 ? dividend.V.sdiv(op2.V) : dividend.V.udiv(op2.V); #endif r.V = t.V >> (int) shr; */ return r; } #define OP_BIN_AF(Sym, Rty) \ template \ INLINE typename RType<_AP_W2, _AP_I2, _AP_S2>::Rty operator Sym( \ const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& \ op2) const { \ typename RType<_AP_W2, _AP_I2, _AP_S2>::Rty##_base ret, lhs(*this), \ rhs(op2); \ ret.V = lhs.V Sym rhs.V; \ return ret; \ } OP_BIN_AF(+, plus) OP_BIN_AF(-, minus) OP_BIN_AF(&, logic) OP_BIN_AF(|, logic) OP_BIN_AF(^, logic) // Arithmetic : assign // ------------------------------------------------------------------------- #define OP_ASSIGN_AF(Sym) \ template \ INLINE ap_fixed_base& operator Sym##=( \ const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& \ op2) { \ *this = operator Sym(op2); \ return *this; \ } OP_ASSIGN_AF(*) OP_ASSIGN_AF(/) OP_ASSIGN_AF(+) OP_ASSIGN_AF(-) OP_ASSIGN_AF(&) OP_ASSIGN_AF(|) OP_ASSIGN_AF(^) // Prefix and postfix increment and decrement. // ------------------------------------------------------------------------- /// Prefix increment INLINE ap_fixed_base& operator++() { operator+=(ap_fixed_base<_AP_W - _AP_I + 1, 1, false>(1)); return *this; } /// Prefix decrement. INLINE ap_fixed_base& operator--() { operator-=(ap_fixed_base<_AP_W - _AP_I + 1, 1, false>(1)); return *this; } /// Postfix increment INLINE const ap_fixed_base operator++(int) { ap_fixed_base r(*this); operator++(); return r; } /// Postfix decrement INLINE const ap_fixed_base operator--(int) { ap_fixed_base r(*this); operator--(); return r; } // Unary arithmetic. // ------------------------------------------------------------------------- INLINE ap_fixed_base operator+() { return *this; } INLINE ap_fixed_base<_AP_W + 1, _AP_I + 1, true> operator-() const { ap_fixed_base<_AP_W + 1, _AP_I + 1, true> r(*this); r.V = -r.V; return r; } INLINE ap_fixed_base<_AP_W, _AP_I, true, _AP_Q, _AP_O, _AP_N> getNeg() { ap_fixed_base<_AP_W, _AP_I, true, _AP_Q, _AP_O, _AP_N> r(*this); r.V = -r.V; return r; } // Not (!) // ------------------------------------------------------------------------- INLINE bool operator!() const { return Base::V == 0; } // Bitwise complement // ------------------------------------------------------------------------- // XXX different from Mentor's ac_fixed. INLINE ap_fixed_base<_AP_W, _AP_I, _AP_S> operator~() const { ap_fixed_base<_AP_W, _AP_I, _AP_S> r; r.V = ~Base::V; return r; } // Shift // ------------------------------------------------------------------------- // left shift is the same as moving point right, i.e. increate I. template INLINE ap_fixed_base<_AP_W, _AP_I + _AP_SHIFT, _AP_S> lshift() const { ap_fixed_base<_AP_W, _AP_I + _AP_SHIFT, _AP_S> r; r.V = Base::V; return r; } template INLINE ap_fixed_base<_AP_W, _AP_I - _AP_SHIFT, _AP_S> rshift() const { ap_fixed_base<_AP_W, _AP_I - _AP_SHIFT, _AP_S> r; r.V = Base::V; return r; } // Because the return type is the type of the the first operand, shift assign // operators do not carry out any quantization or overflow // While systemc, shift assigns for sc_fixed/sc_ufixed will result in // quantization or overflow (depending on the mode of the first operand) INLINE ap_fixed_base operator<<(unsigned int sh) const { ap_fixed_base r; r.V = Base::V << sh; // TODO check shift overflow? #ifdef __SC_COMPATIBLE__ if (sh == 0) return r; if (_AP_O != AP_WRAP || _AP_N != 0) { bool neg_src = _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1); bool allones, allzeros; ap_int_base<_AP_W, false> ones(-1); if (sh <= _AP_W) { ap_int_base<_AP_W, false> range1; range1.V = _AP_ROOT_op_get_range( const_cast(this)->Base::V, _AP_W - sh, _AP_W - 1); allones = range1 == (ones >> (_AP_W - sh)); allzeros = range1 == 0; } else { allones = false; allzeros = Base::V == 0; } bool overflow = !allzeros && !neg_src; bool underflow = !allones && neg_src; if ((_AP_O == AP_SAT_SYM) && _AP_S) underflow |= neg_src && (_AP_W > 1 ? _AP_ROOT_op_get_range(r.V, 0, _AP_W - 2) == 0 : true); bool lD = false; if (sh < _AP_W) lD = _AP_ROOT_op_get_bit(Base::V, _AP_W - sh - 1); r.overflow_adjust(underflow, overflow, lD, neg_src); } #endif return r; } INLINE ap_fixed_base operator>>(unsigned int sh) const { ap_fixed_base r; r.V = Base::V >> sh; // TODO check shift overflow? #ifdef __SC_COMPATIBLE__ if (sh == 0) return r; if (_AP_Q != AP_TRN) { bool qb = false; if (sh <= _AP_W) qb = _AP_ROOT_op_get_bit(Base::V, sh - 1); bool rb = false; if (sh > 1 && sh <= _AP_W) rb = _AP_ROOT_op_get_range(const_cast(this)->Base::V, 0, sh - 2) != 0; else if (sh > _AP_W) rb = Base::V != 0; r.quantization_adjust(qb, rb, _AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1)); } #endif return r; } // left and right shift for int INLINE ap_fixed_base operator<<(int sh) const { ap_fixed_base r; bool isNeg = sh < 0; unsigned int ush = isNeg ? -sh : sh; if (isNeg) { return operator>>(ush); } else { return operator<<(ush); } } INLINE ap_fixed_base operator>>(int sh) const { bool isNeg = sh < 0; unsigned int ush = isNeg ? -sh : sh; if (isNeg) { return operator<<(ush); } else { return operator>>(ush); } } // left and right shift for ap_int. template INLINE ap_fixed_base operator<<(const ap_int_base<_AP_W2, true>& op2) const { // TODO the code seems not optimal. ap_fixed<8,8> << ap_int<2> needs only a // small mux, but integer need a big one! int sh = op2.to_int(); return operator<<(sh); } template INLINE ap_fixed_base operator>>(const ap_int_base<_AP_W2, true>& op2) const { int sh = op2.to_int(); return operator>>(sh); } // left and right shift for ap_uint. template INLINE ap_fixed_base operator<<(const ap_int_base<_AP_W2, false>& op2) const { unsigned int sh = op2.to_uint(); return operator<<(sh); } template INLINE ap_fixed_base operator>>(const ap_int_base<_AP_W2, false>& op2) const { unsigned int sh = op2.to_uint(); return operator>>(sh); } // left and right shift for ap_fixed template INLINE ap_fixed_base operator<<( const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op2) { return operator<<(op2.to_ap_int_base()); } template INLINE ap_fixed_base operator>>( const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op2) { return operator>>(op2.to_ap_int_base()); } // Shift assign. // ------------------------------------------------------------------------- // left shift assign. INLINE ap_fixed_base& operator<<=(const int sh) { *this = operator<<(sh); return *this; } INLINE ap_fixed_base& operator<<=(const unsigned int sh) { *this = operator<<(sh); return *this; } template INLINE ap_fixed_base& operator<<=(const ap_int_base<_AP_W2, _AP_S2>& sh) { *this = operator<<(sh.to_int()); return *this; } template INLINE ap_fixed_base& operator<<=( const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& sh) { *this = operator<<(sh.to_int()); return *this; } // right shift assign. INLINE ap_fixed_base& operator>>=(const int sh) { *this = operator>>(sh); return *this; } INLINE ap_fixed_base& operator>>=(const unsigned int sh) { *this = operator>>(sh); return *this; } template INLINE ap_fixed_base& operator>>=(const ap_int_base<_AP_W2, _AP_S2>& sh) { *this = operator>>(sh.to_int()); return *this; } template INLINE ap_fixed_base& operator>>=( const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& sh) { *this = operator>>(sh.to_int()); return *this; } // Comparisons. // ------------------------------------------------------------------------- #define OP_CMP_AF(Sym) \ template \ INLINE bool operator Sym(const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, \ _AP_O2, _AP_N2>& op2) const { \ enum { _AP_F = _AP_W - _AP_I, F2 = _AP_W2 - _AP_I2 }; \ if (_AP_F == F2) \ return Base::V Sym op2.V; \ else if (_AP_F > F2) \ return Base::V Sym ap_fixed_base(op2).V; \ else \ return ap_fixed_base(*this).V Sym op2.V; \ return false; \ } OP_CMP_AF(>) OP_CMP_AF(<) OP_CMP_AF(>=) OP_CMP_AF(<=) OP_CMP_AF(==) OP_CMP_AF(!=) // FIXME: Move compare with double out of struct ap_fixed_base defination // and combine it with compare operator(double, ap_fixed_base) #define DOUBLE_CMP_AF(Sym) \ INLINE bool operator Sym(double d) const { return to_double() Sym d; } DOUBLE_CMP_AF(>) DOUBLE_CMP_AF(<) DOUBLE_CMP_AF(>=) DOUBLE_CMP_AF(<=) DOUBLE_CMP_AF(==) DOUBLE_CMP_AF(!=) // Bit and Slice Select INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator[]( unsigned index) { _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB"); return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this, index); } template INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator[]( const ap_int_base<_AP_W2, _AP_S2>& index) { _AP_WARNING(index < 0, "Attempting to read bit with negative index"); _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB"); return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this, index.to_int()); } INLINE bool operator[](unsigned index) const { _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB"); return _AP_ROOT_op_get_bit(const_cast(this)->V, index); } INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> bit( unsigned index) { _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB"); return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this, index); } template INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> bit( const ap_int_base<_AP_W2, _AP_S2>& index) { _AP_WARNING(index < 0, "Attempting to read bit with negative index"); _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB"); return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this, index.to_int()); } INLINE bool bit(unsigned index) const { _AP_WARNING(index >= _AP_W, "Attempting to read bit beyond MSB"); return _AP_ROOT_op_get_bit(const_cast(this)->V, index); } template INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> get_bit( const ap_int_base<_AP_W2, true>& index) { _AP_WARNING(index < _AP_I - _AP_W, "Attempting to read bit with negative index"); _AP_WARNING(index >= _AP_I, "Attempting to read bit beyond MSB"); return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>( this, index.to_int() + _AP_W - _AP_I); } INLINE bool get_bit(int index) const { _AP_WARNING(index >= _AP_I, "Attempting to read bit beyond MSB"); _AP_WARNING(index < _AP_I - _AP_W, "Attempting to read bit beyond MSB"); return _AP_ROOT_op_get_bit(const_cast(this)->V, index + _AP_W - _AP_I); } #if 0 INLINE af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> get_bit( int index) { _AP_WARNING(index < _AP_I - _AP_W, "Attempting to read bit with negative index"); _AP_WARNING(index >= _AP_I, "Attempting to read bit beyond MSB"); return af_bit_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>( this, index + _AP_W - _AP_I); } #endif template INLINE bool get_bit(const ap_int_base<_AP_W2, true>& index) const { _AP_WARNING(index >= _AP_I, "Attempting to read bit beyond MSB"); _AP_WARNING(index < _AP_I - _AP_W, "Attempting to read bit beyond MSB"); return _AP_ROOT_op_get_bit(const_cast(this)->V, index.to_int() + _AP_W - _AP_I); } INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range(int Hi, int Lo) { _AP_WARNING((Hi >= _AP_W) || (Lo >= _AP_W), "Out of bounds in range()"); return af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(this, Hi, Lo); } // This is a must to strip constness to produce reference type. INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range( int Hi, int Lo) const { _AP_WARNING((Hi >= _AP_W) || (Lo >= _AP_W), "Out of bounds in range()"); return af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>( const_cast(this), Hi, Lo); } template INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range( const ap_int_base<_AP_W2, _AP_S2>& HiIdx, const ap_int_base<_AP_W3, _AP_S3>& LoIdx) { int Hi = HiIdx.to_int(); int Lo = LoIdx.to_int(); return this->range(Hi, Lo); } template INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range( const ap_int_base<_AP_W2, _AP_S2>& HiIdx, const ap_int_base<_AP_W3, _AP_S3>& LoIdx) const { int Hi = HiIdx.to_int(); int Lo = LoIdx.to_int(); return this->range(Hi, Lo); } INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range() { return this->range(_AP_W - 1, 0); } INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> range() const { return this->range(_AP_W - 1, 0); } INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator()( int Hi, int Lo) { return this->range(Hi, Lo); } INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator()( int Hi, int Lo) const { return this->range(Hi, Lo); } template INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator()( const ap_int_base<_AP_W2, _AP_S2>& HiIdx, const ap_int_base<_AP_W3, _AP_S3>& LoIdx) { int Hi = HiIdx.to_int(); int Lo = LoIdx.to_int(); return this->range(Hi, Lo); } template INLINE af_range_ref<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N> operator()( const ap_int_base<_AP_W2, _AP_S2>& HiIdx, const ap_int_base<_AP_W3, _AP_S3>& LoIdx) const { int Hi = HiIdx.to_int(); int Lo = LoIdx.to_int(); return this->range(Hi, Lo); } INLINE bool is_zero() const { return Base::V == 0; } INLINE bool is_neg() const { if (_AP_S && _AP_ROOT_op_get_bit(Base::V, _AP_W - 1)) return true; return false; } INLINE int wl() const { return _AP_W; } INLINE int iwl() const { return _AP_I; } INLINE ap_q_mode q_mode() const { return _AP_Q; } INLINE ap_o_mode o_mode() const { return _AP_O; } INLINE int n_bits() const { return _AP_N; } // print a string representation of this number in the given radix. // Radix support is 2, 8, 10, or 16. // The result will include a prefix indicating the radix, except for decimal, // where no prefix is needed. The default is to output a signed representation // of signed numbers, or an unsigned representation of unsigned numbers. For // non-decimal formats, this can be changed by the 'sign' argument. #ifndef __SYNTHESIS__ std::string to_string(unsigned char radix = 2, bool sign = _AP_S) const { // XXX in autosim/autowrap.tcl "(${name}).to_string(2).c_str()" is used to // initialize sc_lv, which seems incapable of handling format "-0b". if (radix == 2) sign = false; std::string str; str.clear(); char step = 0; bool isNeg = sign && (Base::V < 0); // Extend to take care of the -MAX case. ap_fixed_base<_AP_W + 1, _AP_I + 1> tmp(*this); if (isNeg) { tmp = -tmp; str += '-'; } std::string prefix; switch (radix) { case 2: prefix = "0b"; step = 1; break; case 8: prefix = "0o"; step = 3; break; case 16: prefix = "0x"; step = 4; break; default: break; } if (_AP_I > 0) { // Note we drop the quantization and rounding flags here. The // integer part is always in range, and the fractional part we // want to drop. Also, the number is always positive, because // of the absolute value above. ap_int_base int_part; // [1] [ I ] d [ W - I ] // | | | // | W-I 0 // W int_part.V = _AP_ROOT_op_get_range( tmp.V, _AP_W - _AP_I, _AP_W); str += int_part.to_string(radix, false); } else { str += prefix; str += '0'; } ap_fixed_base frac_part = tmp; if (radix == 10) { if (frac_part != 0) { str += "."; while (frac_part != 0) { char digit = (frac_part * radix).to_char(); str += static_cast(digit + '0'); frac_part *= radix; } } } else { if (frac_part != 0) { str += "."; for (signed i = _AP_W - _AP_I - 1; i >= 0; i -= step) { char digit = frac_part.range(i, AP_MAX(0, i - step + 1)).to_char(); // If we have a partial bit pattern at the end, then we need // to put it in the high-order bits of 'digit'. int offset = AP_MIN(0, i - step + 1); digit <<= -offset; str += digit < 10 ? static_cast(digit + '0') : static_cast(digit - 10 + 'a'); } if (radix == 16) str += "p0"; // C99 Hex constants are required to have an exponent. } } return str; } #else // XXX HLS will delete this in synthesis INLINE char* to_string(unsigned char radix = 2, bool sign = _AP_S) const { return 0; } #endif }; // struct ap_fixed_base. template INLINE void b_not( ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) { ret.V = ~op.V; } template INLINE void b_and( ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op1, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) { ret.V = op1.V & op2.V; } template INLINE void b_or( ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op1, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) { ret.V = op1.V | op2.V; } template INLINE void b_xor( ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op1, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) { ret.V = op1.V ^ op2.V; } template INLINE void neg( ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret, const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op) { ap_fixed_base<_AP_W2 + !_AP_S2, _AP_I2 + !_AP_S2, true, _AP_Q2, _AP_O2, _AP_N2> t; t.V = -op.V; ret = t; } template INLINE void lshift( ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret, const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op, int i) { enum { F2 = _AP_W2 - _AP_I2, _AP_I3 = AP_MAX(_AP_I, _AP_I2), _AP_W3 = _AP_I3 + F2, }; // wide buffer ap_fixed_base<_AP_W3, _AP_I3, _AP_S2, _AP_Q2, _AP_O2, _AP_N2> t; t.V = op.V; t.V <<= i; // FIXME overflow? // handle quantization and overflow ret = t; } template INLINE void rshift( ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& ret, const ap_fixed_base<_AP_W2, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2>& op, int i) { enum { F = _AP_W - _AP_I, F2 = _AP_W2 - _AP_I2, F3 = AP_MAX(F, F2), _AP_W3 = _AP_I2 + F3, sh = F - F2, }; // wide buffer ap_fixed_base<_AP_W3, _AP_I2, _AP_S2, _AP_Q2, _AP_O2, _AP_N2> t; t.V = op.V; if (sh >= 0) t.V <<= (int) sh; t.V >>= i; // handle quantization and overflow ret = t; } //// FIXME //// These partial specialization ctors allow code like //// char c = 'a'; //// ap_fixed_base<8, 8, true> x(c); //// but what bout ap_fixed_base<9, 9, true> y(c) ? // #ifndef __SYNTHESIS__ INLINE std::string scientificFormat(std::string& input) { if (input.length() == 0) return input; size_t decPosition = input.find('.'); if (decPosition == std::string::npos) decPosition = input.length(); size_t firstNonZeroPos = 0; for (; input[firstNonZeroPos] > '9' || input[firstNonZeroPos] < '1'; firstNonZeroPos++) ; int exp; if (firstNonZeroPos > decPosition) exp = decPosition - firstNonZeroPos; else exp = decPosition - firstNonZeroPos - 1; std::string expString = ""; if (exp == 0) ; else if (exp < 0) { expString += "e-"; exp = -exp; } else expString += "e+"; if (exp < 10 && exp > 0) { expString += '0'; expString += (char)('0' + exp); } else if (exp != 0) { std::string tmp; std::ostringstream oss; oss << exp; tmp = oss.str(); expString += tmp; } int lastNonZeroPos = (int)(input.length() - 1); for (; lastNonZeroPos >= 0; --lastNonZeroPos) if (input[lastNonZeroPos] <= '9' && input[lastNonZeroPos] > '0') break; std::string ans = ""; ans += input[firstNonZeroPos]; if (firstNonZeroPos != (size_t)lastNonZeroPos) { ans += '.'; for (int i = firstNonZeroPos + 1; i <= lastNonZeroPos; i++) if (input[i] != '.') ans += input[i]; } ans += expString; return ans; } INLINE std::string reduceToPrecision(std::string& input, int precision) { bool isZero = true; size_t inputLen = input.length(); for (size_t i = 0; i < inputLen && isZero; i++) if (input[i] != '.' && input[i] != '0') isZero = false; if (isZero) return "0"; // Find the first valid number, skip '-' int FirstNonZeroPos = 0; int LastNonZeroPos = (int)inputLen - 1; int truncBitPosition = 0; size_t decPosition = input.find('.'); for (; input[FirstNonZeroPos] < '1' || input[FirstNonZeroPos] > '9'; FirstNonZeroPos++) ; for (; input[LastNonZeroPos] < '1' || input[LastNonZeroPos] > '9'; LastNonZeroPos--) ; if (decPosition == std::string::npos) decPosition = inputLen; // Count the valid number, to decide whether we need to truncate if ((int)decPosition > LastNonZeroPos) { if (LastNonZeroPos - FirstNonZeroPos + 1 <= precision) return input; truncBitPosition = FirstNonZeroPos + precision; } else if ((int)decPosition < FirstNonZeroPos) { // This is pure decimal if (LastNonZeroPos - FirstNonZeroPos + 1 <= precision) { if (FirstNonZeroPos - decPosition - 1 < 4) { return input; } else { if (input[0] == '-') { std::string tmp = input.substr(1, inputLen - 1); return std::string("-") + scientificFormat(tmp); } else return scientificFormat(input); } } truncBitPosition = FirstNonZeroPos + precision; } else { if (LastNonZeroPos - FirstNonZeroPos <= precision) return input; truncBitPosition = FirstNonZeroPos + precision + 1; } // duplicate the input string, we want to add "0" before the valid numbers // This is easy for quantization, since we may change 9999 to 10000 std::string ans = ""; std::string dupInput = "0"; if (input[0] == '-') { ans += '-'; dupInput += input.substr(1, inputLen - 1); } else { dupInput += input.substr(0, inputLen); ++truncBitPosition; } // Add 'carry' after truncation, if necessary bool carry = dupInput[truncBitPosition] > '4'; for (int i = truncBitPosition - 1; i >= 0 && carry; i--) { if (dupInput[i] == '.') continue; if (dupInput[i] == '9') dupInput[i] = '0'; else { ++dupInput[i]; carry = false; } } // bits outside precision range should be set to 0 if (dupInput[0] == '1') FirstNonZeroPos = 0; else { FirstNonZeroPos = 0; while (dupInput[FirstNonZeroPos] < '1' || dupInput[FirstNonZeroPos] > '9') ++FirstNonZeroPos; } unsigned it = FirstNonZeroPos; int NValidNumber = 0; while (it < dupInput.length()) { if (dupInput[it] == '.') { ++it; continue; } ++NValidNumber; if (NValidNumber > precision) dupInput[it] = '0'; ++it; } // Here we wanted to adjust the truncate position and the value decPosition = dupInput.find('.'); if (decPosition == std::string::npos) // When this is integer truncBitPosition = (int)dupInput.length(); else for (truncBitPosition = (int)(dupInput.length() - 1); truncBitPosition >= 0; --truncBitPosition) { if (dupInput[truncBitPosition] == '.') break; if (dupInput[truncBitPosition] != '0') { truncBitPosition++; break; } } if (dupInput[0] == '1') dupInput = dupInput.substr(0, truncBitPosition); else dupInput = dupInput.substr(1, truncBitPosition - 1); decPosition = dupInput.find('.'); if (decPosition != std::string::npos) { size_t it = 0; for (it = decPosition + 1; dupInput[it] == '0'; it++) ; if (it - decPosition - 1 < 4) { ans += dupInput; return ans; } else { ans += scientificFormat(dupInput); return ans; } } else if ((int)(dupInput.length()) <= precision) { ans += dupInput; return ans; } ans += scientificFormat(dupInput); return ans; } template INLINE void print( const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& x) { if (_AP_I > 0) { ap_int_base<_AP_I, _AP_S> p1; p1.V = x.V >> (_AP_W - _AP_I); print(p1.V); // print overlaod for .V should exit } else { printf("0"); } printf("."); if (_AP_I < _AP_W) { ap_int_base<_AP_W - _AP_I, false> p2; p2.V = _AP_ROOT_op_get_range(x.V, 0, _AP_W - _AP_I); print(p2.V, false); // print overlaod for .V should exit } } #endif // ifndef __SYNTHESIS__ // XXX the following two functions have to exist in synthesis, // as some old HLS Video Library code uses the ostream overload, // although HLS will later delete I/O function call. /// Output streaming //----------------------------------------------------------------------------- // XXX apcc cannot handle global std::ios_base::Init() brought in by #ifndef AP_AUTOCC #ifndef __SYNTHESIS__ template INLINE std::ostream& operator<<( std::ostream& out, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& x) { // TODO support std::ios_base::fmtflags unsigned width = out.width(); unsigned precision = out.precision(); char fill = out.fill(); std::string str = x.to_string(10, _AP_S); str = reduceToPrecision(str, precision); if (width > str.length()) { for (unsigned i = 0; i < width - str.length(); ++i) out << fill; } out << str; return out; } #endif // ifndef __SYNTHESIS__ /// Input streaming // ----------------------------------------------------------------------------- #ifndef __SYNTHESIS__ template INLINE std::istream& operator>>( std::istream& in, ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& x) { double d; in >> d; x = ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>(d); return in; } #endif #endif // ifndef AP_AUTOCC /// Operators mixing Integers with ap_fixed_base // ----------------------------------------------------------------------------- #define AF_BIN_OP_WITH_INT_SF(BIN_OP, C_TYPE, _AP_W2, _AP_S2, RTYPE) \ template \ INLINE typename ap_fixed_base<_AP_W, _AP_I, _AP_S>::template RType< \ _AP_W2, _AP_W2, _AP_S2>::RTYPE \ operator BIN_OP( \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op, \ C_TYPE i_op) { \ return op.operator BIN_OP(ap_int_base<_AP_W2, _AP_S2>(i_op)); \ } #define AF_BIN_OP_WITH_INT(BIN_OP, C_TYPE, _AP_W2, _AP_S2, RTYPE) \ template \ INLINE typename ap_fixed_base<_AP_W, _AP_I, _AP_S>::template RType< \ _AP_W2, _AP_W2, _AP_S2>::RTYPE \ operator BIN_OP( \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op, \ C_TYPE i_op) { \ return op.operator BIN_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \ } \ template \ INLINE typename ap_fixed_base<_AP_W, _AP_I, _AP_S>::template RType< \ _AP_W2, _AP_W2, _AP_S2>::RTYPE \ operator BIN_OP( \ C_TYPE i_op, \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) { \ return ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op).operator BIN_OP(op); \ } #define AF_REL_OP_WITH_INT(REL_OP, C_TYPE, _AP_W2, _AP_S2) \ template \ INLINE bool operator REL_OP( \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op, \ C_TYPE i_op) { \ return op.operator REL_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \ } \ template \ INLINE bool operator REL_OP( \ C_TYPE i_op, \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) { \ return ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op).operator REL_OP(op); \ } #define AF_ASSIGN_OP_WITH_INT(ASSIGN_OP, C_TYPE, _AP_W2, _AP_S2) \ template \ INLINE ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& \ operator ASSIGN_OP( \ ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op, \ C_TYPE i_op) { \ return op.operator ASSIGN_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \ } #define AF_ASSIGN_OP_WITH_INT_SF(ASSIGN_OP, C_TYPE, _AP_W2, _AP_S2) \ template \ INLINE ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& \ operator ASSIGN_OP( \ ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op, \ C_TYPE i_op) { \ return op.operator ASSIGN_OP(ap_int_base<_AP_W2, _AP_S2>(i_op)); \ } #define ALL_AF_OP_WITH_INT(C_TYPE, BITS, SIGN) \ AF_BIN_OP_WITH_INT(+, C_TYPE, (BITS), (SIGN), plus) \ AF_BIN_OP_WITH_INT(-, C_TYPE, (BITS), (SIGN), minus) \ AF_BIN_OP_WITH_INT(*, C_TYPE, (BITS), (SIGN), mult) \ AF_BIN_OP_WITH_INT(/, C_TYPE, (BITS), (SIGN), div) \ AF_BIN_OP_WITH_INT(&, C_TYPE, (BITS), (SIGN), logic) \ AF_BIN_OP_WITH_INT(|, C_TYPE, (BITS), (SIGN), logic) \ AF_BIN_OP_WITH_INT(^, C_TYPE, (BITS), (SIGN), logic) \ AF_BIN_OP_WITH_INT_SF(>>, C_TYPE, (BITS), (SIGN), lhs) \ AF_BIN_OP_WITH_INT_SF(<<, C_TYPE, (BITS), (SIGN), lhs) \ \ AF_ASSIGN_OP_WITH_INT(+=, C_TYPE, (BITS), (SIGN)) \ AF_ASSIGN_OP_WITH_INT(-=, C_TYPE, (BITS), (SIGN)) \ AF_ASSIGN_OP_WITH_INT(*=, C_TYPE, (BITS), (SIGN)) \ AF_ASSIGN_OP_WITH_INT(/=, C_TYPE, (BITS), (SIGN)) \ AF_ASSIGN_OP_WITH_INT(&=, C_TYPE, (BITS), (SIGN)) \ AF_ASSIGN_OP_WITH_INT(|=, C_TYPE, (BITS), (SIGN)) \ AF_ASSIGN_OP_WITH_INT(^=, C_TYPE, (BITS), (SIGN)) \ AF_ASSIGN_OP_WITH_INT_SF(>>=, C_TYPE, (BITS), (SIGN)) \ AF_ASSIGN_OP_WITH_INT_SF(<<=, C_TYPE, (BITS), (SIGN)) \ \ AF_REL_OP_WITH_INT(>, C_TYPE, (BITS), (SIGN)) \ AF_REL_OP_WITH_INT(<, C_TYPE, (BITS), (SIGN)) \ AF_REL_OP_WITH_INT(>=, C_TYPE, (BITS), (SIGN)) \ AF_REL_OP_WITH_INT(<=, C_TYPE, (BITS), (SIGN)) \ AF_REL_OP_WITH_INT(==, C_TYPE, (BITS), (SIGN)) \ AF_REL_OP_WITH_INT(!=, C_TYPE, (BITS), (SIGN)) ALL_AF_OP_WITH_INT(bool, 1, false) ALL_AF_OP_WITH_INT(char, 8, CHAR_IS_SIGNED) ALL_AF_OP_WITH_INT(signed char, 8, true) ALL_AF_OP_WITH_INT(unsigned char, 8, false) ALL_AF_OP_WITH_INT(short, _AP_SIZE_short, true) ALL_AF_OP_WITH_INT(unsigned short, _AP_SIZE_short, false) ALL_AF_OP_WITH_INT(int, _AP_SIZE_int, true) ALL_AF_OP_WITH_INT(unsigned int, _AP_SIZE_int, false) ALL_AF_OP_WITH_INT(long, _AP_SIZE_long, true) ALL_AF_OP_WITH_INT(unsigned long, _AP_SIZE_long, false) ALL_AF_OP_WITH_INT(ap_slong, _AP_SIZE_ap_slong, true) ALL_AF_OP_WITH_INT(ap_ulong, _AP_SIZE_ap_slong, false) #undef ALL_AF_OP_WITH_INT #undef AF_BIN_OP_WITH_INT #undef AF_BIN_OP_WITH_INT_SF #undef AF_ASSIGN_OP_WITH_INT #undef AF_ASSIGN_OP_WITH_INT_SF #undef AF_REL_OP_WITH_INT /* * ********************************************************************** * TODO * There is no operator defined with float/double/long double, so that * code like * ap_fixed<8,4> a = 1.5f; * a += 0.5f; * will fail in compilation. * Operator with warning about conversion might be wanted. * ********************************************************************** */ #define AF_BIN_OP_WITH_AP_INT(BIN_OP, RTYPE) \ template \ INLINE typename ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>::template RType< \ _AP_W, _AP_I, _AP_S>::RTYPE \ operator BIN_OP( \ const ap_int_base<_AP_W2, _AP_S2>& i_op, \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) { \ return ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op).operator BIN_OP(op); \ } \ \ template \ INLINE typename ap_fixed_base<_AP_W, _AP_I, _AP_S>::template RType< \ _AP_W2, _AP_W2, _AP_S2>::RTYPE \ operator BIN_OP( \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op, \ const ap_int_base<_AP_W2, _AP_S2>& i_op) { \ return op.operator BIN_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \ } #define AF_REL_OP_WITH_AP_INT(REL_OP) \ template \ INLINE bool operator REL_OP( \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op, \ const ap_int_base<_AP_W2, _AP_S2>& i_op) { \ return op.operator REL_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \ } \ \ template \ INLINE bool operator REL_OP( \ const ap_int_base<_AP_W2, _AP_S2>& i_op, \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) { \ return ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op).operator REL_OP(op); \ } #define AF_ASSIGN_OP_WITH_AP_INT(ASSIGN_OP) \ template \ INLINE ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& \ operator ASSIGN_OP( \ ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op, \ const ap_int_base<_AP_W2, _AP_S2>& i_op) { \ return op.operator ASSIGN_OP(ap_fixed_base<_AP_W2, _AP_W2, _AP_S2>(i_op)); \ } \ \ template \ INLINE ap_int_base<_AP_W2, _AP_S2>& operator ASSIGN_OP( \ ap_int_base<_AP_W2, _AP_S2>& i_op, \ const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op) { \ return i_op.operator ASSIGN_OP(op.to_ap_int_base()); \ } AF_BIN_OP_WITH_AP_INT(+, plus) AF_BIN_OP_WITH_AP_INT(-, minus) AF_BIN_OP_WITH_AP_INT(*, mult) AF_BIN_OP_WITH_AP_INT(/, div) AF_BIN_OP_WITH_AP_INT(&, logic) AF_BIN_OP_WITH_AP_INT(|, logic) AF_BIN_OP_WITH_AP_INT(^, logic) #undef AF_BIN_OP_WITH_AP_INT AF_ASSIGN_OP_WITH_AP_INT(+=) AF_ASSIGN_OP_WITH_AP_INT(-=) AF_ASSIGN_OP_WITH_AP_INT(*=) AF_ASSIGN_OP_WITH_AP_INT(/=) AF_ASSIGN_OP_WITH_AP_INT(&=) AF_ASSIGN_OP_WITH_AP_INT(|=) AF_ASSIGN_OP_WITH_AP_INT(^=) #undef AF_ASSIGN_OP_WITH_AP_INT AF_REL_OP_WITH_AP_INT(==) AF_REL_OP_WITH_AP_INT(!=) AF_REL_OP_WITH_AP_INT(>) AF_REL_OP_WITH_AP_INT(>=) AF_REL_OP_WITH_AP_INT(<) AF_REL_OP_WITH_AP_INT(<=) #undef AF_REL_OP_WITH_AP_INT // Relational Operators with double template INLINE bool operator==( double op1, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) { return op2.operator==(op1); } template INLINE bool operator!=( double op1, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) { return op2.operator!=(op1); } template INLINE bool operator>( double op1, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) { return op2.operator<(op1); } template INLINE bool operator>=( double op1, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) { return op2.operator<=(op1); } template INLINE bool operator<( double op1, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) { return op2.operator>(op1); } template INLINE bool operator<=( double op1, const ap_fixed_base<_AP_W, _AP_I, _AP_S, _AP_Q, _AP_O, _AP_N>& op2) { return op2.operator>=(op1); } #endif // ifndef __cplusplus else #endif // ifndef __AP_FIXED_BASE_H__ else // -*- cpp -*-