Merge branch 'cleanup'

This commit is contained in:
Nicolas Clauvelin 2019-04-24 17:03:09 -04:00
commit 6433346d01
30 changed files with 1888 additions and 1904 deletions

View File

@ -0,0 +1,41 @@
### Summary
*(Summarize the bug or unexpected behavior encountered concisely)*
### Version and platform information
*(Give the commit/branch or version information of the project for which the bug was observed)*
*(If the bug was observed on a specific platform give the details here)*
### Customer information
*(If the bug was reported by a customer give the relevant details here)*
### Steps to reproduce
*(How can the bug be reproduced - be as accurate as possible)*
### What is the current *bug* behavior?
*(What actually happens: e.g. crash, wrong results, ...)*
### What is the expected *correct* behavior?
*(What you should be happening instead)*
### Relevant logs, screenshots, or inputs
*(Paste using code block (```) or attach any piece of information)*
*(If specific inputs are needed to observe the bug paste them or attach them)*
### Possible causes or fixes
*(If you have identified the cause of the bug or have a potential fix please link the line(s) of code and comment appropriately)*
*Final check: set origin label, set priority/severity if applicable*
*Clean up: remove any comment from the template if not applicable*
/label ~"kind:bug"

View File

@ -0,0 +1,27 @@
### Questionnaire
* [ ] Issue related to GitLab project configuration?
* [ ] Issue related to project build process (e.g. toolchain)?
* [ ] Issue related to Git configuration (e.g. gitignore file)?
### Version information
*(Give the commit/branch or version information of the project for which this configuration issue needs to be considered)*
### Policy or guidelines reference
*(Refer here to the policy or guideline that relates to this issue and indicate if a violation or deviation should be addressed)*
### Details
*(Explain here or attach what should be done to fix this issue)*
### Attachments
*(Paste using code block (```) or attach any piece of information illustrating the issue)*
*Final check: set origin label, set priority/severity if applicable*
*Clean up: remove any comment from the template if not applicable*
/label ~"kind:configuration"

View File

@ -0,0 +1,19 @@
### Questionnaire
* [ ] New or additional documentation needed?
* [ ] Current documentation is out of date or erroneous?
* [ ] Documentation needs review?
### Version information
*(Give the commit/branch or version information of the project for which this documentation issue needs to be considered)*
### Details
*(Explain here or attach what should be done to fix this issue)*
*Final check: set origin label, set priority/severity if applicable*
*Clean up: remove any comment from the template if not applicable*
/label ~"kind:documentation"

View File

@ -0,0 +1,21 @@
### Problem to solve
*(What is the new feature trying to solve or provide?)*
### Customer information
*(If the feature was suggested by a customer give the relevant details here)*
### Proposal
*(Explain what this feature will bring in terms of benefits and how it fits in the project scope)*
### Links and references
*(Add links or references if applicable)*
*Final check: set origin label, set priority/severity if applicable*
*Clean up: remove any comment from the template if not applicable*
/label ~"kind:feature request"

View File

@ -0,0 +1,21 @@
### Functionality to improve and goal
*(Explain what exactly needs to be improved and what is the goal)*
### Customer information
*(If the improvement was suggested by a customer give the relevant details here)*
### Proposal
*(Explain how the functionality could be improved and the various things to consider to achieve this goal)*
### Links and references
*(Add links or references if applicable)*
*Final check: set origin label, set priority/severity if applicable*
*Clean up: remove any comment from the template if not applicable*
/label ~"kind:improvement"

View File

@ -0,0 +1,17 @@
### Question
*(Write the question as explicit as you can)*
### Customer information
*(If the question was submitted by a customer give the relevant details here)*
### Additional information
*(Add links, references, or any other piece of information)*
*Final check: set origin label, set priority/severity if applicable*
*Clean up: remove any comment from the template if not applicable*
/label ~"kind:question"

View File

@ -0,0 +1,21 @@
### Requirement specifications
*(Give the details about the requirement that needs to be implemented)*
### Project information
*(If this issue relates to a requirement defined in a project specification existing somewhere give all references and information here)*
### Customer information
*(If the requirement was defined by a customer give the relevant details here)*
### Testing
*(Explain how the requirement implementation should be tested or how it fit in the current testing plan of the project)*
*Final check: set origin label, set priority/severity if applicable*
*Clean up: remove any comment from the template if not applicable*
/label ~"kind:requirement"

View File

@ -0,0 +1,23 @@
### Summary of the MR
*(Explain what modifications are part of this MR; refer to issues or milestones if needed)*
### List of issues addressed by this MR
*(Edit the list of issues)*
* Closes #xxx
* Closes #xxx
* ...
### Additional information
* [ ] Does the MR update any submodule?
* [ ] Does the MR break any functionality or alter performance ?
* [ ] Does the MR require issues to be created to deal with side effects?
*(Give more details if needed)*
*Clean up: remove any comment from the template if not applicable*
/label ~"merge:review"

35
API.md
View File

