Skip to content

Commit 99af25c

Browse files
Earle F. Philhower, IIIearlephilhower
Earle F. Philhower, III
authored andcommittedJan 5, 2019
Move to a lockless waveform generator
Make the waveform generator lockless by doing all dangerous structure updates in the interrupt handler. start/stopWaveform set a flag and cause an interrupt. They wait for the interrupt to complete and clear those flags before returning. Also rework the Waveform[] array to be lockless, results in ~48b additonal heap. About 40b of IRAM additional code generated vs. non-NMI. Tweak parameters to make more accurate timing at 80 and 160 MHz.
1 parent c43b321 commit 99af25c

File tree

1 file changed

+114
-145
lines changed

1 file changed

+114
-145
lines changed
 

‎cores/esp8266/core_esp8266_waveform.c

+114-145
Original file line numberDiff line numberDiff line change
@@ -41,82 +41,41 @@
4141
#include "ets_sys.h"
4242
#include "core_esp8266_waveform.h"
4343

44-
// Need speed, not size, here
45-
#pragma GCC optimize ("O2")
46-
4744
// Maximum delay between IRQs
4845
#define MAXIRQUS (10000)
4946

5047
// If the cycles from now to an event are below this value, perform it anyway since IRQs take longer than this
5148
#define CYCLES_FLUFF (100)
5249

53-
// Macro to get count of predefined array elements
54-
#define countof(a) ((size_t)(sizeof(a)/sizeof(a[0])))
55-
56-
// Set/clear *any* GPIO
57-
#define SetGPIOPin(a) do { if (a < 16) { GPOS |= (1<<a); } else { GP16O |= 1; } } while (0)
58-
#define ClearGPIOPin(a) do { if (a < 16) { GPOC |= (1<<a); } else { GP16O &= ~1; } } while (0)
59-
// Set/clear GPIO 0-15
50+
// Set/clear GPIO 0-15 by bitmask
6051
#define SetGPIO(a) do { GPOS = a; } while (0)
6152
#define ClearGPIO(a) do { GPOC = a; } while (0)
6253

54+
// Convert from us to ESP8266 clocks
55+
#define MicrosecondsToCycles(m) (clockCyclesPerMicrosecond() * (m))
56+
6357
// Waveform generator can create tones, PWM, and servos
6458
typedef struct {
65-
uint32_t nextServiceCycle; // ESP cycle timer when a transition required
66-
uint32_t timeLeftCycles; // For time-limited waveform, how many ESP cycles left
67-
const uint16_t gpioMask; // Mask instead of value to speed IRQ loop
68-
const uint16_t gpio16Mask; // Mask instead of value to speed IRQ loop
69-
unsigned state : 1; // Current state of this pin
70-
unsigned nextTimeHighCycles : 31; // Copy over low->high to keep smooth waveform
71-
unsigned enabled : 1; // Is this GPIO generating a waveform?
72-
unsigned nextTimeLowCycles : 31; // Copy over high->low to keep smooth waveform
59+
uint32_t nextServiceCycle; // ESP cycle timer when a transition required
60+
uint32_t timeLeftCycles; // For time-limited waveform, how many ESP cycles left
61+
uint32_t nextTimeHighCycles; // Copy over low->high to keep smooth waveform
62+
uint32_t nextTimeLowCycles; // Copy over high->low to keep smooth waveform
7363
} Waveform;
7464

7565
// These can be accessed in interrupts, so ensure to bracket access with SEI/CLI
76-
static Waveform waveform[] = {
77-
{0, 0, 1<<0, 0, 0, 0, 0, 0}, // GPIO0
78-
{0, 0, 1<<1, 0, 0, 0, 0, 0}, // GPIO1
79-
{0, 0, 1<<2, 0, 0, 0, 0, 0},
80-
{0, 0, 1<<3, 0, 0, 0, 0, 0},
81-
{0, 0, 1<<4, 0, 0, 0, 0, 0},
82-
{0, 0, 1<<5, 0, 0, 0, 0, 0},
83-
// GPIOS 6-8 not allowed, used for flash
84-
// GPIO 9 and 10 only allowed in 2-bit flash mode
85-
#if !isFlashInterfacePin(9)
86-
{0, 0, 1<<9, 0, 0, 0, 0, 0},
87-
{0, 0, 1<<10, 0, 0, 0, 0, 0},
88-
#endif
89-
// GPIO 11 not allowed, used for flash
90-
{0, 0, 1<<12, 0, 0, 0, 0, 0},
91-
{0, 0, 1<<13, 0, 0, 0, 0, 0},
92-
{0, 0, 1<<14, 0, 0, 0, 0, 0},
93-
{0, 0, 1<<15, 0, 0, 0, 0, 0},
94-
{0, 0, 0, 1, 0, 0, 0, 0} // GPIO16
95-
};
66+
static Waveform waveform[17]; // State of all possible pins
67+
static volatile uint32_t waveformState = 0; // Is the pin high or low
68+
static volatile uint32_t waveformEnabled = 0; // Is it actively running
9669

