aboutsummaryrefslogtreecommitdiffstats
path: root/firmware/util.h
blob: 05b4aa0787792ae2c4ae878f7fb9305279aaa854 (plain)
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
#ifndef UTIL_H_
#define UTIL_H_

#include "compat.h"

#include <stdint.h>
#include <stdbool.h>
#include <limits.h>
#include <string.h>


/* Return the smaller value of 'a' and 'b'. */
#define min(a, b)	({						\
		__typeof__(a) __amin = (a);				\
		__typeof__(b) __bmin = (b);				\
		(__typeof__(a))(__amin < __bmin ? __amin : __bmin);	\
	})

/* Return the bigger value of 'a' and 'b'. */
#define max(a, b)	({						\
		__typeof__(a) __amax = (a);				\
		__typeof__(b) __bmax = (b);				\
		(__typeof__(a))(__amax > __bmax ? __amax : __bmax);	\
	})

/* Return 'value' clamped inbetween 'min_val' and 'max_val'. */
#define clamp(value, min_val, max_val)		\
	max(min(value, max_val), min_val)

/* Limit an unsigned integer to uint8_t/uint16_t range. */
#define lim_u8(v)	((uint8_t)clamp((v), (__typeof__(v))0, (__typeof__(v))UINT8_MAX))
#define lim_u16(v)	((uint16_t)clamp((v), (__typeof__(v))0, (__typeof__(v))UINT16_MAX))

/* Return the absolute value of 'val' */
#undef abs
#define abs(val)	({			\
		__typeof__(val) __val = (val);	\
		__val >= 0 ? __val : -__val;	\
	})


/* Get the number of elements in a C array. */
#define ARRAY_SIZE(a)		(sizeof(a) / sizeof((a)[0]))

/* Memory barrier. */
#define memory_barrier()	__asm__ __volatile__("" : : : "memory")

/* Do-not-inline function attribute. */
#define noinline		__attribute__((__noinline__))

/* Always-inline function attribute. */
#define alwaysinline		inline __attribute__((__always_inline__))

/* Packed structure. */
#define _packed			__attribute__((__packed__))

/* Build-time assertion.
 * 'cond' must be a compile-time constant.
 * Build will fail, if 'cond' is false.
 */
#define build_assert(cond)	((void)sizeof(char[1 - 2 * !(cond)]))

/* Code flow attributes */
#define noreturn		__attribute__((__noreturn__))
#define _mainfunc		__attribute__((__OS_main__))
#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 5
# define unreachable()		__builtin_unreachable()
#else
# define unreachable()		while (1)
#endif

/* Code section attributes */
#define section_init3		__attribute__((naked, used, section(".init3")))

/* Data section attributes */
#define section_noinit		__attribute__((section(".noinit")))

/* Non-standard integer types. */
typedef __int24		int24_t;
#define int24_t int24_t
typedef __uint24	uint24_t;
#define uint24_t uint24_t


/* Disable interrupts globally. */
static alwaysinline void irq_disable(void)
{
	cli();
	memory_barrier();
}

/* Enable interrupts globally. */
static alwaysinline void irq_enable(void)
{
	memory_barrier();
	sei();
}

/* Save flags and disable interrupts globally. */
static alwaysinline uint8_t irq_disable_save(void)
{
	uint8_t sreg = SREG;
	irq_disable();
	return sreg;
}

/* Restore interrupt flags. */
static alwaysinline void irq_restore(uint8_t sreg_flags)
{
	memory_barrier();
	SREG = sreg_flags;
}

typedef uint16_t le16_t;

/* Convert native endianness to little-endian 16-bit. */
static inline le16_t to_le16(uint16_t x)
{
	/* Architecture is little-endian. */
	return (le16_t)x;
}

/* Convert little-endian 16-bit to native endianness. */
static inline uint16_t from_le16(le16_t x)
{
	/* Architecture is little-endian. */
	return (uint16_t)x;
}

/* Reboot into the bootloader. */
static inline noreturn void enter_bootloader(void)
{
	/* Use the watchdog timer to reboot the system. */
	irq_disable();
	wdt_enable(WDTO_15MS);
	while (1);
	unreachable();
}

/* Port toggle for debugging purposes. */
#define debug_toggle(portname, portbit, delay_us, count)			\
	do {									\
		uint8_t _dt_i;							\
		DDR##portname |= 1u << (portbit);				\
		for (_dt_i = 0; _dt_i < (uint8_t)(count); _dt_i++) {		\
			PIN##portname |= 1u << (portbit);			\
			_delay_us(delay_us);					\
		}								\
	} while (0)

#endif /* UTIL_H_ */
bues.ch cgit interface