@ -1,5 +1,5 @@
# cppreg: API #
Copyright Sendyne Corp., 2010-2018. All rights reserved ([LICENSE](LICENSE)).
Copyright Sendyne Corp., 2010-2019. All rights reserved ([LICENSE](LICENSE)).
## Introduction ##
@ -23,9 +23,9 @@ As explained below, when using `cppreg`, registers and fields are defined as C++
## Data types ##
`cppreg` introduces type aliases in order to parameterize the set of data types used in the implementation. By default the following types are defined (see [cppreg_Defines.h](cppreg_Defines.h) for more details):
* `Address_t` is the data type used to hold addresses of registers and fields; it is equivalent to `std::uintptr_t`,
* `Address` is the data type used to hold addresses of registers and fields; it is equivalent to `std::uintptr_t`,
* register sizes are represented by the enumeration type `RegBitSize`,
* `FieldWidth_t` and `FieldOffset_t` are the data types to represent field sizes and offsets; both are equivalent to `std::uint8_t`.
* `FieldWidth` and `FieldOffset` are the data types to represent field sizes and offsets; both are equivalent to `std::uint8_t`.
### Register size ###
The `RegBitSize` enumeration type represents the register sizes supported in `cppreg` and the values are:
@ -46,7 +46,7 @@ In `cppreg`, registers are represented as memory locations that contain fields,
Most of the times registers are part of groups related to peripherals or specific functionalities within a MCU. It is therefore recommended to use the register pack implementation rather than the standalone one. This ensures that the assembly generated from `cppreg`-based code will be optimal. In other words, the difference between packed registers and standalone registers is only a matter of performance in the generated assembly: the packed register interface relies on mapping an array on the pack memory region, which provides to the compiler the ability to use offset between the various registers versus reloading their absolute addresses.
Moreover, the same level of functionality is provided by both implementations (`RegisterPack` is simply deriving from `Register` and redefining accessor and modifier methods). That is, a packed register type can be replaced by a standalone register type (and *vice versa*).
Moreover, the same level of functionality is provided by both implementations (`RegisterPack` is simply deriving from `Register`). That is, a packed register type can be replaced by a standalone register type (and *vice versa*).
### Register pack interface ###
To define a pack of registers:
@ -73,7 +73,7 @@ The interface is (see [RegisterPack.h](register/RegisterPack.h)):
| `reset_value` | register reset value (defaulted to zero) |
| `use_shadow_value` | enable shadow value if `true` (see below) |
Note that, the reset value is only useful when a shadow value is used.
Note that, the reset value is only used when shadow value support is enabled.
The following example defines a 4 bytes register pack starting at address 0xA4000000 and containing: two 8-bit register and a 16-bit register. The `cppreg` implementation is:
@ -106,7 +106,7 @@ struct SomePeripheral {
}
```
There are a few requirements for when defining packed registers:
There are a few requirements when defining packed registers:
* for a register of size N bits, the pack base address has to be aligned on a N bits boundary,
* for a register of size N bits, the pack base address plus the offset has to be aligned on a N bits boundary,
@ -126,7 +126,7 @@ The interface for standalone register is (see [Register.h](register/Register.h))
| `reset_value` | register reset value (defaulted to zero) |
| `use_shadow_value` | enable shadow value if `true` (see below) |
Note that, the reset value is only useful when a shadow value is used.
Note that, the reset value is only used when shadow value support is enabled.
For example, consider a 32-bit register `SomeRegister` mapped at `0x40004242`. The `Register` type is created using:
@ -226,7 +226,7 @@ static std::array<std::uint8_t, Channels::n_elems> some_buffer = {};
// Iterate over the pack and use a lambda.
// Note the "auto index" ... this is required because the loop will
// use std::integral_constant to pass the index while iterating.
pack_loop<Channels>::apply<ChannelsCollector>([](auto index) {
pack_loop<Channels>::apply([](auto index) {
some_buffer[index] = Channels::elem<index>::read();
Channels::elem<index>::template write<index>();
});
@ -305,12 +305,12 @@ SomeField::write<0xAB>(); // Template version for constant value write.
SomeField::write(0xAB); // Function argument version.
```
The advantages of using the constant value version are:
The advantages of using the constant value form are:
* `cppreg` will most of the time use a faster implementation for the write operation (this is particularly true if the field spans an entire register),
* a compile-time error will occur if the value overflow the field.
Note that, even when using the non-constant value version overflow will not occur: only the bits part of the `Field`-type will be written and any data that does not fit the region of the memory assigned to the `Field`-type will not be modified. For example:
Note that, even when using the non-constant value form overflow will not occur: only the bits fitting in the `Field`-type will be written and any data that does not fit the region of the memory assigned to the `Field`-type will not be modified. For example:
```c++
// Register definition with nested fields definitions.
@ -332,7 +332,7 @@ SomeRegister::Frequency::write<0x111>();
## Shadow value: a workaround for write-only fields ##
Write-only fields are somewhat special as extra-care has to be taken when manipulating them. The main difficulty resides in the fact that write-only field can be read but the value obtained by reading it is fixed (*e.g.*, it always reads as zero). `cppreg` assumes that write-only fields can actually be read from; if such an access on some given architecture would trigger an error (*à la FPGA*) then `cppreg` is not a good choice to deal with write-only fields on this particular architecture.
Write-only fields are somewhat special and extra-care has to be taken when manipulating them. The main difficulty resides in the fact that write-only field can be read but the value obtained by reading it is fixed (*e.g.* it always reads as zero). `cppreg` assumes that write-only fields can actually be read from; if such an access on some architecture would trigger an error (*à la FPGA*) then `cppreg` is not a good choice to deal with write-only fields on this particular architecture.
Consider the following situation:
@ -364,7 +364,7 @@ As a workaround, `cppreg` offers a shadow value implementation which mitigates t
struct Reg : Register<
0x40004242, // Register address
RegBitSize::b32, // Register size
0x42u // Register reset value
0x42u, // Register reset value
true // Enable shadow value for the register
>
{
@ -373,7 +373,7 @@ struct Reg : Register<
};
```
The shadow value implementation for a write-only field works as follow:
The shadow value implementation for a write-only field works as follows:
* at static initialization time, the reset value of the register owning the field is used to initialize the shadow value (the shadow value is used for the entire register content),
* at each write access to any of the register fields, the shadow value will be updated and written to the entire register memory.
@ -388,9 +388,9 @@ A few safety guidelines:
## MergeWrite: writing to multiple fields at once ##
It is sometimes the case that multiple fields within a register needs to be written at the same time. For example, when setting the clock dividers in a MCU it is often recommended to write all their values to the corresponding register at the same time (to avoid overclocking part of the MCU).
It is sometimes the case that multiple fields within a register needs to be written at the same time. For example, when setting the clock dividers in a MCU it is often recommended to write all their values to the corresponding register at the same time (to avoid mis-clocking part of the MCU).
Consider the following setup (not so artifical; it is inspired by a real flash memory controller peripheral):
Consider the following setup (not so artificial; it is inspired by a real flash memory controller peripheral):
```c++
struct FlashCtrl : Register<0xF0008282, RegBitSize::b8> {
@ -414,14 +414,14 @@ struct FlashCtrl : Register<0xF0008282, RegBitSize::b8> {
Now let's assume the following scenario:
1. The previous flash command failed because it attempted to write or erase in a protected section, at that point the content of the `FlashCtrl` register is `1001 XXXX` where `XXXX` is whatver value associated with the command that failed.
1. The previous flash command failed because it attempted to write or erase in a protected section, at that point the content of the `FlashCtrl` register is `1001 XXXX` where `XXXX` is whatever value associated with the command that failed.
2. Before we can perform a new flash command we need to clear the `ProtectionError` by writing 1 to it (otherwise the new command will not be started); so one could think about doing:
```c++
FlashCtrl::ProtectionError::set(); // Write to ProtectionError to clear it.
```
however this write at `1000 XXXX | 0001 0000 = 1001 XXXX` at the register level and thus start the command that previously failed.
however this write `1000 XXXX | 0001 0000 = 1001 XXXX` at the register level and thus start the command that previously failed.
3. At this point one could try to set the value for the new command but that will fail as well (because `ProtectionError` was not cleared and it is required to be).
4. A possible alternative would be to fully zero out the `FlashCtrl` register but that would somewhat defeat the purpose of `cppreg`.
@ -442,4 +442,3 @@ The `merge_write` method is only available in register type (`PackedRegister` or
**Warning:** if`done()` is not called at the end of the successive write operations no write at all will be performed.
Similarly to regular write operations it is recommended to use the template version (as shown in the example) if possible: this will enable overflow checking and possibly use faster write implementations. If not possible the values to be written are passed as arguments to the various calls.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -7,48 +7,50 @@
# -------------------------------------------------------------------------- #
# --- cppreg library files ---
# --- cppreg library ---
# Header directories.
set(CPPREG_HEADERS_DIRS
set(cppreg_headers_dirs
.
policies/
register/)
# List of API headers.
set(CPPREG_API_HEADERS
set(cppreg_headers
cppreg.h
cppreg_Defines.h
cppreg_Includes.h
policies/AccessPolicy.h
register/Field.h
register/Internals.h
register/Mask.h
register/Overflow.h
register/Memory.h
register/MergeWrite.h
register/Register.h
register/RegisterPack.h
register/ShadowValue.h
register/Traits.h)
# Refactor headers directories.
set(BUILD_HEADERS_DIRS "")
foreach(dir IN ITEMS ${CPPREG_HEADERS_DIRS})
list(APPEND BUILD_HEADERS_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/${dir}")
set(build_cppreg_headers_dirs "")
foreach(dir IN ITEMS ${cppreg_headers_dirs})
list(APPEND build_cppreg_headers_dirs "${CMAKE_CURRENT_SOURCE_DIR}/${dir}")
endforeach()
# Header-only library.
add_library(cppreg INTERFACE)
target_include_directories(cppreg INTERFACE . register/ policies/)
target_include_directories(cppreg INTERFACE ${cppreg_headers_dirs})
# Include directories interfaces.
# This properly setup the API headers for the build and install phase.
set_property(TARGET cppreg
PROPERTY INTERFACE_INCLUDE_DIRECTORIES
$<BUILD_INTERFACE:${BUILD_HEADERS_DIRS}>
$<BUILD_INTERFACE:${build_cppreg_headers_dirs}>
$<INSTALL_INTERFACE:include/cppreg>)
# --- Install directives ---
install(FILES ${CPPREG_API_HEADERS} DESTINATION include/cppreg)
install(TARGETS cppreg DESTINATION lib EXPORT cppreg-libs)
install(EXPORT cppreg-libs DESTINATION include/cppreg)
install(FILES ${cppreg_headers} DESTINATION include/cppreg)
install(TARGETS cppreg DESTINATION lib EXPORT cppreg-target)
install(EXPORT cppreg-target DESTINATION include/cppreg)

View File

@ -1,46 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at nclauvelin+github@sendyne.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -5,7 +5,7 @@
/____/\___/_/ /_/\__,_/\__, /_/ /_/\___/ \___/\____/_/ / .___(_).
/____/ /_/
Copyright 2010-2018 Sendyne Corporation.
Copyright 2010-2019 Sendyne Corporation.
All rights reserved.
Sendyne Corp., 250 West Broadway, New York, NY 10013, USA.

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
# cppreg: quick start #
Copyright Sendyne Corp., 2010-2018. All rights reserved ([LICENSE](LICENSE)).
Copyright Sendyne Corp., 2010-2019. All rights reserved ([LICENSE](LICENSE)).
This document is a brief introduction to `cppreg` and how to use it. For more details about the implementation refers to the [API documentation](API.md).
@ -14,7 +14,8 @@ There are two recommended ways to use `cppreg` in a project:
# Include cppreg library.
# The second argument is not necessarily required depending on the project setup.
# This will import the cppreg target.
# Because cppreg is a header-only library the cppreg target is actually an INTERFACE library target.
# Because cppreg is a header-only library the cppreg target is actually an
# INTERFACE library target.
add_subdirectory(/path/to/cppreg ${PROJECT_BINARY_DIR)/cppreg)
...
@ -59,7 +60,7 @@ The RX and TX data registers both contain a single DATA field occupying the whol
The goal of `cppreg` is to facilitate the manipulation of such a peripheral. This can be done as follow:
```c++
#include <cppreg.h> // use cppreg-all.h instead if you are using the single header.
#include <cppreg.h> // use cppreg-all.h if you are using the single header.
using namespace cppreg;
// Peripheral structure.
@ -150,7 +151,7 @@ while (true) {
A few remarks:
* the `write` calls for the `Setup` register pass the data as template arguments, while the write call for the `TX` register pass it as a function argument: if the value to be written is known at compile time it is recommended to use the template form; the template form will detect overflow (see below) and will also make it possible to use a faster write implementation in some cases,
* the `write` calls for the `Setup` register pass the data as template arguments, while the write call for the `TX` register pass it as a function argument: if the value to be written is known at compile time it is recommended to use the template form; the template form will detect overflow (see below) and will also make it possible to use a more efficient write implementation in some cases,
* `Field`-based types have `is_set` and `is_clear` defined to conveniently query their states.
In this example, we can already see how `cppreg` limits the possibility of errors (see the [API documentation](API.md) for more details):

View File

@ -1,24 +1,24 @@
# cppreg #
Copyright Sendyne Corp., 2010-2018. All rights reserved ([LICENSE](LICENSE)).
Copyright Sendyne Corp., 2010-2019. All rights reserved ([LICENSE](LICENSE)).
## Description ##
`cppreg`is a header-only C++11 library to facilitate the manipulation of MMIO registers (*i.e.*, memory-mapped I/O registers) in embedded devices. The idea is to make it possible to write expressive code and minimize the likelihood of ill-defined expressions when dealing with hardware registers on a MCU. The current features are:
`cppreg` is a header-only C++11 library to facilitate the manipulation of MMIO registers (*i.e.*, memory-mapped I/O registers) in embedded devices. The idea is to provide a way to write expressive code and minimize the likelihood of ill-defined expressions when dealing with hardware registers on a MCU. The current features are:
* expressive syntax which shows the intent of the code when dealing with registers and fields,
* efficiency and performance on par with traditional C implementations (*e.g.*, CMSIS C code) when *at least some compiler optimizations* are enabled,
* [huge emphasis](Performance.md) on ensuring the assembly is the same if not better than CMSIS versions,
* field access policies (*e.g.*, read-only vs read-write) detect ill-defined access at compile-time,
* efficiency and performance on par with traditional C implementations (*e.g.* CMSIS C code) when *at least some compiler optimizations* are enabled,
* [emphasis](Performance.md) on ensuring the assembly is the same if not better than CMSIS versions,
* field access policies (*e.g.* read-only vs read-write) detect ill-defined access at compile-time,
* compile-time detection of overflow,
* easily extendable to support, for example, mock-up.
* register memory can easily be mocked up so that testing is possible.
For a short introduction and how-to see the [quick start guide](QuickStart.md). A more complete and detailed documentation is available [here](API.md).
The features provided by `cppreg` come with no overhead or performance penalty compared to traditional low-level C approaches. We give [here](Performance.md) an example comparing the assembly generated by a CMSIS-like implementation versus a `cppreg` one.
The features provided by `cppreg` come with no overhead or performance penalty compared to traditional low-level C approaches. We give [here](Performance.md) an example comparing the assembly generated by a CMSIS-like implementation versus a `cppreg`-based one.
## Requirements ##
`cppreg` is designed to be usable on virtually any hardware that statisfies the following requirements:
`cppreg` is designed to be usable on virtually any hardware that satisfies the following requirements:
* MMIO register sizes are integral numbers of bytes (*e.g.*, 8 bits, 16 bits, ...),
* registers are properly aligned: a N-bit register is aligned on a N-bit boundary,
@ -41,11 +41,11 @@ while ((MCG->S & MCG_S_PLLST_MASK) == 0)
This piece of code is part of the clock setup on a flavor of the K64F MCU. `MCG` is a peripheral and `MCG->C6` and `MCG->S` are registers containing some fields which are required to be set to specific values to properly clock the MCU. Some of the issues with such code are:
* the intent of the code is poorly expressed, and it requires at least the MCU data sheet or reference manual to be somewhat deciphered/understood,
* since the offsets and masks are known at compile time, it is error prone and somewhat tedious that the code has to manually implement shifting and masking operations,
* since the offsets and masks are known at compile time, it is error prone and somewhat tedious that the code has to re-implement shifting and masking operations,
* the code syntax itself is extremely error prone; for example, forget the `|` in `|=` and you are most likely going to spend some time debugging the code,
* there is no safety at all, that is, you might overflow the field, or you might try to write to a read-only field and no one will tell you (not the compiler, not the linker, and at runtime this could fail in various ways with little, if any, indication of where is the error coming from).
This does not have to be this way, and C++11 brings a lot of features and concepts that make it possible to achieve the same goal while clearly expressing the intent and being aware of any ill-formed instructions. Some will argue this will come at the cost of a massive performance hit, but this is actually not always the case (and more often than not, a C++ implementation can be very efficient; see [Ken Smith paper] and the example below).
This does not have to be this way, and C++11 brings a lot of features and concepts that make it possible to achieve the same goal while clearly expressing the intent and being aware of any ill-formed instructions. Some will argue this will come at the cost of a massive performance hit, but this is actually not always the case (and more often than not, a C++ implementation can be very efficient; see [Ken Smith] paper and the example below).
This project has been inspired by the following previous works:

View File

@ -2,7 +2,7 @@
/**
* @file cppreg.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*/
@ -10,11 +10,11 @@
#define CPPREG_CPPREG_H
#include "Field.h"
#include "Internals.h"
#include "MergeWrite.h"
#include "Register.h"
#include "RegisterPack.h"
#include "Field.h"
#endif // CPPREG_CPPREG_H

View File

@ -2,7 +2,7 @@
/**
* @file cppreg_Defines.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*
* This header mostly defines data types used across the project. These data
* types have been defined with 32-bits address space hardware in mind. It
@ -17,7 +17,6 @@
#include "cppreg_Includes.h"
//! cppreg namespace.
namespace cppreg {
@ -25,14 +24,14 @@ namespace cppreg {
/**
* By design this type ensures that any address can be stored.
*/
using Address_t = std::uintptr_t;
using Address = std::uintptr_t;
//! Enumeration type for register size in bits.
/**
* This is used to enforce the supported register sizes.
*/
enum class RegBitSize {
enum class RegBitSize : std::uint8_t {
b8, //!< 8-bit register.
b16, //!< 16-bit register.
b32, //!< 32-bit register.
@ -41,11 +40,11 @@ namespace cppreg {
//! Type alias field width.
using FieldWidth_t = std::uint8_t;
using FieldWidth = std::uint8_t;
//! Type alias for field offset.
using FieldOffset_t = std::uint8_t;
using FieldOffset = std::uint8_t;
//! Shorthand for max value as a mask.
@ -60,7 +59,7 @@ namespace cppreg {
};
}
} // namespace cppreg
#endif // CPPREG_CPPREG_DEFINES_H

View File

@ -2,7 +2,7 @@
/**
* @file cppreg_Defines.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*/

View File

@ -2,7 +2,7 @@
/**
* @file AccessPolicy.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*
* Access policies are used to describe if register fields are read-write,
* read-only or write-only.
@ -14,7 +14,7 @@
* the register proper masking and shifting is required. The switch between
* trivial and non-trivial implementations is done automatically.
* - The write implementation also distinguishes between writing constant
* values (i.e., known at compile time) and non-constant value. This was
* values (i.e., known at compile time) and non-constant value. This is
* intended to make it possible to simplify operations at compile time and
* minimize overhead.
*/
@ -27,43 +27,68 @@
#include "cppreg_Defines.h"
//! cppreg namespace.
namespace cppreg {
//!@{ Trivial register read/write detector.
//! Boolean flag to detect trivial read/write.
/**
* @tparam T Register data type.
* @tparam mask Mask for the read operation.
* @tparam offset Offset for the read operation.
*/
template <typename T, T mask, FieldOffset offset>
struct is_trivial_rw
: std::integral_constant<bool,
(mask == type_mask<T>::value)
&& (offset == FieldOffset{0})> {};
//! Trivial read/write enable_if helper.
/**
* @tparam T Register data type.
* @tparam mask Mask for the read operation.
* @tparam offset Offset for the read operation.
* @tparam U Enabled type if trivial.
*/
template <typename T, T mask, FieldOffset offset, typename U>
using is_trivial =
typename std::enable_if<is_trivial_rw<T, mask, offset>::value, U>::type;
//! Non-trivial read/write enable_if helper.
/**
* @tparam T Register data type.
* @tparam mask Mask for the read operation.
* @tparam offset Offset for the read operation.
* @tparam U Enabled type if non-trivial.
*/
template <typename T, T mask, FieldOffset offset, typename U>
using is_not_trivial =
typename std::enable_if<!is_trivial_rw<T, mask, offset>::value, U>::type;
//!@}
//! Register read implementation.
/**
* @tparam MMIO_t Memory device type.
* @tparam MMIO Memory device type.
* @tparam T Register data type.
* @tparam mask Mask for the read operation.
* @tparam offset Offset for the read operation.
*
* The mask and offset are used to define a specific field within the
* register.
*/
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
template <typename MMIO, typename T, T mask, FieldOffset offset>
struct RegisterRead {
//! Boolean flag for trivial implementation.
/**
* The trivial corresponds to reading the whole register, that is,
* the mask is identity and the offset is zero.
*/
constexpr static const bool is_trivial =
(mask == type_mask<T>::value) && (offset == 0u);
//! Non-trivial read implementation.
/**
* @param mmio_device Pointer to the register memory device.
* @return The content of the register field.
*/
template <typename U = void>
static T read(
const MMIO_t& mmio_device,
typename std::enable_if<!is_trivial, U*>::type = nullptr
) noexcept {
static T read(const MMIO& mmio_device,
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
return static_cast<T>((mmio_device & mask) >> offset);
};
}
//! Trivial read implementation.
/**
@ -71,55 +96,35 @@ namespace cppreg {
* @return The content of the register field.
*/
template <typename U = void>
static T read(
const MMIO_t& mmio_device,
typename std::enable_if<is_trivial, U*>::type = nullptr
) noexcept {
static T read(const MMIO& mmio_device,
is_trivial<T, mask, offset, U>* = nullptr) noexcept {
return static_cast<T>(mmio_device);
};
}
};
//! Register write implementation.
/**
* @tparam MMIO_t Memory device type.
* @tparam MMIO Memory device type.
* @tparam T Register data type.
* @tparam mask Mask for the read operation.
* @tparam offset Offset for the read operation.
*
* The mask and offset are used to define a specific field within the
* register.
*
* This write implementation is used only when the value to be written is
* not a constant expression.
*/
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
template <typename MMIO, typename T, T mask, FieldOffset offset>
struct RegisterWrite {
//! Boolean flag for trivial implementation.
/**
* The trivial corresponds to writing to the whole register, that is,
* the mask is identity and the offset is zero.
*/
constexpr static const bool is_trivial =
(mask == type_mask<T>::value) && (offset == 0u);
//! Non-trivial write implementation.
/**
* @param mmio_device Pointer to the register memory device.
* @param value Value to be written to the register field.
*/
template <typename U = void>
static void write(
MMIO_t& mmio_device,
static void write(MMIO& mmio_device,
T value,
typename std::enable_if<!is_trivial, U*>::type = nullptr
) noexcept {
mmio_device = static_cast<T>(
(mmio_device & ~mask) | ((value << offset) & mask)
);
};
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device =
static_cast<T>((mmio_device & ~mask) | ((value << offset) & mask));
}
//! Trivial write implementation.
/**
@ -127,70 +132,45 @@ namespace cppreg {
* @param value Value to be written to the register field.
*/
template <typename U = void>
static void write(
MMIO_t& mmio_device,
static void write(MMIO& mmio_device,
T value,
typename std::enable_if<is_trivial, U*>::type = nullptr
) noexcept {
is_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = value;
};
}
};
//! Register write constant implementation.
/**
* @tparam MMIO_t Memory device type.
* @tparam MMIO Memory device type.
* @tparam T Register data type.
* @tparam mask Mask for the read operation.
* @tparam offset Offset for the read operation.
* @tparam value Value to be written to the register field.
*
* The mask and offset are used to define a specific field within the
* register.
*
* This write implementation is used only when the value to be written is
* a constant expression.
*/
template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
template <typename MMIO, typename T, T mask, FieldOffset offset, T value>
struct RegisterWriteConstant {
//! Boolean flag for trivial implementation.
/**
* The trivial corresponds to writing to the whole register, that is,
* the mask is identity and the offset is zero.
*/
constexpr static const bool is_trivial =
(mask == type_mask<T>::value) && (offset == 0u);
//! Non-trivial write implementation.
/**
* @param mmio_device Pointer to the register memory device.
*/
template <typename U = void>
static void write(
MMIO_t& mmio_device,
typename std::enable_if<!is_trivial, U*>::type = nullptr
) noexcept {
mmio_device = static_cast<T>(
(mmio_device & ~mask) | ((value << offset) & mask)
);
};
static void write(MMIO& mmio_device,
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device =
static_cast<T>((mmio_device & ~mask) | ((value << offset) & mask));
}
//! Trivial write implementation.
/**
* @param mmio_device Pointer to the register memory device.
*/
template <typename U = void>
static void write(
MMIO_t& mmio_device,
typename std::enable_if<is_trivial, U*>::type = nullptr
) noexcept {
static void write(MMIO& mmio_device,
is_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = value;
};
}
};
@ -199,18 +179,17 @@ namespace cppreg {
//! Read access implementation.
/**
* @tparam MMIO_t Register memory device type.
* @tparam MMIO Register memory device type.
* @tparam T Field data type.
* @tparam mask Field mask.
* @tparam offset Field offset.
* @param mmio_device Pointer to register mapped memory.
* @return The value at the field location.
*/
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
static T read(const MMIO_t& mmio_device) noexcept {
return RegisterRead<MMIO_t, T, mask, offset>::read(mmio_device);
};
template <typename MMIO, typename T, T mask, FieldOffset offset>
static T read(const MMIO& mmio_device) noexcept {
return RegisterRead<MMIO, T, mask, offset>::read(mmio_device);
}
};
@ -219,35 +198,31 @@ namespace cppreg {
//! Write access implementation.
/**
* @tparam MMIO_t Register memory device type.
* @tparam MMIO Register memory device type.
* @tparam T Field data type.
* @tparam mask Field mask.
* @tparam offset Field offset.
* @param mmio_device Pointer to register mapped memory.
* @param value Value to be written at the field location.
*/
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
static void write(MMIO_t& mmio_device,
const T value) noexcept {
RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value);
};
template <typename MMIO, typename T, T mask, FieldOffset offset>
static void write(MMIO& mmio_device, const T value) noexcept {
RegisterWrite<MMIO, T, mask, offset>::write(mmio_device, value);
}
//! Write access implementation for constant value.
/**
* @tparam MMIO_t Register memory device type.
* @tparam MMIO Register memory device type.
* @tparam T Field data type.
* @tparam mask Field mask.
* @tparam offset Field offset.
* @tparam value Value to be written at the field location.
* @param mmio_device Pointer to register mapped memory.
*/
template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
static void write(MMIO_t& mmio_device) noexcept {
RegisterWriteConstant<MMIO_t, T, mask, offset, value>
::write(mmio_device);
};
template <typename MMIO, typename T, T mask, FieldOffset offset, T value>
static void write(MMIO& mmio_device) noexcept {
RegisterWriteConstant<MMIO, T, mask, offset, value>::write(mmio_device);
}
//! Set field implementation.
/**
@ -255,40 +230,39 @@ namespace cppreg {
* @tparam mask Field mask.
* @param mmio_device Pointer to register mapped memory.
*/
template <typename MMIO_t, typename T, T mask>
static void set(MMIO_t& mmio_device)
noexcept {
RegisterWriteConstant<MMIO_t, T, mask, 0u, mask>
::write(mmio_device);
};
template <typename MMIO, typename T, T mask>
static void set(MMIO& mmio_device) noexcept {
RegisterWriteConstant<MMIO, T, mask, FieldOffset{0}, mask>::write(
mmio_device);
}
//! Clear field implementation.
/**
* @tparam MMIO_t Register memory device type.
* @tparam MMIO Register memory device type.
* @tparam T Field data type.
* @tparam mask Field mask.
* @param mmio_device Pointer to register mapped memory.
*/
template <typename MMIO_t, typename T, T mask>
static void clear(MMIO_t& mmio_device)
noexcept {
RegisterWriteConstant<MMIO_t, T, mask, 0u, ~mask>
::write(mmio_device);
};
template <typename MMIO, typename T, T mask>
static void clear(MMIO& mmio_device) noexcept {
RegisterWriteConstant<MMIO,
T,
mask,
FieldOffset{0},
static_cast<T>(~mask)>::write(mmio_device);
}
//! Toggle field implementation.
/**
* @tparam MMIO_t Register memory device type.
* @tparam MMIO Register memory device type.
* @tparam T Field data type.
* @tparam mask Field mask.
* @param mmio_device Pointer to register mapped memory.
*/
template <typename MMIO_t, typename T, T mask>
static void toggle(MMIO_t& mmio_device)
noexcept {
template <typename MMIO, typename T, T mask>
static void toggle(MMIO& mmio_device) noexcept {
mmio_device = static_cast<T>((mmio_device) ^ mask);
};
}
};
@ -297,48 +271,44 @@ namespace cppreg {
//! Write access implementation.
/**
* @tparam MMIO_t Register memory device type.
* @tparam MMIO Register memory device type.
* @tparam T Field data type.
* @tparam mask Field mask.
* @tparam offset Field offset
* @param mmio_device Pointer to register mapped memory.
* @param value Value to be written at the field location.
*/
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
static void write(MMIO_t& mmio_device, const T value) noexcept {
template <typename MMIO, typename T, T mask, FieldOffset offset>
static void write(MMIO& mmio_device, const T value) noexcept {
// For write-only fields we can only write to the whole register.
RegisterWrite<MMIO_t, T, type_mask<T>::value, 0u>::write(
mmio_device, ((value << offset) & mask)
);
};
RegisterWrite<MMIO, T, type_mask<T>::value, FieldOffset{0}>::write(
mmio_device, ((value << offset) & mask));
}
//! Write access implementation for constant value.
/**
* @tparam MMIO_t Register memory device type.
* @tparam MMIO Register memory device type.
* @tparam T Field data type.
* @tparam mask Field mask.
* @tparam offset Field offset
* @tparam value Value to be written at the field location.
* @param mmio_device Pointer to register mapped memory.
*/
template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
static void write(MMIO_t& mmio_device) noexcept {
template <typename MMIO, typename T, T mask, FieldOffset offset, T value>
static void write(MMIO& mmio_device) noexcept {
// For write-only fields we can only write to the whole register.
RegisterWriteConstant<
MMIO_t, T, type_mask<T>::value, 0u, ((value << offset) & mask)
>
::write(mmio_device);
};
};
RegisterWriteConstant<MMIO,
T,
type_mask<T>::value,
FieldOffset{0},
((value << offset) & mask)>::write(mmio_device);
}
};
} // namespace cppreg
#endif // CPPREG_ACCESSPOLICY_H

