GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/fsw_init.c Lines: 260 277 93.9 %
Date: 2018-11-13 15:31:29 Branches: 59 108 54.6 %

Line Branch Exec Source
1
/*------------------------------------------------------------------------------
2
--  Solar Orbiter's Low Frequency Receiver Flight Software (LFR FSW),
3
--  This file is a part of the LFR FSW
4
--  Copyright (C) 2012-2018, Plasma Physics Laboratory - CNRS
5
--
6
--  This program is free software; you can redistribute it and/or modify
7
--  it under the terms of the GNU General Public License as published by
8
--  the Free Software Foundation; either version 2 of the License, or
9
--  (at your option) any later version.
10
--
11
--  This program is distributed in the hope that it will be useful,
12
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
--  GNU General Public License for more details.
15
--
16
--  You should have received a copy of the GNU General Public License
17
--  along with this program; if not, write to the Free Software
18
--  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
-------------------------------------------------------------------------------*/
20
/*--                  Author : Paul Leroy
21
--                   Contact : Alexis Jeandet
22
--                      Mail : alexis.jeandet@lpp.polytechnique.fr
23
----------------------------------------------------------------------------*/
24
25
/** This is the RTEMS initialization module.
26
 *
27
 * @file
28
 * @author P. LEROY
29
 *
30
 * This module contains two very different information:
31
 * - specific instructions to configure the compilation of the RTEMS executive
32
 * - functions related to the fligth softwre initialization, especially the INIT RTEMS task
33
 *
34
 */
35
36
#include <rtems.h>
37
38
39
/* configuration information */
40
41
#define CONFIGURE_INIT
42
43
#include <bsp.h> /* for device driver prototypes */
44
45
/* configuration information */
46
47
#include <fsw_params.h>
48
49
#include <rtems/confdefs.h>
50
51
/* If --drvmgr was enabled during the configuration of the RTEMS kernel */
52
#ifdef RTEMS_DRVMGR_STARTUP
53
    #ifdef LEON3
54
    /* Add Timer and UART Driver */
55
56
        #ifdef CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
57
            #define CONFIGURE_DRIVER_AMBAPP_GAISLER_GPTIMER
58
        #endif
59
60
        #ifdef CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
61
            #define CONFIGURE_DRIVER_AMBAPP_GAISLER_APBUART
62
        #endif
63
64
    #endif
65
    #define CONFIGURE_DRIVER_AMBAPP_GAISLER_GRSPW   /* GRSPW Driver */
66
    #include <drvmgr/drvmgr_confdefs.h>
