Statistics
| Branch: | Tag: | Revision:

amiro-os / core / src / aos_sssp.c @ 510b93cc

History | View | Annotate | Download (18.213 KB)

1
/*
2
AMiRo-OS is an operating system designed for the Autonomous Mini Robot (AMiRo) platform.
3
Copyright (C) 2016..2019  Thomas Schöpping et al.
4

5
This program is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9

10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
GNU General Public License for more details.
14

15
You should have received a copy of the GNU General Public License
16
along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18

    
19
/**
20
 * @file    aos_sssp.c
21
 * @brief   SSSP related code.
22
 *
23
 * @addtogroup aos_sssp
24
 * @{
25
 */
26

    
27
#include <amiroos.h>
28

    
29
#if (AMIROOS_CFG_SSSP_ENABLE == true) || defined(__DOXYGEN__)
30

    
31
/******************************************************************************/
32
/* LOCAL DEFINITIONS                                                          */
33
/******************************************************************************/
34

    
35
/******************************************************************************/
36
/* EXPORTED VARIABLES                                                         */
37
/******************************************************************************/
38

    
39
/******************************************************************************/
40
/* LOCAL TYPES                                                                */
41
/******************************************************************************/
42

    
43
/******************************************************************************/
44
/* LOCAL VARIABLES                                                            */
45
/******************************************************************************/
46

    
47
/**
48
 * @brief   A timer event based delays.
49
 */
50
static virtual_timer_t _delayTimer;
51

    
52
/**
53
 * @brief   Event source for the delay timer.
54
 */
55
static event_source_t _eventSourceDelay;
56

    
57
/**
58
 * @brief   Event listener for the delay event.
59
 */
60
static event_listener_t _eventListenerDelay;
61

    
62
/******************************************************************************/
63
/* LOCAL FUNCTIONS                                                            */
64
/******************************************************************************/
65

    
66
/**
67
 * @brief   General callback function to be used for any local timers.
68
 *
69
 * @param[in] par   A pointer to an @p event_source_t to be fired.
70
 */
71
static void _timerCallback(void* par)
72
{
73
  aosDbgCheck(par != NULL);
74

    
75
  chSysLockFromISR();
76
  chEvtBroadcastI((event_source_t*)par);
77
  chSysUnlockFromISR();
78

    
79
  return;
80
}
81

    
82
/**
83
 * @brief   Waits for the S signal to switch to the desired state.
84
 *
85
 * @param[in]   listener      Pointer to the GPIO event listener to be used.
86
 * @param[in]   flags         Event flags to listen to by the event listener (indicating a S signal event).
87
 * @param[in]   signalstate   Desired state of the S signal ti be waited for.
88
 * @param[out]  received      Output variable to store the received event mask to.
89
 *
90
 * @return  Status, indicating whether the expected event was received.
91
 */
92
static inline aos_status_t _waitForS(event_listener_t* listener, eventflags_t flags, apalControlGpioState_t signalstate, eventmask_t* received)
93
{
94
  aosDbgCheck(listener != NULL);
95
  aosDbgCheck(flags != 0);
96
  aosDbgCheck(received != NULL);
97

    
98
  // wait for the next event (do not apply any filters in order not to miss any events)
99
  *received = chEvtWaitOne(ALL_EVENTS);
100
  // if the correct event was triggered
101
  if (*received == listener->events) {
102
    apalControlGpioState_t s;
103
    apalControlGpioGet(&moduleSsspGpioS, &s);
104
    // only check for the expected event
105
    if (flags & listener->flags && s == signalstate) {
106
      // unset the expected flags but keep any other ones
107
      listener->flags &= ~flags;
108
      return AOS_SUCCESS;
109
    }
110
  }
111

    
112
  return AOS_FAILURE;
113
}
114

    
115
/**
116
 * @brief   Uses the local delay timer to setup a timed event and waits for it to fire.
117
 *
118
 * @param[in]   dt          Delay time in microseconds to be set to the timer.
119
 * @param[in]   mask        Event mask to be used for the delay timer.
120
 * @param[in]   arm_timer   Flag to indicate whether the timer has to be armed yet (i.e. on first call of this function).
121
 * @param[out]  received    Output variable to store the received event mask to.
122
 *
123
 * @return  Status, indicating whether the expected event (delay timer) was received.
124
 */
