cxxrtl: add .get() and .set() accessors on value<> and wire<>.
authorwhitequark <whitequark@whitequark.org>
Thu, 18 Jun 2020 21:51:30 +0000 (21:51 +0000)
committerwhitequark <whitequark@whitequark.org>
Fri, 19 Jun 2020 02:31:35 +0000 (02:31 +0000)
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.

backends/cxxrtl/cxxrtl.h

index 85f45ac7fbc1801661698a0836ccb66a952fc003..f0d7b9fc7a06c734cd18d8c0a7f12dde0a2de44f 100644 (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