summaryrefslogtreecommitdiffstats
path: root/src/occ/rtls/test/main.c
blob: 50167585476d97edcbee92c8b40a5bc0245aa0e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
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
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
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
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
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
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
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/occ/rtls/test/main.c $                                    */
/*                                                                        */
/* OpenPOWER OnChipController Project                                     */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2011,2014                        */
/* [+] Google Inc.                                                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

#include "ssx.h"
#include "ssx_io.h"
#include "simics_stdio.h"
#include <thread.h>
#include <errl.h>
#include <rand.h>
#include <rtls.h>
#include <appletId.h>  // For applet ID

extern void __ssx_boot;

IMAGE_HEADER (G_mainAppImageHdr,__ssx_boot,MAIN_APP_ID,OCC_APLT_TEST);

// Period in which to run #timer_routine
#define TIMER_INTERVAL (SsxInterval) SSX_MICROSECONDS(5000)

int g_j = 0;
int g_k = 0;

extern uint32_t G_run_mask;

// Globals from local copy of rtls_tables.c:
extern task_t G_task_table[TASK_END];  // Global task table

extern uint32_t task_0_data; // Default task data buffers
extern uint32_t task_1_data;
extern uint32_t task_2_data;
extern uint32_t task_3_data;

extern uint32_t task_0_alt_data; // Alternate task data buffers
extern uint32_t task_1_alt_data;
extern uint32_t task_2_alt_data;
extern uint32_t task_3_alt_data;

SimicsStdio simics_stdout;
SimicsStdio simics_stderr;


/*----------------------------------------------------------------------------*/
/* Critical/Non-Critical stack                                                */
/*----------------------------------------------------------------------------*/
uint8_t noncritical_stack[NONCRITICAL_STACK_SIZE];
uint8_t critical_stack[CRITICAL_STACK_SIZE];
uint8_t test_thread_stack[THREAD_STACK_SIZE];

SsxThread test_thread;
SsxTimer G_test_timer;

// Inits the IRQ handler.
extern void rtl_ocb_init( void );

// Run the IRQ handler for at least MAX_NUM_TICKS, then disable it again.
int rtl_run_max_ticks(void);

// Utility clears the data buffers in all tasks in the global task table.
void clear_task_data_bufs(void);

// Utility clears the flags in all tasks in the global task table.
void clear_task_flags(void);

// Utility writes the flags in the specified task to the specified value.
void write_task_flags(task_id_t i_task_id, uint32_t new_flags);

// Utility to dump flags & data for all tasks in G_task_table, along with
// the current values of G_run_mask and CURRENT_TICK.
void dump_global_state(void);

// Tests the API that reads & writes the global & task run flags.
void test_flags_api(void);

// Tests the function ('rtl_stop_task') that indicates a task is NOT ready to run.
void test_task_stop_api(void);

// Tests the function ('rtl_set_task_data') that assigns a new data buffer to a
// task in the global task list.
void test_set_data_api(void);

// Thread routine that runs all our tests.
void test_thread_routine(void *private);

// The main routine.
int main(int argc, char **argv);

// Function Specification
//
// Name: rtl_run_max_ticks
//
// Description:
// Run the RTLS IRQ handler for at least MAX_NUM_TICKS, then stop.
// Note: This function does not guarantee that the IRQ handler will run for
// exactly MAX_NUM_TICKS ticks.  It does guarantee that the IRQ handler will
// run for _at_least_ MAX_NUM_TICKS ticks (but could run for more).
//
// End Function Specification

