GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/fsw_init.c Lines: 272 277 98.2 %
Date: 2018-11-13 11:16:07 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
87
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
87
    CCR_resetCacheControlRegister();
93
87
    ASR16_resetRegisterProtectionControlRegister();
94
95
87
    cacheControlRegister = CCR_getValue();
96
    PRINTF1("(0) CCR - Cache Control Register = %x\n", cacheControlRegister);
97
    PRINTF1("(0) ASR16                        = %x\n", *asr16Ptr);
98
99
87
    CCR_enableInstructionCache();       // ICS bits
100
87
    CCR_enableDataCache();              // DCS bits
101
87
    CCR_enableInstructionBurstFetch();  // IB  bit
102
103
87
    faultTolerantScheme();
104
105
87
    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
87
}
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
87
    reset_lfr();
128
129
87
    reset_local_time();
130
131
87
    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
87
    old_isr_handler = NULL;
138
139
    // UART settings
140
87
    enable_apbuart_transmitter();
141
87
    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
87
    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
87
    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
87
    init_parameter_dump();
167
87
    init_kcoefficients_dump();
168
87
    init_local_mode_parameters();
169
87
    init_housekeeping_parameters();
170
87
    init_k_coefficients_prc0();
171
87
    init_k_coefficients_prc1();
172
87
    init_k_coefficients_prc2();
173
87
    pa_bia_status_info = INIT_CHAR;
174
175
    // initialize all reaction wheels frequencies to NaN
176
87
    rw_f.cp_rpw_sc_rw1_f1 = NAN;
177
87
    rw_f.cp_rpw_sc_rw1_f2 = NAN;
178
87
    rw_f.cp_rpw_sc_rw1_f3 = NAN;
179
87
    rw_f.cp_rpw_sc_rw1_f4 = NAN;
180
87
    rw_f.cp_rpw_sc_rw2_f1 = NAN;
181
87
    rw_f.cp_rpw_sc_rw2_f2 = NAN;
182
87
    rw_f.cp_rpw_sc_rw2_f3 = NAN;
183
87
    rw_f.cp_rpw_sc_rw2_f4 = NAN;
184
87
    rw_f.cp_rpw_sc_rw3_f1 = NAN;
185
87
    rw_f.cp_rpw_sc_rw3_f2 = NAN;
186
87
    rw_f.cp_rpw_sc_rw3_f3 = NAN;
187
87
    rw_f.cp_rpw_sc_rw3_f4 = NAN;
188
87
    rw_f.cp_rpw_sc_rw4_f1 = NAN;
189
87
    rw_f.cp_rpw_sc_rw4_f2 = NAN;
190
87
    rw_f.cp_rpw_sc_rw4_f3 = NAN;
191
87
    rw_f.cp_rpw_sc_rw4_f4 = NAN;
192
193
    // initialize filtering parameters
194
87
    filterPar.spare_sy_lfr_pas_filter_enabled   = DEFAULT_SY_LFR_PAS_FILTER_ENABLED;
195
87
    filterPar.sy_lfr_sc_rw_delta_f              = DEFAULT_SY_LFR_SC_RW_DELTA_F;
196
87
    filterPar.sy_lfr_pas_filter_tbad            = DEFAULT_SY_LFR_PAS_FILTER_TBAD;
197
87
    filterPar.sy_lfr_pas_filter_shift           = DEFAULT_SY_LFR_PAS_FILTER_SHIFT;
198
87
    filterPar.modulus_in_finetime               = DEFAULT_MODULUS;
199
87
    filterPar.tbad_in_finetime                  = DEFAULT_TBAD;
200
87
    filterPar.offset_in_finetime                = DEFAULT_OFFSET;
201
87
    filterPar.shift_in_finetime                 = DEFAULT_SHIFT;
202
87
    update_last_valid_transition_date( DEFAULT_LAST_VALID_TRANSITION_DATE  );
203
204
    // waveform picker initialization
205
87
    WFP_init_rings();
206
87
    LEON_Clear_interrupt( IRQ_SPARC_GPTIMER_WATCHDOG );    // initialize the waveform rings
207
87
    WFP_reset_current_ring_nodes();
208
87
    reset_waveform_picker_regs();
209
210
    // spectral matrices initialization
211
87
    SM_init_rings();            // initialize spectral matrices rings
212
87
    SM_reset_current_ring_nodes();
213
87
    reset_spectral_matrix_regs();
214
215
    // configure calibration
216
87
    configureCalibration( false );   // true means interleaved mode, false is for normal mode
217
218
87
    updateLFRCurrentMode( LFR_MODE_STANDBY );
219
220
    BOOT_PRINTF1("in INIT *** lfrCurrentMode is %d\n", lfrCurrentMode)
221
222
87
    create_names();                             // create all names
223
224
87
    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
87
    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
87
    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
87
    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
87
    if ( status_spw == RTEMS_SUCCESSFUL )   // (2) configure the link
251
    {
252
87
        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
87
    if ( status_spw == RTEMS_SUCCESSFUL)    // (3) start the link
260
    {
261
87
        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
87
    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
87
    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
87
    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
87
    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
87
    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
87
    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
87
    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
87
    status = rtems_timer_fire_after( timecode_timer_id, TIMECODE_TIMER_TIMEOUT_INIT, timecode_timer_routine, NULL );
316
317
87
    grspw_timecode_callback = &timecode_irq_handler;
318
319
87
    status = rtems_task_delete(RTEMS_SELF);
320
321
}
322
323
87
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
1131
    for(i = 0; i<SEQ_CNT_NB_DEST_ID; i++)
339
    {
340
1044
        sequenceCounters_TC_EXE[i] = INIT_CHAR;
341
1044
        sequenceCounters_TM_DUMP[i] = INIT_CHAR;
342
    }
343
87
    sequenceCounters_SCIENCE_NORMAL_BURST = INIT_CHAR;
344
87
    sequenceCounters_SCIENCE_SBM1_SBM2 =    INIT_CHAR;
345
87
    sequenceCounterHK =                     TM_PACKET_SEQ_CTRL_STANDALONE << TM_PACKET_SEQ_SHIFT;
346
87
}
347
348
87
void reset_local_time( void )
349
{
350
87
    time_management_regs->ctrl = time_management_regs->ctrl | VAL_SOFTWARE_RESET;  // [0010] software reset, coarse time = 0x80000000
351
87
}
352
353
87
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
87
    Task_name[TASKID_AVGV] = rtems_build_name( 'A', 'V', 'G', 'V' );
364
87
    Task_name[TASKID_RECV] = rtems_build_name( 'R', 'E', 'C', 'V' );
365
87
    Task_name[TASKID_ACTN] = rtems_build_name( 'A', 'C', 'T', 'N' );
366
87
    Task_name[TASKID_SPIQ] = rtems_build_name( 'S', 'P', 'I', 'Q' );
367
87
    Task_name[TASKID_LOAD] = rtems_build_name( 'L', 'O', 'A', 'D' );
368
87
    Task_name[TASKID_AVF0] = rtems_build_name( 'A', 'V', 'F', '0' );
369
87
    Task_name[TASKID_SWBD] = rtems_build_name( 'S', 'W', 'B', 'D' );
370
87
    Task_name[TASKID_WFRM] = rtems_build_name( 'W', 'F', 'R', 'M' );
371
87
    Task_name[TASKID_DUMB] = rtems_build_name( 'D', 'U', 'M', 'B' );
372
87
    Task_name[TASKID_HOUS] = rtems_build_name( 'H', 'O', 'U', 'S' );
373
87
    Task_name[TASKID_PRC0] = rtems_build_name( 'P', 'R', 'C', '0' );
374
87
    Task_name[TASKID_CWF3] = rtems_build_name( 'C', 'W', 'F', '3' );
375
87
    Task_name[TASKID_CWF2] = rtems_build_name( 'C', 'W', 'F', '2' );
376
87
    Task_name[TASKID_CWF1] = rtems_build_name( 'C', 'W', 'F', '1' );
377
87
    Task_name[TASKID_SEND] = rtems_build_name( 'S', 'E', 'N', 'D' );
378
87
    Task_name[TASKID_LINK] = rtems_build_name( 'L', 'I', 'N', 'K' );
379
87
    Task_name[TASKID_AVF1] = rtems_build_name( 'A', 'V', 'F', '1' );
380
87
    Task_name[TASKID_PRC1] = rtems_build_name( 'P', 'R', 'C', '1' );
381
87
    Task_name[TASKID_AVF2] = rtems_build_name( 'A', 'V', 'F', '2' );
382
87
    Task_name[TASKID_PRC2] = rtems_build_name( 'P', 'R', 'C', '2' );
383
87
    Task_name[TASKID_SCRB] = rtems_build_name( 'S', 'C', 'R', 'B' );
384
87
    Task_name[TASKID_CALI] = rtems_build_name( 'C', 'A', 'L', 'I' );
385
386
    // rate monotonic period names
387
87
    name_hk_rate_monotonic = rtems_build_name( 'H', 'O', 'U', 'S' );
388
87
    name_avgv_rate_monotonic = rtems_build_name( 'A', 'V', 'G', 'V' );
389
390
87
    misc_name[QUEUE_RECV] = rtems_build_name( 'Q', '_', 'R', 'V' );
391
87
    misc_name[QUEUE_SEND] = rtems_build_name( 'Q', '_', 'S', 'D' );
392
87
    misc_name[QUEUE_PRC0] = rtems_build_name( 'Q', '_', 'P', '0' );
393
87
    misc_name[QUEUE_PRC1] = rtems_build_name( 'Q', '_', 'P', '1' );
394
87
    misc_name[QUEUE_PRC2] = rtems_build_name( 'Q', '_', 'P', '2' );
395
396
87
    timecode_timer_name = rtems_build_name( 'S', 'P', 'T', 'C' );
397
87
}
398
399
87
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
87
    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
87
    if (status == RTEMS_SUCCESSFUL) // SEND
426
    {
427
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // LINK
434
    {
435
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // ACTN
442
    {
443
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // SPIQ
450
    {
451
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // AVF0
461
    {
462
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // PRC0
469
    {
470
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // AVF1
477
    {
478
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // PRC1
485
    {
486
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // AVF2
493
    {
494
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // PRC2
501
    {
502
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // WFRM
512
    {
513
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // CWF3
520
    {
521
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // CWF2
528
    {
529
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // CWF1
536
    {
537
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // SWBD
544
    {
545
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // LOAD
555
    {
556
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // DUMB
563
    {
564
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // SCRUBBING TASK
571
    {
572
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // HOUS
579
    {
580
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // AVGV
587
    {
588
87
        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
87
    if (status == RTEMS_SUCCESSFUL) // CALI
595
    {
596
87
        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
87
    return status;
604
}
605
606
87
int start_recv_send_tasks( void )
607
{
608
    rtems_status_code status;
609
610
87
    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
87
    if (status == RTEMS_SUCCESSFUL)     // SEND
616
    {
617
87
        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
87
    return status;
624
}
625
626
87
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
87
    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
87
    if (status == RTEMS_SUCCESSFUL)     // LINK
650
    {
651
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // ACTN
658
    {
659
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // AVF0
668
    {
669
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // PRC0
675
    {
676
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // AVF1
682
    {
683
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // PRC1
689
    {
690
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // AVF2
696
    {
697
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // PRC2
703
    {
704
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // WFRM
713
    {
714
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // CWF3
720
    {
721
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // CWF2
727
    {
728
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // CWF1
734
    {
735
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // SWBD
741
    {
742
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // HOUS
751
    {
752
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // AVGV
758
    {
759
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // DUMB
765
    {
766
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // SCRUBBING
772
    {
773
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // LOAD
779
    {
780
87
        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
87
    if (status == RTEMS_SUCCESSFUL)     // CALI
786
    {
787
87
        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
87
    return status;
794
}
795
796
87
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
87
    ret = RTEMS_SUCCESSFUL;
807
87
    queue_id = RTEMS_ID_NONE;
808
809
    //****************************************
810
    // create the queue for handling valid TCs
811
87
    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
87
    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
87
    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
87
    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
87
    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
87
    if ( status_recv != RTEMS_SUCCESSFUL )
855
    {
856
        ret = status_recv;
857
    }
858
87
    else if( status_send != RTEMS_SUCCESSFUL )
859
    {
860
        ret = status_send;
861
    }
862
87
    else if( status_q_p0 != RTEMS_SUCCESSFUL )
863
    {
864
        ret = status_q_p0;
865
    }
866
87
    else if( status_q_p1 != RTEMS_SUCCESSFUL )
867
    {
868
        ret = status_q_p1;
869
    }
870
    else
871
    {
872
87
        ret = status_q_p2;
873
    }
874
875
87
    return ret;
876
}
877
878
87
rtems_status_code create_timecode_timer( void )
879
{
880
    rtems_status_code status;
881
882
87
    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
87
    return status;
894
}
895
896
9852
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
9852
    queue_name = rtems_build_name( 'Q', '_', 'S', 'D' );
902
903
9852
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
904
905
9852
    return status;
906
}
907
908
181
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
181
    queue_name = rtems_build_name( 'Q', '_', 'R', 'V' );
914
915
181
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
916
917
181
    return status;
918
}
919
920
3212
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
3212
    queue_name = rtems_build_name( 'Q', '_', 'P', '0' );
926
927
3212
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
928
929
3212
    return status;
930
}
931
932
3212
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
3212
    queue_name = rtems_build_name( 'Q', '_', 'P', '1' );
938
939
3212
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
940
941
3212
    return status;
942
}
943
944
3212
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
3212
    queue_name = rtems_build_name( 'Q', '_', 'P', '2' );
950
951
3212
    status =  rtems_message_queue_ident( queue_name, 0, queue_id );
952
953
3212
    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
836508
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
836508
    count = 0;
967
968
836508
    status = rtems_message_queue_get_number_pending( queue_id, &count );
969
970
836508
    count = count + 1;
971
972
836508
    if (status != RTEMS_SUCCESSFUL)
973
    {
974
        PRINTF1("in update_queue_max_count *** ERR = %d\n", status)
975
    }
976
    else
977
    {
978
836508
        if (count > *fifo_size_max)
979
        {
980
593
            *fifo_size_max = count;
981
        }
982
    }
983
836508
}
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
5427
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
24492
    for(i=0; i<nbNodes; i++)
1006
    {
1007
19065
        ring[i].coarseTime  = INT32_ALL_F;
1008
19065
        ring[i].fineTime    = INT32_ALL_F;
1009
19065
        ring[i].sid         = INIT_CHAR;
1010
19065
        ring[i].status      = INIT_CHAR;
1011
19065
        ring[i].buffer_address  = (int) &buffer[ i * bufferSize ];
1012
    }
1013
1014
    //*****
1015
    // NEXT
1016
5427
     ring[ nbNodes - 1 ].next  = (ring_node*) &ring[ 0 ];
1017
19065
     for(i=0; i<nbNodes-1; i++)
1018
     {
1019
13638
         ring[i].next      = (ring_node*) &ring[ i + 1 ];
1020
     }
1021
1022
    //*********
1023
    // PREVIOUS
1024
5427
    ring[ 0 ].previous       = (ring_node*) &ring[ nbNodes - 1 ];
1025
19065
    for(i=1; i<nbNodes; i++)
1026
    {
1027
13638
        ring[i].previous   = (ring_node*) &ring[ i - 1 ];
1028
    }
1029
5427
}