67
#endif
68
69
#include "fsw_init.h"
70
#include "fsw_config.c"
71
#include "GscMemoryLPP.hpp"
72
73
1
void initCache()
74
{
75
    // ASI 2 contains a few control registers that have not been assigned as ancillary state registers.
76
    // These should only be read and written using 32-bit LDA/STA instructions.
77
    // All cache registers are accessed through load/store operations to the alternate address space (LDA/STA), using ASI = 2.
78
    // The table below shows the register addresses:
79
    //      0x00 Cache control register
80
    //      0x04 Reserved
81
    //      0x08 Instruction cache configuration register
82
    //      0x0C Data cache configuration register
83
84
    // Cache Control Register Leon3 / Leon3FT
85
    // 31..30  29   28  27..24  23  22  21  20..19  18  17  16
86
    //         RFT  PS  TB      DS  FD  FI  FT          ST  IB
87
    // 15  14  13..12  11..10  9..8  7..6  5   4   3..2  1..0
88
    // IP  DP  ITE     IDE     DTE   DDE   DF  IF  DCS   ICS
89
90
    unsigned int cacheControlRegister;
91
92
1
    CCR_resetCacheControlRegister();
93
1
    ASR16_resetRegisterProtectionControlRegister();
94
95
1
    cacheControlRegister = CCR_getValue();
96
    PRINTF1("(0) CCR - Cache Control Register = %x\n", cacheControlRegister);
97
    PRINTF1("(0) ASR16                        = %x\n", *asr16Ptr);
98
99
1
    CCR_enableInstructionCache();       // ICS bits
100
1
    CCR_enableDataCache();              // DCS bits
101
1
    CCR_enableInstructionBurstFetch();  // IB  bit
102
103
1
    faultTolerantScheme();
104
105
1
    cacheControlRegister = CCR_getValue();
106
    PRINTF1("(1) CCR - Cache Control Register = %x\n", cacheControlRegister);
107
    PRINTF1("(1) ASR16 Register protection control register = %x\n", *asr16Ptr);
108
109
    PRINTF("\n");
110
1
}
111
112
rtems_task Init( rtems_task_argument ignored )
113
{
114
    /** This is the RTEMS INIT taks, it is the first task launched by the system.
115
     *
116
     * @param unused is the starting argument of the RTEMS task
117
     *
118
     * The INIT task create and run all other RTEMS tasks.
119
     *
120
     */
121
122
    //***********
123
    // INIT CACHE
124
125
    unsigned char *vhdlVersion;
126
127
1
    reset_lfr();
128
129
1
    reset_local_time();
130
131
1
    rtems_cpu_usage_reset();
132
133
    rtems_status_code status;
134
    rtems_status_code status_spw;
135
    rtems_isr_entry  old_isr_handler;
136
137
1
    old_isr_handler = NULL;
138
139
    // UART settings
140
1
    enable_apbuart_transmitter();
141
1
    set_apbuart_scaler_reload_register(REGS_ADDR_APBUART, APBUART_SCALER_RELOAD_VALUE);
142
143
    DEBUG_PRINTF("\n\n\n\n\nIn INIT *** Now the console is on port COM1\n")
144
145
146
    PRINTF("\n\n\n\n\n")
147
148
1
    initCache();
149
150
    PRINTF("*************************\n")
151
    PRINTF("** LFR Flight Software **\n")
152
153
    PRINTF1("** %d-", SW_VERSION_N1)
154
    PRINTF1("%d-"   , SW_VERSION_N2)
155
    PRINTF1("%d-"   , SW_VERSION_N3)
156
    PRINTF1("%d             **\n", SW_VERSION_N4)
157
158
1
    vhdlVersion = (unsigned char *) (REGS_ADDR_VHDL_VERSION);
159
    PRINTF("** VHDL                **\n")
160
    PRINTF1("** %d-", vhdlVersion[1])
161
    PRINTF1("%d-"   , vhdlVersion[2])
162
    PRINTF1("%d              **\n", vhdlVersion[3])
163
    PRINTF("*************************\n")
164
    PRINTF("\n\n")
165
166
1
    init_parameter_dump();
167
1
    init_kcoefficients_dump();
168
1
    init_local_mode_parameters();
169
1
    init_housekeeping_parameters();
170
1
    init_k_coefficients_prc0();
171
1
    init_k_coefficients_prc1();
172
1
    init_k_coefficients_prc2();
173
1
    pa_bia_status_info = INIT_CHAR;
174
175
    // initialize all reaction wheels frequencies to NaN
176
1
    rw_f.cp_rpw_sc_rw1_f1 = NAN;
177
1
    rw_f.cp_rpw_sc_rw1_f2 = NAN;
178
1
    rw_f.cp_rpw_sc_rw1_f3 = NAN;
179
1
    rw_f.cp_rpw_sc_rw1_f4 = NAN;
180
1
    rw_f.cp_rpw_sc_rw2_f1 = NAN;
181
1
    rw_f.cp_rpw_sc_rw2_f2 = NAN;
182
1
    rw_f.cp_rpw_sc_rw2_f3 = NAN;
183
1
    rw_f.cp_rpw_sc_rw2_f4 = NAN;
184
1
    rw_f.cp_rpw_sc_rw3_f1 = NAN;
185
1
    rw_f.cp_rpw_sc_rw3_f2 = NAN;
186
1
    rw_f.cp_rpw_sc_rw3_f3 = NAN;
187
1
    rw_f.cp_rpw_sc_rw3_f4 = NAN;
188
1
    rw_f.cp_rpw_sc_rw4_f1 = NAN;
189
1
    rw_f.cp_rpw_sc_rw4_f2 = NAN;
190
1
    rw_f.cp_rpw_sc_rw4_f3 = NAN;
191
1
    rw_f.cp_rpw_sc_rw4_f4 = NAN;
192
193
    // initialize filtering parameters
194
1
    filterPar.spare_sy_lfr_pas_filter_enabled   = DEFAULT_SY_LFR_PAS_FILTER_ENABLED;
195
1
    filterPar.sy_lfr_sc_rw_delta_f              = DEFAULT_SY_LFR_SC_RW_DELTA_F;
196
1
    filterPar.sy_lfr_pas_filter_tbad            = DEFAULT_SY_LFR_PAS_FILTER_TBAD;
197
1
    filterPar.sy_lfr_pas_filter_shift           = DEFAULT_SY_LFR_PAS_FILTER_SHIFT;
198
1
    filterPar.modulus_in_finetime               = DEFAULT_MODULUS;
199
1
    filterPar.tbad_in_finetime                  = DEFAULT_TBAD;
200
1
    filterPar.offset_in_finetime                = DEFAULT_OFFSET;
201
1
    filterPar.shift_in_finetime                 = DEFAULT_SHIFT;
202
1
    update_last_valid_transition_date( DEFAULT_LAST_VALID_TRANSITION_DATE  );
203
204
    // waveform picker initialization
205
1
    WFP_init_rings();
206
1
    LEON_Clear_interrupt( IRQ_SPARC_GPTIMER_WATCHDOG );    // initialize the waveform rings
207
1
    WFP_reset_current_ring_nodes();
208
1
    reset_waveform_picker_regs();
209
210
    // spectral matrices initialization
211
1
    SM_init_rings();            // initialize spectral matrices rings
212
1
    SM_reset_current_ring_nodes();
213
1
    reset_spectral_matrix_regs();
214
215
    // configure calibration
216
1
    configureCalibration( false );   // true means interleaved mode, false is for normal mode
217
218
1
    updateLFRCurrentMode( LFR_MODE_STANDBY );
219
220
    BOOT_PRINTF1("in INIT *** lfrCurrentMode is %d\n", lfrCurrentMode)
221
222
1
    create_names();                             // create all names
223
224
1
    status = create_timecode_timer();           // create the timer used by timecode_irq_handler
225
    if (status != RTEMS_SUCCESSFUL)
226
    {
227
        PRINTF1("in INIT *** ERR in create_timer_timecode, code %d", status)
228
    }
229
230
1
    status = create_message_queues();           // create message queues
231
    if (status != RTEMS_SUCCESSFUL)
232
    {
233
        PRINTF1("in INIT *** ERR in create_message_queues, code %d", status)
234
    }
235
236
1
    status = create_all_tasks();                // create all tasks
237
    if (status != RTEMS_SUCCESSFUL)
238
    {
239
        PRINTF1("in INIT *** ERR in create_all_tasks, code %d\n", status)
240
    }
241
242
    // **************************
243
    // <SPACEWIRE INITIALIZATION>
244
1
    status_spw = spacewire_open_link();     // (1) open the link
245
    if ( status_spw != RTEMS_SUCCESSFUL )
246
    {
247
        PRINTF1("in INIT *** ERR spacewire_open_link code %d\n", status_spw )
248
    }
249
250
1
    if ( status_spw == RTEMS_SUCCESSFUL )   // (2) configure the link
251
    {
252
1
        status_spw = spacewire_configure_link( fdSPW );
253
        if ( status_spw != RTEMS_SUCCESSFUL )
254
        {
255
            PRINTF1("in INIT *** ERR spacewire_configure_link code %d\n", status_spw )
256
        }
257
    }
258
259
1
    if ( status_spw == RTEMS_SUCCESSFUL)    // (3) start the link
260
    {
261
1
        status_spw = spacewire_start_link( fdSPW );
262
        if (  status_spw != RTEMS_SUCCESSFUL )
263
        {
264
            PRINTF1("in INIT *** ERR spacewire_start_link code %d\n",  status_spw )
265
        }
266
    }
267
    // </SPACEWIRE INITIALIZATION>
268
    // ***************************
269
270
1
    status = start_all_tasks();     // start all tasks
271
    if (status != RTEMS_SUCCESSFUL)
272
    {
273
        PRINTF1("in INIT *** ERR in start_all_tasks, code %d", status)
274
    }
275
276
    // start RECV and SEND *AFTER* SpaceWire Initialization, due to the timeout of the start call during the initialization
277
1
    status = start_recv_send_tasks();
278
    if ( status != RTEMS_SUCCESSFUL )
279
    {
280
        PRINTF1("in INIT *** ERR start_recv_send_tasks code %d\n",  status )
281
    }
282
283
    // suspend science tasks, they will be restarted later depending on the mode
284
1
    status = suspend_science_tasks();   // suspend science tasks (not done in stop_current_mode if current mode = STANDBY)
285
    if (status != RTEMS_SUCCESSFUL)
286
    {
287
        PRINTF1("in INIT *** in suspend_science_tasks *** ERR code: %d\n", status)
288
    }
289
290
    // configure IRQ handling for the waveform picker unit
291
1
    status = rtems_interrupt_catch( waveforms_isr,
292
                                   IRQ_SPARC_WAVEFORM_PICKER,
293
                                   &old_isr_handler) ;
294
    // configure IRQ handling for the spectral matrices unit
295
1
    status = rtems_interrupt_catch( spectral_matrices_isr,
296
                                   IRQ_SPARC_SPECTRAL_MATRIX,
297
                                   &old_isr_handler) ;
298
299
    // if the spacewire link is not up then send an event to the SPIQ task for link recovery
300
1
    if ( status_spw != RTEMS_SUCCESSFUL )
301
    {
302
        status = rtems_event_send( Task_id[TASKID_SPIQ], SPW_LINKERR_EVENT );
303
        if ( status != RTEMS_SUCCESSFUL ) {
304
            PRINTF1("in INIT *** ERR rtems_event_send to SPIQ code %d\n",  status )
305
        }
306
    }
307
308
    BOOT_PRINTF("delete INIT\n")
309
310
1
    set_hk_lfr_sc_potential_flag( true );
311
312
    // start the timer to detect a missing spacewire timecode
313
    // the timeout is larger because the spw IP needs to receive several valid timecodes before generating a tickout
314
    // if a tickout is generated, the timer is restarted
315
1
    status = rtems_timer_fire_after( timecode_timer_id, TIMECODE_TIMER_TIMEOUT_INIT, timecode_timer_routine, NULL );
316
317
1
    grspw_timecode_callback = &timecode_irq_handler;
318
319
1
    status = rtems_task_delete(RTEMS_SELF);
320
321
}
322
323
1
void init_local_mode_parameters( void )
324
{
325
    /** This function initialize the param_local global variable with default values.
326
     *
327
     */
328
329
    unsigned int i;
330
331
    // LOCAL PARAMETERS
332
333
    BOOT_PRINTF1("local_sbm1_nb_cwf_max %d \n", param_local.local_sbm1_nb_cwf_max)
334
    BOOT_PRINTF1("local_sbm2_nb_cwf_max %d \n", param_local.local_sbm2_nb_cwf_max)
335
336
    // init sequence counters
337
338
13
    for(i = 0; i<SEQ_CNT_NB_DEST_ID; i++)
339
    {
340
12
        sequenceCounters_TC_EXE[i] = INIT_CHAR;
341
12
        sequenceCounters_TM_DUMP[i] = INIT_CHAR;
342
    }
343
1
    sequenceCounters_SCIENCE_NORMAL_BURST = INIT_CHAR;
344
1
    sequenceCounters_SCIENCE_SBM1_SBM2 =    INIT_CHAR;
345
1
    sequenceCounterHK =                     TM_PACKET_SEQ_CTRL_STANDALONE << TM_PACKET_SEQ_SHIFT;
346
1
}
347
348
1
void reset_local_time( void )
349
{
350
1
    time_management_regs->ctrl = time_management_regs->ctrl | VAL_SOFTWARE_RESET;  // [0010] software reset, coarse time = 0x80000000
351
1
}
352
353
1
void create_names( void ) // create all names for tasks and queues
354
{
355
    /** This function creates all RTEMS names used in the software for tasks and queues.
356
     *
357
     * @return RTEMS directive status codes:
358
     * - RTEMS_SUCCESSFUL - successful completion
359
     *
360
     */
361
362
    // task names
363
1
    Task_name[TASKID_AVGV] = rtems_build_name( 'A', 'V', 'G', 'V' );
364
1
    Task_name[TASKID_RECV] = rtems_build_name( 'R', 'E', 'C', 'V' );
365
1
    Task_name[TASKID_ACTN] = rtems_build_name( 'A', 'C', 'T', 'N' );
366
1
    Task_name[TASKID_SPIQ] = rtems_build_name( 'S', 'P', 'I', 'Q' );
367
1
    Task_name[TASKID_LOAD] = rtems_build_name( 'L', 'O', 'A', 'D' );
368
1
    Task_name[TASKID_AVF0] = rtems_build_name( 'A', 'V', 'F', '0' );
369
1
    Task_name[TASKID_SWBD] = rtems_build_name( 'S', 'W', 'B', 'D' );
370
1
    Task_name[TASKID_WFRM] = rtems_build_name( 'W', 'F', 'R', 'M' );
371
1
    Task_name[TASKID_DUMB] = rtems_build_name( 'D', 'U', 'M', 'B' );
372
1
    Task_name[TASKID_HOUS] = rtems_build_name( 'H', 'O', 'U', 'S' );
373
1
    Task_name[TASKID_PRC0] = rtems_build_name( 'P', 'R', 'C', '0' );
374
1
    Task_name[TASKID_CWF3] = rtems_build_name( 'C', 'W', 'F', '3' );
375
1
    Task_name[TASKID_CWF2] = rtems_build_name( 'C', 'W', 'F', '2' );
376
1
    Task_name[TASKID_CWF1] = rtems_build_name( 'C', 'W', 'F', '1' );
377
1
    Task_name[TASKID_SEND] = rtems_build_name( 'S', 'E', 'N', 'D' );
378
1
    Task_name[TASKID_LINK] = rtems_build_name( 'L', 'I', 'N', 'K' );
379
1
    Task_name[TASKID_AVF1] = rtems_build_name( 'A', 'V', 'F', '1' );
380
1
    Task_name[TASKID_PRC1] = rtems_build_name( 'P', 'R', 'C', '1' );
381
1
    Task_name[TASKID_AVF2] = rtems_build_name( 'A', 'V', 'F', '2' );
382
1
    Task_name[TASKID_PRC2] = rtems_build_name( 'P', 'R', 'C', '2' );
383
1
    Task_name[TASKID_SCRB] = rtems_build_name( 'S', 'C', 'R', 'B' );
384
1
    Task_name[TASKID_CALI] = rtems_build_name( 'C', 'A', 'L', 'I' );
385
386
    // rate monotonic period names
387
1
    name_hk_rate_monotonic = rtems_build_name( 'H', 'O', 'U', 'S' );
388
1
    name_avgv_rate_monotonic = rtems_build_name( 'A', 'V', 'G', 'V' );
389
390
1
    misc_name[QUEUE_RECV] = rtems_build_name( 'Q', '_', 'R', 'V' );
391
1
    misc_name[QUEUE_SEND] = rtems_build_name( 'Q', '_', 'S', 'D' );
392
1
    misc_name[QUEUE_PRC0] = rtems_build_name( 'Q', '_', 'P', '0' );
393
1
    misc_name[QUEUE_PRC1] = rtems_build_name( 'Q', '_', 'P', '1' );
394
1
    misc_name[QUEUE_PRC2] = rtems_build_name( 'Q', '_', 'P', '2' );
395
396
1
    timecode_timer_name = rtems_build_name( 'S', 'P', 'T', 'C' );
397
1
}
398
399
1
int create_all_tasks( void ) // create all tasks which run in the software
400
{
401
    /** This function creates all RTEMS tasks used in the software.
402
     *
403
     * @return RTEMS directive status codes:
404
     * - RTEMS_SUCCESSFUL - task created successfully
405
     * - RTEMS_INVALID_ADDRESS - id is NULL
406
     * - RTEMS_INVALID_NAME - invalid task name
407
     * - RTEMS_INVALID_PRIORITY - invalid task priority
408
     * - RTEMS_MP_NOT_CONFIGURED - multiprocessing not configured
409
     * - RTEMS_TOO_MANY - too many tasks created
410
     * - RTEMS_UNSATISFIED - not enough memory for stack/FP context
411
     * - RTEMS_TOO_MANY - too many global objects
412
     *
413
     */
414
415
    rtems_status_code status;
416
417
    //**********
418
    // SPACEWIRE
419
    // RECV
420
1
    status = rtems_task_create(
421
        Task_name[TASKID_RECV], TASK_PRIORITY_RECV, RTEMS_MINIMUM_STACK_SIZE,
422
        RTEMS_DEFAULT_MODES,
423
        RTEMS_DEFAULT_ATTRIBUTES, &Task_id[TASKID_RECV]
424
    );
425
1
    if (status == RTEMS_SUCCESSFUL) // SEND
426
    {
427
1
        status = rtems_task_create(
428
            Task_name[TASKID_SEND], TASK_PRIORITY_SEND, RTEMS_MINIMUM_STACK_SIZE * STACK_SIZE_MULT,
429
            RTEMS_DEFAULT_MODES,
430
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_SEND]
431
        );
432
    }
