OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rtems-20020807/] [c/] [src/] [libmisc/] [capture/] [capture.c] - Blame information for rev 1780

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1026 ivang
/*
2
  ------------------------------------------------------------------------
3
  capture.c,v 1.2 2002/07/20 09:18:37 ralf Exp
4
  ------------------------------------------------------------------------
5
 
6
  Copyright Objective Design Systems Pty Ltd, 2002
7
  All rights reserved Objective Design Systems Pty Ltd, 2002
8
  Chris Johns (ccj@acm.org)
9
 
10
  COPYRIGHT (c) 1989-1998.
11
  On-Line Applications Research Corporation (OAR).
12
 
13
  The license and distribution terms for this file may be
14
  found in the file LICENSE in this distribution.
15
 
16
  This software with is provided ``as is'' and with NO WARRANTY.
17
 
18
  ------------------------------------------------------------------------
19
 
20
  RTEMS Performance Monitoring and Measurement Framework.
21
 
22
  This is the Capture Engine component.
23
 
24
*/
25
 
26
#include <stdlib.h>
27
#include <string.h>
28
 
29
#include "capture.h"
30
#include <rtems/score/states.inl>
31
#include <rtems/score/wkspace.h>
32
#include <rtems/score/wkspace.inl>
33
 
34
/*
35
 * These events are always recorded and are not part of the
36
 * watch filters.
37
 */
38
#define RTEMS_CAPTURE_RECORD_EVENTS  (RTEMS_CAPTURE_CREATED_BY_EVENT | \
39
                                      RTEMS_CAPTURE_CREATED_EVENT | \
40
                                      RTEMS_CAPTURE_STARTED_BY_EVENT | \
41
                                      RTEMS_CAPTURE_STARTED_EVENT | \
42
                                      RTEMS_CAPTURE_RESTARTED_BY_EVENT | \
43
                                      RTEMS_CAPTURE_RESTARTED_EVENT | \
44
                                      RTEMS_CAPTURE_DELETED_BY_EVENT | \
45
                                      RTEMS_CAPTURE_DELETED_EVENT | \
46
                                      RTEMS_CAPTURE_BEGIN_EVENT | \
47
                                      RTEMS_CAPTURE_EXITTED_EVENT)
48
 
49
/*
50
 * Global capture flags.
51
 */
52
#define RTEMS_CAPTURE_ON             (1 << 0)
53
#define RTEMS_CAPTURE_NO_MEMORY      (1 << 1)
54
#define RTEMS_CAPTURE_OVERFLOW       (1 << 2)
55
#define RTEMS_CAPTURE_TRIGGERED      (1 << 3)
56
#define RTEMS_CAPTURE_READER_ACTIVE  (1 << 4)
57
#define RTEMS_CAPTURE_READER_WAITING (1 << 5)
58
#define RTEMS_CAPTURE_GLOBAL_WATCH   (1 << 6)
59
 
60
/*
61
 * RTEMS Capture Data.
62
 */
63
static rtems_capture_record_t*  capture_records;
64
static rtems_unsigned32         capture_size;
65
static rtems_unsigned32         capture_count;
66
static rtems_capture_record_t*  capture_in;
67
static rtems_unsigned32         capture_out;
68
static rtems_unsigned32         capture_flags;
69
static rtems_capture_task_t*    capture_tasks;
70
static rtems_capture_control_t* capture_controls;
71
static int                      capture_extension_index;
72
static rtems_id                 capture_id;
73
static rtems_capture_timestamp  capture_timestamp;
74
static rtems_task_priority      capture_ceiling;
75
static rtems_task_priority      capture_floor;
76
static rtems_unsigned32         capture_tick_period;
77
static rtems_id                 capture_reader;
78
 
79
/*
80
 * RTEMS Event text.
81
 */
82
static const char* capture_event_text[] =
83
{
84
  "CREATED_BY",
85
  "CREATED",
86
  "STARTED_BY",
87
  "STARTED",
88
  "RESTARTED_BY",
89
  "RESTARTED",
90
  "DELETED_BY",
91
  "DELETED",
92
  "BEGIN",
93
  "EXITTED",
94
  "SWITCHED_OUT",
95
  "SWITCHED_IN",
96
  "TIMESTAMP"
97
};
98
 
99
/*
100
 * rtems_capture_get_time
101
 *
102
 *  DESCRIPTION:
103
 *
104
 * This function returns the current time. If a handler is provided
105
 * by the user get the time from that.
106
 */
107
static inline void rtems_capture_get_time (rtems_unsigned32* ticks,
108
                                           rtems_unsigned32* tick_offset)
109
{
110
  if (capture_timestamp)
111
    capture_timestamp (ticks, tick_offset);
112
  else
113
  {
114
    *ticks       = _Watchdog_Ticks_since_boot;
115
    *tick_offset = 0;
116
  }
117
}
118
 
119
/*
120
 * rtems_capture_match_names
121
 *
122
 *  DESCRIPTION:
123
 *
124
 * This function compares rtems_names. It protects the
125
 * capture engine from a change to the way names are supported
126
 * in RTEMS.
127
 *
128
 */
129
static inline rtems_boolean
130
rtems_capture_match_names (rtems_name lhs, rtems_name rhs)
131
{
132
  return lhs == rhs;
133
}
134
 
135
/*
136
 * rtems_capture_dup_name
137
 *
138
 *  DESCRIPTION:
139
 *
140
 * This function duplicates an rtems_names. It protects the
141
 * cpature engine from a change to the way names are supported
142
 * in RTEMS.
143
 *
144
 */
145
static inline void
146
rtems_capture_dup_name (rtems_name* dst, rtems_name src)
147
{
148
  *dst = src;
149
}
150
 
151
/*
152
 * rtems_capture_name_in_group
153
 *
154
 *  DESCRIPTION:
155
 *
156
 * This function sees if a name is in a group of names.
157
 *
158
 */
159
static inline rtems_boolean
160
rtems_capture_name_in_group (rtems_name task, rtems_name* tasks)
161
{
162
  if (tasks)
163
  {
164
    int i;
165
    for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
166
      if (rtems_capture_match_names (task, *tasks++))
167
        return 1;
168
  }
169
  return 0;
170
}
171
 
172
/*
173
 * rtems_capture_match_name_id
174
 *
175
 *  DESCRIPTION:
176
 *
177
 * This function matches a name and/or id.
178
 */
