From 962a2f3bff9da3ae1ccf0bf05fabdbecf30f15c8 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 18 Jun 2020 21:51:30 +0000 Subject: [PATCH] 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. --- backends/cxxrtl/cxxrtl.h | 53 +++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/backends/cxxrtl/cxxrtl.h b/backends/cxxrtl/cxxrtl.h index 85f45ac7f..f0d7b9fc7 100644 --- a/backends/cxxrtl/cxxrtl.h +++ b/backends/cxxrtl/cxxrtl.h @@ -111,6 +111,35 @@ struct value : public expr_base> { 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 + CXXRTL_ALWAYS_INLINE + IntegerT get() const { + static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, + "get() requires T to be an unsigned integral type"); + static_assert(std::numeric_limits::digits >= Bits, + "get() 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 + CXXRTL_ALWAYS_INLINE + void set(IntegerT other) { + static_assert(std::numeric_limits::is_integer && !std::numeric_limits::is_signed, + "set() requires T to be an unsigned integral type"); + static_assert(std::numeric_limits::digits >= Bits, + "set() 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> { 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> { 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 &&) = default; wire &operator=(const wire &) = delete; + template + CXXRTL_ALWAYS_INLINE + IntegerT get() const { + return curr.template get(); + } + + template + CXXRTL_ALWAYS_INLINE + void set(IntegerT other) { + next.template set(other); + } + bool commit() { if (curr != next) { curr = next; @@ -967,13 +1008,13 @@ value logic_not(const value &a) { template CXXRTL_ALWAYS_INLINE value logic_and(const value &a, const value &b) { - return value { (bool(a) & bool(b)) ? 1u : 0u }; + return value { (bool(a) && bool(b)) ? 1u : 0u }; } template CXXRTL_ALWAYS_INLINE value logic_or(const value &a, const value &b) { - return value { (bool(a) | bool(b)) ? 1u : 0u }; + return value { (bool(a) || bool(b)) ? 1u : 0u }; } // Reduction operations -- 2.30.2