125
static inline aos_status_t _delay(uint32_t dt, eventmask_t mask, bool arm_timer, eventmask_t* received)
126
{
127
  aosDbgCheck(dt != TIME_IMMEDIATE);
128
  aosDbgCheck(mask != 0);
129
  aosDbgCheck(received != NULL);
130

    
131
  // arm the delay timer once
132
  if (arm_timer) {
133
    chEvtRegisterMask(&_eventSourceDelay, &_eventListenerDelay, mask);
134
    chVTSet(&_delayTimer, chTimeUS2I(dt), _timerCallback, &_eventSourceDelay);
135
  }
136

    
137
  // wait for any event to occur (do not apply any filters in order not to miss any events)
138
  *received = chEvtWaitOne(ALL_EVENTS);
139

    
140
  // if the timer event was received, cleanup
141
  if (*received & _eventListenerDelay.events) {
142
    chEvtUnregister(&_eventSourceDelay, &_eventListenerDelay);
143
    return AOS_SUCCESS;
144
  }
145

    
146
  return AOS_FAILURE;
147
}
148

    
149
/******************************************************************************/
150
/* EXPORTED FUNCTIONS                                                         */
151
/******************************************************************************/
152

    
153
/**
154
 * @brief   Initialize all SSSP related data.
155
 */
156
void aosSsspInit(void)
157
{
158
  // local variables
159
  apalControlGpioState_t state;
160

    
161
#if (AMIROOS_CFG_SSSP_STARTUP == true)
162
  // AMiRo-OS has to perform the basic initialization
163
  aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_1_1;
164
#else /* (AMIROOS_CFG_SSSP_STARTUP == true) */
165
  // basic initialization was already performed by a bootloader
166
  aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_2_1;
167
#endif /* (AMIROOS_CFG_SSSP_STARTUP == true) */
168

    
169
  // check all SSSP signals for correct state
170
  apalControlGpioGet(&moduleSsspGpioS, &state);
171
  aosDbgAssert(state == APAL_GPIO_ON);
172
  apalControlGpioGet(&moduleSsspGpioPD, &state);
173
  aosDbgAssert(state == APAL_GPIO_OFF);
174
#if (AMIROOS_CFG_SSSP_STACK_END != true)
175
  apalControlGpioGet(&moduleSsspGpioUP, &state);
176
  aosDbgAssert(state == APAL_GPIO_OFF);
177
#endif /* (AMIROOS_CFG_SSSP_STACK_END != true) */
178
#if (AMIROOS_CFG_SSSP_STACK_START != true)
179
  apalControlGpioGet(&moduleSsspGpioDN, &state);
180
  aosDbgAssert(state == APAL_GPIO_OFF);
181
#endif /* (AMIROOS_CFG_SSSP_STACK_START != true) */
182

    
183
#if (AMIROOS_CFG_SSSP_MSI == true)
184
  // module ID is initialized as 'invalid'
185
  aos.sssp.moduleId = AOS_SSSP_MODULEID_INVALID;
186
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
187

    
188
  // initialize static variables
189
  chVTObjectInit(&_delayTimer);
190
  chEvtObjectInit(&_eventSourceDelay);
191

    
192
  return;
193
}
194

    
195
/**
196
 * @brief   Proceed to the next SSSP stage.
197
 *
198
 * @param[in]   listener  An optional listener, in case the system has to wait for a specific event.
199
 * @param[in]   flags     The relevant flags to wait for (mandatory if a listener is given).
200
 * @param[in]   mask      If the stage transition involves a timer event (handled internally by this function),
201
 *                        the given input value of this parameter defines the mask to be used for that event.
202
 *                        In case a listener was specified, the according mask is retrieved from the listener but this parameter is still used as input for sanity checks.
203
 * @param[out]  received  Output variable to store the received event mask to.
204
 *
205
 * @return  Status, indicating whether proceeding in the protocol was successful.
206
 */