179
static inline rtems_boolean
180
rtems_capture_match_name_id (rtems_name lhs_name,
181
                             rtems_id   lhs_id,
182
                             rtems_name rhs_name,
183
                             rtems_id   rhs_id)
184
{
185
  /*
186
   * The left hand side name or id could be 0 which means a wildcard.
187
   */
188
  if ((lhs_name == 0) && (lhs_id == rhs_id))
189
    return 1;
190
  else if ((lhs_id == 0) || (lhs_id == rhs_id))
191
  {
192
    if (rtems_capture_match_names (lhs_name, rhs_name))
193
      return 1;
194
  }
195
  return 0;
196
}
197
 
198
/*
199
 * rtems_capture_init_stack_usage
200
 *
201
 *  DESCRIPTION:
202
 *
203
 * This function setups a stack so its usage can be monitored.
204
 */
205
static inline void
206
rtems_capture_init_stack_usage (rtems_capture_task_t* task)
207
{
208
  if (task->tcb)
209
  {
210
    rtems_unsigned32* s;
211
    rtems_unsigned32  i;
212
 
213
    task->stack_size  = task->tcb->Start.Initial_stack.size;
214
    task->stack_clean = task->stack_size;
215
 
216
    s = task->tcb->Start.Initial_stack.area;
217
 
218
    for (i = 0; i < (task->stack_size - 128); i += 4)
219
      *(s++) = 0xdeaddead;
220
  }
221
}
222
 
223
/*
224
 * rtems_capture_find_control
225
 *
226
 *  DESCRIPTION:
227
 *
228
 * This function searches for a trigger given a name.
229
 *
230
 */
231
static inline rtems_capture_control_t*
232
rtems_capture_find_control (rtems_name name, rtems_id id)
233
{
234
  rtems_capture_control_t* control;
235
 
236
  for (control = capture_controls; control != NULL; control = control->next)
237
    if (rtems_capture_match_name_id (name, id, control->name, control->id))
238
      break;
239
  return control;
240
}
241
 
242
/*
243
 * rtems_capture_create_control
244
 *
245
 *  DESCRIPTION:
246
 *
247
 * This function creates a capture control for the capture engine.
248
 *
249
 */
250
static inline rtems_capture_control_t*
251
rtems_capture_create_control (rtems_name name, rtems_id id)
252
{
253
  rtems_interrupt_level    level;
254
  rtems_capture_control_t* control;
255
  rtems_capture_task_t*    task;
256
 
257
  if ((name == 0) && (id == 0))
258
    return NULL;
259
 
260
  control = rtems_capture_find_control (name, id);
261
 
262
  if (control == NULL)
263
  {
264
    control = _Workspace_Allocate (sizeof (rtems_capture_control_t));
265
 
266
    if (control == NULL)
267
    {
268
      capture_flags |= RTEMS_CAPTURE_NO_MEMORY;
269
      return NULL;
270
    }
271
 
272
    control->name  = name;
273
    control->id    = id;
274
    control->flags = 0;
275
 
276
    memset (control->from,    0, sizeof (control->from));
277
    memset (control->from_id, 0, sizeof (control->from_id));
278
 
279
    rtems_interrupt_disable (level);
280
 
281
    control->next    = capture_controls;
282
    capture_controls = control;
283
 
284
    /*
285
     * We need to scan the task list as set the control to the
286
     * tasks.
287
     */
288
    for (task = capture_tasks; task != NULL; task = task->next)
289
      if (rtems_capture_match_name_id (name, id, task->name, task->id))
290
        task->control = control;
291
 
292
    rtems_interrupt_enable (level);
293
  }
294
 
295
  return control;
296
}
297
 
298
/*
299
 * rtems_capture_create_capture_task
300
 *
301
 *  DESCRIPTION:
302
 *
303
 * This function create the task control.
304
 *
305
 */
306
static inline rtems_capture_task_t*
307
rtems_capture_create_capture_task (rtems_tcb* new_task)
308
{
309
  rtems_interrupt_level    level;
310
  rtems_capture_task_t*    task;
311
  rtems_capture_control_t* control;
312
 
313
  task = _Workspace_Allocate (sizeof (rtems_capture_task_t));
314
 
315
  if (task == NULL)
316
  {
317
    capture_flags |= RTEMS_CAPTURE_NO_MEMORY;
318
    return NULL;
319
  }
320
 
321
  rtems_capture_dup_name (&task->name, *((rtems_name*) new_task->Object.name));
322
 
323
  task->id               = new_task->Object.id;
324
  task->flags            = 0;
325
  task->in               = 0;
326
  task->out              = 0;
327
  task->tcb              = new_task;
328
  task->ticks            = 0;
329
  task->tick_offset      = 0;
330
  task->ticks_in         = 0;
331
  task->tick_offset_in   = 0;
332
  task->control          = 0;
333
  task->last_ticks       = 0;
334
  task->last_tick_offset = 0;
335
 
336
  task->tcb->extensions[capture_extension_index] = task;
337
 
338
  task->start_priority = new_task->Start.initial_priority;
339
  task->stack_size     = new_task->Start.Initial_stack.size;
340
  task->stack_clean    = task->stack_size;
341
 
342
  rtems_interrupt_disable (level);
343
 
344
  task->next    = capture_tasks;
345
  capture_tasks = task;
346
 
347
  rtems_interrupt_enable (level);
348
 
349
  /*
350
   * We need to scan the default control list to initialise
351
   * this control.
352
   */
353
 
354
  for (control = capture_controls; control != NULL; control = control->next)
355
    if (rtems_capture_match_name_id (control->name, control->id,
356
                                     task->name, task->id))
357
      task->control = control;
358
 
359
  return task;
360
}
361
 
362
/*
363
 * rtems_capture_record
364
 *
365
 *  DESCRIPTION:
366
 *
367
 * This function records a capture record into the capture buffer.
368
 *
369
 */
370
static inline void
371
rtems_capture_record (rtems_capture_task_t* task,
372
                      rtems_unsigned32      events)
