1
0
mirror of https://github.com/sendyne/cppreg.git synced 2025-05-09 23:24:05 +00:00
cppreg/register/MergeWrite.h
Nicolas Clauvelin c1081a114e Code maintenance
* Revise and simplify most enable_if statements in the project.
* Update code style in all files.
* Update copyright year.
* Add a dedicated Memory.h header with the memory device implementation.
* Update documentation.
* CMake cleanup.
2019-04-24 17:00:57 -04:00

231 lines
7.2 KiB
C++

//! Merge write implementation.
/**
* @file MergeWrite.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*
* The "merge write" implementation is designed to make it possible to merge
* write operations on different fields into a single one.
* The implementation distinguishes between values known at compile time and
* value known at run time.
*
* By design the merge write implementation forces the caller to chain and
* finalize all write operations in a single pass.
*/
#ifndef CPPREG_MERGEWRITE_H
#define CPPREG_MERGEWRITE_H
#include "AccessPolicy.h"
#include "Internals.h"
namespace cppreg {
//! Merge write constant implementation.
/**
* @tparam Register Register on which the merged write will be performed.
* @tparam mask Initial mask.
* @tparam offset Initial offset.
* @tparam value Initial value.
*
* The initial data will come from the field on which the merge write
* will be initiated.
*
* This implementation is designed for operations in which all data is
* available at compile time. This makes it possible to leverage a
* template implementation and simplify most of the operations (based on
* the access polict implementation that detects trivial operations). In
* addition, this will also perform overflow checking.
*/
template <typename Register,
typename Register::type mask,
FieldOffset offset,
typename Register::type value>
class MergeWrite_tmpl {
private:
// Type alias to register base type.
using base_type = typename Register::type;
// Accumulated value.
constexpr static auto _accumulated_value =
base_type{(value << offset) & mask};
// Combined mask.
constexpr static auto _combined_mask = mask;
// Type helper.
template <typename F, base_type new_value>
using propagated =
MergeWrite_tmpl<Register,
(_combined_mask | F::mask),
FieldOffset{0},
(_accumulated_value & ~F::mask)
| ((new_value << F::offset) & F::mask)>;
// Default constructor.
MergeWrite_tmpl() = default;
// Disabled for shadow value register.
static_assert(!Register::shadow::value,
"merge write is not available for shadow value register");
public:
//! Instantiation method.
static MergeWrite_tmpl create() noexcept {
return {};
}
//!@{ Non-copyable and non-moveable.
MergeWrite_tmpl(const MergeWrite_tmpl&) = delete;
MergeWrite_tmpl& operator=(const MergeWrite_tmpl&) = delete;
MergeWrite_tmpl& operator=(MergeWrite_tmpl&&) = delete;
MergeWrite_tmpl operator=(MergeWrite_tmpl) = delete;
MergeWrite_tmpl(MergeWrite_tmpl&&) = delete;
//!@}
//! Closure method.
/**
* This is where the write happens.
*/
void done() const&& noexcept {
// Get memory pointer.
typename Register::MMIO& mmio_device = Register::rw_mem_device();
// Write to the whole register using the current accumulated value
// and combined mask.
// No offset needed because we write to the whole register.
RegisterWriteConstant<typename Register::MMIO,
typename Register::type,
_combined_mask,
FieldOffset{0},
_accumulated_value>::write(mmio_device);
}
//! With method for constant value.
/**
* @tparam F Field to be written
* @tparam new_value Value to write to the field.
* @return A merge write instance with accumulated data.
*/
template <typename F, base_type field_value>
propagated<F, field_value>&& with() const&& noexcept {
// Check that the field belongs to the register.
static_assert(
std::is_same<typename F::parent_register, Register>::value,
"MergeWrite_tmpl:: field is not from the same register");
// Check that there is no overflow.
constexpr auto no_overflow =
internals::check_overflow<typename Register::type,
field_value,
(F::mask >> F::offset)>::value;
static_assert(no_overflow,
"MergeWrite_tmpl:: field overflow in with() call");
return std::move(propagated<F, field_value>{});
}
};
//! Merge write implementation.
/**
* @tparam Register Register on which the merged write will be performed.
* @tparam mask Initial mask.
*
* The initial mask will come from the field on which the merge write
* will be initiated.
*/
template <typename Register, typename Register::type mask>
class MergeWrite {
private:
// Type alias to register base type.
using base_type = typename Register::type;
// Accumulated value.
base_type _accumulated_value;
// Combined mask.
constexpr static auto _combined_mask = mask;
// Type helper.
template <typename F>
using propagated = MergeWrite<Register, _combined_mask | F::mask>;
// Private default constructor.
constexpr MergeWrite() : _accumulated_value{0} {};
constexpr explicit MergeWrite(const base_type v) : _accumulated_value{v} {};
// Disabled for shadow value register.
static_assert(!Register::shadow::value,
"merge write is not available for shadow value register");
public:
//! Static instantiation method.
constexpr static MergeWrite create(const base_type value) noexcept {
return MergeWrite(value);
}
//!@ Move constructor.
MergeWrite(MergeWrite&& mw) noexcept
: _accumulated_value{mw._accumulated_value} {};
//!@{ Non-copyable.
MergeWrite(const MergeWrite&) = delete;
MergeWrite& operator=(const MergeWrite&) = delete;
MergeWrite& operator=(MergeWrite&&) = delete;
//!@}
//! Closure method.
/**
* This is where the write happens.
*/
void done() const&& noexcept {
// Get memory pointer.
typename Register::MMIO& mmio_device = Register::rw_mem_device();
// Write to the whole register using the current accumulated value
// and combined mask.
// No offset needed because we write to the whole register.
RegisterWrite<typename Register::MMIO,
base_type,
_combined_mask,
FieldOffset{0}>::write(mmio_device, _accumulated_value);
}
//! With method.
/**
* @tparam F Field type describing where to write in the register.
* @param value Value to write to the register.
* @return A merge write instance with accumulated data.
*/
template <typename F>
propagated<F> with(const base_type value) const&& noexcept {
// Check that the field belongs to the register.
static_assert(
std::is_same<typename F::parent_register, Register>::value,
"field is not from the same register in merge_write");
// Update accumulated value.
const auto new_value = static_cast<base_type>(
(_accumulated_value & ~F::mask) | ((value << F::offset) & F::mask));
return std::move(propagated<F>::create(new_value));
}
};
} // namespace cppreg
#endif // CPPREG_MERGEWRITE_H