File bit.hpp
File List > include > libhal-util > bit.hpp
Go to the documentation of this file
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <concepts>
#include <cstddef>
#include <cstdint>
#include <limits>
namespace hal {
struct bit_mask
{
std::uint32_t position;
std::uint32_t width;
template<std::uint32_t position1, std::uint32_t position2>
static consteval bit_mask from()
{
constexpr std::uint32_t plus_one = 1;
if constexpr (position1 < position2) {
return bit_mask{ .position = position1,
.width = plus_one + (position2 - position1) };
} else {
return bit_mask{ .position = position2,
.width = plus_one + (position1 - position2) };
}
}
template<std::uint32_t position>
static constexpr bit_mask from()
{
return bit_mask{ .position = position, .width = 1U };
}
static consteval bit_mask from(std::uint32_t position1,
std::uint32_t position2)
{
constexpr std::uint32_t plus_one = 1;
if (position1 < position2) {
return bit_mask{ .position = position1,
.width = plus_one + (position2 - position1) };
} else {
return bit_mask{ .position = position2,
.width = plus_one + (position1 - position2) };
}
}
static constexpr bit_mask from(std::uint32_t position)
{
return bit_mask{ .position = position, .width = 1U };
}
template<std::unsigned_integral T>
constexpr auto origin() const
{
// At compile time, generate variable containing all 1s with the size of the
// target parameter.
constexpr T field_of_ones = std::numeric_limits<T>::max();
// At compile time calculate the number of bits in the target parameter.
constexpr size_t target_width = sizeof(T) * 8;
// Create bit_mask by shifting the set of 1s down so that the number of 1s
// from bit position 0 is equal to the width parameter.
T mask_at_origin = static_cast<T>(field_of_ones >> (target_width - width));
return mask_at_origin;
}
template<std::unsigned_integral T>
constexpr auto value() const
{
return static_cast<T>(origin<T>() << position);
}
constexpr bool operator==(const bit_mask& other)
{
return other.position == position && other.width == width;
}
};
template<size_t ByteIndex>
struct byte_mask
{
static constexpr hal::bit_mask value{ .position = ByteIndex, .width = 8 };
};
template<size_t ByteIndex>
constexpr hal::bit_mask byte_m = byte_mask<ByteIndex>::value;
template<size_t NibbleIndex>
struct nibble_mask
{
static constexpr hal::bit_mask value{ .position = NibbleIndex, .width = 4 };
};
template<size_t NibbleIndex>
constexpr hal::bit_mask nibble_m = nibble_mask<NibbleIndex>::value;
template<bit_mask field>
constexpr auto bit_extract(std::unsigned_integral auto p_value)
{
using T = decltype(p_value);
// Shift desired value to the right to position 0
const auto shifted = p_value >> field.position;
// Mask away any bits left of the value based on the field width
const auto masked = shifted & field.origin<T>();
// Leaving only the desired bits
return static_cast<T>(masked);
}
constexpr auto bit_extract(bit_mask p_field,
std::unsigned_integral auto p_value)
{
using T = decltype(p_value);
// Shift desired value to the right to position 0
const auto shifted = p_value >> p_field.position;
// Mask away any bits left of the value based on the field width
const auto masked = shifted & p_field.origin<T>();
// Leaving only the desired bits
return static_cast<T>(masked);
}
template<std::unsigned_integral T>
class bit_value
{
public:
static constexpr std::uint32_t width = sizeof(T) * 8;
constexpr bit_value(T p_initial_value = 0)
: m_value(p_initial_value)
{
}
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);
m_value = m_value | mask;
return *this;
}
constexpr auto& set(bit_mask p_field)
{
const auto mask = static_cast<T>(1U << p_field.position);
m_value = m_value | mask;
return *this;
}
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;
m_value = m_value & inverted_mask;
return *this;
}
constexpr auto& clear(bit_mask p_field)
{
const auto mask = static_cast<T>(1U << p_field.position);
const auto inverted_mask = ~mask;
m_value = m_value & inverted_mask;
return *this;
}
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);
m_value = m_value ^ mask;
return *this;
}
constexpr auto& toggle(bit_mask p_field)
{
const auto mask = static_cast<T>(1U << p_field.position);
m_value = m_value ^ mask;
return *this;
}
template<bit_mask field>
constexpr auto& insert(std::unsigned_integral auto p_value)
{
const auto value_to_insert = static_cast<T>(p_value);
// AND value with mask to remove any bits beyond the specified width.
// Shift masked value into bit position and OR with target value.
const auto shifted_field = value_to_insert << field.position;
const auto new_value = shifted_field & field.value<T>();
// Clear width's number of bits in the target value at the bit position
// specified.
m_value = m_value & ~field.value<T>();
m_value = m_value | static_cast<T>(new_value);
return *this;
}
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.
// Shift masked value into bit position and OR with target value.
auto shifted_field = static_cast<T>(p_value) << p_field.position;
auto new_value = shifted_field & p_field.value<T>();
// Clear width's number of bits in the target value at the bit position
// specified.
m_value = m_value & ~p_field.value<T>();
m_value = m_value | static_cast<T>(new_value);
return *this;
}
template<std::integral U>
[[nodiscard]] constexpr auto to()
{
return static_cast<U>(m_value);
}
[[nodiscard]] constexpr T get()
{
return m_value;
}
protected:
T m_value;
};
template<std::unsigned_integral T>
class bit_modify : public bit_value<T>
{
public:
constexpr bit_modify(volatile T& p_register_reference)
: bit_value<T>(p_register_reference)
, m_pointer(&p_register_reference)
{
}
~bit_modify()
{
*m_pointer = this->m_value;
}
private:
volatile T* m_pointer;
};
} // namespace hal