373
{
374
  /*
375
   * Check the watch state if we have a task control, and
376
   * the task's real priority is lower or equal to the ceiling.
377
   */
378
  if (task)
379
  {
380
    rtems_capture_control_t* control;
381
 
382
    control = task->control;
383
 
384
    /*
385
     * Capure the record if we have an event that is always
386
     * captured, or the task's real priority is greater than the
387
     * watch ceiling, and the global watch or task watch is enabled.
388
     */
389
 
390
    if ((events & RTEMS_CAPTURE_RECORD_EVENTS) ||
391
        ((task->tcb->real_priority >= capture_ceiling) &&
392
         (task->tcb->real_priority <= capture_floor) &&
393
         ((capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH) ||
394
          (control && (control->flags & RTEMS_CAPTURE_WATCH)))))
395
    {
396
      rtems_interrupt_level level;
397
 
398
      rtems_interrupt_disable (level);
399
 
400
      if (capture_count < capture_size)
401
      {
402
        capture_count++;
403
        capture_in->task   = task;
404
        capture_in->events = (events |
405
                              (task->tcb->real_priority) |
406
                              (task->tcb->current_priority << 8));
407
 
408
        if ((events & RTEMS_CAPTURE_RECORD_EVENTS) == 0)
409
          task->flags |= RTEMS_CAPTURE_TRACED;
410
 
411
        rtems_capture_get_time (&capture_in->ticks, &capture_in->tick_offset);
412
 
413
        if (capture_in == &capture_records[capture_size - 1])
414
          capture_in = capture_records;
415
        else
416
          capture_in++;
417
      }
418
      else
419
        capture_flags |= RTEMS_CAPTURE_OVERFLOW;
420
      rtems_interrupt_enable (level);
421
    }
422
  }
423
}
424
 
425
/*
426
 * rtems_capture_create_task
427
 *
428
 *  DESCRIPTION:
429
 *
430
 * This function is called when a task is created.
431
 *
432
 */
433
static rtems_boolean
434
rtems_capture_create_task (rtems_tcb* current_task,
435
                           rtems_tcb* new_task)
436
{
437
  rtems_capture_task_t* ct;
438
  rtems_capture_task_t* nt;
439
 
440
  ct = current_task->extensions[capture_extension_index];
441
 
442
  /*
443
   * The task ponters may not be known as the task may have
444
   * been created before the capture engine was open. Add them.
445
   */
446
 
447
  if (ct == NULL)
448
    ct = rtems_capture_create_capture_task (current_task);
449
 
450
  /*
451
   * Create the new task's capture control block.
452
   */
453
  nt = rtems_capture_create_capture_task (new_task);
454
 
455
  /*
456
   * If we are logging then record this fact.
457
   */
458
  rtems_capture_record (ct, RTEMS_CAPTURE_CREATED_BY_EVENT);
459
  rtems_capture_record (nt, RTEMS_CAPTURE_CREATED_EVENT);
460
 
461
  return 1 == 1;
462
}
463
 
464
/*
465
 * rtems_capture_start_task
466
 *
467
 *  DESCRIPTION:
468
 *
469
 * This function is called when a task is started.
470
 *
471
 */
472
static rtems_extension
473
rtems_capture_start_task (rtems_tcb* current_task,
474
                          rtems_tcb* started_task)
475
{
476
  /*
477
   * Get the capture task control block so we can trace this
478
   * event.
479
   */
480
  rtems_capture_task_t* ct;
481
  rtems_capture_task_t* st;
482
 
483
  ct = current_task->extensions[capture_extension_index];
484
  st = started_task->extensions[capture_extension_index];
485
 
486
  /*
487
   * The task ponters may not be known as the task may have
488
   * been created before the capture engine was open. Add them.
489
   */
490
 
491
  if (ct == NULL)
492
    ct = rtems_capture_create_capture_task (current_task);
493
 
494
  if (st == NULL)
495
    st = rtems_capture_create_capture_task (started_task);
496
 
497
  rtems_capture_record (ct, RTEMS_CAPTURE_STARTED_BY_EVENT);
498
  rtems_capture_record (st, RTEMS_CAPTURE_STARTED_EVENT);
499
 
500
  rtems_capture_init_stack_usage (st);
501
}
502
 
503
/*
504
 * rtems_capture_restart_task
505
 *
506
 *  DESCRIPTION:
507
 *
508
 * This function is called when a task is restarted.
509
 *
510
 */
511
static rtems_extension
512
rtems_capture_restart_task (rtems_tcb* current_task,
513
                            rtems_tcb* restarted_task)
514
{
515
  /*
516
   * Get the capture task control block so we can trace this
517
   * event.
518
   */
519
  rtems_capture_task_t* ct;
520
  rtems_capture_task_t* rt;
521
 
522
  ct = current_task->extensions[capture_extension_index];
523
  rt = restarted_task->extensions[capture_extension_index];
524
 
525
  /*
526
   * The task ponters may not be known as the task may have
527
   * been created before the capture engine was open. Add them.
528
   */
529
 
530
  if (ct == NULL)
531
    ct = rtems_capture_create_capture_task (current_task);
532
 
533
  if (rt == NULL)
534
    rt = rtems_capture_create_capture_task (restarted_task);
535
 
536
  rtems_capture_record (ct, RTEMS_CAPTURE_RESTARTED_BY_EVENT);
537
  rtems_capture_record (rt, RTEMS_CAPTURE_RESTARTED_EVENT);
538
 
539
  rtems_capture_task_stack_usage (rt);
540
  rtems_capture_init_stack_usage (rt);
541
}
542
 
543
/*
544
 * rtems_capture_delete_task
545
 *
546
 *  DESCRIPTION:
547
 *
548
 * This function is called when a task is deleted.
549
 *
550
 */
551
static rtems_extension
552
rtems_capture_delete_task (rtems_tcb* current_task,
553
                           rtems_tcb* deleted_task)
554
{
555
  /*
556
   * Get the capture task control block so we can trace this
557
   * event.
558
   */
559
  rtems_capture_task_t* ct;
560
  rtems_capture_task_t* dt;
561
 
562
  /*
563
   * The task ponters may not be known as the task may have
564
   * been created before the capture engine was open. Add them.
565
   */
566
 
567
  ct = current_task->extensions[capture_extension_index];
568
  dt = deleted_task->extensions[capture_extension_index];
569
 
570
  if (ct == NULL)
571
    ct = rtems_capture_create_capture_task (current_task);
572
 
573
  if (dt == NULL)
574
    dt = rtems_capture_create_capture_task (deleted_task);
575
 
576
  rtems_capture_record (ct, RTEMS_CAPTURE_DELETED_BY_EVENT);
577
  rtems_capture_record (dt, RTEMS_CAPTURE_DELETED_EVENT);
578
 
579
  rtems_capture_task_stack_usage (dt);
580
 
581
  /*
582
   * This task's tcb will be invalid.
583
   */
584
  dt->tcb = 0;
585
}
586
 