433
1
    if (status == RTEMS_SUCCESSFUL) // LINK
434
    {
435
1
        status = rtems_task_create(
436
            Task_name[TASKID_LINK], TASK_PRIORITY_LINK, RTEMS_MINIMUM_STACK_SIZE,
437
            RTEMS_DEFAULT_MODES,
438
            RTEMS_DEFAULT_ATTRIBUTES, &Task_id[TASKID_LINK]
439
        );
440
    }
441
1
    if (status == RTEMS_SUCCESSFUL) // ACTN
442
    {
443
1
        status = rtems_task_create(
444
            Task_name[TASKID_ACTN], TASK_PRIORITY_ACTN, RTEMS_MINIMUM_STACK_SIZE,
445
            RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT,
446
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_ACTN]
447
        );
448
    }
449
1
    if (status == RTEMS_SUCCESSFUL) // SPIQ
450
    {
451
1
        status = rtems_task_create(
452
            Task_name[TASKID_SPIQ], TASK_PRIORITY_SPIQ, RTEMS_MINIMUM_STACK_SIZE,
453
            RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT,
454
            RTEMS_DEFAULT_ATTRIBUTES, &Task_id[TASKID_SPIQ]
455
        );
456
    }
457
458
    //******************
459
    // SPECTRAL MATRICES
