OpenFPGA/libs/librtlnumber/src/rtl_int.cpp

831 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Authors: Aaron Graham (aaron.graham@unb.ca, aarongraham9@gmail.com),
* Jean-Philippe Legault (jlegault@unb.ca, jeanphilippe.legault@gmail.com),
* Alexandrea Demmings (alexandrea.demmings@unb.ca, lxdemmings@gmail.com) and
* Dr. Kenneth B. Kent (ken@unb.ca)
* for the Reconfigurable Computing Research Lab at the
* Univerity of New Brunswick in Fredericton, New Brunswick, Canada
*/
#include <string>
#include "internal_bits.hpp"
#include "rtl_int.hpp"
#include "rtl_utils.hpp"
using namespace BitSpace;
class compare_bit
{
private:
uint8_t result = 0x0;
public:
compare_bit(uint8_t set_to){result = set_to;}
bool is_unk(){ return (!result); }
bool is_gt(){ return (result&(0x1)); }
bool is_eq(){ return (result&(0x2)); }
bool is_lt(){ return (result&(0x4)); }
bool is_ne(){ return (!is_eq()); }
bool is_ge(){ return (result&(0x3)); }
bool is_le(){ return (result&(0x6)); }
};
#define UNK_EVAL compare_bit(0x0)
#define GT_EVAL compare_bit(0x1)
#define EQ_EVAL compare_bit(0x2)
#define LT_EVAL compare_bit(0x4)
static compare_bit eval_op(VNumber& a_in, VNumber& b_in)
{
assert_Werr( a_in.size() ,
"empty 1st bit string"
);
assert_Werr( b_in.size() ,
"empty 2nd bit string"
);
bool neg_a = (a_in.is_negative());
bool neg_b = (b_in.is_negative());
if(neg_a && !neg_b)
{
return LT_EVAL;
}
else if(!neg_a && neg_b)
{
return GT_EVAL;
}
VNumber a;
VNumber b;
bool invert_result = (neg_a && neg_b);
if(invert_result)
{
a = a_in.twos_complement();
b = b_in.twos_complement();
}
else
{
a = a_in;
b = b_in;
}
size_t std_length = std::max(a.size(), b.size());
bit_value_t pad_a = a.get_padding_bit();
bit_value_t pad_b = b.get_padding_bit();
for(size_t i=std_length-1; i < std_length ; i--)
{
bit_value_t bit_a = pad_a;
if(i < a.size())
{
bit_a = a.get_bit_from_lsb(i);
}
bit_value_t bit_b = pad_b;
if(i < b.size())
{
bit_b = b.get_bit_from_lsb(i);
}
if(BitSpace::l_lt[bit_a][bit_b] == BitSpace::_1)
{
return (!invert_result)? LT_EVAL: GT_EVAL;
}
else if(BitSpace::l_gt[bit_a][bit_b] == BitSpace::_1)
{
return (!invert_result)? GT_EVAL: LT_EVAL;
}
else if(BitSpace::l_eq[bit_a][bit_b] == BitSpace::_1)
{
continue;
}
else
{
return UNK_EVAL;
}
}
return EQ_EVAL;
}
static compare_bit eval_op(VNumber a,int64_t b)
{
VNumber bits_value = VNumber(std::to_string(std::abs(b)));
if(b < 0)
bits_value = bits_value.twos_complement();
return eval_op(a, bits_value);
}
/**
* Check if the Operation Should be Signed by Checking if Both Operands Are Signed:
*/
static bool is_signed_operation(VNumber& a, VNumber& b)
{
bool is_signed_operation = false;
if((true == a.is_signed()) && (true == b.is_signed()))
{
is_signed_operation = true;
}
return is_signed_operation;
}
/**
* Addition operations
*/
static VNumber sum_op(VNumber& a, VNumber& b, const bit_value_t& initial_carry, bool is_twos_complement_subtraction)
{
assert_Werr( a.size() ,
"empty 1st bit string"
);
assert_Werr( b.size() ,
"empty 2nd bit string"
);
size_t std_length = std::max(a.size(), b.size());
size_t new_length = ((true == is_twos_complement_subtraction) ? (std_length) : (std_length + 1));
const bit_value_t pad_a = a.get_padding_bit();
const bit_value_t pad_b = b.get_padding_bit();
bool is_addition_signed_operation = is_signed_operation(a, b);
//("pad_b: '" << (unsigned(pad_b)) << "'");
bit_value_t previous_carry = initial_carry;
VNumber result(new_length, _0, is_addition_signed_operation, a.is_defined_size() && b.is_defined_size());
for(size_t i = 0; i < new_length; i++)
{
bit_value_t bit_a = pad_a;
if(i < a.size())
{
bit_a = a.get_bit_from_lsb(i);
}
bit_value_t bit_b = pad_b;
if(i < b.size())
{
bit_b = b.get_bit_from_lsb(i);
}
result.set_bit_from_lsb(i, l_sum[previous_carry][bit_a][bit_b]);
previous_carry = l_carry[previous_carry][bit_a][bit_b];
}
return result;
}
static VNumber shift_op(VNumber& a, int64_t b, bool sign_shift)
{
VNumber to_return;
if(b==0)
{
to_return = a;
}
//if b is negative then shift right
else if(b < 0)
{
size_t u_b = static_cast<size_t>(-b);
bit_value_t pad = ( sign_shift ) ? a.get_padding_bit(): BitSpace::_0;
to_return = VNumber(a.size(), pad, sign_shift, a.is_defined_size());
for(size_t i=0; i < (a.size() - u_b); i++)
{
to_return.set_bit_from_lsb(i, a.get_bit_from_lsb(i+u_b));
}
}
else
{
size_t u_b = static_cast<size_t>(b);
bit_value_t pad = BitSpace::_0;
to_return =VNumber((a.size() + u_b), pad, sign_shift, a.is_defined_size());
for(size_t i=0; i < a.size(); i++)
{
to_return.set_bit_from_lsb(i+u_b, a.get_bit_from_lsb(i));
}
}
return to_return;
}
bool V_TRUE(VNumber& a)
{
return a.is_true();
}
bool V_FALSE(VNumber& a)
{
return a.is_false();
}
bool V_UNK(VNumber& a)
{
return a.is_dont_care_string();
}
bool V_IS_X(VNumber& a)
{
return a.is_x();
}
bool V_IS_Z(VNumber& a)
{
return a.is_z();
}
bool V_IS_SIGNED(VNumber& a)
{
return a.is_signed();
}
bool V_IS_UNSIGNED(VNumber& a)
{
return !a.is_signed();
}
std::string V_STRING(VNumber& a)
{
return a.to_printable();
}
/***
* __ __ __ ___ __ ___ __
* | | |\ | /\ |__) \ / / \ |__) |__ |__) /\ | | / \ |\ |
* \__/ | \| /~~\ | \ | \__/ | |___ | \ /~~\ | | \__/ | \|
*
*/
VNumber V_BITWISE_NOT(VNumber& a)
{
return a.invert();
}
VNumber V_LOGICAL_NOT(VNumber& a)
{
VNumber ored = a.bitwise_reduce(l_or);
VNumber noted = ored.invert();
return noted;
}
VNumber V_ADD(VNumber& a)
{
VNumber result(a);
return result;
}
VNumber V_MINUS(VNumber& a)
{
return a.twos_complement();
}
VNumber V_UNSIGNED(VNumber& a)
{
return a.to_unsigned();
}
VNumber V_SIGNED(VNumber& a)
{
return a.to_signed();
}
VNumber V_BITWISE_AND(VNumber& a)
{
VNumber to_return = a.bitwise_reduce(l_and);
return to_return;
}
VNumber V_BITWISE_OR(VNumber& a)
{
VNumber to_return = a.bitwise_reduce(l_or);
return to_return;
}
VNumber V_BITWISE_XOR(VNumber& a)
{
VNumber to_return = a.bitwise_reduce(l_xor);
return to_return;
}
VNumber V_BITWISE_NAND(VNumber& a)
{
VNumber to_return = a.bitwise_reduce(l_nand);
return to_return;
}
VNumber V_BITWISE_NOR(VNumber& a)
{
VNumber to_return = a.bitwise_reduce(l_nor);
return to_return;
}
VNumber V_BITWISE_XNOR(VNumber& a)
{
VNumber to_return = a.bitwise_reduce(l_xnor);
return to_return;
}
/***
* __ __ __ __ ___ __ ___ __
* |__) | |\ | /\ |__) \ / / \ |__) |__ |__) /\ | | / \ |\ |
* |__) | | \| /~~\ | \ | \__/ | |___ | \ /~~\ | | \__/ | \|
*
*/
VNumber V_REPLICATE(VNumber& a, VNumber& n_times)
{
assert_Werr(! n_times.is_dont_care_string(),
"Cannot use undefined number for the replication count");
return a.replicate(n_times.get_value());
}
VNumber V_CONCAT(std::vector<VNumber> concat_list)
{
assert_Werr(!concat_list.empty(),
"Concat List cannot be empty");
VNumber init = concat_list[0];
for(size_t i=1; i<concat_list.size(); i++)
{
init = init.insert_at_lsb(concat_list[i]);
}
return init;
}
VNumber V_BITWISE_AND(VNumber& a, VNumber& b)
{
return a.bitwise(b,l_and);
}
VNumber V_BITWISE_OR(VNumber& a, VNumber& b)
{
return a.bitwise(b,l_or);
}
VNumber V_BITWISE_XOR(VNumber& a, VNumber& b)
{
return a.bitwise(b,l_xor);
}
VNumber V_BITWISE_NAND(VNumber& a, VNumber& b)
{
return a.bitwise(b,l_nand);
}
VNumber V_BITWISE_NOR(VNumber& a, VNumber& b)
{
return a.bitwise(b,l_nor);
}
VNumber V_BITWISE_XNOR(VNumber& a, VNumber& b)
{
return a.bitwise(b,l_xnor);
}
/**
* Logical Operations
*/
VNumber V_CASE_EQUAL(VNumber& a, VNumber& b)
{
VNumber longEval = a.bitwise(b, l_case_eq);
VNumber eq = V_BITWISE_AND(longEval);
return eq;
}
VNumber V_CASE_NOT_EQUAL(VNumber& a, VNumber& b)
{
VNumber eq = V_CASE_EQUAL(a,b);
VNumber neq = V_LOGICAL_NOT(eq);
return neq;
}
VNumber V_LOGICAL_AND(VNumber& a, VNumber& b)
{
VNumber reduxA = a.bitwise_reduce(l_or);
VNumber reduxB = b.bitwise_reduce(l_or);
VNumber to_return = reduxA.bitwise(reduxB, l_and);
return to_return;
}
VNumber V_LOGICAL_OR(VNumber& a, VNumber& b)
{
VNumber reduxA = a.bitwise_reduce(l_or);
VNumber reduxB = b.bitwise_reduce(l_or);
VNumber to_return = reduxA.bitwise(reduxB, l_or);
return to_return;
}
VNumber V_LT(VNumber& a, VNumber& b)
{
compare_bit cmp = eval_op(a,b);
BitSpace::bit_value_t result = cmp.is_unk()? BitSpace::_x: cmp.is_lt()? BitSpace::_1: BitSpace::_0;
VNumber to_return(1, result, false, true);
return to_return;
}
VNumber V_GT(VNumber& a, VNumber& b)
{
compare_bit cmp = eval_op(a,b);
BitSpace::bit_value_t result = cmp.is_unk()? BitSpace::_x: cmp.is_gt()? BitSpace::_1: BitSpace::_0;
VNumber to_return(1, result, false, true);
return to_return;
}
VNumber V_EQUAL(VNumber& a, VNumber& b)
{
compare_bit cmp = eval_op(a,b);
BitSpace::bit_value_t result = cmp.is_unk()? BitSpace::_x: cmp.is_eq()? BitSpace::_1: BitSpace::_0;
VNumber to_return(1, result, false, true);
return to_return;
}
VNumber V_GE(VNumber& a, VNumber& b)
{
compare_bit cmp = eval_op(a,b);
BitSpace::bit_value_t result = cmp.is_unk()? BitSpace::_x: cmp.is_ge()? BitSpace::_1: BitSpace::_0;
VNumber to_return(1, result, false, true);
return to_return;
}
VNumber V_LE(VNumber& a, VNumber& b)
{
compare_bit cmp = eval_op(a,b);
BitSpace::bit_value_t result = cmp.is_unk()? BitSpace::_x: cmp.is_le()? BitSpace::_1: BitSpace::_0;
VNumber to_return(1, result, false, true);
return to_return;
}
VNumber V_NOT_EQUAL(VNumber& a, VNumber& b)
{
compare_bit cmp = eval_op(a,b);
BitSpace::bit_value_t result = cmp.is_unk()? BitSpace::_x: cmp.is_ne()? BitSpace::_1: BitSpace::_0;
VNumber to_return(1, result, false, true);
return to_return;
}
VNumber V_SIGNED_SHIFT_LEFT(VNumber& a, VNumber& b)
{
if(b.is_dont_care_string())
return VNumber("2'sbxx");
return shift_op(a, b.get_value(), a.is_signed());
}
VNumber V_SHIFT_LEFT(VNumber& a, VNumber& b)
{
if(b.is_dont_care_string())
return VNumber("2'sbxx");
return shift_op(a, b.get_value(), false);
}
VNumber V_SIGNED_SHIFT_RIGHT(VNumber& a, VNumber& b)
{
if(b.is_dont_care_string())
return VNumber("2'sbxx");
return shift_op(a, -1* b.get_value(), a.is_signed());
}
VNumber V_SHIFT_RIGHT(VNumber& a, VNumber& b)
{
if(b.is_dont_care_string())
return VNumber("2'sbxx");
return shift_op(a, -1* b.get_value(), false);
}
VNumber V_ADD(VNumber& a, VNumber& b)
{
return sum_op(a, b, _0, /* is_twos_complement_subtraction */ false);
}
VNumber V_MINUS(VNumber& a, VNumber& b)
{
size_t std_length = std::max(a.size(), b.size());
VNumber padded_a(a, std_length);
VNumber padded_b(b, std_length);
VNumber complement = V_MINUS(padded_b);
if (padded_b.is_negative() && complement.is_negative())
{
/* special case: 2's comp is identical to original, must pad */
complement = VNumber(padded_b, padded_b.size()+1);
complement = V_MINUS(complement);
}
return sum_op(padded_a, complement, _0, /* is_twos_complement_subtraction */ true);
}
VNumber V_MULTIPLY(VNumber& a_in, VNumber& b_in)
{
if(a_in.is_dont_care_string() || b_in.is_dont_care_string())
{
return VNumber("2'sbxx");
}
VNumber a;
VNumber b;
bool is_multiply_signed_operation = is_signed_operation(a_in, b_in);
bool neg_a = a_in.is_negative();
bool neg_b = b_in.is_negative();
if(neg_a)
{
a = V_MINUS(a_in);
if (a.is_negative())
{
/* special case: 2's comp is identical to original, must pad */
a = VNumber(a_in, a_in.size()+1);
a = V_MINUS(a);
}
}
else
{
a = a_in;
}
if(neg_b)
{
b = V_MINUS(b_in);
if (b.is_negative())
{
/* special case: 2's comp is identical to original, must pad */
b = VNumber(b_in, b_in.size()+1);
b = V_MINUS(b);
}
}
else
{
b = b_in;
}
bool invert_result = ((!neg_a && neg_b) || (neg_a && !neg_b));
VNumber result("0");
VNumber b_copy = b;
for(size_t i = 0; i < a.size(); i++)
{
bit_value_t bit_a = a.get_bit_from_lsb(i);
if(bit_a == _1)
{
result = V_ADD(result, b_copy);
}
b_copy = shift_op(b_copy, 1, is_multiply_signed_operation);
}
if(invert_result)
{
result = V_MINUS(result);
}
return result;
}
/*
* From Table 5-6 "Power operator rules" of IEEE Standard 1364-2005:
* "Verilog Hardware Description Language"; on Page 46 (PDF Page 76):
*
* Table 5-6 — Power operator rules:
*
* |-----------------------------------------------------------------------------|
* | | \ op1 is -> | | | | | |
* | \/ \ | negative < 1 | 1 | zero | 1 | positive > 1 |
* | op2 is \ | | | | | |
* |-----------------------------------------------------------------------------|
* | | | | | | |
* | Positive | op1 ** op2 | op2 is odd -> 1 | 0 | 1 | op1 ** op2 |
* | | | op2 is even -> 1 | | | |
* | | | | | | |
* |-----------------------------------------------------------------------------|
* | | | | | | |
* | Zero | 1 | 1 | 1 | 1 | 1 |
* | | | | | | |
* |-----------------------------------------------------------------------------|
* | | | | | | |
* | Negative | 0 | op2 is odd -> 1 | 'bx | 1 | 0 |
* | | | op2 is even -> 1 | | | |
* | | | | | | |
* |-----------------------------------------------------------------------------|
*/
VNumber V_POWER(VNumber& a, VNumber& b)
{
if(a.is_dont_care_string() || b.is_dont_care_string())
{
return VNumber("2'sbxx");
}
compare_bit res_a = eval_op(a, 0);
short val_a = (res_a.is_eq()) ? 0:
(res_a.is_lt()) ? (eval_op(a,-1).is_lt()) ? -2: -1:
/* GREATHER_THAN */ (eval_op(a,1).is_gt()) ? 2: 1;
compare_bit res_b = eval_op(b, 0);
short val_b = (res_b.is_eq()) ? 0:
(res_b.is_lt()) ? -1:
/* GREATHER_THAN */ 1;
// Compute: Case Where 'val_a <= -2' or 'val_a >= 2'; As-Per the Spec:
if(val_b > 0 && (val_a < -1 || val_a > 1 ))
{
VNumber result("2'sb01");
VNumber one = VNumber("2'sb01");
VNumber tmp_b = b;
while(eval_op(tmp_b, 0).is_gt())
{
VNumber tmp_b_comp = V_MINUS(tmp_b, one);
if (tmp_b_comp.is_negative() && tmp_b.is_negative())
{
/* special case: 2's comp is identical to original, must pad */
tmp_b_comp = VNumber(tmp_b, tmp_b.size()+1);
tmp_b_comp = V_MINUS(tmp_b_comp);
}
tmp_b = tmp_b_comp;
result = V_MULTIPLY(result, a);
}
return result;
}
else if (val_b == 0 || val_a == 1)
{
return VNumber("2'sb01");
}
else if(val_b == -1 && val_a == 0)
{
return VNumber("2'sbxx");
}
else if(val_a == -1)
{
// Even:
if(BitSpace::_0 == b.get_bit_from_lsb(0))
{
return VNumber("2'sb01");
}
// Odd:
else
{
return VNumber("2'sb11");
}
}
else
{
return VNumber("2'sb00");
}
}
/////////////////////////////
VNumber V_DIV(VNumber& a_in, VNumber& b_in)
{
if(a_in.is_dont_care_string() || b_in.is_dont_care_string() || eval_op(b_in,0).is_eq())
return VNumber("2'sbxx");
VNumber result("0");
bool is_division_signed_operation = is_signed_operation(a_in, b_in);
bool neg_a = a_in.is_negative();
bool neg_b = b_in.is_negative();
VNumber a = neg_a ? V_MINUS(a_in) : a_in;
VNumber b = neg_b ? V_MINUS(b_in) : b_in;
if (neg_a && a.is_negative())
{
/* special case: 2's comp is identical to original, must pad */
a = VNumber(a_in, a_in.size()+1);
a = V_MINUS(a);
}
if (neg_b && b.is_negative())
{
/* special case: 2's comp is identical to original, must pad */
b = VNumber(b_in, b_in.size()+1);
b = V_MINUS(b);
}
while(eval_op(a, b).is_ge() )
{
VNumber count("1");
VNumber tmp = b;
// initialize our variables
VNumber sub_with = tmp;
VNumber count_sub_with = count;
while(eval_op(tmp, a).is_le())
{
sub_with = tmp;
count_sub_with = count;
count = shift_op(count, 1, is_division_signed_operation);
tmp = shift_op(tmp, 1, is_division_signed_operation);
}
a = V_MINUS(a, sub_with);
result = V_ADD(result, count_sub_with);
}
return (neg_a != neg_b) ? V_MINUS(result) : result;
}
VNumber V_MOD(VNumber& a_in, VNumber& b_in)
{
if(a_in.is_dont_care_string() || b_in.is_dont_care_string() || eval_op(b_in, 0).is_eq())
return VNumber("2'sbxx");
bool neg_a = a_in.is_negative();
bool neg_b = b_in.is_negative();
VNumber a = neg_a ? V_MINUS(a_in) : a_in;
VNumber b = neg_b ? V_MINUS(b_in) : b_in;
if (neg_a && a.is_negative())
{
/* special case: 2's comp is identical to original, must pad */
a = VNumber(a_in, a_in.size()+1);
a = V_MINUS(a);
}
if (neg_b && b.is_negative())
{
/* special case: 2's comp is identical to original, must pad */
b = VNumber(b_in, b_in.size()+1);
b = V_MINUS(b);
}
bool is_modulo_signed_operation = is_signed_operation(a, b);
while(eval_op(a, b).is_ge())
{
VNumber tmp = b;
VNumber sub_with = tmp;
while( eval_op(tmp, a).is_le() )
{
sub_with = tmp;
tmp = shift_op(tmp, 1, is_modulo_signed_operation);
}
a = V_MINUS(a, sub_with);
}
return (neg_a) ? V_MINUS(a) : a;
}
/***
* ___ ___ __ __ __ __ ___ __ ___ __
* | |__ |__) |\ | /\ |__) \ / / \ |__) |__ |__) /\ | | / \ |\ |
* | |___ | \ | \| /~~\ | \ | \__/ | |___ | \ /~~\ | | \__/ | \|
*
*/
VNumber V_TERNARY(VNumber& a_in, VNumber& b_in, VNumber& c_in)
{
/* if a evaluates properly */
compare_bit eval = eval_op(V_LOGICAL_NOT(a_in),0);
return (eval.is_unk())? b_in.bitwise(c_in, l_ternary):
(eval.is_eq())? VNumber(b_in):
VNumber(c_in);
}