587
/*
588
 * rtems_capture_begin_task
589
 *
590
 *  DESCRIPTION:
591
 *
592
 * This function is called when a task is begun.
593
 *
594
 */
595
static rtems_extension
596
rtems_capture_begin_task (rtems_tcb* begin_task)
597
{
598
  /*
599
   * Get the capture task control block so we can trace this
600
   * event.
601
   */
602
  rtems_capture_task_t* bt;
603
 
604
  bt = begin_task->extensions[capture_extension_index];
605
 
606
  /*
607
   * The task ponters may not be known as the task may have
608
   * been created before the capture engine was open. Add them.
609
   */
610
 
611
  if (bt == NULL)
612
    bt = rtems_capture_create_capture_task (begin_task);
613
 
614
  rtems_capture_record (bt, RTEMS_CAPTURE_BEGIN_EVENT);
615
}
616
 
617
/*
618
 * rtems_capture_exitted_task
619
 *
620
 *  DESCRIPTION:
621
 *
622
 * This function is called when a task is exitted. That is
623
 * returned rather than was deleted.
624
 *
625
 */
626
static rtems_extension
627
rtems_capture_exitted_task (rtems_tcb* exitted_task)
628
{
629
  /*
630
   * Get the capture task control block so we can trace this
631
   * event.
632
   */
633
  rtems_capture_task_t* et;
634
 
635
  et = exitted_task->extensions[capture_extension_index];
636
 
637
  /*
638
   * The task ponters may not be known as the task may have
639
   * been created before the capture engine was open. Add them.
640
   */
641
 
642
  if (et == NULL)
643
    et = rtems_capture_create_capture_task (exitted_task);
644
 
645
  rtems_capture_record (et, RTEMS_CAPTURE_EXITTED_EVENT);
646
 
647
  rtems_capture_task_stack_usage (et);
648
}
649
 
650
/*
651
 * rtems_capture_switch_task
652
 *
653
 *  DESCRIPTION:
654
 *
655
 * This function is called when a context is switched.
656
 *
657
 */
658
static rtems_extension
659
rtems_capture_switch_task (rtems_tcb* current_task,
660
                           rtems_tcb* heir_task)
661
{
662
  /*
663
   * Only perform context switch trace processing if tracing is
664
   * enabled.
665
   */
666
  if (capture_flags & RTEMS_CAPTURE_ON)
667
  {
668
    rtems_unsigned32 ticks;
669
    rtems_unsigned32 tick_offset;
670
 
671
    /*
672
     * Get the cpature task control block so we can update the
673
     * reference anbd perform any watch or trigger functions.
674
     * The task ponters may not be known as the task may have
675
     * been created before the capture engine was open. Add them.
676
     */
677
    rtems_capture_task_t* ct;
678
    rtems_capture_task_t* ht;
679
 
680
    if (_States_Is_transient (current_task->current_state))
681
    {
682
      rtems_id ct_id = current_task->Object.id;
683
 
684
      for (ct = capture_tasks; ct; ct = ct->next)
685
        if (ct->id == ct_id)
686
          break;
687
    }
688
    else
689
    {
690
      ct = current_task->extensions[capture_extension_index];
691
 
692
      if (ct == NULL)
693
        ct = rtems_capture_create_capture_task (current_task);
694
    }
695
 
696
    ht = heir_task->extensions[capture_extension_index];
697
 
698
    if (ht == NULL)
699
      ht = rtems_capture_create_capture_task (heir_task);
700
 
701
    /*
702
     * Update the execution time. Assume the tick will not overflow
703
     * for now. This may need to change.
704
     */
705
    rtems_capture_get_time (&ticks, &tick_offset);
706
 
707
    /*
708
     * We could end up with null pointers for both the current task
709
     * and the heir task.
710
     */
711
 
712
    if (ht)
713
    {
714
      ht->in++;
715
      ht->ticks_in       = ticks;
716
      ht->tick_offset_in = tick_offset;
717
    }
718
 
719
    if (ct)
720
    {
721
      ct->out++;
722
      ct->ticks += ticks - ct->ticks_in;
723
 
724
      if (capture_timestamp)
725
      {
726
        tick_offset += capture_tick_period - ct->tick_offset_in;
727
 
728
        if (tick_offset < capture_tick_period)
729
          ct->tick_offset = tick_offset;
730
        else
731
        {
732
          ct->ticks++;
733
          ct->tick_offset = tick_offset - capture_tick_period;
734
        }
735
      }
736
      else
737
      {
738
        ct->tick_offset += 100;
739
      }
740
    }
741
 
742
    /*
743
     * If we have not triggered then see if this is a trigger condition.
744
     */
745
    if (!(capture_flags & RTEMS_CAPTURE_TRIGGERED))
746
    {
747
      rtems_capture_control_t* cc = NULL;
748
      rtems_capture_control_t* hc = NULL;
749
 
750
      if (ct)
751
      {
752
        cc = ct->control;
753
 
754
        /*
755
         * Check the current task for a TO_ANY trigger.
756
         */
757
        if (cc && (cc->flags & RTEMS_CAPTURE_TO_ANY))
758
        {
759
          capture_flags |= RTEMS_CAPTURE_TRIGGERED;
760
          goto triggered;
761
        }
762
      }
763
 
764
      if (ht)
765
      {
766
        hc = ht->control;
767
 
768
        /*
769
         * Check the next task for a FROM_ANY.
770
         */
771
        if (hc && (hc->flags & RTEMS_CAPTURE_FROM_ANY))
772
        {
773
          capture_flags |= RTEMS_CAPTURE_TRIGGERED;
774
          goto triggered;
775
        }
776
      }
777
 
778
      /*
779
       * Check is the trigger is from the current task
780
       * to the next task.
781
       */
782
      if (cc && hc && (hc->flags & RTEMS_CAPTURE_FROM_TO))
783
        if (rtems_capture_name_in_group (cc->name, hc->from))
784
        {
785
          capture_flags |= RTEMS_CAPTURE_TRIGGERED;
786
          goto triggered;
787
        }
788
    }
789
    else
790
    {
791
triggered:
792
 
793
      rtems_capture_record (ct, RTEMS_CAPTURE_SWITCHED_OUT_EVENT);
794
      rtems_capture_record (ht, RTEMS_CAPTURE_SWITCHED_IN_EVENT);
795
    }
796
  }
797
}
798
 