int rtl_run_max_ticks(void)
{
// If we can loop this many cycles without seeing our IRQ stop condition,
// then something is broken.
#define MAX_WAIT_CYCLES 10000

    int  i = 0;  // Counter
    int rc = 0;  // Return Code

    // Start the RTLS IRQ timer.
    ssx_irq_enable( PGP_IRQ_OCC_TIMER0 );

    // Wait for the timer to hit MAX_NUM_TICKS ticks.
    // Note: When we enter this loop, CURRENT_TICK may still equal 0xFFFFFFFF,
    // so handle that condition appropriately.
    for( i = 0; i < MAX_WAIT_CYCLES; i++) {

        // Warning: ssx_sleep only works if called from a thread OTHER than the one that
        // calls ocb_timer_setup (function called by rtl_ocb_init).  If this rule is not
        // followed, the OCB IRQ handler will run a trap instruction and hang.
        ssx_sleep(1);

        if( (CURRENT_TICK >= MAX_NUM_TICKS) && (CURRENT_TICK < 0xFFFFFFFF) ) {
            ssx_irq_disable(PGP_IRQ_OCC_TIMER0);
            CURRENT_TICK = 0xFFFFFFFF;
            break;
        }
    }

    if( i >= MAX_WAIT_CYCLES ) {
        // We timed-out waiting for CURRENT_TICK to equal MAX_NUM_TICKS.  Make noise & exit.
        printf("  ERROR: %s: %d: Timed-out waiting for CURRENT_TICK to reach %d.\n\n", __FILE__, __LINE__, MAX_NUM_TICKS);
        rc = -1;
    }
    return rc;
}


// Function Specification
//
// Name: clear_task_data_bufs
//
// Description:
// Utility clears the data buffers in all tasks in the global task table.
//
// End Function Specification

void clear_task_data_bufs(void)
{
    int i;

    for( i = 0; i < TASK_END; i++ ) {
        *((uint32_t *)(G_task_table[i].data_ptr)) = 0x00000000;
    }
}


// Function Specification
//
// Name: clear_task_flags
//
// Description:
// Utility clears the flags in all tasks in the global task table.
// Note: This sets all task flags to 0.
//
// End Function Specification

void clear_task_flags(void)
{
    int i;

    for( i = 0; i < TASK_END; i++ ) {
        G_task_table[i].flags = 0x00000000;
    }
}

// Function Specification
//
// Name: write_task_flags
//
// Description:
// Utility writes the flags in the specified task to the specified value.
// Note: This overwrites the prior flags value for the given task.
//
// End Function Specification

void write_task_flags(task_id_t i_task_id, uint32_t new_flags)
{
    if( i_task_id >= TASK_END ) {
        printf("Warning: %s: write_task_flags: Invalid task ID: %02x.  Not overwriting flags.\n", __FILE__, i_task_id);
    }
    else {
        G_task_table[i_task_id].flags = new_flags;
    }
}


// Function Specification
//
// Name: dump_global_state
//
// Description:
// Utility to dump flags & data for all tasks in G_task_table, along with
// the current values of G_run_mask and CURRENT_TICK.
//
// End Function Specification

void dump_global_state(void)
{
    int i;
    printf("\n  G_run_mask = [0x%04x] ; CURRENT_TICK = [0x%04x]\n", G_run_mask, CURRENT_TICK);

    for( i = 0; i < TASK_END; i++ ) {
        printf("  TASK_ID_%02d : flags [0x%08x] ; data_p [0x%p] ; data [0x%08x]\n", \
                i, G_task_table[i].flags, G_task_table[i].data_ptr, *((uint32_t *)(G_task_table[i].data_ptr)) );
    }
    printf("\n");
}


// Function Specification
//
// Name: test_1
//
// Description:
// This test is to stop ocb timer after complete 1 loop
//
// End Function Specification

void test_1()
{
    // Announce which test is running.
    printf("\ntest_1:\n");

    // re-enable interrupt so RTL will continue again (CURRENT_TICK = 0xFFFFFFFF)
    printf("  Test_1_1: starting ocb timer\n");

    rtl_run_max_ticks();

    printf("  Test_1_1: exiting subroutine.\n");
    return;
}


// Function Specification
//
// Name: test_2
//
// Description:
// This test to set and clear the global run mask interfaces
//
// End Function Specification
void test_2()
{
    // Announce which test is running.
    printf("\ntest_2:\n");

    //test rtl_set_run_mask() interface
    G_run_mask = 0x00000000;
    printf("  Test_2_1: G_run_mask = 0x%08x before setting\n", G_run_mask);

    rtl_set_run_mask( RTL_FLAG_ACTIVE | RTL_FLAG_OBS );
    printf("  Test_2_2: G_run_mask = 0x%08x after setting\n", G_run_mask);

    //test rtl_clr_run_mask() interface
    G_run_mask = RTL_FLAG_MSTR | RTL_FLAG_NOTMSTR | RTL_FLAG_ACTIVE |
                 RTL_FLAG_OBS | RTL_FLAG_RST_REQ |RTL_FLAG_NO_APSS ;
    printf("  Test_2_3: G_run_mask = 0x%08x before clearing\n", G_run_mask);

    rtl_clr_run_mask( RTL_FLAG_ACTIVE | RTL_FLAG_OBS );
    printf("  Test_2_4: G_run_mask = 0x%08x after clearing\n", G_run_mask);

    return;
}