460
1
    if (status == RTEMS_SUCCESSFUL) // AVF0
461
    {
462
1
        status = rtems_task_create(
463
            Task_name[TASKID_AVF0], TASK_PRIORITY_AVF0, RTEMS_MINIMUM_STACK_SIZE,
464
            RTEMS_DEFAULT_MODES,
465
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_AVF0]
466
        );
467
    }
468
1
    if (status == RTEMS_SUCCESSFUL) // PRC0
469
    {
470
1
        status = rtems_task_create(
471
            Task_name[TASKID_PRC0], TASK_PRIORITY_PRC0, RTEMS_MINIMUM_STACK_SIZE * STACK_SIZE_MULT,
472
            RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT,
473
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_PRC0]
474
        );
475
    }
476
1
    if (status == RTEMS_SUCCESSFUL) // AVF1
477
    {
478
1
        status = rtems_task_create(
479
            Task_name[TASKID_AVF1], TASK_PRIORITY_AVF1, RTEMS_MINIMUM_STACK_SIZE,
480
            RTEMS_DEFAULT_MODES,
481
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_AVF1]
482
        );
483
    }
484
1
    if (status == RTEMS_SUCCESSFUL) // PRC1
485
    {
486
1
        status = rtems_task_create(
487
            Task_name[TASKID_PRC1], TASK_PRIORITY_PRC1, RTEMS_MINIMUM_STACK_SIZE * STACK_SIZE_MULT,
488
            RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT,
489
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_PRC1]
490
        );
