Skip to content

Commit e5c0612

Browse files
committed
Further MISRA compliance fixes and moving globals into namespaces.
What's Wrong: * Nothing should be in the top level namespace except a few things. * MISRA deviations must be spelled out and justified. How Was it Fixed (if not obvious): * Move some functions to various namespaces. * Add comments to fix MISRA deviations. What side effects does this have (could be none): * None Which builds did you run to make sure they build? [X] arm-none-eabi-gcc Cortex M4 [X] arm-none-eabi-gcc Cortex M7 [ ] (Apple) Native Clang [ ] (Apple) Homebrew GCC [X] (Apple) Homebrew LLVM How Do We Know and Can Show It's Fixed: * Builds Which Unittest Series did you Check? [ ] (Apple) Native Clang [ ] (Apple) Homebrew GCC [X] (Apple) Homebrew LLVM Did this affect any on-target builds? If so which were tested? [ ] STM32F407VE board [ ] STM32H753ZI board
1 parent 7e72cde commit e5c0612

33 files changed

Lines changed: 341 additions & 220 deletions

File tree

boards/netduinoplus2/source/BoardContext.cpp

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -379,36 +379,36 @@ void nvic(void) {
379379
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
380380

381381
// 0 is the highest priority
382-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::Timer2), 1);
383-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::Timer2));
382+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::Timer2), 1);
383+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::Timer2));
384384

385-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream0)); // SPI1_RX
386-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream3)); // SPI1_TX
387-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream0), 2); // SPI1_RX
388-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream3), 2); // SPI1_TX
385+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream0)); // SPI1_RX
386+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream3)); // SPI1_TX
387+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream0), 2); // SPI1_RX
388+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream3), 2); // SPI1_TX
389389

390-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream2)); // SPI2_RX
391-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream7)); // SPI2_TX
392-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream2), 2); // SPI2_RX
393-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream7), 2); // SPI2_TX
390+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream2)); // SPI2_RX
391+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream7)); // SPI2_TX
392+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream2), 2); // SPI2_RX
393+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream7), 2); // SPI2_TX
394394

395395
// enable the I2C1 event and error interrupts
396-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Event));
397-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Event), 3);
398-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Error));
399-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Error), 3);
396+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Event));
397+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Event), 3);
398+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Error));
399+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Error), 3);
400400

401401
// enable the SPI1 interrupt and the DMA interrupts for SPI1_RX and SPI1_TX
402-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::SerialPeripheralInterface1));
403-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::SerialPeripheralInterface1), 3);
402+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::SerialPeripheralInterface1));
403+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::SerialPeripheralInterface1), 3);
404404

405405
// enable the SPI2 interrupt and the DMA interrupts for SPI2_RX and SPI2_TX
406-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::SerialPeripheralInterface2));
407-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::SerialPeripheralInterface2), 4);
406+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::SerialPeripheralInterface2));
407+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::SerialPeripheralInterface2), 4);
408408

409409
// enable the USART1 interrupt
410-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::UniversalSynchronousAsynchronousReceiverTransmitter1));
411-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::UniversalSynchronousAsynchronousReceiverTransmitter1), 5);
410+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::UniversalSynchronousAsynchronousReceiverTransmitter1));
411+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::UniversalSynchronousAsynchronousReceiverTransmitter1), 5);
412412
}
413413

414414
} // namespace initialize

boards/stm32_f4ve_v2/source/BoardContext.cpp

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -479,36 +479,36 @@ void nvic(void) {
479479
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
480480

481481
// 0 is the highest priority
482-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::Timer2), 1);
483-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::Timer2));
482+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::Timer2), 1);
483+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::Timer2));
484484

485-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream0)); // SPI1_RX
486-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream3)); // SPI1_TX
487-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream0), 2); // SPI1_RX
488-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream3), 2); // SPI1_TX
485+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream0)); // SPI1_RX
486+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream3)); // SPI1_TX
487+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream0), 2); // SPI1_RX
488+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream3), 2); // SPI1_TX
489489

490-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream2)); // SPI2_RX
491-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream7)); // SPI2_TX
492-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream2), 2); // SPI2_RX
493-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream7), 2); // SPI2_TX
490+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream2)); // SPI2_RX
491+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream7)); // SPI2_TX
492+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream2), 2); // SPI2_RX
493+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::DirectMemoryAccess2Stream7), 2); // SPI2_TX
494494

