Remove spike tandem simulation

This commit is contained in:
Florian Zaruba 2018-11-20 19:10:50 +01:00
parent 0947762025
commit 20f6b42d89
No known key found for this signature in database
GPG key ID: E742FFE8EC38A792
541 changed files with 0 additions and 44611 deletions

View file

@ -55,23 +55,6 @@ module ariane_tb;
.exit_o
);
// `ifdef TANDEM
// initial $display("Tandem defined",);
// spike #(
// .Size ( NUM_WORDS * 8 )
// ) i_spike (
// .clk_i,
// .rst_ni,
// .clint_tick_i ( rtc_i ),
// .commit_instr_i ( dut.i_ariane.commit_instr_id_commit ),
// .commit_ack_i ( dut.i_ariane.commit_ack ),
// .exception_i ( dut.i_ariane.ex_commit ),
// .waddr_i ( dut.i_ariane.waddr_commit_id ),
// .wdata_i ( dut.i_ariane.wdata_commit_id ),
// .priv_lvl_i ( dut.i_ariane.priv_lvl )
// );
// `endif
// Clock process
initial begin
clk_i = 1'b0;

View file

@ -1,119 +0,0 @@
// Copyright 2018 ETH Zurich and University of Bologna.
// Copyright and related rights are licensed under the Solderpad Hardware
// License, Version 0.51 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
// or agreed to in writing, software, hardware and materials distributed under
// this 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.
//
// Author: Florian Zaruba, ETH Zurich
// Date: 3/11/2018
// Description: Wrapped Spike Model for Tandem Verification
import uvm_pkg::*;
`include "uvm_macros.svh"
import "DPI-C" function int spike_create(string filename, longint unsigned dram_base, int unsigned size);
import "DPI-C" function void spike_tick(output riscv::commit_log_t commit_log);
import "DPI-C" function void clint_tick();
module spike #(
parameter longint unsigned DramBase = 'h8000_0000,
parameter int unsigned Size = 64 * 1024 * 1024 // 64 Mega Byte
)(
input logic clk_i,
input logic rst_ni,
input logic clint_tick_i,
input ariane_pkg::scoreboard_entry_t [ariane_pkg::NR_COMMIT_PORTS-1:0] commit_instr_i,
input logic [ariane_pkg::NR_COMMIT_PORTS-1:0] commit_ack_i,
input ariane_pkg::exception_t exception_i,
input logic [ariane_pkg::NR_COMMIT_PORTS-1:0][4:0] waddr_i,
input logic [ariane_pkg::NR_COMMIT_PORTS-1:0][63:0] wdata_i,
input riscv::priv_lvl_t priv_lvl_i
);
static uvm_cmdline_processor uvcl = uvm_cmdline_processor::get_inst();
string binary = "";
logic fake_clk;
logic clint_tick_q, clint_tick_qq, clint_tick_qqq, clint_tick_qqqq;
initial begin
void'(uvcl.get_arg_value("+PRELOAD=", binary));
assert(binary != "") else $error("We need a preloaded binary for tandem verification");
void'(spike_create(binary, DramBase, Size));
end
riscv::commit_log_t commit_log;
logic [31:0] instr;
always_ff @(posedge clk_i) begin
if (rst_ni) begin
for (int i = 0; i < ariane_pkg::NR_COMMIT_PORTS; i++) begin
if (commit_instr_i[i].valid && commit_ack_i[i]) begin
spike_tick(commit_log);
instr = (commit_log.instr[1:0] != 2'b11) ? {16'b0, commit_log.instr[15:0]} : commit_log.instr;
// $display("\x1B[32m%h %h\x1B[0m", commit_log.pc, instr);
// $display("%p", commit_log);
// $display("\x1B[37m%h %h\x1B[0m", commit_instr_i[i].pc, commit_instr_i[i].ex.tval[31:0]);
assert (commit_log.pc === commit_instr_i[i].pc) else begin
$warning("\x1B[33m[Tandem] PCs Mismatch\x1B[0m");
// $stop;
end
assert (commit_log.was_exception === exception_i.valid) else begin
$warning("\x1B[33m[Tandem] Exception not detected\x1B[0m");
// $stop;
$display("Spike: %p", commit_log);
$display("Ariane: %p", commit_instr_i[i]);
end
if (!exception_i.valid) begin
assert (commit_log.priv === priv_lvl_i) else begin
$warning("\x1B[33m[Tandem] Privilege level mismatches\x1B[0m");
// $stop;
$display("\x1B[37m %2d == %2d @ PC %h\x1B[0m", priv_lvl_i, commit_log.priv, commit_log.pc);
end
assert (instr === commit_instr_i[i].ex.tval) else begin
$warning("\x1B[33m[Tandem] Decoded instructions mismatch\x1B[0m");
// $stop;
$display("\x1B[37m%h === %h @ PC %h\x1B[0m", commit_instr_i[i].ex.tval, instr, commit_log.pc);
end
// TODO(zarubaf): Adapt for floating point instructions
if (commit_instr_i[i].rd != 0) begin
// check the return value
// $display("\x1B[37m%h === %h\x1B[0m", commit_instr_i[i].rd, commit_log.rd);
assert (waddr_i[i] === commit_log.rd) else begin
$warning("\x1B[33m[Tandem] Destination register mismatches\x1B[0m");
// $stop;
end
assert (wdata_i[i] === commit_log.data) else begin
$warning("\x1B[33m[Tandem] Write back data mismatches\x1B[0m");
$display("\x1B[37m%h === %h @ PC %h\x1B[0m", wdata_i[i], commit_log.data, commit_log.pc);
end
end
end
end
end
end
end
// we want to schedule the timer increment at the end of this cycle
assign #1ps fake_clk = clk_i;
always_ff @(posedge fake_clk) begin
clint_tick_q <= clint_tick_i;
clint_tick_qq <= clint_tick_q;
clint_tick_qqq <= clint_tick_qq;
clint_tick_qqqq <= clint_tick_qqq;
end
always_ff @(posedge clint_tick_qqqq) begin
if (rst_ni) begin
void'(clint_tick());
end
end
endmodule

View file

@ -1,7 +0,0 @@
build/
*.gch
autom4te.cache/
.*.swp
*.o
*.d
.gdb_history

View file

@ -1,24 +0,0 @@
Copyright (c) 2010-2017, The Regents of the University of California
(Regents). All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the Regents nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

View file

@ -1,491 +0,0 @@
#=========================================================================
# Toplevel Makefile for the Modular C++ Build System
#=========================================================================
# Please read the documenation in 'mcppbs-doc.txt' for more details on
# how the Modular C++ Build System works. For most projects, a developer
# will not need to make any changes to this makefile. The key targets
# are as follows:
#
# - default : build all libraries and programs
# - check : build and run all unit tests
# - install : install headers, project library, and some programs
# - clean : remove all generated content (except autoconf files)
# - dist : make a source tarball
# - distcheck : make a source tarball, untar it, check it, clean it
# - distclean : remove everything
#
#-------------------------------------------------------------------------
# Basic setup
#-------------------------------------------------------------------------
# Remove all default implicit rules since they can cause subtle bugs
# and they just make things run slower
.SUFFIXES:
% : %,v
% : RCS/%,v
% : RCS/%
% : s.%
% : SCCS/s.%
# Default is to build the prereqs of the all target (defined at bottom)
default : all
.PHONY : default
project_name := @PACKAGE_TARNAME@
src_dir := @srcdir@
scripts_dir := $(src_dir)/scripts
# If the version information is not in the configure script, then we
# assume that we are in a working directory. We use the vcs-version.sh
# script in the scripts directory to generate an appropriate version
# string. Currently the way things are setup we have to run this script
# everytime we run make so the script needs to be as fast as possible.
ifeq (@PACKAGE_VERSION@,?)
project_ver:=$(shell $(scripts_dir)/vcs-version.sh $(src_dir))
else
project_ver:=@PACKAGE_VERSION@
endif
# Installation directories
prefix := @prefix@
enable_stow := @enable_stow@
ifeq ($(enable_stow),yes)
stow_pkg_dir := $(prefix)/pkgs
INSTALLDIR ?= $(DESTDIR)$(stow_pkg_dir)/$(project_name)-$(project_ver)
else
INSTALLDIR ?= $(DESTDIR)$(prefix)
endif
install_hdrs_dir := $(INSTALLDIR)/include/$(project_name)
install_libs_dir := $(INSTALLDIR)/lib
install_exes_dir := $(INSTALLDIR)/bin
#-------------------------------------------------------------------------
# List of subprojects
#-------------------------------------------------------------------------
sprojs := @subprojects@
sprojs_enabled := @subprojects_enabled@
sprojs_include := -I. -I$(src_dir) $(addprefix -I$(src_dir)/, $(sprojs_enabled))
VPATH := $(addprefix $(src_dir)/, $(sprojs_enabled))
#-------------------------------------------------------------------------
# Programs and flags
#-------------------------------------------------------------------------
# C++ compiler
# - CPPFLAGS : flags for the preprocessor (eg. -I,-D)
# - CXXFLAGS : flags for C++ compiler (eg. -Wall,-g,-O3)
CC := @CC@
CXX := @CXX@
CFLAGS += @CFLAGS@ -DPREFIX=\"$(prefix)\"
CPPFLAGS += @CPPFLAGS@
CXXFLAGS += @CXXFLAGS@ -DPREFIX=\"$(prefix)\"
COMPILE := $(CXX) -fPIC -MMD -MP $(CPPFLAGS) $(CXXFLAGS) \
$(sprojs_include)
COMPILE_C := $(CC) -fPIC -MMD -MP $(CPPFLAGS) $(CFLAGS) \
$(sprojs_include)
# Linker
# - LDFLAGS : Flags for the linker (eg. -L)
# - LIBS : Library flags (eg. -l)
comma := ,
LD := $(CXX)
LDFLAGS := @LDFLAGS@
LIBS := @LIBS@
LINK := $(LD) -L. $(LDFLAGS) -Wl,-rpath,$(install_libs_dir) $(patsubst -L%,-Wl$(comma)-rpath$(comma)%,$(filter -L%,$(LDFLAGS)))
# Library creation
AR := @AR@
RANLIB := @RANLIB@
# Host simulator
RUN := @RUN@
RUNFLAGS := @RUNFLAGS@
# Installation
MKINSTALLDIRS := $(scripts_dir)/mk-install-dirs.sh
INSTALL := @INSTALL@
INSTALL_HDR := $(INSTALL) -m 444
INSTALL_LIB := $(INSTALL) -m 644
INSTALL_EXE := $(INSTALL) -m 555
STOW := @stow@
# Tests
bintests = $(src_dir)/tests/ebreak.py
#-------------------------------------------------------------------------
# Include subproject makefile fragments
#-------------------------------------------------------------------------
sprojs_mk = $(addsuffix .mk, $(sprojs_enabled))
-include $(sprojs_mk)
dist_junk += $(sprojs_mk)
#-------------------------------------------------------------------------
# Reverse list helper function
#-------------------------------------------------------------------------
# This function is used by the subproject template to reverse the list
# of dependencies. It uses recursion to perform the reversal.
#
# Arguments:
# $(1) : space separated input list
# retval : input list in reverse order
#
reverse_list = $(call reverse_list_h,$(1),)
define reverse_list_h
$(if $(strip $(1)), \
$(call reverse_list_h, \
$(wordlist 2,$(words $(1)),$(1)), \
$(firstword $(1)) $(2)), \
$(2))
endef
#-------------------------------------------------------------------------
# Template for per subproject rules
#-------------------------------------------------------------------------
# The template is instantiated for each of the subprojects. It relies on
# subprojects defining a certain set of make variables which are all
# prefixed with the subproject name. Since subproject names can have
# dashes in them (and the make variables are assumed to only use
# underscores) the template takes two arguments - one with the regular
# subproject name and one with dashes replaced with underscores.
#
# Arguments:
# $(1) : real subproject name (ie with dashes)
# $(2) : normalized subproject name (ie dashes replaced with underscores)
#
define subproject_template
# In some (rare) cases, a subproject might not have any actual object
# files. It might only include header files or program sources. To keep
# things consistent we still want a library for this subproject, so in
# this spectial case we create a dummy source file and thus the build
# system will create a library for this subproject with just the
# corresponding dummy object file.
ifeq ($$(strip $$($(2)_srcs) $$($(2)_c_srcs)),)
$(2)_srcs += _$(1).cc
$(2)_junk += _$(1).cc
endif
_$(1).cc :
echo "int _$(2)( int arg ) { return arg; }" > $$@
# Build the object files for this subproject
$(2)_pch := $$(patsubst %.h, %.h.gch, $$($(2)_precompiled_hdrs))
$(2)_objs := $$(patsubst %.cc, %.o, $$($(2)_srcs))
$(2)_c_objs := $$(patsubst %.c, %.o, $$($(2)_c_srcs))
$(2)_deps := $$(patsubst %.o, %.d, $$($(2)_objs))
$(2)_deps += $$(patsubst %.o, %.d, $$($(2)_c_objs))
$(2)_deps += $$(patsubst %.h, %.h.d, $$($(2)_precompiled_hdrs))
$$($(2)_pch) : %.h.gch : %.h
$(COMPILE) -x c++-header $$< -o $$@
# If using clang, don't depend (and thus don't build) precompiled headers
$$($(2)_objs) : %.o : %.cc $$($(2)_gen_hdrs) $(if $(filter-out clang,$(CC)),$$($(2)_pch))
$(COMPILE) -c $$<
$$($(2)_c_objs) : %.o : %.c $$($(2)_gen_hdrs)
$(COMPILE_C) -c $$<
$(2)_junk += $$($(2)_pch) $$($(2)_objs) $$($(2)_c_objs) $$($(2)_deps) \
$$($(2)_gen_hdrs)
# Reverse the dependency list so that a given subproject only depends on
# subprojects listed to its right. This is the correct order for linking
# the list of subproject libraries.
$(2)_reverse_deps := $$(call reverse_list,$$($(2)_subproject_deps))
# Build a library for this subproject
$(2)_lib_libs := $$($(2)_reverse_deps)
$(2)_lib_libnames := $$(patsubst %, lib%.so, $$($(2)_lib_libs))
$(2)_lib_libarg := $$(patsubst %, -l%, $$($(2)_lib_libs))
lib$(1).so : $$($(2)_objs) $$($(2)_c_objs) $$($(2)_lib_libnames)
$(LINK) -shared -o $$@ $(if $(filter Darwin,$(shell uname -s)),-install_name $(install_libs_dir)/$$@) $$^ $$($(2)_lib_libarg) $(LIBS)
$(2)_junk += lib$(1).so
# Build unit tests
$(2)_test_objs := $$(patsubst %.cc, %.o, $$($(2)_test_srcs))
$(2)_test_deps := $$(patsubst %.o, %.d, $$($(2)_test_objs))
$(2)_test_exes := $$(patsubst %.t.cc, %-utst, $$($(2)_test_srcs))
$(2)_test_outs := $$(patsubst %, %.out, $$($(2)_test_exes))
$(2)_test_libs := $(1) $$($(2)_reverse_deps) utst
$(2)_test_libnames := $$(patsubst %, lib%.so, $$($(2)_test_libs))
$(2)_test_libarg := $$(patsubst %, -l%, $$($(2)_test_libs))
$$($(2)_test_objs) : %.o : %.cc
$(COMPILE) -c $$<
$$($(2)_test_exes) : %-utst : %.t.o $$($(2)_test_libnames)
$(LINK) -o $$@ $$< $$($(2)_test_libarg) $(LIBS)
$(2)_deps += $$($(2)_test_deps)
$(2)_junk += \
$$($(2)_test_objs) $$($(2)_test_deps) \
$$($(2)_test_exes) *.junk-dat
# Run unit tests
$$($(2)_test_outs) : %.out : %
$(RUN) $(RUNFLAGS) ./$$< default | tee $$@
$(2)_junk += $$($(2)_test_outs)
# Build programs
$(2)_prog_objs := $$(patsubst %.cc, %.o, $$($(2)_prog_srcs))
$(2)_prog_deps := $$(patsubst %.o, %.d, $$($(2)_prog_objs))
$(2)_prog_exes := $$(patsubst %.cc, %, $$($(2)_prog_srcs))
$(2)_prog_libs := $(1) $$($(2)_reverse_deps)
$(2)_prog_libnames := $$(patsubst %, lib%.so, $$($(2)_prog_libs))
$(2)_prog_libarg := $$(patsubst %, -l%, $$($(2)_prog_libs))
$$($(2)_prog_objs) : %.o : %.cc
$(COMPILE) -c $$<
$$($(2)_prog_exes) : % : %.o $$($(2)_prog_libnames)
$(LINK) -o $$@ $$< $$($(2)_prog_libarg) $(LIBS)
$(2)_deps += $$($(2)_prog_deps)
$(2)_junk += $$($(2)_prog_objs) $$($(2)_prog_deps) $$($(2)_prog_exes)
# Build programs which will be installed
$(2)_install_prog_objs := $$(patsubst %.cc, %.o, $$($(2)_install_prog_srcs))
$(2)_install_prog_deps := $$(patsubst %.o, %.d, $$($(2)_install_prog_objs))
$(2)_install_prog_exes := $$(patsubst %.cc, %, $$($(2)_install_prog_srcs))
$$($(2)_install_prog_objs) : %.o : %.cc $$($(2)_gen_hdrs)
$(COMPILE) -c $$<
$$($(2)_install_prog_exes) : % : %.o $$($(2)_prog_libnames)
$(LINK) -o $$@ $$< $$($(2)_prog_libarg) $(LIBS)
$(2)_deps += $$($(2)_install_prog_deps)
$(2)_junk += \
$$($(2)_install_prog_objs) $$($(2)_install_prog_deps) \
$$($(2)_install_prog_exes)
# Subproject specific targets
all-$(1) : lib$(1).so $$($(2)_install_prog_exes)
check-$(1) : $$($(2)_test_outs)
echo; grep -h -e'Unit Tests' -e'FAILED' -e'Segementation' $$^; echo
clean-$(1) :
rm -rf $$($(2)_junk)
.PHONY : all-$(1) check-$(1) clean-$(1)
# Update running variables
libs += lib$(1).so
objs += $$($(2)_objs)
srcs += $$(addprefix $(src_dir)/$(1)/, $$($(2)_srcs))
hdrs += $$(addprefix $(src_dir)/$(1)/, $$($(2)_hdrs)) $$($(2)_gen_hdrs)
junk += $$($(2)_junk)
deps += $$($(2)_deps)
test_outs += $$($(2)_test_outs)
install_hdrs += $$(addprefix $(src_dir)/$(1)/, $$($(2)_hdrs)) $$($(2)_gen_hdrs)
install_libs += lib$(1).so
install_exes += $$($(2)_install_prog_exes)
install_pcs += riscv-$(1).pc
endef
# Iterate over the subprojects and call the template for each one
$(foreach sproj,$(sprojs_enabled), \
$(eval $(call subproject_template,$(sproj),$(subst -,_,$(sproj)))))
#-------------------------------------------------------------------------
# Autodependency files
#-------------------------------------------------------------------------
-include $(deps)
deps : $(deps)
.PHONY : deps
#-------------------------------------------------------------------------
# Check
#-------------------------------------------------------------------------
bintest_outs = $(bintests:=.out)
junk += $(bintest_outs)
%.out: % all
./$* < /dev/null 2>&1 | tee $@
check-cpp : $(test_outs)
@echo
! grep -h -e'Unit Tests' -e'FAILED' -e'Segmentation' $^ < /dev/null
@echo
check-bin : $(bintest_outs)
! tail -n 1 $^ < /dev/null 2>&1 | grep FAILED
check : check-cpp check-bin
.PHONY : check
#-------------------------------------------------------------------------
# Installation
#-------------------------------------------------------------------------
install-hdrs : $(install_hdrs) config.h
$(MKINSTALLDIRS) $(install_hdrs_dir)
for file in $^; \
do \
$(INSTALL_HDR) $$file $(install_hdrs_dir); \
done
install-libs : $(install_libs)
$(MKINSTALLDIRS) $(install_libs_dir)
for file in $^; \
do \
$(INSTALL_LIB) $$file $(install_libs_dir); \
done
install-exes : $(install_exes)
$(MKINSTALLDIRS) $(install_exes_dir)
for file in $^; \
do \
$(INSTALL_EXE) $$file $(install_exes_dir); \
done
install-pc : $(install_pcs)
$(MKINSTALLDIRS) $(install_libs_dir)/pkgconfig/
for file in $^; \
do \
$(INSTALL_HDR) $$file $(install_libs_dir)/pkgconfig/; \
done
install : install-hdrs install-libs install-exes install-pc
ifeq ($(enable_stow),yes)
$(MKINSTALLDIRS) $(stow_pkg_dir)
cd $(stow_pkg_dir) && \
$(STOW) --delete $(project_name)-* && \
$(STOW) $(project_name)-$(project_ver)
endif
.PHONY : install install-hdrs install-libs install-exes
#-------------------------------------------------------------------------
# Regenerate configure information
#-------------------------------------------------------------------------
config.status : $(src_dir)/configure
./config.status --recheck
sprojs_mk_in = \
$(join $(addprefix $(src_dir)/, $(sprojs_enabled)), \
$(patsubst %, /%.mk.in, $(sprojs_enabled)))
Makefile : $(src_dir)/Makefile.in $(sprojs_mk_in) config.status
./config.status
dist_junk += config.status config.h Makefile config.log
#-------------------------------------------------------------------------
# Distribution
#-------------------------------------------------------------------------
# The distribution tarball is named project-ver.tar.gz and it includes
# both enabled and disabled subprojects.
dist_files = \
$(sprojs) \
README \
style-guide.txt \
mcppbs-uguide.txt \
scripts \
configure.ac \
aclocal.m4 \
configure \
config.h.in \
Makefile.in \
dist_dir := $(project_name)-$(project_ver)
dist_tgz := $(project_name)-$(project_ver).tar.gz
# Notice that when we make the distribution we rewrite the configure.ac
# script with the current version and we rerun autoconf in the new
# source directory so that the distribution will have the proper version
# information. We also rewrite the "Version : " line in the README.
dist :
rm -rf $(dist_dir)
mkdir $(dist_dir)
tar -C $(src_dir) -cf - $(dist_files) | tar -C $(dist_dir) -xpf -
sed -i.bak 's/^\(# Version :\).*/\1 $(project_ver)/' $(dist_dir)/README
sed -i.bak 's/\( proj_version,\).*/\1 [$(project_ver)])/' $(dist_dir)/configure.ac
cd $(dist_dir) && \
autoconf && autoheader && \
rm -rf autom4te.cache configure.ac.bak README.bak
tar -czvf $(dist_tgz) $(dist_dir)
rm -rf $(dist_dir)
# You can use the distcheck target to try untarring the distribution and
# then running configure, make, make check, and make distclean. A
# "directory is not empty" error means distclean is not removing
# everything.
distcheck : dist
rm -rf $(dist_dir)
tar -xzvf $(dist_tgz)
mkdir -p $(dist_dir)/build
cd $(dist_dir)/build; ../configure; make; make check; make distclean
rm -rf $(dist_dir)
junk += $(project_name)-*.tar.gz
.PHONY : dist distcheck
#-------------------------------------------------------------------------
# Default
#-------------------------------------------------------------------------
all : $(install_hdrs) $(install_libs) $(install_exes)
.PHONY : all
#-------------------------------------------------------------------------
# Makefile debugging
#-------------------------------------------------------------------------
# This handy rule will display the contents of any make variable by
# using the target debug-<varname>. So for example, make debug-junk will
# display the contents of the junk variable.
debug-% :
@echo $* = $($*)
#-------------------------------------------------------------------------
# Clean up junk
#-------------------------------------------------------------------------
clean :
rm -rf *~ \#* $(junk)
distclean :
rm -rf *~ \#* $(junk) $(dist_junk)
.PHONY : clean distclean

View file

@ -1,223 +0,0 @@
Spike RISC-V ISA Simulator
============================
About
-------------
Spike, the RISC-V ISA Simulator, implements a functional model of one or more
RISC-V processors.
Spike is named after the golden spike used to celebrate the completion of the
US transcontinental railway.
Build Steps
---------------
We assume that the RISCV environment variable is set to the RISC-V tools
install path, and that the riscv-fesvr package is installed there.
$ apt-get install device-tree-compiler
$ mkdir build
$ cd build
$ ../configure --prefix=$RISCV --with-fesvr=$RISCV
$ make
$ [sudo] make install
Compiling and Running a Simple C Program
-------------------------------------------
Install spike (see Build Steps), riscv-gnu-toolchain, and riscv-pk.
Write a short C program and name it hello.c. Then, compile it into a RISC-V
ELF binary named hello:
$ riscv64-unknown-elf-gcc -o hello hello.c
Now you can simulate the program atop the proxy kernel:
$ spike pk hello
Simulating a New Instruction
------------------------------------
Adding an instruction to the simulator requires two steps:
1. Describe the instruction's functional behavior in the file
riscv/insns/<new_instruction_name>.h. Examine other instructions
in that directory as a starting point.
2. Add the opcode and opcode mask to riscv/opcodes.h. Alternatively,
add it to the riscv-opcodes package, and it will do so for you:
$ cd ../riscv-opcodes
$ vi opcodes // add a line for the new instruction
$ make install
3. Rebuild the simulator.
Interactive Debug Mode
---------------------------
To invoke interactive debug mode, launch spike with -d:
$ spike -d pk hello
To see the contents of an integer register (0 is for core 0):
: reg 0 a0
To see the contents of a floating point register:
: fregs 0 ft0
or:
: fregd 0 ft0
depending upon whether you wish to print the register as single- or double-precision.
To see the contents of a memory location (physical address in hex):
: mem 2020
To see the contents of memory with a virtual address (0 for core 0):
: mem 0 2020
You can advance by one instruction by pressing <enter>. You can also
execute until a desired equality is reached:
: until pc 0 2020 (stop when pc=2020)
: until mem 2020 50a9907311096993 (stop when mem[2020]=50a9907311096993)
Alternatively, you can execute as long as an equality is true:
: while mem 2020 50a9907311096993
You can continue execution indefinitely by:
: r
At any point during execution (even without -d), you can enter the
interactive debug mode with `<control>-<c>`.
To end the simulation from the debug prompt, press `<control>-<c>` or:
: q
Debugging With Gdb
------------------
An alternative to interactive debug mode is to attach using gdb. Because spike
tries to be like real hardware, you also need OpenOCD to do that. OpenOCD
doesn't currently know about address translation, so it's not possible to
easily debug programs that are run under `pk`. We'll use the following test
program:
```
$ cat rot13.c
char text[] = "Vafgehpgvba frgf jnag gb or serr!";
// Don't use the stack, because sp isn't set up.
volatile int wait = 1;
int main()
{
while (wait)
;
// Doesn't actually go on the stack, because there are lots of GPRs.
int i = 0;
while (text[i]) {
char lower = text[i] | 32;
if (lower >= 'a' && lower <= 'm')
text[i] += 13;
else if (lower > 'm' && lower <= 'z')
text[i] -= 13;
i++;
}
while (!wait)
;
}
$ cat spike.lds
OUTPUT_ARCH( "riscv" )
SECTIONS
{
. = 0x10010000;
.text : { *(.text) }
.data : { *(.data) }
}
$ riscv64-unknown-elf-gcc -g -Og -o rot13-64.o -c rot13.c
$ riscv64-unknown-elf-gcc -g -Og -T spike.lds -nostartfiles -o rot13-64 rot13-64.o
```
To debug this program, first run spike telling it to listen for OpenOCD:
```
$ spike --rbb-port=9824 -m0x10000000:0x20000 rot13-64
Listening for remote bitbang connection on port 9824.
```
In a separate shell run OpenOCD with the appropriate configuration file:
```
$ cat spike.cfg
interface remote_bitbang
remote_bitbang_host localhost
remote_bitbang_port 9824
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
gdb_report_data_abort enable
init
halt
$ openocd -f spike.cfg
Open On-Chip Debugger 0.10.0-dev-00002-gc3b344d (2017-06-08-12:14)
...
riscv.cpu: target state: halted
```
In yet another shell, start your gdb debug session:
```
tnewsome@compy-vm:~/SiFive/spike-test$ riscv64-unknown-elf-gdb rot13-64
GNU gdb (GDB) 7.12.50.20170505-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=riscv64-unknown-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from rot13-64...done.
(gdb) target remote localhost:3333
Remote debugging using localhost:3333
0x000000001001000a in main () at rot13.c:8
8 while (wait)
(gdb) print wait
$1 = 1
(gdb) print wait=0
$2 = 0
(gdb) print text
$3 = "Vafgehpgvba frgf jnag gb or serr!"
(gdb) b 23
Breakpoint 1 at 0x10010064: file rot13.c, line 23.
(gdb) c
Continuing.
Breakpoint 1, main () at rot13.c:23
23 while (!wait)
(gdb) print wait
$4 = 0
(gdb) print text
...
```

View file

@ -1,345 +0,0 @@
#=========================================================================
# Local Autoconf Macros
#=========================================================================
# This file contains the macros for the Modular C++ Build System and
# additional autoconf macros which developers can use in their
# configure.ac scripts. Please read the documentation in
# 'mcppbs-doc.txt' for more details on how the Modular C++ Build System
# works. The documenation for each macro should include information
# about the author, date, and copyright.
#-------------------------------------------------------------------------
# MCPPBS_PROG_INSTALL
#-------------------------------------------------------------------------
# This macro will add an --enable-stow command line option to the
# configure script. When enabled, this macro will first check to see if
# the stow program is available and if so it will set the $stow shell
# variable to the binary name and the $enable_stow shell variable to
# "yes". These variables can be used in a makefile to conditionally use
# stow for installation.
#
# This macro uses two environment variables to help setup default stow
# locations. The $STOW_PREFIX is used for stowing native built packages.
# The packages are staged in $STOW_PREFIX/pkgs and then symlinks are
# created from within $STOW_PREFIX into the pkgs subdirectory. If you
# only do native builds then this is all you need to set. If you don't
# set $STOW_PREFIX then the default is just the normal default prefix
# which is almost always /usr/local.
#
# For non-native builds we probably want to install the packages in a
# different location which includes the host architecture name as part
# of the prefix. For these kind of builds, we can specify the $STOW_ROOT
# environment variable and the effective prefix will be
# $STOW_ROOT/${host_alias} where ${host_alias} is specified on the
# configure command line with "--host".
#
# Here is an example setup:
#
# STOW_ROOT="$HOME/install"
# STOW_ARCH="i386-macosx10.4"
# STOW_PREFIX="${STOW_ROOT}/${STOW_ARCH}"
#
AC_DEFUN([MCPPBS_PROG_INSTALL],
[
# Configure command line option
AC_ARG_ENABLE(stow,
AS_HELP_STRING(--enable-stow,[Enable stow-based install]),
[enable_stow="yes"],[enable_stow="no"])
AC_SUBST([enable_stow])
# Environment variables
AC_ARG_VAR([STOW_ROOT], [Root for non-native stow-based installs])
AC_ARG_VAR([STOW_PREFIX], [Prefix for stow-based installs])
# Check for install script
AC_PROG_INSTALL
# Deterimine if native build and set prefix appropriately
AS_IF([ test ${enable_stow} = "yes" ],
[
AC_CHECK_PROGS([stow],[stow],[no])
AS_IF([ test ${stow} = "no" ],
[
AC_MSG_ERROR([Cannot use --enable-stow since stow is not available])
])
# Check if native or non-native build
AS_IF([ test "${build}" = "${host}" ],
[
# build == host so this is a native build. Make sure --prefix not
# set and $STOW_PREFIX is set, then set prefix=$STOW_PREFIX.
AS_IF([ test "${prefix}" = "NONE" && test -n "${STOW_PREFIX}" ],
[
prefix="${STOW_PREFIX}"
AC_MSG_NOTICE([Using \$STOW_PREFIX from environment])
AC_MSG_NOTICE([prefix=${prefix}])
])
],[
# build != host so this is a non-native build. Make sure --prefix
# not set and $STOW_ROOT is set, then set
# prefix=$STOW_ROOT/${host_alias}.
AS_IF([ test "${prefix}" = "NONE" && test -n "${STOW_ROOT}" ],
[
prefix="${STOW_ROOT}/${host_alias}"
AC_MSG_NOTICE([Using \$STOW_ROOT from environment])
AC_MSG_NOTICE([prefix=${prefix}])
])
])
])
])
#-------------------------------------------------------------------------
# MCPPBS_PROG_RUN
# -------------------------------------------------------------------------
# If we are doing a non-native build then we look for an isa simulator
# to use for running tests. We set the RUN substitution variable to be
# empty for native builds or to the name of the isa simulator for
# non-native builds. Thus a makefile can run compiled programs
# regardless if we are doing a native or non-native build like this:
#
# $(RUN) $(RUNFLAGS) ./test-program
#
AC_DEFUN([MCPPBS_PROG_RUN],
[
AS_IF([ test "${build}" != "${host}" ],
[
AC_CHECK_TOOLS([RUN],[isa-run run],[no])
AS_IF([ test ${RUN} = "no" ],
[
AC_MSG_ERROR([Cannot find simulator for target ${target_alias}])
])
],[
RUN=""
])
AC_SUBST([RUN])
AC_SUBST([RUNFLAGS])
])
#-------------------------------------------------------------------------
# MCPPBS_SUBPROJECTS([ sproj1, sproj2, ... ])
#-------------------------------------------------------------------------
# The developer should call this macro with a list of the subprojects
# which make up this project. One should order the list such that any
# given subproject only depends on subprojects listed before it. The
# subproject names can also include an * suffix which indicates that
# this is an optional subproject. Optional subprojects are only included
# as part of the project build if enabled on the configure command line
# with a --enable-<subproject> flag. The user can also specify that all
# optional subprojects should be included in the build with the
# --enable-optional-subprojects flag.
#
# Subproject names can also include a ** suffix which indicates that it
# is an optional subproject, but there is a group with the same name.
# Thus the --enable-<sproj> command line option will enable not just the
# subproject sproj but all of the subprojects which are in the group.
# There is no error checking to make sure that if you use the ** suffix
# you actually define a group so be careful.
#
# Both required and optional subprojects should have a 'subproject.ac'
# file. The script's filename should be the abbreivated subproject name
# (assuming the subproject name is sproj then we would use 'sproj.ac')
# The MCPPBS_SUBPROJECTS macro includes the 'subproject.ac' files for
# enabled subprojects. Whitespace and newlines are allowed within the
# list.
#
# Author : Christopher Batten
# Date : September 10, 2008
AC_DEFUN([MCPPBS_SUBPROJECTS],
[
# Add command line argument to enable all optional subprojects
AC_ARG_ENABLE(optional-subprojects,
AS_HELP_STRING([--enable-optional-subprojects],
[Enable all optional subprojects]))
# Loop through the subprojects given in the macro argument
m4_foreach([MCPPBS_SPROJ],[$1],
[
# Determine if this is a required or an optional subproject
m4_define([MCPPBS_IS_REQ],
m4_bmatch(MCPPBS_SPROJ,[\*+],[false],[true]))
# Determine if there is a group with the same name
m4_define([MCPPBS_IS_GROUP],
m4_bmatch(MCPPBS_SPROJ,[\*\*],[true],[false]))
# Create variations of the subproject name suitable for use as a CPP
# enabled define, a shell enabled variable, and a shell function
m4_define([MCPPBS_SPROJ_NORM],
m4_normalize(m4_bpatsubsts(MCPPBS_SPROJ,[*],[])))
m4_define([MCPPBS_SPROJ_DEFINE],
m4_toupper(m4_bpatsubst(MCPPBS_SPROJ_NORM[]_ENABLED,[-],[_])))
m4_define([MCPPBS_SPROJ_FUNC],
m4_bpatsubst(_mpbp_[]MCPPBS_SPROJ_NORM[]_configure,[-],[_]))
m4_define([MCPPBS_SPROJ_UNDERSCORES],
m4_bpatsubsts(MCPPBS_SPROJ,[-],[_]))
m4_define([MCPPBS_SPROJ_SHVAR],
m4_bpatsubst(enable_[]MCPPBS_SPROJ_NORM[]_sproj,[-],[_]))
# Add subproject to our running list
subprojects="$subprojects MCPPBS_SPROJ_NORM"
# Process the subproject appropriately. If enabled add it to the
# $enabled_subprojects running shell variable, set a
# SUBPROJECT_ENABLED C define, and include the appropriate
# 'subproject.ac'.
m4_if(MCPPBS_IS_REQ,[true],
[
AC_MSG_NOTICE([configuring default subproject : MCPPBS_SPROJ_NORM])
AC_CONFIG_FILES(MCPPBS_SPROJ_NORM[].mk:MCPPBS_SPROJ_NORM[]/MCPPBS_SPROJ_NORM[].mk.in)
MCPPBS_SPROJ_SHVAR="yes"
subprojects_enabled="$subprojects_enabled MCPPBS_SPROJ_NORM"
AC_DEFINE(MCPPBS_SPROJ_DEFINE,,
[Define if subproject MCPPBS_SPROJ_NORM is enabled])
m4_include(MCPPBS_SPROJ_NORM[]/MCPPBS_SPROJ_NORM[].ac)
],[
# For optional subprojects we capture the 'subproject.ac' as a
# shell function so that in the MCPPBS_GROUP macro we can just
# call this shell function instead of reading in 'subproject.ac'
# again.
MCPPBS_SPROJ_FUNC ()
{
AC_MSG_NOTICE([configuring optional subproject : MCPPBS_SPROJ_NORM])
AC_CONFIG_FILES(MCPPBS_SPROJ_NORM[].mk:MCPPBS_SPROJ_NORM[]/MCPPBS_SPROJ_NORM[].mk.in)
MCPPBS_SPROJ_SHVAR="yes"
subprojects_enabled="$subprojects_enabled MCPPBS_SPROJ_NORM"
AC_DEFINE(MCPPBS_SPROJ_DEFINE,,
[Define if subproject MCPPBS_SPROJ_NORM is enabled])
m4_include(MCPPBS_SPROJ_NORM[]/MCPPBS_SPROJ_NORM[].ac)
};
# Optional subprojects add --enable-subproject command line
# options, _if_ the subproject name is not also a group name.
m4_if(MCPPBS_IS_GROUP,[false],
[
AC_ARG_ENABLE(MCPPBS_SPROJ_NORM,
AS_HELP_STRING(--enable-MCPPBS_SPROJ_NORM,
[Subproject MCPPBS_SPROJ_NORM]),
[MCPPBS_SPROJ_SHVAR="yes"],[MCPPBS_SPROJ_SHVAR="no"])
AS_IF([test "$MCPPBS_SPROJ_SHVAR" = "yes"],
[
eval "MCPPBS_SPROJ_FUNC"
],[
AC_MSG_NOTICE([processing optional subproject : MCPPBS_SPROJ_NORM])
])
],[
# If the subproject name is also a group name then we need to
# make sure that we set the shell variable for that subproject to
# no so that the group code knows we haven't run it yet.
AC_MSG_NOTICE([processing optional subproject : MCPPBS_SPROJ_NORM])
MCPPBS_SPROJ_SHVAR="no"
])
# Always execute the subproject configure code if we are enabling
# all subprojects.
AS_IF([ test "$enable_optional_subprojects" = "yes" \
&& test "$MCPPBS_SPROJ_SHVAR" = "no" ],
[
eval "MCPPBS_SPROJ_FUNC"
])
])
])
# Output make variables
AC_SUBST([subprojects])
AC_SUBST([subprojects_enabled])
])
#-------------------------------------------------------------------------
# MCPPBS_GROUP( [group-name], [ sproj1, sproj2, ... ] )
#-------------------------------------------------------------------------
# This macro creates a subproject group with the given group-name. When
# a user specifies --enable-<group-name> the listed subprojects will be
# enabled. Groups can have the same name as a subproject and in that
# case whenever a user specifies --enable-<subproject> the subprojects
# listed in the corresponding group will also be enabled. Groups are
# useful for specifying related subprojects which are usually enabled
# together, as well as for specifying that a specific optional
# subproject has dependencies on other optional subprojects.
#
# Author : Christopher Batten
# Date : September 10, 2008
AC_DEFUN([MCPPBS_GROUP],
[
m4_define([MCPPBS_GROUP_NORM],
m4_normalize([$1]))
m4_define([MCPPBS_GROUP_SHVAR],
m4_bpatsubst(enable_[]MCPPBS_GROUP_NORM[]_group,[-],[_]))
AC_ARG_ENABLE(MCPPBS_GROUP_NORM,
AS_HELP_STRING(--enable-MCPPBS_GROUP_NORM,
[Group MCPPBS_GROUP_NORM: $2]),
[MCPPBS_GROUP_SHVAR="yes"],[MCPPBS_GROUP_SHVAR="no"])
AS_IF([test "$MCPPBS_GROUP_SHVAR" = "yes" ],
[
AC_MSG_NOTICE([configuring optional group : MCPPBS_GROUP_NORM])
])
m4_foreach([MCPPBS_SPROJ],[$2],
[
m4_define([MCPPBS_SPROJ_NORM],
m4_normalize(MCPPBS_SPROJ))
m4_define([MCPPBS_SPROJ_SHVAR],
m4_bpatsubst(enable_[]MCPPBS_SPROJ_NORM[]_sproj,[-],[_]))
m4_define([MCPPBS_SPROJ_FUNC],
m4_bpatsubst(_mpbp_[]MCPPBS_SPROJ_NORM[]_configure,[-],[_]))
AS_IF([ test "$MCPPBS_GROUP_SHVAR" = "yes" \
&& test "$MCPPBS_SPROJ_SHVAR" = "no" ],
[
eval "MCPPBS_SPROJ_FUNC"
])
])
])

View file

@ -1,100 +0,0 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define if building universal (internal helper macro) */
#undef AC_APPLE_UNIVERSAL_BUILD
/* Default value for --isa switch */
#undef DEFAULT_ISA
/* Path to the device-tree-compiler */
#undef DTC
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
#undef DUMMY_ROCC_ENABLED
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `fesvr' library (-lfesvr). */
#undef HAVE_LIBFESVR
/* Define to 1 if you have the `pthread' library (-lpthread). */
#undef HAVE_LIBPTHREAD
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
#undef RISCV_ENABLED
/* Enable commit log generation */
#undef RISCV_ENABLE_COMMITLOG
/* Enable hardware management of PTE accessed and dirty bits */
#undef RISCV_ENABLE_DIRTY
/* Enable PC histogram generation */
#undef RISCV_ENABLE_HISTOGRAM
/* Enable hardware support for misaligned loads and stores */
#undef RISCV_ENABLE_MISALIGNED
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
#undef SOFTFLOAT_ENABLED
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
#undef SPIKE_MAIN_ENABLED
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
# undef WORDS_BIGENDIAN
# endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,113 +0,0 @@
#=========================================================================
# Toplevel configure.ac for the Modular C++ Build System
#=========================================================================
# Please read the documenation in 'mcppbs-doc.txt' for more details on
# how the Modular C++ Build System works. For most new projects, a
# developer will only need to make the following changes:
#
# - change the project metadata listed right below
# - update the list of subprojects via the 'MCPPBS_SUBPROJECTS' macro
# - possibly add subproject groups if needed to ease configuration
# - add more configure checks for platform specific configuration
#
#-------------------------------------------------------------------------
# Project metadata
#-------------------------------------------------------------------------
m4_define( proj_name, [RISC-V ISA Simulator])
m4_define( proj_maintainer, [Andrew Waterman])
m4_define( proj_abbreviation, [spike])
#-------------------------------------------------------------------------
# Project version information
#-------------------------------------------------------------------------
# Version information is meant to be managed through a version control
# system's tags and revision numbers. In a working copy the version will
# not be defined here (you should just use the version control system's
# mechanisms). When we make a distribution then we can set the version
# here as formed by the scripts/vcs-version.sh script so that the
# distribution knows what version it came from. If you are not using
# version control then it is fine to set this directly.
m4_define( proj_version, [?])
#-------------------------------------------------------------------------
# Setup
#-------------------------------------------------------------------------
AC_INIT(proj_name,proj_version,proj_maintainer,proj_abbreviation)
AC_LANG_CPLUSPLUS
AC_CONFIG_SRCDIR([riscv/common.h])
AC_CONFIG_AUX_DIR([scripts])
AC_CANONICAL_BUILD
AC_CANONICAL_HOST
#-------------------------------------------------------------------------
# Checks for programs
#-------------------------------------------------------------------------
AC_PROG_CC
AC_PROG_CXX
AC_CHECK_TOOL([AR],[ar])
AC_CHECK_TOOL([RANLIB],[ranlib])
AC_PATH_PROG([DTC],[dtc],[no])
AS_IF([test x"$DTC" == xno],AC_MSG_ERROR([device-tree-compiler not found]))
AC_DEFINE_UNQUOTED(DTC, ["$DTC"], [Path to the device-tree-compiler])
AC_C_BIGENDIAN(AC_MSG_ERROR([Spike requires a little-endian host]))
#-------------------------------------------------------------------------
# MCPPBS specific program checks
#-------------------------------------------------------------------------
# These macros check to see if we can do a stow-based install and also
# check for an isa simulator suitable for running the unit test programs
# via the makefile.
MCPPBS_PROG_INSTALL
#-------------------------------------------------------------------------
# Checks for header files
#-------------------------------------------------------------------------
AC_HEADER_STDC
#-------------------------------------------------------------------------
# Default compiler flags
#-------------------------------------------------------------------------
AC_SUBST([CFLAGS], ["-Wall -Wno-unused -g -O2"])
AC_SUBST([CXXFLAGS],["-Wall -Wno-unused -g -O2 -std=c++11"])
#-------------------------------------------------------------------------
# MCPPBS subproject list
#-------------------------------------------------------------------------
# Order list so that subprojects only depend on those listed earlier.
# The '*' suffix indicates an optional subproject. The '**' suffix
# indicates an optional subproject which is also the name of a group.
MCPPBS_SUBPROJECTS([ riscv, dummy_rocc, softfloat, spike_main ])
#-------------------------------------------------------------------------
# MCPPBS subproject groups
#-------------------------------------------------------------------------
# If a group has the same name as a subproject then you must add the
# '**' suffix in the subproject list above. The list of subprojects in a
# group should be ordered so that subprojets only depend on those listed
# earlier. Here is an example:
#
# MCPPBS_GROUP( [group-name], [sproja,sprojb,...] )
#
#-------------------------------------------------------------------------
# Output
#-------------------------------------------------------------------------
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile])
AC_CONFIG_FILES([riscv-spike.pc])
AC_CONFIG_FILES([riscv-riscv.pc])
AC_CONFIG_FILES([riscv-softfloat.pc])
AC_CONFIG_FILES([riscv-dummy_rocc.pc])
AC_CONFIG_FILES([riscv-spike_main.pc])
AC_OUTPUT