View File

@ -2,11 +2,11 @@
/**
* @file Field.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*
* This header provides the definitions related to register field
* implementation.
* Strictly speaking a field is defined as a region of a register.
* implementation. Strictly speaking a field is defined as a region of a
* register.
*/
@ -14,15 +14,14 @@
#define CPPREG_REGISTERFIELD_H
#include "Register.h"
#include "Mask.h"
#include "AccessPolicy.h"
#include "ShadowValue.h"
#include "Internals.h"
#include "Mask.h"
//! cppreg namespace.
namespace cppreg {
//! Register field implementation.
/**
* @tparam BaseRegister Parent register.
@ -37,12 +36,10 @@ namespace cppreg {
* In practice, fields are implemented by deriving from this class to
* create custom types.
*/
template <
typename BaseRegister,
FieldWidth_t field_width,
FieldOffset_t field_offset,
typename AccessPolicy
>
template <typename BaseRegister,
FieldWidth field_width,
FieldOffset field_offset,
typename AccessPolicy>
struct Field {
//! Parent register for the field.
@ -52,156 +49,130 @@ namespace cppreg {
using type = typename parent_register::type;
//! MMIO type.
using MMIO_t = typename parent_register::MMIO_t;
using MMIO = typename parent_register::MMIO;
//! Field policy.
using policy = AccessPolicy;
//! Field width.
constexpr static const FieldWidth_t width = field_width;
constexpr static auto width = field_width;
//! Field offset.
constexpr static const FieldOffset_t offset = field_offset;
constexpr static auto offset = field_offset;
//! Field mask.
constexpr static const type mask = make_shifted_mask<type>(width,
offset);
constexpr static auto mask = make_shifted_mask<type>(width, offset);
//! Boolean flag indicating if a shadow value is used.
constexpr static const bool has_shadow =
parent_register::shadow::value;
//! Customized overflow check implementation for Field types.
/**
* @tparam value Value to be checked for overflow.
* @return `true` if no overflow, `false` otherwise.
*
* This is only used for the template form of the write method.
*/
template <type value>
struct check_overflow : internals::check_overflow<
type,
value,
(mask >> offset)
> {};
//!@{ Helpers for write method selection based on shadow value.
template <type value, typename T>
using if_no_shadow =
typename std::enable_if<!parent_register::shadow::value, T>::type;
template <type value, typename T>
using if_shadow =
typename std::enable_if<parent_register::shadow::value, T>::type;
//!@}
//!@ Field read method.
/**
* @return Field value.
*/
static type read() noexcept {
return policy::template read<MMIO_t, type, mask, offset>(
parent_register::ro_mem_device()
);
};
return policy::template read<MMIO, type, mask, offset>(
parent_register::ro_mem_device());
}
//! Field write method (no shadow value).
//! Field write value method (no shadow value).
/**
* @param value Value to be written to the field.
*/
template <typename T = type>
static void
write(const typename std::enable_if<!has_shadow, T>::type value)
noexcept {
policy::template write<MMIO_t, type, mask, offset>(
parent_register::rw_mem_device(),
value
);
};
static void write(const if_no_shadow<type{0}, T> value) noexcept {
policy::template write<MMIO, type, mask, offset>(
parent_register::rw_mem_device(), value);
}
//! Field write method (w/ shadow value).
//! Field write value method (w/ shadow value).
/**
* @param value Value to be written to the field.
*/
template <typename T = type>
static void
write(const typename std::enable_if<has_shadow, T>::type value)
noexcept {
static void write(const if_shadow<type{0}, T> value) noexcept {
// Update shadow value.
// This assumes that reading a write-only fields return some value.
RegisterWrite<type, type, mask, offset>
::write(parent_register::shadow::shadow_value, value);
RegisterWrite<type, type, mask, offset>::write(
parent_register::shadow::shadow_value, value);
// Write as a block to the register, that is, we do not use the
// mask and offset.
policy::template write<MMIO_t, type, type_mask<type>::value, 0u>(
// field mask and field offset.
policy::
template write<MMIO, type, type_mask<type>::value, FieldOffset{0}>(
parent_register::rw_mem_device(),
parent_register::shadow::shadow_value
);
parent_register::shadow::shadow_value);
}
};
//! Field write method with overflow check (no shadow value).
//! Field write constant method (no shadow value).
/**
* @tparam value Value to be written to the field
* @tparam value Constant to be written to the field
*
* This method performs a compile-time check to avoid overflowing the
* field and uses the constant write implementation.
*/
template <type value, typename T = void>
static void write(
typename std::enable_if<
!has_shadow
&&
check_overflow<value>::value,
T
>::type* = nullptr
) noexcept {
policy::template write<MMIO_t, type, mask, offset, value>(
parent_register::rw_mem_device()
);
};
static void write(if_no_shadow<value, T>* = nullptr) noexcept {
policy::template write<MMIO, type, mask, offset, value>(
parent_register::rw_mem_device());
//! Field write method with overflow check (w/ shadow value).
// Check for overflow.
static_assert(
internals::check_overflow<type, value, (mask >> offset)>::value,
"Field::write<value>: value too large for the field");
}
//! Field write constant method (w/ shadow value).
/**
* @tparam value Value to be written to the field
* @tparam value Constant to be written to the field
*
* This method performs a compile-time check to avoid overflowing the
* field and uses the constant write implementation.
*/
template <type value, typename T = void>
static void write(
typename std::enable_if<
has_shadow
&&
check_overflow<value>::value,
T
>::type* = nullptr
) noexcept {
// For this particular we simply forward to the non-constant
static void write(if_shadow<value, T>* = nullptr) noexcept {
// For this one we simply forward to the non-constant
// implementation because the shadow value needs to be updated.
write(value);
};
// Check for overflow.
static_assert(
internals::check_overflow<type, value, (mask >> offset)>::value,
"Field::write<value>: value too large for the field");
}
//! Field set method.
/**
* This method will set all bits in the field.
*/
static void set() noexcept {
policy::template
set<MMIO_t, type, mask>(parent_register::rw_mem_device());
};
policy::template set<MMIO, type, mask>(
parent_register::rw_mem_device());
}
//! Field clear method.
/**
* This method will clear all bits in the field.
*/
static void clear() noexcept {
policy::template
clear<MMIO_t, type, mask>(parent_register::rw_mem_device());
};
policy::template clear<MMIO, type, mask>(
parent_register::rw_mem_device());
}
//! Field toggle method.
/**
* This method will toggle all bits in the field.
*/
static void toggle() noexcept {
policy::template
toggle<MMIO_t, type, mask>(parent_register::rw_mem_device());
};
policy::template toggle<MMIO, type, mask>(
parent_register::rw_mem_device());
}
//! Is field set bool method.
/**
@ -209,30 +180,29 @@ namespace cppreg {
*/
static bool is_set() noexcept {
return (Field::read() == (mask >> offset));
};
}
//! Is field clear bool method.
/**
* @return `true` if all the bits are set to 0, `false` otherwise.
*/
static bool is_clear() noexcept {
return (Field::read() == 0u);
};
return (Field::read() == type{0});
}
// Consistency checking.
// The width of the field cannot exceed the register size and the
// width added to the offset cannot exceed the register size.
static_assert(parent_register::size >= width,
"field width is larger than parent register size");
"Field:: field width is larger than parent register size");
static_assert(parent_register::size >= width + offset,
"offset + width is larger than parent register size");
static_assert(width != 0u,
"defining a Field type of width 0u is not allowed");
"Field:: offset + width is larger than parent register size");
static_assert(width != FieldWidth{0},
"Field:: defining a Field type of zero width is not allowed");
};
}
} // namespace cppreg
#endif // CPPREG_REGISTERFIELD_H