// Function Specification
//
// Name: test_flags_api
//
// Description: Tests the global & task run flags, including the API that
// reads & writes these flags.
//
// End Function Specification
void test_flags_api(void)
{
    int i = 0;  // Counter
    BOOLEAN l_result = FALSE;

    // Local copy of new run mask (odd-numbered bits set).
    uint32_t l_odd_run_mask = RTL_FLAG_MSTR | RTL_FLAG_OBS | RTL_FLAG_RST_REQ;

    // Partial inverse of l_odd_run_mask (even-numbered bits set, except for the 'run' flag: bit 0).
    uint32_t l_even_run_mask = RTL_FLAG_NOTMSTR | RTL_FLAG_ACTIVE | RTL_FLAG_NO_APSS;

    // Announce which test is running.
    printf("\ntest_flags_api:\n");

    // Clear all task data & flags.
    clear_task_data_bufs();
    clear_task_flags();

    // Use API to set 'run' flag + odd-numbered bits in global flags mask.
    rtl_clr_run_mask(0xFFFFFFFF);
    rtl_set_run_mask(RTL_FLAG_RUN | l_odd_run_mask);

    // Set 1/2 the tasks to run w/same mask as global, and the other 1/2 of the
    // tasks to run w/opposite mask from the global.
    for (i = 0; i < TASK_END; i++)
    {
        // Use i as i_task_id
        if (i % 2)
        {
            // Task ID is odd
            write_task_flags((task_id_t) i, l_odd_run_mask);
        }
        else
        {
            // Task ID is even
            write_task_flags((task_id_t) i, l_even_run_mask);
        }
    }

    // Use API to set all tasks as runnable.
    for (i = 0; i < TASK_END; i++)
    {
        rtl_start_task((task_id_t) i);
    }

    // Use API to check which tasks are runnable.
    // Expect all tasks to report as runnable.
    printf("  Before IRQ enable.  Using \'odd\' global mask.\n");
    printf("  Expect: All tasks report as runnable.\n");
    printf("  Actual: Tasks reporting as runnable: ");

    for (i = 0; i < TASK_END; i++)
    {
        l_result = rtl_task_is_runnable((task_id_t) i);

        if (l_result)
        {
            printf("%d ", i);
        }
    }
    printf("\n");
    dump_global_state();

    // Run the rtls for MAX_NUM_TICKS, then stop.
    rtl_run_max_ticks();

    // Dump task data, task flags, global flags and CURRENT_TICK.
    printf("  After IRQ ran.\n");
    printf("  Expect: Only tasks using the \'odd\' flags mask actually ran.\n");
    printf("  Actual: Task state:\n");
    dump_global_state();

    // Clear all task data, but not the task flags.
    clear_task_data_bufs();

    // Use the API to reverse the sense of the global flags only.
    rtl_clr_run_mask(0xFFFFFFFF);
    rtl_set_run_mask(RTL_FLAG_RUN | l_even_run_mask);

    // Use API to check which tasks are runnable.
    // Expect all tasks to report as runnable.
    printf("  Before IRQ enable.  Using \'even\' global mask.\n");
    printf("  Expect: All tasks report as runnable.\n");
    printf("  Actual: Tasks reporting as runnable: ");

    for (i = 0; i < TASK_END; i++) {

        l_result = rtl_task_is_runnable((task_id_t) i);
        printf("%d ", i);
    }
    printf("\n");
    dump_global_state();

    // Run the rtls for MAX_NUM_TICKS, then stop.
    rtl_run_max_ticks();

    // Dump task data, task flags, global flags and CURRENT_TICK.
    printf("  After IRQ ran.\n");
    printf("  Expect: Only tasks using the \'even\' flags mask actually ran.\n");
    printf("  Actual: Task state:\n");
    dump_global_state();

    return;
}


// Function Specification
//
// Name: test_task_stop_api
//
// Description: Tests the API function that indicates a task is NOT ready
// to run: 'rtl_stop_task'.
//
// End Function Specification