View file

@ -1,5 +0,0 @@
/debug_rom
/debug_rom32
/debug_rom64
/debug_rom32.h
/debug_rom64.h

View file

@ -1,24 +0,0 @@
# Recursive make is bad, but in this case we're cross compiling which is a
# pretty unusual use case.
CC = $(RISCV)/bin/riscv64-unknown-elf-gcc
OBJCOPY = $(RISCV)/bin/riscv64-unknown-elf-objcopy
COMPILE = $(CC) -nostdlib -nostartfiles -I.. -Tlink.ld
ELFS = debug_rom
DEPS = debug_rom.S link.ld ../riscv/debug_rom_defines.h ../riscv/encoding.h
all: $(patsubst %,%.h,$(ELFS))
%.h: %.raw
xxd -i $^ | sed "s/^unsigned/static const unsigned/" > $@
%.raw: %
$(OBJCOPY) -O binary --only-section .text $^ $@
debug_rom: $(DEPS)
$(COMPILE) -o $@ $^
clean:
rm -f $(ELFS) debug_rom*.raw debug_rom.h

View file

@ -1,72 +0,0 @@
// See LICENSE.SiFive for license details.
#include "riscv/encoding.h"
#include "riscv/debug_rom_defines.h"
.option norvc
.global entry
.global exception
// Entry location on ebreak, Halt, or Breakpoint
// It is the same for all harts. They branch when
// their GO or RESUME bit is set.
entry:
jal zero, _entry
resume:
jal zero, _resume
exception:
jal zero, _exception
_entry:
// This fence is required because the execution may have written something
// into the Abstract Data or Program Buffer registers.
fence
csrw CSR_DSCRATCH, s0 // Save s0 to allow signaling MHARTID
// We continue to let the hart know that we are halted in order that
// a DM which was reset is still made aware that a hart is halted.
// We keep checking both whether there is something the debugger wants
// us to do, or whether we should resume.
entry_loop:
csrr s0, CSR_MHARTID
sw s0, DEBUG_ROM_HALTED(zero)
lbu s0, DEBUG_ROM_FLAGS(s0) // 1 byte flag per hart. Only one hart advances here.
andi s0, s0, (1 << DEBUG_ROM_FLAG_GO)
bnez s0, going
csrr s0, CSR_MHARTID
lbu s0, DEBUG_ROM_FLAGS(s0) // multiple harts can resume here
andi s0, s0, (1 << DEBUG_ROM_FLAG_RESUME)
bnez s0, resume
jal zero, entry_loop
_exception:
sw zero, DEBUG_ROM_EXCEPTION(zero) // Let debug module know you got an exception.
ebreak
going:
csrr s0, CSR_DSCRATCH // Restore s0 here
sw zero, DEBUG_ROM_GOING(zero) // When debug module sees this write, the GO flag is reset.
fence
fence.i
jalr zero, zero, %lo(whereto) // Debug module will put different instructions and data in the RAM,
// so we use fence and fence.i for safety. (rocket-chip doesn't have this
// because jalr is special there)
_resume:
csrr s0, CSR_MHARTID
sw s0, DEBUG_ROM_RESUMING(zero) // When Debug Module sees this write, the RESUME flag is reset.
csrr s0, CSR_DSCRATCH // Restore s0
dret
// END OF ACTUAL "ROM" CONTENTS. BELOW IS JUST FOR LINKER SCRIPT.
.section .whereto
whereto:
nop
// Variable "ROM" This is : jal x0 abstract, jal x0 program_buffer,
// or jal x0 resume, as desired.
// Debug Module state machine tracks what is 'desired'.
// We don't need/want to use jalr here because all of the
// Variable ROM contents are set by
// Debug Module before setting the OK_GO byte.