799
/*
800
 * rtems_capture_open
801
 *
802
 *  DESCRIPTION:
803
 *
804
 * This function initialises the realtime capture engine allocating the trace
805
 * buffer. It is assumed we have a working heap at stage of initialisation.
806
 *
807
 */
808
rtems_status_code
809
rtems_capture_open (rtems_unsigned32 size, rtems_capture_timestamp timestamp)
810
{
811
  rtems_extensions_table capture_extensions;
812
  rtems_name             name;
813
  rtems_status_code      sc;
814
 
815
  /*
816
   * See if the capture engine is already open.
817
   */
818
 
819
  if (capture_records)
820
    return RTEMS_RESOURCE_IN_USE;
821
 
822
  capture_records = malloc (size * sizeof (rtems_capture_record_t));
823
 
824
  if (capture_records == NULL)
825
    return RTEMS_NO_MEMORY;
826
 
827
  capture_size    = size;
828
  capture_count   = 0;
829
  capture_in      = capture_records;
830
  capture_out     = 0;
831
  capture_flags   = 0;
832
  capture_tasks   = NULL;
833
  capture_ceiling = 0;
834
  capture_floor   = 255;
835
 
836
  /*
837
   * Create the extension table. This is copied so we
838
   * can create it as a local.
839
   */
840
  capture_extensions.thread_create  = rtems_capture_create_task;
841
  capture_extensions.thread_start   = rtems_capture_start_task;
842
  capture_extensions.thread_restart = rtems_capture_restart_task;
843
  capture_extensions.thread_delete  = rtems_capture_delete_task;
844
  capture_extensions.thread_switch  = rtems_capture_switch_task;
845
  capture_extensions.thread_begin   = rtems_capture_begin_task;
846
  capture_extensions.thread_exitted = rtems_capture_exitted_task;
847
  capture_extensions.fatal          = NULL;
848
 
849
  /*
850
   * Get the tick period from the BSP Configuration Table.
851
   */
852
  capture_tick_period = _Configuration_Table->microseconds_per_tick;
853
 
854
  /*
855
   * Register the user extension handlers for the CAPture Engine.
856
   */
857
  name = rtems_build_name ('C', 'A', 'P', 'E');
858
  sc   = rtems_extension_create (name, &capture_extensions, &capture_id);
859
 
860
  if (sc != RTEMS_SUCCESSFUL)
861
  {
862
    capture_id = 0;
863
    free (capture_records);
864
    capture_records = NULL;
865
  }
866
  else
867
  {
868
    capture_extension_index = rtems_get_index (capture_id);;
869
  }
870
 
871
  /*
872
   * Iterate over the list of existing tasks.
873
   */
874
 
875
  return sc;
876
}
877
 
878
/*
879
 * rtems_capture_close
880
 *
881
 *  DESCRIPTION:
882
 *
883
 * This function shutdowns the capture engine and release any claimed
884
 * resources.
885
 */
886
rtems_status_code
887
rtems_capture_close ()
888
{
889
  rtems_interrupt_level    level;
890
  rtems_capture_task_t*    task;
891
  rtems_capture_control_t* control;
892
  rtems_capture_record_t*  records;
893
  rtems_status_code        sc;
894
 
895
  rtems_interrupt_disable (level);
896
 
897
  if (!capture_records)
898
  {
899
    rtems_interrupt_enable (level);
900
    return RTEMS_SUCCESSFUL;
901
  }
902
 
903
  capture_flags &= ~RTEMS_CAPTURE_ON;
904
 
905
  records = capture_records;
906
  capture_records = NULL;
907
 
908
  rtems_interrupt_enable (level);
909
 
910
  /*
911
   * Delete the extension first. This means we are now able to
912
   * release the resources we have without them being used.
913
   */
914
 
915
  sc = rtems_extension_delete (capture_id);
916
 
917
  if (sc != RTEMS_SUCCESSFUL)
918
    return sc;
919
 
920
  task = capture_tasks;
921
 
922
  while (task)
923
  {
924
    rtems_capture_task_t* delete = task;
925
    task = task->next;
926
    _Workspace_Free (delete);
927
  }
928
 
929
  capture_tasks = NULL;
930
 
931
  control = capture_controls;
932
 
933
  while (control)
934
  {
935
    rtems_capture_control_t* delete = control;
936
    control = control->next;
937
    _Workspace_Free (delete);
938
  }
939
 
940
  capture_controls = NULL;
941
 
942
  if (capture_records)
943
  {
944
    free (capture_records);
945
    capture_records = NULL;
946
  }
947
 
948
  return RTEMS_SUCCESSFUL;
949
}
950
 
951
/*
952
 * rtems_capture_control
953
 *
954
 *  DESCRIPTION:
955
 *
956
 * This function allows control of tracing at a global level.
957
 */
958
rtems_status_code
959
rtems_capture_control (rtems_boolean enable)
960
{
961
  rtems_interrupt_level level;
962
 
963
  rtems_interrupt_disable (level);
964
 
965
  if (!capture_records)
966
  {
967
    rtems_interrupt_enable (level);
968
    return RTEMS_UNSATISFIED;
969
  }
970
 
971
  if (enable)
972
    capture_flags |= RTEMS_CAPTURE_ON;
973
  else
974
    capture_flags &= ~RTEMS_CAPTURE_ON;
975
 
976
  rtems_interrupt_enable (level);
977
 
978
  return RTEMS_SUCCESSFUL;
979
}
980
 
981
/*
982
 * rtems_capture_flush
983
 *
984
 *  DESCRIPTION:
985
 *
986
 * This function flushes the capture buffer. The prime parameter allows the
987
 * capture engine to also be primed again.
988
 */
