-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathField.h
208 lines (173 loc) · 6.47 KB
/
Field.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! Register field type implementation.
/**
* @file Field.h
* @author Nicolas Clauvelin (nclauvelin@sendyne.com)
* @copyright Copyright 2010-2022 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.
*/
#ifndef CPPREG_REGISTERFIELD_H
#define CPPREG_REGISTERFIELD_H
#include "AccessPolicy.h"
#include "Internals.h"
#include "Mask.h"
namespace cppreg {
//! Register field implementation.
/**
* @tparam BaseRegister Parent register.
* @tparam width Field width.
* @tparam offset Field offset.
* @tparam P Access policy type.
*
* This data structure provides static methods to deal with field access
* (read, write, set, clear, and toggle). These methods availability depends
* on the access policy (e.g., a read-only field does not implement a
* write method).
* In practice, fields are implemented by deriving from this class to
* create custom types.
*/
template <typename BaseRegister,
FieldWidth field_width,
FieldOffset field_offset,
typename AccessPolicy>
struct Field {
//! Parent register for the field.
using parent_register = BaseRegister; // NOLINT
//! Field data type derived from register data type.
using type = typename parent_register::type; // NOLINT
//! MMIO type.
using MMIO = typename parent_register::MMIO; // NOLINT
//! Field policy.
using policy = AccessPolicy; // NOLINT
//! Field width.
constexpr static auto width = field_width;
//! Field offset.
constexpr static auto offset = field_offset;
//! Field mask.
constexpr static auto mask = make_shifted_mask<type>(width, offset);
//!@{ Helpers for write method selection based on shadow value.
template <type value, typename T>
using if_no_shadow = // NOLINT
typename std::enable_if<!parent_register::shadow::value, T>::type;
template <type value, typename T>
using if_shadow = // NOLINT
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, type, mask, offset>(
parent_register::ro_mem_device());
}
//! Field write value method (no shadow value).
/**
* @param value Value to be written to the field.
*/
template <typename T = type>
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 value method (w/ shadow value).
/**
* @param value Value to be written to the field.
*/
template <typename T = type>
static void write(const if_shadow<type{0}, T> value) noexcept {
// Update shadow 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
// 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);
}
//! Field write constant method (no shadow value).
/**
* @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(if_no_shadow<value, T>* = nullptr) noexcept { // NOLINT
policy::template write<MMIO, type, mask, offset, value>(
parent_register::rw_mem_device());
// 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 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(if_shadow<value, T>* = nullptr) noexcept { // NOLINT
// 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, 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, 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, type, mask>(
parent_register::rw_mem_device());
}
//! Is field set bool method.
/**
* @return `true` if all the bits are set to 1, `false` otherwise.
*/
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() == 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:: field width is larger than parent register size");
static_assert(parent_register::size >= width + offset,
"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