View file

@ -1,12 +0,0 @@
static const unsigned char debug_rom_raw[] = {
0x6f, 0x00, 0xc0, 0x00, 0x6f, 0x00, 0x40, 0x05, 0x6f, 0x00, 0x40, 0x03,
0x0f, 0x00, 0xf0, 0x0f, 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x40, 0xf1,
0x23, 0x20, 0x80, 0x10, 0x03, 0x44, 0x04, 0x40, 0x13, 0x74, 0x14, 0x00,
0x63, 0x10, 0x04, 0x02, 0x73, 0x24, 0x40, 0xf1, 0x03, 0x44, 0x04, 0x40,
0x13, 0x74, 0x24, 0x00, 0xe3, 0x18, 0x04, 0xfc, 0x6f, 0xf0, 0xdf, 0xfd,
0x23, 0x26, 0x00, 0x10, 0x73, 0x00, 0x10, 0x00, 0x73, 0x24, 0x20, 0x7b,
0x23, 0x22, 0x00, 0x10, 0x0f, 0x00, 0xf0, 0x0f, 0x0f, 0x10, 0x00, 0x00,
0x67, 0x00, 0x00, 0x30, 0x73, 0x24, 0x40, 0xf1, 0x23, 0x24, 0x80, 0x10,
0x73, 0x24, 0x20, 0x7b, 0x73, 0x00, 0x20, 0x7b
};
static const unsigned int debug_rom_raw_len = 104;

View file

@ -1,15 +0,0 @@
OUTPUT_ARCH( "riscv" )
ENTRY( entry )
SECTIONS
{
.whereto 0x300 :
{
*(.whereto)
}
. = 0x800;
.text :
{
*(.text)
}
_end = .;
}

View file

@ -1,47 +0,0 @@
#include "rocc.h"
#include "mmu.h"
#include <cstring>
class dummy_rocc_t : public rocc_t
{
public:
const char* name() { return "dummy_rocc"; }
reg_t custom0(rocc_insn_t insn, reg_t xs1, reg_t xs2)
{
reg_t prev_acc = acc[insn.rs2];
if (insn.rs2 >= num_acc)
illegal_instruction();
switch (insn.funct)
{
case 0: // acc <- xs1
acc[insn.rs2] = xs1;
break;
case 1: // xd <- acc (the only real work is the return statement below)
break;
case 2: // acc[rs2] <- Mem[xs1]
acc[insn.rs2] = p->get_mmu()->load_uint64(xs1);
break;
case 3: // acc[rs2] <- accX + xs1
acc[insn.rs2] += xs1;
break;
default:
illegal_instruction();
}
return prev_acc; // in all cases, xd <- previous value of acc[rs2]
}
dummy_rocc_t()
{
memset(acc, 0, sizeof(acc));
}
private:
static const int num_acc = 4;
reg_t acc[num_acc];
};
REGISTER_EXTENSION(dummy_rocc, []() { return new dummy_rocc_t; })

View file

@ -1,7 +0,0 @@
dummy_rocc_subproject_deps = \
spike_main \
riscv \
softfloat \
dummy_rocc_srcs = \
dummy_rocc.cc \

View file

@ -1,29 +0,0 @@
// The following is a RISC-V program to test the functionality of the
// dummy RoCC accelerator.
// Compile with riscv64-unknown-elf-gcc dummy_rocc_test.c
// Run with spike --extension=dummy_rocc pk a.out
#include <assert.h>
#include <stdio.h>
#include <stdint.h>
int main() {
uint64_t x = 123, y = 456, z = 0;
// load x into accumulator 2 (funct=0)
asm volatile ("custom0 x0, %0, 2, 0" : : "r"(x));
// read it back into z (funct=1) to verify it
asm volatile ("custom0 %0, x0, 2, 1" : "=r"(z));
assert(z == x);
// accumulate 456 into it (funct=3)
asm volatile ("custom0 x0, %0, 2, 3" : : "r"(y));
// verify it
asm volatile ("custom0 %0, x0, 2, 1" : "=r"(z));
assert(z == x+y);
// do it all again, but initialize acc2 via memory this time (funct=2)
asm volatile ("custom0 x0, %0, 2, 2" : : "r"(&x));
asm volatile ("custom0 x0, %0, 2, 3" : : "r"(y));
asm volatile ("custom0 %0, x0, 2, 1" : "=r"(z));
assert(z == x+y);
printf("success!\n");
}

