vcd_signal_dumper.c 21.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.0  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

22
23
24
/*! \file vcd_signal_dumper.c
 * \brief Dump functions calls and variables to VCD file. Use GTKWave to display this file.
 * \author S. Roux
nikaeinn's avatar
nikaeinn committed
25
 * \maintainer: navid nikaein
26
 * \date 2012 - 2104
27
28
 * \version 0.1
 * \company Eurecom
nikaeinn's avatar
nikaeinn committed
29
 * \email: navid.nikaein@eurecom.fr
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
 * \note
 * \warning
 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <error.h>
#include <time.h>
#include <unistd.h>

46
#include "assertions.h"
47
#include "signals.h"
48
49
50
51
52
53
54
55
56
57
58
59
60
61

#include "vcd_signal_dumper.h"

#if defined(ENABLE_RTAI_CLOCK)
#include "rtai_lxrt.h"
#endif

#define VCDSIGNALDUMPER_VERSION_MAJOR 0
#define VCDSIGNALDUMPER_VERSION_MINOR 1

// Global variable. If the VCD option is set at execution time, output VCD trace. Otherwise this module has no effect.
int ouput_vcd = 0;

struct vcd_module_s {
62
63
64
65
66
  const char     *name;
  int             number_of_signals;
  const char    **signals_names;
  vcd_signal_type signal_type;
  int             signal_size;
67
} vcd_module_s;
68

69
const char* eurecomVariablesNames[] = {
70
71
72
73
74
75
76
77
  "frame_number_TX0_eNB",
  "frame_number_TX1_eNB",
  "frame_number_RX0_eNB",
  "frame_number_RX1_eNB",
  "subframe_number_TX0_eNB",
  "subframe_number_TX1_eNB",
  "subframe_number_RX0_eNB",
  "subframe_number_RX1_eNB",
78
79
  "runtime_TX_eNB",
  "runtime_RX_eNB",
80
81
82
83
84
85
86
87
  "frame_number_TX0_UE",
  "frame_number_TX1_UE",
  "frame_number_RX0_UE",
  "frame_number_RX1_UE",
  "subframe_TX0_UE",
  "subframe_TX1_UE",
  "subframe_RX0_UE",
  "subframe_RX1_UE",
88
  "missed_slot_enb",
89
90
91
92
93
94
  "daq_mbox",
  "rx_offset_mbox",
  "ue_rx_offset",
  "diff2",
  "hw_subframe",
  "hw_frame",
navid's avatar
navid committed
95
96
  "hw_subframe_rx",
  "hw_frame_rx",
97
98
99
100
  "txcnt",
  "rxcnt",
  "trx_ts",
  "trx_tst",
knopp's avatar
knopp committed
101
  "trx_write_flags",
102
103
104
105
106
107
108
  "tx_ts",
  "rx_ts",
  "hw_cnt_rx",
  "lhw_cnt_rx",
  "hw_cnt_tx",
  "lhw_cnt_tx",
  "pck_rx",
navid's avatar
navid committed
109
  "pck_tx",
110
111
112
  "rx_seq_num",
  "rx_seq_num_prv",
  "tx_seq_num",
navid's avatar
navid committed
113
  "cnt",
114
  "dummy_dump",
115
116
117
118
119
120
  "itti_send_msg",
  "itti_poll_msg",
  "itti_recv_msg",
  "itti_alloc_msg",
  "mp_alloc",
  "mp_free",
121
  "ue_inst_cnt_rx",
122
  "ue_inst_cnt_tx",
123
  "dci_info",
124
125
126
  "ue0_BSR",
  "ue0_BO",
  "ue0_scheduled",
127
  "ue0_timing_advance",
128
129
  "ue0_SR_ENERGY",
  "ue0_SR_THRES",
130
131
132
133
134
135
136
137
  "ue0_rssi0",
  "ue0_rssi1",
  "ue0_rssi2",
  "ue0_rssi3",
  "ue0_rssi4",
  "ue0_rssi5",
  "ue0_rssi6",
  "ue0_rssi7",
138
139
140
141
142
143
144
145
  "ue0_res0",
  "ue0_res1",
  "ue0_res2",
  "ue0_res3",
  "ue0_res4",
  "ue0_res5",
  "ue0_res6",
  "ue0_res7",
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  "ue0_MCS0",
  "ue0_MCS1",
  "ue0_MCS2",
  "ue0_MCS3",
  "ue0_MCS4",
  "ue0_MCS5",
  "ue0_MCS6",
  "ue0_MCS7",
  "ue0_RB0",
  "ue0_RB1",
  "ue0_RB2",
  "ue0_RB3",
  "ue0_RB4",
  "ue0_RB5",
  "ue0_RB6",
  "ue0_RB7",
  "ue0_ROUND0",
  "ue0_ROUND1",
  "ue0_ROUND2",
  "ue0_ROUND3",
  "ue0_ROUND4",
  "ue0_ROUND5",
  "ue0_ROUND6",
  "ue0_ROUND7",
170
171
172
173
174
175
176
177
  "ue0_SFN0",
  "ue0_SFN1",
  "ue0_SFN2",
  "ue0_SFN3",
  "ue0_SFN4",
  "ue0_SFN5",
  "ue0_SFN6",
  "ue0_SFN7",
Cedric Roux's avatar
fix vcd    
Cedric Roux committed
178
179
180
181
  "send_if4_symbol",
  "recv_if4_symbol",
  "send_if5_pkt_id",
  "recv_if5_pkt_id",
182
183
  "ue_pdcp_flush_size",
  "ue_pdcp_flush_err",
184
185
  "ue0_trx_read_ns",
  "ue0_trx_write_ns",
186
  "ue0_trx_read_ns_missing",
Cedric Roux's avatar
fix vcd    
Cedric Roux committed
187
  "ue0_trx_write_ns_missing",
188
};
189

190
const char* eurecomFunctionsNames[] = {
191
192
193
194
  /*  softmodem signals   */
  "rt_sleep",
  "trx_read",
  "trx_write",
