From c2f4d16db57b99c4fee58bcfa01bdc02e909f405 Mon Sep 17 00:00:00 2001 From: pengyiqiang Date: Thu, 28 Oct 2021 20:39:54 +0800 Subject: [PATCH] system: add ramspeed test. Signed-off-by: pengyiqiang --- system/ramspeed/Kconfig | 29 +++ system/ramspeed/Make.defs | 23 ++ system/ramspeed/Makefile | 32 +++ system/ramspeed/ramspeed.c | 435 +++++++++++++++++++++++++++++++++++++ 4 files changed, 519 insertions(+) create mode 100644 system/ramspeed/Kconfig create mode 100644 system/ramspeed/Make.defs create mode 100644 system/ramspeed/Makefile create mode 100644 system/ramspeed/ramspeed.c diff --git a/system/ramspeed/Kconfig b/system/ramspeed/Kconfig new file mode 100644 index 000000000..25c0bd763 --- /dev/null +++ b/system/ramspeed/Kconfig @@ -0,0 +1,29 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config SYSTEM_RAMSPEED + tristate "RAM Speed Test" + default n + ---help--- + Enable a simple RAM speed test. + +if SYSTEM_RAMSPEED + +config SYSTEM_RAMSPEED_PROGNAME + string "Program name" + default "ramspeed" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config SYSTEM_RAMSPEED_PRIORITY + int "RAM speed test task priority" + default 100 + +config SYSTEM_RAMSPEED_STACKSIZE + int "RAM speed test stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/system/ramspeed/Make.defs b/system/ramspeed/Make.defs new file mode 100644 index 000000000..44361700e --- /dev/null +++ b/system/ramspeed/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/system/ramspeed/Make.defs +# +# 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_RAMSPEED),) +CONFIGURED_APPS += $(APPDIR)/system/ramspeed +endif diff --git a/system/ramspeed/Makefile b/system/ramspeed/Makefile new file mode 100644 index 000000000..4edbfc8d8 --- /dev/null +++ b/system/ramspeed/Makefile @@ -0,0 +1,32 @@ +############################################################################ +# apps/system/ramspeed/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 + +# RAM speed test + +PROGNAME = $(CONFIG_SYSTEM_RAMSPEED_PROGNAME) +PRIORITY = $(CONFIG_SYSTEM_RAMSPEED_PRIORITY) +STACKSIZE = $(CONFIG_SYSTEM_RAMSPEED_STACKSIZE) +MODULE = $(CONFIG_SYSTEM_RAMSPEED) + +MAINSRC = ramspeed.c + +include $(APPDIR)/Application.mk diff --git a/system/ramspeed/ramspeed.c b/system/ramspeed/ramspeed.c new file mode 100644 index 000000000..68dca3d0a --- /dev/null +++ b/system/ramspeed/ramspeed.c @@ -0,0 +1,435 @@ +/**************************************************************************** + * apps/system/ramspeed/ramspeed.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 + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define RAMSPEED_PREFIX "RAM Speed: " + +#if defined(UINTPTR_MAX) && UINTPTR_MAX > 0xFFFFFFFF +# define MEM_UNIT uint64_t +# define ALIGN_MASK 0x7 +#else +# define MEM_UNIT uint32_t +# define ALIGN_MASK 0x3 +#endif + +#define COPY32 *d32 = *s32; d32++; s32++; +#define COPY8 *d8 = *s8; d8++; s8++; +#define SET32(x) *d32 = x; d32++; +#define SET8(x) *d8 = x; d8++; +#define REPEAT8(expr) expr expr expr expr expr expr expr expr + +#define OPTARG_TO_VALUE(value, type, base) \ + do \ + { \ + FAR char *ptr; \ + value = (type)strtoul(optarg, &ptr, base); \ + if (*ptr != '\0') \ + { \ + printf(RAMSPEED_PREFIX "Parameter error: -%c %s\n", ch, optarg); \ + show_usage(argv[0], EXIT_FAILURE); \ + } \ + } while (0) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct ramspeed_s +{ + FAR void *dest; + FAR const void *src; + size_t size; + uint8_t value; + uint32_t repeat_num; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: show_usage + ****************************************************************************/ + +static void show_usage(FAR const char *progname, int exitcode) +{ + printf("\nUsage: %s -r -w -s " + " -v [0x00] -n [100]\n", + progname); + printf("\nWhere:\n"); + printf(" -r read address.\n"); + printf(" -w write address.\n"); + printf(" -s number of memory locations (in bytes).\n"); + printf(" -v value to fill in memory" + " [default value: 0x00].\n"); + printf(" -n number of repetitions" + " [default value: 100].\n"); + exit(exitcode); +} + +/**************************************************************************** + * Name: parse_commandline + ****************************************************************************/ + +static void parse_commandline(int argc, FAR char **argv, + FAR struct ramspeed_s *info) +{ + int ch; + + memset(info, 0, sizeof(struct ramspeed_s)); + info->repeat_num = 100; + + if (argc < 7) + { + printf(RAMSPEED_PREFIX "Missing required arguments\n"); + show_usage(argv[0], EXIT_FAILURE); + } + + while ((ch = getopt(argc, argv, "r:w:s:v:n:")) != ERROR) + { + switch (ch) + { + case 'r': + OPTARG_TO_VALUE(info->src, const void *, 16); + break; + case 'w': + OPTARG_TO_VALUE(info->dest, void *, 16); + break; + case 's': + OPTARG_TO_VALUE(info->size, size_t, 10); + break; + case 'v': + OPTARG_TO_VALUE(info->value, uint8_t, 16); + break; + case 'n': + OPTARG_TO_VALUE(info->repeat_num, uint32_t, 10); + if (info->repeat_num == 0) + { + printf(RAMSPEED_PREFIX " must > 0\n"); + exit(EXIT_FAILURE); + } + break; + case '?': + printf(RAMSPEED_PREFIX "Unknown option: %c\n", (char)optopt); + show_usage(argv[0], EXIT_FAILURE); + break; + } + } + + if (info->dest == NULL || info->src == NULL || info->size == 0) + { + printf(RAMSPEED_PREFIX "Missing required arguments\n"); + show_usage(argv[0], EXIT_FAILURE); + } +} + +/**************************************************************************** + * Name: get_timestamp + ****************************************************************************/ + +static uint32_t get_timestamp(void) +{ + struct timespec ts; + uint32_t ms; + clock_gettime(CLOCK_MONOTONIC, &ts); + ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + return ms; +} + +/**************************************************************************** + * Name: get_time_elaps + ****************************************************************************/ + +static uint32_t get_time_elaps(uint32_t prev_time) +{ + uint32_t act_time = get_timestamp(); + + /* If there is no overflow in sys_time simple subtract */ + + if (act_time >= prev_time) + { + prev_time = act_time - prev_time; + } + else + { + prev_time = UINT32_MAX - prev_time + 1; + prev_time += act_time; + } + + return prev_time; +} + +/**************************************************************************** + * Name: internal_memcpy + ****************************************************************************/ + +static void *internal_memcpy(FAR void *dst, FAR const void *src, size_t len) +{ + FAR uint8_t *d8 = dst; + FAR const uint8_t *s8 = src; + + uintptr_t d_align = (uintptr_t)d8 & ALIGN_MASK; + uintptr_t s_align = (uintptr_t)s8 & ALIGN_MASK; + FAR uint32_t *d32; + FAR const uint32_t *s32; + + /* Byte copy for unaligned memories */ + + if (s_align != d_align) + { + while (len > 32) + { + REPEAT8(COPY8); + REPEAT8(COPY8); + REPEAT8(COPY8); + REPEAT8(COPY8); + len -= 32; + } + + while (len) + { + COPY8; + len--; + } + + return dst; + } + + /* Make the memories aligned */ + + if (d_align) + { + d_align = ALIGN_MASK + 1 - d_align; + while (d_align && len) + { + COPY8; + d_align--; + len--; + } + } + + d32 = (FAR uint32_t *)d8; + s32 = (FAR uint32_t *)s8; + while (len > 32) + { + REPEAT8(COPY32); + len -= 32; + } + + while (len > 4) + { + COPY32; + len -= 4; + } + + d8 = (FAR uint8_t *)d32; + s8 = (FAR const uint8_t *)s32; + while (len) + { + COPY8; + len--; + } + + return dst; +} + +/**************************************************************************** + * Name: internal_memset + ****************************************************************************/ + +static void internal_memset(FAR void *dst, uint8_t v, size_t len) +{ + FAR uint8_t *d8 = (FAR uint8_t *)dst; + uintptr_t d_align = (uintptr_t) d8 & ALIGN_MASK; + FAR uint32_t v32; + FAR uint32_t *d32; + + /* Make the address aligned */ + + if (d_align) + { + d_align = ALIGN_MASK + 1 - d_align; + while (d_align && len) + { + SET8(v); + len--; + d_align--; + } + } + + v32 = (uint32_t)v + ((uint32_t)v << 8) + + ((uint32_t)v << 16) + ((uint32_t)v << 24); + + d32 = (FAR uint32_t *)d8; + + while (len > 32) + { + REPEAT8(SET32(v32)); + len -= 32; + } + + while (len > 4) + { + SET32(v32); + len -= 4; + } + + d8 = (FAR uint8_t *)d32; + while (len) + { + SET8(v); + len--; + } +} + +/**************************************************************************** + * Name: print_rate + ****************************************************************************/ + +static void print_rate(FAR const char *name, size_t bytes, + uint32_t cost_time) +{ + uint32_t rate; + if (cost_time == 0) + { + printf(RAMSPEED_PREFIX + "Time-consuming is too short," + " please increase the \n"); + exit(EXIT_FAILURE); + } + + rate = (uint64_t)bytes * 1000 / cost_time / 1024; + printf(RAMSPEED_PREFIX + "%s Rate = %" PRIu32 " KB/s\t[cost: %" PRIu32 "ms]\n", + name, rate, cost_time); +} + +/**************************************************************************** + * Name: memcpy_speed_test + ****************************************************************************/ + +static void memcpy_speed_test(FAR void *dest, FAR const void *src, + size_t size, uint32_t repeat_cnt) +{ + uint32_t start_time; + uint32_t cost_time; + uint32_t cnt; + const size_t total_size = size * repeat_cnt; + + start_time = get_timestamp(); + + for (cnt = 0; cnt < repeat_cnt; cnt++) + { + memcpy(dest, src, size); + } + + cost_time = get_time_elaps(start_time); + + print_rate("system memcpy():\t", total_size, cost_time); + + start_time = get_timestamp(); + + for (cnt = 0; cnt < repeat_cnt; cnt++) + { + internal_memcpy(dest, src, size); + } + + cost_time = get_time_elaps(start_time); + + print_rate("internal memcpy():\t", total_size, cost_time); +} + +/**************************************************************************** + * Name: memset_speed_test + ****************************************************************************/ + +static void memset_speed_test(FAR void *dest, uint8_t value, + size_t size, uint32_t repeat_num) +{ + uint32_t start_time; + uint32_t cost_time; + uint32_t cnt; + const size_t total_size = size * repeat_num; + + start_time = get_timestamp(); + + for (cnt = 0; cnt < repeat_num; cnt++) + { + memset(dest, value, size); + } + + cost_time = get_time_elaps(start_time); + + print_rate("system memset():\t", total_size, cost_time); + + start_time = get_timestamp(); + + for (cnt = 0; cnt < repeat_num; cnt++) + { + internal_memset(dest, value, size); + } + + cost_time = get_time_elaps(start_time); + + print_rate("internal memset():\t", total_size, cost_time); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: main + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + struct ramspeed_s ramspeed; + + parse_commandline(argc, argv, &ramspeed); + + memcpy_speed_test(ramspeed.dest, ramspeed.src, + ramspeed.size, ramspeed.repeat_num); + + memset_speed_test(ramspeed.dest, ramspeed.value, + ramspeed.size, ramspeed.repeat_num); + + return EXIT_SUCCESS; +}