View file

@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=@prefix@
libdir=${prefix}/@libdir@
includedir=${prefix}/@includedir@
Name: riscv-dummy_rocc
Description: Example RISC-V ROCC accelerator
Version: git
Libs: -Wl,-rpath,${libdir} -L${libdir} -ldummy_rocc
Cflags: -I${includedir}
URL: http://riscv.org/download.html#tab_spike

View file

@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=@prefix@
libdir=${prefix}/@libdir@
includedir=${prefix}/@includedir@
Name: riscv-riscv
Description: RISC-V
Version: git
Libs: -Wl,-rpath,${libdir} -L${libdir} -lriscv
Cflags: -I${includedir}
URL: http://riscv.org/download.html#tab_spike

View file

@ -1,11 +0,0 @@
prefix=@prefix@
exec_prefix=@prefix@
libdir=${prefix}/@libdir@
includedir=${prefix}/@includedir@
Name: riscv-softfloat
Description: RISC-V softfloat library
Version: git
Libs: -Wl,-rpath,${libdir} -L${libdir} -lsoftfloat
Cflags: -I${includedir}
URL: http://riscv.org/download.html#tab_spike

View file

@ -1,10 +0,0 @@
prefix=@prefix@
exec_prefix=@prefix@
libdir=${prefix}/@libdir@
includedir=${prefix}/@includedir@
Name: riscv-spike
Description: RISC-V spike meta library
Version: git
Depends: riscv-spike_main riscv-riscv riscv-softfloat
URL: http://riscv.org/download.html#tab_spike

View file

@ -1,12 +0,0 @@
prefix=@prefix@
exec_prefix=@prefix@
libdir=${prefix}/@libdir@
includedir=${prefix}/@includedir@
Name: riscv-spike_main
Description: RISC-V ISA simulator library
Version: git
Depends: riscv-riscv riscv-softfloat
Libs: -Wl,-rpath,${libdir} -L${libdir} -lspike_main
Cflags: -I${includedir}
URL: http://riscv.org/download.html#tab_spike

View file

@ -1,185 +0,0 @@
// See LICENSE for license details.
#include "cachesim.h"
#include "common.h"
#include <cstdlib>
#include <iostream>
#include <iomanip>
cache_sim_t::cache_sim_t(size_t _sets, size_t _ways, size_t _linesz, const char* _name)
: sets(_sets), ways(_ways), linesz(_linesz), name(_name), log(false)
{
init();
}
static void help()
{
std::cerr << "Cache configurations must be of the form" << std::endl;
std::cerr << " sets:ways:blocksize" << std::endl;
std::cerr << "where sets, ways, and blocksize are positive integers, with" << std::endl;
std::cerr << "sets and blocksize both powers of two and blocksize at least 8." << std::endl;
exit(1);
}
cache_sim_t* cache_sim_t::construct(const char* config, const char* name)
{
const char* wp = strchr(config, ':');
if (!wp++) help();
const char* bp = strchr(wp, ':');
if (!bp++) help();
size_t sets = atoi(std::string(config, wp).c_str());
size_t ways = atoi(std::string(wp, bp).c_str());
size_t linesz = atoi(bp);
if (ways > 4 /* empirical */ && sets == 1)
return new fa_cache_sim_t(ways, linesz, name);
return new cache_sim_t(sets, ways, linesz, name);
}
void cache_sim_t::init()
{
if(sets == 0 || (sets & (sets-1)))
help();
if(linesz < 8 || (linesz & (linesz-1)))
help();
idx_shift = 0;
for (size_t x = linesz; x>1; x >>= 1)
idx_shift++;
tags = new uint64_t[sets*ways]();
read_accesses = 0;
read_misses = 0;
bytes_read = 0;
write_accesses = 0;
write_misses = 0;
bytes_written = 0;
writebacks = 0;
miss_handler = NULL;
}
cache_sim_t::cache_sim_t(const cache_sim_t& rhs)
: sets(rhs.sets), ways(rhs.ways), linesz(rhs.linesz),
idx_shift(rhs.idx_shift), name(rhs.name), log(false)
{
tags = new uint64_t[sets*ways];
memcpy(tags, rhs.tags, sets*ways*sizeof(uint64_t));
}
cache_sim_t::~cache_sim_t()
{
print_stats();
delete [] tags;
}
void cache_sim_t::print_stats()
{
if(read_accesses + write_accesses == 0)
return;
float mr = 100.0f*(read_misses+write_misses)/(read_accesses+write_accesses);
std::cout << std::setprecision(3) << std::fixed;
std::cout << name << " ";
std::cout << "Bytes Read: " << bytes_read << std::endl;
std::cout << name << " ";
std::cout << "Bytes Written: " << bytes_written << std::endl;
std::cout << name << " ";
std::cout << "Read Accesses: " << read_accesses << std::endl;
std::cout << name << " ";
std::cout << "Write Accesses: " << write_accesses << std::endl;
std::cout << name << " ";
std::cout << "Read Misses: " << read_misses << std::endl;
std::cout << name << " ";
std::cout << "Write Misses: " << write_misses << std::endl;
std::cout << name << " ";
std::cout << "Writebacks: " << writebacks << std::endl;
std::cout << name << " ";
std::cout << "Miss Rate: " << mr << '%' << std::endl;
}
uint64_t* cache_sim_t::check_tag(uint64_t addr)
{
size_t idx = (addr >> idx_shift) & (sets-1);
size_t tag = (addr >> idx_shift) | VALID;
for (size_t i = 0; i < ways; i++)
if (tag == (tags[idx*ways + i] & ~DIRTY))
return &tags[idx*ways + i];
return NULL;
}
uint64_t cache_sim_t::victimize(uint64_t addr)
{
size_t idx = (addr >> idx_shift) & (sets-1);
size_t way = lfsr.next() % ways;
uint64_t victim = tags[idx*ways + way];
tags[idx*ways + way] = (addr >> idx_shift) | VALID;
return victim;
}
void cache_sim_t::access(uint64_t addr, size_t bytes, bool store)
{
store ? write_accesses++ : read_accesses++;
(store ? bytes_written : bytes_read) += bytes;
uint64_t* hit_way = check_tag(addr);
if (likely(hit_way != NULL))
{
if (store)
*hit_way |= DIRTY;
return;
}
store ? write_misses++ : read_misses++;
if (log)
{
std::cerr << name << " "
<< (store ? "write" : "read") << " miss 0x"
<< std::hex << addr << std::endl;
}
uint64_t victim = victimize(addr);
if ((victim & (VALID | DIRTY)) == (VALID | DIRTY))
{
uint64_t dirty_addr = (victim & ~(VALID | DIRTY)) << idx_shift;
if (miss_handler)
miss_handler->access(dirty_addr, linesz, true);
writebacks++;
}
if (miss_handler)
miss_handler->access(addr & ~(linesz-1), linesz, false);
if (store)
*check_tag(addr) |= DIRTY;
}
fa_cache_sim_t::fa_cache_sim_t(size_t ways, size_t linesz, const char* name)
: cache_sim_t(1, ways, linesz, name)
{
}
uint64_t* fa_cache_sim_t::check_tag(uint64_t addr)
{
auto it = tags.find(addr >> idx_shift);
return it == tags.end() ? NULL : &it->second;
}
uint64_t fa_cache_sim_t::victimize(uint64_t addr)
{
uint64_t old_tag = 0;
if (tags.size() == ways)
{
auto it = tags.begin();
std::advance(it, lfsr.next() % ways);
old_tag = it->second;
tags.erase(it);
}
tags[addr >> idx_shift] = (addr >> idx_shift) | VALID;
return old_tag;
}

View file

@ -1,130 +0,0 @@
// See LICENSE for license details.
#ifndef _RISCV_CACHE_SIM_H
#define _RISCV_CACHE_SIM_H
#include "memtracer.h"
#include <cstring>
#include <string>
#include <map>
#include <cstdint>
class lfsr_t
{
public:
lfsr_t() : reg(1) {}
lfsr_t(const lfsr_t& lfsr) : reg(lfsr.reg) {}
uint32_t next() { return reg = (reg>>1)^(-(reg&1) & 0xd0000001); }
private:
uint32_t reg;
};
class cache_sim_t
{
public:
cache_sim_t(size_t sets, size_t ways, size_t linesz, const char* name);
cache_sim_t(const cache_sim_t& rhs);
virtual ~cache_sim_t();
void access(uint64_t addr, size_t bytes, bool store);
void print_stats();
void set_miss_handler(cache_sim_t* mh) { miss_handler = mh; }
void set_log(bool _log) { log = _log; }
static cache_sim_t* construct(const char* config, const char* name);
protected:
static const uint64_t VALID = 1ULL << 63;
static const uint64_t DIRTY = 1ULL << 62;
virtual uint64_t* check_tag(uint64_t addr);
virtual uint64_t victimize(uint64_t addr);
lfsr_t lfsr;
cache_sim_t* miss_handler;
size_t sets;
size_t ways;
size_t linesz;
size_t idx_shift;
uint64_t* tags;
uint64_t read_accesses;
uint64_t read_misses;
uint64_t bytes_read;
uint64_t write_accesses;
uint64_t write_misses;
uint64_t bytes_written;
uint64_t writebacks;
std::string name;
bool log;
void init();
};
class fa_cache_sim_t : public cache_sim_t
{
public:
fa_cache_sim_t(size_t ways, size_t linesz, const char* name);
uint64_t* check_tag(uint64_t addr);
uint64_t victimize(uint64_t addr);
private:
static bool cmp(uint64_t a, uint64_t b);
std::map<uint64_t, uint64_t> tags;
};
class cache_memtracer_t : public memtracer_t
{
public:
cache_memtracer_t(const char* config, const char* name)
{
cache = cache_sim_t::construct(config, name);
}
~cache_memtracer_t()
{
delete cache;
}
void set_miss_handler(cache_sim_t* mh)
{
cache->set_miss_handler(mh);
}
void set_log(bool log)
{
cache->set_log(log);
}
protected:
cache_sim_t* cache;
};
class icache_sim_t : public cache_memtracer_t
{
public:
icache_sim_t(const char* config) : cache_memtracer_t(config, "I$") {}
bool interested_in_range(uint64_t begin, uint64_t end, access_type type)
{
return type == FETCH;
}
void trace(uint64_t addr, size_t bytes, access_type type)
{
if (type == FETCH) cache->access(addr, bytes, false);
}
};
class dcache_sim_t : public cache_memtracer_t
{
public:
dcache_sim_t(const char* config) : cache_memtracer_t(config, "D$") {}
bool interested_in_range(uint64_t begin, uint64_t end, access_type type)
{
return type == LOAD || type == STORE;
}
void trace(uint64_t addr, size_t bytes, access_type type)
{
if (type == LOAD || type == STORE) cache->access(addr, bytes, type == STORE);
}
};
#endif

View file

@ -1,76 +0,0 @@
#include "devices.h"
#include "processor.h"
clint_t::clint_t(std::vector<processor_t*>& procs)
: procs(procs), mtimecmp(procs.size())
{
}
/* 0000 msip hart 0
* 0004 msip hart 1
* 4000 mtimecmp hart 0 lo
* 4004 mtimecmp hart 0 hi
* 4008 mtimecmp hart 1 lo
* 400c mtimecmp hart 1 hi
* bff8 mtime lo
* bffc mtime hi
*/
#define MSIP_BASE 0x0
#define MTIMECMP_BASE 0x4000
#define MTIME_BASE 0xbff8
void clint_t::reset() {
mtime = 0;
}
bool clint_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
if (addr >= MSIP_BASE && addr + len <= MSIP_BASE + procs.size()*sizeof(msip_t)) {
std::vector<msip_t> msip(procs.size());
for (size_t i = 0; i < procs.size(); ++i)
msip[i] = !!(procs[i]->state.mip & MIP_MSIP);
memcpy(bytes, (uint8_t*)&msip[0] + addr - MSIP_BASE, len);
} else if (addr >= MTIMECMP_BASE && addr + len <= MTIMECMP_BASE + procs.size()*sizeof(mtimecmp_t)) {
memcpy(bytes, (uint8_t*)&mtimecmp[0] + addr - MTIMECMP_BASE, len);
} else if (addr >= MTIME_BASE && addr + len <= MTIME_BASE + sizeof(mtime_t)) {
memcpy(bytes, (uint8_t*)&mtime + addr - MTIME_BASE, len);
} else {
return false;
}
return true;
}
bool clint_t::store(reg_t addr, size_t len, const uint8_t* bytes)
{
if (addr >= MSIP_BASE && addr + len <= MSIP_BASE + procs.size()*sizeof(msip_t)) {
std::vector<msip_t> msip(procs.size());
std::vector<msip_t> mask(procs.size(), 0);
memcpy((uint8_t*)&msip[0] + addr - MSIP_BASE, bytes, len);
memset((uint8_t*)&mask[0] + addr - MSIP_BASE, 0xff, len);
for (size_t i = 0; i < procs.size(); ++i) {
if (!(mask[i] & 0xFF)) continue;
procs[i]->state.mip &= ~MIP_MSIP;
if (!!(msip[i] & 1))
procs[i]->state.mip |= MIP_MSIP;
}
} else if (addr >= MTIMECMP_BASE && addr + len <= MTIMECMP_BASE + procs.size()*sizeof(mtimecmp_t)) {
memcpy((uint8_t*)&mtimecmp[0] + addr - MTIMECMP_BASE, bytes, len);
} else if (addr >= MTIME_BASE && addr + len <= MTIME_BASE + sizeof(mtime_t)) {
memcpy((uint8_t*)&mtime + addr - MTIME_BASE, bytes, len);
} else {
return false;
}
increment(0);
return true;
}
void clint_t::increment(reg_t inc)
{
mtime += inc;
for (size_t i = 0; i < procs.size(); i++) {
procs[i]->state.mip &= ~MIP_MTIP;
if (mtime >= mtimecmp[i])
procs[i]->state.mip |= MIP_MTIP;
}
}

View file