Raymond Knopp's avatar
Raymond Knopp committed
195
196
  "trx_read_if",
  "trx_write_if",
knopp's avatar
knopp committed
197
198
  "eNB_thread_rxtx0",
  "eNB_thread_rxtx1",
199
200
201
  "ue_thread_synch",
  "ue_thread_rxtx0",
  "ue_thread_rxtx1",
202
203
  "trx_read_sf9",
  "trx_write_sf9",
204
205
  "ue_signal_cond_rxtx0",
  "ue_signal_cond_rxtx1",
206
207
208
209
210
211
212
213
  "ue_wait_cond_rxtx0",
  "ue_wait_cond_rxtx1",
  "ue_lock_mutex_rxtx_for_cond_wait0",
  "ue_lock_mutex_rxtx_for_cond_wait1",
  "ue_lock_mutex_rxtx_for_cnt_decrement0",
  "ue_lock_mutex_rxtx_for_cnt_decrement1",
  "ue_lock_mutex_rxtx_for_cnt_increment0",
  "ue_lock_mutex_rxtx_for_cnt_increment1",
214

215
216
217
218
219
220
221
222
 /* RRH signals  */ 
  "eNB_tx",
  "eNB_rx",
  "eNB_trx",
  "eNB_tm",
  "eNB_rx_sleep",
  "eNB_tx_sleep",
  "eNB_proc_sleep",
navid's avatar
navid committed
223
224
  "trx_read_rf",
  "trx_write_rf",
225

226
227
228
229
230
231
232
233
234
  /* PHY signals  */
  "ue_synch",
  "ue_slot_fep",
  "ue_rrc_measurements",
  "ue_gain_control",
  "ue_adjust_synch",
  "lte_ue_measurement_procedures",
  "lte_ue_pdcch_procedures",
  "lte_ue_pbch_procedures",
