Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hal::bit_value multi-bit set/clear/xor #27

Merged
merged 1 commit into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ FixNamespaceComments: true
SpacesBeforeTrailingComments: 2
ColumnLimit: 80
QualifierAlignment: Right
InsertNewlineAtEOF: true
216 changes: 206 additions & 10 deletions include/libhal-util/bit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#pragma once

#include <climits>
#include <concepts>
#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -223,6 +224,8 @@ constexpr hal::bit_mask byte_m = byte_mask<ByteIndex>::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<size_t NibbleIndex>
Expand All @@ -240,6 +243,28 @@ struct nibble_mask
template<size_t NibbleIndex>
constexpr hal::bit_mask nibble_m = nibble_mask<NibbleIndex>::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<peripheral_state>(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<bit_mask field>
constexpr auto bit_extract(std::unsigned_integral auto p_value)
{
Expand All @@ -251,7 +276,28 @@ constexpr auto bit_extract(std::unsigned_integral auto p_value)
// Leaving only the desired bits
return static_cast<T>(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<mask>` 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)
{
Expand All @@ -264,83 +310,166 @@ constexpr auto bit_extract(bit_mask p_field,
return static_cast<T>(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<pre_scalar>(120UL)
* .set<enable>();
* reg->control = my_value.get();
*
* @tparam T - defaults to `std::uint32_t`, but can be any unsigned integral
* type.
*/
template<std::unsigned_integral T = std::uint32_t>
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<bit_mask field>
constexpr auto& set()
{
static_assert(field.position < width,
"Bit position exceeds register width");
constexpr auto mask = static_cast<T>(1U << field.position);
constexpr auto mask = field.value<T>();

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<T>(1U << p_field.position);
auto const mask = p_field.value<T>();

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<bit_mask field>
constexpr auto& clear()
{
static_assert(field.position < width,
"Bit position exceeds register width");
constexpr auto mask = static_cast<T>(1U << field.position);
constexpr auto inverted_mask = ~mask;
constexpr auto inverted_mask = ~field.value<T>();

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<T>(1U << p_field.position);
auto const inverted_mask = ~mask;
auto const inverted_mask = ~p_field.value<T>();

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<bit_mask field>
constexpr auto& toggle()
{
static_assert(field.position < width,
"Bit position exceeds register width");

constexpr auto mask = static_cast<T>(1U << field.position);
constexpr auto mask = field.value<T>();

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<T>(1U << p_field.position);
auto const mask = p_field.value<T>();

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<bit_mask field>
constexpr auto& insert(std::unsigned_integral auto p_value)
{
Expand All @@ -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.
Expand All @@ -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<bit_mask p_field, std::unsigned_integral auto p_value>
constexpr auto& insert()
{
Expand All @@ -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<std::integral U>
[[nodiscard]] constexpr auto to()
{
return static_cast<U>(m_value);
}

/**
* @brief Returns the represented value as a T instance.
*
* @return The represented value.
*/
[[nodiscard]] constexpr T get()
{
return m_value;
Expand All @@ -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<pre_scalar>(120UL)
* .set<enable>();
* // At this line, reg->control will be updated
*
* @tparam T - defaults to `std::uint32_t`, but can be any unsigned integral
* type.
*/
template<std::unsigned_integral T>
class bit_modify : public bit_value<T>
{
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<T>(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
2 changes: 1 addition & 1 deletion include/libhal-util/can.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,4 @@ struct can_bus_divider_t
p_lhs.length == p_rhs.length &&
p_lhs.is_remote_request == p_rhs.is_remote_request;
}
} // namespace hal
} // namespace hal
2 changes: 1 addition & 1 deletion src/streams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,4 +197,4 @@ work_state stream_skip::state()
{
return (m_skip == 0) ? work_state::finished : work_state::in_progress;
}
} // namespace hal
} // namespace hal
2 changes: 1 addition & 1 deletion tests/as_bytes.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,4 @@ void as_bytes_test()
};
};
};
} // namespace hal
} // namespace hal
Loading
Loading