491
    }
492
1
    if (status == RTEMS_SUCCESSFUL) // AVF2
493
    {
494
1
        status = rtems_task_create(
495
            Task_name[TASKID_AVF2], TASK_PRIORITY_AVF2, RTEMS_MINIMUM_STACK_SIZE,
496
            RTEMS_DEFAULT_MODES,
497
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_AVF2]
498
        );
499
    }
500
1
    if (status == RTEMS_SUCCESSFUL) // PRC2
501
    {
502
1
        status = rtems_task_create(
503
            Task_name[TASKID_PRC2], TASK_PRIORITY_PRC2, RTEMS_MINIMUM_STACK_SIZE * STACK_SIZE_MULT,
504
            RTEMS_DEFAULT_MODES | RTEMS_NO_PREEMPT,
505
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_PRC2]
506
        );
507
    }
508
509
    //****************
510
    // WAVEFORM PICKER
511
1
    if (status == RTEMS_SUCCESSFUL) // WFRM
512
    {
513
1
        status = rtems_task_create(
514
            Task_name[TASKID_WFRM], TASK_PRIORITY_WFRM, RTEMS_MINIMUM_STACK_SIZE,
515
            RTEMS_DEFAULT_MODES,
516
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_WFRM]
517
        );
518
    }
519
1
    if (status == RTEMS_SUCCESSFUL) // CWF3
520
    {
521
1
        status = rtems_task_create(
522
            Task_name[TASKID_CWF3], TASK_PRIORITY_CWF3, RTEMS_MINIMUM_STACK_SIZE,
523
            RTEMS_DEFAULT_MODES,
524
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_CWF3]
525
        );
526
    }
527
1
    if (status == RTEMS_SUCCESSFUL) // CWF2
528
    {
529
1
        status = rtems_task_create(
530
            Task_name[TASKID_CWF2], TASK_PRIORITY_CWF2, RTEMS_MINIMUM_STACK_SIZE,
531
            RTEMS_DEFAULT_MODES,
532
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_CWF2]
533
        );
534
    }
535
1
    if (status == RTEMS_SUCCESSFUL) // CWF1
536
    {
537
1
        status = rtems_task_create(
538
            Task_name[TASKID_CWF1], TASK_PRIORITY_CWF1, RTEMS_MINIMUM_STACK_SIZE,
539
            RTEMS_DEFAULT_MODES,
540
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_CWF1]
541
        );
542
    }
543
1
    if (status == RTEMS_SUCCESSFUL) // SWBD
544
    {
545
1
        status = rtems_task_create(
546
            Task_name[TASKID_SWBD], TASK_PRIORITY_SWBD, RTEMS_MINIMUM_STACK_SIZE,
547
            RTEMS_DEFAULT_MODES,
548
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_SWBD]
549
        );
550
    }
551
552
    //*****
553
    // MISC
554
1
    if (status == RTEMS_SUCCESSFUL) // LOAD
555
    {
556
1
        status = rtems_task_create(
557
            Task_name[TASKID_LOAD], TASK_PRIORITY_LOAD, RTEMS_MINIMUM_STACK_SIZE,
558
            RTEMS_DEFAULT_MODES,
559
            RTEMS_DEFAULT_ATTRIBUTES, &Task_id[TASKID_LOAD]
560
        );
561
    }
562
1
    if (status == RTEMS_SUCCESSFUL) // DUMB
563
    {
564
1
        status = rtems_task_create(
565
            Task_name[TASKID_DUMB], TASK_PRIORITY_DUMB, RTEMS_MINIMUM_STACK_SIZE,
566
            RTEMS_DEFAULT_MODES,
567
            RTEMS_DEFAULT_ATTRIBUTES, &Task_id[TASKID_DUMB]
568
        );
569
    }
570
1
    if (status == RTEMS_SUCCESSFUL) // SCRUBBING TASK
571
    {
572
1
        status = rtems_task_create(
573
            Task_name[TASKID_SCRB], TASK_PRIORITY_SCRB, RTEMS_MINIMUM_STACK_SIZE,
574
            RTEMS_DEFAULT_MODES,
575
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_SCRB]
576
        );
577
    }
578
1
    if (status == RTEMS_SUCCESSFUL) // HOUS
579
    {
580
1
        status = rtems_task_create(
581
            Task_name[TASKID_HOUS], TASK_PRIORITY_HOUS, RTEMS_MINIMUM_STACK_SIZE,
582
            RTEMS_DEFAULT_MODES,
583
            RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_HOUS]
584
        );
585
    }
586
1
    if (status == RTEMS_SUCCESSFUL) // AVGV
587
    {
588
1
        status = rtems_task_create(
589
                    Task_name[TASKID_AVGV], TASK_PRIORITY_AVGV, RTEMS_MINIMUM_STACK_SIZE,
590
                    RTEMS_DEFAULT_MODES,
591
                    RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_AVGV]
592
                    );
593
    }
594
1
    if (status == RTEMS_SUCCESSFUL) // CALI
595
    {
596
1
        status = rtems_task_create(
597
                    Task_name[TASKID_CALI], TASK_PRIORITY_CALI, RTEMS_MINIMUM_STACK_SIZE,
598
                    RTEMS_DEFAULT_MODES,
599
                    RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT, &Task_id[TASKID_CALI]
600
                    );
601
    }
602
603
1
    return status;
