cxxrtl: add .get() and .set() accessors on value<> and wire<>.

For several reasons:
  * They're more convenient than accessing .data.
  * They accommodate variably-sized types like size_t transparently.
  * They statically ensure that no out of range conversions happen.

For now these are only provided for unsigned integers, but eventually
they should be provided for signed integers too. (Annoyingly this
affects conversions to/from `char` at the moment.)

Fixes #2127.
This commit is contained in:
whitequark 2020-06-18 21:51:30 +00:00
parent dfde1cf1c5
commit 962a2f3bff
1 changed files with 47 additions and 6 deletions

View File

@ -111,6 +111,35 @@ struct value : public expr_base<value<Bits>> {
return ss.str();
}
// Conversion operations.
//
// These functions ensure that a conversion is never out of range, and should be always used, if at all
// possible, instead of direct manipulation of the `data` member. For very large types, .slice() and
// .concat() can be used to split them into more manageable parts.
template<class IntegerT>
CXXRTL_ALWAYS_INLINE
IntegerT get() const {
static_assert(std::numeric_limits<IntegerT>::is_integer && !std::numeric_limits<IntegerT>::is_signed,
"get<T>() requires T to be an unsigned integral type");
static_assert(std::numeric_limits<IntegerT>::digits >= Bits,
"get<T>() requires T to be at least as wide as the value is");
IntegerT result = 0;
for (size_t n = 0; n < chunks; n++)
result |= IntegerT(data[n]) << (n * chunk::bits);
return result;
}
template<class IntegerT>
CXXRTL_ALWAYS_INLINE
void set(IntegerT other) {
static_assert(std::numeric_limits<IntegerT>::is_integer && !std::numeric_limits<IntegerT>::is_signed,
"set<T>() requires T to be an unsigned integral type");
static_assert(std::numeric_limits<IntegerT>::digits >= Bits,
"set<T>() requires the value to be at least as wide as T is");
for (size_t n = 0; n < chunks; n++)
data[n] = (other >> (n * chunk::bits)) & chunk::mask;
}
// Operations with compile-time parameters.
//
// These operations are used to implement slicing, concatenation, and blitting.
@ -274,6 +303,10 @@ struct value : public expr_base<value<Bits>> {
data[offset_chunks] |= value ? 1 << offset_bits : 0;
}
explicit operator bool() const {
return !is_zero();
}
bool is_zero() const {
for (size_t n = 0; n < chunks; n++)
if (data[n] != 0)
@ -281,10 +314,6 @@ struct value : public expr_base<value<Bits>> {
return true;
}
explicit operator bool() const {
return !is_zero();
}
bool is_neg() const {
return data[chunks - 1] & (1 << ((Bits - 1) % chunk::bits));
}
@ -621,6 +650,18 @@ struct wire {
wire(wire<Bits> &&) = default;
wire<Bits> &operator=(const wire<Bits> &) = delete;
template<class IntegerT>
CXXRTL_ALWAYS_INLINE
IntegerT get() const {
return curr.template get<IntegerT>();
}
template<class IntegerT>
CXXRTL_ALWAYS_INLINE
void set(IntegerT other) {
next.template set<IntegerT>(other);
}
bool commit() {
if (curr != next) {
curr = next;
@ -967,13 +1008,13 @@ value<BitsY> logic_not(const value<BitsA> &a) {
template<size_t BitsY, size_t BitsA, size_t BitsB>
CXXRTL_ALWAYS_INLINE
value<BitsY> logic_and(const value<BitsA> &a, const value<BitsB> &b) {
return value<BitsY> { (bool(a) & bool(b)) ? 1u : 0u };
return value<BitsY> { (bool(a) && bool(b)) ? 1u : 0u };
}
template<size_t BitsY, size_t BitsA, size_t BitsB>
CXXRTL_ALWAYS_INLINE
value<BitsY> logic_or(const value<BitsA> &a, const value<BitsB> &b) {
return value<BitsY> { (bool(a) | bool(b)) ? 1u : 0u };
return value<BitsY> { (bool(a) || bool(b)) ? 1u : 0u };
}
// Reduction operations