forked from ARMmbed/mbed-os
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmbed_wait_api_no_rtos.c
138 lines (120 loc) · 4.58 KB
/
mbed_wait_api_no_rtos.c
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
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cmsis.h"
#include "platform/mbed_toolchain.h"
#include "platform/mbed_wait_api.h"
#include "hal/lp_ticker_api.h"
#include "hal/us_ticker_api.h"
#include "hal/ticker_api.h"
// This wait_us is used by both RTOS and non-RTOS builds
/* The actual time delay may be 1 less usec */
#if DEVICE_USTICKER
#if defined US_TICKER_PERIOD_NUM
/* Real definition for binary compatibility with binaries not using the new macro */
void (wait_us)(int us)
{
wait_us(us);
}
/* External definition for the inline function */
extern void _wait_us_inline(unsigned int us);
void _wait_us_ticks(uint32_t ticks)
{
const uint32_t start = us_ticker_read();
while (((us_ticker_read() - start) & US_TICKER_MASK) < ticks);
}
void _wait_us_generic(unsigned int us)
#else
void wait_us(int us)
#endif
{
// Generic version using full ticker, allowing for initialization, scaling and widening of timer
const ticker_data_t *const ticker = get_us_ticker_data();
const uint32_t start = ticker_read(ticker);
while ((ticker_read(ticker) - start) < (uint32_t)us);
}
#else // DEVICE_USTICKER
// fallback to wait_ns for targets without usticker
void wait_us(int us)
{
while (us > 1024) {
us -= 1024;
wait_ns(1024000);
}
if (us > 0) {
wait_ns(us * 1000);
}
}
#endif // DEVICE_USTICKER
// This wait_ns is used by both RTOS and non-RTOS builds
#ifdef __CORTEX_M
#if (__CORTEX_M == 0 && !defined __CM0PLUS_REV) || __CORTEX_M == 1
// Cortex-M0 and Cortex-M1 take 6 cycles per iteration - SUBS = 1, 2xNOP = 2, BCS = 3
#define LOOP_SCALER 6000
#elif (__CORTEX_M == 0 && defined __CM0PLUS_REV) || __CORTEX_M == 3 || __CORTEX_M == 4 || \
__CORTEX_M == 23
// Cortex-M0+, M3, M4 and M23 take 5 cycles per iteration - SUBS = 1, 2xNOP = 2, BCS = 2
#define LOOP_SCALER 5000
#elif __CORTEX_M == 33
// Cortex-M33 can dual issue for 3 cycles per iteration (SUB,NOP) = 1, (NOP,BCS) = 2
#define LOOP_SCALER 3000
#elif __CORTEX_M == 7
// Cortex-M7 manages to dual-issue for 2 cycles per iteration (SUB,NOP) = 1, (NOP,BCS) = 1
// (The NOPs were added to stabilise this - with just the SUB and BCS, it seems that the
// M7 sometimes takes 1 cycle, sometimes 2, possibly depending on alignment)
#define LOOP_SCALER 2000
#endif
#elif defined __CORTEX_A
#if __CORTEX_A == 9 || __CORTEX_A == 5
// Cortex-A9 can dual issue for 3 cycles per iteration (SUB,NOP) = 1, (NOP,BCS) = 2
#define LOOP_SCALER 3000
#endif
#endif
/* We only define the function if we've identified the CPU. If we haven't,
* rather than a compile-time error, leave it undefined, rather than faulting
* with an immediate #error. This leaves the door open to non-ARM
* builds with or people providing substitutes for other CPUs, and only if
* needed.
*/
#ifdef LOOP_SCALER
/* Timing seems to depend on alignment, and toolchains do not support aligning
* functions well. So sidestep that by hand-assembling the code. Also avoids
* the hassle of handling multiple toolchains with different assembler
* syntax.
*/
MBED_ALIGN(16)
static const uint16_t delay_loop_code[] = {
0x1E40, // SUBS R0,R0,#1
0xBF00, // NOP
0xBF00, // NOP
0xD2FB, // BCS .-3 (0x00 would be .+2, so 0xFB = -5 = .-3)
0x4770 // BX LR
};
/* Take the address of the code, set LSB to indicate Thumb, and cast to void() function pointer */
#define delay_loop ((void(*)()) ((uintptr_t) delay_loop_code + 1))
/* Some targets may not provide zero-wait-state flash performance. Export this function
* to be overridable for targets to provide more accurate implementation like locating
* 'delay_loop_code' in SRAM. */
MBED_WEAK void wait_ns(unsigned int ns)
{
uint32_t cycles_per_us = SystemCoreClock / 1000000;
// Note that this very calculation, plus call overhead, will take multiple
// cycles. Could well be 100ns on its own... So round down here, startup is
// worth at least one loop iteration.
uint32_t count = (cycles_per_us * ns) / LOOP_SCALER;
delay_loop(count);
}
#endif // LOOP_SCALER