source: rtems/testsuites/sptests/spcache01/init.c @ 75e71b2

5
Last change on this file since 75e71b2 was 75e71b2, checked in by Sebastian Huber <sebastian.huber@…>, on 04/05/17 at 07:55:45

spcache01: Instruction cache invalidate workaround

On some systems, the instruction cache invalidation is not allowed by
the MMU.

  • Property mode set to 100644
File size: 11.6 KB
Line 
1/*
2 * Copyright (c) 2014, 2017 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#ifdef HAVE_CONFIG_H
16  #include "config.h"
17#endif
18
19#include <inttypes.h>
20#include <setjmp.h>
21#include <stdio.h>
22#include <stdlib.h>
23
24#include <rtems.h>
25#include <rtems/counter.h>
26#include <rtems/score/sysstate.h>
27
28#define TESTS_USE_PRINTF
29#include "tmacros.h"
30
31const char rtems_test_name[] = "SPCACHE 1";
32
33#ifdef __or1k__
34  #define I() __asm__ volatile ("l.nop")
35#else
36  #define I() __asm__ volatile ("nop")
37#endif
38
39#define I8() I(); I(); I(); I(); I(); I(); I(); I()
40
41#define I64() I8(); I8(); I8(); I8(); I8(); I8(); I8(); I8()
42
43#define I512() I64(); I64(); I64(); I64(); I64(); I64(); I64(); I64()
44
45CPU_STRUCTURE_ALIGNMENT static int data[1024];
46
47static bool do_longjmp;
48
49static jmp_buf instruction_invalidate_return_context;
50
51static void test_data_flush_and_invalidate(void)
52{
53  if (rtems_cache_get_data_line_size() > 0) {
54    RTEMS_INTERRUPT_LOCK_DECLARE(, lock)
55    rtems_interrupt_lock_context lock_context;
56    volatile int *vdata = &data[0];
57    int n = 32;
58    int i;
59    size_t data_size = n * sizeof(data[0]);
60    bool write_through;
61
62    printf("data cache flush and invalidate test\n");
63
64    rtems_interrupt_lock_initialize(&lock, "test");
65    rtems_interrupt_lock_acquire(&lock, &lock_context);
66
67    for (i = 0; i < n; ++i) {
68      vdata[i] = i;
69    }
70
71    rtems_cache_flush_multiple_data_lines(&data[0], data_size);
72
73    for (i = 0; i < n; ++i) {
74      rtems_test_assert(vdata[i] == i);
75    }
76
77    for (i = 0; i < n; ++i) {
78      vdata[i] = ~i;
79    }
80
81    rtems_cache_invalidate_multiple_data_lines(&data[0], data_size);
82
83    write_through = vdata[0] == ~0;
84    if (write_through) {
85      for (i = 0; i < n; ++i) {
86        rtems_test_assert(vdata[i] == ~i);
87      }
88    } else {
89      for (i = 0; i < n; ++i) {
90        rtems_test_assert(vdata[i] == i);
91      }
92    }
93
94    for (i = 0; i < n; ++i) {
95      vdata[i] = ~i;
96    }
97
98    rtems_cache_flush_multiple_data_lines(&data[0], data_size);
99    rtems_cache_invalidate_multiple_data_lines(&data[0], data_size);
100
101    for (i = 0; i < n; ++i) {
102      rtems_test_assert(vdata[i] == ~i);
103    }
104
105    rtems_interrupt_lock_release(&lock, &lock_context);
106    rtems_interrupt_lock_destroy(&lock);
107
108    printf(
109      "data cache operations by line passed the test (%s cache detected)\n",
110      write_through ? "write-through" : "copy-back"
111    );
112  } else {
113    printf(
114      "skip data cache flush and invalidate test"
115        " due to cache line size of zero\n"
116    );
117  }
118
119  /* Make sure these are nops */
120  rtems_cache_flush_multiple_data_lines(NULL, 0);
121  rtems_cache_invalidate_multiple_data_lines(NULL, 0);
122}
123
124static uint64_t do_some_work(void)
125{
126  rtems_counter_ticks a;
127  rtems_counter_ticks b;
128  rtems_counter_ticks d;
129
130  /* This gives 1024 nop instructions */
131  a = rtems_counter_read();
132  I512();
133  I512();
134  b = rtems_counter_read();
135
136  d = rtems_counter_difference(b, a);
137
138  return rtems_counter_ticks_to_nanoseconds(d);
139}
140
141static uint64_t load(void)
142{
143  rtems_counter_ticks a;
144  rtems_counter_ticks b;
145  rtems_counter_ticks d;
146  size_t i;
147  volatile int *vdata = &data[0];
148
149  a = rtems_counter_read();
150  for (i = 0; i < RTEMS_ARRAY_SIZE(data); ++i) {
151    vdata[i];
152  }
153  b = rtems_counter_read();
154
155  d = rtems_counter_difference(b, a);
156
157  return rtems_counter_ticks_to_nanoseconds(d);
158}
159
160static uint64_t store(void)
161{
162  rtems_counter_ticks a;
163  rtems_counter_ticks b;
164  rtems_counter_ticks d;
165  size_t i;
166  volatile int *vdata = &data[0];
167
168  a = rtems_counter_read();
169  for (i = 0; i < RTEMS_ARRAY_SIZE(data); ++i) {
170    vdata[i] = 0;
171  }
172  b = rtems_counter_read();
173
174  d = rtems_counter_difference(b, a);
175
176  return rtems_counter_ticks_to_nanoseconds(d);
177}
178
179static void test_timing(void)
180{
181  RTEMS_INTERRUPT_LOCK_DECLARE(, lock)
182  rtems_interrupt_lock_context lock_context;
183  size_t data_size = sizeof(data);
184  uint64_t d[3];
185  uint32_t cache_level;
186  size_t cache_size;
187  bool exception;
188
189  rtems_interrupt_lock_initialize(&lock, "test");
190
191  printf(
192    "data cache line size %zi bytes\n"
193    "data cache size %zi bytes\n",
194    rtems_cache_get_data_line_size(),
195    rtems_cache_get_data_cache_size(0)
196  );
197
198  cache_level = 1;
199  cache_size = rtems_cache_get_data_cache_size(cache_level);
200  while (cache_size > 0) {
201    printf(
202      "data cache level %" PRIu32 " size %zi bytes\n",
203      cache_level,
204      cache_size
205    );
206    ++cache_level;
207    cache_size = rtems_cache_get_data_cache_size(cache_level);
208  }
209
210  rtems_interrupt_lock_acquire(&lock, &lock_context);
211
212  d[0] = load();
213  d[1] = load();
214  rtems_cache_flush_entire_data();
215  d[2] = load();
216
217  rtems_interrupt_lock_release(&lock, &lock_context);
218
219  printf(
220    "load %zi bytes with flush entire data\n"
221    "  duration with normal cache %" PRIu64 " ns\n"
222    "  duration with warm cache %" PRIu64 " ns\n"
223    "  duration with flushed cache %" PRIu64 " ns\n",
224    data_size,
225    d[0],
226    d[1],
227    d[2]
228  );
229
230  rtems_interrupt_lock_acquire(&lock, &lock_context);
231
232  d[0] = load();
233  d[1] = load();
234  rtems_cache_flush_multiple_data_lines(&data[0], sizeof(data));
235  d[2] = load();
236
237  rtems_interrupt_lock_release(&lock, &lock_context);
238
239  printf(
240    "load %zi bytes with flush multiple data\n"
241    "  duration with normal cache %" PRIu64 " ns\n"
242    "  duration with warm cache %" PRIu64 " ns\n"
243    "  duration with flushed cache %" PRIu64 " ns\n",
244    data_size,
245    d[0],
246    d[1],
247    d[2]
248  );
249
250  rtems_interrupt_lock_acquire(&lock, &lock_context);
251
252  d[0] = load();
253  d[1] = load();
254  rtems_cache_invalidate_multiple_data_lines(&data[0], sizeof(data));
255  d[2] = load();
256
257  rtems_interrupt_lock_release(&lock, &lock_context);
258
259  printf(
260    "load %zi bytes with invalidate multiple data\n"
261    "  duration with normal cache %" PRIu64 " ns\n"
262    "  duration with warm cache %" PRIu64 " ns\n"
263    "  duration with invalidated cache %" PRIu64 " ns\n",
264    data_size,
265    d[0],
266    d[1],
267    d[2]
268  );
269
270  rtems_interrupt_lock_acquire(&lock, &lock_context);
271
272  d[0] = store();
273  d[1] = store();
274  rtems_cache_flush_entire_data();
275  d[2] = store();
276
277  rtems_interrupt_lock_release(&lock, &lock_context);
278
279  printf(
280    "store %zi bytes with flush entire data\n"
281    "  duration with normal cache %" PRIu64 " ns\n"
282    "  duration with warm cache %" PRIu64 " ns\n"
283    "  duration with flushed cache %" PRIu64 " ns\n",
284    data_size,
285    d[0],
286    d[1],
287    d[2]
288  );
289
290  rtems_interrupt_lock_acquire(&lock, &lock_context);
291
292  d[0] = store();
293  d[1] = store();
294  rtems_cache_flush_multiple_data_lines(&data[0], sizeof(data));
295  d[2] = store();
296
297  rtems_interrupt_lock_release(&lock, &lock_context);
298
299  printf(
300    "store %zi bytes with flush multiple data\n"
301    "  duration with normal cache %" PRIu64 " ns\n"
302    "  duration with warm cache %" PRIu64 " ns\n"
303    "  duration with flushed cache %" PRIu64 " ns\n",
304    data_size,
305    d[0],
306    d[1],
307    d[2]
308  );
309
310  rtems_interrupt_lock_acquire(&lock, &lock_context);
311
312  d[0] = store();
313  d[1] = store();
314  rtems_cache_invalidate_multiple_data_lines(&data[0], sizeof(data));
315  d[2] = store();
316
317  rtems_interrupt_lock_release(&lock, &lock_context);
318
319  printf(
320    "store %zi bytes with invalidate multiple data\n"
321    "  duration with normal cache %" PRIu64 " ns\n"
322    "  duration with warm cache %" PRIu64 " ns\n"
323    "  duration with invalidated cache %" PRIu64 " ns\n",
324    data_size,
325    d[0],
326    d[1],
327    d[2]
328  );
329
330  printf(
331    "instruction cache line size %zi bytes\n"
332    "instruction cache size %zi bytes\n",
333    rtems_cache_get_instruction_line_size(),
334    rtems_cache_get_instruction_cache_size(0)
335  );
336
337  cache_level = 1;
338  cache_size = rtems_cache_get_instruction_cache_size(cache_level);
339  while (cache_size > 0) {
340    printf(
341      "instruction cache level %" PRIu32 " size %zi bytes\n",
342      cache_level,
343      cache_size
344    );
345    ++cache_level;
346    cache_size = rtems_cache_get_instruction_cache_size(cache_level);
347  }
348
349  rtems_interrupt_lock_acquire(&lock, &lock_context);
350
351  d[0] = do_some_work();
352  d[1] = do_some_work();
353  rtems_cache_invalidate_entire_instruction();
354  d[2] = do_some_work();
355
356  rtems_interrupt_lock_release(&lock, &lock_context);
357
358  printf(
359    "invalidate entire instruction\n"
360    "  duration with normal cache %" PRIu64 " ns\n"
361    "  duration with warm cache %" PRIu64 " ns\n"
362    "  duration with invalidated cache %" PRIu64 " ns\n",
363    d[0],
364    d[1],
365    d[2]
366  );
367
368  rtems_interrupt_lock_acquire(&lock, &lock_context);
369
370  d[0] = do_some_work();
371  d[1] = do_some_work();
372
373  do_longjmp = true;
374
375  if (setjmp(instruction_invalidate_return_context) == 0) {
376    rtems_cache_invalidate_multiple_instruction_lines(do_some_work, 4096);
377    exception = false;
378  } else {
379    exception = true;
380  }
381
382  do_longjmp = false;
383
384  d[2] = do_some_work();
385
386  rtems_interrupt_lock_release(&lock, &lock_context);
387
388  printf(
389    "invalidate multiple instruction\n"
390    "  duration with normal cache %" PRIu64 " ns\n"
391    "  duration with warm cache %" PRIu64 " ns\n"
392    "  duration with invalidated cache %" PRIu64 " ns\n",
393    d[0],
394    d[1],
395    d[2]
396  );
397
398  if (exception) {
399    printf(
400      "rtems_cache_invalidate_multiple_data_lines() generated an exception\n"
401    );
402  }
403
404  rtems_interrupt_lock_destroy(&lock);
405}
406
407static void test_cache_aligned_alloc(void)
408{
409  void *p0;
410  void *p1;
411  size_t cls;
412
413  printf("test rtems_cache_aligned_malloc()\n");
414
415  p0 = rtems_cache_aligned_malloc(1);
416  p1 = rtems_cache_aligned_malloc(1);
417
418  rtems_test_assert(p0 != NULL);
419  rtems_test_assert(p1 != NULL);
420
421  cls = rtems_cache_get_data_line_size();
422  if (cls > 0) {
423    size_t m = cls - 1;
424    uintptr_t a0 = (uintptr_t) p0;
425    uintptr_t a1 = (uintptr_t) p1;
426
427    rtems_test_assert(a1 - a0 > cls);
428    rtems_test_assert((a0 & m) == 0);
429    rtems_test_assert((a1 & m) == 0);
430  }
431
432  free(p0);
433  free(p1);
434}
435
436#define AREA_SIZE 256
437
438static char cache_coherent_area_0[AREA_SIZE];
439
440static char cache_coherent_area_1[AREA_SIZE];
441
442static char cache_coherent_area_2[AREA_SIZE];
443
444static void add_area(void *begin)
445{
446  rtems_cache_coherent_add_area(NULL, 0);
447  rtems_cache_coherent_add_area(begin, AREA_SIZE);
448}
449
450static void test_cache_coherent_alloc(void)
451{
452  void *p0;
453  void *p1;
454  System_state_Codes previous_state;
455
456  printf("test cache coherent allocation\n");
457
458  p0 = rtems_cache_coherent_allocate(1, 0, 0);
459  rtems_test_assert(p0 != NULL);
460
461  rtems_cache_coherent_free(p0);
462
463  p0 = rtems_cache_coherent_allocate(1, 0, 0);
464  rtems_test_assert(p0 != NULL);
465
466  add_area(&cache_coherent_area_0[0]);
467  add_area(&cache_coherent_area_1[0]);
468
469  previous_state = _System_state_Get();
470  _System_state_Set(previous_state + 1);
471  add_area(&cache_coherent_area_2[0]);
472  _System_state_Set(previous_state);
473
474  p1 = rtems_cache_coherent_allocate(1, 0, 0);
475  rtems_test_assert(p1 != NULL);
476
477  rtems_cache_coherent_free(p0);
478  rtems_cache_coherent_free(p1);
479}
480
481static void Init(rtems_task_argument arg)
482{
483  TEST_BEGIN();
484
485  test_data_flush_and_invalidate();
486  test_timing();
487  test_cache_aligned_alloc();
488  test_cache_coherent_alloc();
489
490  TEST_END();
491
492  rtems_test_exit(0);
493}
494
495static void fatal_extension(
496  rtems_fatal_source source,
497  bool always_set_to_false,
498  rtems_fatal_code error
499)
500{
501  if (source == RTEMS_FATAL_SOURCE_EXCEPTION && do_longjmp) {
502    _ISR_Set_level(0);
503    longjmp(instruction_invalidate_return_context, 1);
504  }
505}
506
507#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
508#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
509
510#define CONFIGURE_MAXIMUM_TASKS 1
511
512#define CONFIGURE_INITIAL_EXTENSIONS \
513  { .fatal = fatal_extension }, \
514  RTEMS_TEST_INITIAL_EXTENSION
515
516#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
517
518#define CONFIGURE_INIT
519
520#include <rtems/confdefs.h>
Note: See TracBrowser for help on using the repository browser.