View File

@ -2,22 +2,20 @@
/**
* @file Internals.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*
* This header collects various implementations which are required for cppreg
* implementation but not intended to be fully exposed to the user.
*/
#include "cppreg_Defines.h"
#include "Traits.h"
#ifndef CPPREG_INTERNALS_H
#define CPPREG_INTERNALS_H
//! cppreg::internals namespace.
#include "Traits.h"
namespace cppreg {
namespace internals {
@ -32,11 +30,7 @@ namespace internals {
* no overflow and set to std::false_type if there is overflow.
* There is overflow if value if strictly larger than limit.
*/
template <
typename T,
T value,
T limit
>
template <typename T, T value, T limit>
struct check_overflow : std::integral_constant<bool, value <= limit> {};
@ -47,56 +41,13 @@ namespace internals {
*
* This will only derived from std::true_type if the address is aligned.
*/
template <Address_t address, std::size_t alignment>
struct is_aligned : std::integral_constant<
bool,
(address & (alignment - 1)) == 0
> {
};
template <Address address, std::size_t alignment>
struct is_aligned
: std::integral_constant<bool, (address & (alignment - 1)) == 0> {};
//! Memory map implementation for packed registers.
/**
* @tparam address Memory region base address.
* @tparam n Memory size in bytes.
* @tparam reg_size Register bit size enum value.
*
* This structure is used to map an array structure onto a memory region.
* The size of the array elements is defined by the register size.
*/
template <Address_t address, std::uint32_t n, RegBitSize reg_size>
struct memory_map {
//! Array type.
using mem_array_t = std::array<
volatile typename TypeTraits<reg_size>::type,
n / sizeof(typename TypeTraits<reg_size>::type)
>;
//! Static reference to memory array.
static mem_array_t& array;
// Alignment check.
static_assert(
is_aligned<address, TypeTraits<reg_size>::byte_size>::value,
"memory_map: base address is mis-aligned for register type"
);
};
//! Memory array reference definition.
template <Address_t address, std::uint32_t n, RegBitSize reg_size>
typename memory_map<address, n, reg_size>::mem_array_t&
memory_map<address, n, reg_size>::array = *(
reinterpret_cast<typename memory_map<address, n, reg_size>
::mem_array_t*>(
address
)
);
}
}
} // namespace internals
} // namespace cppreg
#endif // CPPREG_INTERNALS_H

View File

@ -2,7 +2,7 @@
/**
* @file Mask.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*
* The implementation is designed to compute masks prior to runtime by
* relying on constexpr function. This will work as intended if the function
@ -17,42 +17,38 @@
#include "cppreg_Defines.h"
//! cppreg namespace.
namespace cppreg {
//! Mask constexpr function.
/**
* @tparam Mask_t Mask data type (will be derived from register).
* @tparam Mask Mask data type (will be derived from register).
* @param width Mask width.
* @return The mask value.
*/
template <typename Mask_t>
constexpr Mask_t make_mask(const FieldWidth_t width) noexcept {
return width == 0 ?
0u
:
static_cast<Mask_t>(
(make_mask<Mask_t>(FieldWidth_t(width - 1)) << 1) | 1
);
};
template <typename Mask>
constexpr Mask make_mask(const FieldWidth width) noexcept {
return width == 0 ? static_cast<Mask>(0u)
: static_cast<Mask>(
(make_mask<Mask>(FieldWidth(width - 1)) << 1) | 1);
}
//! Shifted mask constexpr function.
/**
* @tparam Mask_t Mask data type (will be derived from register).
* @tparam Mask Mask data type (will be derived from register).
* @param width Mask width.
* @param offset Mask offset.
* @return The mask value.
*/
template <typename Mask_t>
constexpr Mask_t make_shifted_mask(const FieldWidth_t width,
const FieldOffset_t offset) noexcept {
return static_cast<Mask_t>(make_mask<Mask_t>(width) << offset);
};
template <typename Mask>
constexpr Mask make_shifted_mask(const FieldWidth width,
const FieldOffset offset) noexcept {
return static_cast<Mask>(make_mask<Mask>(width) << offset);
}
} // namespace cppreg
#endif // CPPREG_MASK_H

106
register/Memory.h Normal file
View File