235
236
237
238
239
240
  "phy_procedures_eNb_tx0",
  "phy_procedures_eNb_tx1",
  "phy_procedures_eNb_rx_common0",
  "phy_procedures_eNb_rx_common1",
  "phy_procedures_eNb_rx_uespec0",
  "phy_procedures_eNb_rx_uespec1",
241
  "phy_eNB_slot_fep",
242
243
  "phy_procedures_ue_tx",
  "phy_procedures_ue_rx",
244
245
246
247
248
  "phy_procedures_ue_tx_ulsch_uespec",
  "phy_procedures_ue_tx_pucch",
  "phy_procedures_ue_tx_ulsch_common",
  "phy_procedures_ue_tx_prach",
  "phy_procedures_ue_tx_ulsch_rar",
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
  "phy_procedures_eNB_lte",
  "phy_procedures_UE_lte",
  "pdsch_thread",
  "dlsch_thread0",
  "dlsch_thread1",
  "dlsch_thread2",
  "dlsch_thread3",
  "dlsch_thread4",
  "dlsch_thread5",
  "dlsch_thread6",
  "dlsch_thread7",
  "dlsch_decoding0",
  "dlsch_decoding1",
  "dlsch_decoding2",
  "dlsch_decoding3",
  "dlsch_decoding4",
  "dlsch_decoding5",
  "dlsch_decoding6",
  "dlsch_decoding7",
  "rx_pdcch",
  "dci_decoding",
  "rx_phich",
Gabriel's avatar
Gabriel committed
271
272
273
274
  "pdsch_procedures",
  "pdsch_procedures_si",
  "pdsch_procedures_p",
  "pdsch_procedures_ra",
275
276
277
278
279
  "phy_ue_config_sib2",
  "macxface_phy_config_sib1_eNB",
  "macxface_phy_config_sib2_eNB",
  "macxface_phy_config_dedicated_eNB",
  "phy_ue_compute_prach",
280
  "phy_enb_ulsch_msg3",
281
282
283
284
285
286
287
288
  "phy_enb_ulsch_decoding0",
  "phy_enb_ulsch_decoding1",
  "phy_enb_ulsch_decoding2",
  "phy_enb_ulsch_decoding3",
  "phy_enb_ulsch_decoding4",
  "phy_enb_ulsch_decoding5",
  "phy_enb_ulsch_decoding6",
  "phy_enb_ulsch_decoding7",
289
290
291
292
293
294
295
  "phy_enb_sfgen",
  "phy_enb_prach_rx",
  "phy_enb_pdcch_tx",
  "phy_enb_rs_tx",
  "phy_ue_generate_prach",
  "phy_ue_ulsch_modulation",
  "phy_ue_ulsch_encoding",
296
297
298
#if 1 // add for debugging losing PDSCH immediately before and after reporting CQI
  "phy_ue_ulsch_encoding_fill_cqi",
#endif
299
300
301
  "phy_ue_ulsch_scrambling",
  "phy_eNB_dlsch_modulation",
  "phy_eNB_dlsch_encoding",
302
  "phy_eNB_dlsch_encoding_w",
303
  "phy_eNB_dlsch_scrambling",
304
305
  "phy_eNB_beam_precoding",
  "phy_eNB_ofdm_mod_l",
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322

  /* MAC  signals  */
  "macxface_macphy_init",
  "macxface_macphy_exit",
  "macxface_eNB_dlsch_ulsch_scheduler",
  "macxface_fill_rar",
  "macxface_terminate_ra_proc",
  "macxface_initiate_ra_proc",
  "macxface_cancel_ra_proc",
  "macxface_get_dci_sdu",
  "macxface_get_dlsch_sdu",
  "macxface_rx_sdu",
  "macxface_mrbch_phy_sync_failure",
  "macxface_SR_indication",
  "mac_dlsch_preprocessor",
  "mac_schedule_dlsch",
  "mac_fill_dlsch_dci",
323

324
325
  "macxface_out_of_sync_ind",
  "macxface_ue_decode_si",