207
aos_status_t aosSsspProceed(event_listener_t* listener, eventflags_t flags, eventmask_t mask, eventmask_t* received)
208
{
209
  // local variables
210
#pragma GCC diagnostic push
211
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
212
  static aos_ssspstage_t laststage = AOS_SSSP_STAGE_UNDEFINED;
213
#pragma GCC diagnostic pop
214
  aos_ssspstage_t nextstage = aos.sssp.stage;
215

    
216
  // behaviour depends on current SSSP stage of the system
217
  switch (aos.sssp.stage) {
218
#if (AMIROOS_CFG_SSSP_STARTUP == true)
219

    
220
    /*
221
     * Deactivate S signal.
222
     * Master node delays execution by one period AOS_SSSP_DELAY.
223
     */
224
    case AOS_SSSP_STAGE_STARTUP_1_1:
225
    {
226
#if (AMIROOS_CFG_SSSP_MASTER == true)
227
      aosDbgCheck(listener == NULL);
228
      aosDbgCheck(flags == 0);
229
      aosDbgCheck(mask != 0);
230
      aosDbgCheck(received != NULL);
231

    
232
      // delay execution and deactivate the S signal on success
233
      if (_delay(AOS_SSSP_DELAY, mask, (laststage != AOS_SSSP_STAGE_STARTUP_1_1), received) == AOS_SUCCESS) {
234
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
235
        nextstage = AOS_SSSP_STAGE_STARTUP_1_2;
236
      }
237
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
238
      aosDbgCheck(listener == NULL);
239
      aosDbgCheck(flags == 0);
240
      aosDbgCheck(mask == 0);
241
      aosDbgCheck(received == NULL);
242

    
243
      // deactivate S signal and proceed
244
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
245
      nextstage = AOS_SSSP_STAGE_STARTUP_1_2;
246
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
247
      break;
248
    }
249

    
250
    /*
251
     * Wait for the S signal to become inactive.
252
     */
253
    case AOS_SSSP_STAGE_STARTUP_1_2:
254
    {
255
      aosDbgCheck(listener != NULL);
256
      aosDbgCheck(flags != 0);
257
      aosDbgCheck(mask == listener->events);
258
      aosDbgCheck(received != NULL);
259

    
260
      // wait for the S signal to become inactive and proceed on success
261
      if (_waitForS(listener, flags, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
262
        nextstage = AOS_SSSP_STAGE_STARTUP_1_3;
263
      }
264
      break;
265
    }
266

    
267
    /*
268
     * Wait for the S signal to be activated by the master module.
269
     * The master delays execution by one period AOS_SSSP_DELAY.
270
     */
271
    case AOS_SSSP_STAGE_STARTUP_1_3:
272
    {
273
#if (AMIROOS_CFG_SSSP_MASTER == true)
274
      aosDbgCheck(listener == NULL);
275
      aosDbgCheck(flags == 0);
276
      aosDbgCheck(mask != 0);
277
      aosDbgCheck(received != NULL);
278

    
279
      // delay execution and deactivate the S signal on success
280
      if (_delay(AOS_SSSP_DELAY, mask, (laststage != AOS_SSSP_STAGE_STARTUP_1_3), received) == AOS_SUCCESS) {
281
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
282
        nextstage = AOS_SSSP_STAGE_STARTUP_2_1;
283
      }
284
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
285
      aosDbgCheck(listener != NULL);
286
      aosDbgCheck(flags != 0);
287
      aosDbgCheck(mask == listener->events);
288
      aosDbgCheck(received != NULL);
289

    
290
      // wait for the S signal to become active and activate it as well and proceed on success
291
      if (_waitForS(listener, flags, APAL_GPIO_ON, received) == AOS_SUCCESS) {
292
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
293
        nextstage = AOS_SSSP_STAGE_STARTUP_2_1;
294
      }
295
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
296
      break;
297
    }
298

    
299
#endif /* (AMIROOS_CFG_SSSP_STARTUP == true) */
300

    
301
    /*
302
     * Deactivate S signal.
303
     * Master node delays execution by one period AOS_SSSP_DELAY
304
     */
305
    case AOS_SSSP_STAGE_STARTUP_2_1:
306
    {
307
#if (AMIROOS_CFG_SSSP_MASTER == true)
308
      aosDbgCheck(listener == NULL);
309
      aosDbgCheck(flags == 0);
310
      aosDbgCheck(mask != 0);
311
      aosDbgCheck(received != NULL);
312

    
313
      // delay execution and deactivate the S signal on success
314
      if (_delay(AOS_SSSP_DELAY, mask, (laststage != AOS_SSSP_STAGE_STARTUP_2_1), received) == AOS_SUCCESS) {
315
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
316
        nextstage = AOS_SSSP_STAGE_STARTUP_2_2;
317
      }
318
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
319
      aosDbgCheck(listener == NULL);
320
      aosDbgCheck(flags == 0);
321
      aosDbgCheck(mask == 0);
322
      aosDbgCheck(received == NULL);
323

    
324
      // deactivate S signal and proceed
325
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
326
      nextstage = AOS_SSSP_STAGE_STARTUP_2_2;
327
      mask = 0;
328
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
329
      break;
330
    }
331

    
332
    /*
333
     * Wait for the S signal to become inactive.
334
     */
335
    case AOS_SSSP_STAGE_STARTUP_2_2:
336
    {
337
      aosDbgCheck(listener != NULL);
338
      aosDbgCheck(flags != 0);
339
      aosDbgCheck(mask == listener->events);
340
      aosDbgCheck(received != NULL);
341

    
342
      // wait for the S signal to become inactive and proceed on success
343
      if (_waitForS(listener, flags, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
344
#if (AMIROOS_CFG_SSSP_MSI == true)
345
        nextstage = AOS_SSSP_STAGE_STARTUP_3;
346
#else /* (AMIROOS_CFG_SSSP_MSI == true) */
347
        nextstage = AOS_SSSP_STAGE_OPERATION;
348
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
349
      }
350
      break;
351
    }
352

    
353
#if (AMIROOS_CFG_SSSP_MSI == true)
354

    
355
      //TODO
356

    
357
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
358

    
359
    /*
360
     * Delay execution by one perion AOS_SSSP_DELAY, deactivate the SYNC signal and proceed.
361
     * NOTE: Actually only the module that initiated the shutdown has to delay execution here.
362
     *       For the sake of simlicity though, each module delays execution.
363
     */
364
    case AOS_SSSP_STAGE_SHUTDOWN_1_2:
365
    {
366
      aosDbgCheck(listener == NULL);
367
      aosDbgCheck(flags == 0);
368
      aosDbgCheck(mask == 0);
369
      aosDbgCheck(received == NULL);
370

    
371
      // delay execution
372
      aosThdUSleep(AOS_SSSP_DELAY);
373
      // deactivate S signal
374
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
375
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_1_3;
376
      break;
377
    }
378

    
379
#if (AMIROOS_CFG_SSSP_SHUTDOWN == true)
380

    
381
    /*
382
     * Wait for S signal to become inactive.
383
     */
384
    case AOS_SSSP_STAGE_SHUTDOWN_1_3:
385
    {
386
      aosDbgCheck(listener != NULL);
387
      aosDbgCheck(flags != 0);
388
      aosDbgCheck(mask == listener->events);
389
      aosDbgCheck(received != NULL);
390

    
391
      // wait for the S signal to become inactive and proceed on success
392
      if (_waitForS(listener, flags, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
393
        nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_1;
394
      }
395
      break;
396
    }
397

    
398
    /*
399
     * Evaluate PD signal to determine whether a shutdown or restart is requested.
400
     * The logical state is returned by the function call.
401
     */
402
    case AOS_SSSP_STAGE_SHUTDOWN_2_1:
403
    {
404
      aosDbgCheck(listener == NULL);
405
      aosDbgCheck(flags == 0);
406
      aosDbgCheck(mask == 0);
407
      aosDbgCheck(received != NULL);
408

    
409
      // evaluate PD signal
410
      apalControlGpioState_t pd;
411
      apalControlGpioGet(&moduleSsspGpioPD, &pd);
412
      *received = (pd == APAL_GPIO_ON) ? (eventmask_t)~0 : 0;
413
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_2;
414
      break;
415
    }
416

    
417
    /*
418
     * Proceed with no further actions required.
419
     */
420
    case AOS_SSSP_STAGE_SHUTDOWN_2_2:
421
    {
422
      aosDbgCheck(listener == NULL);
423
      aosDbgCheck(flags == 0);
424
      aosDbgCheck(mask == 0);
425
      aosDbgCheck(received == NULL);
426

    
427
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_3;
428
      break;
429
    }
430

    
431
#endif /* (AMIROOS_CFG_SSSP_SHUTDOWN == true) */
432

    
433
    default:
434
    {
435
      // this case must never occur!
436
      aosDbgAssertMsg(false, "Invalid SSSP stage tranition occurred");
437
      break;
438
    }
439
  } /* end of switch (aos.sssp.stage) */
440

    
441
  // outro and return
442
  laststage = aos.sssp.stage;
443
  const aos_status_t status = (nextstage > aos.sssp.stage) ? AOS_SUCCESS : AOS_FAILURE;
444
  aos.sssp.stage = nextstage;
445

    
446
  return status;
447
}
448

    
449
void aosSsspShutdownInit(void)
450
{
451
  // activate S signal
452
  apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
453
  // proceed to shutdown phase
454
  aos.sssp.stage = AOS_SSSP_STAGE_SHUTDOWN_1_2;
455

    
456
  return;
457
}
458

    
459
#if (AMIROOS_CFG_SSSP_SHUTDOWN == true) || defined(__DOXYGEN__)
460

    
461
/**
462
 * @brief   Sequnetially broadcast an identifier via S to specify a specific way to shutdown or restart.
463
 *
464
 * @param[in] identifier  Identifier to be broadcasted.
465
 */
466
void aosSsspShutdownBroadcastIdentifier(unsigned int identifier)
467
{
468
  // only broadcast anything if a identifier greater than 0 was specified
469
  if (identifier > 0) {
470
    // broadcast identifier
471
    for (unsigned int pulse = 0; pulse < identifier; ++pulse) {
472
      // wait one delay time with S being deactivated
473
      aosThdUSleep(AOS_SSSP_DELAY);
474

    
475
      // activate S for one AOS_SSSP_DELAY.
476
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
477
      aosThdUSleep(AOS_SSSP_DELAY);
478
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
479
    }
480
  }
481

    
482
  // let a timeout pass and return
483
  aosThdUSleep(AOS_SSSP_TIMEOUT);
484

    
485
  return;
486
}
487

    
488
/**
489
 * @brief   Wait for a pulse during the SSSP shutdown disambiguation procedure.
490
 * @details Whenever a pulse is received, the identifier is incremented.
491
 *          Otherwise the internal timer is configured and will eventually trigger a timeout event.
492
 *
493
 * @param[in]     gpiolistener  Event listener for GPIO events.
494
 * @param[in]     sflags        Event flags to be set by edges on the S signal.
495
 * @param[in]     timermask     Event maks to use for the internal timer.
496
 * @param[in,out] identifier    Identifier variable to increment.
497
 *
498
 * @return  The event mask of whatever event was received during this function call.
499
 */
500
eventmask_t aosSsspShutdownWaitForIdentifierPulse(event_listener_t* gpiolistener, const eventflags_t sflags, eventmask_t timermask, unsigned int* identifier)
501
{
502
  aosDbgCheck(gpiolistener != NULL);
503
  aosDbgCheck(sflags != 0);
504
  aosDbgCheck(timermask != 0);
505
  aosDbgCheck(identifier != NULL);
506

    
507
  // local variables
508
  eventmask_t mask;
509
  eventflags_t flags;
510
  apalControlGpioState_t sstate;
511

    
512
  // arm the timer once
513
  if (!chVTIsArmed(&_delayTimer)) {
514
    chEvtRegisterMask(&_eventSourceDelay, &_eventListenerDelay, timermask);
515
    chVTSet(&_delayTimer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &_eventSourceDelay);
516
  }
517

    
518
  // wait for any event to occur (do not apply any filters in order not to miss any events)
519
  mask = chEvtWaitOne(ALL_EVENTS);
520

    
521
  // GPIO event
522
  if (mask & gpiolistener->events) {
523
    // retreive flags without clearing them
524
    flags = gpiolistener->flags;
525
    apalControlGpioGet(&moduleSsspGpioS, &sstate);
526
    if (flags == sflags) {
527
      apalControlGpioGet(&moduleSsspGpioS, &sstate);
528
      // if this was the end of a pulse
529
      if (sstate == APAL_GPIO_OFF) {
530
        // restart the timer
531
        chVTReset(&_delayTimer);
532
        chVTSet(&_delayTimer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &_eventSourceDelay);
533
        // increment the identifier
534
        ++(*identifier);
535
      }
536
    }
537
  }
538
  // timer event
539
  else if (mask & _eventListenerDelay.events) {
540
    // unregister event
541
    chEvtUnregister(&_eventSourceDelay, &_eventListenerDelay);
542
  }
543
  // any further events must be handled externally
544

    
545
  return mask;
546
}
547

    
548
#endif /* (AMIROOS_CFG_SSSP_SHUTDOWN == true) */
549

    
550
#endif /* (AMIROOS_CFG_SSSP_ENABLE == true) */
551

    
552
/** @} */