Skip to content

Commit c703160

Browse files
committed
readme update
1 parent d980570 commit c703160

3 files changed

Lines changed: 126 additions & 39 deletions

File tree

README.md

Lines changed: 123 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,141 @@
11
# ~~Bitfield~~ Bitfilled support for C++
22

3-
These couple of C++20 headers provide a new, more flexible way of manipulating bit-fields.
3+
This header-only C++20 library provides a new, portable and more flexible way of manipulating bit-fields.
44
While the current codebase is functional, it is overall still in the concept development phase,
5-
with important features on the horizon. Feedback is very welcome!
5+
with important features on the horizon. [Feedback](https://github.com/IntergatedCircuits/bitfilled/discussions) is very welcome!
66

77
## Introduction
88

9-
Let's look at the C(++) language's [built-in bit field][stdbitfield] first:
9+
What makes bitfilled different from the language standard's [bit fields][stdbitfield]?
10+
11+
1. **Portability** - regardless of platform or toolchain, the code behavior is the same (**except MSVC**, as it refuses to implement `[[no_unique_address]]`)
12+
2. **Performance** - optimized binary is identical to standard bit fields
13+
3. **Flexibility** - allows bit fields on custom types, bit field arrays, and customizing bit operations (e.g. bit-banding)
14+
15+
## Containing types
16+
17+
One key thing to note in advance is that bitfilled fields are closely tied with their containing class,
18+
as it defines the available memory size and alignment, and bit field operations.
19+
Therefore let's go through these types first:
20+
21+
### 1. Host integers
22+
23+
The `host_integer` type is simply encapsulating an integral type, forwarding all operations to it,
24+
and provides the necessary scope information to the bitfilled member fields:
1025

1126
```cpp
12-
struct legacy
13-
{
14-
bool boolean : 1;
15-
std::endian enumerated : 2;
16-
std::int32_t integer : 5;
17-
};
27+
#include "bitfilled/integer.hpp"
28+
namespace bitfilled {
29+
template <Integral T, typename TOps = bitfilled::base>
30+
struct host_integer;
31+
}
32+
```
33+
34+
### 2. Packed integers with fixed endianness
35+
36+
The `packed_integer` type is stored as a byte array, but accessible as an integral type,
37+
the conversion being performed based on the endianness of the type.
38+
The purpose of this type is to facilitate portable definition of various network protocol data units.
39+
```cpp
40+
#include "bitfilled/integer.hpp"
41+
namespace bitfilled {
42+
template <std::endian ENDIAN, std::size_t SIZE, Integral T = sized_unsigned_t<std::bit_ceil(SIZE)>>
43+
struct packed_integer;
44+
}
45+
```
46+
47+
### 3. Memory-mapped I/O registers
48+
49+
The `mmreg` type serves as an accurate representation of a memory-mapped register,
50+
with specific access limitation (e.g. read-write / read-only / write-only).
51+
Its bit fields have their own access specifier as well.
52+
```cpp
53+
#include "bitfilled/mmreg.hpp"
54+
namespace bitfilled {
55+
template <Integral T, enum access ACCESS = access::readwrite, typename TOps = bitfilled::base>
56+
struct mmreg;
57+
}
1858
```
1959
20-
We can manipulate each field as normal members of `struct legacy`, their values don't cross bit boundaries,
21-
the signed integers even get sign extended, that's all well.
22-
There are a number of drawbacks to using standard bitfields, such as:
23-
1. Lack of standardization (and thus portability),
24-
a C++ compiler implementation can freely decide how these bits are stored in memory
25-
(see also [bitfield traits](./bitfilled/bitfield_traits.hpp) for an overview).
26-
2. Lack of flexibility,
27-
e.g. they may only have integral or enum type,
28-
they cannot be passed by reference,
29-
nor can they be organized into arrays, etc.
60+
## Bit field types
3061
31-
This is a problem in many use cases involving bit-fields. So let's solve that:
62+
There are currently two types of fields supported:
63+
1. Regular bit fields, which take a contiguous bit range in memory
64+
2. Bit field sets, a adjacent bit fields organized into an array indexible set
3265
3366
```cpp
34-
struct nextgen : bitfilled::host_integer<std::uint8_t>
67+
#include "bitfilled/bits.hpp"
68+
namespace bitfilled {
69+
template <typename T, typename TOps, std::size_t FIRST_BIT, std::size_t LAST_BIT>
70+
using bitfield = regbitfield<T, TOps, access::readwrite, FIRST_BIT, LAST_BIT>;
71+
72+
template <typename T, typename TOps, std::size_t ITEM_SIZE, std::size_t ITEM_COUNT, std::size_t OFFSET>
73+
using bitfieldset = regbitfieldset<T, TOps, access::readwrite, ITEM_SIZE, ITEM_COUNT, OFFSET>;
74+
}
75+
```
76+
77+
The main difference compared to standard bit fields is that their position is absolute,
78+
which also means that they can overlap one another. (Don't repeat the same field with the same type
79+
at the same position though, as that breaks `[[no_unique_address]]` guarantee!)
80+
Both of these types have an explicit access specified `reg-` version, for `mmreg` use.
81+
Some examples are due:
82+
83+
(Do not be alarmed by the macros, their main purpose is to reduce the character count,
84+
as having `[[no_unique_address]]` and a long type name isn't all that informative in this context.)
85+
```cpp
86+
#include <bitfilled/bitfilled.hpp>
87+
struct myint : bitfilled::host_integer<unsigned>
3588
{
36-
BF_COPY_SUPERCLASS(nextgen)
37-
BF_BITS(bool, 0) boolean;
38-
BF_BITS(std::endian, 1, 2) enumerated;
39-
BF_BITS(std::int32_t, 3, 7) integer;
89+
BF_BITS(bool, 0) boolean; // 1 bit at offset 0
90+
BF_BITS(std::memory_order, 1, 3) enumerated; // 3 bits at offset 1
91+
BF_BITSET(bool, 1, 16, 4) bitset; // 16 * 1 bits at offset 4
4092
};
4193
```
4294
43-
Our `nextgen` type's bit-fields work with the exact same syntax as their `legacy` counterparts.
44-
The difference here is that a `nextgen` object can be converted to and from any `uint8_t` type,
45-
no more explicit casting necessary to get the underlying integer type.
46-
One thing to note is that the nextgen fields have absolute bit offsets, as opposed to the legacy
47-
fields (which in turn only have a bit size specifier).
48-
This characteristic of the behavior also means that nextgen's bit-fields can be made to overlap one another.
95+
These fields can be accesses as regular members, however their value is stored inside the containing class's (superclass's) memory. Bit field set elements are accessible via `operator[]`.
96+
97+
I encourage everyone to try it online:
98+
https://godbolt.org/z/bba7a8sTT
99+
100+
### Register bit fields
101+
102+
Let's look at a more advanced use-case, memory-mapped register definition.
103+
We will use the SysTick timer, found in most popular ARM MCUs:
104+
105+
```cpp
106+
#include <bitfilled/bitfilled.hpp>
107+
struct systick {
108+
struct csr : BF_MMREG(std::uint32_t, rw) {
109+
BF_COPY_SUPERCLASS(csr)
110+
BF_MMREGBITS(bool, r, 16) COUNTFLAG;
111+
BF_MMREGBITS(bool, rw, 2) CLKSOURCE;
112+
BF_MMREGBITS(bool, rw, 1) TICKINT;
113+
BF_MMREGBITS(bool, rw, 0) ENABLE;
114+
} CSR;
115+
struct rvr : BF_MMREG(std::uint32_t, rw) {
116+
BF_COPY_SUPERCLASS(rvr)
117+
BF_MMREGBITS(bool, rw, 0, 23) RELOAD; // optional, same as accessing the register itself
118+
} RVR;
119+
struct cvr : BF_MMREG(std::uint32_t, rw) {
120+
BF_COPY_SUPERCLASS(cvr)
121+
BF_MMREGBITS(bool, rw, 0, 23) CURRENT; // any write clears the field and COUNTFLAG to 0
122+
} CVR;
123+
struct calib : BF_MMREG(std::uint32_t, r) {
124+
BF_COPY_SUPERCLASS(calib)
125+
BF_MMREGBITS(bool, r, 31) NOREF;
126+
BF_MMREGBITS(bool, r, 30) SKEW;
127+
BF_MMREGBITS(bool, r, 0, 23) TENMS;
128+
} CALIB;
129+
} & SYSTICK = *reinterpret_cast<volatile systick*>(SysTick_BASE);
130+
```
131+
132+
The code is self-explanatory, and provides an accurate interface to the hardware, by accessing the `SYSTICK` reference. As an example, the `COUNTFLAG` bit is read-only in an otherwise read-write register, which is reflected in its definition, and consequently assigning a value to this member is a compile-time error. The same is true for the `CALIB` register, and all its fields.
133+
134+
A fully functional MM I/O example is available [here][bitfilled-stm32f4],
135+
where the **significant** code size savings are also illustrated.
136+
137+
The project also comes with a [python code generator](tools/svd2mmregmap.py) (draft version),
138+
that let's you create register map definition out of CMSIS SVD files.
49139
50140
## Theory of operation
51141
@@ -70,16 +160,11 @@ but rather whichever member is preceeding them in the encapsulating type layout.
70160
Therefore the encapsulating type must have a preceeding member variable for bit-field use,
71161
and the bitfilled members must be made aware of this member variable's type - this is what the operators are achieving.
72162
Mismatches between the storage member variable and the operators type is impossible to catch at compile time,
73-
therefore it is recommended to use predefined helper base classes such as `bitfilled::host_integer` and `bitfilled::mmreg`,
163+
therefore it is necessary to use predefined helper base classes such as `bitfilled::host_integer` and `bitfilled::mmreg`,
74164
instead of defining the storage member variable and the operators independently.
75165
76-
## Memory mapped registers
77-
78-
This library goes beyond simple bit-field manipulation by providing an accurate and easy-to-use API
79-
for memory-mapped register and bit-field manipulation, including access limitations on all levels,
80-
ensuring safe and optimized use.
81-
82166
[stdbitfield]: https://en.cppreference.com/w/cpp/language/bit_field
83167
[no_unique_address]: https://en.cppreference.com/w/cpp/language/attributes/no_unique_address
84168
[property wiki]: https://en.wikipedia.org/wiki/Property_(programming)
85169
[armcortexmbitband]: https://atadiat.com/en/e-bit-banding-explained-a-feature-of-arm-cortex-m3/
170+
[bitfilled-stm32f4]: https://github.com/IntergatedCircuits/bitfilled-stm32f4

bitfilled/bitfilled/bitband_ops.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ struct bitband
1111
{
1212
/// @brief These bitfield operations use bit-band memory access for single-bit manipulation,
1313
/// as it is implemented on ARM Cortex M3/M4 CPU architectures.
14+
/// @note These operations shall only be used on types that map directly to memory
15+
/// ( @ref host_integer and @ref mmreg )
1416
/// @tparam T
1517
/// @tparam ACCESS
1618
template <typename T, enum access ACCESS>

bitfilled/bitfilled/size.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include <bit>
66
#include <concepts>
77
#include <cstdint>
8-
#include <numeric>
8+
#include <limits>
99
#include <type_traits>
1010

1111
namespace bitfilled

0 commit comments

Comments
 (0)