326
  "macxface_ue_decode_pcch",
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  "macxface_ue_decode_ccch",
  "macxface_ue_decode_bcch",
  "macxface_ue_send_sdu",
  "macxface_ue_get_sdu",
  "macxface_ue_get_rach",
  "macxface_ue_process_rar",
  "macxface_ue_scheduler",
  "macxface_ue_get_sr",

  "ue_send_mch_sdu",

  /*RLC signals   */
  "rlc_data_req",
  // "rlc_data_ind", // this calls "pdcp_data_ind",
  "mac_rlc_status_ind",
  "mac_rlc_data_req",
  "mac_rlc_data_ind",
  "rlc_um_try_reassembly",
  "rlc_um_check_timer_dar_time_out",
  "rlc_um_receive_process_dar",

  /* PDCP signals   */
  "pdcp_run",
  "pdcp_data_req",
  "pdcp_data_ind",
  "pdcp_apply_security",
  "pdcp_validate_security",
354
355
356
357
  "pdcp_fifo_read",
  "pdcp_fifo_read_buffer",
  "pdcp_fifo_flush",
  "pdcp_fifo_flush_buffer",
358
  /* RRC signals  */
359
  "rrc_rx_tx",
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  "rrc_mac_config_req",
  "rrc_ue_decode_sib1",
  "rrc_ue_decode_si",
  /* GTPV1U signals */
  "gtpv1u_enb_task",
  "gtpv1u_process_udp_req",
  "gtpv1u_process_tunnel_data_req",
  /* UDP signals */
  "udp_enb_task",
  /* MISC signals  */
  "emu_transport",
  "log_record",
  "itti_enqueue_message",
  "itti_dump_enqueue_message",
  "itti_dump_enqueue_message_malloc",
  "itti_relay_thread",
376
377
  "test",
  
378
  /* IF4/IF5 signals */
379
  "send_if4",
380
381
  "recv_if4",
  "send_if5",
382
383
384
  "recv_if5",

  "compress_if",
Cedric Roux's avatar
fix vcd    
Cedric Roux committed
385
  "decompress_if",
386
387
};

Cedric Roux's avatar
fix vcd    
Cedric Roux committed
388
struct vcd_module_s vcd_modules[] = {
389
390
391
  { "variables", VCD_SIGNAL_DUMPER_VARIABLES_END, eurecomVariablesNames, VCD_WIRE, 64 },
  { "functions", VCD_SIGNAL_DUMPER_FUNCTIONS_END, eurecomFunctionsNames, VCD_WIRE, 1 },
  //    { "ue_procedures_functions", VCD_SIGNAL_DUMPER_UE_PROCEDURES_FUNCTIONS_END, eurecomUEFunctionsNames, VCD_WIRE, 1 },
392
393
394
395
396
397
398
399
400
401
402
403
404
405
};

FILE *vcd_fd = NULL;
static inline unsigned long long int vcd_get_time(void);

#if defined(ENABLE_USE_CPU_EXECUTION_TIME)
struct timespec     g_time_start;
#elif defined(ENABLE_RTAI_CLOCK)
RTIME start;
#endif


#if defined(ENABLE_VCD_FIFO)

406
407
# define VCD_POLL_DELAY         (500)           // Poll delay in micro-seconds
# define VCD_MAX_WAIT_DELAY     (200 * 1000)    // Maximum data ready wait delay in micro-seconds
408
# define VCD_FIFO_NB_ELEMENTS   (1 << 24)       // Must be a power of 2
409
# define VCD_FIFO_MASK          (VCD_FIFO_NB_ELEMENTS - 1)
410

411
typedef struct vcd_queue_user_data_s {
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  uint32_t log_id;
  vcd_signal_dumper_modules module;
  union data_u {
    struct function_s {
      vcd_signal_dump_functions function_name;
      vcd_signal_dump_in_out    in_out;
    } function;
    struct variable_s {
      vcd_signal_dump_variables variable_name;
      unsigned long value;
    } variable;
  } data;

  long long unsigned int time;
426
427
} vcd_queue_user_data_t;