@ -1,9 +0,0 @@
// See LICENSE for license details.
#ifndef _RISCV_COMMON_H
#define _RISCV_COMMON_H
#define likely(x) __builtin_expect(x, 1)
#define unlikely(x) __builtin_expect(x, 0)
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,790 +0,0 @@
#include <cassert>
#include "debug_module.h"
#include "debug_defines.h"
#include "opcodes.h"
#include "mmu.h"
#include "sim.h"
#include "debug_rom/debug_rom.h"
#include "debug_rom_defines.h"
#if 0
# define D(x) x
#else
# define D(x)
#endif
///////////////////////// debug_module_t
debug_module_t::debug_module_t(sim_t *sim, unsigned progbufsize, unsigned max_bus_master_bits,
bool require_authentication) :
progbufsize(progbufsize),
program_buffer_bytes(4 + 4*progbufsize),
max_bus_master_bits(max_bus_master_bits),
require_authentication(require_authentication),
debug_progbuf_start(debug_data_start - program_buffer_bytes),
debug_abstract_start(debug_progbuf_start - debug_abstract_size*4),
custom_base(0),
sim(sim)
{
D(fprintf(stderr, "debug_data_start=0x%x\n", debug_data_start));
D(fprintf(stderr, "debug_progbuf_start=0x%x\n", debug_progbuf_start));
D(fprintf(stderr, "debug_abstract_start=0x%x\n", debug_abstract_start));
program_buffer = new uint8_t[program_buffer_bytes];
memset(halted, 0, sizeof(halted));
memset(debug_rom_flags, 0, sizeof(debug_rom_flags));
memset(resumeack, 0, sizeof(resumeack));
memset(havereset, 0, sizeof(havereset));
memset(program_buffer, 0, program_buffer_bytes);
program_buffer[4*progbufsize] = ebreak();
program_buffer[4*progbufsize+1] = ebreak() >> 8;
program_buffer[4*progbufsize+2] = ebreak() >> 16;
program_buffer[4*progbufsize+3] = ebreak() >> 24;
memset(dmdata, 0, sizeof(dmdata));
write32(debug_rom_whereto, 0,
jal(ZERO, debug_abstract_start - DEBUG_ROM_WHERETO));
memset(debug_abstract, 0, sizeof(debug_abstract));
reset();
}
debug_module_t::~debug_module_t()
{
delete[] program_buffer;
}
void debug_module_t::reset()
{
for (unsigned i = 0; i < sim->nprocs(); i++) {
processor_t *proc = sim->get_core(i);
if (proc)
proc->halt_request = false;
}
dmcontrol = {0};
dmstatus = {0};
dmstatus.impebreak = true;
dmstatus.authenticated = !require_authentication;
dmstatus.version = 2;
abstractcs = {0};
abstractcs.datacount = sizeof(dmdata) / 4;
abstractcs.progbufsize = progbufsize;
abstractauto = {0};
sbcs = {0};
if (max_bus_master_bits > 0) {
sbcs.version = 1;
sbcs.asize = sizeof(reg_t) * 8;
}
if (max_bus_master_bits >= 64)
sbcs.access64 = true;
if (max_bus_master_bits >= 32)
sbcs.access32 = true;
if (max_bus_master_bits >= 16)
sbcs.access16 = true;
if (max_bus_master_bits >= 8)
sbcs.access8 = true;
challenge = random();
}
void debug_module_t::add_device(bus_t *bus) {
bus->add_device(DEBUG_START, this);
}
bool debug_module_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
addr = DEBUG_START + addr;
if (addr >= DEBUG_ROM_ENTRY &&
(addr + len) <= (DEBUG_ROM_ENTRY + debug_rom_raw_len)) {
memcpy(bytes, debug_rom_raw + addr - DEBUG_ROM_ENTRY, len);
return true;
}
if (addr >= DEBUG_ROM_WHERETO && (addr + len) <= (DEBUG_ROM_WHERETO + 4)) {
memcpy(bytes, debug_rom_whereto + addr - DEBUG_ROM_WHERETO, len);
return true;
}
if (addr >= DEBUG_ROM_FLAGS && ((addr + len) <= DEBUG_ROM_FLAGS + 1024)) {
memcpy(bytes, debug_rom_flags + addr - DEBUG_ROM_FLAGS, len);
return true;
}
if (addr >= debug_abstract_start && ((addr + len) <= (debug_abstract_start + sizeof(debug_abstract)))) {
memcpy(bytes, debug_abstract + addr - debug_abstract_start, len);
return true;
}
if (addr >= debug_data_start && (addr + len) <= (debug_data_start + sizeof(dmdata))) {
memcpy(bytes, dmdata + addr - debug_data_start, len);
return true;
}
if (addr >= debug_progbuf_start && ((addr + len) <= (debug_progbuf_start + program_buffer_bytes))) {
memcpy(bytes, program_buffer + addr - debug_progbuf_start, len);
return true;
}
fprintf(stderr, "ERROR: invalid load from debug module: %zd bytes at 0x%016"
PRIx64 "\n", len, addr);
return false;
}
bool debug_module_t::store(reg_t addr, size_t len, const uint8_t* bytes)
{
D(
switch (len) {
case 4:
fprintf(stderr, "store(addr=0x%lx, len=%d, bytes=0x%08x); "
"hartsel=0x%x\n", addr, (unsigned) len, *(uint32_t *) bytes,
dmcontrol.hartsel);
break;
default:
fprintf(stderr, "store(addr=0x%lx, len=%d, bytes=...); "
"hartsel=0x%x\n", addr, (unsigned) len, dmcontrol.hartsel);
break;
}
);
uint8_t id_bytes[4];
uint32_t id = 0;
if (len == 4) {
memcpy(id_bytes, bytes, 4);
id = read32(id_bytes, 0);
}
addr = DEBUG_START + addr;
if (addr >= debug_data_start && (addr + len) <= (debug_data_start + sizeof(dmdata))) {
memcpy(dmdata + addr - debug_data_start, bytes, len);
return true;
}
if (addr >= debug_progbuf_start && ((addr + len) <= (debug_progbuf_start + program_buffer_bytes))) {
memcpy(program_buffer + addr - debug_progbuf_start, bytes, len);
return true;
}
if (addr == DEBUG_ROM_HALTED) {
assert (len == 4);
halted[id] = true;
if (dmcontrol.hartsel == id) {
if (0 == (debug_rom_flags[id] & (1 << DEBUG_ROM_FLAG_GO))){
if (dmcontrol.hartsel == id) {
abstractcs.busy = false;
}
}
}
return true;
}
if (addr == DEBUG_ROM_GOING) {
debug_rom_flags[dmcontrol.hartsel] &= ~(1 << DEBUG_ROM_FLAG_GO);
return true;
}
if (addr == DEBUG_ROM_RESUMING) {
assert (len == 4);
halted[id] = false;
resumeack[id] = true;
debug_rom_flags[id] &= ~(1 << DEBUG_ROM_FLAG_RESUME);
return true;
}
if (addr == DEBUG_ROM_EXCEPTION) {
if (abstractcs.cmderr == CMDERR_NONE) {
abstractcs.cmderr = CMDERR_EXCEPTION;
}
return true;
}
fprintf(stderr, "ERROR: invalid store to debug module: %zd bytes at 0x%016"
PRIx64 "\n", len, addr);
return false;
}
void debug_module_t::write32(uint8_t *memory, unsigned int index, uint32_t value)
{
uint8_t* base = memory + index * 4;
base[0] = value & 0xff;
base[1] = (value >> 8) & 0xff;
base[2] = (value >> 16) & 0xff;
base[3] = (value >> 24) & 0xff;
}
uint32_t debug_module_t::read32(uint8_t *memory, unsigned int index)
{
uint8_t* base = memory + index * 4;
uint32_t value = ((uint32_t) base[0]) |
(((uint32_t) base[1]) << 8) |
(((uint32_t) base[2]) << 16) |
(((uint32_t) base[3]) << 24);
return value;
}
processor_t *debug_module_t::current_proc() const
{
processor_t *proc = NULL;
try {
proc = sim->get_core(dmcontrol.hartsel);
} catch (const std::out_of_range&) {
}
return proc;
}
unsigned debug_module_t::sb_access_bits()
{
return 8 << sbcs.sbaccess;
}
void debug_module_t::sb_autoincrement()
{
if (!sbcs.autoincrement || !max_bus_master_bits)
return;
uint64_t value = sbaddress[0] + sb_access_bits() / 8;
sbaddress[0] = value;
uint32_t carry = value >> 32;
value = sbaddress[1] + carry;
sbaddress[1] = value;
carry = value >> 32;
value = sbaddress[2] + carry;
sbaddress[2] = value;
carry = value >> 32;
sbaddress[3] += carry;
}
void debug_module_t::sb_read()
{
reg_t address = ((uint64_t) sbaddress[1] << 32) | sbaddress[0];
try {
if (sbcs.sbaccess == 0 && max_bus_master_bits >= 8) {
sbdata[0] = sim->debug_mmu->load_uint8(address);
} else if (sbcs.sbaccess == 1 && max_bus_master_bits >= 16) {
sbdata[0] = sim->debug_mmu->load_uint16(address);
} else if (sbcs.sbaccess == 2 && max_bus_master_bits >= 32) {
sbdata[0] = sim->debug_mmu->load_uint32(address);
} else if (sbcs.sbaccess == 3 && max_bus_master_bits >= 64) {
uint64_t value = sim->debug_mmu->load_uint64(address);
sbdata[0] = value;
sbdata[1] = value >> 32;
} else {
sbcs.error = 3;
}
} catch (trap_load_access_fault& t) {
sbcs.error = 2;
}
}
void debug_module_t::sb_write()
{
reg_t address = ((uint64_t) sbaddress[1] << 32) | sbaddress[0];
D(fprintf(stderr, "sb_write() 0x%x @ 0x%lx\n", sbdata[0], address));
if (sbcs.sbaccess == 0 && max_bus_master_bits >= 8) {
sim->debug_mmu->store_uint8(address, sbdata[0]);
} else if (sbcs.sbaccess == 1 && max_bus_master_bits >= 16) {
sim->debug_mmu->store_uint16(address, sbdata[0]);
} else if (sbcs.sbaccess == 2 && max_bus_master_bits >= 32) {
sim->debug_mmu->store_uint32(address, sbdata[0]);
} else if (sbcs.sbaccess == 3 && max_bus_master_bits >= 64) {
sim->debug_mmu->store_uint64(address,
(((uint64_t) sbdata[1]) << 32) | sbdata[0]);
} else {
sbcs.error = 3;
}
}
bool debug_module_t::dmi_read(unsigned address, uint32_t *value)
{
uint32_t result = 0;
D(fprintf(stderr, "dmi_read(0x%x) -> ", address));
if (address >= DMI_DATA0 && address < DMI_DATA0 + abstractcs.datacount) {
unsigned i = address - DMI_DATA0;
result = read32(dmdata, i);
if (abstractcs.busy) {
result = -1;
fprintf(stderr, "\ndmi_read(0x%02x (data[%d]) -> -1 because abstractcs.busy==true\n", address, i);
}
if (abstractcs.busy && abstractcs.cmderr == CMDERR_NONE) {
abstractcs.cmderr = CMDERR_BUSY;
}
if (!abstractcs.busy && ((abstractauto.autoexecdata >> i) & 1)) {
perform_abstract_command();
}
} else if (address >= DMI_PROGBUF0 && address < DMI_PROGBUF0 + progbufsize) {
unsigned i = address - DMI_PROGBUF0;
result = read32(program_buffer, i);
if (abstractcs.busy) {
result = -1;
fprintf(stderr, "\ndmi_read(0x%02x (progbuf[%d]) -> -1 because abstractcs.busy==true\n", address, i);
}
if (!abstractcs.busy && ((abstractauto.autoexecprogbuf >> i) & 1)) {
perform_abstract_command();
}
} else {
switch (address) {
case DMI_DMCONTROL:
{
processor_t *proc = current_proc();
if (proc)
dmcontrol.haltreq = proc->halt_request;
result = set_field(result, DMI_DMCONTROL_HALTREQ, dmcontrol.haltreq);
result = set_field(result, DMI_DMCONTROL_RESUMEREQ, dmcontrol.resumereq);
result = set_field(result, DMI_DMCONTROL_HARTSELHI,
dmcontrol.hartsel >> DMI_DMCONTROL_HARTSELLO_LENGTH);
result = set_field(result, DMI_DMCONTROL_HARTSELLO, dmcontrol.hartsel);
result = set_field(result, DMI_DMCONTROL_HARTRESET, dmcontrol.hartreset);
result = set_field(result, DMI_DMCONTROL_NDMRESET, dmcontrol.ndmreset);
result = set_field(result, DMI_DMCONTROL_DMACTIVE, dmcontrol.dmactive);
}
break;
case DMI_DMSTATUS:
{
processor_t *proc = current_proc();
dmstatus.allnonexistant = false;
dmstatus.allunavail = false;
dmstatus.allrunning = false;
dmstatus.allhalted = false;
dmstatus.allresumeack = false;
if (proc) {
if (halted[dmcontrol.hartsel]) {
dmstatus.allhalted = true;
} else {
dmstatus.allrunning = true;
}
} else {
dmstatus.allnonexistant = true;
}
dmstatus.anynonexistant = dmstatus.allnonexistant;
dmstatus.anyunavail = dmstatus.allunavail;
dmstatus.anyrunning = dmstatus.allrunning;
dmstatus.anyhalted = dmstatus.allhalted;
if (proc) {
if (resumeack[dmcontrol.hartsel]) {
dmstatus.allresumeack = true;
} else {
dmstatus.allresumeack = false;
}
} else {
dmstatus.allresumeack = false;
}
result = set_field(result, DMI_DMSTATUS_IMPEBREAK,
dmstatus.impebreak);
result = set_field(result, DMI_DMSTATUS_ALLHAVERESET,
havereset[dmcontrol.hartsel]);
result = set_field(result, DMI_DMSTATUS_ANYHAVERESET,
havereset[dmcontrol.hartsel]);
result = set_field(result, DMI_DMSTATUS_ALLNONEXISTENT, dmstatus.allnonexistant);
result = set_field(result, DMI_DMSTATUS_ALLUNAVAIL, dmstatus.allunavail);
result = set_field(result, DMI_DMSTATUS_ALLRUNNING, dmstatus.allrunning);
result = set_field(result, DMI_DMSTATUS_ALLHALTED, dmstatus.allhalted);
result = set_field(result, DMI_DMSTATUS_ALLRESUMEACK, dmstatus.allresumeack);
result = set_field(result, DMI_DMSTATUS_ANYNONEXISTENT, dmstatus.anynonexistant);
result = set_field(result, DMI_DMSTATUS_ANYUNAVAIL, dmstatus.anyunavail);
result = set_field(result, DMI_DMSTATUS_ANYRUNNING, dmstatus.anyrunning);
result = set_field(result, DMI_DMSTATUS_ANYHALTED, dmstatus.anyhalted);
result = set_field(result, DMI_DMSTATUS_ANYRESUMEACK, dmstatus.anyresumeack);
result = set_field(result, DMI_DMSTATUS_AUTHENTICATED, dmstatus.authenticated);
result = set_field(result, DMI_DMSTATUS_AUTHBUSY, dmstatus.authbusy);
result = set_field(result, DMI_DMSTATUS_VERSION, dmstatus.version);
}
break;
case DMI_ABSTRACTCS:
result = set_field(result, DMI_ABSTRACTCS_CMDERR, abstractcs.cmderr);
result = set_field(result, DMI_ABSTRACTCS_BUSY, abstractcs.busy);
result = set_field(result, DMI_ABSTRACTCS_DATACOUNT, abstractcs.datacount);
result = set_field(result, DMI_ABSTRACTCS_PROGBUFSIZE,
abstractcs.progbufsize);
break;
case DMI_ABSTRACTAUTO:
result = set_field(result, DMI_ABSTRACTAUTO_AUTOEXECPROGBUF, abstractauto.autoexecprogbuf);
result = set_field(result, DMI_ABSTRACTAUTO_AUTOEXECDATA, abstractauto.autoexecdata);
break;
case DMI_COMMAND:
result = 0;
break;
case DMI_HARTINFO:
result = set_field(result, DMI_HARTINFO_NSCRATCH, 1);
result = set_field(result, DMI_HARTINFO_DATAACCESS, 1);
result = set_field(result, DMI_HARTINFO_DATASIZE, abstractcs.datacount);
result = set_field(result, DMI_HARTINFO_DATAADDR, debug_data_start);
break;
case DMI_SBCS:
result = set_field(result, DMI_SBCS_SBVERSION, sbcs.version);
result = set_field(result, DMI_SBCS_SBREADONADDR, sbcs.readonaddr);
result = set_field(result, DMI_SBCS_SBACCESS, sbcs.sbaccess);
result = set_field(result, DMI_SBCS_SBAUTOINCREMENT, sbcs.autoincrement);
result = set_field(result, DMI_SBCS_SBREADONDATA, sbcs.readondata);
result = set_field(result, DMI_SBCS_SBERROR, sbcs.error);
result = set_field(result, DMI_SBCS_SBASIZE, sbcs.asize);
result = set_field(result, DMI_SBCS_SBACCESS128, sbcs.access128);
result = set_field(result, DMI_SBCS_SBACCESS64, sbcs.access64);
result = set_field(result, DMI_SBCS_SBACCESS32, sbcs.access32);
result = set_field(result, DMI_SBCS_SBACCESS16, sbcs.access16);
result = set_field(result, DMI_SBCS_SBACCESS8, sbcs.access8);
break;
case DMI_SBADDRESS0:
result = sbaddress[0];
break;
case DMI_SBADDRESS1:
result = sbaddress[1];
break;
case DMI_SBADDRESS2:
result = sbaddress[2];
break;
case DMI_SBADDRESS3:
result = sbaddress[3];
break;
case DMI_SBDATA0:
result = sbdata[0];
if (sbcs.error == 0) {
sb_autoincrement();
if (sbcs.readondata) {
sb_read();
}
}
break;
case DMI_SBDATA1:
result = sbdata[1];
break;
case DMI_SBDATA2:
result = sbdata[2];
break;
case DMI_SBDATA3:
result = sbdata[3];
break;
case DMI_AUTHDATA:
result = challenge;
break;
default:
result = 0;
D(fprintf(stderr, "Unexpected. Returning Error."));
return false;
}
}
D(fprintf(stderr, "0x%x\n", result));
*value = result;
return true;
}
bool debug_module_t::perform_abstract_command()
{
if (abstractcs.cmderr != CMDERR_NONE)
return true;
if (abstractcs.busy) {
abstractcs.cmderr = CMDERR_BUSY;
return true;
}
if ((command >> 24) == 0) {
// register access
unsigned size = get_field(command, AC_ACCESS_REGISTER_SIZE);
bool write = get_field(command, AC_ACCESS_REGISTER_WRITE);
unsigned regno = get_field(command, AC_ACCESS_REGISTER_REGNO);
if (!halted[dmcontrol.hartsel]) {
abstractcs.cmderr = CMDERR_HALTRESUME;
return true;
}
unsigned i = 0;
if (get_field(command, AC_ACCESS_REGISTER_TRANSFER)) {
if (regno < 0x1000 && progbufsize < 2) {
// Make the debugger use the program buffer if it's available, so it
// can test both use cases.
write32(debug_abstract, i++, csrw(S0, CSR_DSCRATCH));
if (write) {
switch (size) {
case 2:
write32(debug_abstract, i++, lw(S0, ZERO, debug_data_start));
break;
case 3:
write32(debug_abstract, i++, ld(S0, ZERO, debug_data_start));
break;
default:
abstractcs.cmderr = CMDERR_NOTSUP;
return true;
}
write32(debug_abstract, i++, csrw(S0, regno));
} else {
write32(debug_abstract, i++, csrr(S0, regno));
switch (size) {
case 2:
write32(debug_abstract, i++, sw(S0, ZERO, debug_data_start));
break;
case 3:
write32(debug_abstract, i++, sd(S0, ZERO, debug_data_start));
break;
default:
abstractcs.cmderr = CMDERR_NOTSUP;
return true;
}
}
write32(debug_abstract, i++, csrr(S0, CSR_DSCRATCH));
} else if (regno >= 0x1000 && regno < 0x1020) {
unsigned regnum = regno - 0x1000;
switch (size) {
case 2:
if (write)
write32(debug_abstract, i++, lw(regnum, ZERO, debug_data_start));
else
write32(debug_abstract, i++, sw(regnum, ZERO, debug_data_start));
break;
case 3:
if (write)
write32(debug_abstract, i++, ld(regnum, ZERO, debug_data_start));
else
write32(debug_abstract, i++, sd(regnum, ZERO, debug_data_start));
break;
default:
abstractcs.cmderr = CMDERR_NOTSUP;
return true;
}
} else if (regno >= 0x1020 && regno < 0x1040) {
// Don't force the debugger to use progbuf if it exists, so the
// debugger has to make the decision not to use abstract commands to
// access 64-bit FPRs on 32-bit targets.
unsigned fprnum = regno - 0x1020;
if (write) {
switch (size) {
case 2:
write32(debug_abstract, i++, flw(fprnum, ZERO, debug_data_start));
break;
case 3:
write32(debug_abstract, i++, fld(fprnum, ZERO, debug_data_start));
break;
default:
abstractcs.cmderr = CMDERR_NOTSUP;
return true;
}
} else {
switch (size) {
case 2:
write32(debug_abstract, i++, fsw(fprnum, ZERO, debug_data_start));
break;
case 3:
write32(debug_abstract, i++, fsd(fprnum, ZERO, debug_data_start));
break;
default:
abstractcs.cmderr = CMDERR_NOTSUP;
return true;
}
}
} else if (regno >= 0xc000 && (regno & 1) == 1) {
// Support odd-numbered custom registers, to allow for debugger testing.
unsigned custom_number = regno - 0xc000;
abstractcs.cmderr = CMDERR_NONE;
if (write) {
// Writing V to custom register N will cause future reads of N to
// return V, reads of N-1 will return V-1, etc.
custom_base = read32(dmdata, 0) - custom_number;
} else {
write32(dmdata, 0, custom_number + custom_base);
write32(dmdata, 1, 0);
}
return true;
} else {
abstractcs.cmderr = CMDERR_NOTSUP;
return true;
}
}
if (get_field(command, AC_ACCESS_REGISTER_POSTEXEC)) {
write32(debug_abstract, i,
jal(ZERO, debug_progbuf_start - debug_abstract_start - 4 * i));
i++;
} else {
write32(debug_abstract, i++, ebreak());
}
debug_rom_flags[dmcontrol.hartsel] |= 1 << DEBUG_ROM_FLAG_GO;
abstractcs.busy = true;
} else {
abstractcs.cmderr = CMDERR_NOTSUP;
}
return true;
}
bool debug_module_t::dmi_write(unsigned address, uint32_t value)
{
D(fprintf(stderr, "dmi_write(0x%x, 0x%x)\n", address, value));
if (!dmstatus.authenticated && address != DMI_AUTHDATA &&
address != DMI_DMCONTROL)
return false;
if (address >= DMI_DATA0 && address < DMI_DATA0 + abstractcs.datacount) {
unsigned i = address - DMI_DATA0;
if (!abstractcs.busy)
write32(dmdata, address - DMI_DATA0, value);
if (abstractcs.busy && abstractcs.cmderr == CMDERR_NONE) {
abstractcs.cmderr = CMDERR_BUSY;
}
if (!abstractcs.busy && ((abstractauto.autoexecdata >> i) & 1)) {
perform_abstract_command();
}
return true;
} else if (address >= DMI_PROGBUF0 && address < DMI_PROGBUF0 + progbufsize) {
unsigned i = address - DMI_PROGBUF0;
if (!abstractcs.busy)
write32(program_buffer, i, value);
if (!abstractcs.busy && ((abstractauto.autoexecprogbuf >> i) & 1)) {
perform_abstract_command();
}
return true;
} else {
switch (address) {
case DMI_DMCONTROL:
{
if (!dmcontrol.dmactive && get_field(value, DMI_DMCONTROL_DMACTIVE))
reset();
dmcontrol.dmactive = get_field(value, DMI_DMCONTROL_DMACTIVE);
if (!dmstatus.authenticated)
return true;
if (dmcontrol.dmactive) {
dmcontrol.haltreq = get_field(value, DMI_DMCONTROL_HALTREQ);
dmcontrol.resumereq = get_field(value, DMI_DMCONTROL_RESUMEREQ);
dmcontrol.hartreset = get_field(value, DMI_DMCONTROL_HARTRESET);
dmcontrol.ndmreset = get_field(value, DMI_DMCONTROL_NDMRESET);
dmcontrol.hartsel = get_field(value, DMI_DMCONTROL_HARTSELHI) <<
DMI_DMCONTROL_HARTSELLO_LENGTH;
dmcontrol.hartsel |= get_field(value, DMI_DMCONTROL_HARTSELLO);
dmcontrol.hartsel &= (1L<<hartsellen) - 1;
if (get_field(value, DMI_DMCONTROL_ACKHAVERESET)) {
havereset[dmcontrol.hartsel] = false;
}
}
processor_t *proc = current_proc();
if (proc) {
proc->halt_request = dmcontrol.haltreq;
if (dmcontrol.resumereq) {
debug_rom_flags[dmcontrol.hartsel] |= (1 << DEBUG_ROM_FLAG_RESUME);
resumeack[dmcontrol.hartsel] = false;
}
if (dmcontrol.hartreset) {
proc->reset();
}
}
if (dmcontrol.ndmreset) {
for (size_t i = 0; i < sim->nprocs(); i++) {
proc = sim->get_core(i);
proc->reset();
}
}
}
return true;
case DMI_COMMAND:
command = value;
return perform_abstract_command();
case DMI_ABSTRACTCS:
abstractcs.cmderr = (cmderr_t) (((uint32_t) (abstractcs.cmderr)) & (~(uint32_t)(get_field(value, DMI_ABSTRACTCS_CMDERR))));
return true;
case DMI_ABSTRACTAUTO:
abstractauto.autoexecprogbuf = get_field(value,
DMI_ABSTRACTAUTO_AUTOEXECPROGBUF);
abstractauto.autoexecdata = get_field(value,
DMI_ABSTRACTAUTO_AUTOEXECDATA);
return true;
case DMI_SBCS:
sbcs.readonaddr = get_field(value, DMI_SBCS_SBREADONADDR);
sbcs.sbaccess = get_field(value, DMI_SBCS_SBACCESS);
sbcs.autoincrement = get_field(value, DMI_SBCS_SBAUTOINCREMENT);
sbcs.readondata = get_field(value, DMI_SBCS_SBREADONDATA);
sbcs.error &= ~get_field(value, DMI_SBCS_SBERROR);
return true;
case DMI_SBADDRESS0:
sbaddress[0] = value;
if (sbcs.error == 0 && sbcs.readonaddr) {
sb_read();
}
return true;
case DMI_SBADDRESS1:
sbaddress[1] = value;
return true;
case DMI_SBADDRESS2:
sbaddress[2] = value;
return true;
case DMI_SBADDRESS3:
sbaddress[3] = value;
return true;
case DMI_SBDATA0:
sbdata[0] = value;
if (sbcs.error == 0) {
sb_write();
if (sbcs.autoincrement && sbcs.error == 0) {
sb_autoincrement();
}
}
return true;
case DMI_SBDATA1:
sbdata[1] = value;
return true;
case DMI_SBDATA2:
sbdata[2] = value;
return true;
case DMI_SBDATA3:
sbdata[3] = value;
return true;
case DMI_AUTHDATA:
D(fprintf(stderr, "debug authentication: got 0x%x; 0x%x unlocks\n", value,
challenge + secret));
if (require_authentication) {
if (value == challenge + secret) {
dmstatus.authenticated = true;
} else {
dmstatus.authenticated = false;
challenge = random();
}
}
return true;
}
}
return false;
}
void debug_module_t::proc_reset(unsigned id)
{
havereset[id] = true;
halted[id] = false;
}