@ -0,0 +1,106 @@
//! Memory device implementation.
/**
* @file Memory.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*/
#ifndef CPPREG_DEV_MEMORY_H
#define CPPREG_DEV_MEMORY_H
#include "Traits.h"
#include <array>
namespace cppreg {
//! Register pack base implementation.
/**
* @tparam base_address Pack base address.
* @tparam pack_byte_size Pack size in bytes.
*/
template <Address base_address, std::uint32_t pack_byte_size>
struct RegisterPack {
//! Base address.
constexpr static const Address pack_base = base_address;
//! Pack size in bytes.
constexpr static const std::uint32_t size_in_bytes = pack_byte_size;
};
//! Memory device.
/**
* @tparam mem_address Address of the memory device.
* @tparam mem_byte_size Memory device size in bytes.
*/
template <Address mem_address, std::size_t mem_byte_size>
struct MemoryDevice {
//! Storage type.
using memory_storage = std::array<volatile std::uint8_t, mem_byte_size>;
//! Memory device storage.
static memory_storage& _mem_storage;
//! Accessor.
template <RegBitSize reg_size, std::size_t byte_offset>
static const volatile typename TypeTraits<reg_size>::type& ro_memory() {
// Check alignment.
static_assert(
internals::is_aligned<mem_address + byte_offset,
std::alignment_of<typename TypeTraits<
reg_size>::type>::value>::value,
"MemoryDevice:: ro request not aligned");
return *(reinterpret_cast<const volatile
typename TypeTraits<reg_size>::type*>(
&_mem_storage[byte_offset]));
}
//! Modifier.
template <RegBitSize reg_size, std::size_t byte_offset>
static volatile typename TypeTraits<reg_size>::type& rw_memory() {
// Check alignment.
static_assert(
internals::is_aligned<mem_address + byte_offset,
std::alignment_of<typename TypeTraits<
reg_size>::type>::value>::value,
"MemoryDevice:: rw request not aligned");
return *(
reinterpret_cast<volatile typename TypeTraits<reg_size>::type*>(
&_mem_storage[byte_offset]));
}
};
//! Static reference definition.
template <Address a, std::size_t s>
typename MemoryDevice<a, s>::memory_storage& MemoryDevice<a, s>::_mem_storage =
*(reinterpret_cast<typename MemoryDevice<a, s>::memory_storage*>(a));
//! Register memory device for register pack.
/**
*
* @tparam pack_address Register pack address.
* @tparam n_bytes Register pack size in bytes.
*/
template <typename RegisterPack>
struct RegisterMemoryDevice {
using mem_device =
MemoryDevice<RegisterPack::pack_base, RegisterPack::size_in_bytes>;
};
} // namespace cppreg
#endif // CPPREG_DEV_MEMORY_H

View File

@ -2,7 +2,7 @@
/**
* @file MergeWrite.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @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.
@ -18,12 +18,10 @@
#define CPPREG_MERGEWRITE_H
#include "Internals.h"
#include "AccessPolicy.h"
#include <functional>
#include "Internals.h"
//! cppreg namespace.
namespace cppreg {
@ -40,41 +38,47 @@ namespace cppreg {
* 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 policies implementation that detects trivial operations). In
* the access polict implementation that detects trivial operations). In
* addition, this will also perform overflow checking.
*/
template <
typename Register,
template <typename Register,
typename Register::type mask,
FieldOffset_t offset,
typename Register::type value
> class MergeWrite_tmpl {
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");
// Accumulated value.
constexpr static const base_type _accumulated_value =
((value << offset) & mask);
// Combined mask.
constexpr static const base_type _combined_mask = mask;
// Default constructor.
MergeWrite_tmpl() = default;
public:
//! Instantiation method.
static MergeWrite_tmpl make() noexcept { return {}; };
static MergeWrite_tmpl create() noexcept {
return {};
}
//!@{ Non-copyable and non-moveable.
MergeWrite_tmpl(const MergeWrite_tmpl&) = delete;
@ -91,59 +95,42 @@ namespace cppreg {
void done() const&& noexcept {
// Get memory pointer.
typename Register::MMIO_t& mmio_device =
Register::rw_mem_device();
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_t,
RegisterWriteConstant<typename Register::MMIO,
typename Register::type,
_combined_mask,
0u,
_accumulated_value
>::write(mmio_device);
};
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 new instance for chaining other write operations.
* @return A merge write instance with accumulated data.
*/
template <
typename F,
base_type new_value,
typename T = MergeWrite_tmpl<
Register,
(_combined_mask | F::mask),
0u,
(_accumulated_value & ~F::mask) | ((new_value << F::offset) &
F::mask)
>
>
typename std::enable_if<
(internals::check_overflow<
typename Register::type, new_value, (F::mask >> F::offset)
>::value),
T
>::type&&
with() const && noexcept {
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,
"field is not from the same register in merge_write");
return std::move(T::make());
};
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>{});
}
};
@ -155,35 +142,40 @@ namespace cppreg {
* The initial mask will come from the field on which the merge write
* will be initiated.
*/
template <
typename Register,
typename Register::type mask
>
template <typename Register, typename Register::type mask>
class MergeWrite {
public:
//! Type alias to register base type.
private:
// Type alias to register base type.
using base_type = typename Register::type;
private:
// Accumulated value.
base_type _accumulated_value;
// Combined mask.
constexpr static const base_type _combined_mask = 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 make(const base_type value) noexcept {
constexpr static MergeWrite create(const base_type value) noexcept {
return MergeWrite(value);
};
}
//!@ Move constructor.
MergeWrite(MergeWrite&& mw) noexcept
: _accumulated_value(mw._accumulated_value) {};
: _accumulated_value{mw._accumulated_value} {};
//!@{ Non-copyable.
MergeWrite(const MergeWrite&) = delete;
@ -198,70 +190,41 @@ namespace cppreg {
void done() const&& noexcept {
// Get memory pointer.
typename Register::MMIO_t& mmio_device =
Register::rw_mem_device();
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_t,
RegisterWrite<typename Register::MMIO,
base_type,
_combined_mask,
0u
>::write(mmio_device, _accumulated_value);
};
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 reference to the current merge write data.
* @return A merge write instance with accumulated data.
*/
template <typename F>
MergeWrite<Register, _combined_mask | F::mask>
with(const base_type value) && noexcept {
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,
static_assert(
std::is_same<typename F::parent_register, Register>::value,
"field is not from the same register in merge_write");
// Update accumulated value.
_accumulated_value = (_accumulated_value & ~F::mask)
| ((value << F::offset) & F::mask);
return
std::move(
MergeWrite<Register, (_combined_mask | F::mask)>
::make(_accumulated_value)
);
};
private:
// Disabled for shadow value register.
static_assert(!Register::shadow::value,
"merge write is not available for shadow value register");
// Private default constructor.
constexpr MergeWrite() : _accumulated_value(0u) {};
constexpr explicit MergeWrite(const base_type v) :
_accumulated_value(v) {};
// Accumulated value.
base_type _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

View File

@ -2,7 +2,7 @@
/**
* @file Register.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*
* This header provides the definitions related to register implementation.
*/
@ -12,12 +12,11 @@
#define CPPREG_REGISTER_H
#include "Traits.h"
#include "Memory.h"
#include "MergeWrite.h"
#include "ShadowValue.h"
//! cppreg namespace.
namespace cppreg {
@ -34,48 +33,50 @@ namespace cppreg {
* In practice, register are implemented by deriving from this class to
* create custom types.
*/
template <
Address_t reg_address,
template <Address reg_address,
RegBitSize reg_size,
typename TypeTraits<reg_size>::type reset_value = 0x0,
bool use_shadow = false
>
bool use_shadow = false>
struct Register {
//! Register base type.
using type = typename TypeTraits<reg_size>::type;
//! MMIO pointer type.
using MMIO_t = volatile type;
using MMIO = volatile type;
//! Boolean flag for shadow value management.
using shadow = Shadow<Register, use_shadow>;
//! Register base address.
constexpr static const Address_t base_address = reg_address;
constexpr static auto base_address = reg_address;
//! Register size in bits.
constexpr static const std::uint8_t size =
TypeTraits<reg_size>::bit_size;
constexpr static auto size = TypeTraits<reg_size>::bit_size;
//! Register reset value.
constexpr static const type reset = reset_value;
constexpr static auto reset = reset_value;
//! Register pack for memory device.
using pack = RegisterPack<base_address, size / 8u>;
//! Memory modifier.
/**
* @return A reference to the writable register memory.
*/
static MMIO_t& rw_mem_device() {
return *(reinterpret_cast<MMIO_t* const>(base_address));
};
static MMIO& rw_mem_device() {
using mem_device = typename RegisterMemoryDevice<pack>::mem_device;
return mem_device::template rw_memory<reg_size, 0>();
}
//! Memory accessor.
/**
* @return A reference to the read-only register memory.
*/
static const MMIO_t& ro_mem_device() {
return *(reinterpret_cast<const MMIO_t* const>(base_address));
};
static const MMIO& ro_mem_device() {
using mem_device = typename RegisterMemoryDevice<pack>::mem_device;
return mem_device::template ro_memory<reg_size, 0>();
}
//! Merge write start function.
/**
@ -84,12 +85,11 @@ namespace cppreg {
* @return A merge write data structure to chain further writes.
*/
template <typename F>
static MergeWrite<typename F::parent_register, F::mask>
merge_write(const typename F::type value) noexcept {
return
MergeWrite<typename F::parent_register, F::mask>
::make(((value << F::offset) & F::mask));
};
static MergeWrite<typename F::parent_register, F::mask> merge_write(
const typename F::type value) noexcept {
return MergeWrite<typename F::parent_register, F::mask>::create(
static_cast<type>((value << F::offset) & F::mask));
}
//! Merge write start function for constant value.
/**
@ -97,42 +97,34 @@ namespace cppreg {
* @tparam value Value to be written to the field.
* @return A merge write data structure to chain further writes.
*/
template <
typename F,
template <typename F,
type value,
typename T = MergeWrite_tmpl<
typename F::parent_register,
typename T = MergeWrite_tmpl<typename F::parent_register,
F::mask,
F::offset,
value
>
>
static
typename std::enable_if<
internals::check_overflow<
type, value, (F::mask >> F::offset)
>::value,
T
>::type&&
merge_write() noexcept {
return std::move(T::make());
};
value>>
static T&& merge_write() noexcept {
// Check overflow.
static_assert(
internals::check_overflow<type, value, (F::mask >> F::offset)>::
value,
"Register::merge_write<value>:: value too large for the field");
return std::move(T::create());
}
// Sanity check.
static_assert(size != 0u,
"Register: register definition with zero size");
static_assert(size != 0u, "Register:: register definition with zero size");
// Enforce alignment.
static_assert(
internals::is_aligned<reg_address, TypeTraits<reg_size>::byte_size>
::value,
"Register: address is mis-aligned for register type"
);
static_assert(internals::is_aligned<reg_address,
TypeTraits<reg_size>::byte_size>::value,
"Register:: address is mis-aligned for register type");
};
}
} // namespace cppreg
#endif // CPPREG_REGISTER_H

View File

@ -2,43 +2,25 @@
/**
* @file RegisterPack.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*
* This header provides the definitions related to register implementation.
* This header provides the definitions related to packed register
* implementation.
*/
#include "Register.h"
#include "Internals.h"
#ifndef CPPREG_REGISTERPACK_H
#define CPPREG_REGISTERPACK_H
//! cppreg namespace.
#include "Internals.h"
#include "Memory.h"
#include "Register.h"
namespace cppreg {
//! Register pack base implementation.
/**
* @tparam base_address Pack base address.
* @tparam pack_byte_size Pack size in bytes.
*/
template <
Address_t base_address,
std::uint32_t pack_byte_size
> struct RegisterPack {
//! Base address.
constexpr static const Address_t pack_base = base_address;
//! Pack size in bytes.
constexpr static const std::uint32_t size_in_bytes = pack_byte_size;
};
//! Packed register implementation.
/**
* @tparam RegisterPack Pack to which the register belongs.
@ -50,79 +32,61 @@ namespace cppreg {
* This implementation is intended to be used when defining a register
* that belongs to a peripheral group.
*/
template <
typename RegisterPack,
template <typename RegisterPack,
RegBitSize reg_size,
std::uint32_t bit_offset,
typename TypeTraits<reg_size>::type reset_value = 0x0,
bool use_shadow = false
>
struct PackedRegister : Register<
RegisterPack::pack_base + (bit_offset / 8u),
bool use_shadow = false>
struct PackedRegister : Register<RegisterPack::pack_base + (bit_offset / 8u),
reg_size,
reset_value,
use_shadow
> {
use_shadow> {
//! Register pack.
using pack = RegisterPack;
//! Register type.
using base_reg = Register<
RegisterPack::pack_base + (bit_offset / 8u),
using base_reg = Register<RegisterPack::pack_base + (bit_offset / 8u),
reg_size,
reset_value,
use_shadow
>;
//! Memory map type.
using mem_map_t = internals::memory_map<
RegisterPack::pack_base,
RegisterPack::size_in_bytes,
reg_size
>;
use_shadow>;
//! Memory modifier.
/**
* @return A reference to the writable register memory.
*/
static typename base_reg::MMIO_t& rw_mem_device() noexcept {
return mem_map_t::array[bit_offset
/ TypeTraits<reg_size>::bit_size];
};
static typename base_reg::MMIO& rw_mem_device() noexcept {
using mem_device =
typename RegisterMemoryDevice<RegisterPack>::mem_device;
return mem_device::template rw_memory<reg_size, (bit_offset / 8u)>();
}
//! Memory accessor.
/**
* @return A reference to the read-only register memory.
*/
static const typename base_reg::MMIO_t& ro_mem_device() noexcept {
return mem_map_t::array[bit_offset
/ TypeTraits<reg_size>::bit_size];
};
static const typename base_reg::MMIO& ro_mem_device() noexcept {
using mem_device =
typename RegisterMemoryDevice<RegisterPack>::mem_device;
return mem_device::template ro_memory<reg_size, (bit_offset / 8u)>();
}
// Safety check to detect if are overflowing the pack.
static_assert(
TypeTraits<reg_size>::byte_size + (bit_offset / 8u) <=
RegisterPack::size_in_bytes,
"PackRegister: packed register is overflowing the pack"
);
static_assert(TypeTraits<reg_size>::byte_size + (bit_offset / 8u)
<= RegisterPack::size_in_bytes,
"PackRegister:: packed register is overflowing the pack");
// A packed register of width N bits requires:
// - the pack address to be N-bits aligned (N/8 aligned),
// - the pack address with offset to be N-bits aligned (N/8 aligned).
static_assert(
internals::is_aligned<
RegisterPack::pack_base,
TypeTraits<reg_size>::byte_size
>::value,
"PackedRegister: pack base address is mis-aligned for register type"
);
internals::is_aligned<RegisterPack::pack_base,
TypeTraits<reg_size>::byte_size>::value,
"PackedRegister:: pack base address is mis-aligned for register type");
static_assert(
internals::is_aligned<
RegisterPack::pack_base + (bit_offset / 8u),
TypeTraits<reg_size>::byte_size
>::value,
"PackedRegister: offset address is mis-aligned for register type"
);
internals::is_aligned<RegisterPack::pack_base + (bit_offset / 8u),
TypeTraits<reg_size>::byte_size>::value,
"PackedRegister:: offset address is mis-aligned for register type");
};
@ -147,7 +111,6 @@ namespace cppreg {
//! Element accessor.
template <std::size_t N>
using elem = typename std::tuple_element<N, tuple_t>::type;
};
@ -170,7 +133,7 @@ namespace cppreg {
Func().template operator()<start>();
if (start < end)
for_loop<start + 1ul, end>::template apply<Func>();
};
}
#if __cplusplus >= 201402L
//! Apply method.
@ -189,17 +152,16 @@ namespace cppreg {
f(std::integral_constant<std::size_t, start>{});
for_loop<start + 1ul, end>::apply(std::forward<Op>(f));
};
};
}
#endif // __cplusplus 201402L
};
template <std::size_t end>
struct for_loop<end, end> {
template <typename Func>
static void apply() noexcept {};
static void apply() noexcept {}
#if __cplusplus >= 201402L
template <typename Op>
static void apply(Op&& f) noexcept {};
static void apply(Op&& f) noexcept {}
#endif // __cplusplus 201402L
};
@ -212,7 +174,7 @@ namespace cppreg {
struct pack_loop : for_loop<0, IndexedPack::n_elems> {};
}
} // namespace cppreg
#endif // CPPREG_REGISTERPACK_H