428
typedef struct vcd_fifo_s {
429
  vcd_queue_user_data_t user_data[VCD_FIFO_NB_ELEMENTS];
430

431
432
  volatile uint32_t write_index;
  volatile uint32_t read_index;
433
434
435
436
} vcd_fifo_t;

vcd_fifo_t vcd_fifo;

437
pthread_t vcd_dumper_thread;
438
#endif
439

winckel's avatar
winckel committed
440
441
442
443
#define BYTE_SIZE   8
#define NIBBLE_SIZE 4
static void uint64_to_binary(uint64_t value, char *binary)
{
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
  static const char * const nibbles_start[] = {
    "",    "1",   "10",   "11",
    "100",  "101",  "110",  "111",
    "1000", "1001", "1010", "1011",
    "1100", "1101", "1110", "1111",
  };
  static const char * const nibbles[] = {
    "0000", "0001", "0010", "0011",
    "0100", "0101", "0110", "0111",
    "1000", "1001", "1010", "1011",
    "1100", "1101", "1110", "1111",
  };
  int nibble;
  int nibble_value;
  int nibble_size;
  int zero = 1;

  for (nibble = 0; nibble < (sizeof (uint64_t) * (BYTE_SIZE / NIBBLE_SIZE)); nibble++) {
    nibble_value = value >> ((sizeof (uint64_t) * BYTE_SIZE) - NIBBLE_SIZE);

    if (zero) {
      if (nibble_value > 0) {
        zero = 0;
        nibble_size = strlen(nibbles_start[nibble_value]);
        memcpy (binary, nibbles_start[nibble_value], nibble_size);
        binary += nibble_size;
      }
    } else {
      memcpy (binary, nibbles[nibble_value], NIBBLE_SIZE);
      binary += NIBBLE_SIZE;
winckel's avatar
winckel committed
474
    }
475
476
477
478
479
480
481
482
483
484
485
486

    value <<= NIBBLE_SIZE;
  }

  /* Add a '0' if the value was null */
  if (zero) {
    binary[0] = '0';
    binary ++;
  }

  /* Add a null value at the end of the string */
  binary[0] = '\0';
winckel's avatar
winckel committed
487
488
}

489
#if defined(ENABLE_VCD_FIFO)
490
491
inline static uint32_t vcd_get_write_index(void)
{
492
493
  uint32_t write_index;
  uint32_t read_index;
494

495
496
497
498
  /* Get current write index and increment it (atomic operation) */
  write_index = __sync_fetch_and_add(&vcd_fifo.write_index, 1);
  /* Wrap index */
  write_index &= VCD_FIFO_MASK;
499

500
501
  /* Check FIFO overflow (increase VCD_FIFO_NB_ELEMENTS if this assert is triggered) */
  DevCheck((read_index = vcd_fifo.read_index, ((write_index + 1) & VCD_FIFO_MASK) != read_index), write_index, read_index, VCD_FIFO_NB_ELEMENTS);
502

503
  return write_index;
504
505
}

