blob: 4158f7b67dec5cbe6162e194057cd9f7eef3160a [file] [log] [blame]
// Copyright 2023 Google LLC
//
// 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.
//
// Reference alu ops implementation
#ifndef TESTS_VERILATOR_SIM_KELVIN_ALU_REF_H_
#define TESTS_VERILATOR_SIM_KELVIN_ALU_REF_H_
#include <stdint.h>
#include <algorithm>
#include <limits>
#include <type_traits>
#include <utility>
// -----------------------------------------------------------------------------
// ALU.
template <typename T>
typename std::make_unsigned<T>::type absd(T a, T b) {
using UT = typename std::make_unsigned<T>::type;
UT ua = static_cast<UT>(a);
UT ub = static_cast<UT>(b);
return a > b ? ua - ub : ub - ua;
}
template <typename Td, typename Ts>
Td acc(Td a, Ts b) {
assert(sizeof(Td) > sizeof(Ts));
using UTd = typename std::make_unsigned<Td>::type;
return static_cast<Td>(static_cast<UTd>(a) + static_cast<UTd>(b));
}
template <typename T>
T add(T a, T b) {
using UT = typename std::make_unsigned<T>::type;
return static_cast<T>(static_cast<UT>(a) + static_cast<UT>(b));
}
template <typename T>
T add3(T a, T b, T c) {
using UT = typename std::make_unsigned<T>::type;
return static_cast<T>(static_cast<UT>(a) + static_cast<UT>(b) +
static_cast<UT>(c));
}
// Saturated addition.
template <typename T>
T adds(T a, T b) {
if (std::is_signed<T>::value) {
int64_t m = static_cast<int64_t>(a) + static_cast<int64_t>(b);
m = std::min<int64_t>(std::max<int64_t>(std::numeric_limits<T>::min(), m),
std::numeric_limits<T>::max());
return m;
}
uint64_t m = static_cast<uint64_t>(a) + static_cast<uint64_t>(b);
m = std::min<uint64_t>(std::numeric_limits<T>::max(), m);
return m;
}
// Widening add.
template <typename T>
uint32_t addw(T a, T b) {
if (std::is_signed<T>::value) {
return int64_t(a) + int64_t(b);
}
return uint64_t(a) + uint64_t(b);
}
template <typename T>
T cmp_eq(T a, T b) {
return a == b;
}
template <typename T>
T cmp_ne(T a, T b) {
return a != b;
}
template <typename T>
T cmp_lt(T a, T b) {
return a < b;
}
template <typename T>
T cmp_le(T a, T b) {
return a <= b;
}
template <typename T>
T cmp_gt(T a, T b) {
return a > b;
}
template <typename T>
T cmp_ge(T a, T b) {
return a >= b;
}
template <typename T>
T dup(T b) {
return b;
}
template <typename T>
T log_and(T a, T b) {
return a & b;
}
template <typename T>
int log_clb(T x) {
constexpr int n = sizeof(T) * 8;
if (x & (1u << (n - 1))) {
x = ~x;
}
for (int count = 0; count < n; count++) {
if ((x << count) >> (n - 1)) {
return count;
}
}
return n;
}
template <typename T>
int log_clz(const T x) {
constexpr int n = sizeof(T) * 8;
for (int count = 0; count < n; count++) {
if ((x << count) >> (n - 1)) {
return count;
}
}
return n;
}
template <typename T>
int log_cpop(T a) {
constexpr int n = sizeof(T) * 8;
int count = 0;
for (int i = 0; i < n; i++) {
if (a & (1 << i)) {
count++;
}
}
return count;
}
template <typename T>
T log_not(T a) {
return ~a;
}
template <typename T>
T log_or(T a, T b) {
return a | b;
}
template <typename T>
T log_rev(T a, T b) {
T count = b & 0b11111;
if (count & 1) a = ((a & 0x55555555) << 1) | ((a & 0xAAAAAAAA) >> 1);
if (count & 2) a = ((a & 0x33333333) << 2) | ((a & 0xCCCCCCCC) >> 2);
if (count & 4) a = ((a & 0x0F0F0F0F) << 4) | ((a & 0xF0F0F0F0) >> 4);
if (sizeof(T) == 1) return a;
if (count & 8) a = ((a & 0x00FF00FF) << 8) | ((a & 0xFF00FF00) >> 8);
if (sizeof(T) == 2) return a;
if (count & 16) a = ((a & 0x0000FFFF) << 16) | ((a & 0xFFFF0000) >> 16);
return a;
}
template <typename T>
T log_ror(T a, T b) {
if (sizeof(T) == 4) {
if (b & 1) a = (a >> 1) | (a << 31);
if (b & 2) a = (a >> 2) | (a << 30);
if (b & 4) a = (a >> 4) | (a << 28);
if (b & 8) a = (a >> 8) | (a << 24);
if (b & 16) a = (a >> 16) | (a << 16);
} else if (sizeof(T) == 2) {
if (b & 1) a = (a >> 1) | (a << 15);
if (b & 2) a = (a >> 2) | (a << 14);
if (b & 4) a = (a >> 4) | (a << 12);
if (b & 8) a = (a >> 8) | (a << 8);
} else if (sizeof(T) == 1) {
if (b & 1) a = (a >> 1) | (a << 7);
if (b & 2) a = (a >> 2) | (a << 6);
if (b & 4) a = (a >> 4) | (a << 4);
} else {
assert(false);
}
return a;
}
template <typename T>
T log_xor(T a, T b) {
return a ^ b;
}
template <typename T>
T hadd(T a, T b, int r) {
if (std::is_signed<T>::value) {
return (static_cast<int64_t>(a) + static_cast<int64_t>(b) + r) >> 1;
}
return (static_cast<uint64_t>(a) + static_cast<uint64_t>(b) + r) >> 1;
}
template <typename T>
T hsub(T a, T b, int r) {
if (std::is_signed<T>::value) {
return (static_cast<int64_t>(a) - static_cast<int64_t>(b) + r) >> 1;
}
return (static_cast<uint64_t>(a) - static_cast<uint64_t>(b) + r) >> 1;
}
template <typename T>
T madd(T a, T b, T c) {
if (std::is_signed<T>::value) {
return static_cast<int64_t>(a) * static_cast<int64_t>(b) +
static_cast<int64_t>(c);
}
return static_cast<uint64_t>(a) * static_cast<uint64_t>(b) +
static_cast<uint64_t>(c);
}
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
template <typename T>
T min(T a, T b) {
return a < b ? a : b;
}
template <typename T>
T mul(T a, T b) {
return a * b;
}
template <typename T>
T muls(T a, T b) {
if (std::is_signed<T>::value) {
int64_t m = static_cast<int64_t>(a) * static_cast<int64_t>(b);
m = std::max(
static_cast<int64_t>(std::numeric_limits<T>::min()),
std::min(static_cast<int64_t>(std::numeric_limits<T>::max()), m));
return m;
}
uint64_t m = uint64_t(a) * uint64_t(b);
m = std::min(static_cast<uint64_t>(std::numeric_limits<T>::max()), m);
return m;
}
// Widening multiplication.
template <typename T>
uint32_t mulw(T a, T b) {
if (std::is_signed<T>::value) {
return static_cast<int64_t>(a) * static_cast<int64_t>(b);
}
return static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
}
template <typename T>
T mv(T a) {
return a;
}
template <typename T>
std::pair<T, T> mvp(T a, T b) {
return {a, b};
}
template <typename T>
T dmulh(T a, T b, bool r, bool neg) {
constexpr int n = sizeof(T) * 8;
constexpr T maxNeg = 0x80000000 >> (32 - n);
int64_t m = static_cast<int64_t>(a) * static_cast<int64_t>(b);
if (r) {
int64_t rnd = 0x40000000ll >> (32 - n);
if (m < 0 && neg) {
rnd = (-0x40000000ll) >> (32 - n);
}
m += rnd;
}
m >>= (n - 1);
if (a == maxNeg && b == maxNeg) {
m = 0x7fffffff >> (32 - n);
}
return m;
}
template <typename T>
T mulh(T a, T b, bool r) {
constexpr int n = sizeof(T) * 8;
if (std::is_signed<T>::value) {
int64_t m = static_cast<int64_t>(a) * static_cast<int64_t>(b);
m += r ? 1ll << (n - 1) : 0;
return static_cast<uint64_t>(m) >> n;
}
uint64_t m = static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
m += r ? 1ull << (n - 1) : 0;
return m >> n;
}
template <typename T>
int32_t padd(T a, T b) {
if (std::is_signed<T>::value) {
return int64_t(a) + int64_t(b);
}
return uint64_t(a) + uint64_t(b);
}
template <typename T>
uint32_t psub(T a, T b) {
if (std::is_signed<T>::value) {
return int64_t(a) - int64_t(b);
}
return uint64_t(a) - uint64_t(b);
}
template <typename T>
T rsub(T a, T b) {
using UT = typename std::make_unsigned<T>::type;
return static_cast<T>(static_cast<UT>(b) - static_cast<UT>(a));
}
template <typename T>
T shl(T a, T b) {
constexpr int n = sizeof(T) * 8;
b &= (n - 1);
return a << b;
}
template <typename T>
T shr(T a, T b) {
constexpr int n = sizeof(T) * 8;
b &= (n - 1);
return a >> b;
}
template <typename T1, typename T2>
T1 srans(T2 a, T1 b, bool r, bool u) {
static_assert(2 * sizeof(T1) == sizeof(T2) || 4 * sizeof(T1) == sizeof(T2));
assert(std::is_signed<T1>::value == true);
assert(std::is_signed<T2>::value == true);
constexpr int n = sizeof(T2) * 8;
constexpr int m = sizeof(T1) * 8;
b &= (n - 1);
int64_t pad_a = u ? (int64_t(a) & ((1ll << n) - 1)) : int64_t(a);
int64_t s = (pad_a + (b && r ? (1ll << (b - 1)) : 0)) >> b;
int64_t neg_max = !u ? -1ll << (m - 1) : 0;
int64_t pos_max = !u ? (1ll << (m - 1)) - 1 : (1ull << m) - 1;
bool neg_sat = s < neg_max;
bool pos_sat = s > pos_max;
bool zero = !a;
if (neg_sat) return neg_max;
if (pos_sat) return pos_max;
if (zero) return 0;
return s;
}
template <typename T>
T shf(T a, T b, bool r) {
if (std::is_signed<T>::value == true) {
constexpr int n = sizeof(T) * 8;
int shamt = b;
int64_t s = a;
if (!a) {
return 0;
} else if (a < 0 && shamt >= n) {
s = -1 + r;
} else if (a > 0 && shamt >= n) {
s = 0;
} else if (shamt > 0) {
s = (static_cast<int64_t>(a) + (r ? (1ll << (shamt - 1)) : 0)) >> shamt;
} else { // shmat < 0
using UT = typename std::make_unsigned<T>::type;
UT ushamt = static_cast<UT>(-shamt <= n ? -shamt : n);
s = static_cast<int64_t>(static_cast<uint64_t>(a) << ushamt);
}
int64_t neg_max = -1ll << (n - 1);
int64_t pos_max = (1ll << (n - 1)) - 1;
bool neg_sat = a < 0 && (shamt <= -n || s < neg_max);
bool pos_sat = a > 0 && (shamt <= -n || s > pos_max);
if (neg_sat) return neg_max;
if (pos_sat) return pos_max;
return s;
}
constexpr int n = sizeof(T) * 8;
int shamt = static_cast<typename std::make_signed<T>::type>(b);
uint64_t s = a;
if (!a) {
return 0;
} else if (shamt > n) {
s = 0;
} else if (shamt > 0) {
s = (static_cast<uint64_t>(a) + (r ? (1ull << (shamt - 1)) : 0)) >> shamt;
} else { // shamt < 0
T ushamt = static_cast<T>(-shamt <= n ? -shamt : n);
s = static_cast<uint64_t>(a) << (ushamt);
}
uint64_t pos_max = (1ull << n) - 1;
bool pos_sat = a && (shamt < -n || s >= (1ull << n));
if (pos_sat) return pos_max;
return s;
}
template <typename T>
T sub(T a, T b) {
using UT = typename std::make_unsigned<T>::type;
return static_cast<T>(static_cast<UT>(a) - static_cast<UT>(b));
}
// Saturated subtraction.
template <typename T>
T subs(T a, T b) {
if (std::is_signed<T>::value) {
int64_t m = static_cast<int64_t>(a) - static_cast<int64_t>(b);
m = std::min<int64_t>(std::max<int64_t>(std::numeric_limits<T>::min(), m),
std::numeric_limits<T>::max());
return m;
}
return a < b ? 0 : a - b;
}
template <typename T>
uint32_t subw(T a, T b) {
if (std::is_signed<T>::value) {
return static_cast<int64_t>(a) - static_cast<int64_t>(b);
}
return static_cast<uint64_t>(a) - static_cast<uint64_t>(b);
}
#endif // TESTS_VERILATOR_SIM_KELVIN_ALU_REF_H_