989
rtems_status_code
990
rtems_capture_flush (rtems_boolean prime)
991
{
992
  rtems_interrupt_level level;
993
  rtems_capture_task_t* task;
994
 
995
  rtems_interrupt_disable (level);
996
 
997
  for (task = capture_tasks; task != NULL; task = task->next)
998
    task->flags &= ~RTEMS_CAPTURE_TRACED;
999
 
1000
  if (prime)
1001
    capture_flags &= ~(RTEMS_CAPTURE_TRIGGERED | RTEMS_CAPTURE_OVERFLOW);
1002
  else
1003
    capture_flags &= ~RTEMS_CAPTURE_OVERFLOW;
1004
 
1005
  capture_in     = capture_records;
1006
  capture_out    = 0;
1007
 
1008
  rtems_interrupt_enable (level);
1009
 
1010
  return RTEMS_SUCCESSFUL;
1011
}
1012
 
1013
/*
1014
 * rtems_capture_watch_add
1015
 *
1016
 *  DESCRIPTION:
1017
 *
1018
 * This function defines a watch for a specific task given a name. A watch
1019
 * causes it to be traced either in or out of context. The watch can be
1020
 * optionally enabled or disabled with the set routine. It is disabled by
1021
 * default.
1022
 */
1023
rtems_status_code
1024
rtems_capture_watch_add (rtems_name name, rtems_id id)
1025
{
1026
  rtems_capture_control_t* control;
1027
 
1028
  if ((name == 0) && (id == 0))
1029
    return RTEMS_UNSATISFIED;
1030
 
1031
  control = rtems_capture_find_control (name, id);
1032
 
1033
  if (control && !id)
1034
    return RTEMS_TOO_MANY;
1035
 
1036
  if (!control)
1037
    control = rtems_capture_create_control (name, id);
1038
 
1039
  if (!control)
1040
    return RTEMS_NO_MEMORY;
1041
 
1042
  return RTEMS_SUCCESSFUL;
1043
}
1044
 
1045
/*
1046
 * rtems_capture_watch_del
1047
 *
1048
 *  DESCRIPTION:
1049
 *
1050
 * This function removes a watch for a specific task given a name. The task
1051
 * description will still exist if referenced by a trace record in the trace
1052
 * buffer or a global watch is defined.
1053
 */
1054
rtems_status_code
1055
rtems_capture_watch_del (rtems_name name, rtems_id id)
1056
{
1057
  rtems_interrupt_level     level;
1058
  rtems_capture_control_t*  control;
1059
  rtems_capture_control_t** prev_control;
1060
  rtems_capture_task_t*     task;
1061
  rtems_boolean             found = 0;
1062
 
1063
  /*
1064
   * Should this test be for wildcards ?
1065
   */
1066
 
1067
  for (prev_control = &capture_controls, control = capture_controls;
1068
       control != NULL; )
1069
  {
1070
    if (rtems_capture_match_name_id (name, id, control->name, control->id))
1071
    {
1072
      rtems_interrupt_disable (level);
1073
 
1074
      for (task = capture_tasks; task != NULL; task = task->next)
1075
        if (task->control == control)
1076
          task->control = 0;
1077
 
1078
      *prev_control = control->next;
1079
 
1080
      rtems_interrupt_enable (level);
1081
 
1082
      _Workspace_Free (control);
1083
 
1084
      control = *prev_control;
1085
 
1086
      found = 1;
1087
    }
1088
    else
1089
    {
1090
      prev_control = &control->next;
1091
      control      = control->next;
1092
      }
1093
  }
1094
 
1095
  if (found)
1096
    return RTEMS_SUCCESSFUL;
1097
 
1098
  return RTEMS_INVALID_NAME;
1099
}
1100
 
1101
/*
1102
 * rtems_capture_watch_set
1103
 *
1104
 *  DESCRIPTION:
1105
 *
1106
 * This function allows control of a watch. The watch can be enabled or
1107
 * disabled.
1108
 */
1109
rtems_status_code
1110
rtems_capture_watch_ctrl (rtems_name name, rtems_id id, rtems_boolean enable)
1111
{
1112
  rtems_interrupt_level    level;
1113
  rtems_capture_control_t* control;
1114
  rtems_boolean            found = 0;
1115
 
1116
  /*
1117
   * Find the control and then set the watch. It must exist before it can
1118
   * be controlled.
1119
   */
1120
  for (control = capture_controls; control != NULL; control = control->next)
1121
  {
1122
    if (rtems_capture_match_name_id (name, id, control->name, control->id))
1123
    {
1124
      rtems_interrupt_disable (level);
1125
 
1126
      if (enable)
1127
        control->flags |= RTEMS_CAPTURE_WATCH;
1128
      else
1129
        control->flags &= ~RTEMS_CAPTURE_WATCH;
1130
 
1131
      rtems_interrupt_enable (level);
1132
 
1133
      found = 1;
1134
    }
1135
  }
1136
 
1137
  if (found)
1138
    return RTEMS_SUCCESSFUL;
1139
 
1140
  return RTEMS_INVALID_NAME;
1141
}
1142
 
1143
/*
1144
 * rtems_capture_watch_global
1145
 *
1146
 *  DESCRIPTION:
1147
 *
1148
 * This function allows control of a global watch. The watch can be enabled or
1149
 * disabled. A global watch configures all tasks below the ceiling and above
1150
 * the floor to be traced.
1151
 */
1152
rtems_status_code
1153
rtems_capture_watch_global (rtems_boolean enable)
1154
{
1155
  rtems_interrupt_level level;
1156
 
1157
  rtems_interrupt_disable (level);
1158
 
1159
  /*
1160
   * We need to keep specific and global watches separate so
1161
   * a global enable/disable does not lose a specific watch.
1162
   */
1163
  if (enable)
1164
    capture_flags |= RTEMS_CAPTURE_GLOBAL_WATCH;
1165
  else
1166
    capture_flags &= ~RTEMS_CAPTURE_GLOBAL_WATCH;
1167
 
1168
  rtems_interrupt_enable (level);
1169
 
1170
  return RTEMS_SUCCESSFUL;
1171
}
1172
 
1173
/*
1174
 * rtems_capture_watch_global_on
1175
 *
1176
 *  DESCRIPTION:
1177
 *
1178
 * This function returns the global watch state.
1179
 */
1180
rtems_boolean
1181
rtems_capture_watch_global_on ()
1182
{
1183
  return capture_flags & RTEMS_CAPTURE_GLOBAL_WATCH ? 1 : 0;
1184
}
1185
 
