Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:46

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (C) 2014, 2017 embedded brains GmbH & Co. KG
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions
0008  * are met:
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0025  * POSSIBILITY OF SUCH DAMAGE.
0026  */
0027 
0028 #ifdef HAVE_CONFIG_H
0029 #include "config.h"
0030 #endif
0031 
0032 #include <inttypes.h>
0033 #include <setjmp.h>
0034 #include <stdio.h>
0035 #include <stdlib.h>
0036 
0037 #include <rtems.h>
0038 #include <rtems/counter.h>
0039 #include <rtems/score/cpuimpl.h>
0040 #include <rtems/score/sysstate.h>
0041 
0042 #include "tmacros.h"
0043 
0044 const char rtems_test_name[] = "SPCACHE 1";
0045 
0046 #define I() _CPU_Instruction_no_operation()
0047 
0048 #define I8() I(); I(); I(); I(); I(); I(); I(); I()
0049 
0050 #define I64() I8(); I8(); I8(); I8(); I8(); I8(); I8(); I8()
0051 
0052 #define I512() I64(); I64(); I64(); I64(); I64(); I64(); I64(); I64()
0053 
0054 CPU_STRUCTURE_ALIGNMENT static int data[1024];
0055 
0056 static bool do_longjmp;
0057 
0058 static jmp_buf instruction_invalidate_return_context;
0059 
0060 static void test_data_flush_and_invalidate(void)
0061 {
0062   if (rtems_cache_get_data_line_size() > 0) {
0063     RTEMS_INTERRUPT_LOCK_DECLARE(, lock)
0064     rtems_interrupt_lock_context lock_context;
0065     volatile int *vdata = &data[0];
0066     int n = 32;
0067     int i;
0068     size_t data_size = n * sizeof(data[0]);
0069     bool write_through;
0070 
0071     printf("data cache flush and invalidate test\n");
0072 
0073     rtems_interrupt_lock_initialize(&lock, "test");
0074     rtems_interrupt_lock_acquire(&lock, &lock_context);
0075 
0076     for (i = 0; i < n; ++i) {
0077       vdata[i] = i;
0078     }
0079 
0080     rtems_cache_flush_multiple_data_lines(&data[0], data_size);
0081 
0082     for (i = 0; i < n; ++i) {
0083       rtems_test_assert(vdata[i] == i);
0084     }
0085 
0086     for (i = 0; i < n; ++i) {
0087       vdata[i] = ~i;
0088     }
0089 
0090     rtems_cache_invalidate_multiple_data_lines(&data[0], data_size);
0091 
0092     write_through = vdata[0] == ~0;
0093     if (write_through) {
0094       for (i = 0; i < n; ++i) {
0095         rtems_test_assert(vdata[i] == ~i);
0096       }
0097     } else {
0098       for (i = 0; i < n; ++i) {
0099         rtems_test_assert(vdata[i] == i);
0100       }
0101     }
0102 
0103     for (i = 0; i < n; ++i) {
0104       vdata[i] = ~i;
0105     }
0106 
0107     rtems_cache_flush_multiple_data_lines(&data[0], data_size);
0108     rtems_cache_invalidate_multiple_data_lines(&data[0], data_size);
0109 
0110     for (i = 0; i < n; ++i) {
0111       rtems_test_assert(vdata[i] == ~i);
0112     }
0113 
0114     rtems_interrupt_lock_release(&lock, &lock_context);
0115     rtems_interrupt_lock_destroy(&lock);
0116 
0117     printf(
0118       "data cache operations by line passed the test (%s cache detected)\n",
0119       write_through ? "write-through" : "copy-back"
0120     );
0121   } else {
0122     printf(
0123       "skip data cache flush and invalidate test"
0124         " due to cache line size of zero\n"
0125     );
0126   }
0127 
0128   /* Make sure these are nops */
0129   rtems_cache_flush_multiple_data_lines(NULL, 0);
0130   rtems_cache_invalidate_multiple_data_lines(NULL, 0);
0131 }
0132 
0133 static uint64_t do_some_work(void)
0134 {
0135   rtems_counter_ticks a;
0136   rtems_counter_ticks b;
0137   rtems_counter_ticks d;
0138 
0139   /* This gives 1024 nop instructions */
0140   a = rtems_counter_read();
0141   I512();
0142   I512();
0143   b = rtems_counter_read();
0144 
0145   d = rtems_counter_difference(b, a);
0146 
0147   return rtems_counter_ticks_to_nanoseconds(d);
0148 }
0149 
0150 static uint64_t load(void)
0151 {
0152   rtems_counter_ticks a;
0153   rtems_counter_ticks b;
0154   rtems_counter_ticks d;
0155   size_t i;
0156   volatile int *vdata = &data[0];
0157 
0158   a = rtems_counter_read();
0159   for (i = 0; i < RTEMS_ARRAY_SIZE(data); ++i) {
0160     vdata[i];
0161   }
0162   b = rtems_counter_read();
0163 
0164   d = rtems_counter_difference(b, a);
0165 
0166   return rtems_counter_ticks_to_nanoseconds(d);
0167 }
0168 
0169 static uint64_t store(void)
0170 {
0171   rtems_counter_ticks a;
0172   rtems_counter_ticks b;
0173   rtems_counter_ticks d;
0174   size_t i;
0175   volatile int *vdata = &data[0];
0176 
0177   a = rtems_counter_read();
0178   for (i = 0; i < RTEMS_ARRAY_SIZE(data); ++i) {
0179     vdata[i] = 0;
0180   }
0181   b = rtems_counter_read();
0182 
0183   d = rtems_counter_difference(b, a);
0184 
0185   return rtems_counter_ticks_to_nanoseconds(d);
0186 }
0187 
0188 static void test_timing(void)
0189 {
0190   RTEMS_INTERRUPT_LOCK_DECLARE(, lock)
0191   rtems_interrupt_lock_context lock_context;
0192   size_t data_size = sizeof(data);
0193   uint64_t d[3];
0194   uint32_t cache_level;
0195   size_t cache_size;
0196   bool exception;
0197 
0198   rtems_interrupt_lock_initialize(&lock, "test");
0199 
0200   printf(
0201     "data cache line size %zi bytes\n"
0202     "data cache size %zi bytes\n",
0203     rtems_cache_get_data_line_size(),
0204     rtems_cache_get_data_cache_size(0)
0205   );
0206 
0207   cache_level = 1;
0208   cache_size = rtems_cache_get_data_cache_size(cache_level);
0209   while (cache_size > 0) {
0210     printf(
0211       "data cache level %" PRIu32 " size %zi bytes\n",
0212       cache_level,
0213       cache_size
0214     );
0215     ++cache_level;
0216     cache_size = rtems_cache_get_data_cache_size(cache_level);
0217   }
0218 
0219   rtems_interrupt_lock_acquire(&lock, &lock_context);
0220 
0221   d[0] = load();
0222   d[1] = load();
0223   rtems_cache_flush_entire_data();
0224   d[2] = load();
0225 
0226   rtems_interrupt_lock_release(&lock, &lock_context);
0227 
0228   printf(
0229     "load %zi bytes with flush entire data\n"
0230     "  duration with normal cache %" PRIu64 " ns\n"
0231     "  duration with warm cache %" PRIu64 " ns\n"
0232     "  duration with flushed cache %" PRIu64 " ns\n",
0233     data_size,
0234     d[0],
0235     d[1],
0236     d[2]
0237   );
0238 
0239   rtems_interrupt_lock_acquire(&lock, &lock_context);
0240 
0241   d[0] = load();
0242   d[1] = load();
0243   rtems_cache_flush_multiple_data_lines(&data[0], sizeof(data));
0244   d[2] = load();
0245 
0246   rtems_interrupt_lock_release(&lock, &lock_context);
0247 
0248   printf(
0249     "load %zi bytes with flush multiple data\n"
0250     "  duration with normal cache %" PRIu64 " ns\n"
0251     "  duration with warm cache %" PRIu64 " ns\n"
0252     "  duration with flushed cache %" PRIu64 " ns\n",
0253     data_size,
0254     d[0],
0255     d[1],
0256     d[2]
0257   );
0258 
0259   rtems_interrupt_lock_acquire(&lock, &lock_context);
0260 
0261   d[0] = load();
0262   d[1] = load();
0263   rtems_cache_invalidate_multiple_data_lines(&data[0], sizeof(data));
0264   d[2] = load();
0265 
0266   rtems_interrupt_lock_release(&lock, &lock_context);
0267 
0268   printf(
0269     "load %zi bytes with invalidate multiple data\n"
0270     "  duration with normal cache %" PRIu64 " ns\n"
0271     "  duration with warm cache %" PRIu64 " ns\n"
0272     "  duration with invalidated cache %" PRIu64 " ns\n",
0273     data_size,
0274     d[0],
0275     d[1],
0276     d[2]
0277   );
0278 
0279   rtems_interrupt_lock_acquire(&lock, &lock_context);
0280 
0281   d[0] = store();
0282   d[1] = store();
0283   rtems_cache_flush_entire_data();
0284   d[2] = store();
0285 
0286   rtems_interrupt_lock_release(&lock, &lock_context);
0287 
0288   printf(
0289     "store %zi bytes with flush entire data\n"
0290     "  duration with normal cache %" PRIu64 " ns\n"
0291     "  duration with warm cache %" PRIu64 " ns\n"
0292     "  duration with flushed cache %" PRIu64 " ns\n",
0293     data_size,
0294     d[0],
0295     d[1],
0296     d[2]
0297   );
0298 
0299   rtems_interrupt_lock_acquire(&lock, &lock_context);
0300 
0301   d[0] = store();
0302   d[1] = store();
0303   rtems_cache_flush_multiple_data_lines(&data[0], sizeof(data));
0304   d[2] = store();
0305 
0306   rtems_interrupt_lock_release(&lock, &lock_context);
0307 
0308   printf(
0309     "store %zi bytes with flush multiple data\n"
0310     "  duration with normal cache %" PRIu64 " ns\n"
0311     "  duration with warm cache %" PRIu64 " ns\n"
0312     "  duration with flushed cache %" PRIu64 " ns\n",
0313     data_size,
0314     d[0],
0315     d[1],
0316     d[2]
0317   );
0318 
0319   rtems_interrupt_lock_acquire(&lock, &lock_context);
0320 
0321   d[0] = store();
0322   d[1] = store();
0323   rtems_cache_invalidate_multiple_data_lines(&data[0], sizeof(data));
0324   d[2] = store();
0325 
0326   rtems_interrupt_lock_release(&lock, &lock_context);
0327 
0328   printf(
0329     "store %zi bytes with invalidate multiple data\n"
0330     "  duration with normal cache %" PRIu64 " ns\n"
0331     "  duration with warm cache %" PRIu64 " ns\n"
0332     "  duration with invalidated cache %" PRIu64 " ns\n",
0333     data_size,
0334     d[0],
0335     d[1],
0336     d[2]
0337   );
0338 
0339   printf(
0340     "instruction cache line size %zi bytes\n"
0341     "instruction cache size %zi bytes\n",
0342     rtems_cache_get_instruction_line_size(),
0343     rtems_cache_get_instruction_cache_size(0)
0344   );
0345 
0346   cache_level = 1;
0347   cache_size = rtems_cache_get_instruction_cache_size(cache_level);
0348   while (cache_size > 0) {
0349     printf(
0350       "instruction cache level %" PRIu32 " size %zi bytes\n",
0351       cache_level,
0352       cache_size
0353     );
0354     ++cache_level;
0355     cache_size = rtems_cache_get_instruction_cache_size(cache_level);
0356   }
0357 
0358   rtems_interrupt_lock_acquire(&lock, &lock_context);
0359 
0360   d[0] = do_some_work();
0361   d[1] = do_some_work();
0362   rtems_cache_invalidate_entire_instruction();
0363   d[2] = do_some_work();
0364 
0365   rtems_interrupt_lock_release(&lock, &lock_context);
0366 
0367   printf(
0368     "invalidate entire instruction\n"
0369     "  duration with normal cache %" PRIu64 " ns\n"
0370     "  duration with warm cache %" PRIu64 " ns\n"
0371     "  duration with invalidated cache %" PRIu64 " ns\n",
0372     d[0],
0373     d[1],
0374     d[2]
0375   );
0376 
0377   rtems_interrupt_lock_acquire(&lock, &lock_context);
0378 
0379   d[0] = do_some_work();
0380   d[1] = do_some_work();
0381 
0382   do_longjmp = true;
0383 
0384   if (setjmp(instruction_invalidate_return_context) == 0) {
0385     rtems_cache_invalidate_multiple_instruction_lines(do_some_work, 4096);
0386     exception = false;
0387   } else {
0388     exception = true;
0389   }
0390 
0391   do_longjmp = false;
0392 
0393   d[2] = do_some_work();
0394 
0395   rtems_interrupt_lock_release(&lock, &lock_context);
0396 
0397   printf(
0398     "invalidate multiple instruction\n"
0399     "  duration with normal cache %" PRIu64 " ns\n"
0400     "  duration with warm cache %" PRIu64 " ns\n"
0401     "  duration with invalidated cache %" PRIu64 " ns\n",
0402     d[0],
0403     d[1],
0404     d[2]
0405   );
0406 
0407   if (exception) {
0408     printf(
0409       "rtems_cache_invalidate_multiple_data_lines() generated an exception\n"
0410     );
0411   }
0412 
0413   rtems_interrupt_lock_destroy(&lock);
0414 }
0415 
0416 static void test_cache_aligned_alloc(void)
0417 {
0418   void *p0;
0419   void *p1;
0420   size_t cls;
0421 
0422   printf("test rtems_cache_aligned_malloc()\n");
0423 
0424   p0 = rtems_cache_aligned_malloc(1);
0425   p1 = rtems_cache_aligned_malloc(1);
0426 
0427   rtems_test_assert(p0 != NULL);
0428   rtems_test_assert(p1 != NULL);
0429 
0430   cls = rtems_cache_get_data_line_size();
0431   if (cls > 0) {
0432     size_t m = cls - 1;
0433     uintptr_t a0 = (uintptr_t) p0;
0434     uintptr_t a1 = (uintptr_t) p1;
0435 
0436     rtems_test_assert(a1 - a0 > cls);
0437     rtems_test_assert((a0 & m) == 0);
0438     rtems_test_assert((a1 & m) == 0);
0439   }
0440 
0441   free(p0);
0442   free(p1);
0443 }
0444 
0445 #define AREA_SIZE 256
0446 
0447 static char cache_coherent_area_0[AREA_SIZE];
0448 
0449 static char cache_coherent_area_1[AREA_SIZE];
0450 
0451 static char cache_coherent_area_2[AREA_SIZE];
0452 
0453 static void add_area(void *begin)
0454 {
0455   rtems_cache_coherent_add_area(NULL, 0);
0456   rtems_cache_coherent_add_area(begin, AREA_SIZE);
0457 }
0458 
0459 static void test_cache_coherent_alloc(void)
0460 {
0461   void *p0;
0462   void *p1;
0463   System_state_Codes previous_state;
0464 
0465   printf("test cache coherent allocation\n");
0466 
0467   p0 = rtems_cache_coherent_allocate(1, 0, 0);
0468   rtems_test_assert(p0 != NULL);
0469 
0470   rtems_cache_coherent_free(p0);
0471 
0472   p0 = rtems_cache_coherent_allocate(1, 0, 0);
0473   rtems_test_assert(p0 != NULL);
0474 
0475   add_area(&cache_coherent_area_0[0]);
0476   add_area(&cache_coherent_area_1[0]);
0477 
0478   previous_state = _System_state_Get();
0479   _System_state_Set(previous_state + 1);
0480   add_area(&cache_coherent_area_2[0]);
0481   _System_state_Set(previous_state);
0482 
0483   p1 = rtems_cache_coherent_allocate(1, 0, 0);
0484   rtems_test_assert(p1 != NULL);
0485 
0486   rtems_cache_coherent_free(p0);
0487   rtems_cache_coherent_free(p1);
0488 }
0489 
0490 static void Init(rtems_task_argument arg)
0491 {
0492   TEST_BEGIN();
0493 
0494   test_data_flush_and_invalidate();
0495   test_timing();
0496   test_cache_aligned_alloc();
0497   test_cache_coherent_alloc();
0498 
0499   TEST_END();
0500 
0501   rtems_test_exit(0);
0502 }
0503 
0504 static void fatal_extension(
0505   rtems_fatal_source source,
0506   bool always_set_to_false,
0507   rtems_fatal_code error
0508 )
0509 {
0510   if (source == RTEMS_FATAL_SOURCE_EXCEPTION && do_longjmp) {
0511     _ISR_Set_level(0);
0512     longjmp(instruction_invalidate_return_context, 1);
0513   }
0514 }
0515 
0516 #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
0517 #define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
0518 
0519 #define CONFIGURE_MAXIMUM_TASKS 1
0520 
0521 #define CONFIGURE_INITIAL_EXTENSIONS \
0522   { .fatal = fatal_extension }, \
0523   RTEMS_TEST_INITIAL_EXTENSION
0524 
0525 #define CONFIGURE_RTEMS_INIT_TASKS_TABLE
0526 
0527 #define CONFIGURE_INIT
0528 
0529 #include <rtems/confdefs.h>