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