View file

@ -1,164 +0,0 @@
// See LICENSE for license details.
#ifndef _RISCV_DEBUG_MODULE_H
#define _RISCV_DEBUG_MODULE_H
#include <set>
#include "devices.h"
class sim_t;
typedef struct {
bool haltreq;
bool resumereq;
unsigned hartsel;
bool hartreset;
bool dmactive;
bool ndmreset;
} dmcontrol_t;
typedef struct {
bool impebreak;
bool allhavereset;
bool anyhavereset;
bool allnonexistant;
bool anynonexistant;
bool allunavail;
bool anyunavail;
bool allrunning;
bool anyrunning;
bool allhalted;
bool anyhalted;
bool allresumeack;
bool anyresumeack;
bool authenticated;
bool authbusy;
bool cfgstrvalid;
unsigned version;
} dmstatus_t;
typedef enum cmderr {
CMDERR_NONE = 0,
CMDERR_BUSY = 1,
CMDERR_NOTSUP = 2,
CMDERR_EXCEPTION = 3,
CMDERR_HALTRESUME = 4,
CMDERR_OTHER = 7
} cmderr_t;
typedef struct {
bool busy;
unsigned datacount;
unsigned progbufsize;
cmderr_t cmderr;
} abstractcs_t;
typedef struct {
unsigned autoexecprogbuf;
unsigned autoexecdata;
} abstractauto_t;
typedef struct {
unsigned version;
bool readonaddr;
unsigned sbaccess;
bool autoincrement;
bool readondata;
unsigned error;
unsigned asize;
bool access128;
bool access64;
bool access32;
bool access16;
bool access8;
} sbcs_t;
class debug_module_t : public abstract_device_t
{
public:
/*
* If require_authentication is true, then a debugger must authenticate as
* follows:
* 1. Read a 32-bit value from authdata:
* 2. Write the value that was read back, plus one, to authdata.
*/
debug_module_t(sim_t *sim, unsigned progbufsize, unsigned max_bus_master_bits,
bool require_authentication);
~debug_module_t();
void add_device(bus_t *bus);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
// Debug Module Interface that the debugger (in our case through JTAG DTM)
// uses to access the DM.
// Return true for success, false for failure.
bool dmi_read(unsigned address, uint32_t *value);
bool dmi_write(unsigned address, uint32_t value);
// Called when one of the attached harts was reset.
void proc_reset(unsigned id);
private:
static const unsigned datasize = 2;
// Size of program_buffer in 32-bit words, as exposed to the rest of the
// world.
unsigned progbufsize;
// Actual size of the program buffer, which is 1 word bigger than we let on
// to implement the implicit ebreak at the end.
unsigned program_buffer_bytes;
unsigned max_bus_master_bits;
bool require_authentication;
static const unsigned debug_data_start = 0x380;
unsigned debug_progbuf_start;
static const unsigned debug_abstract_size = 5;
unsigned debug_abstract_start;
// R/W this through custom registers, to allow debuggers to test that
// functionality.
unsigned custom_base;
// We only support 1024 harts currently. More requires at least resizing
// the arrays below, and their corresponding special memory regions.
static const unsigned hartsellen = 10;
sim_t *sim;
uint8_t debug_rom_whereto[4];
uint8_t debug_abstract[debug_abstract_size * 4];
uint8_t *program_buffer;
uint8_t dmdata[datasize * 4];
bool halted[1024];
bool resumeack[1024];
bool havereset[1024];
uint8_t debug_rom_flags[1024];
void write32(uint8_t *rom, unsigned int index, uint32_t value);
uint32_t read32(uint8_t *rom, unsigned int index);
void sb_autoincrement();
void sb_read();
void sb_write();
unsigned sb_access_bits();
dmcontrol_t dmcontrol;
dmstatus_t dmstatus;
abstractcs_t abstractcs;
abstractauto_t abstractauto;
uint32_t command;
sbcs_t sbcs;
uint32_t sbaddress[4];
uint32_t sbdata[4];
uint32_t challenge;
const uint32_t secret = 1;
processor_t *current_proc() const;
void reset();
bool perform_abstract_command();
};
#endif

View file

@ -1,23 +0,0 @@
// See LICENSE file for license details.
#ifndef DEBUG_ROM_DEFINES_H
#define DEBUG_ROM_DEFINES_H
// These are implementation-specific addresses in the Debug Module
#define DEBUG_ROM_HALTED 0x100
#define DEBUG_ROM_GOING 0x104
#define DEBUG_ROM_RESUMING 0x108
#define DEBUG_ROM_EXCEPTION 0x10C
// Region of memory where each hart has 1
// byte to read.
#define DEBUG_ROM_FLAGS 0x400
#define DEBUG_ROM_FLAG_GO 0
#define DEBUG_ROM_FLAG_RESUME 1
// These needs to match the link.ld
#define DEBUG_ROM_WHERETO 0x300
#define DEBUG_ROM_ENTRY 0x800
#define DEBUG_ROM_TVEC 0x808
#endif

View file

@ -1,282 +0,0 @@
// See LICENSE for license details.
#ifndef _RISCV_DECODE_H
#define _RISCV_DECODE_H
#if (-1 != ~0) || ((-1 >> 1) != -1)
# error spike requires a two''s-complement c++ implementation
#endif
#ifdef WORDS_BIGENDIAN
# error spike requires a little-endian host
#endif
#include <cstdint>
#include <string.h>
#include <strings.h>
#include "encoding.h"
#include "config.h"
#include "common.h"
#include "softfloat_types.h"
#include "specialize.h"
#include <cinttypes>
typedef int64_t sreg_t;
typedef uint64_t reg_t;
const int NXPR = 32;
const int NFPR = 32;
const int NCSR = 4096;
#define X_RA 1
#define X_SP 2
#define FP_RD_NE 0
#define FP_RD_0 1
#define FP_RD_DN 2
#define FP_RD_UP 3
#define FP_RD_NMM 4
#define FSR_RD_SHIFT 5
#define FSR_RD (0x7 << FSR_RD_SHIFT)
#define FPEXC_NX 0x01
#define FPEXC_UF 0x02
#define FPEXC_OF 0x04
#define FPEXC_DZ 0x08
#define FPEXC_NV 0x10
#define FSR_AEXC_SHIFT 0
#define FSR_NVA (FPEXC_NV << FSR_AEXC_SHIFT)
#define FSR_OFA (FPEXC_OF << FSR_AEXC_SHIFT)
#define FSR_UFA (FPEXC_UF << FSR_AEXC_SHIFT)
#define FSR_DZA (FPEXC_DZ << FSR_AEXC_SHIFT)
#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT)
#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
#define insn_length(x) \
(((x) & 0x03) < 0x03 ? 2 : \
((x) & 0x1f) < 0x1f ? 4 : \
((x) & 0x3f) < 0x3f ? 6 : \
8)
#define MAX_INSN_LENGTH 8
#define PC_ALIGN 2
typedef uint64_t insn_bits_t;
class insn_t
{
public:
insn_t() = default;
insn_t(insn_bits_t bits) : b(bits) {}
insn_bits_t bits() { return b; }
int length() { return insn_length(b); }
int64_t i_imm() { return int64_t(b) >> 20; }
int64_t shamt() { return x(20, 6); }
int64_t s_imm() { return x(7, 5) + (xs(25, 7) << 5); }
int64_t sb_imm() { return (x(8, 4) << 1) + (x(25,6) << 5) + (x(7,1) << 11) + (imm_sign() << 12); }
int64_t u_imm() { return int64_t(b) >> 12 << 12; }
int64_t uj_imm() { return (x(21, 10) << 1) + (x(20, 1) << 11) + (x(12, 8) << 12) + (imm_sign() << 20); }
uint64_t rd() { return x(7, 5); }
uint64_t rs1() { return x(15, 5); }
uint64_t rs2() { return x(20, 5); }
uint64_t rs3() { return x(27, 5); }
uint64_t rm() { return x(12, 3); }
uint64_t csr() { return x(20, 12); }
int64_t rvc_imm() { return x(2, 5) + (xs(12, 1) << 5); }
int64_t rvc_zimm() { return x(2, 5) + (x(12, 1) << 5); }
int64_t rvc_addi4spn_imm() { return (x(6, 1) << 2) + (x(5, 1) << 3) + (x(11, 2) << 4) + (x(7, 4) << 6); }
int64_t rvc_addi16sp_imm() { return (x(6, 1) << 4) + (x(2, 1) << 5) + (x(5, 1) << 6) + (x(3, 2) << 7) + (xs(12, 1) << 9); }
int64_t rvc_lwsp_imm() { return (x(4, 3) << 2) + (x(12, 1) << 5) + (x(2, 2) << 6); }
int64_t rvc_ldsp_imm() { return (x(5, 2) << 3) + (x(12, 1) << 5) + (x(2, 3) << 6); }
int64_t rvc_swsp_imm() { return (x(9, 4) << 2) + (x(7, 2) << 6); }
int64_t rvc_sdsp_imm() { return (x(10, 3) << 3) + (x(7, 3) << 6); }
int64_t rvc_lw_imm() { return (x(6, 1) << 2) + (x(10, 3) << 3) + (x(5, 1) << 6); }
int64_t rvc_ld_imm() { return (x(10, 3) << 3) + (x(5, 2) << 6); }
int64_t rvc_j_imm() { return (x(3, 3) << 1) + (x(11, 1) << 4) + (x(2, 1) << 5) + (x(7, 1) << 6) + (x(6, 1) << 7) + (x(9, 2) << 8) + (x(8, 1) << 10) + (xs(12, 1) << 11); }
int64_t rvc_b_imm() { return (x(3, 2) << 1) + (x(10, 2) << 3) + (x(2, 1) << 5) + (x(5, 2) << 6) + (xs(12, 1) << 8); }
int64_t rvc_simm3() { return x(10, 3); }
uint64_t rvc_rd() { return rd(); }
uint64_t rvc_rs1() { return rd(); }
uint64_t rvc_rs2() { return x(2, 5); }
uint64_t rvc_rs1s() { return 8 + x(7, 3); }
uint64_t rvc_rs2s() { return 8 + x(2, 3); }
private:
insn_bits_t b;
uint64_t x(int lo, int len) { return (b >> lo) & ((insn_bits_t(1) << len)-1); }
uint64_t xs(int lo, int len) { return int64_t(b) << (64-lo-len) >> (64-len); }
uint64_t imm_sign() { return xs(63, 1); }
};
template <class T, size_t N, bool zero_reg>
class regfile_t
{
public:
void write(size_t i, T value)
{
if (!zero_reg || i != 0)
data[i] = value;
}
const T& operator [] (size_t i) const
{
return data[i];
}
private:
T data[N];
};
// helpful macros, etc
#define MMU (*p->get_mmu())
#define STATE (*p->get_state())
#define READ_REG(reg) STATE.XPR[reg]
#define READ_FREG(reg) STATE.FPR[reg]
#define RS1 READ_REG(insn.rs1())
#define RS2 READ_REG(insn.rs2())
#define WRITE_RD(value) WRITE_REG(insn.rd(), value)
#ifndef RISCV_ENABLE_COMMITLOG
# define WRITE_REG(reg, value) STATE.XPR.write(reg, value)
# define WRITE_FREG(reg, value) DO_WRITE_FREG(reg, freg(value))
#else
# define WRITE_REG(reg, value) ({ \
reg_t wdata = (value); /* value may have side effects */ \
STATE.log_reg_write = (commit_log_reg_t){(reg) << 1, {wdata, 0}}; \
STATE.XPR.write(reg, wdata); \
})
# define WRITE_FREG(reg, value) ({ \
freg_t wdata = freg(value); /* value may have side effects */ \
STATE.log_reg_write = (commit_log_reg_t){((reg) << 1) | 1, wdata}; \
DO_WRITE_FREG(reg, wdata); \
})
#endif
// RVC macros
#define WRITE_RVC_RS1S(value) WRITE_REG(insn.rvc_rs1s(), value)
#define WRITE_RVC_RS2S(value) WRITE_REG(insn.rvc_rs2s(), value)
#define WRITE_RVC_FRS2S(value) WRITE_FREG(insn.rvc_rs2s(), value)
#define RVC_RS1 READ_REG(insn.rvc_rs1())
#define RVC_RS2 READ_REG(insn.rvc_rs2())
#define RVC_RS1S READ_REG(insn.rvc_rs1s())
#define RVC_RS2S READ_REG(insn.rvc_rs2s())
#define RVC_FRS2 READ_FREG(insn.rvc_rs2())
#define RVC_FRS2S READ_FREG(insn.rvc_rs2s())
#define RVC_SP READ_REG(X_SP)
// FPU macros
#define FRS1 READ_FREG(insn.rs1())
#define FRS2 READ_FREG(insn.rs2())
#define FRS3 READ_FREG(insn.rs3())
// #define dirty_fp_state (STATE.mstatus |= MSTATUS_FS | (xlen == 64 ? MSTATUS64_SD : MSTATUS32_SD))
#define dirty_fp_state (STATE.mstatus &= MSTATUS_FS | (xlen == 64 ? MSTATUS64_SD : MSTATUS32_SD))
#define dirty_ext_state (STATE.mstatus |= MSTATUS_XS | (xlen == 64 ? MSTATUS64_SD : MSTATUS32_SD))
#define DO_WRITE_FREG(reg, value) (STATE.FPR.write(reg, value), dirty_fp_state)
#define WRITE_FRD(value) WRITE_FREG(insn.rd(), value)
#define SHAMT (insn.i_imm() & 0x3F)
#define BRANCH_TARGET (pc + insn.sb_imm())
#define JUMP_TARGET (pc + insn.uj_imm())
#define RM ({ int rm = insn.rm(); \
if(rm == 7) rm = STATE.frm; \
if(rm > 4) throw trap_illegal_instruction(0); \
rm; })
#define get_field(reg, mask) (((reg) & (decltype(reg))(mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(decltype(reg))(mask)) | (((decltype(reg))(val) * ((mask) & ~((mask) << 1))) & (decltype(reg))(mask)))
#define require(x) if (unlikely(!(x))) throw trap_illegal_instruction(0)
#define require_privilege(p) require(STATE.prv >= (p))
#define require_rv64 require(xlen == 64)
#define require_rv32 require(xlen == 32)
#define require_extension(s) require(p->supports_extension(s))
#define require_fp require((STATE.mstatus & MSTATUS_FS) != 0)
#define require_accelerator require((STATE.mstatus & MSTATUS_XS) != 0)
#define set_fp_exceptions ({ if (softfloat_exceptionFlags) { \
dirty_fp_state; \
STATE.fflags |= softfloat_exceptionFlags; \
} \
softfloat_exceptionFlags = 0; })
#define sext32(x) ((sreg_t)(int32_t)(x))
#define zext32(x) ((reg_t)(uint32_t)(x))
#define sext_xlen(x) (((sreg_t)(x) << (64-xlen)) >> (64-xlen))
#define zext_xlen(x) (((reg_t)(x) << (64-xlen)) >> (64-xlen))
#define set_pc(x) \
do { p->check_pc_alignment(x); \
npc = sext_xlen(x); \
} while(0)
#define set_pc_and_serialize(x) \
do { reg_t __npc = (x) & p->pc_alignment_mask(); \
npc = PC_SERIALIZE_AFTER; \
STATE.pc = __npc; \
} while(0)
#define wfi() \
do { set_pc_and_serialize(npc); \
npc = PC_SERIALIZE_WFI; \
} while(0)
#define serialize() set_pc_and_serialize(npc)
/* Sentinel PC values to serialize simulator pipeline */
#define PC_SERIALIZE_BEFORE 3
#define PC_SERIALIZE_AFTER 5
#define PC_SERIALIZE_WFI 7
#define invalid_pc(pc) ((pc) & 1)
/* Convenience wrappers to simplify softfloat code sequences */
#define isBoxedF32(r) (isBoxedF64(r) && ((uint32_t)((r.v[0] >> 32) + 1) == 0))
#define unboxF32(r) (isBoxedF32(r) ? (uint32_t)r.v[0] : defaultNaNF32UI)
#define isBoxedF64(r) ((r.v[1] + 1) == 0)
#define unboxF64(r) (isBoxedF64(r) ? r.v[0] : defaultNaNF64UI)
typedef float128_t freg_t;
inline float32_t f32(uint32_t v) { return { v }; }
inline float64_t f64(uint64_t v) { return { v }; }
inline float32_t f32(freg_t r) { return f32(unboxF32(r)); }
inline float64_t f64(freg_t r) { return f64(unboxF64(r)); }
inline float128_t f128(freg_t r) { return r; }
inline freg_t freg(float32_t f) { return { ((uint64_t)-1 << 32) | f.v, (uint64_t)-1 }; }
inline freg_t freg(float64_t f) { return { f.v, (uint64_t)-1 }; }
inline freg_t freg(float128_t f) { return f; }
#define F32_SIGN ((uint32_t)1 << 31)
#define F64_SIGN ((uint64_t)1 << 63)
#define fsgnj32(a, b, n, x) \
f32((f32(a).v & ~F32_SIGN) | ((((x) ? f32(a).v : (n) ? F32_SIGN : 0) ^ f32(b).v) & F32_SIGN))
#define fsgnj64(a, b, n, x) \
f64((f64(a).v & ~F64_SIGN) | ((((x) ? f64(a).v : (n) ? F64_SIGN : 0) ^ f64(b).v) & F64_SIGN))
#define isNaNF128(x) isNaNF128UI(x.v[1], x.v[0])
inline float128_t defaultNaNF128()
{
float128_t nan;
nan.v[1] = defaultNaNF128UI64;
nan.v[0] = defaultNaNF128UI0;
return nan;
}
inline freg_t fsgnj128(freg_t a, freg_t b, bool n, bool x)
{
a.v[1] = (a.v[1] & ~F64_SIGN) | (((x ? a.v[1] : n ? F64_SIGN : 0) ^ b.v[1]) & F64_SIGN);
return a;
}
inline freg_t f128_negate(freg_t a)
{
a.v[1] ^= F64_SIGN;
return a;
}
#define validate_csr(which, write) ({ \
if (!STATE.serialized) return PC_SERIALIZE_BEFORE; \
STATE.serialized = false; \
unsigned csr_priv = get_field((which), 0x300); \
unsigned csr_read_only = get_field((which), 0xC00) == 3; \
if (((write) && csr_read_only) || STATE.prv < csr_priv) \
throw trap_illegal_instruction(0); \
(which); })
// Seems that 0x0 doesn't work.
#define DEBUG_START 0x100
#define DEBUG_END (0x1000 - 1)
#endif

