diff --git a/examples/sw/benchmarks/README.md b/examples/sw/benchmarks/README.md new file mode 100644 index 00000000..2d9ecca3 --- /dev/null +++ b/examples/sw/benchmarks/README.md @@ -0,0 +1,91 @@ +# Benchmarks + +This directory contains benchmarks that can be run on ibex simple system. +Benchmarks may rely on code external to this directory (e.g. it may be found in +`vendor/`) see the specific benchmark information below for details on how to +build and run each benchmark and where benchmark code is located. + +## Building Simulation + +All of these benchmarks run on Simple System. A verilator simulation suitable +for running them can be built with: + +``` +fusesoc --cores-root=. run --target=sim --setup --build lowrisc:ibex:ibex_simple_system --RV32M=1 --RV32E=0 +``` + +See examples/simple_system/README.md for full details. + +## Coremark + +Coremark (https://www.eembc.org/coremark/ https://github.com/eembc/coremark) is +an industry standard benchmark with results available for a wide variety of +systems. + +The Coremark source is vendored into the Ibex repository at +`vendor/eembc_coremark`. Support structure and a makefile to build Coremark for +running on simple system is found in `examples/sw/benchmarks/coremark`. + +To build Coremark: + +``` +make -C ./examples/sw/benchmarks/coremark/ +``` + +To run Coremark (after building a suitable simulator binary, see above): + +``` +build/lowrisc_ibex_ibex_simple_system_0/sim-verilator/Vibex_simple_system --meminit=ram,examples/sw/benchmarks/coremark/coremark.elf +``` + +The simulator outputs the performance counter values observed for the benchmark +(the counts do not include anything from pre or post benchmark loops). + +Coremark should output (to `ibex_simple_system.log`) something like the +following: + +``` +2K performance run parameters for coremark. +CoreMark Size : 666 +Total ticks : 4244465 +Total time (secs): 8 +Iterations/Sec : 1 +Iterations : 10 +Compiler version : GCC +Compiler flags : +Memory location : +seedcrc : 0xe9f5 +[0]crclist : 0xe714 +[0]crcmatrix : 0x1fd7 +[0]crcstate : 0x8e3a +[0]crcfinal : 0xfcaf +Correct operation validated. See README.md for run and reporting rules. +``` + +A Coremark score is given as the number of iterations executed per second. The +Coremark binary is hard-coded to execute 10 iterations (see +`examples/sw/benchmarks/coremark/Makefile` if you wish to alter this). To obtain +a useful Coremark score from the simulation you need to choose a clock speed the +Ibex implementation you are interested in would run at, e.g. 100 MHz, taking +the above example: + +* 10 iterations take 4244465 clock cycles +* So at 100 MHz Ibex would execute (100 * 10^6) / (4244465 / 10) = 235.6 + Iterations in 1 second. +* Coremark (at 100 MHz) is 235.6 + +Coremark/MHz is often used instead of a raw Coremark score. The example above +gives a Coremark/MHz of 2.36 (235.6 / 100 rounded to 2 decimal places). + +To directly produce Coremark/MHz from the number of iterations (I) and total +ticks (T) use the follow formula: + +``` +Coremark/MHz = (10 ^ 6) * I / T +``` + +Note that `core_main.c` from Coremark has had a minor modification to prevent it +from reporting an error if it executes for less than 10 seconds. This violates +the run reporting rules (though does not effect benchmark execution). It is +trivial to restore `core_main.c` to the version supplied by EEMBC in the +Coremark repository if an official result is desired. diff --git a/examples/sw/benchmarks/coremark/Makefile b/examples/sw/benchmarks/coremark/Makefile new file mode 100644 index 00000000..9c7f644a --- /dev/null +++ b/examples/sw/benchmarks/coremark/Makefile @@ -0,0 +1,19 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 +# +# Build coremark benchmark for Ibex Simple System + +COREMARK_DIR = ../../../../vendor/eembc_coremark + +export PORT_DIR = $(CURDIR)/ibex +export ITERATIONS = 10 +export OPATH = $(CURDIR)/ + +# Export OPATH above doesn't seem to work so need to explicitly give it on the +# make command line +all: + $(MAKE) -C $(COREMARK_DIR) + +clean: + $(MAKE) -C $(COREMARK_DIR) clean diff --git a/examples/sw/benchmarks/coremark/ibex/core_portme.c b/examples/sw/benchmarks/coremark/ibex/core_portme.c new file mode 100644 index 00000000..7b66e04c --- /dev/null +++ b/examples/sw/benchmarks/coremark/ibex/core_portme.c @@ -0,0 +1,183 @@ +// Copyright lowRISC contributors. +// Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC) +// Original Author: Shay Gal-on +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "core_portme.h" + +#include "coremark.h" + +#include "simple_system_common.h" + +#if VALIDATION_RUN +volatile ee_s32 seed1_volatile = 0x3415; +volatile ee_s32 seed2_volatile = 0x3415; +volatile ee_s32 seed3_volatile = 0x66; +#endif +#if PERFORMANCE_RUN +volatile ee_s32 seed1_volatile = 0x0; +volatile ee_s32 seed2_volatile = 0x0; +volatile ee_s32 seed3_volatile = 0x66; +#endif +#if PROFILE_RUN +volatile ee_s32 seed1_volatile = 0x8; +volatile ee_s32 seed2_volatile = 0x8; +volatile ee_s32 seed3_volatile = 0x8; +#endif +volatile ee_s32 seed4_volatile = ITERATIONS; +volatile ee_s32 seed5_volatile = 0; +/* Porting : Timing functions + How to capture time and convert to seconds must be ported to whatever is + supported by the platform. e.g. Read value from on board RTC, read value from + cpu clock cycles performance counter etc. Sample implementation for standard + time.h and windows.h definitions included. +*/ +CORETIMETYPE barebones_clock() { + ee_u32 result; + + PCOUNT_READ(mcycle, result); + + return result; +} + +/* Define : TIMER_RES_DIVIDER + Divider to trade off timer resolution and total time that can be + measured. + + Use lower values to increase resolution, but make sure that overflow + does not occur. If there are issues with the return value overflowing, + increase this value. + */ +#define GETMYTIME(_t) (*_t = barebones_clock()) +#define MYTIMEDIFF(fin, ini) ((fin) - (ini)) +#define TIMER_RES_DIVIDER 1 +#define SAMPLE_TIME_IMPLEMENTATION 1 +#define CLOCKS_PER_SEC 500000 +#define EE_TICKS_PER_SEC (CLOCKS_PER_SEC / TIMER_RES_DIVIDER) + +void pcount_read(uint32_t pcount_out[]) { + PCOUNT_READ(minstret, pcount_out[0]); + PCOUNT_READ(mhpmcounter3, pcount_out[1]); + PCOUNT_READ(mhpmcounter4, pcount_out[2]); + PCOUNT_READ(mhpmcounter5, pcount_out[3]); + PCOUNT_READ(mhpmcounter6, pcount_out[4]); + PCOUNT_READ(mhpmcounter7, pcount_out[5]); + PCOUNT_READ(mhpmcounter8, pcount_out[6]); + PCOUNT_READ(mhpmcounter9, pcount_out[7]); + PCOUNT_READ(mhpmcounter10, pcount_out[8]); +} + +const char *pcount_names[] = {"Instructions Retired", + "LSU Busy", + "IFetch wait", + "Loads", + "Stores", + "Jumps", + "Branches", + "Taken Branches", + "Compressed Instructions"}; + +const uint32_t pcount_num = sizeof(pcount_names) / sizeof(char *); + +void dump_pcounts() { + uint32_t pcounts[pcount_num]; + + pcount_read(pcounts); + ee_printf( + "Performance Counters\n" + "--------------------\n"); + for (uint32_t i = 0; i < pcount_num; ++i) { + ee_printf("%s: %u\n", pcount_names[i], pcounts[i]); + } + ee_printf("\n"); +} + +/** Define Host specific (POSIX), or target specific global time variables. */ +static CORETIMETYPE start_time_val, stop_time_val; + +/* Function : start_time + This function will be called right before starting the timed portion of + the benchmark. + + Implementation may be capturing a system timer (as implemented in the + example code) or zeroing some system parameters - e.g. setting the cpu clocks + cycles to 0. +*/ +void start_time(void) { + pcount_enable(0); + pcount_reset(); + pcount_enable(1); + GETMYTIME(&start_time_val); +} + +/* Function : stop_time + This function will be called right after ending the timed portion of the + benchmark. + + Implementation may be capturing a system timer (as implemented in the + example code) or other system parameters - e.g. reading the current value of + cpu cycles counter. +*/ +void stop_time(void) { + GETMYTIME(&stop_time_val); + pcount_enable(0); +} + +/* Function : get_time + Return an abstract "ticks" number that signifies time on the system. + + Actual value returned may be cpu cycles, milliseconds or any other + value, as long as it can be converted to seconds by . This + methodology is taken to accomodate any hardware or simulated platform. The + sample implementation returns millisecs by default, and the resolution is + controlled by +*/ +CORE_TICKS get_time(void) { + CORE_TICKS elapsed = (CORE_TICKS)(MYTIMEDIFF(stop_time_val, start_time_val)); + return elapsed; +} +/* Function : time_in_secs + Convert the value returned by get_time to seconds. + + The type is used to accomodate systems with no support for + floating point. Default implementation implemented by the EE_TICKS_PER_SEC + macro above. +*/ +secs_ret time_in_secs(CORE_TICKS ticks) { + secs_ret retval = ((secs_ret)ticks) / (secs_ret)EE_TICKS_PER_SEC; + return retval; +} + +ee_u32 default_num_contexts = 1; + +/* Function : portable_init + Target specific initialization code + Test for some common mistakes. +*/ +void portable_init(core_portable *p, int *argc, char *argv[]) { + ee_printf("Ibex coremark platform init...\n"); + if (sizeof(ee_ptr_int) != sizeof(ee_u8 *)) { + ee_printf( + "ERROR! Please define ee_ptr_int to a type that holds a pointer!\n"); + } + if (sizeof(ee_u32) != 4) { + ee_printf("ERROR! Please define ee_u32 to a 32b unsigned type!\n"); + } + p->portable_id = 1; +} +/* Function : portable_fini + Target specific final code +*/ +void portable_fini(core_portable *p) { + dump_pcounts(); + + CORE_TICKS elapsed = get_time(); + float coremark_mhz; + + coremark_mhz = (1000000.0f * (float)ITERATIONS) / elapsed; + + ee_printf("Coremark / MHz: %f\n", coremark_mhz); + + p->portable_id = 0; +} diff --git a/examples/sw/benchmarks/coremark/ibex/core_portme.h b/examples/sw/benchmarks/coremark/ibex/core_portme.h new file mode 100644 index 00000000..894b8805 --- /dev/null +++ b/examples/sw/benchmarks/coremark/ibex/core_portme.h @@ -0,0 +1,201 @@ +// Copyright lowRISC contributors. +// Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC) +// Original Author: Shay Gal-on +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +/* Topic : Description + This file contains configuration constants required to execute on + different platforms +*/ + +#ifndef CORE_PORTME_H +#define CORE_PORTME_H + +#include + +extern unsigned int _stack_start; + +/************************/ +/* Data types and settings */ +/************************/ +/* Configuration : HAS_FLOAT + Define to 1 if the platform supports floating point. +*/ +#ifndef HAS_FLOAT +#define HAS_FLOAT 1 +#endif +/* Configuration : HAS_TIME_H + Define to 1 if platform has the time.h header file, + and implementation of functions thereof. +*/ +#ifndef HAS_TIME_H +#define HAS_TIME_H 0 +#endif +/* Configuration : USE_CLOCK + Define to 1 if platform has the time.h header file, + and implementation of functions thereof. +*/ +#ifndef USE_CLOCK +#define USE_CLOCK 0 +#endif +/* Configuration : HAS_STDIO + Define to 1 if the platform has stdio.h. +*/ +#ifndef HAS_STDIO +#define HAS_STDIO 0 +#endif +/* Configuration : HAS_PRINTF + Define to 1 if the platform has stdio.h and implements the printf + function. +*/ +#ifndef HAS_PRINTF +#define HAS_PRINTF 0 +#endif + +/* Definitions : COMPILER_VERSION, COMPILER_FLAGS, MEM_LOCATION + Initialize these strings per platform +*/ +#ifndef COMPILER_VERSION +#ifdef __GNUC__ +#define COMPILER_VERSION "GCC" +#else +#define COMPILER_VERSION "unknown" +#endif +#endif +#ifndef COMPILER_FLAGS +#define COMPILER_FLAGS "" /* "Please put compiler flags here (e.g. -o3)" */ +#endif +#ifndef MEM_LOCATION +#define MEM_LOCATION "STACK" +#endif + +/* Data Types : + To avoid compiler issues, define the data types that need to be used for + 8b, 16b and 32b in . + + *Imprtant* : + ee_ptr_int needs to be the data type used to hold pointers, otherwise + coremark may fail!!! +*/ +typedef signed short ee_s16; +typedef unsigned short ee_u16; +typedef signed int ee_s32; +typedef double ee_f32; +typedef unsigned char ee_u8; +typedef unsigned int ee_u32; +typedef ee_u32 ee_ptr_int; +typedef size_t ee_size_t; +#define NULL ((void *)0) +/* align_mem : + This macro is used to align an offset to point to a 32b value. It is + used in the Matrix algorithm to initialize the input memory blocks. +*/ +#define align_mem(x) (void *)(4 + (((ee_ptr_int)(x)-1) & ~3)) + +/* Configuration : CORE_TICKS + Define type of return from the timing functions. + */ +#define CORETIMETYPE ee_u32 +typedef ee_u32 CORE_TICKS; + +/* Configuration : SEED_METHOD + Defines method to get seed values that cannot be computed at compile + time. + + Valid values : + SEED_ARG - from command line. + SEED_FUNC - from a system function. + SEED_VOLATILE - from volatile variables. +*/ +#ifndef SEED_METHOD +#define SEED_METHOD SEED_VOLATILE +#endif + +/* Configuration : MEM_METHOD + Defines method to get a block of memry. + + Valid values : + MEM_MALLOC - for platforms that implement malloc and have malloc.h. + MEM_STATIC - to use a static memory array. + MEM_STACK - to allocate the data block on the stack (NYI). +*/ +#ifndef MEM_METHOD +#define MEM_METHOD MEM_STACK +#endif + +/* Configuration : MULTITHREAD + Define for parallel execution + + Valid values : + 1 - only one context (default). + N>1 - will execute N copies in parallel. + + Note : + If this flag is defined to more then 1, an implementation for launching + parallel contexts must be defined. + + Two sample implementations are provided. Use or + to enable them. + + It is valid to have a different implementation of + and in , to fit a particular architecture. +*/ +#ifndef MULTITHREAD +#define MULTITHREAD 1 +#define USE_PTHREAD 0 +#define USE_FORK 0 +#define USE_SOCKET 0 +#endif + +/* Configuration : MAIN_HAS_NOARGC + Needed if platform does not support getting arguments to main. + + Valid values : + 0 - argc/argv to main is supported + 1 - argc/argv to main is not supported + + Note : + This flag only matters if MULTITHREAD has been defined to a value + greater then 1. +*/ +#ifndef MAIN_HAS_NOARGC +#define MAIN_HAS_NOARGC 0 +#endif + +/* Configuration : MAIN_HAS_NORETURN + Needed if platform does not support returning a value from main. + + Valid values : + 0 - main returns an int, and return value will be 0. + 1 - platform does not support returning a value from main +*/ +#ifndef MAIN_HAS_NORETURN +#define MAIN_HAS_NORETURN 0 +#endif + +/* Variable : default_num_contexts + Not used for this simple port, must cintain the value 1. +*/ +extern ee_u32 default_num_contexts; + +typedef struct CORE_PORTABLE_S { ee_u8 portable_id; } core_portable; + +/* target specific init/fini */ +void portable_init(core_portable *p, int *argc, char *argv[]); +void portable_fini(core_portable *p); + +#if !defined(PROFILE_RUN) && !defined(PERFORMANCE_RUN) && \ + !defined(VALIDATION_RUN) +#if (TOTAL_DATA_SIZE == 1200) +#define PROFILE_RUN 1 +#elif (TOTAL_DATA_SIZE == 2000) +#define PERFORMANCE_RUN 1 +#else +#define VALIDATION_RUN 1 +#endif +#endif + +int ee_printf(const char *fmt, ...); + +#endif /* CORE_PORTME_H */ diff --git a/examples/sw/benchmarks/coremark/ibex/core_portme.mak b/examples/sw/benchmarks/coremark/ibex/core_portme.mak new file mode 100755 index 00000000..97dc3160 --- /dev/null +++ b/examples/sw/benchmarks/coremark/ibex/core_portme.mak @@ -0,0 +1,90 @@ +# Copyright lowRISC contributors. +# Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC) +# Original Author: Shay Gal-on +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +OUTFILES = $(OPATH)coremark.dis $(OPATH)coremark.map + +NAME = coremark +PORT_CLEAN := $(OUTFILES) +SIMPLE_SYSTEM_COMMON = ../../examples/sw/simple_system/common +EXT_SRCS = $(wildcard $(SIMPLE_SYSTEM_COMMON)/*.c) +CRT0 = $(SIMPLE_SYSTEM_COMMON)/crt0.S +LINKER_SCRIPT = $(SIMPLE_SYSTEM_COMMON)/link.ld + +# Flag : OUTFLAG +# Use this flag to define how to to get an executable (e.g -o) +OUTFLAG = -o +# Flag : CC +# Use this flag to define compiler to use +CC = riscv32-unknown-elf-gcc +# Flag : LD +# Use this flag to define compiler to use +LD = riscv32-unknown-elf-ld +# Flag : AS +# Use this flag to define compiler to use +AS = riscv32-unknown-elf-as +# Flag : CFLAGS +# Use this flag to define compiler options. Note, you can add compiler options from the command line using XCFLAGS="other flags" +PORT_CFLAGS = -g -march=rv32imc -mabi=ilp32 -static -mcmodel=medlow -mtune=sifive-3-series \ + -O3 -falign-functions=16 -funroll-all-loops \ + -finline-functions -falign-jumps=4 \ + -nostdlib -nostartfiles -ffreestanding -mstrict-align \ + -DTOTAL_DATA_SIZE=2000 -DMAIN_HAS_NOARGC=1 \ + -DPERFORMANCE_RUN=1 + +FLAGS_STR = "$(PORT_CFLAGS) $(XCFLAGS) $(XLFLAGS) $(LFLAGS_END)" +CFLAGS += $(PORT_CFLAGS) $(XCFLAGS) -I$(SIMPLE_SYSTEM_COMMON) -I$(PORT_DIR) -I. +#Flag : LFLAGS_END +# Define any libraries needed for linking or other flags that should come at the end of the link line (e.g. linker scripts). +# Note : On certain platforms, the default clock_gettime implementation is supported but requires linking of librt. +#SEPARATE_COMPILE=1 +# Flag : SEPARATE_COMPILE +# You must also define below how to create an object file, and how to link. +OBJOUT = -o +LFLAGS = +ASFLAGS = +OFLAG = -o +COUT = -c + +LFLAGS_END = -T $(LINKER_SCRIPT) -Xlinker -Map=$(OPATH)coremark.map -lm -lgcc +# Flag : PORT_SRCS +# Port specific source files can be added here +# You may also need cvt.c if the fcvt functions are not provided as intrinsics by your compiler! +PORT_SRCS = $(PORT_DIR)/core_portme.c $(PORT_DIR)/ee_printf.c ./barebones/cvt.c $(CRT0) $(EXT_SRCS) +vpath %.c $(PORT_DIR) +vpath %.s $(PORT_DIR) + +# Flag : LOAD +# For a simple port, we assume self hosted compile and run, no load needed. + +# Flag : RUN +# For a simple port, we assume self hosted compile and run, simple invocation of the executable + +LOAD = echo "Please set LOAD to the process of loading the executable to the flash" +RUN = echo "Please set LOAD to the process of running the executable (e.g. via jtag, or board reset)" + +OEXT = .o +EXE = .elf + +$(OPATH)$(PORT_DIR)/%$(OEXT) : %.c + $(CC) $(CFLAGS) $(XCFLAGS) $(COUT) $< $(OBJOUT) $@ + +$(OPATH)%$(OEXT) : %.c + $(CC) $(CFLAGS) $(XCFLAGS) $(COUT) $< $(OBJOUT) $@ + +$(OPATH)$(PORT_DIR)/%$(OEXT) : %.s + $(AS) $(ASFLAGS) $< $(OBJOUT) $@ + +# Target : port_pre% and port_post% +# For the purpose of this simple port, no pre or post steps needed. + +.PHONY : port_clean port_prebuild port_postbuild port_prerun port_postrun port_preload port_postload + +port_postbuild: + riscv32-unknown-elf-objdump -SD $(OPATH)coremark.elf > $(OPATH)coremark.dis + +# FLAG : OPATH +# Path to the output folder. Default - current folder. +MKDIR = mkdir -p diff --git a/examples/sw/benchmarks/coremark/ibex/ee_printf.c b/examples/sw/benchmarks/coremark/ibex/ee_printf.c new file mode 100644 index 00000000..913baefb --- /dev/null +++ b/examples/sw/benchmarks/coremark/ibex/ee_printf.c @@ -0,0 +1,579 @@ +// Copyright lowRISC contributors. +// Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC) +// Original Author: Shay Gal-on +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "simple_system_common.h" + +#define ZEROPAD (1 << 0) /* Pad with zero */ +#define SIGN (1 << 1) /* Unsigned/signed long */ +#define PLUS (1 << 2) /* Show plus */ +#define SPACE (1 << 3) /* Spacer */ +#define LEFT (1 << 4) /* Left justified */ +#define HEX_PREP (1 << 5) /* 0x */ +#define UPPERCASE (1 << 6) /* 'ABCDEF' */ + +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +static char *digits = "0123456789abcdefghijklmnopqrstuvwxyz"; +static char *upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static ee_size_t strnlen(const char *s, ee_size_t count); + +static ee_size_t strnlen(const char *s, ee_size_t count) { + const char *sc; + for (sc = s; *sc != '\0' && count--; ++sc) + ; + return sc - s; +} + +static int skip_atoi(const char **s) { + int i = 0; + while (is_digit(**s)) + i = i * 10 + *((*s)++) - '0'; + return i; +} + +static char *number(char *str, long num, int base, int size, int precision, + int type) { + char c, sign, tmp[66]; + char *dig = digits; + int i; + + if (type & UPPERCASE) + dig = upper_digits; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + + if (type & HEX_PREP) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + + i = 0; + + if (num == 0) + tmp[i++] = '0'; + else { + while (num != 0) { + tmp[i++] = dig[((unsigned long)num) % (unsigned)base]; + num = ((unsigned long)num) / (unsigned)base; + } + } + + if (i > precision) + precision = i; + size -= precision; + if (!(type & (ZEROPAD | LEFT))) + while (size-- > 0) + *str++ = ' '; + if (sign) + *str++ = sign; + + if (type & HEX_PREP) { + if (base == 8) + *str++ = '0'; + else if (base == 16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + + return str; +} + +static char *eaddr(char *str, unsigned char *addr, int size, int precision, + int type) { + char tmp[24]; + char *dig = digits; + int i, len; + + if (type & UPPERCASE) + dig = upper_digits; + len = 0; + for (i = 0; i < 6; i++) { + if (i != 0) + tmp[len++] = ':'; + tmp[len++] = dig[addr[i] >> 4]; + tmp[len++] = dig[addr[i] & 0x0F]; + } + + if (!(type & LEFT)) + while (len < size--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = tmp[i]; + while (len < size--) + *str++ = ' '; + + return str; +} + +static char *iaddr(char *str, unsigned char *addr, int size, int precision, + int type) { + char tmp[24]; + int i, n, len; + + len = 0; + for (i = 0; i < 4; i++) { + if (i != 0) + tmp[len++] = '.'; + n = addr[i]; + + if (n == 0) + tmp[len++] = digits[0]; + else { + if (n >= 100) { + tmp[len++] = digits[n / 100]; + n = n % 100; + tmp[len++] = digits[n / 10]; + n = n % 10; + } else if (n >= 10) { + tmp[len++] = digits[n / 10]; + n = n % 10; + } + + tmp[len++] = digits[n]; + } + } + + if (!(type & LEFT)) + while (len < size--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = tmp[i]; + while (len < size--) + *str++ = ' '; + + return str; +} + +#if HAS_FLOAT + +char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); +char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); +static void ee_bufcpy(char *d, char *s, int count); + +void ee_bufcpy(char *pd, char *ps, int count) { + char *pe = ps + count; + while (ps != pe) + *pd++ = *ps++; +} + +static void parse_float(double value, char *buffer, char fmt, int precision) { + int decpt, sign, exp, pos; + char *digits = NULL; + char cvtbuf[80]; + int capexp = 0; + int magnitude; + + if (fmt == 'G' || fmt == 'E') { + capexp = 1; + fmt += 'a' - 'A'; + } + + if (fmt == 'g') { + digits = ecvtbuf(value, precision, &decpt, &sign, cvtbuf); + magnitude = decpt - 1; + if (magnitude < -4 || magnitude > precision - 1) { + fmt = 'e'; + precision -= 1; + } else { + fmt = 'f'; + precision -= decpt; + } + } + + if (fmt == 'e') { + digits = ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf); + + if (sign) + *buffer++ = '-'; + *buffer++ = *digits; + if (precision > 0) + *buffer++ = '.'; + ee_bufcpy(buffer, digits + 1, precision); + buffer += precision; + *buffer++ = capexp ? 'E' : 'e'; + + if (decpt == 0) { + if (value == 0.0) + exp = 0; + else + exp = -1; + } else + exp = decpt - 1; + + if (exp < 0) { + *buffer++ = '-'; + exp = -exp; + } else + *buffer++ = '+'; + + buffer[2] = (exp % 10) + '0'; + exp = exp / 10; + buffer[1] = (exp % 10) + '0'; + exp = exp / 10; + buffer[0] = (exp % 10) + '0'; + buffer += 3; + } else if (fmt == 'f') { + digits = fcvtbuf(value, precision, &decpt, &sign, cvtbuf); + if (sign) + *buffer++ = '-'; + if (*digits) { + if (decpt <= 0) { + *buffer++ = '0'; + *buffer++ = '.'; + for (pos = 0; pos < -decpt; pos++) + *buffer++ = '0'; + while (*digits) + *buffer++ = *digits++; + } else { + pos = 0; + while (*digits) { + if (pos++ == decpt) + *buffer++ = '.'; + *buffer++ = *digits++; + } + } + } else { + *buffer++ = '0'; + if (precision > 0) { + *buffer++ = '.'; + for (pos = 0; pos < precision; pos++) + *buffer++ = '0'; + } + } + } + + *buffer = '\0'; +} + +static void decimal_point(char *buffer) { + while (*buffer) { + if (*buffer == '.') + return; + if (*buffer == 'e' || *buffer == 'E') + break; + buffer++; + } + + if (*buffer) { + int n = strnlen(buffer, 256); + while (n > 0) { + buffer[n + 1] = buffer[n]; + n--; + } + + *buffer = '.'; + } else { + *buffer++ = '.'; + *buffer = '\0'; + } +} + +static void cropzeros(char *buffer) { + char *stop; + + while (*buffer && *buffer != '.') + buffer++; + if (*buffer++) { + while (*buffer && *buffer != 'e' && *buffer != 'E') + buffer++; + stop = buffer--; + while (*buffer == '0') + buffer--; + if (*buffer == '.') + buffer--; + while (buffer != stop) + *++buffer = 0; + } +} + +static char *flt(char *str, double num, int size, int precision, char fmt, + int flags) { + char tmp[80]; + char c, sign; + int n, i; + + // Left align means no zero padding + if (flags & LEFT) + flags &= ~ZEROPAD; + + // Determine padding and sign char + c = (flags & ZEROPAD) ? '0' : ' '; + sign = 0; + if (flags & SIGN) { + if (num < 0.0) { + sign = '-'; + num = -num; + size--; + } else if (flags & PLUS) { + sign = '+'; + size--; + } else if (flags & SPACE) { + sign = ' '; + size--; + } + } + + // Compute the precision value + if (precision < 0) + precision = 6; // Default precision: 6 + + // Convert floating point number to text + parse_float(num, tmp, fmt, precision); + + if ((flags & HEX_PREP) && precision == 0) + decimal_point(tmp); + if (fmt == 'g' && !(flags & HEX_PREP)) + cropzeros(tmp); + + n = strnlen(tmp, 256); + + // Output number with alignment and padding + size -= n; + if (!(flags & (ZEROPAD | LEFT))) + while (size-- > 0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (!(flags & LEFT)) + while (size-- > 0) + *str++ = c; + for (i = 0; i < n; i++) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + + return str; +} + +#endif + +static int ee_vsprintf(char *buf, const char *fmt, va_list args) { + int len; + unsigned long num; + int i, base; + char *str; + char *s; + + int flags; // Flags to number() + + int field_width; // Width of output field + int precision; // Min. # of digits for integers; max number of chars for from + // string + int qualifier; // 'h', 'l', or 'L' for integer fields + + for (str = buf; *fmt; fmt++) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + // Process flags + flags = 0; + repeat: + fmt++; // This also skips first '%' + switch (*fmt) { + case '-': + flags |= LEFT; + goto repeat; + case '+': + flags |= PLUS; + goto repeat; + case ' ': + flags |= SPACE; + goto repeat; + case '#': + flags |= HEX_PREP; + goto repeat; + case '0': + flags |= ZEROPAD; + goto repeat; + } + + // Get field width + field_width = -1; + if (is_digit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + fmt++; + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + // Get the precision + precision = -1; + if (*fmt == '.') { + ++fmt; + if (is_digit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + // Get the conversion qualifier + qualifier = -1; + if (*fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + fmt++; + } + + // Default base + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char)va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + len = strnlen(s, precision); + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2 * sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, (unsigned long)va_arg(args, void *), 16, field_width, + precision, flags); + continue; + + case 'A': + flags |= UPPERCASE; + + case 'a': + if (qualifier == 'l') + str = eaddr(str, va_arg(args, unsigned char *), field_width, + precision, flags); + else + str = iaddr(str, va_arg(args, unsigned char *), field_width, + precision, flags); + continue; + + // Integer number formats - set up the flags and "break" + case 'o': + base = 8; + break; + + case 'X': + flags |= UPPERCASE; + + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + + case 'u': + break; + +#if HAS_FLOAT + + case 'f': + str = flt(str, va_arg(args, double), field_width, precision, *fmt, + flags | SIGN); + continue; + +#endif + + default: + if (*fmt != '%') + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + + str = number(str, num, base, field_width, precision, flags); + } + + *str = '\0'; + return str - buf; +} + +int ee_printf(const char *fmt, ...) { + char buf[256], *p; + va_list args; + int n = 0; + + va_start(args, fmt); + ee_vsprintf(buf, fmt, args); + va_end(args); + p = buf; + while (*p) { + putchar(*p); + n++; + p++; + } + + return n; +} diff --git a/vendor/patches/eembc_coremark/0001-no-minimum-run-time.patch b/vendor/patches/eembc_coremark/0001-no-minimum-run-time.patch new file mode 100644 index 00000000..71927aa9 --- /dev/null +++ b/vendor/patches/eembc_coremark/0001-no-minimum-run-time.patch @@ -0,0 +1,25 @@ +diff --git a/core_main.c b/core_main.c +index 6161974..cca596c 100644 +--- a/core_main.c ++++ b/core_main.c +@@ -295,10 +295,16 @@ MAIN_RETURN_TYPE main(int argc, char *argv[]) { + if (time_in_secs(total_time) > 0) + ee_printf("Iterations/Sec : %d\n",default_num_contexts*results[0].iterations/time_in_secs(total_time)); + #endif +- if (time_in_secs(total_time) < 10) { +- ee_printf("ERROR! Must execute for at least 10 secs for a valid result!\n"); +- total_errors++; +- } ++ // Remove error report if execution time low. ++ // On Ibex a few loops suffices to get an accurate result. With this check in ++ // the verilator simulation of coremark will report failure (unless left running ++ // for a significant number of iterations greatly increasing simulation time). ++ // The other error checking is useful to determine if the benchmark has been ++ // broken in some way which is masked if this check is left in. ++ //if (time_in_secs(total_time) < 10) { ++ // ee_printf("ERROR! Must execute for at least 10 secs for a valid result!\n"); ++ // total_errors++; ++ //} + + ee_printf("Iterations : %lu\n", (long unsigned) default_num_contexts*results[0].iterations); + ee_printf("Compiler version : %s\n",COMPILER_VERSION);