Statistics
| Branch: | Tag: | Revision:

amiro-os / core / src / aos_sssp.c @ cda14729

History | View | Annotate | Download (18.761 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
#if (AMIROOS_CFG_SSSP_MASTER == true) || defined (__DOXYGEN__)
48

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

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

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

    
64
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
65

    
66
/******************************************************************************/
67
/* LOCAL FUNCTIONS                                                            */
68
/******************************************************************************/
69

    
70
#if (AMIROOS_CFG_SSSP_MASTER == true) || defined (__DOXYGEN__)
71

    
72
/**
73
 * @brief   General callback function to be used for any local timers.
74
 *
75
 * @param[in] par   A pointer to an @p event_source_t to be fired.
76
 */
77
static void _timerCallback(void* par)
78
{
79
  aosDbgCheck(par != NULL);
80

    
81
  chSysLockFromISR();
82
  chEvtBroadcastI((event_source_t*)par);
83
  chSysUnlockFromISR();
84

    
85
  return;
86
}
87

    
88
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
89

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

    
106
  // wait for the next event (do not apply any filters in order not to miss any events)
107
  *received = chEvtWaitOne(ALL_EVENTS);
108
  // if the correct event was triggered
109
  if (*received == listener->events) {
110
    apalControlGpioState_t s;
111
    apalControlGpioGet(&moduleSsspGpioS, &s);
112
    // only check for the expected event
113
    if (flags & listener->flags && s == signalstate) {
114
      // unset the expected flags but keep any other ones
115
      listener->flags &= ~flags;
116
      return AOS_SUCCESS;
117
    }
118
  }
119

    
120
  return AOS_FAILURE;
121
}
122

    
123
#if (AMIROOS_CFG_SSSP_MASTER == true) || defined(__DOXYGEN__)
124

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

    
141
  // arm the delay timer once
142
  if (arm_timer) {
143
    chEvtRegisterMask(&_eventSourceDelay, &_eventListenerDelay, mask);
144
    chVTSet(&_delayTimer, chTimeUS2I(dt), _timerCallback, &_eventSourceDelay);
145
  }
146

    
147
  // wait for any event to occur (do not apply any filters in order not to miss any events)
148
  *received = chEvtWaitOne(ALL_EVENTS);
149

    
150
  // if the timer event was received, cleanup
151
  if (*received & _eventListenerDelay.events) {
152
    chEvtUnregister(&_eventSourceDelay, &_eventListenerDelay);
153
    return AOS_SUCCESS;
154
  }
155

    
156
  return AOS_FAILURE;
157
}
158

    
159
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
160

    
161
/******************************************************************************/
162
/* EXPORTED FUNCTIONS                                                         */
163
/******************************************************************************/
164

    
165
/**
166
 * @brief   Initialize all SSSP related data.
167
 */