View file

@ -1,50 +0,0 @@
#include "devices.h"
void bus_t::add_device(reg_t addr, abstract_device_t* dev)
{
// Searching devices via lower_bound/upper_bound
// implicitly relies on the underlying std::map
// container to sort the keys and provide ordered
// iteration over this sort, which it does. (python's
// SortedDict is a good analogy)
devices[addr] = dev;
}
bool bus_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
// Find the device with the base address closest to but
// less than addr (price-is-right search)
auto it = devices.upper_bound(addr);
if (devices.empty() || it == devices.begin()) {
// Either the bus is empty, or there weren't
// any items with a base address <= addr
return false;
}
// Found at least one item with base address <= addr
// The iterator points to the device after this, so
// go back by one item.
it--;
return it->second->load(addr - it->first, len, bytes);
}
bool bus_t::store(reg_t addr, size_t len, const uint8_t* bytes)
{
// See comments in bus_t::load
auto it = devices.upper_bound(addr);
if (devices.empty() || it == devices.begin()) {
return false;
}
it--;
return it->second->store(addr - it->first, len, bytes);
}
std::pair<reg_t, abstract_device_t*> bus_t::find_device(reg_t addr)
{
// See comments in bus_t::load
auto it = devices.upper_bound(addr);
if (devices.empty() || it == devices.begin()) {
return std::make_pair((reg_t)0, (abstract_device_t*)NULL);
}
it--;
return std::make_pair(it->first, it->second);
}

View file

@ -1,107 +0,0 @@
#ifndef _RISCV_DEVICES_H
#define _RISCV_DEVICES_H
#include "decode.h"
#include <cstdlib>
#include <string>
#include <map>
#include <vector>
#include <fstream>
class processor_t;
class abstract_device_t {
public:
virtual bool load(reg_t addr, size_t len, uint8_t* bytes) = 0;
virtual bool store(reg_t addr, size_t len, const uint8_t* bytes) = 0;
virtual ~abstract_device_t() {}
};
class bus_t : public abstract_device_t {
public:
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
void add_device(reg_t addr, abstract_device_t* dev);
std::pair<reg_t, abstract_device_t*> find_device(reg_t addr);
private:
std::map<reg_t, abstract_device_t*> devices;
};
class rom_device_t : public abstract_device_t {
public:
rom_device_t(std::vector<char> data);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
const std::vector<char>& contents() { return data; }
private:
std::vector<char> data;
};
class mem_t : public abstract_device_t {
public:
mem_t(size_t size) : len(size) {
if (!size)
throw std::runtime_error("zero bytes of target memory requested");
data = (char*)calloc(1, size);
if (!data)
throw std::runtime_error("couldn't allocate " + std::to_string(size) + " bytes of target memory");
}
mem_t(const mem_t& that) = delete;
~mem_t() { free(data); }
bool load(reg_t addr, size_t len, uint8_t* bytes) { return false; }
bool store(reg_t addr, size_t len, const uint8_t* bytes) { return false; }
char* contents() { return data; }
size_t size() { return len; }
private:
char* data;
size_t len;
};
class clint_t : public abstract_device_t {
public:
clint_t(std::vector<processor_t*>&);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
void reset();
size_t size() { return CLINT_SIZE; }
void increment(reg_t inc);
private:
typedef uint64_t mtime_t;
typedef uint64_t mtimecmp_t;
typedef uint32_t msip_t;
std::vector<processor_t*>& procs;
mtime_t mtime;
std::vector<mtimecmp_t> mtimecmp;
};
class uart_t : public abstract_device_t {
public:
uart_t();
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
private:
uint8_t ier;
uint8_t dll;
uint8_t dlm;
uint8_t lcr;
uint8_t lsr;
uint8_t scr;
uint8_t msr;
uint8_t mcr;
bool fifo_enabled;
};
class dump_t : public abstract_device_t {
public:
dump_t();
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
private:
std::ofstream ofs;
};
#endif

View file

@ -1,74 +0,0 @@
// See LICENSE for license details.
#ifndef _RISCV_DISASM_H
#define _RISCV_DISASM_H
#include "decode.h"
#include <string>
#include <sstream>
#include <vector>
extern const char* xpr_name[NXPR];
extern const char* fpr_name[NFPR];
extern const char* csr_name(int which);
class arg_t
{
public:
virtual std::string to_string(insn_t val) const = 0;
virtual ~arg_t() {}
};
class disasm_insn_t
{
public:
disasm_insn_t(const char* name, uint32_t match, uint32_t mask,
const std::vector<const arg_t*>& args)
: match(match), mask(mask), args(args), name(name) {}
bool operator == (insn_t insn) const
{
return (insn.bits() & mask) == match;
}
std::string to_string(insn_t insn) const
{
std::stringstream s;
int len;
for (len = 0; name[len]; len++)
s << (name[len] == '_' ? '.' : name[len]);
if (args.size())
{
s << std::string(std::max(1, 8 - len), ' ');
for (size_t i = 0; i < args.size()-1; i++)
s << args[i]->to_string(insn) << ", ";
s << args[args.size()-1]->to_string(insn);
}
return s.str();
}
uint32_t get_match() const { return match; }
uint32_t get_mask() const { return mask; }
private:
uint32_t match;
uint32_t mask;
std::vector<const arg_t*> args;
const char* name;
};
class disassembler_t
{
public:
disassembler_t(int xlen);
~disassembler_t();
std::string disassemble(insn_t insn) const;
void add_insn(disasm_insn_t* insn);
private:
static const int HASH_SIZE = 256;
std::vector<const disasm_insn_t*> chain[HASH_SIZE+1];
const disasm_insn_t* lookup(insn_t insn) const;
};
#endif

View file

@ -1,166 +0,0 @@
// See LICENSE for license details.
#include "dts.h"
#include <iostream>
#include <sstream>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
std::vector<processor_t*> procs,
std::vector<std::pair<reg_t, mem_t*>> mems)
{
std::stringstream s;
s << std::dec <<
"/dts-v1/;\n"
"\n"
"/ {\n"
" #address-cells = <2>;\n"
" #size-cells = <2>;\n"
" compatible = \"ucbbar,spike-bare-dev\";\n"
" model = \"ucbbar,spike-bare\";\n"
" cpus {\n"
" #address-cells = <1>;\n"
" #size-cells = <0>;\n"
" timebase-frequency = <" << (cpu_hz/insns_per_rtc_tick) << ">;\n";
for (size_t i = 0; i < procs.size(); i++) {
s << " CPU" << i << ": cpu@" << i << " {\n"
" device_type = \"cpu\";\n"
" reg = <" << i << ">;\n"
" status = \"okay\";\n"
" compatible = \"riscv\";\n"
" riscv,isa = \"" << procs[i]->get_isa_string() << "\";\n"
" mmu-type = \"riscv," << (procs[i]->get_max_xlen() <= 32 ? "sv32" : "sv48") << "\";\n"
" clock-frequency = <" << cpu_hz << ">;\n"
" CPU" << i << "_intc: interrupt-controller {\n"
" #interrupt-cells = <1>;\n"
" interrupt-controller;\n"
" compatible = \"riscv,cpu-intc\";\n"
" };\n"
" };\n";
}
s << " };\n";
for (auto& m : mems) {
s << std::hex <<
" memory@" << m.first << " {\n"
" device_type = \"memory\";\n"
" reg = <0x" << (m.first >> 32) << " 0x" << (m.first & (uint32_t)-1) <<
" 0x" << (m.second->size() >> 32) << " 0x" << (m.second->size() & (uint32_t)-1) << ">;\n"
" };\n";
}
s << " soc {\n"
" #address-cells = <2>;\n"
" #size-cells = <2>;\n"
" compatible = \"ucbbar,spike-bare-soc\", \"simple-bus\";\n"
" ranges;\n"
" clint@" << CLINT_BASE << " {\n"
" compatible = \"riscv,clint0\";\n"
" interrupts-extended = <" << std::dec;
for (size_t i = 0; i < procs.size(); i++)
s << "&CPU" << i << "_intc 3 &CPU" << i << "_intc 7 ";
reg_t clintbs = CLINT_BASE;
reg_t clintsz = CLINT_SIZE;
s << std::hex << ">;\n"
" reg = <0x" << (clintbs >> 32) << " 0x" << (clintbs & (uint32_t)-1) <<
" 0x" << (clintsz >> 32) << " 0x" << (clintsz & (uint32_t)-1) << ">;\n"
" };\n"
" };\n"
// " htif {\n"
// " compatible = \"ucb,htif0\";\n"
// " };\n"
" uart@" << UART_BASE << " {\n"
" compatible = \"ns16750\";\n"
" reg = <0x0 0x10000000 0x0 0x1000>;\n"
" clock-frequency = <50000000>;\n"
" current-speed = <115200>;\n"
// " interrupt-parent = <&PLIC0>;\n"
// " interrupts = <1>;\n"
" reg-shift = <2>;\n"
" reg-io-width = <4>;\n"
" };\n"
"};\n";
return s.str();
}
std::string dts_compile(const std::string& dts)
{
// Convert the DTS to DTB
int dts_pipe[2];
pid_t dts_pid;
if (pipe(dts_pipe) != 0 || (dts_pid = fork()) < 0) {
std::cerr << "Failed to fork dts child: " << strerror(errno) << std::endl;
exit(1);
}
// Child process to output dts
if (dts_pid == 0) {
close(dts_pipe[0]);
int step, len = dts.length();
const char *buf = dts.c_str();
for (int done = 0; done < len; done += step) {
step = write(dts_pipe[1], buf+done, len-done);
if (step == -1) {
std::cerr << "Failed to write dts: " << strerror(errno) << std::endl;
exit(1);
}
}
close(dts_pipe[1]);
exit(0);
}
pid_t dtb_pid;
int dtb_pipe[2];
if (pipe(dtb_pipe) != 0 || (dtb_pid = fork()) < 0) {
std::cerr << "Failed to fork dtb child: " << strerror(errno) << std::endl;
exit(1);
}
// Child process to output dtb
if (dtb_pid == 0) {
dup2(dts_pipe[0], 0);
dup2(dtb_pipe[1], 1);
close(dts_pipe[0]);
close(dts_pipe[1]);
close(dtb_pipe[0]);
close(dtb_pipe[1]);
execl(DTC, DTC, "-O", "dtb", 0);
std::cerr << "Failed to run " DTC ": " << strerror(errno) << std::endl;
exit(1);
}
close(dts_pipe[1]);
close(dts_pipe[0]);
close(dtb_pipe[1]);
// Read-out dtb
std::stringstream dtb;
int got;
char buf[4096];
while ((got = read(dtb_pipe[0], buf, sizeof(buf))) > 0) {
dtb.write(buf, got);
}
if (got == -1) {
std::cerr << "Failed to read dtb: " << strerror(errno) << std::endl;
exit(1);
}
close(dtb_pipe[0]);
// Reap children
int status;
waitpid(dts_pid, &status, 0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
std::cerr << "Child dts process failed" << std::endl;
exit(1);
}
waitpid(dtb_pid, &status, 0);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
std::cerr << "Child dtb process failed" << std::endl;
exit(1);
}
return dtb.str();
}

View file