604
}
605
606
1
int start_recv_send_tasks( void )
607
{
608
    rtems_status_code status;
609
610
1
    status = rtems_task_start( Task_id[TASKID_RECV], recv_task, 1 );
611
    if (status!=RTEMS_SUCCESSFUL) {
612
        BOOT_PRINTF("in INIT *** Error starting TASK_RECV\n")
613
    }
614
615
1
    if (status == RTEMS_SUCCESSFUL)     // SEND
616
    {
617
1
        status = rtems_task_start( Task_id[TASKID_SEND], send_task, 1 );
618
        if (status!=RTEMS_SUCCESSFUL) {
619
            BOOT_PRINTF("in INIT *** Error starting TASK_SEND\n")
620
        }
621
    }
622
623
1
    return status;
624
}
625
626
1
int start_all_tasks( void ) // start all tasks except SEND RECV and HOUS
627
{
628
    /** This function starts all RTEMS tasks used in the software.
629
     *
630
     * @return RTEMS directive status codes:
631
     * - RTEMS_SUCCESSFUL - ask started successfully
632
     * - RTEMS_INVALID_ADDRESS - invalid task entry point
633
     * - RTEMS_INVALID_ID - invalid task id
634
     * - RTEMS_INCORRECT_STATE - task not in the dormant state
635
     * - RTEMS_ILLEGAL_ON_REMOTE_OBJECT - cannot start remote task
636
     *
637
     */
638
    // starts all the tasks fot eh flight software
639
640
    rtems_status_code status;
641
642
    //**********
643
    // SPACEWIRE
644
1
    status = rtems_task_start( Task_id[TASKID_SPIQ], spiq_task, 1 );
645
    if (status!=RTEMS_SUCCESSFUL) {
646
        BOOT_PRINTF("in INIT *** Error starting TASK_SPIQ\n")
647
    }
648
649
1
    if (status == RTEMS_SUCCESSFUL)     // LINK
650
    {
651
1
        status = rtems_task_start( Task_id[TASKID_LINK], link_task, 1 );
652
        if (status!=RTEMS_SUCCESSFUL) {
653
            BOOT_PRINTF("in INIT *** Error starting TASK_LINK\n")
654
        }
655
    }
656
657
1
    if (status == RTEMS_SUCCESSFUL)     // ACTN
658
    {
659
1
        status = rtems_task_start( Task_id[TASKID_ACTN], actn_task, 1 );
660
        if (status!=RTEMS_SUCCESSFUL) {
661
            BOOT_PRINTF("in INIT *** Error starting TASK_ACTN\n")
662
        }
663
    }
664
665
    //******************
666
    // SPECTRAL MATRICES
667
1
    if (status == RTEMS_SUCCESSFUL)     // AVF0
668
    {
669
1
        status = rtems_task_start( Task_id[TASKID_AVF0], avf0_task, LFR_MODE_STANDBY );
670
        if (status!=RTEMS_SUCCESSFUL) {
671
            BOOT_PRINTF("in INIT *** Error starting TASK_AVF0\n")
672
        }
673
    }
674
1
    if (status == RTEMS_SUCCESSFUL)     // PRC0
675
    {
676
1
        status = rtems_task_start( Task_id[TASKID_PRC0], prc0_task, LFR_MODE_STANDBY );
677
        if (status!=RTEMS_SUCCESSFUL) {
678
            BOOT_PRINTF("in INIT *** Error starting TASK_PRC0\n")
679
        }
680
    }
681
1
    if (status == RTEMS_SUCCESSFUL)     // AVF1
682
    {
683
1
        status = rtems_task_start( Task_id[TASKID_AVF1], avf1_task, LFR_MODE_STANDBY );
684
        if (status!=RTEMS_SUCCESSFUL) {
685
            BOOT_PRINTF("in INIT *** Error starting TASK_AVF1\n")
686
        }
687
    }
688
1
    if (status == RTEMS_SUCCESSFUL)     // PRC1
689
    {
690
1
        status = rtems_task_start( Task_id[TASKID_PRC1], prc1_task, LFR_MODE_STANDBY );
691
        if (status!=RTEMS_SUCCESSFUL) {
692
            BOOT_PRINTF("in INIT *** Error starting TASK_PRC1\n")
693
        }
694
    }
695
1
    if (status == RTEMS_SUCCESSFUL)     // AVF2
696
    {
697
1
        status = rtems_task_start( Task_id[TASKID_AVF2], avf2_task, 1 );
698
        if (status!=RTEMS_SUCCESSFUL) {
699
            BOOT_PRINTF("in INIT *** Error starting TASK_AVF2\n")
700
        }
701
    }
702
1
    if (status == RTEMS_SUCCESSFUL)     // PRC2
703
    {
704
1
        status = rtems_task_start( Task_id[TASKID_PRC2], prc2_task, 1 );
705
        if (status!=RTEMS_SUCCESSFUL) {
706
            BOOT_PRINTF("in INIT *** Error starting TASK_PRC2\n")
707
        }
708
    }
709
710
    //****************
711
    // WAVEFORM PICKER
712
1
    if (status == RTEMS_SUCCESSFUL)     // WFRM
713
    {
714
1
        status = rtems_task_start( Task_id[TASKID_WFRM], wfrm_task, 1 );
715
        if (status!=RTEMS_SUCCESSFUL) {
716
            BOOT_PRINTF("in INIT *** Error starting TASK_WFRM\n")
717
        }
718
    }
719
1
    if (status == RTEMS_SUCCESSFUL)     // CWF3
720
    {
721
1
        status = rtems_task_start( Task_id[TASKID_CWF3], cwf3_task, 1 );
722
        if (status!=RTEMS_SUCCESSFUL) {
723
            BOOT_PRINTF("in INIT *** Error starting TASK_CWF3\n")
724
        }
725
    }
726
1
    if (status == RTEMS_SUCCESSFUL)     // CWF2
727
    {
728
1
        status = rtems_task_start( Task_id[TASKID_CWF2], cwf2_task, 1 );
729
        if (status!=RTEMS_SUCCESSFUL) {
730
            BOOT_PRINTF("in INIT *** Error starting TASK_CWF2\n")
731
        }
732
    }
733
1
    if (status == RTEMS_SUCCESSFUL)     // CWF1
734
    {
735
1
        status = rtems_task_start( Task_id[TASKID_CWF1], cwf1_task, 1 );
736
        if (status!=RTEMS_SUCCESSFUL) {
737
            BOOT_PRINTF("in INIT *** Error starting TASK_CWF1\n")
738
        }
739
    }
740
1
    if (status == RTEMS_SUCCESSFUL)     // SWBD
741
    {
742
1
        status = rtems_task_start( Task_id[TASKID_SWBD], swbd_task, 1 );
743
        if (status!=RTEMS_SUCCESSFUL) {
744
            BOOT_PRINTF("in INIT *** Error starting TASK_SWBD\n")
745
        }
746
    }
747
748
    //*****
749
    // MISC
750
1
    if (status == RTEMS_SUCCESSFUL)     // HOUS
751
    {
752
1
        status = rtems_task_start( Task_id[TASKID_HOUS], hous_task, 1 );
753
        if (status!=RTEMS_SUCCESSFUL) {
754
            BOOT_PRINTF("in INIT *** Error starting TASK_HOUS\n")
755
        }
756
    }
757
1
    if (status == RTEMS_SUCCESSFUL)     // AVGV
758
    {
759
1
        status = rtems_task_start( Task_id[TASKID_AVGV], avgv_task, 1 );
760
        if (status!=RTEMS_SUCCESSFUL) {
761
            BOOT_PRINTF("in INIT *** Error starting TASK_AVGV\n")
762
        }
763
    }
764
1
    if (status == RTEMS_SUCCESSFUL)     // DUMB
765
    {
766
1
        status = rtems_task_start( Task_id[TASKID_DUMB], dumb_task, 1 );
767
        if (status!=RTEMS_SUCCESSFUL) {
768
            BOOT_PRINTF("in INIT *** Error starting TASK_DUMB\n")
769
        }
770
    }
771
1
    if (status == RTEMS_SUCCESSFUL)     // SCRUBBING
772
    {
773
1
        status = rtems_task_start( Task_id[TASKID_SCRB], scrubbing_task, 1 );
774
        if (status!=RTEMS_SUCCESSFUL) {
775
            BOOT_PRINTF("in INIT *** Error starting TASK_DUMB\n")
776
        }
777
    }
778
1
    if (status == RTEMS_SUCCESSFUL)     // LOAD
779
    {
780
1
        status = rtems_task_start( Task_id[TASKID_LOAD], load_task, 1 );
781
        if (status!=RTEMS_SUCCESSFUL) {
782
            BOOT_PRINTF("in INIT *** Error starting TASK_LOAD\n")
783
        }
784
    }
785
1
    if (status == RTEMS_SUCCESSFUL)     // CALI
786
    {
787
1
        status = rtems_task_start( Task_id[TASKID_CALI], calibration_sweep_task, 1 );
788
        if (status!=RTEMS_SUCCESSFUL) {
789
            BOOT_PRINTF("in INIT *** Error starting TASK_LOAD\n")
790
        }
791
    }
792
793
1
    return status;
794
}
795
796
1
rtems_status_code create_message_queues( void ) // create the five message queues used in the software
797
{
798
    rtems_status_code status_recv;
799
    rtems_status_code status_send;
800
    rtems_status_code status_q_p0;
801
    rtems_status_code status_q_p1;
802
    rtems_status_code status_q_p2;
803
    rtems_status_code ret;
804
    rtems_id queue_id;
805
806
1
    ret = RTEMS_SUCCESSFUL;
807
1
    queue_id = RTEMS_ID_NONE;
808
809
    //****************************************
810
    // create the queue for handling valid TCs
811
1
    status_recv = rtems_message_queue_create( misc_name[QUEUE_RECV],
812
                                              MSG_QUEUE_COUNT_RECV, CCSDS_TC_PKT_MAX_SIZE,
813
                                         RTEMS_FIFO | RTEMS_LOCAL, &queue_id );
814
    if ( status_recv != RTEMS_SUCCESSFUL ) {
815
        PRINTF1("in create_message_queues *** ERR creating QUEU queue, %d\n", status_recv)
816
    }
817
818
    //************************************************
819
    // create the queue for handling TM packet sending
820
1
    status_send = rtems_message_queue_create( misc_name[QUEUE_SEND],
821
                                              MSG_QUEUE_COUNT_SEND, MSG_QUEUE_SIZE_SEND,
822
                                      RTEMS_FIFO | RTEMS_LOCAL, &queue_id );
823
    if ( status_send != RTEMS_SUCCESSFUL ) {
824
        PRINTF1("in create_message_queues *** ERR creating PKTS queue, %d\n", status_send)
825
    }
826
827
    //*****************************************************************************
828
    // create the queue for handling averaged spectral matrices for processing @ f0
829
1
    status_q_p0 = rtems_message_queue_create( misc_name[QUEUE_PRC0],
830
                                              MSG_QUEUE_COUNT_PRC0, MSG_QUEUE_SIZE_PRC0,
831
                                      RTEMS_FIFO | RTEMS_LOCAL, &queue_id );
832
    if ( status_q_p0 != RTEMS_SUCCESSFUL ) {
833
        PRINTF1("in create_message_queues *** ERR creating Q_P0 queue, %d\n", status_q_p0)
834
    }
835
836
    //*****************************************************************************
837
    // create the queue for handling averaged spectral matrices for processing @ f1
838
1
    status_q_p1 = rtems_message_queue_create( misc_name[QUEUE_PRC1],
839
                                              MSG_QUEUE_COUNT_PRC1, MSG_QUEUE_SIZE_PRC1,
840
                                      RTEMS_FIFO | RTEMS_LOCAL, &queue_id );
841
    if ( status_q_p1 != RTEMS_SUCCESSFUL ) {
842
        PRINTF1("in create_message_queues *** ERR creating Q_P1 queue, %d\n", status_q_p1)
843
    }
844
845
    //*****************************************************************************
846
    // create the queue for handling averaged spectral matrices for processing @ f2
847
1
    status_q_p2 = rtems_message_queue_create( misc_name[QUEUE_PRC2],
848
                                              MSG_QUEUE_COUNT_PRC2, MSG_QUEUE_SIZE_PRC2,
849
                                      RTEMS_FIFO | RTEMS_LOCAL, &queue_id );
850
    if ( status_q_p2 != RTEMS_SUCCESSFUL ) {
851
        PRINTF1("in create_message_queues *** ERR creating Q_P2 queue, %d\n", status_q_p2)
852
    }
853
854
1
    if ( status_recv != RTEMS_SUCCESSFUL )
855
    {
856
        ret = status_recv;
857
    }
858
1
    else if( status_send != RTEMS_SUCCESSFUL )
859
    {
860
        ret = status_send;
861
    }
862
1
    else if( status_q_p0 != RTEMS_SUCCESSFUL )
863
    {
864
        ret = status_q_p0;
865
    }
866
1
    else if( status_q_p1 != RTEMS_SUCCESSFUL )
867
    {
868
        ret = status_q_p1;
869
    }
870
    else
871
    {
872
1
        ret = status_q_p2;
873
    }
874
875
1
    return ret;
876
}
877
878
1
rtems_status_code create_timecode_timer( void )
879
{
880
    rtems_status_code status;
881
882
1
    status =  rtems_timer_create( timecode_timer_name, &timecode_timer_id );
883
884
    if ( status != RTEMS_SUCCESSFUL )
885
    {
886
        PRINTF1("in create_timer_timecode *** ERR creating SPTC timer, %d\n", status)
887
    }
888
    else
889
    {
890
        PRINTF("in create_timer_timecode *** OK creating SPTC timer\n")
891
    }
892
893
1
    return status;
894
}
895
896
4
rtems_status_code get_message_queue_id_send( rtems_id *queue_id )
897
{
898
    rtems_status_code status;
899
    rtems_name queue_name;
900
901
4
    queue_name = rtems_build_name( 'Q', '_', 'S', 'D' );
902
903
4
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
904
905
4
    return status;
906
}
907
908
2
rtems_status_code get_message_queue_id_recv( rtems_id *queue_id )
909
{
910
    rtems_status_code status;
911
    rtems_name queue_name;
912
913
2
    queue_name = rtems_build_name( 'Q', '_', 'R', 'V' );
914
915
2
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
916
917
2
    return status;
918
}
919
920
rtems_status_code get_message_queue_id_prc0( rtems_id *queue_id )
921
{
922
    rtems_status_code status;
923
    rtems_name queue_name;
924
925
    queue_name = rtems_build_name( 'Q', '_', 'P', '0' );
926
927
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
928
929
    return status;
930
}
931
932
rtems_status_code get_message_queue_id_prc1( rtems_id *queue_id )
933
{
934
    rtems_status_code status;
935
    rtems_name queue_name;
936
937
    queue_name = rtems_build_name( 'Q', '_', 'P', '1' );
938
939
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
940
941
    return status;
942
}
943
944
rtems_status_code get_message_queue_id_prc2( rtems_id *queue_id )
945
{
946
    rtems_status_code status;
947
    rtems_name queue_name;
948
949
    queue_name = rtems_build_name( 'Q', '_', 'P', '2' );
950
951
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
952
953
    return status;
954
}
955
956
/**
957
 * @brief update_queue_max_count returns max(fifo_size_max, pending_messages + 1)
958
 * @param queue_id
959
 * @param fifo_size_max
960
 */
