/* * Copyright (c) 2019 Nuclei Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed 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 * * 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. */ #ifndef __NMSIS_BENCH__ #define __NMSIS_BENCH__ /*! * @file nmsis_bench.h * @brief benchmark and helper related API for Nuclei N/NX Core */ #ifdef __cplusplus extern "C" { #endif #include "core_feature_base.h" #include /** * \defgroup NMSIS_Core_Bench_Helpers NMSIS Bench and Test Related Helper Functions * \ingroup NMSIS_Core * \brief Functions that used to do benchmark and test suite. * \details * * NMSIS benchmark and test related helper functions are provided to help do benchmark * and test case pass/fail assertion. * * If you want to do calculate cpu cycle cost of a process, you can use BENCH_xxx macros * defined in this. * * In a single c source code file, you should include `nmsis_bench.h`, and then you should place `BENCH_DECLARE_VAR();` * before call other BENCH_xxx macros. If you want to start to do benchmark, you should only call `BENCH_INIT();` * once in your source code, and then place `BENCH_START(proc_name);` and `BENCH_END(proc_name)` before * and after the process you want to measure. You can refer to `/application/baremetal/demo_dsp` * for how to use it. * * If you want to disable the benchmark calculation, you can place `#define DISABLE_NMSIS_BENCH` * before include `nmsis_bench.h` * * If in your c test source code, you can add `NMSIS_TEST_PASS();` and `NMSIS_TEST_FAIL();` to mark c test * is pass or fail. * * @{ */ /** * \brief Prepare benchmark environment * \details * Prepare benchmark required environment, such as turn on necessary units * like vpu, cycle, instret counters, hpm counters */ __STATIC_FORCEINLINE void __prepare_bench_env(void) { #ifdef __riscv_vector __RV_CSR_SET(CSR_MSTATUS, MSTATUS_VS); #endif __enable_all_counter(); } #ifndef READ_CYCLE /** Read run cycle of cpu */ #define READ_CYCLE __get_rv_cycle #endif #ifndef DISABLE_NMSIS_BENCH /** Declare benchmark required variables, need to be placed above all BENCH_xxx macros in each c source code if BENCH_xxx used */ #define BENCH_DECLARE_VAR() static volatile uint32_t _bc_sttcyc, _bc_endcyc, _bc_usecyc, _bc_sumcyc, _bc_lpcnt, _bc_ercd; /** Initialize benchmark environment, need to called in before other BENCH_xxx macros are called */ #define BENCH_INIT() printf("Benchmark initialized\n"); \ __prepare_bench_env(); \ _bc_ercd = 0; _bc_sumcyc = 0; /** Reset benchmark sum cycle and use cycle for proc */ #define BENCH_RESET(proc) _bc_sumcyc = 0; _bc_usecyc = 0; _bc_lpcnt = 0; _bc_ercd = 0; /** Start to do benchmark for proc, and record start cycle, and reset error code */ #define BENCH_START(proc) _bc_ercd = 0; \ _bc_sttcyc = READ_CYCLE(); /** Sample a benchmark for proc, and record this start -> sample cost cycle, and accumulate it to sum cycle */ #define BENCH_SAMPLE(proc) _bc_endcyc = READ_CYCLE(); \ _bc_usecyc = _bc_endcyc - _bc_sttcyc; \ _bc_sumcyc += _bc_usecyc; _bc_lpcnt += 1; /** Mark end of benchmark for proc, and calc used cycle, and print it */ #define BENCH_END(proc) BENCH_SAMPLE(proc); \ printf("CSV, %s, %lu\n", #proc, (unsigned long)_bc_usecyc); /** Mark stop of benchmark, start -> sample -> sample -> stop, and print the sum cycle of a proc */ #define BENCH_STOP(proc) printf("CSV, %s, %lu\n", #proc, (unsigned long)_bc_sumcyc); /** Show statistics of benchmark, format: STAT, proc, loopcnt, sumcyc */ #define BENCH_STAT(proc) printf("STAT, %s, %lu, %lu\n", #proc, (unsigned long)_bc_lpcnt, (unsigned long)_bc_sumcyc); /** Get benchmark use cycle */ #define BENCH_GET_USECYC() (_bc_usecyc) /** Get benchmark sum cycle */ #define BENCH_GET_SUMCYC() (_bc_sumcyc) /** Get benchmark loop count */ #define BENCH_GET_LPCNT() (_bc_lpcnt) /** Mark benchmark for proc is errored */ #define BENCH_ERROR(proc) _bc_ercd = 1; /** Show the status of the benchmark */ #define BENCH_STATUS(proc) if (_bc_ercd) { \ printf("ERROR, %s\n", #proc); \ } else { \ printf("SUCCESS, %s\n", #proc); \ } #else #define BENCH_DECLARE_VAR() static volatile uint32_t _bc_ercd, _bc_lpcnt; #define BENCH_INIT() _bc_ercd = 0; __prepare_bench_env(); #define BENCH_RESET(proc) #define BENCH_START(proc) _bc_ercd = 0; #define BENCH_SAMPLE(proc) _bc_lpcnt += 1; #define BENCH_END(proc) #define BENCH_STOP(proc) #define BENCH_STAT(proc) #define BENCH_GET_USECYC() (0) #define BENCH_GET_SUMCYC() (0) #define BENCH_GET_LPCNT() (_bc_lpcnt) #define BENCH_ERROR(proc) _bc_ercd = 1; #define BENCH_STATUS(proc) if (_bc_ercd) { \ printf("ERROR, %s\n", #proc); \ } else { \ printf("SUCCESS, %s\n", #proc); \ } #endif // NMSIS Helpers #ifndef DISABLE_NMSIS_HELPER /** Mark test or application passed */ #define NMSIS_TEST_PASS() printf("\nNMSIS_TEST_PASS\n"); /** Mark test or application failed */ #define NMSIS_TEST_FAIL() printf("\nNMSIS_TEST_FAIL\n"); #else #define NMSIS_TEST_PASS() #define NMSIS_TEST_FAIL() #endif /** @} */ /* End of Doxygen Group NMSIS_Core_Bench_Helpers */ #ifdef __cplusplus } #endif #endif /* __NMSIS_BENCH__ */