Skip to content

Latest commit

 

History

History
1014 lines (892 loc) · 32.6 KB

memory.md

File metadata and controls

1014 lines (892 loc) · 32.6 KB

MCUTL memory access layer (mcutl/memory)

MCUTL routes all MCU memory accesses to a separate memory access layer. This is a zero-cost abstraction, which allows to unit-test your firmware on a host PC. Moreover, all the MCU-specific type casting is located exclusively in this layer. If you plan to unit-test your firmware on host PC, you need to use the functions described below when accessing the MCU memory. The MCUTL library uses these functions and never accesses MCU memory directly, which makes it testable. All of the definitions are in the mcutl::memory namespace, unless otherwise stated.

memory.h header

Contains functions to convert addresses and references to byte pointers.

to_bytes

template<typename Address>
auto to_bytes(Address* address) noexcept;
template<typename Struct>
auto to_bytes(Struct& obj) noexcept;

Convert either a pointer or a struct reference to the MCU byte pointer. Address and Struct types must be trivially copyable.

volatile_memory.h header

Contains functions to access volatile memory of the MCU. When compiling for the MCU, these functions represent raw volatile memory accesses. When the MCUTL_TEST macro is defined, these functions call the special test interface, which is then used for unit-testing. Basically, to test the firmware, you need to check that the code performs correct writes to (and sometime reads from) correct memory locations with the right order. The test mocks and interfaces are described in mcutl/tests in detail.

For Cortex-M3 microcontrollers, the memory access layer performs automatic memory bit-banding, when possible (i.e., when writing a single bit of a memory word inside the bitband regions). If this behavior is not desired, it can be disabled by defining the MCUTL_CORTEX_M3_BITBAND_DISABLE macro.

volatile_memory

template<typename Type, auto Address>
auto volatile_memory() noexcept;
template<typename Type, typename Address>
auto volatile_memory(Address address) noexcept;

Provides access to the memory location with specified address and type. Basically, casts the address to the volatile pointer of the required type.

to_address

template<typename Pointer>
uintptr_t to_address(volatile Pointer* pointer) noexcept;

Casts pointer to its integer address value.

max_bitmask

template<typename ValueType>
constexpr std::enable_if_t<std::is_unsigned_v<ValueType>, ValueType>
	max_bitmask = (std::numeric_limits<ValueType>::max)();

Represents a bitmask for the unsigned representation of the ValueType type, which have all bits set to 1.

set_register_bits, set_register_value, get_register_bits, get_register_flag

Hardware registers for many microcontrollers are represented as structs. For example, for STM32 Cortex-M microcontrollers, this is the definition of RCC (reset and clock control) registers:

#define __IO volatile
typedef struct
{
  __IO uint32_t CR;
  __IO uint32_t CFGR;
  __IO uint32_t CIR;
  __IO uint32_t APB2RSTR;
  __IO uint32_t APB1RSTR;
  __IO uint32_t AHBENR;
  __IO uint32_t APB2ENR;
  __IO uint32_t APB1ENR;
  __IO uint32_t BDCR;
  __IO uint32_t CSR;
} RCC_TypeDef;
#define RCC_BASE ...
#define RCC ((RCC_TypeDef *)RCC_BASE)

The set_register_bits, set_register_value, get_register_bits, get_register_flag groups of overridden functions are designed to work with such registers. You can write to the registers and read them directly, but it's much better to use these MCUTL functions, which enables unit-testing of your firmware on a host PC. MCUTL itself never writes or reads any registers directly to allow unit-testing. You may want to use as much template arguments as possible when calling these functions to allow more compile-time optimizations.


template<auto BitMask, auto BitValues, auto Reg, typename RegStruct>
void set_register_bits(volatile RegStruct* ptr) noexcept;

Sets BitValues bits of a bitmask BitMask to 1 in a register Reg of a struct RegStruct. For example, instead of the following code:

auto rcc_cfgr = RCC->CFGR;
rcc_cfgr &= ~RCC_CFGR_SW;
rcc_cfgr |= RCC_CFGR_SW_PLL;
RCC->CFGR = rcc_cfgr;

you may write the following:

mcutl::memory::set_register_bits<RCC_CFGR_SW, RCC_CFGR_SW_PLL, &RCC_TypeDef::CFGR>(RCC);

template<auto BitMask, auto Reg, typename RegStruct>
void set_register_bits(volatile RegStruct* ptr, type_of_Reg_value value) noexcept;

Sets value bits of a bitmask BitMask to 1 in a register Reg of a struct RegStruct. For example, instead of the following code:

auto rcc_cfgr = RCC->CFGR;
rcc_cfgr &= ~RCC_CFGR_SW;
rcc_cfgr |= RCC_CFGR_SW_PLL;
RCC->CFGR = rcc_cfgr;

you may write the following:

mcutl::memory::set_register_bits<RCC_CFGR_SW_PLL, &RCC_TypeDef::CFGR>(RCC, RCC_CFGR_SW);

template<auto BitMask, auto Reg, uintptr_t RegStructBase>
void set_register_bits(type_of_Reg_value value) noexcept;

Sets value bits of a bitmask BitMask to 1 in a register Reg of a struct with address RegStructBase. For example, instead of the following code:

auto rcc_cfgr = RCC->CFGR;
rcc_cfgr &= ~RCC_CFGR_SW;
rcc_cfgr |= RCC_CFGR_SW_PLL;
RCC->CFGR = rcc_cfgr;

you may write the following:

mcutl::memory::set_register_bits<RCC_CFGR_SW_PLL, &RCC_TypeDef::CFGR, RCC_BASE>(RCC_CFGR_SW);

template<auto BitMask, auto BitValues, auto Reg, uintptr_t RegStructBase>
void set_register_bits() noexcept;

Sets BitValues bits of a bitmask BitMask to 1 in a register Reg of a struct with address RegStructBase. For example, instead of the following code:

auto rcc_cfgr = RCC->CFGR;
rcc_cfgr &= ~RCC_CFGR_SW;
rcc_cfgr |= RCC_CFGR_SW_PLL;
RCC->CFGR = rcc_cfgr;

you may write the following:

mcutl::memory::set_register_bits<RCC_CFGR_SW_PLL, RCC_CFGR_SW, &RCC_TypeDef::CFGR, RCC_BASE>();

template<auto BitMask, auto BitValues, typename RegType, typename RegStruct>
void set_register_bits(RegType RegStruct::*reg_ptr, volatile RegStruct* ptr) noexcept;

Sets BitValues bits of a bitmask BitMask to 1 in a register reg_ptr of a struct RegStruct. For example, instead of the following code:

auto rcc_cfgr = RCC->CFGR;
rcc_cfgr &= ~RCC_CFGR_SW;
rcc_cfgr |= RCC_CFGR_SW_PLL;
RCC->CFGR = rcc_cfgr;

you may write the following:

mcutl::memory::set_register_bits<RCC_CFGR_SW, RCC_CFGR_SW_PLL>(&RCC_TypeDef::CFGR, RCC);

template<auto BitMask, typename RegType, typename RegStruct>
void set_register_bits(RegType RegStruct::*reg_ptr,
	volatile RegStruct* ptr, type_of_RegType_value value) noexcept;

Sets value bits of a bitmask BitMask to 1 in a register reg_ptr of a struct RegStruct. For example, instead of the following code:

auto rcc_cfgr = RCC->CFGR;
rcc_cfgr &= ~RCC_CFGR_SW;
rcc_cfgr |= RCC_CFGR_SW_PLL;
RCC->CFGR = rcc_cfgr;

you may write the following:

mcutl::memory::set_register_bits<RCC_CFGR_SW_PLL>(&RCC_TypeDef::CFGR, RCC, RCC_CFGR_SW);

template<auto BitMask, uintptr_t RegStructBase, typename RegType, typename RegStruct>
void set_register_bits(RegType RegStruct::*reg_ptr, type_of_RegType_value value) noexcept;

Sets value bits of a bitmask BitMask to 1 in a register reg_ptr of a struct with address RegStructBase. For example, instead of the following code:

auto rcc_cfgr = RCC->CFGR;
rcc_cfgr &= ~RCC_CFGR_SW;
rcc_cfgr |= RCC_CFGR_SW_PLL;
RCC->CFGR = rcc_cfgr;

you may write the following:

mcutl::memory::set_register_bits<RCC_CFGR_SW_PLL, RCC_BASE>(&RCC_TypeDef::CFGR, RCC_CFGR_SW);

template<auto BitMask, auto BitValues, uintptr_t RegStructBase,
	typename RegType, typename RegStruct>
void set_register_bits(RegType RegStruct::*reg_ptr) noexcept;

Sets BitValues bits of a bitmask BitMask to 1 in a register reg_ptr of a struct with address RegStructBase. For example, instead of the following code:

auto rcc_cfgr = RCC->CFGR;
rcc_cfgr &= ~RCC_CFGR_SW;
rcc_cfgr |= RCC_CFGR_SW_PLL;
RCC->CFGR = rcc_cfgr;