97-
static uint32_t (*timer1CB)() = NULL;;
70+
// Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine
71+
static volatile uint32_t waveformToEnable = 0; // Message from startWaveform to IRQ to actuall begin a wvf
72+
static volatile uint32_t waveformToDisable = 0; // Message from startWaveform to IRQ to actuall begin a wvf
9873

74+
static uint32_t (*timer1CB)() = NULL;
9975

100-
// Helper functions
101-
static inline ICACHE_RAM_ATTR uint32_t MicrosecondsToCycles(uint32_t microseconds) {
102-
return clockCyclesPerMicrosecond() * microseconds;
103-
}
10476

105-
static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) {
106-
if (a < b) {
107-
return a;
108-
}
109-
return b;
110-
}
111-
112-
static inline ICACHE_RAM_ATTR void ReloadTimer(uint32_t a) {
113-
// Below a threshold you actually miss the edge IRQ, so ensure enough time
114-
if (a > 32) {
115-
timer1_write(a);
116-
} else {
117-
timer1_write(32);
118-
}
119-
}
77+
// Non-speed critical bits
78+
#pragma GCC optimize ("Os")
12079

12180
static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() {
12281
uint32_t ccount;
@@ -126,7 +85,7 @@ static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() {
12685

12786
// Interrupt on/off control
12887
static ICACHE_RAM_ATTR void timer1Interrupt();
129-
static uint8_t timerRunning = false;
88+
static bool timerRunning = false;
13089
static uint32_t lastCycleCount = 0; // Last ESP cycle counter on running the interrupt routine
13190

13291
static void initTimer() {
@@ -150,122 +109,129 @@ void setTimer1Callback(uint32_t (*fn)()) {
150109
timer1CB = fn;
151110
if (!timerRunning && fn) {
152111
initTimer();
153-
} else if (timerRunning && !fn) {
154-
int cnt = 0;
155-
for (size_t i = 0; i < countof(waveform); i++) {
156-
cnt += waveform[i].enabled ? 1 : 0;
157-
}
158-
if (!cnt) {
159-
deinitTimer();
160-
}
112+
timer1_write(MicrosecondsToCycles(1)); // Cause an interrupt post-haste
113+
} else if (timerRunning && !fn && !waveformEnabled) {
114+
deinitTimer();
161115
}
162-
ReloadTimer(MicrosecondsToCycles(1)); // Cause an interrupt post-haste
163116
}
164117

165118
// Start up a waveform on a pin, or change the current one. Will change to the new
166119
// waveform smoothly on next low->high transition. For immediate change, stopWaveform()
167120
// first, then it will immediately begin.
168121
int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) {
169-
Waveform *wave = NULL;
170-
for (size_t i = 0; i < countof(waveform); i++) {
171-
if (((pin == 16) && waveform[i].gpio16Mask==1) || ((pin != 16) && (waveform[i].gpioMask == 1<<pin))) {
172-
wave = (Waveform*) & (waveform[i]);
173-
break;
174-
}
175-
}
176-
if (!wave) {
122+
if ((pin > 16) || isFlashInterfacePin(pin)) {
177123
return false;
178124
}
179-
180-
// To safely update the packed bitfields we need to stop interrupts while setting them as we could
181-
// get an IRQ in the middle of a multi-instruction mask-and-set required to change them which would
182-
// then cause an IRQ update of these values (.enabled only, for now) to be lost.
183-
ets_intr_lock();
184-
185-
wave->nextTimeHighCycles = MicrosecondsToCycles(timeHighUS) - 70; // Take out some time for IRQ codepath
186-
wave->nextTimeLowCycles = MicrosecondsToCycles(timeLowUS) - 70; // Take out some time for IRQ codepath
125+
Waveform *wave = &waveform[pin];
126+
#if F_CPU == 160000000
127+
wave->nextTimeHighCycles = MicrosecondsToCycles(timeHighUS) - 140; // Take out some time for IRQ codepath
128+
wave->nextTimeLowCycles = MicrosecondsToCycles(timeLowUS) - 140; // Take out some time for IRQ codepath
129+
#else
130+
wave->nextTimeHighCycles = MicrosecondsToCycles(timeHighUS) > 280 ? MicrosecondsToCycles(timeHighUS) - 280 : MicrosecondsToCycles(timeHighUS); // Take out some time for IRQ codepath
131+
wave->nextTimeLowCycles = MicrosecondsToCycles(timeLowUS) > 280 ? MicrosecondsToCycles(timeLowUS)- 280 : MicrosecondsToCycles(timeLowUS); // Take out some time for IRQ codepath
132+
#endif
187133
wave->timeLeftCycles = MicrosecondsToCycles(runTimeUS);
188-
if (!wave->enabled) {
189-
wave->state = 0;
134+
uint32_t mask = 1<<pin;
135+
if (!waveformEnabled && mask) {
190136
// Actually set the pin high or low in the IRQ service to guarantee times
191137
wave->nextServiceCycle = GetCycleCount() + MicrosecondsToCycles(1);
192-
wave->enabled = 1;
138+
waveformToEnable |= mask;
193139
if (!timerRunning) {
194140
initTimer();
195141
}
196-
ReloadTimer(MicrosecondsToCycles(1)); // Cause an interrupt post-haste
142+
timer1_write(MicrosecondsToCycles(1)); // Cause an interrupt post-haste
143+
while (waveformToEnable) {
144+
delay(1); // Wait for waveform to update
145+
}
197146
}
198147

199-
// Re-enable interrupts here since we're done with the update
200-
ets_intr_unlock();
201-
202148
return true;
203149
}
204150

151+
// Speed critical bits
152+
#pragma GCC optimize ("O2")
153+
// Normally would not want two copies like this, but due to different
154+
// optimization levels the inline attribute gets lost if we try the
155+
// other version.
156+
157+
static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ() {
158+
uint32_t ccount;
159+
__asm__ __volatile__("esync; rsr %0,ccount":"=a"(ccount));
160+
return ccount;
161+
}
162+
163+
static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) {
164+
if (a < b) {
165+
return a;
166+
}
167+
return b;
168+
}
169+
205170
// Stops a waveform on a pin
206171
int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
207172
// Can't possibly need to stop anything if there is no timer active
208173
if (!timerRunning) {
209174
return false;
210175
}
211-
212-
for (size_t i = 0; i < countof(waveform); i++) {
213-
if (!waveform[i].enabled) {
214-
continue; // Skip fast to next one, can't need to stop this one since it's not running
215-
}
216-
if (((pin == 16) && waveform[i].gpio16Mask) || ((pin != 16) && (waveform[i].gpioMask == 1<<pin))) {
217-
// Note that there is no interrupt unsafety here. The IRQ can only ever change .enabled from 1->0
218-
// We're also doing that, so even if an IRQ occurred it would still stay as 0.
219-
waveform[i].enabled = 0;
220-
int cnt = timer1CB ? 1 : 0;
221-
for (size_t i = 0; (cnt == 0) && (i < countof(waveform)); i++) {
222-
cnt += waveform[i].enabled ? 1 : 0;
223-
}
224-
if (!cnt) {
225-
deinitTimer();
226-
}
227-
return true;
228-
}
176+
// If user sends in a pin >16 but <32, this will always point to a 0 bit
177+
// If they send >=32, then the shift will result in 0 and it will also return false
178+
uint32_t mask = 1<<pin;
179+
if (!(waveformEnabled & mask)) {
180+
return false; //It's not running, nothing to do here
181+
}
182+
waveformToDisable |= mask;
183+
timer1_write(MicrosecondsToCycles(1));
184+
while (waveformToDisable) {
185+
delay(1); // Wait for IRQ to update
229186
}
230-
return false;
187+
if (!waveformEnabled && !timer1CB) {
188+
deinitTimer();
189+
}
190+
return true;
231191
}
232192

233193
static ICACHE_RAM_ATTR void timer1Interrupt() {
234194
uint32_t nextEventCycles;
235-
#if F_CPU == 160000000
195+
#if F_CPU == 160000000
236196
uint8_t cnt = 20;
237-
#else
238-
uint8_t cnt = 10;
239-
#endif
197+
#else
198+
uint8_t cnt = 5;
199+
#endif
200+
201+
// Handle enable/disable requests from main app.
202+
waveformEnabled = (waveformEnabled & ~waveformToDisable) | waveformToEnable; // Set the requested waveforms on/off
203+
waveformState &= ~waveformToEnable; // And clear the state of any just started
204+
waveformToEnable = 0;
205+
waveformToDisable = 0;
240206

241207
do {
242208
nextEventCycles = MicrosecondsToCycles(MAXIRQUS);
243-
for (size_t i = 0; i < countof(waveform); i++) {
244-
Waveform *wave = &waveform[i];
245-
uint32_t now;
209+
for (size_t i = 0; i <= 16; i++) {
210+
uint32_t mask = 1<<i;
246211

247212
// If it's not on, ignore!
248-
if (!wave->enabled) {
213+
if (!(waveformEnabled & mask)) {
249214
continue;
250215
}
251216

217+
Waveform *wave = &waveform[i];
218+
uint32_t now;
219+
252220
// Check for toggles
253-
now = GetCycleCount();
221+
now = GetCycleCountIRQ();
254222
int32_t cyclesToGo = wave->nextServiceCycle - now;
255223
if (cyclesToGo < 0) {
256-
wave->state = !wave->state;
257-
if (wave->state) {
258-
SetGPIO(wave->gpioMask);
259-
if (wave->gpio16Mask) {
260-
GP16O |= wave->gpio16Mask; // GPIO16 write slow as it's RMW
261-
}
224+
waveformState ^= mask;
225+
if (waveformState & mask) {
226+
if (i==16) GP16O |= 1; // GPIO16 write slow as it's RMW
227+
else SetGPIO(mask);
228+
262229
wave->nextServiceCycle = now + wave->nextTimeHighCycles;
263230
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeHighCycles);
264231
} else {
265-
ClearGPIO(wave->gpioMask);
266-
if (wave->gpio16Mask) {
267-
GP16O &= ~wave->gpio16Mask;
268-
}
232+
if (i==16) GP16O &= ~1; // GPIO16 write slow as it's RMW
233+
else ClearGPIO(mask);
234+
269235
wave->nextServiceCycle = now + wave->nextTimeLowCycles;
270236
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeLowCycles);
271237
}
@@ -276,20 +242,21 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
276242
}
277243
} while (--cnt && (nextEventCycles < MicrosecondsToCycles(4)));
278244

279-
uint32_t curCycleCount = GetCycleCount();
245+
uint32_t curCycleCount = GetCycleCountIRQ();
280246
uint32_t deltaCycles = curCycleCount - lastCycleCount;
281247
lastCycleCount = curCycleCount;
282248

283249
// Check for timed-out waveforms out of the high-frequency toggle loop
284-
for (size_t i = 0; i < countof(waveform); i++) {
250+
for (size_t i = 0; i <= 16; i++) {
285251
Waveform *wave = &waveform[i];
286-
if (wave->enabled && wave->timeLeftCycles) {
252+
uint32_t mask = 1<<i;
253+
if ((waveformEnabled & mask) && wave->timeLeftCycles) {
287254
// Check for unsigned underflow with new > old
288255
if (deltaCycles >= wave->timeLeftCycles) {
289256
// Done, remove!
290-
wave->enabled = false;
291-
ClearGPIO(wave->gpioMask);
292-
GP16O &= ~wave->gpio16Mask;
257+
waveformEnabled &= ~mask;
258+
if (i==16) GP16O &= ~1;
259+
else ClearGPIO(mask);
293260
} else {
294261
uint32_t newTimeLeftCycles = wave->timeLeftCycles - deltaCycles;
295262
wave->timeLeftCycles = newTimeLeftCycles;
@@ -301,20 +268,22 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
301268
nextEventCycles = min_u32(nextEventCycles, timer1CB());
302269
}
303270

304-
#if F_CPU == 160000000
305-
if (nextEventCycles <= 5 * MicrosecondsToCycles(1)) {
306-
nextEventCycles = MicrosecondsToCycles(1) / 2;
271+
#if F_CPU == 160000000
272+
if (nextEventCycles < MicrosecondsToCycles(5)) {
273+
nextEventCycles = MicrosecondsToCycles(1);
307274
} else {
308-
nextEventCycles -= 5 * MicrosecondsToCycles(1);
275+
nextEventCycles -= MicrosecondsToCycles(4) + MicrosecondsToCycles(1)/2;
309276
}
310277
nextEventCycles = nextEventCycles >> 1;
311-
#else
312-
if (nextEventCycles <= 6 * MicrosecondsToCycles(1)) {
313-
nextEventCycles = MicrosecondsToCycles(1) / 2;
278+
#else
279+
if (nextEventCycles < MicrosecondsToCycles(8)) {
280+
nextEventCycles = MicrosecondsToCycles(2);
314281
} else {
315-
nextEventCycles -= 6 * MicrosecondsToCycles(1);
282+
nextEventCycles -= MicrosecondsToCycles(6);
316283
}
317-
#endif
284+
#endif
318285

319-
ReloadTimer(nextEventCycles);
286+
// Do it here instead of global function to save time and because we know it's edge-IRQ
287+
T1L = nextEventCycles; // Already know we're in range by MAXIRQUS
288+
TEIE |= TEIE1; //edge int enable
320289
}

0 commit comments

Comments
 (0)