495495
// enable the I2C1 event and error interrupts
496-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Event));
497-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Event), 3);
498-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Error));
499-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Error), 3);
496+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Event));
497+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Event), 3);
498+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Error));
499+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::InterIntegratedCircuit1_Error), 3);
500500

501501
// enable the SPI1 interrupt and the DMA interrupts for SPI1_RX and SPI1_TX
502-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::SerialPeripheralInterface1));
503-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::SerialPeripheralInterface1), 3);
502+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::SerialPeripheralInterface1));
503+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::SerialPeripheralInterface1), 3);
504504

505505
// enable the SPI2 interrupt and the DMA interrupts for SPI2_RX and SPI2_TX
506-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::SerialPeripheralInterface2));
507-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::SerialPeripheralInterface2), 4);
506+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::SerialPeripheralInterface2));
507+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::SerialPeripheralInterface2), 4);
508508

509509
// enable the USART1 interrupt
510-
cortex::nvic::Enable(to_underlying(stm32::InterruptRequest::UniversalSynchronousAsynchronousReceiverTransmitter1));
511-
cortex::nvic::Prioritize(to_underlying(stm32::InterruptRequest::UniversalSynchronousAsynchronousReceiverTransmitter1), 5);
510+
cortex::nvic::Enable(polyfill::to_underlying(stm32::InterruptRequest::UniversalSynchronousAsynchronousReceiverTransmitter1));
511+
cortex::nvic::Prioritize(polyfill::to_underlying(stm32::InterruptRequest::UniversalSynchronousAsynchronousReceiverTransmitter1), 5);
512512
}
513513

514514
} // namespace initialize