you may write the following:

mcutl::memory::set_register_bits<RCC_CFGR_SW_PLL, RCC_CFGR_SW, RCC_BASE>(&RCC_TypeDef::CFGR);

template<auto Value, auto Reg, typename RegStruct>
void set_register_value(volatile RegStruct* ptr) noexcept;

Writes Value to a register Reg of a struct RegStruct. For example, instead of the following code:

RCC->CFGR = RCC_CFGR_USBPRE;

you may write the following:

mcutl::memory::set_register_value<RCC_CFGR_USBPRE, &RCC_TypeDef::CFGR>(RCC);

template<auto Reg, typename RegStruct, typename Value>
void set_register_value(volatile RegStruct* ptr, Value value) noexcept;

Writes value to a register Reg of a struct RegStruct. For example, instead of the following code:

RCC->CFGR = RCC_CFGR_USBPRE;

you may write the following:

mcutl::memory::set_register_value<&RCC_TypeDef::CFGR>(RCC, RCC_CFGR_USBPRE);

template<auto Reg, uintptr_t RegStructBase, typename Value>
void set_register_value(Value value) noexcept;

Writes value to a register Reg of a struct with address RegStructBase. For example, instead of the following code:

RCC->CFGR = RCC_CFGR_USBPRE;

you may write the following:

mcutl::memory::set_register_value<&RCC_TypeDef::CFGR, RCC_BASE>(RCC_CFGR_USBPRE);

template<auto Value, auto Reg, uintptr_t RegStructBase>
void set_register_value() noexcept;

Writes Value to a register Reg of a struct with address RegStructBase. For example, instead of the following code:

RCC->CFGR = RCC_CFGR_USBPRE;

you may write the following:

mcutl::memory::set_register_value<RCC_CFGR_USBPRE, &RCC_TypeDef::CFGR, RCC_BASE>();

template<auto Value, typename RegType, typename RegStruct>
void set_register_value(RegType RegStruct::*reg_ptr, volatile RegStruct* ptr) noexcept;

Writes Value to a register reg_ptr of a struct RegStruct. For example, instead of the following code:

RCC->CFGR = RCC_CFGR_USBPRE;

you may write the following:

mcutl::memory::set_register_value<RCC_CFGR_USBPRE>(&RCC_TypeDef::CFGR, RCC);

template<typename RegType, typename RegStruct>
void set_register_value(RegType RegStruct::*reg_ptr, volatile RegStruct* ptr,
	type_of_RegType_value value) noexcept;

Writes value to a register reg_ptr of a struct RegStruct. For example, instead of the following code:

RCC->CFGR = RCC_CFGR_USBPRE;

you may write the following:

mcutl::memory::set_register_value(&RCC_TypeDef::CFGR, RCC, RCC_CFGR_USBPRE);

template<uintptr_t RegStructBase, typename RegType, typename RegStruct, typename Value>
void set_register_value(RegType RegStruct::*reg_ptr,
	type_of_RegType_value value) noexcept;

Writes value to a register reg_ptr of a struct with address RegStructBase. For example, instead of the following code:

RCC->CFGR = RCC_CFGR_USBPRE;

you may write the following:

mcutl::memory::set_register_value<RCC_BASE>(&RCC_TypeDef::CFGR, RCC_CFGR_USBPRE);

template<auto Value, uintptr_t RegStructBase, typename RegType, typename RegStruct>
void set_register_value(RegType RegStruct::*reg_ptr) noexcept;

Writes Value to a register reg_ptr of a struct with address RegStructBase. For example, instead of the following code:

RCC->CFGR = RCC_CFGR_USBPRE;

you may write the following:

mcutl::memory::set_register_value<RCC_CFGR_USBPRE, RCC_BASE>(&RCC_TypeDef::CFGR);

template<auto Reg, typename RegStruct>
auto get_register_bits(const volatile RegStruct* ptr) noexcept;

Returns Reg register value of a struct RegStruct. For example, instead of the following code:

auto value = RCC->CFGR;

you may write the following:

auto value = mcutl::memory::get_register_bits<&RCC_TypeDef::CFGR>(RCC);

template<auto Reg, uintptr_t RegStructBase>
auto get_register_bits() noexcept;

Returns Reg register value of a struct with address RegStructBase. For example, instead of the following code:

auto value = RCC->CFGR;

you may write the following:

auto value = mcutl::memory::get_register_bits<&RCC_TypeDef::CFGR, RCC_BASE>();

template<auto BitMask, auto Reg, uintptr_t RegStructBase>
auto get_register_bits() noexcept;

