Skip to content

isobchuk/cpp_register

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cpp_register - safe, no-cost and easy-to-use Cpp header to work with HW registers

Metaprogrammed header to work safely with MCU registers without any influence to run-time (for Cortex-M3/M4 even more effective than plain C) that as written with C++20 using templates, constexpr objects, static_assert, overloading and a bit and concepts.

Basic compile-time checks:

  • Does the register contain this field?
  • Access mode for both registers and fields (more than ten for now)
  • Size of value to write to the register
  • et cetera

Advantages

  1. Extended compile-time checks for the registers and fields ownership, size, access, overflowing, et cetera
  2. As effective (size and speed) as the standard C CMSIS (in the some particular cases even more effective).
  3. Writing several fields in the one register.
  4. Cast from constexpr values to field values (without loosing security) for the integration to the driver level abstractions.
  5. Automated bit-band to increase speed and reduce size.
  6. Mimic to traditional CMSIS patterns (easy-to-use) with some improvements.
  7. Short and easy-to-read form for the multi-field writing.
  8. The most of errors can be seen during code writing (IntelliSense)
  9. Work in the strict environment (-Werror, -Weverything, et cetera)
  10. Cross-platform and cross-environment.
  11. Support dynamic writing for the operations that are really needed it.

Description

Abstract

CMSIS files from the chip-makers is the common way to work with MCUs on the low-level. However, these files have one big problem - it is unsafety.

Short example below show this:

//Example for stm32f407 gpio

//ERROR: Wanted to give clock for GPIOD - mismatch with the register
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
....
//ERROR: Try to write to the read-only register
GPIOD->IDR |= GPIO_IDR_IDR_13
//ERROR: Try to read from write-only register
uint32_t val = GPIO->BSRR & GPIO_BSRR_BS_10

Basically, such type of errors do not happen often but if it was occurred - it is nightmare to find the root case.

The library propose the no-cost compile-time checks for this.

Worth noting that there are some attempts to resolve this problem also with modern C++. However, resolving the safety problem, solutions produced new problems such as hard-to-read, hard-to-use, inflexibility, loosing some basic opportunities against CMSIS, et cetera.

Summarizing, cpp_register library gets advantages from both traditional and modern approaches without negative effects. Apart from that, author allowed himself to add some general improvements.

Features

The only one requirement - at least C++17 standard (fully supported by the most of compilers).

The library basically contain the only one file - registers.hpp. It is contains three interface classes:

  1. RegVal - helper class to transform register value to constexpr value as a type property. This class allows to resolve the problem (constexpr function parameters are not constexpr). Supported a bit '|' (or) operation. Note: register value is a value that is one of: unsigned arithmetic, pointer (to static) or enum (class) with base unsigned arithmetic.
  2. Field - type for registers' fields.
  3. Register - type for registers.

Access mode for the fields and registers - defined in the AccessMode enumeration.

Some supported operations (stm32f407 as example, also can be found in unittest):

  • Set bit(s) '|=' in the register:
RCC->AHB1ENR |= RCC_AHB1ENR::GPIODEN;
  • Reset bit(s) '&=' in the register:
RCC->AHB1ENR &= RCC_AHB1ENR::GPIODEN;

Note: The traditional '&=~' was replaced by '&=' on purpose but it is possible to use '&=~' instead. (Basically, '~' has no effect for fields).

  • Assign bit(s) '=' in the register:
RCC->AHB1ENR = RCC_AHB1ENR::GPIODEN;
  • Toggle bit(s) '^=' in the register:
RCC->AHB1ENR ^= RCC_AHB1ENR::GPIODEN;
  • Read bit(s) binary '&' of the register:
// For one bit returns boolean
if(RCC->AHB1ENR & RCC_AHB1ENR::GPIODEN)

// Several bits value can be requested and reading operation returns Register Size type
if(RCC->CFGR & RCC_CFGR::SWS(reg_v<MASK>))

// Also operators of comparison ('==' and '!=') are overloaded to check several bits value
enum class : unsigned {
    HSI = 0b01,
    HSE = 0b10,
    MASK = 0b11
}