506
507
void *vcd_dumper_thread_rt(void *args)
{
508
509
510
511
  vcd_queue_user_data_t *data;
  char binary_string[(sizeof (uint64_t) * BYTE_SIZE) + 1];
  struct sched_param sched_param;
  uint32_t data_ready_wait;
winckel's avatar
winckel committed
512

513
# if defined(ENABLE_ITTI)
514
  signal_mask();
515
516
# endif

517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
  sched_param.sched_priority = sched_get_priority_min(SCHED_FIFO) + 1;
  sched_setscheduler(0, SCHED_FIFO, &sched_param);

  while(1) {
    if (vcd_fifo.read_index == (vcd_fifo.write_index & VCD_FIFO_MASK)) {
      /* No element -> sleep a while */
      usleep(VCD_POLL_DELAY);
    } else {
      data = &vcd_fifo.user_data[vcd_fifo.read_index];
      data_ready_wait = 0;

      while (data->module == VCD_SIGNAL_DUMPER_MODULE_FREE) {
        /* Check wait delay (increase VCD_MAX_WAIT_DELAY if this assert is triggered and that no thread is locked) */
        DevCheck(data_ready_wait < VCD_MAX_WAIT_DELAY, data_ready_wait, VCD_MAX_WAIT_DELAY, 0);

        /* data is not yet ready, wait for it to be completed */
        data_ready_wait += VCD_POLL_DELAY;
        usleep(VCD_POLL_DELAY);
      }

      switch (data->module) {
      case VCD_SIGNAL_DUMPER_MODULE_VARIABLES:
        if (vcd_fd != NULL) {
          int variable_name;
          variable_name = (int)data->data.variable.variable_name;
          fprintf(vcd_fd, "#%llu\n", data->time);
          /* Set variable to value */
          uint64_to_binary(data->data.variable.value, binary_string);
          fprintf(vcd_fd, "b%s %s_w\n", binary_string,
                  eurecomVariablesNames[variable_name]);
547
        }
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576

        break;

      case VCD_SIGNAL_DUMPER_MODULE_FUNCTIONS:
        if (vcd_fd != NULL) {
          int function_name;

          function_name = (int)data->data.function.function_name;
          fprintf(vcd_fd, "#%llu\n", data->time);

          /* Check if we are entering or leaving the function ( 0 = leaving, 1 = entering) */
          if (data->data.function.in_out == VCD_FUNCTION_IN)
            /* Set event to 1 */
            fprintf(vcd_fd, "1%s_w\n", eurecomFunctionsNames[function_name]);
          else
            fprintf(vcd_fd, "0%s_w\n", eurecomFunctionsNames[function_name]);

          fflush(vcd_fd);
        }

        break;

      default:
        DevParam(data->module, 0, 0);
        break;
      }

      data->module = VCD_SIGNAL_DUMPER_MODULE_FREE;
      vcd_fifo.read_index = (vcd_fifo.read_index + 1) & VCD_FIFO_MASK;
577
    }
578
579
580
  }

  return NULL;
581
582
583
584
585
}
#endif

void vcd_signal_dumper_init(char *filename)
{
586
587
  if (ouput_vcd) {
    //        char filename[] = "/tmp/openair_vcd_dump.vcd";
588

589
590
591
592
    if ((vcd_fd = fopen(filename, "w+")) == NULL) {
      perror("vcd_signal_dumper_init: cannot open file");
      return;
    }
593
594

#if defined(ENABLE_USE_CPU_EXECUTION_TIME)
595
    clock_gettime(CLOCK_MONOTONIC, &g_time_start);
596
#elif defined(ENABLE_RTAI_CLOCK)
597
    start=rt_get_time_ns();
598
599
#endif

600
    vcd_signal_dumper_create_header();
601
602

#if defined(ENABLE_VCD_FIFO)
603
604
    vcd_fifo.write_index = 0;
    vcd_fifo.read_index = 0;
605

606
    fprintf(stderr, "[VCD] Creating dumper thread\n");
607

608
609
610
611
612
    if (pthread_create(&vcd_dumper_thread, NULL, vcd_dumper_thread_rt, NULL) < 0) {
      fprintf(stderr, "vcd_signal_dumper_init: Failed to create thread: %s\n",
              strerror(errno));
      ouput_vcd = 0;
      return;
613
    }
614
615
616

#endif
  }
617
618
619
620
}

void vcd_signal_dumper_close(void)
{
621
  if (ouput_vcd) {
622
#if defined(ENABLE_VCD_FIFO)
623

624
#else
625
626
627
628

    if (vcd_fd != NULL) {
      fclose(vcd_fd);
      vcd_fd = NULL;
629
    }
630
631
632

#endif
  }
633
634
635
636
}