include/debug.hpp

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,103 @@ namespace debug {
1212

1313
/// @brief Used to compare two sizes at compile time.
1414
/// Typically this will force a compiler to evaluate the expression at compile time. Used in place of the availability of consteval.
15+
/// @tparam A The first value to compare
16+
/// @tparam B The second value to compare
17+
/// @code
18+
/// using Check = ConstexprValueCompare<sizeof(MyType), 4>;
19+
/// @endcode
1520
template <std::size_t A, std::size_t B>
1621
class ConstexprValueCompare final {
1722
public:
1823
static_assert(A == B, "Values must match!");
24+
static constexpr bool value = true; // for SFINAE purposes
1925
};
2026

2127
/// @brief Used to compare two values at compile time to see if A is evenly divisible by B.
22-
/// Typically this will force a compiler to evaluate the expression at compile time. Used in place of the availability of consteval.
28+
/// Use in a manner to force a compiler to evaluate the expression at compile time.
29+
/// @tparam A The first value to compare
30+
/// @tparam B The second value to compare
2331
template <std::size_t A, std::size_t B>
2432
class ConstexprValueDivisible final {
2533
public:
2634
static_assert((A % B) == 0, "Second value must evenly divide first value!");
35+
static constexpr bool value = true; // for SFINAE purposes
36+
};
37+
38+
/// @brief Used to compare two values at compile time to see if A is less than B.
39+
/// Use in a manner to force a compiler to evaluate the expression at compile time.
40+
/// @tparam A The first value to compare
41+
/// @tparam B The second value to compare
42+
template <std::size_t A, std::size_t B>
43+
class ConstexprValueLessThan final {
44+
public:
45+
static_assert(A < B, "First value must be less than second value!");
46+
static constexpr bool value = true; // for SFINAE purposes
47+
};
48+
49+
/// @brief Used to compare two values at compile time to see if A is less than or equal to B.
50+
/// Use in a manner to force a compiler to evaluate the expression at compile time.
51+
/// @tparam A The first value to compare
52+
/// @tparam B The second value to compare
53+
template <std::size_t A, std::size_t B>
54+
class ConstexprValueLessEqual final {
55+
public:
56+
static_assert(A <= B, "First value must be less than or equal to second value!");
57+
static constexpr bool value = true; // for SFINAE purposes
58+
};
59+
60+
/// @brief Used to determine if a value is a power of two at compile time.
61+
/// Use in a manner to force a compiler to evaluate the expression at compile time.
62+
/// @tparam N The value to check if it is a power of two
63+
template <std::size_t N>
64+
class ConstexprIsPowerOfTwo final {
65+
public:
66+
static_assert(N > 0 && (N & (N - 1)) == 0, "Value must be a power of two!");
67+
static constexpr bool value = true; // for SFINAE purposes
68+
};
69+
70+
/// @brief Used to determine if a value is within a specified range at compile time.
71+
/// Use in a manner to force a compiler to evaluate the expression at compile time.
72+
/// @tparam Value The value to check if it is within the range
73+
/// @tparam Min The minimum value of the range (inclusive)
74+
/// @tparam Max The maximum value of the range (inclusive)
75+
template <std::size_t Value, std::size_t Min, std::size_t Max>
76+
class ConstexprValueInRange final {
77+
public:
78+
static_assert(Value >= Min && Value <= Max, "Value must be within specified range!");
79+
static constexpr bool value = true; // for SFINAE purposes
80+
};
81+
82+
/// @brief Force a compile error showing the type T
83+
/// @tparam T The type to show in the compile error
84+
/// Usage: TypeDebugger<decltype(my_var)> show_type;
85+
template <typename T>
86+
class TypeDebugger; // Intentionally no definition - will cause compile error showing T
87+
88+
/// @brief Show size of a type at compile time (will fail, but show size in error)
89+
/// @tparam T The type to show the size of
90+
/// Usage: TypeSizeDebugger<my_type> show_size;
91+
template <typename T, std::size_t Size = sizeof(T)>
92+
class TypeSizeDebugger; // Intentionally no definition
93+
94+
/// @brief Verify type sizes match expectations
95+
/// @tparam T The type to check the size of
96+
/// @tparam ExpectedSize The expected size of the type in bytes
97+
template <typename T, std::size_t ExpectedSize>
98+
class TypeSizeValidator final {
99+
public:
100+
static_assert(sizeof(T) == ExpectedSize, "Type size does not match expected size!");
101+
static constexpr std::size_t value = sizeof(T);
102+
};
103+
104+
/// @brief Verify type alignment matches expectations
105+
/// @tparam T The type to check the alignment of
106+
/// @tparam ExpectedAlignment The expected alignment of the type in bytes
107+
template <typename T, std::size_t ExpectedAlignment>
108+
class TypeAlignmentValidator final {
109+
public:
110+
static_assert(alignof(T) == ExpectedAlignment, "Type alignment does not match expected alignment!");
111+
static constexpr std::size_t value = alignof(T);
27112
};
28113

29114
/// The type used to store debug masks.
@@ -107,18 +192,35 @@ enum class MaskType : StorageType {
107192
All = ~0U, ///< Used to indicate that the message will be grouped with All operations.
108193
};
109194

195+
/// @brief Bitwise operators for MaskType to allow for easy combination and checking of masks.
196+
/// @param lhs The left-hand side of the bitwise operation, which is a StorageType value.
197+
/// @param rhs The right-hand side of the bitwise operation, which is a MaskType
198+
/// @return A boolean value indicating whether the bitwise AND of the StorageType and MaskType is non-zero.
110199
constexpr bool operator&(StorageType lhs, MaskType rhs) {
111200
return (lhs & static_cast<StorageType>(rhs)) != 0;
112201
}
202+
203+
/// @brief Bitwise operators for MaskType to allow for easy combination and checking of masks.
204+
/// @param lhs The left-hand side of the bitwise operation, which is a MaskType value.
205+
/// @param rhs The right-hand side of the bitwise operation, which is a StorageType value.
206+
/// @return A boolean value indicating whether the bitwise AND of the MaskType and StorageType is non-zero.
113207
constexpr bool operator&(MaskType lhs, StorageType rhs) {
114208
return (static_cast<StorageType>(lhs) & rhs) != 0;
115209
}
210+
211+
/// @brief Bitwise operators for MaskType to allow for easy combination of MaskType values.
212+
/// @param lhs The left-hand side of the bitwise operation, which is a MaskType value.
213+
/// @param rhs The right-hand side of the bitwise operation, which is a MaskType value.
214+
/// @return A MaskType value that is the result of the bitwise OR of the two MaskType values. This "value" WILL NO LONGER be a valid MaskType, but can
215+
/// be used to nest further bitwise operations to combine multiple MaskType values together.
116216
constexpr MaskType operator|(MaskType lhs, MaskType rhs) {
117217
return static_cast<MaskType>(static_cast<StorageType>(lhs) | static_cast<StorageType>(rhs));
118218
}
119219

120220
#if not defined(DEBUG_MASK)
121-
#define DEBUG_MASK static_cast<StorageType>(0x7ULL)
221+
/// @brief The mask used to determine which debug messages are enabled. This should be defined by the user before including this header.
222+
/// The default value does not enable all messages.
223+
#define DEBUG_MASK static_cast<StorageType>(0x0000'0002'0000'0007ULL)
122224
#endif
123225

124226
constexpr static bool Fatal{DEBUG_MASK & MaskType::Fatal};

include/polyfill.hpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,35 @@
99
#include <type_traits>
1010
#include <utility>
1111

12-
/// Return the number of elements in a C style array
12+
/// Return the number of elements in a C style array, not their byte size, but
13+
/// the dimensionality of the array. This is a replacement for older macros which
14+
/// divides the sizeof of the array by the sizeof of the first element, which
15+
/// is not safe for all types and can lead to errors if the array is passed to a
16+
/// function as a pointer. This function will return the correct number of elements
17+
/// in the array regardless of the type of the elements or how the array is passed around.
18+
/// @TODO Rename to countof?
1319
template <typename TYPE, std::size_t N>
1420
constexpr std::size_t dimof(TYPE const (&)[N]) {
1521
return N;
1622
}
1723

24+
/// @brief The polyfill namespace contains implementations of features that are not available in the current version of C++. These features may be
25+
/// added to the standard library in future versions of C++, but for now they are implemented here for use in this project.
26+
namespace polyfill {
27+
1828
/// Casts an enumeration class value to it's underlying type and returns the value as that type.
1929
/// @tparam ENUM_TYPE The enumeration class type.
30+
/// @warning Intentional violation of MISRA: This function will convert an enumeration to the underlying type of the enumeration. This will always be
31+
/// safe so long as a valid enumeration is passed in. However reversing the process by casting the underlying type back to the enumeration type is not
32+
/// guaranteed to be safe, as the underlying type may not be a value in the enumeration type.
2033
template <typename ENUM_TYPE>
2134
constexpr typename std::underlying_type<ENUM_TYPE>::type to_underlying(ENUM_TYPE e) {
2235
static_assert(std::is_enum<ENUM_TYPE>::value, "Must be an enumeration class");
2336
return static_cast<typename std::underlying_type<ENUM_TYPE>::type>(e);
2437
}
2538

2639
/// Ensures that the value is a power of two
27-
/// @tparam TYPE
40+
/// @tparam TYPE The type of the value to check. Must be an integral type.
2841
/// @return True if the value is a power of two, false otherwise.
2942
template <typename TYPE>
3043
constexpr bool is_power_of_two(TYPE value) {
@@ -34,8 +47,6 @@ constexpr bool is_power_of_two(TYPE value) {
3447
static_assert(is_power_of_two(1U << 9U), "Must be a power of two");
3548
static_assert(not is_power_of_two(42U), "Must NOT be a power of two");
3649

37-
namespace polyfill {
38-
3950
/// Recursively computes a log2 input value.
4051
/// @param n The value to compute the log2 of
4152
/// @return Returns the log2 value of the input
@@ -46,12 +57,13 @@ constexpr std::uint8_t log2(TYPE value) {
4657
}
4758
static_assert(log2(1U << 1U) == 1U, "Must be this value exactly");
4859
static_assert(log2(1U << 3U) == 3U, "Must be this value exactly");
60+
static_assert(log2(1U << 19U) == 19U, "Must be this value exactly");
4961

5062
/// @brief Swaps the values of two typed variables. The intermediate object will live on the stack and be destroyed upon return.
5163
/// @tparam TYPE The type of the variables to swap.
5264
/// @param a The first variable
5365
/// @param b The second variable
54-
/// @requires
66+
/// @requires The type must be move assignable.
5567
template <typename TYPE>
5668
constexpr void swap(TYPE& lhs, TYPE& rhs) {
5769
static_assert(std::is_move_assignable<TYPE>::value, "Must be move assignable");

0 commit comments

Comments
 (0)