For a struct with address RegStructBase, returns Reg register value masked with BitMask. For example, instead of the following code:

auto value = RCC->CFGR & RCC_CFGR_SWS;

you may write the following:

auto value = mcutl::memory::get_register_bits<RCC_CFGR_SWS, &RCC_TypeDef::CFGR, RCC_BASE>();

template<auto BitMask, auto Reg, typename RegStruct>
auto get_register_bits(const volatile RegStruct* ptr) noexcept;

For a struct RegStruct, returns Reg register value masked with BitMask. For example, instead of the following code:

auto value = RCC->CFGR & RCC_CFGR_SWS;

you may write the following:

auto value = mcutl::memory::get_register_bits<RCC_CFGR_SWS, &RCC_TypeDef::CFGR>(RCC);

template<typename RegType, typename RegStruct>
auto get_register_bits(RegType RegStruct::*reg_ptr,
	const volatile RegStruct* ptr) noexcept;

Returns reg_ptr register value of a struct RegStruct. For example, instead of the following code:

auto value = RCC->CFGR;

you may write the following:

auto value = mcutl::memory::get_register_bits(&RCC_TypeDef::CFGR, RCC);

template<uintptr_t RegStructBase, typename RegType, typename RegStruct>
auto get_register_bits(RegType RegStruct::*reg_ptr) noexcept;

Returns reg_ptr register value of a struct with address RegStructBase. For example, instead of the following code:

auto value = RCC->CFGR;

you may write the following:

auto value = mcutl::memory::get_register_bits<RCC_BASE>(&RCC_TypeDef::CFGR);

template<auto BitMask, uintptr_t RegStructBase, typename RegType, typename RegStruct>
auto get_register_bits(RegType RegStruct::*reg_ptr) noexcept;

For a struct with address RegStructBase, returns reg_ptr register value masked with BitMask. For example, instead of the following code:

auto value = RCC->CFGR & RCC_CFGR_SWS;

you may write the following:

auto value = mcutl::memory::get_register_bits<RCC_CFGR_SWS, RCC_BASE>(&RCC_TypeDef::CFGR);

template<auto BitMask, typename RegType, typename RegStruct>
auto get_register_bits(RegType RegStruct::*reg_ptr,
	const volatile RegStruct* ptr) noexcept;

For a struct RegStruct, returns reg_ptr register value masked with BitMask. For example, instead of the following code:

auto value = RCC->CFGR & RCC_CFGR_SWS;

you may write the following:

auto value = mcutl::memory::get_register_bits<RCC_CFGR_SWS>(&RCC_TypeDef::CFGR, RCC);

template<auto BitMask, auto Reg, uintptr_t RegStructBase>
bool get_register_flag() noexcept;

For a struct with address RegStructBase, returns Reg register value masked with BitMask and converted to bool. For example, instead of the following code:

bool value = !!(RCC->CFGR & RCC_CFGR_SWS);

you may write the following:

bool value = mcutl::memory::get_register_flag<RCC_CFGR_SWS, &RCC_TypeDef::CFGR, RCC_BASE>();

template<auto BitMask, auto Reg, typename RegStruct>
bool get_register_flag(const volatile RegStruct* ptr) noexcept;

For a struct RegStruct, returns Reg register value masked with BitMask and converted to bool. For example, instead of the following code:

bool value = !!(RCC->CFGR & RCC_CFGR_SWS);

you may write the following:

bool value = mcutl::memory::get_register_flag<RCC_CFGR_SWS, &RCC_TypeDef::CFGR>(RCC);

template<auto BitMask, uintptr_t RegStructBase, typename RegType, typename RegStruct>
bool get_register_flag(RegType RegStruct::*reg_ptr) noexcept;

For a struct with address RegStructBase, returns reg_ptr register value masked with BitMask and converted to bool. For example, instead of the following code:

bool value = !!(RCC->CFGR & RCC_CFGR_SWS);

you may write the following:

bool value = mcutl::memory::get_register_flag<RCC_CFGR_SWS, RCC_BASE>(&RCC_TypeDef::CFGR);

template<auto BitMask, typename RegType, typename RegStruct>
bool get_register_flag(RegType RegStruct::*reg_ptr,
	const volatile RegStruct* ptr) noexcept;

For a struct RegStruct, returns reg_ptr register value masked with BitMask and converted to bool. For example, instead of the following code:

bool value = !!(RCC->CFGR & RCC_CFGR_SWS);

you may write the following:

bool value = mcutl::memory::get_register_flag<RCC_CFGR_SWS>(&RCC_TypeDef::CFGR, RCC);