static inline void vcd_signal_dumper_print_time_since_start(void)
{
637
  if (vcd_fd != NULL) {
638
#if defined(ENABLE_USE_CPU_EXECUTION_TIME)
639
640
641
    struct timespec time;
    long long unsigned int nanosecondsSinceStart;
    long long unsigned int secondsSinceStart;
642

643
    clock_gettime(CLOCK_MONOTONIC, &time);
644

645
646
647
648
649
    /* Get current execution time in nanoseconds */
    nanosecondsSinceStart = (long long unsigned int)((time.tv_nsec - g_time_start.tv_nsec));
    secondsSinceStart     = (long long unsigned int)time.tv_sec - (long long unsigned int)g_time_start.tv_sec;
    /* Write time in nanoseconds */
    fprintf(vcd_fd, "#%llu\n", nanosecondsSinceStart + (secondsSinceStart * 1000000000UL));
650
#elif defined(ENABLE_RTAI_CLOCK)
651
652
    /* Write time in nanoseconds */
    fprintf(vcd_fd, "#%llu\n",rt_get_time_ns()-start);
653
#endif
654
  }
655
656
657
658
659
}

static inline unsigned long long int vcd_get_time(void)
{
#if defined(ENABLE_USE_CPU_EXECUTION_TIME)
660
  struct timespec time;
661

662
  clock_gettime(CLOCK_MONOTONIC, &time);
663

664
665
  return (long long unsigned int)((time.tv_nsec - g_time_start.tv_nsec)) +
         ((long long unsigned int)time.tv_sec - (long long unsigned int)g_time_start.tv_sec) * 1000000000UL;
666
#elif defined(ENABLE_RTAI_CLOCK)
667
  return rt_get_time_ns() - start;
668
669
670
671
672
#endif
}

void vcd_signal_dumper_create_header(void)
{
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
  if (ouput_vcd) {
    struct tm *pDate;
    time_t intps;

    intps = time(NULL);
    pDate = localtime(&intps);

    if (vcd_fd != NULL) {
      int i, j;
      fprintf(vcd_fd, "$date\n\t%s$end\n", asctime(pDate));
      // Display version
      fprintf(vcd_fd, "$version\n\tVCD plugin ver%d.%d\n$end\n", VCDSIGNALDUMPER_VERSION_MAJOR, VCDSIGNALDUMPER_VERSION_MINOR);
      // Init timescale, here = 1ns
      fprintf(vcd_fd, "$timescale 1 ns $end\n");

      /* Initialize each module definition */
Cedric Roux's avatar
fix vcd    
Cedric Roux committed
689
      for(i = 0; i < sizeof(vcd_modules) / sizeof(struct vcd_module_s); i++) {
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
        struct vcd_module_s *module;
        module = &vcd_modules[i];
        fprintf(vcd_fd, "$scope module %s $end\n", module->name);

        /* Declare each signal as defined in array */
        for (j = 0; j < module->number_of_signals; j++) {
          const char *signal_name;
          signal_name = module->signals_names[j];

          if (VCD_WIRE == module->signal_type) {
            fprintf(vcd_fd, "$var wire %d %s_w %s $end\n", module->signal_size, signal_name, signal_name);
          } else  if (VCD_REAL == module->signal_type) {
            fprintf(vcd_fd, "$var real %d %s_r %s $end\n", module->signal_size, signal_name, signal_name);
          } else {
            // Handle error here
          }
        }

        fprintf(vcd_fd, "$upscope $end\n");
      }

      /* Init variables and functions to 0 */
      fprintf(vcd_fd, "$dumpvars\n");

Cedric Roux's avatar
fix vcd    
Cedric Roux committed
714
      for(i = 0; i < sizeof(vcd_modules) / sizeof(struct vcd_module_s); i++) {
715
716
        struct vcd_module_s *module;
        module = &vcd_modules[i];
717

718
719
720
721
722
723
724
725
726
727
        /* Declare each signal as defined in array */
        for (j = 0; j < module->number_of_signals; j++) {
          const char *signal_name;
          signal_name = module->signals_names[j];

          if (VCD_WIRE == module->signal_type) {
            if (module->signal_size > 1) {
              fprintf(vcd_fd, "b0 %s_w $end\n", signal_name);
            } else {
              fprintf(vcd_fd, "0%s_w $end\n", signal_name);
728
            }
729
730
731
732
733
          } else  if (VCD_REAL == module->signal_type) {
            fprintf(vcd_fd, "r0 %s_r $end\n", signal_name);
          } else {
            // Handle error here
          }
734
        }
735
736
737
738
739
      }

      fprintf(vcd_fd, "$end\n");
      fprintf(vcd_fd, "$enddefinitions $end\n\n");
      //fflush(vcd_fd);
740
    }
741
  }
742
743
744
}

