-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathunittrace.h
138 lines (103 loc) · 5.75 KB
/
unittrace.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
/*
* unittrace.h
*
* Created: 29.10.2018 20:36:59
* Author: Dennis aka nqtronix (github.com/nqtronix)
*
* Description:
* Testing is an important part of writing code, especially if the code is meant to be re-used for
* other projects. On small MCUs testing can be quite tricky as the memory is limited and errors
* can't be easily reported with `printf()` or similiar.
*
* unittrace provides basic testing functionality and is designed with the limits of MCUs in mind.
* It runs directly on the hardware and thus can catch errors other test software can't. Fetch the
* results through a debugger or with a function.
*
* See included 'README.md' for additional information.
*/
#ifndef UNITTRACE_H_
#define UNITTRACE_H_
#include <stdint.h> // required for data types (uint8_t, uint16_t, ...)
#include "utility/macros/com/macro_type.h"
//////////////////////////////////////////////////////////////////////////
// Check requirements
//////////////////////////////////////////////////////////////////////////
#ifndef __GNUC__
#error unittrace.h requires "always inline functions", "local labels" and "typeof" offered by a GNU C/ GCC compiler!
#endif
// Workarounds are possible. See comments in the source code.
//////////////////////////////////////////////////////////////////////////
// General Info
//////////////////////////////////////////////////////////////////////////
// version numbering is based on "Semantic Versioning 2.0.0" (semver.org)
#define UNITTRACE_VERSION_MAJOR 0
#define UNITTRACE_VERSION_MINOR 1
#define UNITTRACE_VERSION_PATCH 0
#define UNITTRACE_VERSION_SUFFIX
#define UNITTRACE_VERSION_META
// For all development versions (0.x.x) the patch version is increased whenever a function was renamed
//////////////////////////////////////////////////////////////////////////
// Settings (This section can be modified by the user)
//////////////////////////////////////////////////////////////////////////
// Define the amount of failed events to log in detail. The same event can be logged multiple times.
// Each event required 4 byte of RAM. There is no upper limit except the device's SRAM. The compiler
// will automatically choose the smallest possible data type for most efficient operation.
#define UNITTRACE_LIST_SIZE 8
// Especially on small embedded systems it is best to choose the smallest data type for every
// variable. This macro selects the smallest possible type based on 'UNITTRACE_LIST_SIZE'. Change
// to 'uint8_t', 'uint16_t' or 'uint32_t' to manually overwrite this feature.
//
// extra '#define UNITTRACE_COUNTER_TYPE' prevents VAssistX from marking 'ut_cnt_t' red
#define UNITTRACE_COUNTER_TYPE _type_min(UNITTRACE_LIST_SIZE)
typedef UNITTRACE_COUNTER_TYPE ut_cnt_t;
// If 'UNITTRACE_USE_EXT_COUNTER' is defined, failed asserts will be counted till the maximum value,
// even if no space in the array is available. This might be helpful for some test cases, but does
// increase the assert execution time slightly.
#define UNITTRACE_USE_EXT_COUNTER
//////////////////////////////////////////////////////////////////////////
// Global Variables
//////////////////////////////////////////////////////////////////////////
// I couldn't figure out how to pass a void array half-way decently, so both variables are
// now global instead. This is not "how it's don", I know, but unittrace is for testing only anyway.
// WATCH THESE VARIABLES IN THE DEBUGGER
// contains all traced events
volatile void* unittrace_array[UNITTRACE_LIST_SIZE];
// amount of traced events
ut_cnt_t unittrace_count;
//////////////////////////////////////////////////////////////////////////
// Function Declarations
//////////////////////////////////////////////////////////////////////////
// 'ut_assert' is used to validate any given condition. If the condition is false (cond == 0), the
// assembler instruction address is stored to an array (unless it is full). This array can be read
// during debugging.
// If your compiler does not support inline functions, use the macro 'UT_ASSERT(cond)'.
static inline void ut_assert(uint8_t cond) __attribute__((always_inline));
// If you manually want to write a value to the array, you can use this macro. Usually the first few
// instructions are generated by the compiler and their addresses will never naturally appear in
// said array. Thus you may abuse low values for your own, custom purpose (<50 should be safe on
// AVR8 MCUs)
void ut_assert_manual(void* addr, uint8_t cond);
//////////////////////////////////////////////////////////////////////////
// Macros
//////////////////////////////////////////////////////////////////////////
// alternative implementation of the macro 'ut_assert(cond)'
#define UT_ASSERT(cond) do{__label__ lcl; lcl: ut_assert_manual(&&lcl, cond);}while(0)
// places a single 'nop' instruction. This will not be optimized an is ideal to place a breakpoint.
// Unlike an inline function a macro is taken "as is". In this case it make debugging a little
// easier, so support for the equivalent inline function has been removed.
#define UT_BREAK() asm("nop")
//////////////////////////////////////////////////////////////////////////
// Inline functions
//////////////////////////////////////////////////////////////////////////
// Inline functions MUST be defined in the .h, not in the .c file to work correctly!
// This function MUST be always inline to create an individual label for each call. In GCC inline
// functions declared with '__attribute__((always_inline))' are as fast as the respective macro.
// '__label__' creates a local label to prevent a littered namespace. Alternatively you could use
// the build-in macro '__COUNTER__' and concat '##' to generate unique labels.
static inline void ut_assert(uint8_t cond)
{
__label__ local_label;
local_label:
ut_assert_manual(&&local_label, cond);
}
#endif /* UNITTRACE_H_ */