961
152
void update_queue_max_count( rtems_id queue_id, unsigned char*fifo_size_max )
962
{
963
    u_int32_t count;
964
    rtems_status_code status;
965
966
152
    count = 0;
967
968
152
    status = rtems_message_queue_get_number_pending( queue_id, &count );
969
970
152
    count = count + 1;
971
972
152
    if (status != RTEMS_SUCCESSFUL)
973
    {
974
        PRINTF1("in update_queue_max_count *** ERR = %d\n", status)
975
    }
976
    else
977
    {
978
152
        if (count > *fifo_size_max)
979
        {
980
2
            *fifo_size_max = count;
981
        }
982
    }
983
152
}
984
985
/**
986
 * @brief init_ring initializes given ring buffer
987
 * @param ring array of nodes to initialize
988
 * @param nbNodes number of node in the ring buffer
989
 * @param buffer memory space given to the ring buffer
990
 * @param bufferSize size of the whole ring buffer memory space
991
 *
992
 * @details This function creates a circular buffer from a given number of nodes and a given memory space. It first sets all nodes attributes to thier defaults values
993
 * and associates a portion of the given memory space with each node. Then it connects each nodes to build a circular buffer.
994
 *
995
 * Each node capacity will be bufferSize/nbNodes.
996
 *
997
 * https://en.wikipedia.org/wiki/Circular_buffer
998
 */