168
void aosSsspInit(void)
169
{
170
#if (AMIROOS_CFG_SSSP_MSI == true)
171
  // module ID is initialized as 'invalid'
172
  aos.sssp.moduleId = AOS_SSSP_MODULEID_INVALID;
173
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
174

    
175
#if (AMIROOS_CFG_SSSP_STARTUP == true)
176
  // AMiRo-OS has to perform the basic initialization
177
  aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_1_1;
178
#if (AMIROOS_CFG_DBG == true)
179
  // check whether the initial signal configuration is correct
180
  {
181
    apalControlGpioState_t state;
182
    // S signal
183
    apalControlGpioGet(&moduleSsspGpioS, &state);
184
    aosDbgAssert(state == APAL_GPIO_ON);
185
    // PD signal
186
    apalControlGpioGet(&moduleSsspGpioPD, &state);
187
    aosDbgAssert(state == APAL_GPIO_OFF);
188
  }
189
#endif /* (AMIROOS_CFG_DBG == true) */
190
#else /* (AMIROOS_CFG_SSSP_STARTUP == true) */
191
  // basic initialization was already performed by a bootloader
192
  aos.sssp.stage = AOS_SSSP_STAGE_STARTUP_2_1;
193
#if (AMIROOS_CFG_DBG == true)
194
  // check whether the initial signal configuration is correct
195
  {
196
    apalControlGpioState_t state;
197
    // S signal
198
    apalControlGpioGet(&moduleSsspGpioS, &state);
199
    aosDbgAssert(state == APAL_GPIO_ON);
200
    // PD signal
201
    apalControlGpioGet(&moduleSsspGpioPD, &state);
202
    aosDbgAssert(state == APAL_GPIO_OFF);
203
  }
204
#endif /* (AMIROOS_CFG_DBG == true) */
205
#endif /* (AMIROOS_CFG_SSSP_STARTUP == true) */
206

    
207
#if (AMIROOS_CFG_SSSP_MASTER == true)
208
  // initialize static variables
209
  chVTObjectInit(&_delayTimer);
210
  chEvtObjectInit(&_eventSourceDelay);
211
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
212

    
213
  return;
214
}
215

    
216
/**
217
 * @brief   Proceed to the next SSSP stage.
218
 *
219
 * @param[in]   listener  An optional listener, in case the system has to wait for a specific event.
220
 * @param[in]   flags     The relevant flags to wait for (mandatory if a listener is given).
221
 * @param[in]   mask      If the stage transition involves a timer event (handled internally by this function),
222
 *                        the given input value of this parameter defines the mask to be used for that event.
223
 *                        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.
224
 * @param[out]  received  Output variable to store the received event mask to.
225
 *
226
 * @return  Status, indicating whether proceeding in the protocol was successful.
227
 */