set_register_array_bits, set_register_array_value, get_register_array_bits, get_register_array_flag

These functions are designed to work with the register arrays. For example, this is the definition of the NVIC_Type structure, which allows accesses to the nested vectored interrupt controller for Cortex-M3 controllers:

#define     __OM     volatile
#define     __IOM    volatile
typedef struct
{
  __IOM uint32_t ISER[8U];
        uint32_t RESERVED0[24U];
  __IOM uint32_t ICER[8U];
        uint32_t RESERVED1[24U];
  __IOM uint32_t ISPR[8U];
        uint32_t RESERVED2[24U];
  __IOM uint32_t ICPR[8U];
        uint32_t RESERVED3[24U];
  __IOM uint32_t IABR[8U];
        uint32_t RESERVED4[56U];
  __IOM uint8_t  IP[240U];
        uint32_t RESERVED5[644U];
  __OM  uint32_t STIR;
}  NVIC_Type;

Here all the registers (ISER, ICER, etc) are arrays, and the set_register_array_bits, set_register_array_value, get_register_array_bits, get_register_array_flag groups of overridden functions are designed to work with such register arrays. These functions support array indexing at compile time only.


template<auto BitMask, auto BitValues,
	auto Reg, size_t RegArrIndex, typename RegStruct>
void set_register_array_bits(volatile RegStruct* ptr) noexcept;

Sets BitValues bits of a bitmask BitMask to 1 in a register array Reg index RegArrIndex of a struct with address RegStructBase. For example, instead of the following code:

auto afio_exticr_2 = AFIO->EXTICR[2];
afio_exticr_2 &= ~AFIO_EXTICR2_EXTI4;
afio_exticr_2 |= AFIO_EXTICR2_EXTI4_PC;
AFIO->EXTICR[2] = afio_exticr_2;

you may write the following:

mcutl::memory::set_register_array_bits<AFIO_EXTICR2_EXTI4,
	AFIO_EXTICR2_EXTI4_PC, &AFIO_TypeDef::EXTICR, 2>(AFIO);

template<auto BitMask, auto Reg, size_t RegArrIndex, typename RegStruct>
void set_register_array_bits(volatile RegStruct* ptr, type_of_Reg_value values) noexcept;

Sets values bits of a bitmask BitMask to 1 in a register array Reg index RegArrIndex of a struct RegStruct. For example, instead of the following code:

auto afio_exticr_2 = AFIO->EXTICR[2];
afio_exticr_2 &= ~AFIO_EXTICR2_EXTI4;
afio_exticr_2 |= AFIO_EXTICR2_EXTI4_PC;
AFIO->EXTICR[2] = afio_exticr_2;

you may write the following:

mcutl::memory::set_register_array_bits<AFIO_EXTICR2_EXTI4,
	&AFIO_TypeDef::EXTICR, 2>(AFIO, AFIO_EXTICR2_EXTI4_PC);

template<auto BitMask, auto Reg, size_t RegArrIndex, uintptr_t RegStructBase>
void set_register_array_bits(type_of_Reg_value value) noexcept;

For a struct with address RegStructBase, sets value bits of a bitmask BitMask to 1 in a register array Reg index RegArrIndex. For example, instead of the following code:

auto afio_exticr_2 = AFIO->EXTICR[2];
afio_exticr_2 &= ~AFIO_EXTICR2_EXTI4;
afio_exticr_2 |= AFIO_EXTICR2_EXTI4_PC;
AFIO->EXTICR[2] = afio_exticr_2;

you may write the following:

mcutl::memory::set_register_array_bits<AFIO_EXTICR2_EXTI4,
	&AFIO_TypeDef::EXTICR, 2, AFIO_BASE>(AFIO_EXTICR2_EXTI4_PC);

template<auto BitMask, auto BitValues, auto Reg,
	size_t RegArrIndex, uintptr_t RegStructBase>
void set_register_array_bits() noexcept;

For a struct with address RegStructBase, sets BitValues bits of a bitmask BitMask to 1 in a register array Reg index RegArrIndex. For example, instead of the following code:

auto afio_exticr_2 = AFIO->EXTICR[2];
afio_exticr_2 &= ~AFIO_EXTICR2_EXTI4;
afio_exticr_2 |= AFIO_EXTICR2_EXTI4_PC;
AFIO->EXTICR[2] = afio_exticr_2;

you may write the following:

mcutl::memory::set_register_array_bits<AFIO_EXTICR2_EXTI4,
	AFIO_EXTICR2_EXTI4_PC, &AFIO_TypeDef::EXTICR, 2, AFIO_BASE>();