999
7
void init_ring(ring_node ring[], unsigned char nbNodes, volatile int buffer[], unsigned int bufferSize )
1000
{
1001
    unsigned char i;
1002
1003
    //***************
1004
    // BUFFER ADDRESS
1005
60
    for(i=0; i<nbNodes; i++)
1006
    {
1007
53
        ring[i].coarseTime  = INT32_ALL_F;
1008
53
        ring[i].fineTime    = INT32_ALL_F;
1009
53
        ring[i].sid         = INIT_CHAR;
1010
53
        ring[i].status      = INIT_CHAR;
1011
53
        ring[i].buffer_address  = (int) &buffer[ i * bufferSize ];
1012
    }
1013
1014
    //*****
1015
    // NEXT
1016
7
     ring[ nbNodes - 1 ].next  = (ring_node*) &ring[ 0 ];
1017
53
     for(i=0; i<nbNodes-1; i++)
1018
     {
1019
46
         ring[i].next      = (ring_node*) &ring[ i + 1 ];
1020
     }
1021
1022
    //*********
1023
    // PREVIOUS
1024
7
    ring[ 0 ].previous       = (ring_node*) &ring[ nbNodes - 1 ];
1025
53
    for(i=1; i<nbNodes; i++)
1026
    {
1027
46
        ring[i].previous   = (ring_node*) &ring[ i - 1 ];
1028
    }
1029
7
}