// Check if HSE is written in the register (read by mask -> compare with HSE value)
while(RCC_CFGR::SWS(reg_v<HSE>) != (RCC->CFGR & RCC_CFGR::SWS(reg_v<MASK>)))
  • Read register value '*':
const auto value = *(RCC->AHB1ENR);
  • Assign dynamic value (register value or pointer):
uint32_t magic = 0x11223344UL;
RCC->AHB1ENR = magic;
RCC->AHB1ENR = &magic;
  • Get address unary '&' of the register:
uint32_t address = &(RCC->AHB1ENR);
  • Operate value (from the driver or app level, for example):
static constexpr auto SYSTEM_MHZ = 16UL;
static constexpr auto SYST_PERIOD = reg_v<(SYSTEM_MHZ * 1000000UL) - 1>;
SYST->RVR = SYST_RVR::RELOAD(SYST_PERIOD);
  • Operate several fields ad once:
SYST->CSR |= (SYST_CSR::CLKSOURCE | SYST_CSR::ENABLE);
  • Operations with several same fields. It is two forms for the user choice.
//Full form
//Set GPIOD pin 12, 13, 14, 15 as output
GPIOD->MODER |= (GPIO_MODER::MODER[NUM_12](NUM_0) | GPIO_MODER::MODER[NUM_13](NUM_0) 
                | GPIO_MODER::MODER[NUM_14](NUM_0) | GPIO_MODER::MODER[NUM_15](NUM_0));
//Short form
//Set GPIOD pin 12, 13, 14, 15 as output
GPIOD->MODER |= GPIO_MODER::MODER[NUM_12 | NUM_13 | NUM_14 | NUM_15](NUM_0);
  • The same registers in the array:
GPIOA->AFR[NUM] |= GPIO_AFR::AFR[NUM_2](NUM_1 | NUM_0);
  • Create RegVal variable:
static constexpr auto SYST_CLOCK = reg_v<168000000UL>;

Note: As RegVal supports the enum it is not necessary to cast from enum class to integral. Example:

enum class Mode : uint32_t { Input, Output };
...

static constexpr auto MODE = cpp_register::reg_v<mode>; // no cast needed
  • Static pointer as a field value (with compile time check for nullptr)
static uint8_t buffer[8];
REG_TEST->TEST1 = TEST1_POINTER::VAL1(reg_v<buffer>);

Automated bit-band:

Useful feature to slightly improve performance for Cortex-M3/M4. To enable this it is needed to define CORTEX_M_BIT_BAND for the project (-DCORTEX_M_BIT_BAND). In this case, for the set and reset operation the bit-band will be applied automatically if the expression is appropriate for the condition:

  • The register address in the bit-band region.
  • Only one bit of the register is needed to be set or reset.

Note: In the future the possibility to use user-defined bit-band will be added.

How to use?

The reference to use this library was located to the example folder. It is minimal comprehensive project with LED blinking for stm32f407. It was written with VSCode + gcc + Make. Also it is builded with '-Oz' optimization and strict environment '-Werror -Wall -Wextra, et cetera'.

First of all, you have to create the register description header files (as at CMSIS) where you should describe the registers and fields you needed (include register.hpp there). The parameters you should pass to Field and Registers are clear and can be found in the Reference manual for the specific chip.

Apart from that, the python script can be used to transform the *.svd file from the chip maker to register description. (For example, you can see svd2rust.py for the Rust language).

In tools folder you can find example of the script file with example of svd file for stm32f407.

To use this script you should pass *.svd file and peripheral modules you want to generate. (I case only file was passed - all file's modules will be showed you)

In the example/stm32f407 folder you can see the result for:

py .\svd2cpp.py STM32F407.svd GPIO STK RCC

It is supposed to use the script to receive only the draft of the description because *.svd files contain a ton inaccuracies and a bit of errors. Apart from that, I am too lazy to write really comprehensive script for all cases.

After the register description file finished, it can be included to the project source files and used to write drivers.

About

safe, no-cost and easy-to-use Cpp header to work safely with HW registers

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published