template<auto BitMask, auto BitValues,
	size_t RegArrIndex, typename RegType, typename RegStruct>
void set_register_array_bits(RegType RegStruct::*reg_ptr, volatile RegStruct* ptr) noexcept;

Sets BitValues bits of a bitmask BitMask to 1 in a register array reg_ptr index RegArrIndex for a struct RegStruct. For example, instead of the following code:

auto afio_exticr_2 = AFIO->EXTICR[2];
afio_exticr_2 &= ~AFIO_EXTICR2_EXTI4;
afio_exticr_2 |= AFIO_EXTICR2_EXTI4_PC;
AFIO->EXTICR[2] = afio_exticr_2;

you may write the following:

mcutl::memory::set_register_array_bits<AFIO_EXTICR2_EXTI4,
	AFIO_EXTICR2_EXTI4_PC, 2>(&AFIO_TypeDef::EXTICR, AFIO);

template<auto BitMask, size_t RegArrIndex, typename RegType, typename RegStruct>
void set_register_array_bits(RegType RegStruct::*reg_ptr,
	volatile RegStruct* ptr, type_of_RegType_value values) noexcept;

Sets values bits of a bitmask BitMask to 1 in a register array reg_ptr index RegArrIndex for a struct RegStruct. For example, instead of the following code:

auto afio_exticr_2 = AFIO->EXTICR[2];
afio_exticr_2 &= ~AFIO_EXTICR2_EXTI4;
afio_exticr_2 |= AFIO_EXTICR2_EXTI4_PC;
AFIO->EXTICR[2] = afio_exticr_2;

you may write the following:

mcutl::memory::set_register_array_bits<AFIO_EXTICR2_EXTI4,
	2>(&AFIO_TypeDef::EXTICR, AFIO, AFIO_EXTICR2_EXTI4_PC);

template<auto BitMask, size_t RegArrIndex, uintptr_t RegStructBase,
	typename RegType, typename RegStruct>
void set_register_array_bits(RegType RegStruct::*reg_ptr, type_of_RegType_value values) noexcept;

For a struct with address RegStructBase, sets values bits of a bitmask BitMask to 1 in a register array reg_ptr index RegArrIndex. For example, instead of the following code:

auto afio_exticr_2 = AFIO->EXTICR[2];
afio_exticr_2 &= ~AFIO_EXTICR2_EXTI4;
afio_exticr_2 |= AFIO_EXTICR2_EXTI4_PC;
AFIO->EXTICR[2] = afio_exticr_2;

you may write the following:

mcutl::memory::set_register_array_bits<AFIO_EXTICR2_EXTI4,
	2, AFIO_BASE>(&AFIO_TypeDef::EXTICR, AFIO_EXTICR2_EXTI4_PC);

template<auto BitMask, auto BitValues,
	size_t RegArrIndex, uintptr_t RegStructBase, typename RegType, typename RegStruct>
void set_register_array_bits(RegType RegStruct::*reg_ptr) noexcept;

For a struct with address RegStructBase, sets BitValues bits of a bitmask BitMask to 1 in a register array reg_ptr index RegArrIndex. For example, instead of the following code:

auto afio_exticr_2 = AFIO->EXTICR[2];
afio_exticr_2 &= ~AFIO_EXTICR2_EXTI4;
afio_exticr_2 |= AFIO_EXTICR2_EXTI4_PC;
AFIO->EXTICR[2] = afio_exticr_2;

you may write the following:

mcutl::memory::set_register_array_bits<AFIO_EXTICR2_EXTI4,
	AFIO_EXTICR2_EXTI4_PC, 2, AFIO_BASE>(&AFIO_TypeDef::EXTICR);

template<auto Value, auto Reg, size_t RegArrIndex, typename RegStruct>
void set_register_array_value(volatile RegStruct* ptr) noexcept;

Writes Value to a register array Reg index RegArrIndex of a struct RegStruct. For example, instead of the following code:

NVIC->ISER[3] = 32;

you may write the following:

mcutl::memory::set_register_array_value<32, &NVIC_Type::ISER, 3>(NVIC);

template<auto Reg, size_t RegArrIndex, typename RegStruct, typename Value>
void set_register_array_value(volatile RegStruct* ptr, Value value) noexcept;

Writes value to a register array Reg index RegArrIndex of a struct RegStruct. For example, instead of the following code:

NVIC->ISER[3] = 32;

you may write the following:

mcutl::memory::set_register_array_value<&NVIC_Type::ISER, 3>(NVIC, 32);

