diff --git a/config/riscv32/boards/spike/board.cfg b/config/riscv32/boards/spike/board.cfg new file mode 100644 index 00000000..791115ee --- /dev/null +++ b/config/riscv32/boards/spike/board.cfg @@ -0,0 +1,62 @@ +# Board configuration for riscv-isa-sim (spike +# +# Copyright (C) 2022 Embecosm Limited and University of Bristol +# +# Contributor TODO +# +# SPDX-License-Identifier: GPL-3.0-or-later + +# This is a python setting of parameters for the board. The following +# parameters may be set (other keys are silently ignored). Defaults are shown +# in brackets +# cc = 'riscv32-unknown-elf-gcc' +# - ld (same value as for cc) +# cflags = (['-c', '-Os', '-ffunction-sections', '-nostdlib', '-march=rv32imac', '-mabi=ilp32']) +# ldflags = (['-Wl,-gc-sections', '-nostdlib', '-march=rv32imac', '-mabi=ilp32', '-T../../../config/riscv32/boards/rv32wallyverilog/link.ld']) +# cflags = (['-c', '-Os', '-ffunction-sections', '-nostartfiles', '-march=rv32imac', '-mabi=ilp32']) +# ldflags = (['-Wl,-gc-sections', '-nostartfiles', '-march=rv32imac', '-mabi=ilp32', '-T../../../config/riscv32/boards/rv32wallyverilog/link.ld']) +cflags = (['-c', '-march=rv32imafdc', '-mabi=ilp32d', '-DSPIKE']) +ldflags = (['-march=rv32imafdc', '-mabi=ilp32d', '-T../../../config/riscv32/boards/spike/link.ld']) +# - cc_define_pattern ('-D{0}') +# - cc_incdir_pattern ('-I{0}') +# - cc_input_pattern ('{0}') +# - cc_output_pattern ('-o {0}') +# - ld_input_pattern ('{0}') +# - ld_output_pattern ('-o {0}') +user_libs = (['-lc']) +# dummy_libs = (['libm']) +# dummy_libs = (['libgcc', 'libm', 'libc']) +# - cpu_mhz (1) +# - warmup_heat (1) + +# The "flags" and "libs" parameters (cflags, ldflags, user_libs, dummy_libs) +# should be lists of arguments to be passed to the compile or link line as +# appropriate. Patterns are Python format patterns used to create arguments. +# Thus for GCC or Clang/LLVM defined constants can be passed using the prefix +# '-D', and the pattern '-D{0}' would be appropriate (which happens to be the +# default). + +# "user_libs" may be absolute file names or arguments to the linker. In the +# latter case corresponding arguments in ldflags may be needed. For example +# with GCC or Clang/LLVM is "-l" flags are used in "user_libs", the "-L" flags +# may be needed in "ldflags". + +# Dummy libs have their source in the "support" subdirectory. Thus if 'crt0' +# is specified, there should be a source file 'dummy-crt0.c' in the support +# directory. + +# There is no need to set an unused parameter, and this file may be empty to +# set no flags. + +# Parameter values which are duplicated in architecture, board, chip or +# command line are used in the following order of priority +# - default value +# - architecture specific value +# - chip specific value +# - board specific value +# - command line value + +# For flags, this priority is applied to individual flags, not the complete +# list of flags. + +cpu_mhz = 1 diff --git a/config/riscv32/boards/spike/boardsupport.c b/config/riscv32/boards/spike/boardsupport.c new file mode 100644 index 00000000..945cc466 --- /dev/null +++ b/config/riscv32/boards/spike/boardsupport.c @@ -0,0 +1,38 @@ +/* Copyright HighTec EDV-Systeme GmbH 2023 + + This file is part of Embench. + + SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0 */ + +#include + +unsigned long long start; +extern void _exit(int i); + +#define CORETIMETYPE unsigned long long +#define read_csr(reg) ({ unsigned long __tmp; \ + asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ + __tmp; }) + + +void __attribute__ ((noinline)) __attribute__ ((externally_visible)) +start_trigger () +{ + unsigned long hi = read_csr(mcycleh); + unsigned long lo = read_csr(mcycle); + start = (unsigned long long)(((CORETIMETYPE)hi) << 32) | lo; +} + +void __attribute__ ((noinline)) __attribute__ ((externally_visible)) +stop_trigger () +{ + unsigned long hi = read_csr(mcycleh); + unsigned long lo = read_csr(mcycle); + unsigned long long end = (unsigned long long)(((CORETIMETYPE)hi) << 32) | lo; + printf("Spike mcycle timer delta: %llu\n", end - start); +} + +void __attribute__ ((noinline)) +initialise_board () +{ +} diff --git a/config/riscv32/boards/spike/crt0.S b/config/riscv32/boards/spike/crt0.S new file mode 100644 index 00000000..b73d9942 --- /dev/null +++ b/config/riscv32/boards/spike/crt0.S @@ -0,0 +1,67 @@ +/* Startup code for spike RISC-V ISA simulator with FPU enabled + + Copyright (C) 2017 SiFive Inc. All rights reserved. + + This file is part of Embench. + + SPDX-License-Identifier: GPL-3.0-or-later OR BSD-2-Clause */ + +; #include "newlib.h" + +#define MSTATUS_FS 0x00006000 +#define MSTATUS_XS 0x00018000 + +#========================================================================= +# crt0.S : Entry point for RISC-V user programs +#========================================================================= + + .text + .global _start + .type _start, @function + .section .text.startup +_start: + # Initialize global pointer +.option push +.option norelax +1:auipc gp, %pcrel_hi(__global_pointer$) + addi gp, gp, %pcrel_lo(1b) +.option pop + + # enable FPU and accelerator if present + li t0, MSTATUS_FS | MSTATUS_XS + csrs mstatus, t0 + + # Clear the bss segment + la sp, __ram_end__ + la a0, __bss_start__ + la a2, _end + sub a2, a2, a0 + li a1, 0 + call memset +#ifdef HAS_ATEXIT +#ifdef _LITE_EXIT + # Make reference to atexit weak to avoid unconditionally pulling in + # support code. Refer to comments in __atexit.c for more details. + .weak atexit + la a0, atexit + beqz a0, .Lweak_atexit + .weak __libc_fini_array +#endif + + la a0, __libc_fini_array # Register global termination functions + call atexit # to be called upon exit +#ifdef _LITE_EXIT +.Lweak_atexit: +#endif +#endif + call __libc_init_array # Run global initialization functions + + #lw a0, 0(sp) # a0 = argc + #addi a1, sp, __SIZEOF_POINTER__ # a1 = argv + #li a2, 0 # a2 = envp = NULL + li a0, 0 + li a1, 0 + li a2, 0 + call main + tail exit + .size _start, .-_start diff --git a/config/riscv32/boards/spike/link.ld b/config/riscv32/boards/spike/link.ld new file mode 100644 index 00000000..56956a37 --- /dev/null +++ b/config/riscv32/boards/spike/link.ld @@ -0,0 +1,93 @@ +/* Linker script for spike RISC-V ISA simulator + + Copyright (C) 2014-2020 Free Software Foundation, Inc. + + This file is part of Embench. + + SPDX-License-Identifier: GPL-3.0-or-later OR FSFAP */ + +OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv") +OUTPUT_ARCH(riscv) + +MEMORY +{ + /* qemu-system-risc32 virt machine */ + RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 1M +} + +ENTRY(_start) /* this will cause an error if the symbol _start is not present */ + +SECTIONS +{ + . = ORIGIN(RAM); + + PROVIDE(__ram_origin__ = ORIGIN(RAM)); + PROVIDE(__ram_end__ = ORIGIN(RAM) + LENGTH(RAM)); + + .text.startup : { *(.text.startup) } + . = ALIGN(0x1000); + .tohost : { *(.htif) } + . = ALIGN(0x1000); + .text : { *(.text*) } + . = ALIGN(0x1000); + + /*. = ALIGN(0x10);*/ /* putting this here does not cause the followng section to move */ + + .rodata : /*ALIGN(0x10):*/ /* this will the align section but others can overlap */ + { + . = ALIGN(0x10); /* aligning here will align the section & update the loc counter */ + __rodata_start = .; + *(.rodata.*) + *(.srodata.*) + __rodata_end = .; + } + + .data : + { + . = ALIGN(0x1000); + __data_start = .; + *(.data.*) + *(.sdata*) /* small data objects */ + __data_end = .; + } + + __global_pointer$ = (__data_start + 0x800); /* stick this somewhere potentially useful */ + + +/**/ + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) + KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) + PROVIDE_HIDDEN (__fini_array_end = .); + } +/**/ + + .bss : + { + . = ALIGN(0x100); + __bss_start = .; + __bss_start__ = .; + *(.sbss*) /* small data objects */ + *(.bss*) + __bss_end = .; + __bss_end__ = .; + } + + _end = .; +} + diff --git a/config/riscv32/boards/spike/ns16550.h b/config/riscv32/boards/spike/ns16550.h new file mode 100644 index 00000000..8c42b398 --- /dev/null +++ b/config/riscv32/boards/spike/ns16550.h @@ -0,0 +1,41 @@ +/* RISC-V rv32 tutorial examples + + Copyright (C) 2021 John Winans + + This file is part of Embench. + + SPDX-License-Identifier: GPL-3.0-or-later */ + + +#ifndef ns16550_H +#define ns16550_H + +#include + +// inferred from the qemu-system-riscv32 dtb values +#define NS16550_THR (*((volatile uint8_t *)0x10000000)) +#define NS16550_RBR (*((volatile uint8_t *)0x10000000)) +#define NS16550_IER (*((volatile uint8_t *)0x10000001)) +#define NS16550_IIR (*((volatile uint8_t *)0x10000002)) +#define NS16550_FCR (*((volatile uint8_t *)0x10000002)) +#define NS16550_LCR (*((volatile uint8_t *)0x10000003)) +#define NS16550_MCR (*((volatile uint8_t *)0x10000004)) +#define NS16550_LSR (*((volatile uint8_t *)0x10000005)) +#define NS16550_MSR (*((volatile uint8_t *)0x10000006)) +#define NS16550_SCR (*((volatile uint8_t *)0x10000007)) + +#define NS16550_LSR_THRE (1<<5) + +/** +* Wait for the TX FIFO to have room for a byte and send it. +***************************************************************************/ +inline __attribute__((always_inline)) void ns16550_tx(uint8_t ch) +{ + // be careful about order of operations here... + while((NS16550_LSR & NS16550_LSR_THRE) == 0) + ; + NS16550_THR = ch; +} + +#endif + diff --git a/config/riscv32/boards/spike/stub.c b/config/riscv32/boards/spike/stub.c new file mode 100644 index 00000000..11613c93 --- /dev/null +++ b/config/riscv32/boards/spike/stub.c @@ -0,0 +1,169 @@ +/* RISC-V rv32 tutorial examples + + Copyright (C) 2021 John Winans + + This file is part of Embench. + + SPDX-License-Identifier: GPL-3.0-or-later */ + +#include "ns16550.h" +#include "util.h" +#include +#include + +#undef errno +extern int errno; + + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-noreturn" +void _exit(int i) +{ + tohost_exit(i); + asm volatile (" ebreak "); +} +#pragma GCC diagnostic pop + +/** +* It is assumed that there is exactly only process running and that +* it does not support signals. Therefore calling this is effectively +* illegal and will therefore terminate the program. +*****************************************************************/ +void _kill(int pid) +{ +#if 1 + asm volatile (" ebreak "); +#else + return; // arguably, this might also be acceptable +#endif +} + +/** +* This returns the process ID of the runnung program. +* This library assumes that there is only process that +* can ever run. +* +* @return 1 +*****************************************************************/ +int _getpid(void) +{ + return 1; +} + + +/** +* This library does not support any file I/O of any kind. +* +* @return -1 Indicating that file could not be closed. +*****************************************************************/ +int _close(int file) +{ + errno = EBADF; + return -1; +} + +/** +* This library does not support any file I/O of any kind. +* This call will return a status indicating that the file +* in question is a character device. +* +* @return 0 Indicating that the call has succeeded. +*****************************************************************/ +int _fstat(int file, struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; +} +/** +* This library does not support any file I/O of any kind. +* +* @return 1 Indicating that file is a tty device (a terminal.) +*****************************************************************/ +int _isatty(int file) +{ + return 1; +} + +/** +* This library does not support any file I/O of any kind. +* +* @return 0 Indicating that the request has succeeded. +*****************************************************************/ +int _lseek(int file, int ptr, int dir) +{ + return 0; +} + +/** +* This library does not support any file I/O of any kind. +* +* @return -1 (error codition.) +*****************************************************************/ +int _open(const char *name, int flags, int mode) +{ + errno = ENFILE; // The system-wide limit (0) on total open files has been reached. + return -1; +} + +/** +* This library does not support any file I/O of any kind. +* +* @return EOF. +*****************************************************************/ +int _read(int file, char *ptr, int len) +{ + return 0; +} + +/** +* This function should satify the caller by simply returning len +* indicating that the write has succeeded as requested in spite +* of the fact that the data is simply ignored/discarded. +* +* @return len +*****************************************************************/ +int _write(int file, char *ptr, int len) +{ +#ifdef QEMU + // qemu-system-riscv32 -machine virt has a 16550 at address 0x10000000 + //volatile char *thr = (volatile char *)0x10000000; + for (int i=0; i +#include + +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +int strcmp(const char *s1, const char *s2); +unsigned int strlen(const char *s); + +void print(const char *s); +void print_hex(size_t x); +void printn(const char *s, int len); + +#define SYS_exit 93 +#define SYS_read 63 +#define SYS_write 64 + +uintptr_t syscall(uintptr_t n, uintptr_t a0, uintptr_t a1, uintptr_t a2, + uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6); + +void shutdown(int code); + +void tohost_exit(uintptr_t code); diff --git a/pylib/run_spike.py b/pylib/run_spike.py new file mode 100644 index 00000000..224dfb14 --- /dev/null +++ b/pylib/run_spike.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +# Python module to run programs with RISC-V ISA simulator spike. + +# Copyright (C) 2023 HighTec edV-Systeme GmbH +# +# Contributor: Emil J. Tywoniak +# +# This file is part of Embench. + +# SPDX-License-Identifier: GPL-3.0-or-later + +""" +Embench module to run benchmark programs. + +This version is suitable for running programs with RISC-V ISA simulator spike. +""" + +__all__ = [ + 'get_target_args', + 'build_benchmark_cmd', + 'decode_results', +] + +import argparse +import re + +from embench_core import log + + +def get_target_args(remnant): + """Parse left over arguments""" + parser = argparse.ArgumentParser(description='Get target specific args') + + # No target arguments + return parser.parse_args(remnant) + + +def build_benchmark_cmd(bench, args): + """Construct the command to run the benchmark. "args" is a + namespace with target specific arguments""" + + # Due to way the target interface currently works we need to construct + # a command that records both the return value and execution time to + # stdin/stdout. Obviously using time will not be very precise. + # Hacky workaround for https://github.com/riscv-software-src/riscv-isa-sim/issues/1493 + return ['script', '-c', f'spike --isa=RV32GC {bench}', '-e'] + + +def decode_results(stdout_str, stderr_str): + """Extract the results from the output string of the run. Return the + elapsed time in milliseconds or zero if the run failed.""" + # See above in build_benchmark_cmd how we record the return value and + # execution time. + + time = re.search('Spike mcycle timer delta: (\d+)', stdout_str, re.S) + fake_freq = 1e6 # 1 MHz + fake_period = 1.0 / fake_freq + + if time: + s_elapsed = int(time.group(1)) * fake_period + ms_elapsed = s_elapsed * 1000 + # Return value cannot be zero (will be interpreted as error) + return max(float(ms_elapsed), 0.001) + + # We must have failed to find a time + log.debug('Warning: Failed to find timing') + return 0.0