void vcd_signal_dumper_dump_variable_by_name(vcd_signal_dump_variables variable_name,
745
    unsigned long             value)
746
{
747
748
  DevCheck((0 <= variable_name) && (variable_name < VCD_SIGNAL_DUMPER_VARIABLES_END),
           variable_name, VCD_SIGNAL_DUMPER_VARIABLES_END, 0);
winckel's avatar
winckel committed
749

750
  if (ouput_vcd) {
751
#if defined(ENABLE_VCD_FIFO)
752
    uint32_t write_index = vcd_get_write_index();
753

754
755
756
757
    vcd_fifo.user_data[write_index].time = vcd_get_time();
    vcd_fifo.user_data[write_index].data.variable.variable_name = variable_name;
    vcd_fifo.user_data[write_index].data.variable.value = value;
    vcd_fifo.user_data[write_index].module = VCD_SIGNAL_DUMPER_MODULE_VARIABLES; // Set when all other fields are set to validate the user_data
758
#else
759
    char binary_string[(sizeof (uint64_t) * BYTE_SIZE) + 1];
winckel's avatar
winckel committed
760

761
762
    if (vcd_fd != NULL) {
      vcd_signal_dumper_print_time_since_start();
763

764
765
766
767
      /* Set variable to value */
      uint64_to_binary(value, binary_string);
      fprintf(vcd_fd, "b%s %s_w\n", binary_string, eurecomVariablesNames[variable_name]);
      //fflush(vcd_fd);
768
    }
769
770
771

#endif
  }
772
773
774
}

void vcd_signal_dumper_dump_function_by_name(vcd_signal_dump_functions  function_name,
775
    vcd_signal_dump_in_out     in_out)
776
{
777
778
  DevCheck((0 <= function_name) && (function_name < VCD_SIGNAL_DUMPER_FUNCTIONS_END),
           function_name, VCD_SIGNAL_DUMPER_FUNCTIONS_END, 0);
winckel's avatar
winckel committed
779

780
  if (ouput_vcd) {
781
#if defined(ENABLE_VCD_FIFO)
782
    uint32_t write_index = vcd_get_write_index();
783

784
785
786
787
    vcd_fifo.user_data[write_index].time = vcd_get_time();
    vcd_fifo.user_data[write_index].data.function.function_name = function_name;
    vcd_fifo.user_data[write_index].data.function.in_out = in_out;
    vcd_fifo.user_data[write_index].module = VCD_SIGNAL_DUMPER_MODULE_FUNCTIONS; // Set when all other fields are set to validate the user_data
788
#else
789
790
791
792
793
794
795
796
797
798
799
800

    if (vcd_fd != NULL) {
      vcd_signal_dumper_print_time_since_start();

      /* Check if we are entering or leaving the function ( 0 = leaving, 1 = entering) */
      if (in_out == VCD_FUNCTION_IN)
        /* Set event to 1 */
        fprintf(vcd_fd, "1%s_w\n", eurecomFunctionsNames[function_name]);
      else
        fprintf(vcd_fd, "0%s_w\n", eurecomFunctionsNames[function_name]);

      //fflush(vcd_fd);
801
    }
802
803
804

#endif
  }
805
806
}