template<auto Reg, size_t RegArrIndex, uintptr_t RegStructBase, typename Value>
void set_register_array_value(Value value) noexcept;

For a struct with address RegStructBase, writes value to a register array Reg index RegArrIndex. For example, instead of the following code:

NVIC->ISER[3] = 32;

you may write the following:

mcutl::memory::set_register_array_value<&NVIC_Type::ISER, 3, NVIC_BASE>(32);

template<auto Value, auto Reg, size_t RegArrIndex, uintptr_t RegStructBase>
void set_register_array_value() noexcept;

For a struct with address RegStructBase, writes Value to a register array Reg index RegArrIndex. For example, instead of the following code:

NVIC->ISER[3] = 32;

you may write the following:

mcutl::memory::set_register_array_value<32, &NVIC_Type::ISER, 3, NVIC_BASE>();

template<auto Value, size_t RegArrIndex, typename RegType, typename RegStruct>
void set_register_array_value(RegType RegStruct::*reg_ptr,
	volatile RegStruct* ptr) noexcept;

Writes Value to a register array reg_ptr index RegArrIndex of a struct RegStruct. For example, instead of the following code:

NVIC->ISER[3] = 32;

you may write the following:

mcutl::memory::set_register_array_value<32, 3>(&NVIC_Type::ISER, NVIC);

template<size_t RegArrIndex, typename RegType, typename RegStruct>
void set_register_array_value(RegType RegStruct::*reg_ptr,
	volatile RegStruct* ptr,
	std::remove_cv_t<std::remove_all_extents_t<RegType>> value) noexcept;

Writes value to a register array reg_ptr index RegArrIndex of a struct RegStruct. For example, instead of the following code:

NVIC->ISER[3] = 32;

you may write the following:

mcutl::memory::set_register_array_value<3>(&NVIC_Type::ISER, NVIC, 32);

template<size_t RegArrIndex, uintptr_t RegStructBase,
	typename RegType, typename RegStruct>
void set_register_array_value(RegType RegStruct::*reg_ptr, type_of_RegType_value value) noexcept;

For a struct with address RegStructBase, writes value to a register array reg_ptr index RegArrIndex. For example, instead of the following code:

NVIC->ISER[3] = 32;

you may write the following:

mcutl::memory::set_register_array_value<3, NVIC_BASE>(&NVIC_Type::ISER, 32);

template<auto Value, size_t RegArrIndex, uintptr_t RegStructBase,
	typename RegType, typename RegStruct>
void set_register_array_value(RegType RegStruct::*reg_ptr) noexcept;

For a struct with address RegStructBase, writes Value to a register array reg_ptr index RegArrIndex. For example, instead of the following code:

NVIC->ISER[3] = 32;

you may write the following:

mcutl::memory::set_register_array_value<32, 3, NVIC_BASE>(&NVIC_Type::ISER);

template<auto Reg, size_t RegArrIndex, typename RegStruct>
auto get_register_array_bits(const volatile RegStruct* ptr) noexcept;

Returns Reg register array index RegArrIndex value of a struct RegStruct. For example, instead of the following code:

auto value = AFIO->EXTICR[2];

you may write the following:

auto value = mcutl::memory::get_register_array_bits<&AFIO_TypeDef::EXTICR, 2>(AFIO);

template<auto Reg, size_t RegArrIndex, uintptr_t RegStructBase>
auto get_register_array_bits() noexcept;

For a struct with address RegStructBase, returns Reg register array index RegArrIndex value. For example, instead of the following code:

auto value = AFIO->EXTICR[2];

you may write the following:

auto value = mcutl::memory::get_register_array_bits<&AFIO_TypeDef::EXTICR, 2, AFIO_BASE>();

template<auto BitMask, auto Reg, size_t RegArrIndex, uintptr_t RegStructBase>
auto get_register_array_bits() noexcept;

For a struct with address RegStructBase, returns Reg register array index RegArrIndex value masked with BitMask. For example, instead of the following code:

auto value = AFIO->EXTICR[2] & AFIO_EXTICR2_EXTI4_PC;

you may write the following:

auto value = mcutl::memory::get_register_array_bits<AFIO_EXTICR2_EXTI4_PC,
	&AFIO_TypeDef::EXTICR, 2, AFIO_BASE>();

template<auto BitMask, auto Reg, size_t RegArrIndex, typename RegStruct>
auto get_register_array_bits(const volatile RegStruct* ptr) noexcept;

For a struct RegStruct, returns Reg register array index RegArrIndex value masked with BitMask. For example, instead of the following code:

