mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-22 05:07:21 -04:00
Remove spike tandem simulation
This commit is contained in:
parent
0947762025
commit
20f6b42d89
541 changed files with 0 additions and 44611 deletions
|
@ -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;
|
||||
|
|
|
@ -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
|
7
tb/riscv-isa-sim/.gitignore
vendored
7
tb/riscv-isa-sim/.gitignore
vendored
|
@ -1,7 +0,0 @@
|
|||
build/
|
||||
*.gch
|
||||
autom4te.cache/
|
||||
.*.swp
|
||||
*.o
|
||||
*.d
|
||||
.gdb_history
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
||||
...
|
||||
```
|
345
tb/riscv-isa-sim/aclocal.m4
vendored
345
tb/riscv-isa-sim/aclocal.m4
vendored
|
@ -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"
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
])
|
|
@ -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
|
6166
tb/riscv-isa-sim/configure
vendored
6166
tb/riscv-isa-sim/configure
vendored
File diff suppressed because it is too large
Load diff
|
@ -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
|
5
tb/riscv-isa-sim/debug_rom/.gitignore
vendored
5
tb/riscv-isa-sim/debug_rom/.gitignore
vendored
|
@ -1,5 +0,0 @@
|
|||
/debug_rom
|
||||
/debug_rom32
|
||||
/debug_rom64
|
||||
/debug_rom32.h
|
||||
/debug_rom64.h
|
|
@ -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
|
|
@ -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.
|
|
@ -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;
|
|
@ -1,15 +0,0 @@
|
|||
OUTPUT_ARCH( "riscv" )
|
||||
ENTRY( entry )
|
||||
SECTIONS
|
||||
{
|
||||
.whereto 0x300 :
|
||||
{
|
||||
*(.whereto)
|
||||
}
|
||||
. = 0x800;
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
}
|
||||
_end = .;
|
||||
}
|
|
@ -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; })
|
|
@ -1,7 +0,0 @@
|
|||
dummy_rocc_subproject_deps = \
|
||||
spike_main \
|
||||
riscv \
|
||||
softfloat \
|
||||
|
||||
dummy_rocc_srcs = \
|
||||
dummy_rocc.cc \
|
|
@ -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");
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
|
@ -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
|
|
@ -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];
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/sh
|
||||
n=$(($1-1))
|
||||
for i in `seq 0 $n`
|
||||
do
|
||||
echo case $i: ICACHE_ACCESS\($i\)\;
|
||||
done
|
||||
echo
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
|
@ -1 +0,0 @@
|
|||
WRITE_RD(sext_xlen(RS1 + RS2));
|
|
@ -1 +0,0 @@
|
|||
WRITE_RD(sext_xlen(RS1 + insn.i_imm()));
|
|
@ -1,2 +0,0 @@
|
|||
require_rv64;
|
||||
WRITE_RD(sext32(insn.i_imm() + RS1));
|
|
@ -1,2 +0,0 @@
|
|||
require_rv64;
|
||||
WRITE_RD(sext32(RS1 + RS2));
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('A');
|
||||
require_rv64;
|
||||
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return lhs + RS2; }));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('A');
|
||||
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return lhs + RS2; })));
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('A');
|
||||
require_rv64;
|
||||
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return lhs & RS2; }));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('A');
|
||||
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return lhs & RS2; })));
|
|
@ -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)); }));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('A');
|
||||
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](int32_t lhs) { return std::max(lhs, int32_t(RS2)); })));
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('A');
|
||||
require_rv64;
|
||||
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return std::max(lhs, RS2); }));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('A');
|
||||
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return std::max(lhs, uint32_t(RS2)); })));
|
|
@ -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)); }));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('A');
|
||||
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](int32_t lhs) { return std::min(lhs, int32_t(RS2)); })));
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('A');
|
||||
require_rv64;
|
||||
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return std::min(lhs, RS2); }));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('A');
|
||||
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return std::min(lhs, uint32_t(RS2)); })));
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('A');
|
||||
require_rv64;
|
||||
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return lhs | RS2; }));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('A');
|
||||
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return lhs | RS2; })));
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('A');
|
||||
require_rv64;
|
||||
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return RS2; }));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('A');
|
||||
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return RS2; })));
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('A');
|
||||
require_rv64;
|
||||
WRITE_RD(MMU.amo_uint64(RS1, [&](uint64_t lhs) { return lhs ^ RS2; }));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('A');
|
||||
WRITE_RD(sext32(MMU.amo_uint32(RS1, [&](uint32_t lhs) { return lhs ^ RS2; })));
|
|
@ -1 +0,0 @@
|
|||
WRITE_RD(RS1 & RS2);
|
|
@ -1 +0,0 @@
|
|||
WRITE_RD(insn.i_imm() & RS1);
|
|
@ -1 +0,0 @@
|
|||
WRITE_RD(sext_xlen(insn.u_imm() + pc));
|
|
@ -1,2 +0,0 @@
|
|||
if(RS1 == RS2)
|
||||
set_pc(BRANCH_TARGET);
|
|
@ -1,2 +0,0 @@
|
|||
if(sreg_t(RS1) >= sreg_t(RS2))
|
||||
set_pc(BRANCH_TARGET);
|
|
@ -1,2 +0,0 @@
|
|||
if(RS1 >= RS2)
|
||||
set_pc(BRANCH_TARGET);
|
|
@ -1,2 +0,0 @@
|
|||
if(sreg_t(RS1) < sreg_t(RS2))
|
||||
set_pc(BRANCH_TARGET);
|
|
@ -1,2 +0,0 @@
|
|||
if(RS1 < RS2)
|
||||
set_pc(BRANCH_TARGET);
|
|
@ -1,2 +0,0 @@
|
|||
if(RS1 != RS2)
|
||||
set_pc(BRANCH_TARGET);
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('C');
|
||||
require(insn.rvc_rs2() != 0);
|
||||
WRITE_RD(sext_xlen(RVC_RS1 + RVC_RS2));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('C');
|
||||
WRITE_RD(sext_xlen(RVC_RS1 + insn.rvc_imm()));
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('C');
|
||||
require(insn.rvc_addi4spn_imm() != 0);
|
||||
WRITE_RVC_RS2S(sext_xlen(RVC_SP + insn.rvc_addi4spn_imm()));
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('C');
|
||||
require_rv64;
|
||||
WRITE_RVC_RS1S(sext32(RVC_RS1S + RVC_RS2S));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('C');
|
||||
WRITE_RVC_RS1S(RVC_RS1S & RVC_RS2S);
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('C');
|
||||
WRITE_RVC_RS1S(RVC_RS1S & insn.rvc_imm());
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('C');
|
||||
if (RVC_RS1S == 0)
|
||||
set_pc(pc + insn.rvc_b_imm());
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('C');
|
||||
if (RVC_RS1S != 0)
|
||||
set_pc(pc + insn.rvc_b_imm());
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('C');
|
||||
throw trap_breakpoint(pc);
|
|
@ -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())));
|
|
@ -1,4 +0,0 @@
|
|||
require_extension('C');
|
||||
require_extension('D');
|
||||
require_fp;
|
||||
WRITE_FRD(f64(MMU.load_uint64(RVC_SP + insn.rvc_ldsp_imm())));
|
|
@ -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()));
|
||||
}
|
|
@ -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()));
|
||||
}
|
|
@ -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]);
|
|
@ -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]);
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('C');
|
||||
set_pc(pc + insn.rvc_j_imm());
|
|
@ -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()));
|
||||
}
|
|
@ -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);
|
|
@ -1,3 +0,0 @@
|
|||
require_extension('C');
|
||||
require(insn.rvc_rs1() != 0);
|
||||
set_pc(RVC_RS1 & ~reg_t(1));
|
|
@ -1,2 +0,0 @@
|
|||
require_extension('C');
|
||||
WRITE_RD(insn.rvc_imm());
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue