LCOV - code coverage report
Current view: top level - src - fsw_init.c (source / functions) Hit Total Coverage
Test: trace.info Lines: 258 264 97.7 %
Date: 2023-02-20 11:47:17 Functions: 14 16 87.5 %

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

Generated by: LCOV version 1.14