1186
/*
1187
 * rtems_capture_watch_ceiling
1188
 *
1189
 *  DESCRIPTION:
1190
 *
1191
 * This function sets a watch ceiling. Tasks at or greating that the
1192
 * ceiling priority are not watched. This is a simple way to monitor
1193
 * an application and exclude system tasks running at a higher
1194
 * priority level.
1195
 */
1196
rtems_status_code
1197
rtems_capture_watch_ceiling (rtems_task_priority ceiling)
1198
{
1199
  capture_ceiling = ceiling;
1200
  return RTEMS_SUCCESSFUL;
1201
}
1202
 
1203
/*
1204
 * rtems_capture_watch_get_ceiling
1205
 *
1206
 *  DESCRIPTION:
1207
 *
1208
 * This function gets the watch ceiling.
1209
 */
1210
rtems_task_priority
1211
rtems_capture_watch_get_ceiling ()
1212
{
1213
  return capture_ceiling;
1214
}
1215
 
1216
/*
1217
 * rtems_capture_watch_floor
1218
 *
1219
 *  DESCRIPTION:
1220
 *
1221
 * This function sets a watch floor. Tasks at or less that the
1222
 * floor priority are not watched. This is a simple way to monitor
1223
 * an application and exclude system tasks running at a lower
1224
 * priority level.
1225
 */
1226
rtems_status_code
1227
rtems_capture_watch_floor (rtems_task_priority floor)
1228
{
1229
  capture_floor = floor;
1230
  return RTEMS_SUCCESSFUL;
1231
}
1232
 
1233
/*
1234
 * rtems_capture_watch_get_floor
1235
 *
1236
 *  DESCRIPTION:
1237
 *
1238
 * This function gets the watch floor.
1239
 */
1240
rtems_task_priority
1241
rtems_capture_watch_get_floor ()
1242
{
1243
  return capture_floor;
1244
}
1245
 
1246
/*
1247
 * rtems_capture_set_trigger
1248
 *
1249
 *  DESCRIPTION:
1250
 *
1251
 * This function sets an edge trigger. Left is the left side of
1252
 * the edge and right is right side of the edge. The trigger type
1253
 * can be -
1254
 *
1255
 *  FROM_ANY : a switch from any task to the right side of the edge.
1256
 *  TO_ANY   : a switch from the left side of the edge to any task.
1257
 *  FROM_TO  : a switch from the left side of the edge to the right
1258
 *             side of the edge.
1259
 *
1260
 * This set trigger routine will create a capture control for the
1261
 * target task. The task list is searched and any existing tasks
1262
 * are linked to the new control.
1263
 *
1264
 * We can have a number of tasks that have the same name so we
1265
 * search using names. This means a number of tasks can be
1266
 * linked to single control.
1267
 */
1268
rtems_status_code
1269
rtems_capture_set_trigger (rtems_name              from,
1270
                           rtems_id                from_id,
1271
                           rtems_name              to,
1272
                           rtems_id                to_id,
1273
                           rtems_capture_trigger_t trigger)
1274
{
1275
  rtems_capture_control_t* control;
1276
  int                      i;
1277
 
1278
  /*
1279
   * Find the capture control blocks for the from and to
1280
   * tasks.
1281
   */
1282
  if (trigger == rtems_capture_to_any)
1283
  {
1284
    control = rtems_capture_create_control (from, from_id);
1285
    if (control == NULL)
1286
      return RTEMS_NO_MEMORY;
1287
    control->flags |= RTEMS_CAPTURE_TO_ANY;
1288
  }
1289
 
1290
  if ((trigger == rtems_capture_from_to) ||
1291
      (trigger == rtems_capture_from_any))
1292
  {
1293
    control = rtems_capture_create_control (to, to_id);
1294
    if (control == NULL)
1295
      return RTEMS_NO_MEMORY;
1296
 
1297
    if (trigger == rtems_capture_from_any)
1298
      control->flags |= RTEMS_CAPTURE_FROM_ANY;
1299
    else
1300
    {
1301
      control->flags |= RTEMS_CAPTURE_FROM_TO;
1302
      for (i = 0; i < RTEMS_CAPTURE_TRIGGER_TASKS; i++)
1303
      {
1304
        if (control->from[i] == 0)
1305
        {
1306
          control->from[i]    = from;
1307
          control->from_id[i] = from_id;
1308
          break;
1309
        }
1310
      }
1311
    }
1312
  }
1313
  return RTEMS_SUCCESSFUL;
1314
}
1315
 
1316
/*
1317
 * rtems_capture_read
1318
 *
1319
 *  DESCRIPTION:
1320
 *
1321
 * This function reads a number of records from the capture buffer.
1322
 * The user can optionally block and wait until the buffer as a
1323
 * specific number of records available or a specific time has
1324
 * elasped.
1325
 *
1326
 * The function returns the number of record that is has that are
1327
 * in a continous block of memory. If the number of available records
1328
 * wrap then only those records are provided. This removes the need for
1329
 * caller to be concerned about buffer wrappings. If the number of
1330
 * requested records cannot be met due to the wrapping of the records
1331
 * less than the specified number will be returned.
1332
 *
1333
 * The user must release the records. This is achieved with a call to
1334
 * rtems_capture_release. Calls this function without a release will
1335
 * result in at least the same number of records being released.
1336
 *
1337
 * The 'threshold' parameter is the number of records that must be
1338
 * captured before returning. If a timeout period is specified (non-0)
1339
 * any captured records will be returned. These parameters stop
1340
 * thrashing occuring for a small number of records, yet allows
1341
 * a user configured latiency to be applied for single events.
1342
 *
1343
 * The 'timeout' parameter is in micro-seconds. A value of 0 will disable
1344
 * the timeout.
1345
 *
1346
 */
1347
rtems_status_code
1348
rtems_capture_read (rtems_unsigned32         threshold,
1349
                    rtems_unsigned32         timeout,
1350
                    rtems_unsigned32*        read,
1351
                    rtems_capture_record_t** recs)
