diff --git a/include/libhal-util/bit.hpp b/include/libhal-util/bit.hpp index d16742b..6b53c8c 100644 --- a/include/libhal-util/bit.hpp +++ b/include/libhal-util/bit.hpp @@ -14,6 +14,7 @@ #pragma once +#include #include #include #include @@ -223,6 +224,8 @@ constexpr hal::bit_mask byte_m = byte_mask::value; * @ingroup Bit * @brief Helper for generating nibble position masks * + * A nibble is considered 4 bits or half a byte's width in bits. + * * @tparam NibbleIndex - the nibble position to make a mask for */ template @@ -240,6 +243,28 @@ struct nibble_mask template constexpr hal::bit_mask nibble_m = nibble_mask::value; +/** + * @ingroup Bit + * @brief Extracts a specific field from an unsigned integral value using a + * compile time (template) bit mask. + * + * Prefer to use this function over the non-template function if the bit mask + * can be known at compile time. This will allow the compiler to optimize this + * bit extract even further. + * + * USAGE: + * + * static constexpr auto peripheral_state = hal::bit_mask::from<4, 9>(); + * auto state = hal::bit_extract(reg->status); + * // Proceed to use `state` for something + * + * @tparam field - The bit mask defining the position and width of the field to + * be extracted. + * @param p_value The unsigned integral value from which the field will be + * extracted. + * @return A value representing only the specified field, with all other bits + * set to zero. + */ template constexpr auto bit_extract(std::unsigned_integral auto p_value) { @@ -251,7 +276,28 @@ constexpr auto bit_extract(std::unsigned_integral auto p_value) // Leaving only the desired bits return static_cast(masked); } - +/** + * @ingroup Bit + * @brief Extracts a specific field from an unsigned integral value using a bit + * mask. + * + * If the bit mask is known and constant at compile time, use the + * `hal::bit_extract` function as it provides more information to the + * compiler that it can use to optimize the bit extraction operation. + * + * USAGE: + * + * auto const peripheral_state = hal::bit_mask::from(4, 9); + * auto state = hal::bit_extract(peripheral_state, reg->status); + * // Proceed to use `state` for something + * + * @param p_field - The bit mask defining the position and width of the field to + * be extracted. + * @param p_value The unsigned integral value from which the field will be + * extracted. + * @return A value representing only the specified field, with all other bits + * set to zero. + */ constexpr auto bit_extract(bit_mask p_field, std::unsigned_integral auto p_value) { @@ -264,83 +310,166 @@ constexpr auto bit_extract(bit_mask p_field, return static_cast(masked); } +/** + * @ingroup Bit + * @brief A class for representing a value as a sequence of bits. + * + * This class provides a flexible way to manipulate and work with bit-level + * values, allowing for efficient and expressive operations on large integers. + * + * This class provides template and non-template options for manipulating bits. + * If the bitmask is KNOWN at COMPILE TIME, OPT to use the template versions of + * the APIs as the compiler will have enough information to deduce the bit mask + * and reduce the set of operations needed to perform the bit manipulation. + * + * USAGE: + * + * static constexpr auto pre_scalar = hal::bit_mask::from<3, 18>(); + * static constexpr auto enable = hal::bit_mask::from<19>(); + * hal::bit_value my_value; + * my_value.insert(120UL) + * .set(); + * reg->control = my_value.get(); + * + * @tparam T - defaults to `std::uint32_t`, but can be any unsigned integral + * type. + */ template class bit_value { public: - static constexpr std::uint32_t width = sizeof(T) * 8; + /** + * @brief The total number of bits in the represented value. + * + * This is calculated as the product of the size of `T` and the number of bits + * per byte (`CHAR_BIT`). + */ + static constexpr std::uint32_t width = sizeof(T) * CHAR_BIT; + /** + * @brief Constructs a new bit_value instance with an initial value. + * + * @param p_initial_value The initial value to use. Defaults to 0. + */ constexpr bit_value(T p_initial_value = 0) : m_value(p_initial_value) { } + /** + * @brief Sets (sets to a 1) multiple bits in the represented value. + * + * @tparam field A bit_mask type, which represents the position and size of + * the bit to set. + * @return A reference to this instance for method chaining. + */ template constexpr auto& set() { static_assert(field.position < width, "Bit position exceeds register width"); - constexpr auto mask = static_cast(1U << field.position); + constexpr auto mask = field.value(); m_value = m_value | mask; return *this; } + /** + * @brief Sets (sets to a 1) multiple bits in the represented value. + * + * @param p_field A bit_mask instance, which represents the position and size + * of the bit to set. + * @return A reference to this instance for chaining. + */ constexpr auto& set(bit_mask p_field) { - auto const mask = static_cast(1U << p_field.position); + auto const mask = p_field.value(); m_value = m_value | mask; return *this; } + /** + * @brief Clears (sets to 0) multiple bits in the represented value. + * + * @tparam field A bit_mask type, which represents the position and size of + * the bit to clear. + * @return A reference to this instance for chaining. + */ template constexpr auto& clear() { static_assert(field.position < width, "Bit position exceeds register width"); - constexpr auto mask = static_cast(1U << field.position); - constexpr auto inverted_mask = ~mask; + constexpr auto inverted_mask = ~field.value(); m_value = m_value & inverted_mask; return *this; } + /** + * @brief Clears (sets to 0) multiple bits in the represented value. + * + * @param p_field A bit_mask instance, which represents the position and size + * of the bit to clear. + * @return A reference to this instance for chaining. + */ constexpr auto& clear(bit_mask p_field) { - auto const mask = static_cast(1U << p_field.position); - auto const inverted_mask = ~mask; + auto const inverted_mask = ~p_field.value(); m_value = m_value & inverted_mask; return *this; } + /** + * @brief Toggles multiple bits in the represented value. + * + * @tparam field A bit_mask type, which represents the position and size of + * the bit to toggle. + * @return A reference to this instance for chaining. + */ template constexpr auto& toggle() { static_assert(field.position < width, "Bit position exceeds register width"); - constexpr auto mask = static_cast(1U << field.position); + constexpr auto mask = field.value(); m_value = m_value ^ mask; return *this; } + /** + * @brief Toggles a single bit in the represented value. + * + * @param p_field A bit_mask instance, which represents the position and size + * of the bit to toggle. + * @return A reference to this instance for chaining. + */ constexpr auto& toggle(bit_mask p_field) { - auto const mask = static_cast(1U << p_field.position); + auto const mask = p_field.value(); m_value = m_value ^ mask; return *this; } + /** + * @brief Inserts a new value into the represented bit sequence. + * + * @tparam field A bit_mask type, which represents the position and size of + * the bit to insert. + * @param p_value The new value to insert. + * @return A reference to this instance for chaining. + */ template constexpr auto& insert(std::unsigned_integral auto p_value) { @@ -358,6 +487,14 @@ class bit_value return *this; } + /** + * @brief Inserts a new value into the represented bit sequence. + * + * @param p_field A bit_mask instance, which represents the position and size + * of the bit to insert. + * @param p_value The new value to insert. + * @return A reference to this instance for chaining. + */ constexpr auto& insert(bit_mask p_field, std::unsigned_integral auto p_value) { // AND value with mask to remove any bits beyond the specified width. @@ -373,6 +510,14 @@ class bit_value return *this; } + /** + * @brief Inserts a new value into the represented bit sequence. + * + * @tparam p_field A bit_mask instance, which represents the position and size + * of the bit to insert. + * @tparam p_value The new value to insert. + * @return A reference to this instance for chaining. + */ template constexpr auto& insert() { @@ -389,12 +534,24 @@ class bit_value return *this; } + /** + * @brief Returns the represented value as an instance of U + * + * Performs truncating static cast if the sizeof(T) > sizeof(U) + * + * @return The represented value as type U. + */ template [[nodiscard]] constexpr auto to() { return static_cast(m_value); } + /** + * @brief Returns the represented value as a T instance. + * + * @return The represented value. + */ [[nodiscard]] constexpr T get() { return m_value; @@ -404,22 +561,61 @@ class bit_value T m_value; }; +/** + * @ingroup Bit + * @brief A class for modifying a register value by manipulating its bits. + * + * This class provides a convenient and expressive way to perform bitwise + * operations on a register value, allowing for efficient and safe modifications + * without compromising performance. + * + * USAGE: + * + * static constexpr auto pre_scalar = hal::bit_mask::from<3, 18>(); + * static constexpr auto enable = hal::bit_mask::from<19>(); + * + * hal::bit_modify(reg->control) + * .insert(120UL) + * .set(); + * // At this line, reg->control will be updated + * + * @tparam T - defaults to `std::uint32_t`, but can be any unsigned integral + * type. + */ template class bit_modify : public bit_value { public: + /** + * @brief Constructs a new bit_modify instance with an initial value and a + * pointer to the register. + * + * @param p_register_reference A reference to the register whose bits will be + * modified. + */ constexpr bit_modify(T volatile& p_register_reference) : bit_value(p_register_reference) , m_pointer(&p_register_reference) { } + /** + * @brief On destruction, update the original register's value with the final + * modified bits. + * + */ ~bit_modify() { *m_pointer = this->m_value; } private: + /** + * @brief A pointer to the original register value being modified. + * + * This allows for safe and efficient updates of the underlying register with + * the final modified bits. + */ T volatile* m_pointer; }; } // namespace hal