void test_task_stop_api(void)
{
    int i = 0;  // Counter
    BOOLEAN l_result = FALSE;

    // Local copy of new run mask (odd-numbered bits set).
    uint32_t l_odd_run_mask = RTL_FLAG_MSTR | RTL_FLAG_OBS | RTL_FLAG_RST_REQ;

    // Announce which test is running.
    printf("\ntest_task_stop_api:\n");

    // Clear all task data & flags.
    clear_task_data_bufs();
    clear_task_flags();

    // Use API to set 'run' flag + odd-numbered bits in global flags mask.
    rtl_clr_run_mask(0xFFFFFFFF);
    rtl_set_run_mask(RTL_FLAG_RUN | l_odd_run_mask);

    // Set other task flags so all task flags == global flag mask.
    // Use the API to set the RTL_FLAG_RUN flags in all tasks.
    for (i = 0; i < TASK_END; i++) {
        // Use i as i_task_id
        write_task_flags((task_id_t) i, l_odd_run_mask);
        rtl_start_task((task_id_t) i);
    }

    // Now use the API to "stop" ~1/2 the tasks (clear RTL_FLAG_RUN).
    for (i = 0; i < TASK_END; i++) {
        // Use i as i_task_id

        if ( (i % 2) == 0 ) {
            // Task ID is even
            rtl_stop_task((task_id_t) i);
        }
    }

    // Use API to check which tasks are runnable.  Expect 1/2 the tasks are runnable.
    printf("  Before IRQ runs.\n");
    printf("  Expect: Half of the tasks report as runnable.\n");
    printf("  Actual: Tasks reporting as runnable: ");

    for (i = 0; i < TASK_END; i++) {

        l_result = rtl_task_is_runnable((task_id_t) i);

        if (l_result) { printf("%d ", i); }
    }
    printf("\n");
    dump_global_state();

    // Run the rtls for MAX_NUM_TICKS, then stop.
    rtl_run_max_ticks();

    // Dump task data, task flags, global flags and CURRENT_TICK.
    printf("  After IRQ ran.\n");
    printf("  Expect: \'Runnable\' tasks ran; \'non-runnable\' tasks did not run.\n");
    printf("  Actual: Task state:\n");
    dump_global_state();
    return;
}


// Function Specification
//
// Name: test_set_data_apii
//
// Description: Tests the API function that assigns a new data buffer to a
// task in the global task list: 'rtl_set_task_data'.
//
// End Function Specification

void test_set_data_api(void)
{
    int i = 0;  // Counter
    BOOLEAN l_result = FALSE;

    uint32_t *l_task_1_old_data_p = NULL;  // Copy of original task 1 data ptr
    uint32_t *l_task_3_old_data_p = NULL;  // Copy of original task 3 data ptr

    // Local copy of new run mask (odd-numbered bits set).
    uint32_t l_odd_run_mask = RTL_FLAG_MSTR | RTL_FLAG_OBS | RTL_FLAG_RST_REQ;

    // Announce which test is running.
    printf("\ntest_set_data_api:\n");

    // Clear all task data & flags.
    clear_task_data_bufs();
    clear_task_flags();

    // Use API to set global flags.
    rtl_clr_run_mask(0xFFFFFFFF);
    rtl_set_run_mask(RTL_FLAG_RUN | l_odd_run_mask);

    printf("  Before task data pointers re-assigned.\n");
    dump_global_state();

    // Save-off old data ptrs for tasks 1 & 3.
    // Use API to re-assign data ptrs for ~1/2 of the tasks.
    // Remember: The task data pointer won't get changed if
    // RTL_FLAG_RUN is set in the task's flags mask.
    l_task_1_old_data_p = G_task_table[1].data_ptr;
    l_task_3_old_data_p = G_task_table[3].data_ptr;

    rtl_set_task_data(TASK_ID_1, &task_1_alt_data);
    rtl_set_task_data(TASK_ID_3, &task_3_alt_data);

    // To be sure we start clean, clear the task data buffers again.
    clear_task_data_bufs();

    // Use API to set task flags so all tasks will run.
    for (i = 0; i < TASK_END; i++) {
        // Use i as i_task_id
        write_task_flags((task_id_t) i, l_odd_run_mask);
        rtl_start_task((task_id_t) i);
    }

    // Use API to check which tasks are runnable.  Expect all tasks are runnable.
    printf("  After task data pointers re-assigned; before IRQ runs.\n");
    printf("  Expect: All tasks will report as runnable.\n");
    printf("  Actual: Tasks reporting as runnable: ");

    for (i = 0; i < TASK_END; i++) {

        l_result = rtl_task_is_runnable((task_id_t) i);

        if (l_result) { printf("%d ", i); }
    }
    printf("\n");
    dump_global_state();

    // Run the rtls for MAX_NUM_TICKS, then stop.
    rtl_run_max_ticks();

    // Dump task data, task flags, global flags and CURRENT_TICK.
    // Dump the old task data (the ones that were de-assigned).
    // Expect: New task data buffers indicate the tasks ran.  Old task data buffers are still cleared.
    printf("  After IRQ ran.\n");
    printf("  Expect: All tasks will have run.  Old task data buffers will still be clear.\n");
    printf("  Actual: Task state:\n");
    dump_global_state();
    printf("  TASK_ID_1 : old data_p: [0x%p] ; old data [0x%08x]\n", l_task_1_old_data_p, *l_task_1_old_data_p);
    printf("  TASK_ID_3 : old data_p: [0x%p] ; old data [0x%08x]\n", l_task_3_old_data_p, *l_task_3_old_data_p);
    printf("\n");

    // Unless you want to report a "task busy" error when you un-do the data ptr assignments,
    // clear your task flags here (mainly just need to clear the run flag).
    clear_task_flags();

    // Now un-do data ptr re-assignments.
    rtl_set_task_data(TASK_ID_1, &task_1_data);
    rtl_set_task_data(TASK_ID_3, &task_3_data);

    return;
}