1352
{
1353
  rtems_interrupt_level level;
1354
  rtems_status_code     sc = RTEMS_SUCCESSFUL;
1355
  rtems_unsigned32      count;
1356
 
1357
  *read = 0;
1358
  *recs = NULL;
1359
 
1360
  rtems_interrupt_disable (level);
1361
 
1362
  /*
1363
   * Only one reader is allowed.
1364
   */
1365
 
1366
  if (capture_flags & RTEMS_CAPTURE_READER_ACTIVE)
1367
  {
1368
    rtems_interrupt_enable (level);
1369
    return RTEMS_RESOURCE_IN_USE;
1370
  }
1371
 
1372
  capture_flags |= RTEMS_CAPTURE_READER_ACTIVE;
1373
  *read = count = capture_count;
1374
 
1375
  rtems_interrupt_enable (level);
1376
 
1377
  *recs = &capture_records[capture_out];
1378
 
1379
  for (;;)
1380
  {
1381
    /*
1382
     * See if the count wraps the end of the record buffer.
1383
     */
1384
    if (count && ((capture_out + count) >= capture_size))
1385
      *read = capture_size - capture_out;
1386
 
1387
    /*
1388
     * Do we have a threshold and the current count has not wrapped
1389
     * around the end of the capture record buffer ?
1390
     */
1391
    if ((*read == count) && threshold)
1392
    {
1393
      /*
1394
       * Do we have enough records ?
1395
       */
1396
      if (*read < threshold)
1397
      {
1398
        rtems_event_set event_out;
1399
 
1400
        rtems_task_ident (RTEMS_SELF, RTEMS_LOCAL, &capture_reader);
1401
 
1402
        rtems_interrupt_disable (level);
1403
 
1404
        capture_flags |= RTEMS_CAPTURE_READER_WAITING;
1405
 
1406
        rtems_interrupt_enable (level);
1407
 
1408
        sc = rtems_event_receive (RTEMS_EVENT_0,
1409
                                  RTEMS_WAIT | RTEMS_EVENT_ANY,
1410
                                  TOD_MICROSECONDS_TO_TICKS (timeout),
1411
                                  &event_out);
1412
 
1413
        /*
1414
         * Let the user handle all other sorts of errors. This may
1415
         * not be the best solution, but oh well, it will do for
1416
         * now.
1417
         */
1418
        if ((sc != RTEMS_SUCCESSFUL) && (sc != RTEMS_TIMEOUT))
1419
          break;
1420
 
1421
        rtems_interrupt_disable (level);
1422
 
1423
        *read = count = capture_count;
1424
 
1425
        rtems_interrupt_enable (level);
1426
 
1427
        continue;
1428
      }
1429
    }
1430
 
1431
    /*
1432
     * Always out if we reach here. To loop use continue.
1433
     */
1434
    break;
1435
  }
1436
 
1437
  rtems_interrupt_disable (level);
1438
 
1439
  capture_flags &= ~RTEMS_CAPTURE_READER_ACTIVE;
1440
 
1441
  rtems_interrupt_enable (level);
1442
 
1443
  return sc;
1444
}
1445
 
1446
/*
1447
 * rtems_capture_release
1448
 *
1449
 *  DESCRIPTION:
1450
 *
1451
 * This function releases the requested number of record slots back
1452
 * to the capture engine. The count must match the number read.
1453
 */
1454
rtems_status_code
1455
rtems_capture_release (rtems_unsigned32 count)
1456
{
1457
  rtems_interrupt_level level;
1458
 
1459
  rtems_interrupt_disable (level);
1460
 
1461
  if (count > capture_count)
1462
    count = capture_count;
1463
 
1464
  capture_count -= count;
1465
 
1466
  capture_out = (capture_count + count) % capture_size;
1467
 
1468
  rtems_interrupt_enable (level);
1469
 
1470
  return RTEMS_SUCCESSFUL;
1471
}
1472
 
1473
/*
1474
 * rtems_capture_tick_time
1475
 *
1476
 *  DESCRIPTION:
1477
 *
1478
 * This function returns the tick period in nano-seconds.
1479
 */
1480
rtems_unsigned32
1481
rtems_capture_tick_time ()
1482
{
1483
  return capture_tick_period;
1484
}
1485
 
1486
/*
1487
 * rtems_capture_event_text
1488
 *
1489
 *  DESCRIPTION:
1490
 *
1491
 * This function returns a string for an event based on the bit in the
1492
 * event. The functions takes the bit offset as a number not the bit
1493
 * set in a bit map.
1494
 */
1495
const char*
1496
rtems_capture_event_text (int event)
1497
{
1498
  if ((event < RTEMS_CAPTURE_EVENT_START) || (event > RTEMS_CAPTURE_EVENT_END))
1499
    return "invalid event id";
1500
  return capture_event_text[event - RTEMS_CAPTURE_EVENT_START];
1501
}
1502
 
1503
/*
1504
 * rtems_capture_get_task_list
1505
 *
1506
 *  DESCRIPTION:
1507
 *
1508
 * This function returns the head of the list of tasks that the
1509
 * capture engine has detected.
1510
 */
1511
rtems_capture_task_t*
1512
rtems_capture_get_task_list ()
1513
{
1514
  return capture_tasks;
1515
}
1516
 
1517
/*
1518
 * rtems_capture_task_stack_usage
1519
 *
1520
 *  DESCRIPTION:
1521
 *
1522
 * This function updates the stack usage. The task control block
1523
 * is updated.
1524
 */
1525
rtems_unsigned32
1526
rtems_capture_task_stack_usage (rtems_capture_task_t* task)
1527
{
1528
  if (task->tcb)
1529
  {
1530
    rtems_unsigned32* st;
1531
    rtems_unsigned32* s;
1532
 
1533
    /*
1534
     * @todo: Assumes all stacks move the same way.
1535
     */
1536
    st = task->tcb->Start.Initial_stack.area + task->stack_size;
1537
    s  = task->tcb->Start.Initial_stack.area;
1538
 
1539
    while (s < st)
1540
    {
1541
      if (*s != 0xdeaddead)
1542
        break;
1543
      s++;
1544
    }
1545
 
1546
    task->stack_clean =
1547
      s - (rtems_unsigned32*) task->tcb->Start.Initial_stack.area;
1548
  }
1549
 
1550
  return task->stack_clean;
1551
}
1552
 
1553
/*
1554
 * rtems_capture_get_control_list
1555
 *
1556
 *  DESCRIPTION:
1557
 *
1558
 * This function returns the head of the list of control in the
1559
 * capture engine.
1560
 */
1561
rtems_capture_control_t*
1562
rtems_capture_get_control_list ()
1563
{
1564
  return capture_controls;
1565
}
1566
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.