@ -1,15 +0,0 @@
// See LICENSE for license details.
#ifndef _RISCV_DTS_H
#define _RISCV_DTS_H
#include "processor.h"
#include "mmu.h"
#include <string>
std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
std::vector<processor_t*> procs,
std::vector<std::pair<reg_t, mem_t*>> mems);
std::string dts_compile(const std::string& dts);
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,252 +0,0 @@
// See LICENSE for license details.
#include "processor.h"
#include "mmu.h"
#include <cassert>
static void commit_log_stash_privilege(processor_t* p)
{
#ifdef RISCV_ENABLE_COMMITLOG
state_t* state = p->get_state();
state->last_inst_priv = state->prv;
state->last_inst_xlen = p->get_xlen();
state->last_inst_flen = p->get_flen();
#endif
}
static void commit_log_print_value(int width, uint64_t hi, uint64_t lo)
{
switch (width) {
case 16:
fprintf(stderr, "0x%04" PRIx16, (uint16_t)lo);
break;
case 32:
fprintf(stderr, "0x%08" PRIx32, (uint32_t)lo);
break;
case 64:
fprintf(stderr, "0x%016" PRIx64, lo);
break;
case 128:
fprintf(stderr, "0x%016" PRIx64 "%016" PRIx64, hi, lo);
break;
default:
abort();
}
}
static void commit_log_print_insn(state_t* state, reg_t pc, insn_t insn)
{
#ifdef RISCV_ENABLE_COMMITLOG
auto& reg = state->log_reg_write;
int priv = state->last_inst_priv;
int xlen = state->last_inst_xlen;
int flen = state->last_inst_flen;
state->last_insn = insn.bits();
if (reg.addr) {
bool fp = reg.addr & 1;
int rd = reg.addr >> 1;
int size = fp ? flen : xlen;
}
#endif
}
inline void processor_t::update_histogram(reg_t pc)
{
#ifdef RISCV_ENABLE_HISTOGRAM
pc_histogram[pc]++;
#endif
}
// This is expected to be inlined by the compiler so each use of execute_insn
// includes a duplicated body of the function to get separate fetch.func
// function calls.
static reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch)
{
commit_log_stash_privilege(p);
reg_t npc = fetch.func(p, fetch.insn, pc);
if (npc != PC_SERIALIZE_BEFORE) {
commit_log_print_insn(p->get_state(), pc, fetch.insn);
p->update_histogram(pc);
}
return npc;
}
bool processor_t::slow_path()
{
return debug || state.single_step != state.STEP_NONE || state.dcsr.cause;
}
// fetch/decode/execute loop
void processor_t::step(size_t n)
{
#ifdef RISCV_ENABLE_COMMITLOG
state.was_exception = false;
#endif
if (state.dcsr.cause == DCSR_CAUSE_NONE) {
if (halt_request) {
enter_debug_mode(DCSR_CAUSE_DEBUGINT);
} // !!!The halt bit in DCSR is deprecated.
else if (state.dcsr.halt) {
enter_debug_mode(DCSR_CAUSE_HALT);
}
}
while (n > 0) {
size_t instret = 0;
reg_t pc = state.pc;
mmu_t* _mmu = mmu;
#define advance_pc() \
if (unlikely(invalid_pc(pc))) { \
switch (pc) { \
case PC_SERIALIZE_BEFORE: state.serialized = true; break; \
case PC_SERIALIZE_AFTER: ++instret; break; \
case PC_SERIALIZE_WFI: n = ++instret; break; \
default: abort(); \
} \
pc = state.pc; \
break; \
} else { \
state.pc = pc; \
instret++; \
}
try
{
insn_fetch_t fetch = mmu->load_insn(pc);
// check whether the instruction is an AMO, we dpn't take interrupts on AMOs
if (likely((fetch.insn.bits() & 0x2f) != 0x2f)) {
take_pending_interrupt();
}
if (unlikely(slow_path()))
{
while (instret < n)
{
if (unlikely(!state.serialized && state.single_step == state.STEP_STEPPED)) {
state.single_step = state.STEP_NONE;
if (state.dcsr.cause == DCSR_CAUSE_NONE) {
enter_debug_mode(DCSR_CAUSE_STEP);
// enter_debug_mode changed state.pc, so we can't just continue.
break;
}
}
if (unlikely(state.single_step == state.STEP_STEPPING)) {
state.single_step = state.STEP_STEPPED;
}
if (debug && !state.serialized)
disasm(fetch.insn);
pc = execute_insn(this, pc, fetch);
advance_pc();
if (unlikely(state.pc >= DEBUG_ROM_ENTRY &&
state.pc < DEBUG_END)) {
// We're waiting for the debugger to tell us something.
return;
}
}
}
else while (instret < n)
{
// This code uses a modified Duff's Device to improve the performance
// of executing instructions. While typical Duff's Devices are used
// for software pipelining, the switch statement below primarily
// benefits from separate call points for the fetch.func function call
// found in each execute_insn. This function call is an indirect jump
// that depends on the current instruction. By having an indirect jump
// dedicated for each icache entry, you improve the performance of the
// host's next address predictor. Each case in the switch statement
// allows for the program flow to contine to the next case if it
// corresponds to the next instruction in the program and instret is
// still less than n.
//
// According to Andrew Waterman's recollection, this optimization
// resulted in approximately a 2x performance increase.
// This figures out where to jump to in the switch statement
size_t idx = _mmu->icache_index(pc);
// This gets the cached decoded instruction from the MMU. If the MMU
// does not have the current pc cached, it will refill the MMU and
// return the correct entry. ic_entry->data.func is the C++ function
// corresponding to the instruction.
auto ic_entry = _mmu->access_icache(pc);
// This macro is included in "icache.h" included within the switch
// statement below. The indirect jump corresponding to the instruction
// is located within the execute_insn() function call.
#define ICACHE_ACCESS(i) { \
insn_fetch_t fetch = ic_entry->data; \
pc = execute_insn(this, pc, fetch); \
ic_entry = ic_entry->next; \
if (i == mmu_t::ICACHE_ENTRIES-1) break; \
if (unlikely(ic_entry->tag != pc)) break; \
if (unlikely(instret+1 == n)) break; \
instret++; \
state.pc = pc; \
}
// This switch statement implements the modified Duff's device as
// explained above.
switch (idx) {
// "icache.h" is generated by the gen_icache script
#include "icache.h"
}
advance_pc();
}
}
catch(trap_t& t)
{
#ifdef RISCV_ENABLE_COMMITLOG
state.was_exception = true;
#endif
take_trap(t, pc);
n = instret;
if (unlikely(state.single_step == state.STEP_STEPPED)) {
state.single_step = state.STEP_NONE;
enter_debug_mode(DCSR_CAUSE_STEP);
}
}
catch (trigger_matched_t& t)
{
if (mmu->matched_trigger) {
// This exception came from the MMU. That means the instruction hasn't
// fully executed yet. We start it again, but this time it won't throw
// an exception because matched_trigger is already set. (All memory
// instructions are idempotent so restarting is safe.)
insn_fetch_t fetch = mmu->load_insn(pc);
pc = execute_insn(this, pc, fetch);
advance_pc();
delete mmu->matched_trigger;
mmu->matched_trigger = NULL;
}
switch (state.mcontrol[t.index].action) {
case ACTION_DEBUG_MODE:
enter_debug_mode(DCSR_CAUSE_HWBP);
break;
case ACTION_DEBUG_EXCEPTION: {
mem_trap_t trap(CAUSE_BREAKPOINT, t.address);
take_trap(trap, pc);
break;
}
default:
abort();
}
}
state.minstret += instret;
n -= instret;
}
}

View file

@ -1,23 +0,0 @@
// See LICENSE for license details.
#include "extension.h"
#include "trap.h"
extension_t::~extension_t()
{
}
void extension_t::illegal_instruction()
{
throw trap_illegal_instruction(0);
}
void extension_t::raise_interrupt()
{
p->take_interrupt((reg_t)1 << IRQ_COP); // must not return
throw std::logic_error("a COP exception was posted, but interrupts are disabled!");
}
void extension_t::clear_interrupt()
{
}

View file

@ -1,38 +0,0 @@
// See LICENSE for license details.
#ifndef _RISCV_COPROCESSOR_H
#define _RISCV_COPROCESSOR_H
#include "processor.h"
#include "disasm.h"
#include <vector>
#include <functional>
class extension_t
{
public:
virtual std::vector<insn_desc_t> get_instructions() = 0;
virtual std::vector<disasm_insn_t*> get_disasms() = 0;
virtual const char* name() = 0;
virtual void reset() {};
virtual void set_debug(bool value) {};
virtual ~extension_t();
void set_processor(processor_t* _p) { p = _p; }
protected:
processor_t* p;
void illegal_instruction();
void raise_interrupt();
void clear_interrupt();
};
std::function<extension_t*()> find_extension(const char* name);
void register_extension(const char* name, std::function<extension_t*()> f);
#define REGISTER_EXTENSION(name, constructor) \
class register_##name { \
public: register_##name() { register_extension(#name, constructor); } \
}; static register_##name dummy_##name;
#endif

View file

@ -1,37 +0,0 @@
// See LICENSE for license details.
#include "extension.h"
#include <string>
#include <map>
#include <dlfcn.h>
static std::map<std::string, std::function<extension_t*()>>& extensions()
{
static std::map<std::string, std::function<extension_t*()>> v;
return v;
}
void register_extension(const char* name, std::function<extension_t*()> f)
{
extensions()[name] = f;
}
std::function<extension_t*()> find_extension(const char* name)
{
if (!extensions().count(name)) {
// try to find extension xyz by loading libxyz.so
std::string libname = std::string("lib") + name + ".so";
if (!dlopen(libname.c_str(), RTLD_LAZY)) {
fprintf(stderr, "couldn't find extension '%s' (or library '%s')\n",
name, libname.c_str());
exit(-1);
}
if (!extensions().count(name)) {
fprintf(stderr, "couldn't find extension '%s' in shared library '%s'\n",
name, libname.c_str());
exit(-1);
}
}
return extensions()[name];
}

View file

@ -1,7 +0,0 @@
#!/bin/sh
n=$(($1-1))
for i in `seq 0 $n`
do
echo case $i: ICACHE_ACCESS\($i\)\;
done
echo

View file

@ -1,21 +0,0 @@
// See LICENSE for license details.
#include "insn_template.h"
reg_t rv32_NAME(processor_t* p, insn_t insn, reg_t pc)
{
int xlen = 32;
reg_t npc = sext_xlen(pc + insn_length(OPCODE));
#include "insns/NAME.h"
trace_opcode(p, OPCODE, insn);
return npc;
}
reg_t rv64_NAME(processor_t* p, insn_t insn, reg_t pc)
{
int xlen = 64;
reg_t npc = sext_xlen(pc + insn_length(OPCODE));
#include "insns/NAME.h"
trace_opcode(p, OPCODE, insn);
return npc;
}

View file

@ -1,9 +0,0 @@
// See LICENSE for license details.
#include "mmu.h"
#include "mulhi.h"
#include "softfloat.h"
#include "internals.h"
#include "specialize.h"
#include "tracer.h"
#include <assert.h>

View file

@ -1 +0,0 @@
WRITE_RD(sext_xlen(RS1 + RS2));

View file

@ -1 +0,0 @@
WRITE_RD(sext_xlen(RS1 + insn.i_imm()));

View file

@ -1,2 +0,0 @@
require_rv64;
WRITE_RD(sext32(insn.i_imm() + RS1));

View file

@ -1,2 +0,0 @@
require_rv64;
WRITE_RD(sext32(RS1 + RS2));

View file

@ -1,3 +0,0 @@
require_extension('A');
require_rv64;
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return lhs + RS2; }));

View file

@ -1,2 +0,0 @@
require_extension('A');
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return lhs + RS2; })));

View file

@ -1,3 +0,0 @@
require_extension('A');
require_rv64;
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return lhs & RS2; }));

View file

@ -1,2 +0,0 @@
require_extension('A');
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return lhs & RS2; })));

View file

@ -1,3 +0,0 @@
require_extension('A');
require_rv64;
WRITE_RD(MMU.amo_uint64(RS1, [&](int64_t lhs) { return std::max(lhs, int64_t(RS2)); }));

View file

@ -1,2 +0,0 @@
require_extension('A');
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](int32_t lhs) { return std::max(lhs, int32_t(RS2)); })));

View file

@ -1,3 +0,0 @@
require_extension('A');
require_rv64;
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return std::max(lhs, RS2); }));

View file

@ -1,2 +0,0 @@
require_extension('A');
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return std::max(lhs, uint32_t(RS2)); })));

View file

@ -1,3 +0,0 @@
require_extension('A');
require_rv64;
WRITE_RD(MMU.amo_uint64(RS1, [&](int64_t lhs) { return std::min(lhs, int64_t(RS2)); }));

View file

@ -1,2 +0,0 @@
require_extension('A');
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](int32_t lhs) { return std::min(lhs, int32_t(RS2)); })));

View file

@ -1,3 +0,0 @@
require_extension('A');
require_rv64;
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return std::min(lhs, RS2); }));

View file

@ -1,2 +0,0 @@
require_extension('A');
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return std::min(lhs, uint32_t(RS2)); })));

View file

@ -1,3 +0,0 @@
require_extension('A');
require_rv64;
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return lhs | RS2; }));

View file

@ -1,2 +0,0 @@
require_extension('A');
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return lhs | RS2; })));

View file

@ -1,3 +0,0 @@
require_extension('A');
require_rv64;
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return RS2; }));

View file

@ -1,2 +0,0 @@
require_extension('A');
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return RS2; })));

View file

@ -1,3 +0,0 @@
require_extension('A');
require_rv64;
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return lhs ^ RS2; }));

View file

@ -1,2 +0,0 @@
require_extension('A');
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return lhs ^ RS2; })));

View file

@ -1 +0,0 @@
WRITE_RD(RS1 & RS2);

View file

@ -1 +0,0 @@
WRITE_RD(insn.i_imm() & RS1);

View file

@ -1 +0,0 @@
WRITE_RD(sext_xlen(insn.u_imm() + pc));

View file

@ -1,2 +0,0 @@
if(RS1 == RS2)
set_pc(BRANCH_TARGET);

View file

@ -1,2 +0,0 @@
if(sreg_t(RS1) >= sreg_t(RS2))
set_pc(BRANCH_TARGET);

View file

@ -1,2 +0,0 @@
if(RS1 >= RS2)
set_pc(BRANCH_TARGET);

View file

@ -1,2 +0,0 @@
if(sreg_t(RS1) < sreg_t(RS2))
set_pc(BRANCH_TARGET);

View file

@ -1,2 +0,0 @@
if(RS1 < RS2)
set_pc(BRANCH_TARGET);

View file

@ -1,2 +0,0 @@
if(RS1 != RS2)
set_pc(BRANCH_TARGET);

View file

@ -1,3 +0,0 @@
require_extension('C');
require(insn.rvc_rs2() != 0);
WRITE_RD(sext_xlen(RVC_RS1 + RVC_RS2));

View file

@ -1,2 +0,0 @@
require_extension('C');
WRITE_RD(sext_xlen(RVC_RS1 + insn.rvc_imm()));

View file

@ -1,3 +0,0 @@
require_extension('C');
require(insn.rvc_addi4spn_imm() != 0);
WRITE_RVC_RS2S(sext_xlen(RVC_SP + insn.rvc_addi4spn_imm()));

View file

@ -1,3 +0,0 @@
require_extension('C');
require_rv64;
WRITE_RVC_RS1S(sext32(RVC_RS1S + RVC_RS2S));

View file

@ -1,2 +0,0 @@
require_extension('C');
WRITE_RVC_RS1S(RVC_RS1S & RVC_RS2S);

View file

@ -1,2 +0,0 @@
require_extension('C');
WRITE_RVC_RS1S(RVC_RS1S & insn.rvc_imm());

View file

@ -1,3 +0,0 @@
require_extension('C');
if (RVC_RS1S == 0)
set_pc(pc + insn.rvc_b_imm());

View file

@ -1,3 +0,0 @@
require_extension('C');
if (RVC_RS1S != 0)
set_pc(pc + insn.rvc_b_imm());

View file

@ -1,2 +0,0 @@
require_extension('C');
throw trap_breakpoint(pc);

View file

@ -1,4 +0,0 @@
require_extension('C');
require_extension('D');
require_fp;
WRITE_RVC_FRS2S(f64(MMU.load_uint64(RVC_RS1S + insn.rvc_ld_imm())));

View file

@ -1,4 +0,0 @@
require_extension('C');
require_extension('D');
require_fp;
WRITE_FRD(f64(MMU.load_uint64(RVC_SP + insn.rvc_ldsp_imm())));

View file

@ -1,8 +0,0 @@
require_extension('C');
if (xlen == 32) {
require_extension('F');
require_fp;
WRITE_RVC_FRS2S(f32(MMU.load_uint32(RVC_RS1S + insn.rvc_lw_imm())));
} else { // c.ld
WRITE_RVC_RS2S(MMU.load_int64(RVC_RS1S + insn.rvc_ld_imm()));
}

View file

@ -1,9 +0,0 @@
require_extension('C');
if (xlen == 32) {
require_extension('F');
require_fp;
WRITE_FRD(f32(MMU.load_uint32(RVC_SP + insn.rvc_lwsp_imm())));
} else { // c.ldsp
require(insn.rvc_rd() != 0);
WRITE_RD(MMU.load_int64(RVC_SP + insn.rvc_ldsp_imm()));
}

View file

@ -1,4 +0,0 @@
require_extension('C');
require_extension('D');
require_fp;
MMU.store_uint64(RVC_RS1S + insn.rvc_ld_imm(), RVC_FRS2S.v[0]);

View file

@ -1,4 +0,0 @@
require_extension('C');
require_extension('D');
require_fp;
MMU.store_uint64(RVC_SP + insn.rvc_sdsp_imm(), RVC_FRS2.v[0]);

View file

@ -1,8 +0,0 @@
require_extension('C');
if (xlen == 32) {
require_extension('F');
require_fp;
MMU.store_uint32(RVC_RS1S + insn.rvc_lw_imm(), RVC_FRS2S.v[0]);
} else { // c.sd
MMU.store_uint64(RVC_RS1S + insn.rvc_ld_imm(), RVC_RS2S);
}

View file

@ -1,8 +0,0 @@
require_extension('C');
if (xlen == 32) {
require_extension('F');
require_fp;
MMU.store_uint32(RVC_SP + insn.rvc_swsp_imm(), RVC_FRS2.v[0]);
} else { // c.sdsp
MMU.store_uint64(RVC_SP + insn.rvc_sdsp_imm(), RVC_RS2);
}

View file

@ -1,2 +0,0 @@
require_extension('C');
set_pc(pc + insn.rvc_j_imm());

View file

@ -1,9 +0,0 @@
require_extension('C');
if (xlen == 32) {
reg_t tmp = npc;
set_pc(pc + insn.rvc_j_imm());
WRITE_REG(X_RA, tmp);
} else { // c.addiw
require(insn.rvc_rd() != 0);
WRITE_RD(sext32(RVC_RS1 + insn.rvc_imm()));
}

View file

@ -1,5 +0,0 @@
require_extension('C');
require(insn.rvc_rs1() != 0);
reg_t tmp = npc;
set_pc(RVC_RS1 & ~reg_t(1));
WRITE_REG(X_RA, tmp);

View file

@ -1,3 +0,0 @@
require_extension('C');
require(insn.rvc_rs1() != 0);
set_pc(RVC_RS1 & ~reg_t(1));

View file

@ -1,2 +0,0 @@
require_extension('C');
WRITE_RD(insn.rvc_imm());

View file

@ -1,8 +0,0 @@
require_extension('C');
if (insn.rvc_rd() == 2) { // c.addi16sp
require(insn.rvc_addi16sp_imm() != 0);
WRITE_REG(X_SP, sext_xlen(RVC_SP + insn.rvc_addi16sp_imm()));
} else {
require(insn.rvc_imm() != 0);
WRITE_RD(insn.rvc_imm() << 12);
}

Some files were not shown because too many files have changed in this diff Show more