// Function Specification
//
// Name: test_thread_routine
//
// Description:
// We run our tests in a thread so that, when a test re-enables the OCB timer for the RTLS,
// the timer will actually interrupt the test, and run until it hits MAX_NUM_TICKS and
// is re-disabled.  At that point, the test resumes its activity and dumps the results
// of the RTLS IRQ's operation.
//
// End Function Specification

void test_thread_routine(void *private)
{
    test_1();
    test_2();
    test_flags_api();
    test_task_stop_api();
    test_set_data_api();

    // Uncomment if you want to re-enable the OCB timer for further testing
    // after this test suite exits.
    // ssx_irq_enable( PGP_IRQ_OCC_TIMER0 );
}

// Function Specification
//
// Name: main
//
// Description: Entry point for running our tests.
// Sets up our OCB timer and our test thread, then kicks them both off.
//
// End Function Specification

int main(int argc, char **argv)
{
    // Initialize Trace Buffers immediately, so they can be used
    // from this point on.

    // Initialize stdout so we can do printf from within simics env
    simics_stdout_create(&simics_stdout);
    simics_stderr_create(&simics_stderr);
    stdout = (FILE *)(&simics_stdout);
    stderr = (FILE *)(&simics_stderr);

    // Initialize SSX Stacks (note that this also reinitializes the time base to 0)
    ssx_initialize((SsxAddress)noncritical_stack, NONCRITICAL_STACK_SIZE,
           (SsxAddress)critical_stack, CRITICAL_STACK_SIZE,
           0);

    // Create our test thread
    ssx_thread_create(&test_thread,                 // Thread control block (struct)
                      test_thread_routine,          // Thread function
                      (void *)0,                    // Thread function arg
                      (SsxAddress)test_thread_stack,// Stack to use for this thread
                      THREAD_STACK_SIZE,            // Size of this thread's stack
                      THREAD_PRIORITY_0);           // This thread's priority (0 is highest)

    // Make the test thread runnable
    ssx_thread_resume(&test_thread);

    // Start rtl code
    rtl_ocb_init();

    // Stop the interrupt handler and initialize CURRENT_TICK (just in case).
    // Also set the 'testing' flag.
    ssx_irq_disable(PGP_IRQ_OCC_TIMER0);
    CURRENT_TICK = 0xFFFFFFFF;
    printf("\n\nmain: ocb timer stopped; waiting for first test\n");

    // Enter SSX Kernel
    ssx_start_threads();

    return 0;
}

OpenPOWER on IntegriCloud