228
aos_status_t aosSsspProceed(event_listener_t* listener, eventflags_t flags, eventmask_t mask, eventmask_t* received)
229
{
230
  // local variables
231
#pragma GCC diagnostic push
232
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
233
  static aos_ssspstage_t laststage = AOS_SSSP_STAGE_UNDEFINED;
234
#pragma GCC diagnostic pop
235
  aos_ssspstage_t nextstage = aos.sssp.stage;
236

    
237
  // behaviour depends on current SSSP stage of the system
238
  switch (aos.sssp.stage) {
239
#if (AMIROOS_CFG_SSSP_STARTUP == true)
240

    
241
    /*
242
     * Deactivate S signal.
243
     * Master node delays execution by one period AOS_SSSP_DELAY.
244
     */
245
    case AOS_SSSP_STAGE_STARTUP_1_1:
246
    {
247
#if (AMIROOS_CFG_SSSP_MASTER == true)
248
      aosDbgCheck(listener == NULL);
249
      aosDbgCheck(flags == 0);
250
      aosDbgCheck(mask != 0);
251
      aosDbgCheck(received != NULL);
252

    
253
      // delay execution and deactivate the S signal on success
254
      if (_delay(AOS_SSSP_DELAY, mask, (laststage != AOS_SSSP_STAGE_STARTUP_1_1), received) == AOS_SUCCESS) {
255
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
256
        nextstage = AOS_SSSP_STAGE_STARTUP_1_2;
257
      }
258
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
259
      aosDbgCheck(listener == NULL);
260
      aosDbgCheck(flags == 0);
261
      aosDbgCheck(mask == 0);
262
      aosDbgCheck(received == NULL);
263

    
264
      // deactivate S signal and proceed
265
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
266
      nextstage = AOS_SSSP_STAGE_STARTUP_1_2;
267
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
268
      break;
269
    }
270

    
271
    /*
272
     * Wait for the S signal to become inactive.
273
     */
274
    case AOS_SSSP_STAGE_STARTUP_1_2:
275
    {
276
      aosDbgCheck(listener != NULL);
277
      aosDbgCheck(flags != 0);
278
      aosDbgCheck(mask == listener->events);
279
      aosDbgCheck(received != NULL);
280

    
281
      // wait for the S signal to become inactive and proceed on success
282
      if (_waitForS(listener, flags, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
283
        nextstage = AOS_SSSP_STAGE_STARTUP_1_3;
284
      }
285
      break;
286
    }
287

    
288
    /*
289
     * Wait for the S signal to be activated by the master module.
290
     * The master delays execution by one period AOS_SSSP_DELAY.
291
     */
292
    case AOS_SSSP_STAGE_STARTUP_1_3:
293
    {
294
#if (AMIROOS_CFG_SSSP_MASTER == true)
295
      aosDbgCheck(listener == NULL);
296
      aosDbgCheck(flags == 0);
297
      aosDbgCheck(mask != 0);
298
      aosDbgCheck(received != NULL);
299

    
300
      // delay execution and deactivate the S signal on success
301
      if (_delay(AOS_SSSP_DELAY, mask, (laststage != AOS_SSSP_STAGE_STARTUP_1_3), received) == AOS_SUCCESS) {
302
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
303
        nextstage = AOS_SSSP_STAGE_STARTUP_2_1;
304
      }
305
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
306
      aosDbgCheck(listener != NULL);
307
      aosDbgCheck(flags != 0);
308
      aosDbgCheck(mask == listener->events);
309
      aosDbgCheck(received != NULL);
310

    
311
      // wait for the S signal to become active and activate it as well and proceed on success
312
      if (_waitForS(listener, flags, APAL_GPIO_ON, received) == AOS_SUCCESS) {
313
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
314
        nextstage = AOS_SSSP_STAGE_STARTUP_2_1;
315
      }
316
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
317
      break;
318
    }
319

    
320
#endif /* (AMIROOS_CFG_SSSP_STARTUP == true) */
321

    
322
    /*
323
     * Deactivate S signal.
324
     * Master node delays execution by one period AOS_SSSP_DELAY
325
     */
326
    case AOS_SSSP_STAGE_STARTUP_2_1:
327
    {
328
#if (AMIROOS_CFG_SSSP_MASTER == true)
329
      aosDbgCheck(listener == NULL);
330
      aosDbgCheck(flags == 0);
331
      aosDbgCheck(mask != 0);
332
      aosDbgCheck(received != NULL);
333

    
334
      // delay execution and deactivate the S signal on success
335
      if (_delay(AOS_SSSP_DELAY, mask, (laststage != AOS_SSSP_STAGE_STARTUP_2_1), received) == AOS_SUCCESS) {
336
        apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
337
        nextstage = AOS_SSSP_STAGE_STARTUP_2_2;
338
      }
339
#else /* (AMIROOS_CFG_SSSP_MASTER == true) */
340
      aosDbgCheck(listener == NULL);
341
      aosDbgCheck(flags == 0);
342
      aosDbgCheck(mask == 0);
343
      aosDbgCheck(received == NULL);
344

    
345
      // deactivate S signal and proceed
346
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
347
      nextstage = AOS_SSSP_STAGE_STARTUP_2_2;
348
      mask = 0;
349
#endif /* (AMIROOS_CFG_SSSP_MASTER == true) */
350
      break;
351
    }
352

    
353
    /*
354
     * Wait for the S signal to become inactive.
355
     */
356
    case AOS_SSSP_STAGE_STARTUP_2_2:
357
    {
358
      aosDbgCheck(listener != NULL);
359
      aosDbgCheck(flags != 0);
360
      aosDbgCheck(mask == listener->events);
361
      aosDbgCheck(received != NULL);
362

    
363
      // wait for the S signal to become inactive and proceed on success
364
      if (_waitForS(listener, flags, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
365
#if (AMIROOS_CFG_SSSP_MSI == true)
366
        nextstage = AOS_SSSP_STAGE_STARTUP_3;
367
#else /* (AMIROOS_CFG_SSSP_MSI == true) */
368
        nextstage = AOS_SSSP_STAGE_OPERATION;
369
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
370
      }
371
      break;
372
    }
373

    
374
#if (AMIROOS_CFG_SSSP_MSI == true)
375

    
376
      //TODO
377

    
378
#endif /* (AMIROOS_CFG_SSSP_MSI == true) */
379

    
380
    /*
381
     * Delay execution by one perion AOS_SSSP_DELAY, deactivate the SYNC signal and proceed.
382
     * NOTE: Actually only the module that initiated the shutdown has to delay execution here.
383
     *       For the sake of simlicity though, each module delays execution.
384
     */
385
    case AOS_SSSP_STAGE_SHUTDOWN_1_2:
386
    {
387
      aosDbgCheck(listener == NULL);
388
      aosDbgCheck(flags == 0);
389
      aosDbgCheck(mask == 0);
390
      aosDbgCheck(received == NULL);
391

    
392
      // delay execution
393
      aosThdUSleep(AOS_SSSP_DELAY);
394
      // deactivate S signal
395
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
396
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_1_3;
397
      break;
398
    }
399

    
400
#if (AMIROOS_CFG_SSSP_SHUTDOWN == true)
401

    
402
    /*
403
     * Wait for S signal to become inactive.
404
     */
405
    case AOS_SSSP_STAGE_SHUTDOWN_1_3:
406
    {
407
      aosDbgCheck(listener != NULL);
408
      aosDbgCheck(flags != 0);
409
      aosDbgCheck(mask == listener->events);
410
      aosDbgCheck(received != NULL);
411

    
412
      // wait for the S signal to become inactive and proceed on success
413
      if (_waitForS(listener, flags, APAL_GPIO_OFF, received) == AOS_SUCCESS) {
414
        nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_1;
415
      }
416
      break;
417
    }
418

    
419
    /*
420
     * Evaluate PD signal to determine whether a shutdown or restart is requested.
421
     * The logical state is returned by the function call.
422
     */
423
    case AOS_SSSP_STAGE_SHUTDOWN_2_1:
424
    {
425
      aosDbgCheck(listener == NULL);
426
      aosDbgCheck(flags == 0);
427
      aosDbgCheck(mask == 0);
428
      aosDbgCheck(received != NULL);
429

    
430
      // evaluate PD signal
431
      apalControlGpioState_t pd;
432
      apalControlGpioGet(&moduleSsspGpioPD, &pd);
433
      *received = (pd == APAL_GPIO_ON) ? (eventmask_t)~0 : 0;
434
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_2;
435
      break;
436
    }
437

    
438
    /*
439
     * Proceed with no further actions required.
440
     */
441
    case AOS_SSSP_STAGE_SHUTDOWN_2_2:
442
    {
443
      aosDbgCheck(listener == NULL);
444
      aosDbgCheck(flags == 0);
445
      aosDbgCheck(mask == 0);
446
      aosDbgCheck(received == NULL);
447

    
448
      nextstage = AOS_SSSP_STAGE_SHUTDOWN_2_3;
449
      break;
450
    }
451

    
452
#endif /* (AMIROOS_CFG_SSSP_SHUTDOWN == true) */
453

    
454
    default:
455
    {
456
      // this case must never occur!
457
      aosDbgAssertMsg(false, "Invalid SSSP stage tranition occurred");
458
      break;
459
    }
460
  } /* end of switch (aos.sssp.stage) */
461

    
462
  // outro and return
463
  laststage = aos.sssp.stage;
464
  const aos_status_t status = (nextstage > aos.sssp.stage) ? AOS_SUCCESS : AOS_FAILURE;
465
  aos.sssp.stage = nextstage;
466

    
467
  return status;
468
}
469

    
470
void aosSsspShutdownInit(void)
471
{
472
  // activate S signal
473
  apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
474
  // proceed to shutdown phase
475
  aos.sssp.stage = AOS_SSSP_STAGE_SHUTDOWN_1_2;
476

    
477
  return;
478
}
479

    
480
#if (AMIROOS_CFG_SSSP_SHUTDOWN == true) || defined(__DOXYGEN__)
481

    
482
/**
483
 * @brief   Sequnetially broadcast an identifier via S to specify a specific way to shutdown or restart.
484
 *
485
 * @param[in] identifier  Identifier to be broadcasted.
486
 */
487
void aosSsspShutdownBroadcastIdentifier(unsigned int identifier)
488
{
489
  // only broadcast anything if a identifier greater than 0 was specified
490
  if (identifier > 0) {
491
    // broadcast identifier
492
    for (unsigned int pulse = 0; pulse < identifier; ++pulse) {
493
      // wait one delay time with S being deactivated
494
      aosThdUSleep(AOS_SSSP_DELAY);
495

    
496
      // activate S for one AOS_SSSP_DELAY.
497
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_ON);
498
      aosThdUSleep(AOS_SSSP_DELAY);
499
      apalControlGpioSet(&moduleSsspGpioS, APAL_GPIO_OFF);
500
    }
501
  }
502

    
503
  // let a timeout pass and return
504
  aosThdUSleep(AOS_SSSP_TIMEOUT);
505

    
506
  return;
507
}
508

    
509
/**
510
 * @brief   Wait for a pulse during the SSSP shutdown disambiguation procedure.
511
 * @details Whenever a pulse is received, the identifier is incremented.
512
 *          Otherwise the internal timer is configured and will eventually trigger a timeout event.
513
 *
514
 * @param[in]     gpiolistener  Event listener for GPIO events.
515
 * @param[in]     sflags        Event flags to be set by edges on the S signal.
516
 * @param[in]     timermask     Event maks to use for the internal timer.
517
 * @param[in,out] identifier    Identifier variable to increment.
518
 *
519
 * @return  The event mask of whatever event was received during this function call.
520
 */
521
eventmask_t aosSsspShutdownWaitForIdentifierPulse(event_listener_t* gpiolistener, const eventflags_t sflags, eventmask_t timermask, unsigned int* identifier)
522
{
523
  aosDbgCheck(gpiolistener != NULL);
524
  aosDbgCheck(sflags != 0);
525
  aosDbgCheck(timermask != 0);
526
  aosDbgCheck(identifier != NULL);
527

    
528
  // local variables
529
  eventmask_t mask;
530
  eventflags_t flags;
531
  apalControlGpioState_t sstate;
532

    
533
  // arm the timer once
534
  if (!chVTIsArmed(&_delayTimer)) {
535
    chEvtRegisterMask(&_eventSourceDelay, &_eventListenerDelay, timermask);
536
    chVTSet(&_delayTimer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &_eventSourceDelay);
537
  }
538

    
539
  // wait for any event to occur (do not apply any filters in order not to miss any events)
540
  mask = chEvtWaitOne(ALL_EVENTS);
541

    
542
  // GPIO event
543
  if (mask & gpiolistener->events) {
544
    // retreive flags without clearing them
545
    flags = gpiolistener->flags;
546
    apalControlGpioGet(&moduleSsspGpioS, &sstate);
547
    if (flags == sflags) {
548
      apalControlGpioGet(&moduleSsspGpioS, &sstate);
549
      // if this was the end of a pulse
550
      if (sstate == APAL_GPIO_OFF) {
551
        // restart the timer
552
        chVTReset(&_delayTimer);
553
        chVTSet(&_delayTimer, chTimeUS2I(AOS_SSSP_TIMEOUT), _timerCallback, &_eventSourceDelay);
554
        // increment the identifier
555
        ++(*identifier);
556
      }
557
    }
558
  }
559
  // timer event
560
  else if (mask & _eventListenerDelay.events) {
561
    // unregister event
562
    chEvtUnregister(&_eventSourceDelay, &_eventListenerDelay);
563
  }
564
  // any further events must be handled externally
565

    
566
  return mask;
567
}
568

    
569
#endif /* (AMIROOS_CFG_SSSP_SHUTDOWN == true) */
570

    
571
#endif /* (AMIROOS_CFG_SSSP_ENABLE == true) */
572

    
573
/** @} */