diff --git a/system/cachespeed/Kconfig b/system/cachespeed/Kconfig new file mode 100644 index 000000000..9b581b05b --- /dev/null +++ b/system/cachespeed/Kconfig @@ -0,0 +1,30 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config SYSTEM_CACHESPEED + tristate "CACHE Speed Test" + default n + ---help--- + Enable a simple CACHE speed test. + +if SYSTEM_CACHESPEED + +config SYSTEM_CACHESPEED_PROGNAME + string "Program name" + default "cachespeed" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config SYSTEM_CACHESPEED_PRIORITY + int "CACHE speed test task priority" + default 100 + +config SYSTEM_CACHESPEED_STACKSIZE + int "CACHE speed test stack size" + default DEFAULT_TASK_STACKSIZE + +endif + diff --git a/system/cachespeed/Make.defs b/system/cachespeed/Make.defs new file mode 100644 index 000000000..ac159b4cb --- /dev/null +++ b/system/cachespeed/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/system/cachespeed/Make.def +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you 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. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_CACHESPEED),) +CONFIGURED_APPS += $(APPDIR)/system/cachespeed +endif \ No newline at end of file diff --git a/system/cachespeed/Makefile b/system/cachespeed/Makefile new file mode 100644 index 000000000..0c5e1e325 --- /dev/null +++ b/system/cachespeed/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/system/cachespeed/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +#CACHE speed test + +PROGNAME = $(CONFIG_SYSTEM_CACHESPEED_PROGNAME) +PRIORITY = $(CONFIG_SYSTEM_CACHESPEED_PRIORITY) +STACKSIZE = $(CONFIG_SYSTEM_CACHESPEED_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_CACHESPEED) + +MAINSRC = cachespeed_main.c + +include $(APPDIR)/Application.mk diff --git a/system/cachespeed/cachespeed_main.c b/system/cachespeed/cachespeed_main.c new file mode 100644 index 000000000..67cb585a8 --- /dev/null +++ b/system/cachespeed/cachespeed_main.c @@ -0,0 +1,368 @@ +/**************************************************************************** + * apps/system/cachespeed/cachespeed_main.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define CACHESPEED_PREFIX "CACHE Speed: " + +#define GET_VALUE(value, type) \ + do \ + { \ + FAR char *ptr; \ + value = (type)strtoul(optarg, &ptr, 0); \ + if (*ptr != '\0') \ + { \ + printf(CACHESPEED_PREFIX "Parameter error: -%c %s\n", option, optarg); \ + show_usage(argv[0], EXIT_FAILURE); \ + } \ + } while (0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct cachespeed_s +{ + FAR void *begin; + size_t memset_size; + uint32_t repeat_num; + size_t opt_size; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: show_usage + ****************************************************************************/ + +static void show_usage(FAR const char *progname, int exitcode) +{ + printf("\nUsage: %s -b
-o " + " -s [262144] -n [100] \n", + progname); + printf("\nWhere:\n"); + printf(" -b begin memset address.\n"); + printf(" -o The size of the operation.\n"); + printf(" -s Execute memset size (in bytes)." + " [default value: 262144].\n"); + printf(" -n number of repetitions" + " [default value: 1000].\n"); + exit(exitcode); +} + +/**************************************************************************** + * Name: parse_commandline + ****************************************************************************/ + +static void parse_commandline(int argc, FAR char **argv, + FAR struct cachespeed_s *info) +{ + int option; + + memset(info, 0, sizeof(struct cachespeed_s)); + info->repeat_num = 1000; + info->memset_size = 262144; + + while ((option = getopt(argc, argv, "b:o:s:n:")) != ERROR) + { + switch (option) + { + case 'b': + GET_VALUE(info->begin, void *); + break; + case 'o': + GET_VALUE(info->opt_size, size_t); + break; + case 's': + GET_VALUE(info->memset_size, size_t); + break; + case 'n': + GET_VALUE(info->repeat_num, uint32_t); + if (info->repeat_num == 0) + { + printf(CACHESPEED_PREFIX " must > 0\n"); + exit(EXIT_FAILURE); + } + break; + case '?': + printf(CACHESPEED_PREFIX "Unknown option: %c\n", optopt); + show_usage(argv[0], EXIT_FAILURE); + break; + } + } + + if (info->opt_size == 0 || info->begin == 0) + { + printf(CACHESPEED_PREFIX "Missing required arguments\n"); + show_usage(argv[0], EXIT_FAILURE); + } +} + +/**************************************************************************** + * Name: get_perf_time + ****************************************************************************/ + +static uint32_t get_perf_time(void) +{ + return up_perf_gettime(); +} + +/**************************************************************************** + * Name: get_time_elaps + ****************************************************************************/ + +static uint32_t get_time_elaps(uint32_t prev_time) +{ + return get_perf_time() - prev_time; +} + +/**************************************************************************** + * Name: print_result + ****************************************************************************/ + +static void print_result(FAR const char *name, size_t bytes, + uint32_t cost_time) +{ + uint32_t rate; + struct timespec ts; + + /* Converted to ms */ + + up_perf_convert(cost_time, &ts); + cost_time = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + + if (cost_time == 0) + { + printf(CACHESPEED_PREFIX + "Time-consuming is too short\n"); + exit(EXIT_FAILURE); + } + + /* rate = (bytes / 1024) / (cost_time / 1000) */ + + rate = (uint64_t)bytes * 1000 / cost_time / 1024; + printf(CACHESPEED_PREFIX + "%s cost = %" PRIu32 " ms\t[Rate: %" PRIu32 "KB/s]\n", + name, cost_time, rate); +} + +/**************************************************************************** + * Name: dcache_speed_test + ****************************************************************************/ + +static void dcache_speed_test(FAR void *begin, size_t memset_size, + size_t opt_size, uint32_t repeat_cnt) +{ + size_t total_size = memset_size * repeat_cnt; + uint32_t invalidate_cost_time; + uint32_t clean_cost_time; + uint32_t flush_cost_time; + uint32_t cnt; + uint32_t pt; + irqstate_t flags; + + /* Initialize a variable */ + + invalidate_cost_time = 0; + clean_cost_time = 0; + flush_cost_time = 0; + + /* Accumulate the time to get the total time */ + + printf("______dcache performance______\n"); + + printf("______do all operation______\n"); + flags = enter_critical_section(); + for (cnt = 0; cnt < repeat_cnt; cnt++) + { + uint32_t start_time; + memset(begin, 0, memset_size); + start_time = get_perf_time(); + up_clean_dcache_all(); + clean_cost_time += get_time_elaps(start_time); + + memset(begin, 0, memset_size); + start_time = get_perf_time(); + up_flush_dcache_all(); + flush_cost_time += get_time_elaps(start_time); + } + + leave_critical_section(flags); + print_result("clean dcache():\t", total_size, clean_cost_time); + print_result("flush dcache():\t", total_size, flush_cost_time); + + for (pt = 32; pt <= opt_size; pt <<= 1) + { + total_size = pt * repeat_cnt; + invalidate_cost_time = 0; + clean_cost_time = 0; + flush_cost_time = 0; + + if (pt < 1024) + { + printf("______do %" PRIu32 " B operation______\n", pt); + } + else + { + printf("______do %" PRIu32 " KB operation______\n", pt / 1024); + } + + flags = enter_critical_section(); + for (cnt = 0; cnt < repeat_cnt; cnt++) + { + uint32_t start_time; + memset(begin, 0, memset_size); + start_time = get_perf_time(); + up_invalidate_dcache((uintptr_t)begin, + (uintptr_t)((uint8_t *)begin + pt)); + invalidate_cost_time += get_time_elaps(start_time); + + memset(begin, 0, memset_size); + start_time = get_perf_time(); + up_clean_dcache((uintptr_t)begin, + (uintptr_t)((uint8_t *)begin + pt)); + clean_cost_time += get_time_elaps(start_time); + + memset(begin, 0, memset_size); + start_time = get_perf_time(); + up_flush_dcache((uintptr_t)begin, + (uintptr_t)((uint8_t *)begin + pt)); + flush_cost_time += get_time_elaps(start_time); + } + + leave_critical_section(flags); + print_result("invalidate dcache():\t", + total_size, invalidate_cost_time); + print_result("clean dcache():\t", total_size, clean_cost_time); + print_result("flush dcache():\t", total_size, flush_cost_time); + } +} + +/**************************************************************************** + * Name: icache_speed_test + ****************************************************************************/ + +static void icache_speed_test(FAR void *begin, size_t memset_size, + size_t opt_size, uint32_t repeat_cnt) +{ + irqstate_t flags; + int32_t cnt; + uint32_t pt; + uint32_t invalidate_cost_time = 0; + + /* Accumulate the time to get the total time */ + + printf("______icache performance______\n"); + + printf("______do all operation______\n"); + flags = enter_critical_section(); + for (cnt = 0; cnt < repeat_cnt; cnt++) + { + uint32_t start_time; + memset(begin, 0, memset_size); + start_time = get_perf_time(); + up_invalidate_icache_all(); + invalidate_cost_time += get_time_elaps(start_time); + } + + leave_critical_section(flags); + print_result("invalidate dcache():\t", + memset_size * repeat_cnt, invalidate_cost_time); + + for (pt = 32; pt <= memset_size; pt <<= 1) + { + const size_t total_size = pt * repeat_cnt; + invalidate_cost_time = 0; + if (pt < 1024) + { + printf("______do %" PRIu32 " B operation______\n", pt); + } + else + { + printf("______do %" PRIu32 " KB operation______\n", pt / 1024); + } + + flags = enter_critical_section(); + for (cnt = 0; cnt < repeat_cnt; cnt++) + { + uint32_t start_time; + memset(begin, 0, memset_size); + start_time = get_perf_time(); + up_invalidate_icache((uintptr_t)begin, + (uintptr_t)((uint8_t *)begin + pt)); + invalidate_cost_time += get_time_elaps(start_time); + } + + leave_critical_section(flags); + print_result("invalidate icache():\t", + total_size, invalidate_cost_time); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: cachespeed_main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct cachespeed_s cachespeed; + + /* Setup defaults and parse the command line */ + + parse_commandline(argc, argv, &cachespeed); + + /* Perform the dcache and icache test */ + + dcache_speed_test(cachespeed.begin, cachespeed.memset_size, + cachespeed.opt_size, cachespeed.repeat_num); + + icache_speed_test(cachespeed.begin, cachespeed.memset_size, + cachespeed.opt_size, cachespeed.repeat_num); + + return 0; +}