auto value = AFIO->EXTICR[2] & AFIO_EXTICR2_EXTI4_PC;

you may write the following:

auto value = mcutl::memory::get_register_array_bits<AFIO_EXTICR2_EXTI4_PC,
	&AFIO_TypeDef::EXTICR, 2>(AFIO);

template<size_t RegArrIndex, typename RegType, typename RegStruct>
auto get_register_array_bits(RegType RegStruct::*reg_ptr,
	const volatile RegStruct* ptr) noexcept;

Returns reg_ptr register array index RegArrIndex value of a struct RegStruct. For example, instead of the following code:

auto value = AFIO->EXTICR[2];

you may write the following:

auto value = mcutl::memory::get_register_array_bits<2>(&AFIO_TypeDef::EXTICR, AFIO);

template<size_t RegArrIndex, uintptr_t RegStructBase,
	typename RegType, typename RegStruct>
auto get_register_array_bits(RegType RegStruct::*reg_ptr) MCUTL_NOEXCEPT

For a struct with address RegStructBase, returns reg_ptr register array index RegArrIndex value. For example, instead of the following code:

auto value = AFIO->EXTICR[2];

you may write the following:

auto value = mcutl::memory::get_register_array_bits<2, AFIO_BASE>(&AFIO_TypeDef::EXTICR);

template<auto BitMask, size_t RegArrIndex, uintptr_t RegStructBase,
	typename RegType, typename RegStruct>
auto get_register_array_bits(RegType RegStruct::*reg_ptr) noexcept;

For a struct with address RegStructBase, returns reg_ptr register array index RegArrIndex value masked with BitMask. For example, instead of the following code:

auto value = AFIO->EXTICR[2] & AFIO_EXTICR2_EXTI4_PC;

you may write the following:

auto value = mcutl::memory::get_register_array_bits<AFIO_EXTICR2_EXTI4_PC,
	2, AFIO_BASE>(&AFIO_TypeDef::EXTICR);

template<auto BitMask, size_t RegArrIndex, typename RegType, typename RegStruct>
auto get_register_array_bits(RegType RegStruct::*reg_ptr,
	const volatile RegStruct* ptr) noexcept;

For a struct RegStruct, returns reg_ptr register array index RegArrIndex value masked with BitMask. For example, instead of the following code:

auto value = AFIO->EXTICR[2] & AFIO_EXTICR2_EXTI4_PC;

you may write the following:

auto value = mcutl::memory::get_register_array_bits<AFIO_EXTICR2_EXTI4_PC, 2>(
	&AFIO_TypeDef::EXTICR, AFIO);

template<auto BitMask, auto Reg, size_t RegArrIndex, uintptr_t RegStructBase>
bool get_register_array_flag() noexcept;

For a struct with address RegStructBase, returns Reg register value masked with BitMask and converted to bool. For example, instead of the following code:

bool value = NVIC->ISER[1] & (1 << 5);

you may write the following:

bool value = mcutl::memory::get_register_array_flag<(1 << 5), &NVIC_Type::ISER, 1, NVIC_BASE>();

template<auto BitMask, auto Reg, size_t RegArrIndex, typename RegStruct>
bool get_register_array_flag(const volatile RegStruct* ptr) noexcept;

For a struct RegStruct, returns Reg register value masked with BitMask and converted to bool. For example, instead of the following code:

bool value = NVIC->ISER[1] & (1 << 5);

you may write the following:

bool value = mcutl::memory::get_register_array_flag<(1 << 5), &NVIC_Type::ISER, 1>(NVIC);

template<auto BitMask, size_t RegArrIndex, uintptr_t RegStructBase,
	typename RegType, typename RegStruct>
bool get_register_array_flag(RegType RegStruct::*reg_ptr) noexcept;

For a struct with address RegStructBase, returns reg_ptr register value masked with BitMask and converted to bool. For example, instead of the following code:

bool value = NVIC->ISER[1] & (1 << 5);

you may write the following:

bool value = mcutl::memory::get_register_array_flag<(1 << 5), 1, NVIC_BASE>(&NVIC_Type::ISER);

template<auto BitMask, size_t RegArrIndex, typename RegType, typename RegStruct>
bool get_register_array_flag(RegType RegStruct::*reg_ptr,
	const volatile RegStruct* ptr) noexcept;

For a struct RegStruct, returns reg_ptr register value masked with BitMask and converted to bool. For example, instead of the following code:

bool value = NVIC->ISER[1] & (1 << 5);

you may write the following:

bool value = mcutl::memory::get_register_array_flag<(1 << 5), 1>(&NVIC_Type::ISER, NVIC);