/**************************************************************************** * apps/system/ramspeed/ramspeed_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 /**************************************************************************** * 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; bool irq_disable; }; /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: show_usage ****************************************************************************/ static void show_usage(FAR const char *progname, int exitcode) { printf("\nUsage: %s -a -r -w -s " " -v [0x00] -n [100] -i\n", progname); printf("\nWhere:\n"); printf(" -a allocate RW buffers on heap. Overwrites -r and -w option.\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"); printf(" -i turn off interrupts while testing" " [default value: false].\n"); exit(exitcode); } /**************************************************************************** * Name: parse_commandline ****************************************************************************/ static void parse_commandline(int argc, FAR char **argv, FAR struct ramspeed_s *info) { int ch; bool allocate_rw_address = false; memset(info, 0, sizeof(struct ramspeed_s)); info->repeat_num = 100; if (argc < 4) { printf(RAMSPEED_PREFIX "Missing required arguments\n"); show_usage(argv[0], EXIT_FAILURE); } while ((ch = getopt(argc, argv, "r:w:s:v:n:i:a")) != ERROR) { switch (ch) { case 'a': allocate_rw_address = true; break; 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); if (info->size < 32) { printf(RAMSPEED_PREFIX " must >= 32"); exit(EXIT_FAILURE); } 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 'i': info->irq_disable = true; break; case '?': printf(RAMSPEED_PREFIX "Unknown option: %c\n", (char)optopt); show_usage(argv[0], EXIT_FAILURE); break; } } if (allocate_rw_address) { info->dest = malloc(info->size); info->src = malloc(info->size); } 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"); return; } 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, bool irq_disable) { uint32_t start_time; uint32_t cost_time_system; uint32_t cost_time_internal; uint32_t cnt; uint32_t step; size_t total_size; irqstate_t flags = 0; printf("______memcpy performance______\n"); for (step = 32; step <= size; step <<= 1) { total_size = step * repeat_cnt; if (step < 1024) { printf("______Perform %" PRIu32 " Bytes access ______\n", step); } else { printf("______Perform %" PRIu32 " KBytes access ______\n", step / 1024); } if (irq_disable) { flags = enter_critical_section(); } start_time = get_timestamp(); for (cnt = 0; cnt < repeat_cnt; cnt++) { memcpy(dest, src, step); } cost_time_system = get_time_elaps(start_time); start_time = get_timestamp(); for (cnt = 0; cnt < repeat_cnt; cnt++) { internal_memcpy(dest, src, step); } cost_time_internal = get_time_elaps(start_time); if (irq_disable) { leave_critical_section(flags); } print_rate("system memcpy():\t", total_size, cost_time_system); print_rate("internal memcpy():\t", total_size, cost_time_internal); } } /**************************************************************************** * Name: memset_speed_test ****************************************************************************/ static void memset_speed_test(FAR void *dest, uint8_t value, size_t size, uint32_t repeat_num, bool irq_disable) { uint32_t start_time; uint32_t cost_time_system; uint32_t cost_time_internal; uint32_t cnt; uint32_t step; size_t total_size; irqstate_t flags = 0; printf("______memset performance______\n"); for (step = 32; step <= size; step <<= 1) { total_size = step * repeat_num; if (step < 1024) { printf("______Perform %" PRIu32 " Bytes access______\n", step); } else { printf("______Perform %" PRIu32 " KBytes access______\n", step / 1024); } if (irq_disable) { flags = enter_critical_section(); } start_time = get_timestamp(); for (cnt = 0; cnt < repeat_num; cnt++) { memset(dest, value, step); } cost_time_system = get_time_elaps(start_time); start_time = get_timestamp(); for (cnt = 0; cnt < repeat_num; cnt++) { internal_memset(dest, value, step); } cost_time_internal = get_time_elaps(start_time); if (irq_disable) { leave_critical_section(flags); } print_rate("system memset():\t", total_size, cost_time_system); print_rate("internal memset():\t", total_size, cost_time_internal); } } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: ramspeed_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, ramspeed.irq_disable); memset_speed_test(ramspeed.dest, ramspeed.value, ramspeed.size, ramspeed.repeat_num, ramspeed.irq_disable); return EXIT_SUCCESS; }