View File

@ -2,7 +2,7 @@
/**
* @file ShadowValue.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*/
@ -13,7 +13,6 @@
#include "cppreg_Defines.h"
//! cppreg namespace.
namespace cppreg {
@ -21,31 +20,26 @@ namespace cppreg {
/**
* @tparam Register Register type.
* @tparam use_shadow Boolean flag indicating if shadow value is required.
*
* This implementation is for register which do not require shadow value.
*/
template <typename Register, bool use_shadow>
struct Shadow : std::false_type {};
//! Shadow value implementation.
//! Shadow value specialization.
/**
* @tparam Register Register type.
*
* This implementation is for register which do require shadow value.
*
* See
*/
template <typename Register>
struct Shadow<Register, true> : std::true_type {
static typename Register::type shadow_value;
};
template <typename Register>
typename Register::type Shadow<Register, true>::shadow_value =
Register::reset;
typename Register::type Shadow<Register, true>::shadow_value = Register::reset;
}
} // namespace cppreg
#endif // CPPREG_SHADOWVALUE_H

View File

@ -2,7 +2,7 @@
/**
* @file Traits.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*
* This header provides some traits for register type instantiation.
*/
@ -15,7 +15,6 @@
#include "cppreg_Defines.h"
//! cppreg namespace.
namespace cppreg {
@ -24,38 +23,47 @@ namespace cppreg {
* @tparam S Register size in bits.
*/
template <RegBitSize S>
struct TypeTraits;
struct TypeTraits {};
//!@{ TypeTraits specializations.
//! 8-bit specialization.
template <> struct TypeTraits<RegBitSize::b8> {
template <>
struct TypeTraits<RegBitSize::b8> {
using type = std::uint8_t;
constexpr static const std::uint8_t bit_size = 8u;
constexpr static const std::uint8_t byte_size = bit_size / 8u;
constexpr static auto bit_size = std::uint8_t{8};
constexpr static auto byte_size = std::uint8_t{bit_size / 8};
};
//! 16-bit specialization.
template <> struct TypeTraits<RegBitSize::b16> {
template <>
struct TypeTraits<RegBitSize::b16> {
using type = std::uint16_t;
constexpr static const std::uint8_t bit_size = 16u;
constexpr static const std::uint8_t byte_size = bit_size / 8u;
constexpr static auto bit_size = std::uint8_t{16};
constexpr static auto byte_size = std::uint8_t{bit_size / 8};
};
//! 32-bit specialization.
template <> struct TypeTraits<RegBitSize::b32> {
template <>
struct TypeTraits<RegBitSize::b32> {
using type = std::uint32_t;
constexpr static const std::uint8_t bit_size = 32u;
constexpr static const std::uint8_t byte_size = bit_size / 8u;
constexpr static auto bit_size = std::uint8_t{32};
constexpr static auto byte_size = std::uint8_t{bit_size / 8};
};
//! 64-bit specialization.
template <> struct TypeTraits<RegBitSize::b64> {
template <>
struct TypeTraits<RegBitSize::b64> {
using type = std::uint64_t;
constexpr static const std::uint8_t bit_size = 64u;
constexpr static const std::uint8_t byte_size = bit_size / 8u;
constexpr static auto bit_size = std::uint8_t{64};
constexpr static auto byte_size = std::uint8_t{bit_size / 8};
};
//!@}
}
} // namespace cppreg
#endif // CPPREG_TRAITS_H

View File

@ -2,7 +2,7 @@
/**
* @file cppreg-all.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2018 Sendyne Corp. All rights reserved.
* @copyright Copyright 2010-2019 Sendyne Corp. All rights reserved.
*/
#include <cstdint>
@ -14,15 +14,15 @@
#ifndef CPPREG_CPPREG_DEFINES_H
#define CPPREG_CPPREG_DEFINES_H
namespace cppreg {
using Address_t = std::uintptr_t;
enum class RegBitSize {
using Address = std::uintptr_t;
enum class RegBitSize : std::uint8_t {
b8,
b16,
b32,
b64
};
using FieldWidth_t = std::uint8_t;
using FieldOffset_t = std::uint8_t;
using FieldWidth = std::uint8_t;
using FieldOffset = std::uint8_t;
template <typename T>
struct type_mask {
constexpr static const T value = std::numeric_limits<T>::max();
@ -35,26 +35,30 @@ namespace cppreg {
#define CPPREG_TRAITS_H
namespace cppreg {
template <RegBitSize S>
struct TypeTraits;
template <> struct TypeTraits<RegBitSize::b8> {
struct TypeTraits {};
template <>
struct TypeTraits<RegBitSize::b8> {
using type = std::uint8_t;
constexpr static const std::uint8_t bit_size = 8u;
constexpr static const std::uint8_t byte_size = bit_size / 8u;
constexpr static auto bit_size = std::uint8_t{8};
constexpr static auto byte_size = std::uint8_t{bit_size / 8};
};
template <> struct TypeTraits<RegBitSize::b16> {
template <>
struct TypeTraits<RegBitSize::b16> {
using type = std::uint16_t;
constexpr static const std::uint8_t bit_size = 16u;
constexpr static const std::uint8_t byte_size = bit_size / 8u;
constexpr static auto bit_size = std::uint8_t{16};
constexpr static auto byte_size = std::uint8_t{bit_size / 8};
};
template <> struct TypeTraits<RegBitSize::b32> {
template <>
struct TypeTraits<RegBitSize::b32> {
using type = std::uint32_t;
constexpr static const std::uint8_t bit_size = 32u;
constexpr static const std::uint8_t byte_size = bit_size / 8u;
constexpr static auto bit_size = std::uint8_t{32};
constexpr static auto byte_size = std::uint8_t{bit_size / 8};
};
template <> struct TypeTraits<RegBitSize::b64> {
template <>
struct TypeTraits<RegBitSize::b64> {
using type = std::uint64_t;
constexpr static const std::uint8_t bit_size = 64u;
constexpr static const std::uint8_t byte_size = bit_size / 8u;
constexpr static auto bit_size = std::uint8_t{64};
constexpr static auto byte_size = std::uint8_t{bit_size / 8};
};
}
#endif
@ -64,164 +68,167 @@ namespace cppreg {
#define CPPREG_INTERNALS_H
namespace cppreg {
namespace internals {
template <
typename T,
T value,
T limit
>
template <typename T, T value, T limit>
struct check_overflow : std::integral_constant<bool, value <= limit> {};
template <Address_t address, std::size_t alignment>
struct is_aligned : std::integral_constant<
bool,
(address & (alignment - 1)) == 0
> {
};
template <Address_t address, std::uint32_t n, RegBitSize reg_size>
struct memory_map {
using mem_array_t = std::array<
volatile typename TypeTraits<reg_size>::type,
n / sizeof(typename TypeTraits<reg_size>::type)
>;
static mem_array_t& array;
static_assert(
is_aligned<address, TypeTraits<reg_size>::byte_size>::value,
"memory_map: base address is mis-aligned for register type"
);
};
template <Address_t address, std::uint32_t n, RegBitSize reg_size>
typename memory_map<address, n, reg_size>::mem_array_t&
memory_map<address, n, reg_size>::array = *(
reinterpret_cast<typename memory_map<address, n, reg_size>
::mem_array_t*>(
address
)
);
template <Address address, std::size_t alignment>
struct is_aligned
: std::integral_constant<bool, (address & (alignment - 1)) == 0> {};
}
}
#endif
// Memory.h
#ifndef CPPREG_DEV_MEMORY_H
#define CPPREG_DEV_MEMORY_H
namespace cppreg {
template <Address base_address, std::uint32_t pack_byte_size>
struct RegisterPack {
constexpr static const Address pack_base = base_address;
constexpr static const std::uint32_t size_in_bytes = pack_byte_size;
};
template <Address mem_address, std::size_t mem_byte_size>
struct MemoryDevice {
using memory_storage = std::array<volatile std::uint8_t, mem_byte_size>;
static memory_storage& _mem_storage;
template <RegBitSize reg_size, std::size_t byte_offset>
static const volatile typename TypeTraits<reg_size>::type& ro_memory() {
static_assert(
internals::is_aligned<mem_address + byte_offset,
std::alignment_of<typename TypeTraits<
reg_size>::type>::value>::value,
"MemoryDevice:: ro request not aligned");
return *(reinterpret_cast<const volatile
typename TypeTraits<reg_size>::type*>(
&_mem_storage[byte_offset]));
}
template <RegBitSize reg_size, std::size_t byte_offset>
static volatile typename TypeTraits<reg_size>::type& rw_memory() {
static_assert(
internals::is_aligned<mem_address + byte_offset,
std::alignment_of<typename TypeTraits<
reg_size>::type>::value>::value,
"MemoryDevice:: rw request not aligned");
return *(
reinterpret_cast<volatile typename TypeTraits<reg_size>::type*>(
&_mem_storage[byte_offset]));
}
};
template <Address a, std::size_t s>
typename MemoryDevice<a, s>::memory_storage& MemoryDevice<a, s>::_mem_storage =
*(reinterpret_cast<typename MemoryDevice<a, s>::memory_storage*>(a));
template <typename RegisterPack>
struct RegisterMemoryDevice {
using mem_device =
MemoryDevice<RegisterPack::pack_base, RegisterPack::size_in_bytes>;
};
}
#endif
// AccessPolicy.h
#ifndef CPPREG_ACCESSPOLICY_H
#define CPPREG_ACCESSPOLICY_H
namespace cppreg {
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
template <typename T, T mask, FieldOffset offset>
struct is_trivial_rw
: std::integral_constant<bool,
(mask == type_mask<T>::value)
&& (offset == FieldOffset{0})> {};
template <typename T, T mask, FieldOffset offset, typename U>
using is_trivial =
typename std::enable_if<is_trivial_rw<T, mask, offset>::value, U>::type;
template <typename T, T mask, FieldOffset offset, typename U>
using is_not_trivial =
typename std::enable_if<!is_trivial_rw<T, mask, offset>::value, U>::type;
template <typename MMIO, typename T, T mask, FieldOffset offset>
struct RegisterRead {
constexpr static const bool is_trivial =
(mask == type_mask<T>::value) && (offset == 0u);
template <typename U = void>
static T read(
const MMIO_t& mmio_device,
typename std::enable_if<!is_trivial, U*>::type = nullptr
) noexcept {
static T read(const MMIO& mmio_device,
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
return static_cast<T>((mmio_device & mask) >> offset);
};
}
template <typename U = void>
static T read(
const MMIO_t& mmio_device,
typename std::enable_if<is_trivial, U*>::type = nullptr
) noexcept {
static T read(const MMIO& mmio_device,
is_trivial<T, mask, offset, U>* = nullptr) noexcept {
return static_cast<T>(mmio_device);
}
};
};
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
template <typename MMIO, typename T, T mask, FieldOffset offset>
struct RegisterWrite {
constexpr static const bool is_trivial =
(mask == type_mask<T>::value) && (offset == 0u);
template <typename U = void>
static void write(
MMIO_t& mmio_device,
static void write(MMIO& mmio_device,
T value,
typename std::enable_if<!is_trivial, U*>::type = nullptr
) noexcept {
mmio_device = static_cast<T>(
(mmio_device & ~mask) | ((value << offset) & mask)
);
};
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device =
static_cast<T>((mmio_device & ~mask) | ((value << offset) & mask));
}
template <typename U = void>
static void write(
MMIO_t& mmio_device,
static void write(MMIO& mmio_device,
T value,
typename std::enable_if<is_trivial, U*>::type = nullptr
) noexcept {
is_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = value;
}
};
};
template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
template <typename MMIO, typename T, T mask, FieldOffset offset, T value>
struct RegisterWriteConstant {
constexpr static const bool is_trivial =
(mask == type_mask<T>::value) && (offset == 0u);
template <typename U = void>
static void write(
MMIO_t& mmio_device,
typename std::enable_if<!is_trivial, U*>::type = nullptr
) noexcept {
mmio_device = static_cast<T>(
(mmio_device & ~mask) | ((value << offset) & mask)
);
};
static void write(MMIO& mmio_device,
is_not_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device =
static_cast<T>((mmio_device & ~mask) | ((value << offset) & mask));
}
template <typename U = void>
static void write(
MMIO_t& mmio_device,
typename std::enable_if<is_trivial, U*>::type = nullptr
) noexcept {
static void write(MMIO& mmio_device,
is_trivial<T, mask, offset, U>* = nullptr) noexcept {
mmio_device = value;
};
}
};
struct read_only {
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
static T read(const MMIO_t& mmio_device) noexcept {
return RegisterRead<MMIO_t, T, mask, offset>::read(mmio_device);
};
template <typename MMIO, typename T, T mask, FieldOffset offset>
static T read(const MMIO& mmio_device) noexcept {
return RegisterRead<MMIO, T, mask, offset>::read(mmio_device);
}
};
struct read_write : read_only {
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
static void write(MMIO_t& mmio_device,
const T value) noexcept {
RegisterWrite<MMIO_t, T, mask, offset>::write(mmio_device, value);
};
template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
static void write(MMIO_t& mmio_device) noexcept {
RegisterWriteConstant<MMIO_t, T, mask, offset, value>
::write(mmio_device);
};
template <typename MMIO_t, typename T, T mask>
static void set(MMIO_t& mmio_device)
noexcept {
RegisterWriteConstant<MMIO_t, T, mask, 0u, mask>
::write(mmio_device);
};
template <typename MMIO_t, typename T, T mask>
static void clear(MMIO_t& mmio_device)
noexcept {
RegisterWriteConstant<MMIO_t, T, mask, 0u, ~mask>
::write(mmio_device);
};
template <typename MMIO_t, typename T, T mask>
static void toggle(MMIO_t& mmio_device)
noexcept {
template <typename MMIO, typename T, T mask, FieldOffset offset>
static void write(MMIO& mmio_device, const T value) noexcept {
RegisterWrite<MMIO, T, mask, offset>::write(mmio_device, value);
}
template <typename MMIO, typename T, T mask, FieldOffset offset, T value>
static void write(MMIO& mmio_device) noexcept {
RegisterWriteConstant<MMIO, T, mask, offset, value>::write(mmio_device);
}
template <typename MMIO, typename T, T mask>
static void set(MMIO& mmio_device) noexcept {
RegisterWriteConstant<MMIO, T, mask, FieldOffset{0}, mask>::write(
mmio_device);
}
template <typename MMIO, typename T, T mask>
static void clear(MMIO& mmio_device) noexcept {
RegisterWriteConstant<MMIO,
T,
mask,
FieldOffset{0},
static_cast<T>(~mask)>::write(mmio_device);
}
template <typename MMIO, typename T, T mask>
static void toggle(MMIO& mmio_device) noexcept {
mmio_device = static_cast<T>((mmio_device) ^ mask);
};
}
};
struct write_only {
template <typename MMIO_t, typename T, T mask, FieldOffset_t offset>
static void write(MMIO_t& mmio_device, const T value) noexcept {
RegisterWrite<MMIO_t, T, type_mask<T>::value, 0u>::write(
mmio_device, ((value << offset) & mask)
);
};
template <
typename MMIO_t, typename T, T mask, FieldOffset_t offset, T value
>
static void write(MMIO_t& mmio_device) noexcept {
RegisterWriteConstant<
MMIO_t, T, type_mask<T>::value, 0u, ((value << offset) & mask)
>
::write(mmio_device);
};
template <typename MMIO, typename T, T mask, FieldOffset offset>
static void write(MMIO& mmio_device, const T value) noexcept {
RegisterWrite<MMIO, T, type_mask<T>::value, FieldOffset{0}>::write(
mmio_device, ((value << offset) & mask));
}
template <typename MMIO, typename T, T mask, FieldOffset offset, T value>
static void write(MMIO& mmio_device) noexcept {
RegisterWriteConstant<MMIO,
T,
type_mask<T>::value,
FieldOffset{0},
((value << offset) & mask)>::write(mmio_device);
}
};
}
#endif
@ -230,20 +237,17 @@ namespace cppreg {
#ifndef CPPREG_MASK_H
#define CPPREG_MASK_H
namespace cppreg {
template <typename Mask_t>
constexpr Mask_t make_mask(const FieldWidth_t width) noexcept {
return width == 0 ?
0u
:
static_cast<Mask_t>(
(make_mask<Mask_t>(FieldWidth_t(width - 1)) << 1) | 1
);
};
template <typename Mask_t>
constexpr Mask_t make_shifted_mask(const FieldWidth_t width,
const FieldOffset_t offset) noexcept {
return static_cast<Mask_t>(make_mask<Mask_t>(width) << offset);
};
template <typename Mask>
constexpr Mask make_mask(const FieldWidth width) noexcept {
return width == 0 ? static_cast<Mask>(0u)
: static_cast<Mask>(
(make_mask<Mask>(FieldWidth(width - 1)) << 1) | 1);
}
template <typename Mask>
constexpr Mask make_shifted_mask(const FieldWidth width,
const FieldOffset offset) noexcept {
return static_cast<Mask>(make_mask<Mask>(width) << offset);
}
}
#endif
@ -258,8 +262,7 @@ namespace cppreg {
static typename Register::type shadow_value;
};
template <typename Register>
typename Register::type Shadow<Register, true>::shadow_value =
Register::reset;
typename Register::type Shadow<Register, true>::shadow_value = Register::reset;
}
#endif
@ -267,115 +270,94 @@ namespace cppreg {
#ifndef CPPREG_MERGEWRITE_H
#define CPPREG_MERGEWRITE_H
namespace cppreg {
template <
typename Register,
template <typename Register,
typename Register::type mask,
FieldOffset_t offset,
typename Register::type value
> class MergeWrite_tmpl {
FieldOffset offset,
typename Register::type value>
class MergeWrite_tmpl {
private:
using base_type = typename Register::type;
constexpr static auto _accumulated_value =
base_type{(value << offset) & mask};
constexpr static auto _combined_mask = mask;
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)>;
MergeWrite_tmpl() = default;
static_assert(!Register::shadow::value,
"merge write is not available for shadow value register");
constexpr static const base_type _accumulated_value =
((value << offset) & mask);
constexpr static const base_type _combined_mask = mask;
MergeWrite_tmpl() = default;
public:
static MergeWrite_tmpl make() noexcept { return {}; };
static MergeWrite_tmpl create() noexcept {
return {};
}
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;
void done() const&& noexcept {
typename Register::MMIO_t& mmio_device =
Register::rw_mem_device();
RegisterWriteConstant<
typename Register::MMIO_t,
typename Register::MMIO& mmio_device = Register::rw_mem_device();
RegisterWriteConstant<typename Register::MMIO,
typename Register::type,
_combined_mask,
0u,
_accumulated_value
>::write(mmio_device);
FieldOffset{0},
_accumulated_value>::write(mmio_device);
}
template <typename F, base_type field_value>
propagated<F, field_value>&& with() const&& noexcept {
static_assert(
std::is_same<typename F::parent_register, Register>::value,
"MergeWrite_tmpl:: field is not from the same register");
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>{});
}
};
template <
typename F,
base_type new_value,
typename T = MergeWrite_tmpl<
Register,
(_combined_mask | F::mask),
0u,
(_accumulated_value & ~F::mask) | ((new_value << F::offset) &
F::mask)
>
>
typename std::enable_if<
(internals::check_overflow<
typename Register::type, new_value, (F::mask >> F::offset)
>::value),
T
>::type&&
with() const && noexcept {
static_assert(std::is_same<
typename F::parent_register,
Register
>::value,
"field is not from the same register in merge_write");
return std::move(T::make());
};
};
template <
typename Register,
typename Register::type mask
>
template <typename Register, typename Register::type mask>
class MergeWrite {
public:
using base_type = typename Register::type;
private:
constexpr static const base_type _combined_mask = mask;
using base_type = typename Register::type;
base_type _accumulated_value;
constexpr static auto _combined_mask = mask;
template <typename F>
using propagated = MergeWrite<Register, _combined_mask | F::mask>;
constexpr MergeWrite() : _accumulated_value{0} {};
constexpr explicit MergeWrite(const base_type v) : _accumulated_value{v} {};
static_assert(!Register::shadow::value,
"merge write is not available for shadow value register");
public:
constexpr static MergeWrite make(const base_type value) noexcept {
constexpr static MergeWrite create(const base_type value) noexcept {
return MergeWrite(value);
};
}
MergeWrite(MergeWrite&& mw) noexcept
: _accumulated_value(mw._accumulated_value) {};
: _accumulated_value{mw._accumulated_value} {};
MergeWrite(const MergeWrite&) = delete;
MergeWrite& operator=(const MergeWrite&) = delete;
MergeWrite& operator=(MergeWrite&&) = delete;
void done() const&& noexcept {
typename Register::MMIO_t& mmio_device =
Register::rw_mem_device();
RegisterWrite<
typename Register::MMIO_t,
typename Register::MMIO& mmio_device = Register::rw_mem_device();
RegisterWrite<typename Register::MMIO,
base_type,
_combined_mask,
0u
>::write(mmio_device, _accumulated_value);
};
FieldOffset{0}>::write(mmio_device, _accumulated_value);
}
template <typename F>
MergeWrite<Register, _combined_mask | F::mask>
with(const base_type value) && noexcept {
static_assert(std::is_same<
typename F::parent_register,
Register
>::value,
propagated<F> with(const base_type value) const&& noexcept {
static_assert(
std::is_same<typename F::parent_register, Register>::value,
"field is not from the same register in merge_write");
_accumulated_value = (_accumulated_value & ~F::mask)
| ((value << F::offset) & F::mask);
return
std::move(
MergeWrite<Register, (_combined_mask | F::mask)>
::make(_accumulated_value)
);
};
private:
static_assert(!Register::shadow::value,
"merge write is not available for shadow value register");
constexpr MergeWrite() : _accumulated_value(0u) {};
constexpr explicit MergeWrite(const base_type v) :
_accumulated_value(v) {};
base_type _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));
}
};
}
#endif
@ -384,60 +366,49 @@ namespace cppreg {
#ifndef CPPREG_REGISTER_H
#define CPPREG_REGISTER_H
namespace cppreg {
template <
Address_t reg_address,
template <Address reg_address,
RegBitSize reg_size,
typename TypeTraits<reg_size>::type reset_value = 0x0,
bool use_shadow = false
>
bool use_shadow = false>
struct Register {
using type = typename TypeTraits<reg_size>::type;
using MMIO_t = volatile type;
using MMIO = volatile type;
using shadow = Shadow<Register, use_shadow>;
constexpr static const Address_t base_address = reg_address;
constexpr static const std::uint8_t size =
TypeTraits<reg_size>::bit_size;
constexpr static const type reset = reset_value;
static MMIO_t& rw_mem_device() {
return *(reinterpret_cast<MMIO_t* const>(base_address));
};
static const MMIO_t& ro_mem_device() {
return *(reinterpret_cast<const MMIO_t* const>(base_address));
};
constexpr static auto base_address = reg_address;
constexpr static auto size = TypeTraits<reg_size>::bit_size;
constexpr static auto reset = reset_value;
using pack = RegisterPack<base_address, size / 8u>;
static MMIO& rw_mem_device() {
using mem_device = typename RegisterMemoryDevice<pack>::mem_device;
return mem_device::template rw_memory<reg_size, 0>();
}
static const MMIO& ro_mem_device() {
using mem_device = typename RegisterMemoryDevice<pack>::mem_device;
return mem_device::template ro_memory<reg_size, 0>();
}
template <typename F>
static MergeWrite<typename F::parent_register, F::mask>
merge_write(const typename F::type value) noexcept {
return
MergeWrite<typename F::parent_register, F::mask>
::make(((value << F::offset) & F::mask));
};
template <
typename F,
static MergeWrite<typename F::parent_register, F::mask> merge_write(
const typename F::type value) noexcept {
return MergeWrite<typename F::parent_register, F::mask>::create(
static_cast<type>((value << F::offset) & F::mask));
}
template <typename F,
type value,
typename T = MergeWrite_tmpl<
typename F::parent_register,
typename T = MergeWrite_tmpl<typename F::parent_register,
F::mask,
F::offset,
value
>
>
static
typename std::enable_if<
internals::check_overflow<
type, value, (F::mask >> F::offset)
>::value,
T
>::type&&
merge_write() noexcept {
return std::move(T::make());
};
static_assert(size != 0u,
"Register: register definition with zero size");
value>>
static T&& merge_write() noexcept {
static_assert(
internals::is_aligned<reg_address, TypeTraits<reg_size>::byte_size>
::value,
"Register: address is mis-aligned for register type"
);
internals::check_overflow<type, value, (F::mask >> F::offset)>::
value,
"Register::merge_write<value>:: value too large for the field");
return std::move(T::create());
}
static_assert(size != 0u, "Register:: register definition with zero size");
static_assert(internals::is_aligned<reg_address,
TypeTraits<reg_size>::byte_size>::value,
"Register:: address is mis-aligned for register type");
};
}
#endif
@ -446,64 +417,41 @@ namespace cppreg {
#ifndef CPPREG_REGISTERPACK_H
#define CPPREG_REGISTERPACK_H
namespace cppreg {
template <
Address_t base_address,
std::uint32_t pack_byte_size
> struct RegisterPack {
constexpr static const Address_t pack_base = base_address;
constexpr static const std::uint32_t size_in_bytes = pack_byte_size;
};
template <
typename RegisterPack,
template <typename RegisterPack,
RegBitSize reg_size,
std::uint32_t bit_offset,
typename TypeTraits<reg_size>::type reset_value = 0x0,
bool use_shadow = false
>
struct PackedRegister : Register<
RegisterPack::pack_base + (bit_offset / 8u),
bool use_shadow = false>
struct PackedRegister : Register<RegisterPack::pack_base + (bit_offset / 8u),
reg_size,
reset_value,
use_shadow
> {
using base_reg = Register<
RegisterPack::pack_base + (bit_offset / 8u),
use_shadow> {
using pack = RegisterPack;
using base_reg = Register<RegisterPack::pack_base + (bit_offset / 8u),
reg_size,
reset_value,
use_shadow
>;
using mem_map_t = internals::memory_map<
RegisterPack::pack_base,
RegisterPack::size_in_bytes,
reg_size
>;
static typename base_reg::MMIO_t& rw_mem_device() noexcept {
return mem_map_t::array[bit_offset
/ TypeTraits<reg_size>::bit_size];
};
static const typename base_reg::MMIO_t& ro_mem_device() noexcept {
return mem_map_t::array[bit_offset
/ TypeTraits<reg_size>::bit_size];
};
use_shadow>;
static typename base_reg::MMIO& rw_mem_device() noexcept {
using mem_device =
typename RegisterMemoryDevice<RegisterPack>::mem_device;
return mem_device::template rw_memory<reg_size, (bit_offset / 8u)>();
}
static const typename base_reg::MMIO& ro_mem_device() noexcept {
using mem_device =
typename RegisterMemoryDevice<RegisterPack>::mem_device;
return mem_device::template ro_memory<reg_size, (bit_offset / 8u)>();
}
static_assert(TypeTraits<reg_size>::byte_size + (bit_offset / 8u)
<= RegisterPack::size_in_bytes,
"PackRegister:: packed register is overflowing the pack");
static_assert(
TypeTraits<reg_size>::byte_size + (bit_offset / 8u) <=
RegisterPack::size_in_bytes,
"PackRegister: packed register is overflowing the pack"
);
internals::is_aligned<RegisterPack::pack_base,
TypeTraits<reg_size>::byte_size>::value,
"PackedRegister:: pack base address is mis-aligned for register type");
static_assert(
internals::is_aligned<
RegisterPack::pack_base,
TypeTraits<reg_size>::byte_size
>::value,
"PackedRegister: pack base address is mis-aligned for register type"
);
static_assert(
internals::is_aligned<
RegisterPack::pack_base + (bit_offset / 8u),
TypeTraits<reg_size>::byte_size
>::value,
"PackedRegister: offset address is mis-aligned for register type"
);
internals::is_aligned<RegisterPack::pack_base + (bit_offset / 8u),
TypeTraits<reg_size>::byte_size>::value,
"PackedRegister:: offset address is mis-aligned for register type");
};
template <typename... T>
struct PackIndexing {
@ -520,7 +468,7 @@ namespace cppreg {
Func().template operator()<start>();
if (start < end)
for_loop<start + 1ul, end>::template apply<Func>();
};
}
#if __cplusplus >= 201402L
template <typename Op>
static void apply(Op&& f) noexcept {
@ -528,16 +476,16 @@ namespace cppreg {
f(std::integral_constant<std::size_t, start>{});
for_loop<start + 1ul, end>::apply(std::forward<Op>(f));
};
};
}
#endif
};
template <std::size_t end>
struct for_loop<end, end> {
template <typename Func>
static void apply() noexcept {};
static void apply() noexcept {}
#if __cplusplus >= 201402L
template <typename Op>
static void apply(Op&& f) noexcept {};
static void apply(Op&& f) noexcept {}
#endif
};
template <typename IndexedPack>
@ -549,102 +497,81 @@ namespace cppreg {
#ifndef CPPREG_REGISTERFIELD_H
#define CPPREG_REGISTERFIELD_H
namespace cppreg {
template <
typename BaseRegister,
FieldWidth_t field_width,
FieldOffset_t field_offset,
typename AccessPolicy
>
template <typename BaseRegister,
FieldWidth field_width,
FieldOffset field_offset,
typename AccessPolicy>
struct Field {
using parent_register = BaseRegister;
using type = typename parent_register::type;
using MMIO_t = typename parent_register::MMIO_t;
using MMIO = typename parent_register::MMIO;
using policy = AccessPolicy;
constexpr static const FieldWidth_t width = field_width;
constexpr static const FieldOffset_t offset = field_offset;
constexpr static const type mask = make_shifted_mask<type>(width,
offset);
constexpr static const bool has_shadow =
parent_register::shadow::value;
template <type value>
struct check_overflow : internals::check_overflow<
type,
value,
(mask >> offset)
> {};
constexpr static auto width = field_width;
constexpr static auto offset = field_offset;
constexpr static auto mask = make_shifted_mask<type>(width, offset);
template <type value, typename T>
using if_no_shadow =
typename std::enable_if<!parent_register::shadow::value, T>::type;
template <type value, typename T>
using if_shadow =
typename std::enable_if<parent_register::shadow::value, T>::type;
static type read() noexcept {
return policy::template read<MMIO_t, type, mask, offset>(
parent_register::ro_mem_device()
);
};
return policy::template read<MMIO, type, mask, offset>(
parent_register::ro_mem_device());
}
template <typename T = type>
static void
write(const typename std::enable_if<!has_shadow, T>::type value)
noexcept {
policy::template write<MMIO_t, type, mask, offset>(
parent_register::rw_mem_device(),
value
);
};
static void write(const if_no_shadow<type{0}, T> value) noexcept {
policy::template write<MMIO, type, mask, offset>(
parent_register::rw_mem_device(), value);
}
template <typename T = type>
static void
write(const typename std::enable_if<has_shadow, T>::type value)
noexcept {
RegisterWrite<type, type, mask, offset>
::write(parent_register::shadow::shadow_value, value);
policy::template write<MMIO_t, type, type_mask<type>::value, 0u>(
static void write(const if_shadow<type{0}, T> value) noexcept {
RegisterWrite<type, type, mask, offset>::write(
parent_register::shadow::shadow_value, value);
policy::
template write<MMIO, type, type_mask<type>::value, FieldOffset{0}>(
parent_register::rw_mem_device(),
parent_register::shadow::shadow_value
);
};
parent_register::shadow::shadow_value);
}
template <type value, typename T = void>
static void write(
typename std::enable_if<
!has_shadow
&&
check_overflow<value>::value,
T
>::type* = nullptr
) noexcept {
policy::template write<MMIO_t, type, mask, offset, value>(
parent_register::rw_mem_device()
);
};
static void write(if_no_shadow<value, T>* = nullptr) noexcept {
policy::template write<MMIO, type, mask, offset, value>(
parent_register::rw_mem_device());
static_assert(
internals::check_overflow<type, value, (mask >> offset)>::value,
"Field::write<value>: value too large for the field");
}
template <type value, typename T = void>
static void write(
typename std::enable_if<
has_shadow
&&
check_overflow<value>::value,
T
>::type* = nullptr
) noexcept {
static void write(if_shadow<value, T>* = nullptr) noexcept {
write(value);
};
static_assert(
internals::check_overflow<type, value, (mask >> offset)>::value,
"Field::write<value>: value too large for the field");
}
static void set() noexcept {
policy::template
set<MMIO_t, type, mask>(parent_register::rw_mem_device());
};
policy::template set<MMIO, type, mask>(
parent_register::rw_mem_device());
}
static void clear() noexcept {
policy::template
clear<MMIO_t, type, mask>(parent_register::rw_mem_device());
};
policy::template clear<MMIO, type, mask>(
parent_register::rw_mem_device());
}
static void toggle() noexcept {
policy::template
toggle<MMIO_t, type, mask>(parent_register::rw_mem_device());
};
policy::template toggle<MMIO, type, mask>(
parent_register::rw_mem_device());
}
static bool is_set() noexcept {
return (Field::read() == (mask >> offset));
};
}
static bool is_clear() noexcept {
return (Field::read() == 0u);
};
return (Field::read() == type{0});
}
static_assert(parent_register::size >= width,
"field width is larger than parent register size");
"Field:: field width is larger than parent register size");
static_assert(parent_register::size >= width + offset,
"offset + width is larger than parent register size");
static_assert(width != 0u,
"defining a Field type of width 0u is not allowed");
"Field:: offset + width is larger than parent register size");
static_assert(width != FieldWidth{0},
"Field:: defining a Field type of zero width is not allowed");
};
}
#endif