Merge pull request #1937 from zchamski/pr/spike-update-vendorized-tree

Update vendorized Spike tree and Spike-related components.
This commit is contained in:
JeanRochCoulon 2023-05-29 16:07:25 +02:00 committed by GitHub
commit e3ec071f66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
512 changed files with 19151 additions and 13856 deletions

View file

@ -152,8 +152,8 @@ pub_build_tools:
# If initially set to "__local__", SPIKE_INSTALL_DIR will be resolved
# to an absolute path by the installation script.
- source cva6/regress/install-spike.sh
# Strip locally built binaries and customext library to reduce artifact size.
- '[ -f $(pwd)/tools/spike/bin/spike ] && strip $(pwd)/tools/spike/bin/spike* $(pwd)/tools/spike/lib/libcustomext.so'
# Strip locally built binaries and libraries to reduce artifact size.
- '[ -f $(pwd)/tools/spike/bin/spike ] && strip $(pwd)/tools/spike/bin/spike* $(pwd)/tools/spike/lib/lib*.*'
artifacts:
paths:
- tools/spike/

View file

@ -196,7 +196,7 @@ endif
dpi-library = $(VCS_WORK_DIR)/work-dpi
dpi_build:
mkdir -p $(dpi-library)
g++ -shared -fPIC -std=c++0x -Bsymbolic -std=c++11 -I../corev_apu/tb/dpi -O3 -I$(SPIKE_ROOT)/include \
g++ -shared -fPIC -std=c++17 -Bsymbolic -I../corev_apu/tb/dpi -O3 -I$(SPIKE_ROOT)/include \
-I$(VCS_HOME)/include -I$(RISCV)/include -c $(CVA6_REPO_DIR)/corev_apu/tb/dpi/elfloader.cc \
-o $(dpi-library)/elfloader.o
g++ -shared -m64 -o $(dpi-library)/ariane_dpi.so $(dpi-library)/elfloader.o -L$(RISCV)/lib -Wl,-rpath,$(RISCV)/lib

View file

@ -0,0 +1,12 @@
diff --git a/fesvr/fesvr.mk.in b/fesvr/fesvr.mk.in
index 695de5278..43aed6786 100644
--- a/fesvr/fesvr.mk.in
+++ b/fesvr/fesvr.mk.in
@@ -20,6 +20,7 @@ fesvr_install_hdrs = $(fesvr_hdrs)
fesvr_install_config_hdr = yes
fesvr_install_lib = yes
+fesvr_install_shared_lib = yes
fesvr_srcs = \
elfloader.cc \

View file

@ -0,0 +1,15 @@
diff --git a/riscv/extensions.cc b/riscv/extensions.cc
index 347dc5e91..b488aad15 100644
--- a/riscv/extensions.cc
+++ b/riscv/extensions.cc
@@ -27,8 +27,8 @@ std::function<extension_t*()> find_extension(const char* name)
if (!dlh) {
dlh = dlopen(libdefault.c_str(), RTLD_LAZY);
if (!dlh) {
- fprintf(stderr, "couldn't find shared library either '%s' or '%s')\n",
- libname.c_str(), libdefault.c_str());
+ fprintf(stderr, "couldn't load shared library (either '%s' or '%s'), reason: %s\n",
+ libname.c_str(), libdefault.c_str(), dlerror());
exit(-1);
}

View file

@ -0,0 +1,533 @@
diff --git a/customext/customext.mk.in b/customext/customext.mk.in
index a14e771c2..888634b46 100644
--- a/customext/customext.mk.in
+++ b/customext/customext.mk.in
@@ -7,5 +7,6 @@ customext_subproject_deps = \
customext_srcs = \
dummy_rocc.cc \
cflush.cc \
+ cvxif.cc \
customext_install_shared_lib = yes
diff --git a/customext/cvxif.cc b/customext/cvxif.cc
new file mode 100644
index 000000000..dd1a7329a
--- /dev/null
+++ b/customext/cvxif.cc
@@ -0,0 +1,226 @@
+// Copyright (C) 2022 Thales DIS Design Services SAS
+//
+// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0.
+//
+// Original Author: Zbigniew CHAMSKI <zbigniew.chamski@thalesgroup.com>
+
+#include "cvxif.h"
+#include "mmu.h"
+#include <cstring>
+
+// Define custom insns templates.
+// The insn-level wrapper is 'c##n' (default implementation,
+// writeback disabled), the default implementation
+// is 'custom##n': illegal instruction, return 0.
+// The writeback controller 'cvxif_extn_t::do_writeback_p'
+// is in charge of determining if writeback is required or not.
+// Expected instruction encoding is 4 bytes.
+#define customX(n) \
+static reg_t c##n(processor_t* p, insn_t insn, reg_t pc) \
+ { \
+ cvxif_t* cvxif = static_cast<cvxif_t*>(p->get_extension()); \
+ cvxif_insn_t custom_insn; \
+ custom_insn.i = insn; \
+ reg_t xd = cvxif->default_custom##n(custom_insn); \
+ if (cvxif->do_writeback_p(custom_insn)) \
+ WRITE_RD(xd); \
+ return pc+4; \
+ } \
+ \
+ reg_t default_custom##n(cvxif_insn_t insn) \
+ { \
+ return custom##n(insn); \
+ }
+
+// This class instantiates the CV-X-IF interface.
+class cvxif_t : public cvxif_extn_t
+{
+ public:
+ const char* name() { return "cvxif_spike"; }
+
+ bool do_writeback_p(cvxif_insn_t copro_insn)
+ {
+ // INSN_R personality serves to simplify access to standard encoding fields.
+ cvxif_r_insn_t insn_r = copro_insn.r_type;
+
+ if (insn_r.opcode != MATCH_CUSTOM3)
+ return false;
+ else switch (insn_r.funct3)
+ {
+ case 0b000:
+ // CUSTOM_NOP and CUSTOM_EXC have rd == x0.
+ // Return TRUE if destination is NOT x0.
+ return (insn_r.rd != 0x0);
+
+ case 0b010:
+ // Return false for CUS_SD.
+ return false;
+
+ default:
+ // All other cases: writeback is assumed REQUIRED.
+ return true;
+ }
+ }
+
+ // Custom0 instructions: default behaviour.
+ reg_t custom0(cvxif_insn_t incoming_insn)
+ {
+ illegal_instruction();
+ return -1;
+ }
+
+ // Custom1 instructions: default behaviour.
+ reg_t custom1(cvxif_insn_t incoming_insn)
+ {
+ illegal_instruction();
+ return -1;
+ }
+
+ // Custom2 instructions: default behaviour.
+ reg_t custom2(cvxif_insn_t incoming_insn)
+ {
+ illegal_instruction();
+ return -1;
+ }
+
+ // Custom3 instructions: provide an explicit implementation of decode+exec.
+ reg_t custom3(cvxif_insn_t incoming_insn)
+ {
+ // Assume R-type insn: it shares opcode and funct3 fields with other CVXIF insn formats.
+ cvxif_r_insn_t r_insn = incoming_insn.r_type;
+ // INSN_T simplifies access to register values.
+ insn_t insn = incoming_insn.i;
+
+ switch (r_insn.funct3)
+ {
+ case 0:
+
+ // funct7[1:0] == 0b01: three-input RV add.
+ // If rd is x0: illegal instruction.
+ if ((r_insn.funct7 & 0x3) == 0b01)
+ {
+ if (insn.rd() == 0x0)
+ illegal_instruction();
+
+ // Destination is not x0: R4-type insn performing a 3-operand RV add
+ return (reg_t) ((reg_t) RS1 + (reg_t) RS2 + (reg_t) RS3);
+ }
+
+ // Non-memory operations (including NOP and EXC)
+ switch (r_insn.funct7 & 0b1111001)
+ {
+ case 0:
+ {
+ // Single-cycle RV addition with privilege: all non-privilege bits are zero.
+ // funct7[2:1] == 0x0 (PRV_U): CUS_ADD (single-cycle RV ADD, any mode)
+ // funct7[2:1] == 0x1 (PRV_S): CUS_S_ADD (single-cycle S-/M-mode RV ADD)
+ // funct7[2:1] == 0x2 (PRV_HS): ILLEGAL
+ // funct7[2:1] == 0x3 (PRV_M): CUS_M_ADD (single-cycle M-mode RV ADD)
+ reg_t required_priv = (r_insn.funct7 & 0x6) >> 1;
+ if (required_priv != PRV_HS && (p->get_state()->prv & required_priv) == required_priv)
+ return (reg_t) ((reg_t) RS1 + (reg_t) RS2);
+ else
+ illegal_instruction();
+ }
+
+ case 0x8:
+ // Multi-cycle RV add.
+ // TODO FIXME: Represent delay.
+ return (reg_t) ((reg_t) RS1 + (reg_t) RS2);
+
+ case 0x40:
+ // Exception. MCAUSE[4:0] encoded in RS1, MCAUSE[5] assumed to be 0.
+ if (insn.rd() == 0x0 && insn.rs2() == 0x0)
+ {
+ // Raise an exception only if registers rd and rs2 are both x0 (no 'bit 5' extension yet).
+ raise_exception(insn, insn.rs1());
+ // Writeback will be disabled by 'do_writeback_p'.
+ return (reg_t) -1;
+ }
+ else
+ // Illegal instruction.
+ illegal_instruction();
+
+ default:
+ illegal_instruction();
+ }
+
+ case 1:
+ // Perform RV load. If runtime XLEN is not 64, assume 32.
+ if (p->get_xlen() == 64)
+ return MMU.load_int64(RS1 + insn.i_imm());
+ else
+ return MMU.load_int32(RS1 + insn.i_imm());
+
+ case 2:
+ // Perform RV store. If runtime XLEN is not 64, assume 32.
+ if (p->get_xlen() == 64)
+ MMU.store_uint64(RS1 + insn.s_imm(), RS2);
+ else
+ MMU.store_uint32(RS1 + insn.s_imm(), RS2);
+
+ // Writeback will be disabled by 'do_writeback_p'.
+ break;
+
+ default:
+ illegal_instruction();
+ }
+
+ // FORNOW: Return 0xf......f to simplify debugging.
+ return (reg_t) -1;
+ }
+
+ cvxif_t()
+ {
+ }
+
+ void raise_exception(insn_t insn, reg_t exc_index)
+ {
+ switch (exc_index) {
+ case CAUSE_MISALIGNED_LOAD:
+ // Use 0x1 as perfectly unaligned address;-)
+ throw trap_load_address_misaligned((p ? p->get_state()->v : false), 1, 0, 0);
+ case CAUSE_LOAD_ACCESS:
+ // Use 0x1 as invalid address.
+ throw trap_load_access_fault((p ? p->get_state()->v : false), 1, 0, 0);
+ case CAUSE_MISALIGNED_STORE:
+ // Use 0x1 as perfectly unaligned address;-)
+ throw trap_store_address_misaligned((p ? p->get_state()->v : false), 1, 0, 0);
+ case CAUSE_STORE_ACCESS:
+ // Use 0x1 as invalid address.
+ throw trap_store_access_fault((p ? p->get_state()->v : false), 1, 0, 0);
+ case CAUSE_LOAD_PAGE_FAULT:
+ // Use 0x1 as always-faulting address.
+ throw trap_load_page_fault((p ? p->get_state()->v : false), 1, 0, 0);
+ case CAUSE_STORE_PAGE_FAULT:
+ // Use 0x1 as always-faulting address.
+ throw trap_store_page_fault((p ? p->get_state()->v : false), 1, 0, 0);
+ default:
+ illegal_instruction();
+ }
+ }
+
+ // Define templates of new instructions.
+ customX(0)
+ customX(1)
+ customX(2)
+ customX(3)
+
+ // Set instruction handlers for customN opcode patterns.
+ // NOTE: This method may need revisiting if multiple custom extensions are to be loaded
+ // simultaneously in the future.
+ std::vector<insn_desc_t> get_instructions()
+ {
+ std::vector<insn_desc_t> insns;
+ insns.push_back((insn_desc_t){0x0b, 0x7f, &::illegal_instruction, c0});
+ insns.push_back((insn_desc_t){0x2b, 0x7f, &::illegal_instruction, c1});
+ insns.push_back((insn_desc_t){0x5b, 0x7f, &::illegal_instruction, c2});
+ insns.push_back((insn_desc_t){0x7b, 0x7f, &c3, c3});
+ return insns;
+ }
+
+private:
+ // State variables go here.
+};
+
+REGISTER_EXTENSION(cvxif, []() { return new cvxif_t; })
diff --git a/customext/cvxif_test.c b/customext/cvxif_test.c
new file mode 100644
index 000000000..d39ca2229
--- /dev/null
+++ b/customext/cvxif_test.c
@@ -0,0 +1,111 @@
+// Copyright (C) 2022 Thales DIS Design Services SAS
+//
+// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0.
+//
+// Original Author: Zbigniew CHAMSKI <zbigniew.chamski@thalesgroup.com>
+//
+// The following is a RISC-V program to test the functionality of the
+// basic CVXIF accelerator interface on the Core-V side.
+// Compile using "riscv-none-elf-gcc -O2 cvxif_test.elf cvxif_test.c"
+// with -march=/-mabi= settings appropriate for your project.
+// Run using "spike -l --extension=cvxif cvxif_test.elf", adding
+// an --isa= setting appropriate for your project.
+//
+// Upon simulating the compiled program, the trace should contain five
+// instances of custom3 instructions (the third one being decoded as
+// 'unknown').
+// The last occurrence of 'custom3' instruction in the trace, encoded as
+// 0x8002007b, should be immediately followed by exception
+// 'trap_load_address_misaligned' with a tval equal to 0x1 and the
+// execution should terminate correctly with exit code 0.
+//
+// In 64-bit mode, the trace of the last occurrence of custom3
+// instruction should be equivalent to
+//
+// core 0: 0x0000000080002686 (0x8002007b) custom3 (args unknown)
+// core 0: exception trap_load_address_misaligned, epc 0x0000000080002686
+// core 0: tval 0x0000000000000001
+//
+// The corresponding trace in 32-bit mode should be equivalent to
+//
+// core 0: 0x8000205a (0x8002007b) custom3 (args unknown)
+// core 0: exception trap_load_address_misaligned, epc 0x8000205a
+// core 0: tval 0x00000001
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+// Values of MCAUSE corresponding to exceptions coming from the coprocessor I/F
+#define CAUSE_MISALIGNED_LOAD 0x4
+#define CAUSE_LOAD_ACCESS 0x5
+#define CAUSE_MISALIGNED_STORE 0x6
+#define CAUSE_STORE_ACCESS 0x7
+#define CAUSE_LOAD_PAGE_FAULT 0xd
+#define CAUSE_STORE_PAGE_FAULT 0xf
+#define CAUSE_COPROCESSOR_EXCEPTION 0x20
+
+// Value of TVAL to pass around.
+#define COPRO_TVAL_TEST 0x1a
+
+// Macro to read a CSR (from spike's "encoding.h")
+#define read_csr(reg) ({ unsigned long __tmp; \
+ asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
+ __tmp; })
+
+int main() {
+ // "unsigned long int" is always XLEN bits wide.
+ unsigned long int x = 123, y = 456, z = 0, t = 0;
+ static unsigned long int amem = 111, bmem = 0;
+ unsigned long a;
+
+ // Add x + y into z. Funct7 == 0, funct3 == 0x0.
+ asm volatile (".insn r CUSTOM_3, 0, 0, %0, %1, %2" : "=r"(z) : "r"(x), "r"(y));
+ if (z != 123 + 456)
+ {
+ // printf("FAILURE!!!\n");
+ return 1;
+ }
+
+ // Add three operands in a single R4-type add.
+ // Leverage current values of x, y and z (z == x + y).
+ asm volatile (".insn r CUSTOM_3, 0, 0x1, %0, %1, %2, %3" : "=r"(t) : "r"(x), "r"(y), "r"(z));
+ if (t != x + y + z)
+ {
+ // printf("FAILURE");
+ return 2;
+ }
+ // Load 'a' from 'amem'. CUSTOM_LD: opcode == CUSTOM_3, insn_type == I, funct3 == 0x1.
+ asm volatile (".insn i CUSTOM_3, 0x1, %0, %1" : "=r"(a) : "m"(amem), "I"(0));
+ if (a != 111)
+ {
+ // printf("FAILURE!!!\n");
+ return 3;
+ }
+
+ // Store 'a' in 'bmem'. CUSTOM_SD: opcode == CUSTOM_3, insn_type == S, funct3 == 0x2.
+ asm volatile (".insn s CUSTOM_3, 0x2, %0, %1" : : "r"(a), "m"(bmem));
+ if (bmem != 111)
+ {
+ // printf("FAILURE!!!\n");
+ return 4;
+ }
+
+ // Generate a misaligned load exception (mcause == 0x4).
+ asm volatile (".insn r CUSTOM_3, 0x0, 0x40, x0, x4, x0" : : );
+
+ // If we get here, then the exception test failed ==> exit with general failure code.
+ exit(1337);
+}
+
+// Override default trap handler.
+uintptr_t handle_trap(uintptr_t cause, uintptr_t epc, uintptr_t regs[32])
+{
+ if (cause == CAUSE_MISALIGNED_LOAD)
+ // Successfully terminate.
+ exit(0);
+ else
+ // Fail with explicit retcode.
+ exit(5);
+}
diff --git a/riscv/cvxif.h b/riscv/cvxif.h
new file mode 100644
index 000000000..e3a6fba1d
--- /dev/null
+++ b/riscv/cvxif.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2022 Thales DIS Design Services SAS
+//
+// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0.
+//
+// Original Author: Zbigniew CHAMSKI <zbigniew.chamski@thalesgroup.com>
+
+#ifndef _RISCV_CVXIF_H
+#define _RISCV_CVXIF_H
+
+#include "extension.h"
+
+// R-type instruction format
+struct cvxif_r_insn_t
+{
+ unsigned opcode : 7;
+ unsigned rd : 5;
+ unsigned funct3 : 3;
+ unsigned rs1 : 5;
+ unsigned rs2 : 5;
+ unsigned funct7 : 7;
+};
+
+// R4-type instruction format
+struct cvxif_r4_insn_t
+{
+ unsigned opcode : 7;
+ unsigned rd : 5;
+ unsigned funct3 : 3;
+ unsigned rs1 : 5;
+ unsigned rs2 : 5;
+ unsigned funct2 : 2;
+ unsigned rs3 : 5;
+};
+
+// I-type instruction format
+struct cvxif_i_insn_t
+{
+ unsigned opcode : 7;
+ unsigned rd : 5;
+ unsigned funct3 : 3;
+ unsigned rs1 : 5;
+ unsigned imm : 12;
+};
+
+// S-type instruction format
+struct cvxif_s_insn_t
+{
+ unsigned opcode : 7;
+ unsigned imm_low : 5;
+ unsigned funct3 : 3;
+ unsigned rs1 : 5;
+ unsigned rs2 : 5;
+ unsigned imm_high : 7;
+};
+
+union cvxif_insn_t
+{
+ cvxif_r_insn_t r_type;
+ cvxif_r4_insn_t r4_type;
+ cvxif_i_insn_t i_type;
+ cvxif_s_insn_t s_type;
+ insn_t i;
+};
+
+class cvxif_extn_t : public extension_t
+{
+ public:
+ virtual bool do_writeback_p(cvxif_insn_t insn);
+ virtual reg_t custom0(cvxif_insn_t insn);
+ virtual reg_t custom1(cvxif_insn_t insn);
+ virtual reg_t custom2(cvxif_insn_t insn);
+ virtual reg_t custom3(cvxif_insn_t insn);
+ std::vector<insn_desc_t> get_instructions();
+ std::vector<disasm_insn_t*> get_disasms();
+};
+
+#endif
diff --git a/riscv/cvxif_base.cc b/riscv/cvxif_base.cc
new file mode 100644
index 000000000..5097344c5
--- /dev/null
+++ b/riscv/cvxif_base.cc
@@ -0,0 +1,64 @@
+// Copyright (C) 2022 Thales DIS Design Services SAS
+//
+// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0.
+//
+// Original Author: Zbigniew CHAMSKI <zbigniew.chamski@thalesgroup.com>
+
+#include "cvxif.h"
+#include "trap.h"
+#include <cstdlib>
+
+// Virtual base class of CVXIF.
+
+// Return true if writeback is required.
+// Be on the safe side, disable writeback.
+bool cvxif_extn_t::do_writeback_p(cvxif_insn_t insn)
+{
+ return false;
+}
+
+// Define custom insns templates.
+// The insn-level wrapper is 'c##n' (default implementation,
+// writeback disabled), the default implementation
+// is 'custom##n': illegal instruction, return 0.
+// The writeback controller 'cvxif_extn_t::do_writeback_p'
+// is in charge of determining if writeback is required or not.
+// Expected instruction encoding is 4 bytes.
+#define customX(n) \
+static reg_t c##n(processor_t* p, insn_t insn, reg_t pc) \
+ { \
+ cvxif_extn_t* cvxif = static_cast<cvxif_extn_t*>(p->get_extension()); \
+ cvxif_insn_t custom_insn; \
+ custom_insn.i = insn; \
+ reg_t xd = cvxif->custom##n(custom_insn); \
+ if (cvxif->do_writeback_p(custom_insn)) \
+ WRITE_RD(xd); \
+ return pc+4; \
+ } \
+ \
+ reg_t cvxif_extn_t::custom##n(cvxif_insn_t insn) \
+ { \
+ illegal_instruction(); \
+ return -1; \
+ }
+
+customX(0)
+customX(1)
+customX(2)
+customX(3)
+
+std::vector<insn_desc_t> cvxif_extn_t::get_instructions()
+{
+ std::vector<insn_desc_t> insns;
+ insns.push_back((insn_desc_t){0x0b, 0x7f, &::illegal_instruction, c0});
+ insns.push_back((insn_desc_t){0x2b, 0x7f, &::illegal_instruction, c1});
+ insns.push_back((insn_desc_t){0x5b, 0x7f, &::illegal_instruction, c2});
+ insns.push_back((insn_desc_t){0x7b, 0x7f, &::illegal_instruction, c3});
+ return insns;
+}
+
+std::vector<disasm_insn_t*> cvxif_extn_t::get_disasms()
+{
+ std::vector<disasm_insn_t*> insns;
+ return insns;
+}
diff --git a/riscv/riscv.mk.in b/riscv/riscv.mk.in
index 56014662c..3da5f6d8b 100644
--- a/riscv/riscv.mk.in
+++ b/riscv/riscv.mk.in
@@ -26,6 +26,7 @@ riscv_hdrs = \
cfg.h \
common.h \
csrs.h \
+ cvxif.h \
debug_defines.h \
debug_module.h \
debug_rom_defines.h \
@@ -58,6 +59,7 @@
extension.cc \
extensions.cc \
rocc.cc \
+ cvxif_base.cc \
devices.cc \
rom.cc \
clint.cc \

View file

@ -5,3 +5,5 @@ autom4te.cache/
*.o
*.d
.gdb_history
.#*
*~

View file

@ -94,11 +94,11 @@ VPATH := $(addprefix $(src_dir)/, $(sprojs_enabled))
# highest.
default-CFLAGS := -DPREFIX=\"$(prefix)\" -Wall -Wno-unused -Wno-nonportable-include-path -g -O2 -fPIC
default-CXXFLAGS := $(default-CFLAGS) -std=c++11
default-CXXFLAGS := $(default-CFLAGS) -std=c++17
mcppbs-CPPFLAGS := @CPPFLAGS@
mcppbs-CFLAGS := $(default-CFLAGS) @CFLAGS@
mcppbs-CXXFLAGS := $(default-CXXFLAGS) @CXXFLAGS@
mcppbs-CXXFLAGS := $(mcppbs-CFLAGS) $(default-CXXFLAGS) @CXXFLAGS@
CC := @CC@
CXX := @CXX@
@ -219,7 +219,7 @@ $(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 $$@
$(COMPILE) -x c++-header $$< -c -o $$@
$$($(2)_objs) : %.o : %.cc $$($(2)_gen_hdrs) $$($(2)_pch)
$(COMPILE) $(if $(HAVE_CLANG_PCH), $$(if $$($(2)_pch), -include-pch $$($(2)_pch))) $$($(2)_CFLAGS) -c $$<
$$($(2)_c_objs) : %.o : %.c $$($(2)_gen_hdrs)
@ -241,7 +241,8 @@ $(2)_lib_libnames := $$(patsubst %, lib%.a, $$($(2)_lib_libs))
$(2)_lib_libarg := $$(patsubst %, -l%, $$($(2)_lib_libs))
$(2)_lib_libnames_shared := $$(if $$($(2)_install_shared_lib),lib$(1).so,)
lib$(1).a : $$($(2)_objs) $$($(2)_c_objs) $$($(2)_lib_libnames)
lib$(1).a : $$($(2)_objs) $$($(2)_c_objs)
rm -f $$@
$(AR) rcs $$@ $$^
lib$(1).so : $$($(2)_objs) $$($(2)_c_objs) $$($(2)_lib_libnames_shared) $$($(2)_lib_libnames)
$(LINK) -shared -o $$@ $(if $(filter Darwin,$(shell uname -s)),-install_name $(install_libs_dir)/$$@) $$^ $$($(2)_lib_libnames) $(LIBS)
@ -326,10 +327,6 @@ clean-$(1) :
# Update running variables
libs += lib$(1).a
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)

View file

@ -27,13 +27,28 @@ Spike supports the following RISC-V ISA features:
- Zbb extension, v1.0
- Zbc extension, v1.0
- Zbs extension, v1.0
- Zfh and Zfhmin half-precision floating-point extensions, v1.0
- Zfinx extension, v1.0
- Zmmul integer multiplication extension, v1.0
- Zicbom, Zicbop, Zicboz cache-block maintenance extensions, v1.0
- Conformance to both RVWMO and RVTSO (Spike is sequentially consistent)
- Machine, Supervisor, and User modes, v1.11
- Hypervisor extension, v1.0
- Svnapot extension, v1.0
- Svpbmt extension, v1.0
- Svinval extension, v1.0
- Debug v0.14
- Sdext extension, v1.0-STABLE
- Sdtrig extension, v1.0-STABLE
- 4 triggers support type={2, 3, 4, 5, 6, 15} (mcontrol, icount, itrigger, etrigger, mcontrol6, disabled)
- Smepmp extension v1.0
- Smstateen extension, v1.0
- Sscofpmf v0.5.2
- Zca extension, v1.0
- Zcb extension, v1.0
- Zcf extension, v1.0
- Zcd extension, v1.0
- Zcmp extension, v1.0
- Zcmt extension, v1.0
As a Spike extension, the remainder of the proposed
[Bit-Manipulation Extensions](https://github.com/riscv/riscv-bitmanip)
@ -41,6 +56,14 @@ is provided under the Spike-custom extension name _Xbitmanip_.
These instructions (and, of course, the extension name) are not RISC-V
standards.
These proposed bit-manipulation extensions can be split into further
groups: Zbp, Zbs, Zbe, Zbf, Zbc, Zbm, Zbr, Zbt. Note that Zbc is
ratified, but the original proposal contained some extra instructions
(64-bit carryless multiplies) which are captured here.
To enable these extensions individually, use the Spike-custom
extension names _XZbp_, _XZbs_, _XZbc_, and so on.
Versioning and APIs
-------------------
@ -211,7 +234,7 @@ OUTPUT_ARCH( "riscv" )
SECTIONS
{
. = 0x10010000;
. = 0x10110000;
.text : { *(.text) }
.data : { *(.data) }
}
@ -221,19 +244,19 @@ $ riscv64-unknown-elf-gcc -g -Og -T spike.lds -nostartfiles -o rot13-64 rot13-64
To debug this program, first run spike telling it to listen for OpenOCD:
```
$ spike --rbb-port=9824 -m0x10000000:0x20000 rot13-64
$ spike --rbb-port=9824 -m0x10100000: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
adapter driver remote_bitbang
remote_bitbang host localhost
remote_bitbang port 9824
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10e31913
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0xdeadbeef
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
@ -277,7 +300,7 @@ $2 = 0
(gdb) print text
$3 = "Vafgehpgvba frgf jnag gb or serr!"
(gdb) b done
Breakpoint 1 at 0x10010064: file rot13.c, line 22.
Breakpoint 1 at 0x10110064: file rot13.c, line 22.
(gdb) c
Continuing.
Disabling abstract command writes to CSRs.

View file

@ -0,0 +1,34 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv$(XLEN)-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;

View file

@ -0,0 +1,7 @@
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv32ec \
+signature=$(*).signature.output +signature-granularity=4\
$<
RUN_TARGET=\
$(RUN_CMD)

View file

@ -0,0 +1,7 @@
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv32e \
+signature=$(*).signature.output +signature-granularity=4\
$<
RUN_TARGET=\
$(RUN_CMD)

View file

@ -0,0 +1,7 @@
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv32em \
+signature=$(*).signature.output +signature-granularity=4\
$<
RUN_TARGET=\
$(RUN_CMD)

View file

@ -1,37 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv32-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv32ic \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -0,0 +1,7 @@
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv32if \
+signature=$(*).signature.output +signature-granularity=4\
$<
RUN_TARGET=\
$(RUN_CMD)

View file

@ -1,37 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv32-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv32i \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -1,37 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv32-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv32im \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -1,37 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv32-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv32i \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -1,37 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv32-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv32ic \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -1,37 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv64-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv64ic \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -0,0 +1,8 @@
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv64ifd \
+signature=$(*).signature.output +signature-granularity=4\
$<
RUN_TARGET=\
$(RUN_CMD)

View file

@ -1,37 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv64-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv64i \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -1,37 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv64-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv64im \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -1,38 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv64-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv64i \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -1,37 +1,4 @@
TARGET_SIM ?= spike
TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
$(error Target simulator executable '$(TARGET_SIM)` not found)
endif
RISCV_PREFIX ?= riscv64-unknown-elf-
RISCV_GCC ?= $(RISCV_PREFIX)gcc
RISCV_OBJDUMP ?= $(RISCV_PREFIX)objdump
RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
-I$(ROOTDIR)/riscv-test-suite/env/ \
-I$(TARGETDIR)/$(RISCV_TARGET)/ \
-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
$$(<) -o $$@
OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
COMPILE_TARGET=\
$(COMPILE_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ; \
$(OBJ_CMD); \
if [ $$$$? -ne 0 ] ; \
then \
echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
exit 1 ; \
fi ;
include $(TARGETDIR)/spike/device/Makefile_common.inc
RUN_CMD = $(TARGET_SIM) $(TARGET_FLAGS) --isa=rv64ic \
+signature=$(*).signature.output +signature-granularity=4\
$<

View file

@ -22,7 +22,7 @@
addi x1, x1, 4; \
li x1, 1; \
write_tohost: \
sw x1, tohost, t5; \
sw x1, tohost, t1; \
self_loop: j self_loop;
#define RVMODEL_BOOT

View file

@ -33,6 +33,9 @@
/* define if the Boost::ASIO library is available */
#undef HAVE_BOOST_ASIO
/* define if the Boost::Regex library is available */
#undef HAVE_BOOST_REGEX
/* Dynamic library loading is supported */
#undef HAVE_DLOPEN
@ -99,21 +102,9 @@
/* 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 support for running target in either endianness */
#undef RISCV_ENABLE_DUAL_ENDIAN
/* 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
@ -126,7 +117,7 @@
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Default value for --with-target switch */
/* Default value for --target switch */
#undef TARGET_ARCH
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most

View file

@ -715,10 +715,6 @@ with_isa
with_priv
with_varch
with_target
enable_commitlog
enable_histogram
enable_dirty
enable_misaligned
enable_dual_endian
'
ac_precious_vars='build_alias
@ -1362,12 +1358,6 @@ Optional Features:
--enable-stow Enable stow-based install
--enable-optional-subprojects
Enable all optional subprojects
--enable-commitlog Enable commit log generation
--enable-histogram Enable PC histogram generation
--enable-dirty Enable hardware management of PTE accessed and dirty
bits
--enable-misaligned Enable hardware support for misaligned loads and
stores
--enable-dual-endian Enable support for running target in either
endianness
@ -1391,7 +1381,8 @@ Optional Packages:
use the Regex library from boost - it is possible to
specify a certain library for the linker e.g.
--with-boost-regex=boost_regex-gcc-mt-d-1_33_1
--with-isa=RV64IMAFDC Sets the default RISC-V ISA
--with-isa=RV64IMAFDC_zicntr_zihpm
Sets the default RISC-V ISA
--with-priv=MSU Sets the default RISC-V privilege modes supported
--with-varch=vlen:128,elen:64
Sets the default vector config
@ -5886,7 +5877,7 @@ _ACEOF
else
cat >>confdefs.h <<_ACEOF
#define DEFAULT_ISA "RV64IMAFDC"
#define DEFAULT_ISA "RV64IMAFDC_zicntr_zihpm"
_ACEOF
fi
@ -6054,58 +6045,6 @@ else
fi
# Check whether --enable-commitlog was given.
if test "${enable_commitlog+set}" = set; then :
enableval=$enable_commitlog;
fi
if test "x$enable_commitlog" = "xyes"; then :
$as_echo "#define RISCV_ENABLE_COMMITLOG /**/" >>confdefs.h
fi
# Check whether --enable-histogram was given.
if test "${enable_histogram+set}" = set; then :
enableval=$enable_histogram;
fi
if test "x$enable_histogram" = "xyes"; then :
$as_echo "#define RISCV_ENABLE_HISTOGRAM /**/" >>confdefs.h
fi
# Check whether --enable-dirty was given.
if test "${enable_dirty+set}" = set; then :
enableval=$enable_dirty;
fi
if test "x$enable_dirty" = "xyes"; then :
$as_echo "#define RISCV_ENABLE_DIRTY /**/" >>confdefs.h
fi
# Check whether --enable-misaligned was given.
if test "${enable_misaligned+set}" = set; then :
enableval=$enable_misaligned;
fi
if test "x$enable_misaligned" = "xyes"; then :
$as_echo "#define RISCV_ENABLE_MISALIGNED /**/" >>confdefs.h
fi
# Check whether --enable-dual-endian was given.
if test "${enable_dual_endian+set}" = set; then :
enableval=$enable_dual_endian;

View file

@ -1,5 +1,6 @@
#include "insn_macros.h"
#include "extension.h"
#include "decode_macros.h"
#include <cstring>
struct : public arg_t {
@ -24,9 +25,9 @@ class cflush_t : public extension_t
std::vector<insn_desc_t> get_instructions() {
std::vector<insn_desc_t> insns;
insns.push_back((insn_desc_t){0xFC000073, 0xFFF07FFF, custom_cflush, custom_cflush});
insns.push_back((insn_desc_t){0xFC200073, 0xFFF07FFF, custom_cflush, custom_cflush});
insns.push_back((insn_desc_t){0xFC100073, 0xFFF07FFF, custom_cflush, custom_cflush});
insns.push_back((insn_desc_t){0xFC000073, 0xFFF07FFF, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush});
insns.push_back((insn_desc_t){0xFC200073, 0xFFF07FFF, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush});
insns.push_back((insn_desc_t){0xFC100073, 0xFFF07FFF, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush, custom_cflush});
return insns;
}

View file

@ -4,6 +4,8 @@
//
// Original Author: Zbigniew CHAMSKI <zbigniew.chamski@thalesgroup.com>
#define DECODE_MACRO_USAGE_LOGGED 1
#include "decode_macros.h"
#include "cvxif.h"
#include "mmu.h"
#include <cstring>
@ -43,7 +45,7 @@ class cvxif_t : public cvxif_extn_t
// INSN_R personality serves to simplify access to standard encoding fields.
cvxif_r_insn_t insn_r = copro_insn.r_type;
if (insn_r.opcode != MATCH_CUSTOM3)
if (insn_r.opcode != 0x7b /* MATCH_CUSTOM3 */)
return false;
else switch (insn_r.funct3)
{
@ -148,16 +150,16 @@ class cvxif_t : public cvxif_extn_t
case 1:
// Perform RV load. If runtime XLEN is not 64, assume 32.
if (p->get_xlen() == 64)
return MMU.load_int64(RS1 + insn.i_imm());
return MMU.load<int64_t>(RS1 + insn.i_imm());
else
return MMU.load_int32(RS1 + insn.i_imm());
return MMU.load<int32_t>(RS1 + insn.i_imm());
case 2:
// Perform RV store. If runtime XLEN is not 64, assume 32.
if (p->get_xlen() == 64)
MMU.store_uint64(RS1 + insn.s_imm(), RS2);
MMU.store<uint64_t>(RS1 + insn.s_imm(), RS2);
else
MMU.store_uint32(RS1 + insn.s_imm(), RS2);
MMU.store<uint32_t>(RS1 + insn.s_imm(), RS2);
// Writeback will be disabled by 'do_writeback_p'.
break;

View file

@ -27,7 +27,7 @@
// core 0: tval 0x0000000000000001
//
// The corresponding trace in 32-bit mode should be equivalent to
//
//
// core 0: 0x8000205a (0x8002007b) custom3 (args unknown)
// core 0: exception trap_load_address_misaligned, epc 0x8000205a
// core 0: tval 0x00000001

View file

@ -7,7 +7,7 @@ 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 custom0(rocc_insn_t insn, reg_t xs1, reg_t UNUSED xs2)
{
reg_t prev_acc = acc[insn.rs2];
@ -22,7 +22,7 @@ class dummy_rocc_t : public rocc_t
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);
acc[insn.rs2] = p->get_mmu()->load<uint64_t>(xs1);
break;
case 3: // acc[rs2] <- accX + xs1
acc[insn.rs2] += xs1;

View file

@ -18,7 +18,7 @@ all: $(patsubst %,%.h,$(ELFS))
$(OBJCOPY) -O binary --only-section .text $^ $@
debug_rom: $(DEPS)
$(COMPILE) -o $@ $^
$(COMPILE) -o $@ $<
clean:
rm -f $(ELFS) debug_rom*.raw debug_rom.h

View file

@ -23,7 +23,7 @@ _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
csrw CSR_DSCRATCH0, 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.
@ -46,14 +46,14 @@ _exception:
// Restore S0, which we always save to dscratch.
// We need this in case the user tried an abstract write to a
// non-existent CSR.
csrr s0, CSR_DSCRATCH
csrr s0, CSR_DSCRATCH0
sw zero, DEBUG_ROM_EXCEPTION(zero) // Let debug module know you got an exception.
ebreak
going:
csrr s0, CSR_MHARTID
sw s0, DEBUG_ROM_GOING(zero) // When debug module sees this write, the GO flag is reset.
csrr s0, CSR_DSCRATCH // Restore s0 here
csrr s0, CSR_DSCRATCH0 // Restore s0 here
fence
fence.i
jalr zero, zero, %lo(whereto) // Debug module will put different instructions and data in the RAM,
@ -63,7 +63,7 @@ going:
_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
csrr s0, CSR_DSCRATCH0 // Restore s0
dret
// END OF ACTUAL "ROM" CONTENTS. BELOW IS JUST FOR LINKER SCRIPT.

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,5 @@
fdt_subproject_deps = \
fdt_hdrs = \
fdt.h \
libfdt.h \
libfdt_env.h \
fdt_c_srcs = \
fdt.c \
fdt_ro.c \

View file

@ -49,7 +49,7 @@ void context_t::init(void (*f)(void*), void* a)
#ifdef USE_UCONTEXT
getcontext(context.get());
context->uc_link = creator->context.get();
context->uc_stack.ss_size = 64*1024;
context->uc_stack.ss_size = 1024 * 1024;
context->uc_stack.ss_sp = new void*[context->uc_stack.ss_size/sizeof(void*)];
#ifndef GLIBC_64BIT_PTR_BUG
makecontext(context.get(), (void(*)(void))&context_t::wrapper, 1, this);

File diff suppressed because it is too large Load diff

View file

@ -33,7 +33,7 @@ void device_t::handle_command(command_t cmd)
command_handlers[cmd.cmd()](cmd);
}
void device_t::handle_null_command(command_t cmd)
void device_t::handle_null_command(command_t)
{
}
@ -41,7 +41,6 @@ void device_t::handle_identify(command_t cmd)
{
size_t what = cmd.payload() % command_t::MAX_COMMANDS;
uint64_t addr = cmd.payload() / command_t::MAX_COMMANDS;
assert(addr % IDENTITY_SIZE == 0);
char id[IDENTITY_SIZE] = {0};
if (what == command_t::MAX_COMMANDS-1)

View file

@ -6,6 +6,7 @@
#include <cstring>
#include <string>
#include <functional>
#include <cstdint>
class memif_t;

View file

@ -1,6 +1,5 @@
#include "dtm.h"
#include "debug_defines.h"
#include "encoding.h"
#include "riscv/debug_defines.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@ -38,16 +37,13 @@
#define S1 9
#define AC_AR_REGNO(x) ((0x1000 | x) << AC_ACCESS_REGISTER_REGNO_OFFSET)
#define AC_AR_SIZE(x) (((x == 128)? 4 : (x == 64 ? 3 : 2)) << AC_ACCESS_REGISTER_SIZE_OFFSET)
#define AC_AR_SIZE(x) (((x == 128)? 4 : (x == 64 ? 3 : 2)) << AC_ACCESS_REGISTER_AARSIZE_OFFSET)
#define WRITE 1
#define SET 2
#define CLEAR 3
#define CSRRx(type, dst, csr, src) (0x73 | ((type) << 12) | ((dst) << 7) | ((src) << 15) | (uint32_t)((csr) << 20))
#define get_field(reg, mask) (((reg) & (mask)) / ((mask) & ~((mask) << 1)))
#define set_field(reg, mask, val) (((reg) & ~(mask)) | (((val) * ((mask) & ~((mask) << 1))) & (mask)))
#define RUN_AC_OR_DIE(a, b, c, d, e) { \
uint32_t cmderr = run_abstract_command(a, b, c, d, e); \
if (cmderr) { \
@ -79,22 +75,22 @@ void dtm_t::nop()
}
void dtm_t::select_hart(int hartsel) {
int dmcontrol = read(DMI_DMCONTROL);
write (DMI_DMCONTROL, set_field(dmcontrol, DMI_DMCONTROL_HARTSEL, hartsel));
int dmcontrol = read(DM_DMCONTROL);
write (DM_DMCONTROL, set_field(dmcontrol, DM_DMCONTROL_HASEL, hartsel));
current_hart = hartsel;
}
int dtm_t::enumerate_harts() {
int max_hart = (1 << DMI_DMCONTROL_HARTSEL_LENGTH) - 1;
write(DMI_DMCONTROL, set_field(read(DMI_DMCONTROL), DMI_DMCONTROL_HARTSEL, max_hart));
read(DMI_DMSTATUS);
max_hart = get_field(read(DMI_DMCONTROL), DMI_DMCONTROL_HARTSEL);
int max_hart = (1 << DM_DMCONTROL_HASEL_LENGTH) - 1;
write(DM_DMCONTROL, set_field(read(DM_DMCONTROL), DM_DMCONTROL_HASEL, max_hart));
read(DM_DMSTATUS);
max_hart = get_field(read(DM_DMCONTROL), DM_DMCONTROL_HASEL);
int hartsel;
for (hartsel = 0; hartsel <= max_hart; hartsel++) {
select_hart(hartsel);
int dmstatus = read(DMI_DMSTATUS);
if (get_field(dmstatus, DMI_DMSTATUS_ANYNONEXISTENT))
int dmstatus = read(DM_DMSTATUS);
if (get_field(dmstatus, DM_DMSTATUS_ANYNONEXISTENT))
break;
}
return hartsel;
@ -103,44 +99,44 @@ int dtm_t::enumerate_harts() {
void dtm_t::halt(int hartsel)
{
if (running) {
write(DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
write(DM_DMCONTROL, DM_DMCONTROL_DMACTIVE);
// Read dmstatus to avoid back-to-back writes to dmcontrol.
read(DMI_DMSTATUS);
read(DM_DMSTATUS);
}
int dmcontrol = DMI_DMCONTROL_HALTREQ | DMI_DMCONTROL_DMACTIVE;
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HARTSEL, hartsel);
write(DMI_DMCONTROL, dmcontrol);
int dmcontrol = DM_DMCONTROL_HALTREQ | DM_DMCONTROL_DMACTIVE;
dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HASEL, hartsel);
write(DM_DMCONTROL, dmcontrol);
int dmstatus;
do {
dmstatus = read(DMI_DMSTATUS);
} while(get_field(dmstatus, DMI_DMSTATUS_ALLHALTED) == 0);
dmcontrol &= ~DMI_DMCONTROL_HALTREQ;
write(DMI_DMCONTROL, dmcontrol);
dmstatus = read(DM_DMSTATUS);
} while(get_field(dmstatus, DM_DMSTATUS_ALLHALTED) == 0);
dmcontrol &= ~DM_DMCONTROL_HALTREQ;
write(DM_DMCONTROL, dmcontrol);
// Read dmstatus to avoid back-to-back writes to dmcontrol.
read(DMI_DMSTATUS);
read(DM_DMSTATUS);
current_hart = hartsel;
}
void dtm_t::resume(int hartsel)
{
int dmcontrol = DMI_DMCONTROL_RESUMEREQ | DMI_DMCONTROL_DMACTIVE;
dmcontrol = set_field(dmcontrol, DMI_DMCONTROL_HARTSEL, hartsel);
write(DMI_DMCONTROL, dmcontrol);
int dmcontrol = DM_DMCONTROL_RESUMEREQ | DM_DMCONTROL_DMACTIVE;
dmcontrol = set_field(dmcontrol, DM_DMCONTROL_HASEL, hartsel);
write(DM_DMCONTROL, dmcontrol);
int dmstatus;
do {
dmstatus = read(DMI_DMSTATUS);
} while (get_field(dmstatus, DMI_DMSTATUS_ALLRESUMEACK) == 0);
dmcontrol &= ~DMI_DMCONTROL_RESUMEREQ;
write(DMI_DMCONTROL, dmcontrol);
dmstatus = read(DM_DMSTATUS);
} while (get_field(dmstatus, DM_DMSTATUS_ALLRESUMEACK) == 0);
dmcontrol &= ~DM_DMCONTROL_RESUMEREQ;
write(DM_DMCONTROL, dmcontrol);
// Read dmstatus to avoid back-to-back writes to dmcontrol.
read(DMI_DMSTATUS);
read(DM_DMSTATUS);
current_hart = hartsel;
if (running) {
write(DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
write(DM_DMCONTROL, DM_DMCONTROL_DMACTIVE);
// Read dmstatus to avoid back-to-back writes to dmcontrol.
read(DMI_DMSTATUS);
read(DM_DMSTATUS);
}
}
@ -182,32 +178,32 @@ uint32_t dtm_t::run_abstract_command(uint32_t command,
assert(data_n <= data_words);
for (size_t i = 0; i < program_n; i++) {
write(DMI_PROGBUF0 + i, program[i]);
write(DM_PROGBUF0 + i, program[i]);
}
if (get_field(command, AC_ACCESS_REGISTER_WRITE) &&
get_field(command, AC_ACCESS_REGISTER_TRANSFER)) {
for (size_t i = 0; i < data_n; i++) {
write(DMI_DATA0 + i, data[i]);
write(DM_DATA0 + i, data[i]);
}
}
write(DMI_COMMAND, command);
write(DM_COMMAND, command);
// Wait for not busy and then check for error.
uint32_t abstractcs;
do {
abstractcs = read(DMI_ABSTRACTCS);
} while (abstractcs & DMI_ABSTRACTCS_BUSY);
abstractcs = read(DM_ABSTRACTCS);
} while (abstractcs & DM_ABSTRACTCS_BUSY);
if ((get_field(command, AC_ACCESS_REGISTER_WRITE) == 0) &&
get_field(command, AC_ACCESS_REGISTER_TRANSFER)) {
for (size_t i = 0; i < data_n; i++){
data[i] = read(DMI_DATA0 + i);
data[i] = read(DM_DATA0 + i);
}
}
return get_field(abstractcs, DMI_ABSTRACTCS_CMDERR);
return get_field(abstractcs, DM_ABSTRACTCS_CMDERR);
}
@ -317,24 +313,24 @@ void dtm_t::write_chunk(uint64_t taddr, size_t len, const void* src)
uint32_t abstractcs;
for (size_t i = 1; i < (len * 8 / xlen); i++){
if (i == 1) {
write(DMI_ABSTRACTAUTO, 1 << DMI_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
write(DM_ABSTRACTAUTO, 1 << DM_ABSTRACTAUTO_AUTOEXECDATA_OFFSET);
}
memcpy(data, curr, xlen/8);
curr += xlen/8;
if (xlen == 64) {
write(DMI_DATA0 + 1, data[1]);
write(DM_DATA0 + 1, data[1]);
}
write(DMI_DATA0, data[0]); //Triggers a command w/ autoexec.
write(DM_DATA0, data[0]); //Triggers a command w/ autoexec.
do {
abstractcs = read(DMI_ABSTRACTCS);
} while (abstractcs & DMI_ABSTRACTCS_BUSY);
if ( get_field(abstractcs, DMI_ABSTRACTCS_CMDERR)) {
die(get_field(abstractcs, DMI_ABSTRACTCS_CMDERR));
abstractcs = read(DM_ABSTRACTCS);
} while (abstractcs & DM_ABSTRACTCS_BUSY);
if ( get_field(abstractcs, DM_ABSTRACTCS_CMDERR)) {
die(get_field(abstractcs, DM_ABSTRACTCS_CMDERR));
}
}
if ((len * 8 / xlen) > 1) {
write(DMI_ABSTRACTAUTO, 0);
write(DM_ABSTRACTAUTO, 0);
}
restore_reg(S0, s0);
@ -360,7 +356,7 @@ void dtm_t::die(uint32_t cmderr)
//throw std::runtime_error("Debug Abstract Command Error #" + std::to_string(cmderr) + "(" + msg + ")");
printf("ERROR: %s:%d, Debug Abstract Command Error #%d (%s)", __FILE__, __LINE__, cmderr, msg);
printf("ERROR: %s:%d, Should die, but allowing simulation to continue and fail.", __FILE__, __LINE__);
write(DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
write(DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
}
@ -458,9 +454,9 @@ uint64_t dtm_t::modify_csr(unsigned which, uint64_t data, uint32_t type)
RUN_AC_OR_DIE(command, prog, sizeof(prog) / sizeof(*prog), adata, xlen/(4*8));
uint64_t res = read(DMI_DATA0);//adata[0];
uint64_t res = read(DM_DATA0);//adata[0];
if (xlen == 64)
res |= read(DMI_DATA0 + 1);//((uint64_t) adata[1]) << 32;
res |= read(DM_DATA0 + 1);//((uint64_t) adata[1]) << 32;
resume(current_hart);
return res;
@ -490,13 +486,13 @@ uint32_t dtm_t::get_xlen()
abort();
return 128;
}
write(DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
write(DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
cmderr = run_abstract_command(command | AC_AR_SIZE(64), prog, 0, data, 0);
if (cmderr == 0){
return 64;
}
write(DMI_ABSTRACTCS, DMI_ABSTRACTCS_CMDERR);
write(DM_ABSTRACTCS, DM_ABSTRACTCS_CMDERR);
cmderr = run_abstract_command(command | AC_AR_SIZE(32), prog, 0, data, 0);
if (cmderr == 0){
@ -545,7 +541,7 @@ void dtm_t::reset()
// In theory any hart can handle the memory accesses,
// this will enforce that hart 0 handles them.
select_hart(0);
read(DMI_DMSTATUS);
read(DM_DMSTATUS);
}
void dtm_t::idle()
@ -560,23 +556,23 @@ void dtm_t::producer_thread()
// depend on in this code.
// Enable the debugger.
write(DMI_DMCONTROL, DMI_DMCONTROL_DMACTIVE);
write(DM_DMCONTROL, DM_DMCONTROL_DMACTIVE);
// Poll until the debugger agrees it's enabled.
while ((read(DMI_DMCONTROL) & DMI_DMCONTROL_DMACTIVE) == 0) ;
while ((read(DM_DMCONTROL) & DM_DMCONTROL_DMACTIVE) == 0) ;
// These are checked every time we run an abstract command.
uint32_t abstractcs = read(DMI_ABSTRACTCS);
ram_words = get_field(abstractcs, DMI_ABSTRACTCS_PROGSIZE);
data_words = get_field(abstractcs, DMI_ABSTRACTCS_DATACOUNT);
uint32_t abstractcs = read(DM_ABSTRACTCS);
ram_words = get_field(abstractcs, DM_ABSTRACTCS_PROGBUFSIZE);
data_words = get_field(abstractcs, DM_ABSTRACTCS_DATACOUNT);
// These things are only needed for the 'modify_csr' function.
// That could be re-written to not use these at some performance
// overhead.
uint32_t hartinfo = read(DMI_HARTINFO);
assert(get_field(hartinfo, DMI_HARTINFO_NSCRATCH) > 0);
assert(get_field(hartinfo, DMI_HARTINFO_DATAACCESS));
uint32_t hartinfo = read(DM_HARTINFO);
assert(get_field(hartinfo, DM_HARTINFO_NSCRATCH) > 0);
assert(get_field(hartinfo, DM_HARTINFO_DATAACCESS));
data_base = get_field(hartinfo, DMI_HARTINFO_DATAADDR);
data_base = get_field(hartinfo, DM_HARTINFO_DATAADDR);
num_harts = enumerate_harts();
halt(0);

View file

@ -64,6 +64,16 @@ class dtm_t : public htif_t
virtual void reset() override;
virtual void idle() override;
uint32_t run_abstract_command(uint32_t command, const uint32_t program[], size_t program_n,
uint32_t data[], size_t data_n);
void die(uint32_t cmderr);
void halt(int);
int enumerate_harts();
void select_hart(int);
void resume(int);
uint32_t get_data_base() { return data_base; };
private:
context_t host;
context_t* target;
@ -76,14 +86,6 @@ class dtm_t : public htif_t
resp resp_buf;
bool running;
uint32_t run_abstract_command(uint32_t command, const uint32_t program[], size_t program_n,
uint32_t data[], size_t data_n);
void die(uint32_t cmderr);
void halt(int);
int enumerate_harts();
void select_hart(int);
void resume(int);
uint64_t save_reg(unsigned regno);
void restore_reg(unsigned regno, uint64_t val);

View file

@ -1,5 +1,6 @@
// See LICENSE for license details.
#include "config.h"
#include "elf.h"
#include "memif.h"
#include "byteorder.h"
@ -16,22 +17,28 @@
#include <vector>
#include <map>
std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t* entry)
std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t* entry, unsigned required_xlen = 0)
{
int fd = open(fn, O_RDONLY);
struct stat s;
assert(fd != -1);
if (fd == -1)
throw std::invalid_argument(std::string("Specified ELF can't be opened: ") + strerror(errno));
if (fstat(fd, &s) < 0)
abort();
size_t size = s.st_size;
char* buf = (char*)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
assert(buf != MAP_FAILED);
if (buf == MAP_FAILED)
throw std::invalid_argument(std::string("Specified ELF can't be mapped: ") + strerror(errno));
close(fd);
assert(size >= sizeof(Elf64_Ehdr));
const Elf64_Ehdr* eh64 = (const Elf64_Ehdr*)buf;
assert(IS_ELF32(*eh64) || IS_ELF64(*eh64));
unsigned xlen = IS_ELF32(*eh64) ? 32 : 64;
if (required_xlen != 0 && required_xlen != xlen) {
throw incompat_xlen(required_xlen, xlen);
}
assert(IS_ELFLE(*eh64) || IS_ELFBE(*eh64));
assert(IS_ELF_EXEC(*eh64));
assert(IS_ELF_RISCV(*eh64) || IS_ELF_EM_NONE(*eh64));
@ -94,7 +101,9 @@ std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t*
} while (0)
if (IS_ELFLE(*eh64)) {
memif->set_target_endianness(memif_endianness_little);
if (memif->get_target_endianness() != endianness_little) {
throw std::invalid_argument("Specified ELF is little endian, but system uses a big-endian memory system. Rerun without --big-endian");
}
if (IS_ELF32(*eh64))
LOAD_ELF(Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym, from_le);
else
@ -103,7 +112,9 @@ std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t*
#ifndef RISCV_ENABLE_DUAL_ENDIAN
throw std::invalid_argument("Specified ELF is big endian. Configure with --enable-dual-endian to enable support");
#else
memif->set_target_endianness(memif_endianness_big);
if (memif->get_target_endianness() != endianness_big) {
throw std::invalid_argument("Specified ELF is big endian, but system uses a little-endian memory system. Rerun with --big-endian");
}
if (IS_ELF32(*eh64))
LOAD_ELF(Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym, from_be);
else

View file

@ -8,6 +8,6 @@
#include <string>
class memif_t;
std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t* entry);
std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t* entry, unsigned required_xlen = 0);
#endif

View file

@ -1,4 +1,4 @@
fesvr_hdrs = \
fesvr_install_hdrs = \
byteorder.h \
elf.h \
elfloader.h \
@ -15,8 +15,6 @@ fesvr_hdrs = \
rfb.h \
tsi.h \
fesvr_install_hdrs = $(fesvr_hdrs)
fesvr_install_config_hdr = yes
fesvr_install_lib = yes

View file

@ -1,16 +1,20 @@
// See LICENSE for license details.
#include "config.h"
#include "htif.h"
#include "rfb.h"
#include "elfloader.h"
#include "platform.h"
#include "byteorder.h"
#include "trap.h"
#include "../riscv/common.h"
#include <algorithm>
#include <assert.h>
#include <vector>
#include <queue>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <stdio.h>
#include <unistd.h>
@ -80,12 +84,25 @@ htif_t::~htif_t()
void htif_t::start()
{
if (!targs.empty() && targs[0] != "none")
if (!targs.empty() && targs[0] != "none") {
try {
load_program();
} catch (const incompat_xlen & err) {
fprintf(stderr, "Error: cannot execute %d-bit program on RV%d hart\n", err.actual_xlen, err.expected_xlen);
exit(1);
}
}
reset();
}
static void bad_address(const std::string& situation, reg_t addr)
{
std::cerr << "Access exception occurred while " << situation << ":\n";
std::cerr << "Memory address 0x" << std::hex << addr << " is invalid\n";
exit(-1);
}
std::map<std::string, uint64_t> htif_t::load_payload(const std::string& payload, reg_t* entry)
{
std::string path;
@ -96,6 +113,12 @@ std::map<std::string, uint64_t> htif_t::load_payload(const std::string& payload,
std::string test_path = PREFIX TARGET_DIR + payload;
if (access(test_path.c_str(), F_OK) == 0)
path = test_path;
else
throw std::runtime_error(
"could not open " + payload + "; searched paths:\n" +
"\t. (current directory)\n" +
"\t" + PREFIX TARGET_DIR + " (based on configured --prefix and --with-target)"
);
}
if (path.empty())
@ -119,7 +142,12 @@ std::map<std::string, uint64_t> htif_t::load_payload(const std::string& payload,
htif_t* htif;
} preload_aware_memif(this);
return load_elf(path.c_str(), &preload_aware_memif, entry);
try {
return load_elf(path.c_str(), &preload_aware_memif, entry, expected_xlen);
} catch (mem_trap_t& t) {
bad_address("loading payload " + payload, t.get_tval());
abort();
}
}
void htif_t::load_program()
@ -134,26 +162,39 @@ void htif_t::load_program()
}
// detect torture tests so we can print the memory signature at the end
if (symbols.count("begin_signature") && symbols.count("end_signature"))
{
if (symbols.count("begin_signature") && symbols.count("end_signature")) {
sig_addr = symbols["begin_signature"];
sig_len = symbols["end_signature"] - sig_addr;
}
for (auto payload : payloads)
{
for (auto payload : payloads) {
reg_t dummy_entry;
load_payload(payload, &dummy_entry);
}
for (auto i : symbols)
{
auto it = addr2symbol.find(i.second);
if ( it == addr2symbol.end())
addr2symbol[i.second] = i.first;
}
class nop_memif_t : public memif_t {
public:
nop_memif_t(htif_t* htif) : memif_t(htif), htif(htif) {}
void read(addr_t UNUSED addr, size_t UNUSED len, void UNUSED *bytes) override {}
void write(addr_t UNUSED taddr, size_t UNUSED len, const void UNUSED *src) override {}
private:
htif_t* htif;
} nop_memif(this);
return;
reg_t nop_entry;
for (auto &s : symbol_elfs) {
std::map<std::string, uint64_t> other_symbols = load_elf(s.c_str(), &nop_memif, &nop_entry,
expected_xlen);
symbols.merge(other_symbols);
}
for (auto i : symbols) {
auto it = addr2symbol.find(i.second);
if ( it == addr2symbol.end())
addr2symbol[i.second] = i.first;
}
return;
}
const char* htif_t::get_symbol(uint64_t addr)
@ -218,19 +259,37 @@ int htif_t::run()
while (!signal_exit && exitcode == 0)
{
if (auto tohost = from_target(mem.read_uint64(tohost_addr))) {
mem.write_uint64(tohost_addr, target_endian<uint64_t>::zero);
command_t cmd(mem, tohost, fromhost_callback);
device_list.handle_command(cmd);
} else {
idle();
uint64_t tohost;
try {
if ((tohost = from_target(mem.read_uint64(tohost_addr))) != 0)
mem.write_uint64(tohost_addr, target_endian<uint64_t>::zero);
} catch (mem_trap_t& t) {
bad_address("accessing tohost", t.get_tval());
}
device_list.tick();
try {
if (tohost != 0) {
command_t cmd(mem, tohost, fromhost_callback);
device_list.handle_command(cmd);
} else {
idle();
}
if (!fromhost_queue.empty() && !mem.read_uint64(fromhost_addr)) {
mem.write_uint64(fromhost_addr, to_target(fromhost_queue.front()));
fromhost_queue.pop();
device_list.tick();
} catch (mem_trap_t& t) {
std::stringstream tohost_hex;
tohost_hex << std::hex << tohost;
bad_address("host was accessing memory on behalf of target (tohost = 0x" + tohost_hex.str() + ")", t.get_tval());
}
try {
if (!fromhost_queue.empty() && !mem.read_uint64(fromhost_addr)) {
mem.write_uint64(fromhost_addr, to_target(fromhost_queue.front()));
fromhost_queue.pop();
}
} catch (mem_trap_t& t) {
bad_address("accessing fromhost", t.get_tval());
}
}
@ -282,7 +341,12 @@ void htif_t::parse_arguments(int argc, char ** argv)
break;
case HTIF_LONG_OPTIONS_OPTIND + 5:
line_size = atoi(optarg);
break;
case HTIF_LONG_OPTIONS_OPTIND + 6:
targs.push_back(optarg);
break;
case HTIF_LONG_OPTIONS_OPTIND + 7:
symbol_elfs.push_back(optarg);
break;
case '?':
if (!opterr)
@ -318,9 +382,17 @@ void htif_t::parse_arguments(int argc, char ** argv)
c = HTIF_LONG_OPTIONS_OPTIND + 4;
optarg = optarg + 9;
}
else if(arg.find("+signature-granularity=")==0){
c = HTIF_LONG_OPTIONS_OPTIND + 5;
optarg = optarg + 23;
else if (arg.find("+signature-granularity=") == 0) {
c = HTIF_LONG_OPTIONS_OPTIND + 5;
optarg = optarg + 23;
}
else if (arg.find("+target-argument=") == 0) {
c = HTIF_LONG_OPTIONS_OPTIND + 6;
optarg = optarg + 17;
}
else if (arg.find("+symbol-elf=") == 0) {
c = HTIF_LONG_OPTIONS_OPTIND + 7;
optarg = optarg + 12;
}
else if (arg.find("+permissive-off") == 0) {
if (opterr)

View file

@ -26,33 +26,28 @@ class htif_t : public chunked_memif_t
int run();
bool done();
int exit_code();
void set_expected_xlen(unsigned int m) { expected_xlen = m; }
virtual memif_t& memif() { return mem; }
template<typename T> inline T from_target(target_endian<T> n) const
{
#ifdef RISCV_ENABLE_DUAL_ENDIAN
memif_endianness_t endianness = get_target_endianness();
assert(endianness == memif_endianness_little || endianness == memif_endianness_big);
endianness_t endianness = get_target_endianness();
assert(endianness == endianness_little || endianness == endianness_big);
return endianness == memif_endianness_big? n.from_be() : n.from_le();
#else
return n.from_le();
#endif
return endianness == endianness_big? n.from_be() : n.from_le();
}
template<typename T> inline target_endian<T> to_target(T n) const
{
#ifdef RISCV_ENABLE_DUAL_ENDIAN
memif_endianness_t endianness = get_target_endianness();
assert(endianness == memif_endianness_little || endianness == memif_endianness_big);
endianness_t endianness = get_target_endianness();
assert(endianness == endianness_little || endianness == endianness_big);
return endianness == memif_endianness_big? target_endian<T>::to_be(n) : target_endian<T>::to_le(n);
#else
return target_endian<T>::to_le(n);
#endif
return endianness == endianness_big? target_endian<T>::to_be(n) : target_endian<T>::to_le(n);
}
addr_t get_tohost_addr() { return tohost_addr; }
addr_t get_fromhost_addr() { return fromhost_addr; }
protected:
virtual void reset() = 0;
@ -68,12 +63,13 @@ class htif_t : public chunked_memif_t
virtual void idle() {}
const std::vector<std::string>& host_args() { return hargs; }
const std::vector<std::string>& target_args() { return targs; }
reg_t get_entry_point() { return entry; }
// indicates that the initial program load can skip writing this address
// range to memory, because it has already been loaded through a sideband
virtual bool is_address_preloaded(addr_t taddr, size_t len) { return false; }
virtual bool is_address_preloaded(addr_t, size_t) { return false; }
// Given an address, return symbol from addr2symbol map
const char* get_symbol(uint64_t addr);
@ -82,7 +78,7 @@ class htif_t : public chunked_memif_t
void parse_arguments(int argc, char ** argv);
void register_devices();
void usage(const char * program_name);
unsigned int expected_xlen = 0;
memif_t mem;
reg_t entry;
bool writezeros;
@ -103,8 +99,7 @@ class htif_t : public chunked_memif_t
std::vector<device_t*> dynamic_devices;
std::vector<std::string> payloads;
const std::vector<std::string>& target_args() { return targs; }
std::vector<std::string> symbol_elfs;
std::map<uint64_t, std::string> addr2symbol;
friend class memif_t;
@ -133,6 +128,8 @@ class htif_t : public chunked_memif_t
+chroot=PATH\n\
--payload=PATH Load PATH memory as an additional ELF payload\n\
+payload=PATH\n\
--symbol-elf=PATH Populate the symbol table with the ELF file at PATH\n\
+symbol-elf=PATH\n\
\n\
HOST OPTIONS (currently unsupported)\n\
--disk=DISK Add DISK device. Use a ramdisk since this isn't\n\
@ -150,7 +147,9 @@ TARGET (RISC-V BINARY) OPTIONS\n\
{"signature", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 2 }, \
{"chroot", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 3 }, \
{"payload", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 4 }, \
{"signature-granularity", optional_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 5 }, \
{"signature-granularity", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 5 }, \
{"target-argument", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 6 }, \
{"symbol-elf", required_argument, 0, HTIF_LONG_OPTIONS_OPTIND + 7 }, \
{0, 0, 0, 0}
#endif // __HTIF_H

View file

@ -21,7 +21,7 @@ protected:
void read_chunk(addr_t taddr, size_t len, void* dst);
void write_chunk(addr_t taddr, size_t len, const void* src);
void clear_chunk(addr_t taddr, size_t len) {}
void clear_chunk(addr_t, size_t) {}
size_t chunk_max_size() { return width; }
size_t chunk_align() { return width; }

View file

@ -5,18 +5,14 @@
#include <stdint.h>
#include <stddef.h>
#include <stdexcept>
#include "byteorder.h"
#include "../riscv/cfg.h"
typedef uint64_t reg_t;
typedef int64_t sreg_t;
typedef reg_t addr_t;
typedef enum {
memif_endianness_undecided,
memif_endianness_little,
memif_endianness_big
} memif_endianness_t;
class chunked_memif_t
{
public:
@ -27,10 +23,11 @@ public:
virtual size_t chunk_align() = 0;
virtual size_t chunk_max_size() = 0;
virtual void set_target_endianness(memif_endianness_t endianness) {}
virtual memif_endianness_t get_target_endianness() const {
return memif_endianness_undecided;
virtual endianness_t get_target_endianness() const {
return endianness_little;
}
virtual ~chunked_memif_t() = default;
};
class memif_t
@ -68,10 +65,7 @@ public:
virtual void write_int64(addr_t addr, target_endian<int64_t> val);
// endianness
virtual void set_target_endianness(memif_endianness_t endianness) {
cmemif->set_target_endianness(endianness);
}
virtual memif_endianness_t get_target_endianness() const {
virtual endianness_t get_target_endianness() const {
return cmemif->get_target_endianness();
}
@ -79,4 +73,11 @@ protected:
chunked_memif_t* cmemif;
};
class incompat_xlen : public std::exception {
public:
const unsigned expected_xlen;
const unsigned actual_xlen;
incompat_xlen(unsigned _expected_xlen, unsigned _actual_xlen) : expected_xlen(_expected_xlen), actual_xlen(_actual_xlen) {}
};
#endif // __MEMIF_H

View file

@ -119,7 +119,7 @@ void rfb_t::set_pixel_format(const std::string& s)
throw std::runtime_error("bad pixel format");
}
void rfb_t::fb_update(const std::string& s)
void rfb_t::fb_update()
{
std::string u;
u += str(uint8_t(0));
@ -153,7 +153,7 @@ void rfb_t::tick()
std::swap(fb1, fb2);
if (pthread_mutex_trylock(&lock) == 0)
{
fb_update("");
fb_update();
pthread_mutex_unlock(&lock);
}
}

View file

@ -25,7 +25,7 @@ class rfb_t : public device_t
void thread_main();
friend void* rfb_thread_main(void*);
std::string pixel_format();
void fb_update(const std::string& s);
void fb_update();
void set_encodings(const std::string& s);
void set_pixel_format(const std::string& s);
void write(const std::string& s);

View file

@ -1,5 +1,6 @@
// See LICENSE for license details.
#include "config.h"
#include "syscall.h"
#include "htif.h"
#include "byteorder.h"
@ -17,6 +18,10 @@ using namespace std::placeholders;
#define RISCV_AT_FDCWD -100
#ifdef __GNUC__
# pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
struct riscv_stat
{
target_endian<uint64_t> dev;
@ -169,9 +174,16 @@ syscall_t::syscall_t(htif_t* htif)
if (stdin_fd < 0 || stdout_fd0 < 0 || stdout_fd1 < 0)
throw std::runtime_error("could not dup stdin/stdout");
fds.alloc(stdin_fd); // stdin -> stdin
fds.alloc(stdout_fd0); // stdout -> stdout
fds.alloc(stdout_fd1); // stderr -> stdout
fds_index.push_back(fds.alloc(stdin_fd)); // stdin -> stdin
fds_index.push_back(fds.alloc(stdout_fd0)); // stdout -> stdout
fds_index.push_back(fds.alloc(stdout_fd1)); // stderr -> stdout
}
syscall_t::~syscall_t() {
for (auto i: fds_index) {
close(fds.lookup(i));
fds.dealloc(i);
}
}
std::string syscall_t::do_chroot(const char* fn)

View file

@ -28,6 +28,7 @@ class syscall_t : public device_t
{
public:
syscall_t(htif_t*);
~syscall_t();
void set_chroot(const char* where);
@ -38,6 +39,7 @@ class syscall_t : public device_t
memif_t* memif;
std::vector<syscall_func_t> table;
fds_t fds;
std::vector<reg_t> fds_index;
void handle_syscall(command_t cmd);
void dispatch(addr_t mm);

View file

@ -1,7 +1,7 @@
prefix=@prefix@
exec_prefix=@prefix@
libdir=${prefix}/@libdir@
includedir=${prefix}/@includedir@
libdir=@libdir@
includedir=@includedir@
Name: riscv-disasm
Description: RISC-V disassembler

View file

@ -1,7 +1,7 @@
prefix=@prefix@
exec_prefix=@prefix@
libdir=${prefix}/@libdir@
includedir=${prefix}/@includedir@
libdir=@libdir@
includedir=@includedir@
Name: riscv-fesvr
Description: RISC-V front-end server

View file

@ -0,0 +1,14 @@
#ifndef _RISCV_ABSTRACT_INTERRUPT_CONTROLLER_H
#define _RISCV_ABSTRACT_INTERRUPT_CONTROLLER_H
#include "decode.h"
#include <cstdint>
#include <cstddef>
class abstract_interrupt_controller_t {
public:
virtual void set_interrupt_level(uint32_t interrupt_id, int level) = 0;
virtual ~abstract_interrupt_controller_t() {}
};
#endif

View file

@ -20,7 +20,6 @@ inline uint64_t mulhu(uint64_t a, uint64_t b)
y2 = t >> 32;
t = a0*b1 + y1;
y1 = t;
t = a1*b1 + y2 + (t >> 32);
y2 = t;
@ -188,6 +187,15 @@ static inline int clz(uint64_t val)
return res;
}
// Count number of contiguous 1 bits starting from the LSB.
static inline int cto(uint64_t val)
{
int res = 0;
while ((val & 1) == 1)
val >>= 1, res++;
return res;
}
static inline int log2(uint64_t val)
{
if (!val)

View file

@ -39,9 +39,9 @@ cache_sim_t* cache_sim_t::construct(const char* config, const char* name)
void cache_sim_t::init()
{
if(sets == 0 || (sets & (sets-1)))
if (sets == 0 || (sets & (sets-1)))
help();
if(linesz < 8 || (linesz & (linesz-1)))
if (linesz < 8 || (linesz & (linesz-1)))
help();
idx_shift = 0;
@ -76,9 +76,6 @@ cache_sim_t::~cache_sim_t()
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;
@ -159,6 +156,31 @@ void cache_sim_t::access(uint64_t addr, size_t bytes, bool store)
*check_tag(addr) |= DIRTY;
}
void cache_sim_t::clean_invalidate(uint64_t addr, size_t bytes, bool clean, bool inval)
{
uint64_t start_addr = addr & ~(linesz-1);
uint64_t end_addr = (addr + bytes + linesz-1) & ~(linesz-1);
uint64_t cur_addr = start_addr;
while (cur_addr < end_addr) {
uint64_t* hit_way = check_tag(cur_addr);
if (likely(hit_way != NULL))
{
if (clean) {
if (*hit_way & DIRTY) {
writebacks++;
*hit_way &= ~DIRTY;
}
}
if (inval)
*hit_way &= ~VALID;
}
cur_addr += linesz;
}
if (miss_handler)
miss_handler->clean_invalidate(addr, bytes, clean, inval);
}
fa_cache_sim_t::fa_cache_sim_t(size_t ways, size_t linesz, const char* name)
: cache_sim_t(1, ways, linesz, name)
{

View file

@ -4,6 +4,7 @@
#define _RISCV_CACHE_SIM_H
#include "memtracer.h"
#include "common.h"
#include <cstring>
#include <string>
#include <map>
@ -27,6 +28,7 @@ class cache_sim_t
virtual ~cache_sim_t();
void access(uint64_t addr, size_t bytes, bool store);
void clean_invalidate(uint64_t addr, size_t bytes, bool clean, bool inval);
void print_stats();
void set_miss_handler(cache_sim_t* mh) { miss_handler = mh; }
void set_log(bool _log) { log = _log; }
@ -90,10 +92,18 @@ class cache_memtracer_t : public memtracer_t
{
cache->set_miss_handler(mh);
}
void clean_invalidate(uint64_t addr, size_t bytes, bool clean, bool inval)
{
cache->clean_invalidate(addr, bytes, clean, inval);
}
void set_log(bool log)
{
cache->set_log(log);
}
void print_stats()
{
cache->print_stats();
}
protected:
cache_sim_t* cache;
@ -102,8 +112,9 @@ class cache_memtracer_t : public memtracer_t
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)
icache_sim_t(const char* config, const char* name = "I$")
: cache_memtracer_t(config, name) {}
bool interested_in_range(uint64_t UNUSED begin, uint64_t UNUSED end, access_type type)
{
return type == FETCH;
}
@ -116,8 +127,9 @@ class icache_sim_t : public cache_memtracer_t
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)
dcache_sim_t(const char* config, const char* name = "D$")
: cache_memtracer_t(config, name) {}
bool interested_in_range(uint64_t UNUSED begin, uint64_t UNUSED end, access_type type)
{
return type == LOAD || type == STORE;
}

27
vendor/riscv/riscv-isa-sim/riscv/cfg.cc vendored Normal file
View file

@ -0,0 +1,27 @@
// See LICENSE for license details.
#include "cfg.h"
#include "mmu.h"
#include "decode.h"
mem_cfg_t::mem_cfg_t(reg_t base, reg_t size) : base(base), size(size)
{
assert(mem_cfg_t::check_if_supported(base, size));
}
bool mem_cfg_t::check_if_supported(reg_t base, reg_t size)
{
// The truth of these conditions should be ensured by whatever is creating
// the regions in the first place, but we have them here to make sure that
// we can't end up describing memory regions that don't make sense. They
// ask that the page size is a multiple of the minimum page size, that the
// page is aligned to the minimum page size, that the page is non-empty and
// that the top address is still representable in a reg_t.
//
// Note: (base + size == 0) part of the assertion is to handle cases like
// { base = 0xffff_ffff_ffff_f000, size: 0x1000 }
return (size % PGSIZE == 0) &&
(base % PGSIZE == 0) &&
(size > 0) &&
((base + size > base) || (base + size == 0));
}

109
vendor/riscv/riscv-isa-sim/riscv/cfg.h vendored Normal file
View file

@ -0,0 +1,109 @@
// See LICENSE for license details.
#ifndef _RISCV_CFG_H
#define _RISCV_CFG_H
#include <optional>
#include <vector>
#include "decode.h"
#include <cassert>
typedef enum {
endianness_little,
endianness_big
} endianness_t;
template <typename T>
class cfg_arg_t {
public:
cfg_arg_t(T default_val)
: value(default_val), was_set(false) {}
bool overridden() const { return was_set; }
T operator()() const { return value; }
T operator=(const T v) {
value = v;
was_set = true;
return value;
}
private:
T value;
bool was_set;
};
// Configuration that describes a memory region
class mem_cfg_t
{
public:
static bool check_if_supported(reg_t base, reg_t size);
mem_cfg_t(reg_t base, reg_t size);
reg_t get_base() const {
return base;
}
reg_t get_size() const {
return size;
}
reg_t get_inclusive_end() const {
return base + size - 1;
}
private:
reg_t base;
reg_t size;
};
class cfg_t
{
public:
cfg_t(std::pair<reg_t, reg_t> default_initrd_bounds,
const char *default_bootargs,
const char *default_isa, const char *default_priv,
const char *default_varch,
const bool default_misaligned,
const endianness_t default_endianness,
const reg_t default_pmpregions,
const std::vector<mem_cfg_t> &default_mem_layout,
const std::vector<size_t> default_hartids,
bool default_real_time_clint,
const reg_t default_trigger_count)
: initrd_bounds(default_initrd_bounds),
bootargs(default_bootargs),
isa(default_isa),
priv(default_priv),
varch(default_varch),
misaligned(default_misaligned),
endianness(default_endianness),
pmpregions(default_pmpregions),
mem_layout(default_mem_layout),
hartids(default_hartids),
explicit_hartids(false),
real_time_clint(default_real_time_clint),
trigger_count(default_trigger_count)
{}
cfg_arg_t<std::pair<reg_t, reg_t>> initrd_bounds;
cfg_arg_t<const char *> bootargs;
cfg_arg_t<const char *> isa;
cfg_arg_t<const char *> priv;
cfg_arg_t<const char *> varch;
bool misaligned;
endianness_t endianness;
reg_t pmpregions;
cfg_arg_t<std::vector<mem_cfg_t>> mem_layout;
std::optional<reg_t> start_pc;
cfg_arg_t<std::vector<size_t>> hartids;
bool explicit_hartids;
cfg_arg_t<bool> real_time_clint;
reg_t trigger_count;
size_t nprocs() const { return hartids().size(); }
size_t max_hartid() const { return hartids().back(); }
};
#endif

View file

@ -1,9 +1,10 @@
#include <sys/time.h>
#include "devices.h"
#include "processor.h"
#include "simif.h"
clint_t::clint_t(std::vector<processor_t*>& procs, uint64_t freq_hz, bool real_time)
: procs(procs), freq_hz(freq_hz), real_time(real_time), mtime(0), mtimecmp(procs.size())
clint_t::clint_t(simif_t* sim, uint64_t freq_hz, bool real_time)
: sim(sim), freq_hz(freq_hz), real_time(real_time), mtime(0)
{
struct timeval base;
@ -11,6 +12,7 @@ clint_t::clint_t(std::vector<processor_t*>& procs, uint64_t freq_hz, bool real_t
real_time_ref_secs = base.tv_sec;
real_time_ref_usecs = base.tv_usec;
increment(0);
}
/* 0000 msip hart 0
@ -29,16 +31,29 @@ clint_t::clint_t(std::vector<processor_t*>& procs, uint64_t freq_hz, bool real_t
bool clint_t::load(reg_t addr, size_t len, uint8_t* bytes)
{
if (len > 8)
return false;
increment(0);
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->read() & 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);
if (addr >= MSIP_BASE && addr < MTIMECMP_BASE) {
if (len == 8) {
// Implement double-word loads as a pair of word loads
return load(addr, 4, bytes) && load(addr + 4, 4, bytes + 4);
}
const auto hart_id = (addr - MSIP_BASE) / sizeof(msip_t);
const msip_t res = sim->get_harts().count(hart_id) && (sim->get_harts().at(hart_id)->state.mip->read() & MIP_MSIP);
read_little_endian_reg(res, addr, len, bytes);
return true;
} else if (addr >= MTIMECMP_BASE && addr < MTIME_BASE) {
const auto hart_id = (addr - MTIMECMP_BASE) / sizeof(mtimecmp_t);
const mtime_t res = sim->get_harts().count(hart_id) ? mtimecmp[hart_id] : 0;
read_little_endian_reg(res, addr, len, bytes);
} else if (addr >= MTIME_BASE && addr < MTIME_BASE + sizeof(mtime_t)) {
read_little_endian_reg(mtime, addr, len, bytes);
} else if (addr + len <= CLINT_SIZE) {
memset(bytes, 0, len);
} else {
return false;
}
@ -47,21 +62,31 @@ bool clint_t::load(reg_t addr, size_t len, uint8_t* bytes)
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->backdoor_write_with_mask(MIP_MSIP, 0);
if (!!(msip[i] & 1))
procs[i]->state.mip->backdoor_write_with_mask(MIP_MSIP, MIP_MSIP);
if (len > 8)
return false;
if (addr >= MSIP_BASE && addr < MTIMECMP_BASE) {
if (len == 8) {
// Implement double-word stores as a pair of word stores
return store(addr, 4, bytes) && store(addr + 4, 4, bytes + 4);
}
} 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);
if (addr % sizeof(msip_t) == 0) { // ignore in-between bytes
msip_t msip = 0;
write_little_endian_reg(&msip, addr, len, bytes);
const auto hart_id = (addr - MSIP_BASE) / sizeof(msip_t);
if (sim->get_harts().count(hart_id))
sim->get_harts().at(hart_id)->state.mip->backdoor_write_with_mask(MIP_MSIP, msip & 1 ? MIP_MSIP : 0);
}
} else if (addr >= MTIMECMP_BASE && addr < MTIME_BASE) {
const auto hart_id = (addr - MTIMECMP_BASE) / sizeof(mtimecmp_t);
if (sim->get_harts().count(hart_id))
write_little_endian_reg(&mtimecmp[hart_id], addr, len, bytes);
} else if (addr >= MTIME_BASE && addr < MTIME_BASE + sizeof(mtime_t)) {
write_little_endian_reg(&mtime, addr, len, bytes);
} else if (addr + len <= CLINT_SIZE) {
// Do nothing
} else {
return false;
}
@ -81,9 +106,9 @@ void clint_t::increment(reg_t inc)
} else {
mtime += inc;
}
for (size_t i = 0; i < procs.size(); i++) {
procs[i]->state.mip->backdoor_write_with_mask(MIP_MTIP, 0);
if (mtime >= mtimecmp[i])
procs[i]->state.mip->backdoor_write_with_mask(MIP_MTIP, MIP_MTIP);
for (const auto& [hart_id, hart] : sim->get_harts()) {
hart->state.time->sync(mtime);
hart->state.mip->backdoor_write_with_mask(MIP_MTIP, mtime >= mtimecmp[hart_id] ? MIP_MTIP : 0);
}
}

View file

@ -8,11 +8,15 @@
# define unlikely(x) __builtin_expect(x, 0)
# define NOINLINE __attribute__ ((noinline))
# define NORETURN __attribute__ ((noreturn))
# define ALWAYS_INLINE __attribute__ ((always_inline))
# define UNUSED __attribute__ ((unused))
#else
# define likely(x) (x)
# define unlikely(x) (x)
# define NOINLINE
# define NORETURN
# define ALWAYS_INLINE
# define UNUSED
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -2,12 +2,15 @@
#ifndef _RISCV_CSRS_H
#define _RISCV_CSRS_H
#include "common.h"
#include "encoding.h"
// For reg_t:
#include "decode.h"
// For std::shared_ptr
#include <memory>
// For access_type:
#include "memtracer.h"
#include <cassert>
class processor_t;
struct state_t;
@ -52,23 +55,29 @@ class csr_t {
private:
const unsigned csr_priv;
const bool csr_read_only;
// For access to written_value() and unlogged_write():
friend class rv32_high_csr_t;
friend class rv32_low_csr_t;
};
typedef std::shared_ptr<csr_t> csr_t_p;
// Basic CSRs, with XLEN bits fully readable and writable.
class basic_csr_t: public csr_t {
public:
basic_csr_t(processor_t* const proc, const reg_t addr, const reg_t init);
virtual reg_t read() const noexcept override;
virtual reg_t read() const noexcept override {
return val;
}
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
private:
reg_t val;
};
class pmpaddr_csr_t: public csr_t {
public:
pmpaddr_csr_t(processor_t* const proc, const reg_t addr);
@ -84,6 +93,11 @@ class pmpaddr_csr_t: public csr_t {
// Is the specified access allowed given the pmpcfg privileges?
bool access_ok(access_type type, reg_t mode) const noexcept;
// To check lock bit status from outside like mseccfg
bool is_locked() const noexcept {
return cfg & PMP_L;
}
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
private:
@ -112,11 +126,24 @@ typedef std::shared_ptr<pmpaddr_csr_t> pmpaddr_csr_t_p;
class pmpcfg_csr_t: public csr_t {
public:
pmpcfg_csr_t(processor_t* const proc, const reg_t addr);
virtual void verify_permissions(insn_t insn, bool write) const override;
virtual reg_t read() const noexcept override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class mseccfg_csr_t: public basic_csr_t {
public:
mseccfg_csr_t(processor_t* const proc, const reg_t addr);
virtual void verify_permissions(insn_t insn, bool write) const override;
bool get_mml() const noexcept;
bool get_mmwp() const noexcept;
bool get_rlb() const noexcept;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
typedef std::shared_ptr<mseccfg_csr_t> mseccfg_csr_t_p;
// For CSRs that have a virtualized copy under another name. Each
// instance of virtualized_csr_t will read/write one of two CSRs,
@ -153,7 +180,6 @@ class epc_csr_t: public csr_t {
reg_t val;
};
// For mtvec, stvec, and vstvec
class tvec_csr_t: public csr_t {
public:
@ -166,7 +192,6 @@ class tvec_csr_t: public csr_t {
reg_t val;
};
// For mcause, scause, and vscause
class cause_csr_t: public basic_csr_t {
public:
@ -175,13 +200,15 @@ class cause_csr_t: public basic_csr_t {
virtual reg_t read() const noexcept override;
};
// For *status family of CSRs
class base_status_csr_t: public csr_t {
public:
base_status_csr_t(processor_t* const proc, const reg_t addr);
// Return true if the specified bits are not 00 (Off)
bool enabled(const reg_t which);
bool field_exists(const reg_t which) {
return (sstatus_write_mask & which) != 0;
}
protected:
reg_t adjust_sd(const reg_t val) const noexcept;
void maybe_flush_tlb(const reg_t newval) noexcept;
@ -194,13 +221,16 @@ class base_status_csr_t: public csr_t {
typedef std::shared_ptr<base_status_csr_t> base_status_csr_t_p;
// For vsstatus, which is its own separate architectural register
// (unlike sstatus)
class vsstatus_csr_t: public base_status_csr_t {
class vsstatus_csr_t final: public base_status_csr_t {
public:
vsstatus_csr_t(processor_t* const proc, const reg_t addr);
virtual reg_t read() const noexcept override;
reg_t read() const noexcept override {
return val;
}
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
private:
@ -209,75 +239,107 @@ class vsstatus_csr_t: public base_status_csr_t {
typedef std::shared_ptr<vsstatus_csr_t> vsstatus_csr_t_p;
class sstatus_proxy_csr_t: public base_status_csr_t {
public:
sstatus_proxy_csr_t(processor_t* const proc, const reg_t addr, csr_t_p mstatus);
virtual reg_t read() const noexcept override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
private:
csr_t_p mstatus;
};
class mstatus_csr_t: public base_status_csr_t {
class mstatus_csr_t final: public base_status_csr_t {
public:
mstatus_csr_t(processor_t* const proc, const reg_t addr);
virtual reg_t read() const noexcept override;
reg_t read() const noexcept override {
return val;
}
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
private:
reg_t compute_mstatus_initial_value() const noexcept;
reg_t val;
friend class mstatush_csr_t;
};
typedef std::shared_ptr<mstatus_csr_t> mstatus_csr_t_p;
class mstatush_csr_t: public csr_t {
class mnstatus_csr_t final: public basic_csr_t {
public:
mstatush_csr_t(processor_t* const proc, const reg_t addr, mstatus_csr_t_p mstatus);
mnstatus_csr_t(processor_t* const proc, const reg_t addr);
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
// For RV32 CSRs that are split into two, e.g. mstatus/mstatush
// CSRW should only modify the lower half
class rv32_low_csr_t: public csr_t {
public:
rv32_low_csr_t(processor_t* const proc, const reg_t addr, csr_t_p orig);
virtual reg_t read() const noexcept override;
virtual void verify_permissions(insn_t insn, bool write) const override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
virtual reg_t written_value() const noexcept override;
private:
csr_t_p orig;
};
class rv32_high_csr_t: public csr_t {
public:
rv32_high_csr_t(processor_t* const proc, const reg_t addr, csr_t_p orig);
virtual reg_t read() const noexcept override;
virtual void verify_permissions(insn_t insn, bool write) const override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
virtual reg_t written_value() const noexcept override;
private:
csr_t_p orig;
};
class sstatus_proxy_csr_t final: public base_status_csr_t {
public:
sstatus_proxy_csr_t(processor_t* const proc, const reg_t addr, mstatus_csr_t_p mstatus);
reg_t read() const noexcept override {
return mstatus->read() & sstatus_read_mask;
}
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
private:
mstatus_csr_t_p mstatus;
const reg_t mask;
};
typedef std::shared_ptr<sstatus_proxy_csr_t> sstatus_proxy_csr_t_p;
class sstatus_csr_t: public virtualized_csr_t {
public:
sstatus_csr_t(processor_t* const proc, base_status_csr_t_p orig, base_status_csr_t_p virt);
sstatus_csr_t(processor_t* const proc, sstatus_proxy_csr_t_p orig, vsstatus_csr_t_p virt);
// Set FS, VS, or XS bits to dirty
void dirty(const reg_t dirties);
// Return true if the specified bits are not 00 (Off)
bool enabled(const reg_t which);
private:
base_status_csr_t_p orig_sstatus;
base_status_csr_t_p virt_sstatus;
sstatus_proxy_csr_t_p orig_sstatus;
vsstatus_csr_t_p virt_sstatus;
};
typedef std::shared_ptr<sstatus_csr_t> sstatus_csr_t_p;
class misa_csr_t: public basic_csr_t {
class misa_csr_t final: public basic_csr_t {
public:
misa_csr_t(processor_t* const proc, const reg_t addr, const reg_t max_isa);
bool extension_enabled(unsigned char ext) const noexcept;
bool extension_enabled(unsigned char ext) const noexcept {
assert(ext >= 'A' && ext <= 'Z');
return (read() >> (ext - 'A')) & 1;
}
bool extension_enabled_const(unsigned char ext) const noexcept;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
private:
const reg_t max_isa;
const reg_t write_mask;
reg_t dependency(const reg_t val, const char feature, const char depends_on) const noexcept;
};
typedef std::shared_ptr<misa_csr_t> misa_csr_t_p;
class mip_or_mie_csr_t: public csr_t {
public:
mip_or_mie_csr_t(processor_t* const proc, const reg_t addr);
@ -292,7 +354,6 @@ class mip_or_mie_csr_t: public csr_t {
virtual reg_t write_mask() const noexcept = 0;
};
// mip is special because some of the bits are driven by hardware pins
class mip_csr_t: public mip_or_mie_csr_t {
public:
@ -306,7 +367,6 @@ class mip_csr_t: public mip_or_mie_csr_t {
typedef std::shared_ptr<mip_csr_t> mip_csr_t_p;
class mie_csr_t: public mip_or_mie_csr_t {
public:
mie_csr_t(processor_t* const proc, const reg_t addr);
@ -316,7 +376,6 @@ class mie_csr_t: public mip_or_mie_csr_t {
typedef std::shared_ptr<mie_csr_t> mie_csr_t_p;
// For sip, hip, hvip, vsip, sie, hie, vsie which are all just (masked
// & shifted) views into mip or mie. Each pair will have one of these
// objects describing the view, e.g. one for sip+sie, one for hip+hie,
@ -348,7 +407,6 @@ class generic_int_accessor_t {
typedef std::shared_ptr<generic_int_accessor_t> generic_int_accessor_t_p;
// For all CSRs that are simply (masked & shifted) views into mip
class mip_proxy_csr_t: public csr_t {
public:
@ -371,8 +429,6 @@ class mie_proxy_csr_t: public csr_t {
generic_int_accessor_t_p accr;
};
class mideleg_csr_t: public basic_csr_t {
public:
mideleg_csr_t(processor_t* const proc, const reg_t addr);
@ -382,7 +438,6 @@ class mideleg_csr_t: public basic_csr_t {
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class medeleg_csr_t: public basic_csr_t {
public:
medeleg_csr_t(processor_t* const proc, const reg_t addr);
@ -393,7 +448,6 @@ class medeleg_csr_t: public basic_csr_t {
const reg_t hypervisor_exceptions;
};
// For CSRs with certain bits hardwired
class masked_csr_t: public basic_csr_t {
public:
@ -404,6 +458,22 @@ class masked_csr_t: public basic_csr_t {
const reg_t mask;
};
// henvcfg.pbmte is read_only 0 when menvcfg.pbmte = 0
// henvcfg.stce is read_only 0 when menvcfg.stce = 0
// henvcfg.hade is read_only 0 when menvcfg.hade = 0
class henvcfg_csr_t final: public masked_csr_t {
public:
henvcfg_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init, csr_t_p menvcfg);
reg_t read() const noexcept override {
return (menvcfg->read() | ~(MENVCFG_PBMTE | MENVCFG_STCE | MENVCFG_HADE)) & masked_csr_t::read();
}
virtual void verify_permissions(insn_t insn, bool write) const override;
private:
csr_t_p menvcfg;
};
// For satp and vsatp
// These are three classes in order to handle the [V]TVM bits permission checks
@ -435,17 +505,15 @@ class virtualized_satp_csr_t: public virtualized_csr_t {
satp_csr_t_p orig_satp;
};
// For minstret, which is always 64 bits, but in RV32 is split into
// high and low halves. The first class always holds the full 64-bit
// value.
class minstret_csr_t: public csr_t {
// For minstret and mcycle, which are always 64 bits, but in RV32 are
// split into high and low halves. The first class always holds the
// full 64-bit value.
class wide_counter_csr_t: public csr_t {
public:
minstret_csr_t(processor_t* const proc, const reg_t addr);
wide_counter_csr_t(processor_t* const proc, const reg_t addr);
// Always returns full 64-bit value
virtual reg_t read() const noexcept override;
void bump(const reg_t howmuch) noexcept;
void write_upper_half(const reg_t val) noexcept;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
virtual reg_t written_value() const noexcept override;
@ -453,22 +521,22 @@ class minstret_csr_t: public csr_t {
reg_t val;
};
typedef std::shared_ptr<minstret_csr_t> minstret_csr_t_p;
typedef std::shared_ptr<wide_counter_csr_t> wide_counter_csr_t_p;
// A simple proxy to read/write the upper half of minstret
class minstreth_csr_t: public csr_t {
class time_counter_csr_t: public csr_t {
public:
minstreth_csr_t(processor_t* const proc, const reg_t addr, minstret_csr_t_p minstret);
time_counter_csr_t(processor_t* const proc, const reg_t addr);
virtual reg_t read() const noexcept override;
void sync(const reg_t val) noexcept;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
virtual bool unlogged_write(const reg_t UNUSED val) noexcept override { return false; };
private:
minstret_csr_t_p minstret;
reg_t shadow_val;
};
typedef std::shared_ptr<minstreth_csr_t> minstreth_csr_t_p;
typedef std::shared_ptr<time_counter_csr_t> time_counter_csr_t_p;
// For a CSR that is an alias of another
class proxy_csr_t: public csr_t {
@ -481,7 +549,6 @@ class proxy_csr_t: public csr_t {
csr_t_p delegate;
};
// For a CSR with a fixed, unchanging value
class const_csr_t: public csr_t {
public:
@ -493,7 +560,6 @@ class const_csr_t: public csr_t {
const reg_t val;
};
// For a CSR that is an unprivileged accessor of a privileged counter
class counter_proxy_csr_t: public proxy_csr_t {
public:
@ -503,6 +569,12 @@ class counter_proxy_csr_t: public proxy_csr_t {
bool myenable(csr_t_p counteren) const noexcept;
};
class mevent_csr_t: public basic_csr_t {
public:
mevent_csr_t(processor_t* const proc, const reg_t addr);
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
// For machine-level CSRs that only exist with Hypervisor
class hypervisor_csr_t: public basic_csr_t {
@ -511,7 +583,6 @@ class hypervisor_csr_t: public basic_csr_t {
virtual void verify_permissions(insn_t insn, bool write) const override;
};
class hideleg_csr_t: public masked_csr_t {
public:
hideleg_csr_t(processor_t* const proc, const reg_t addr, csr_t_p mideleg);
@ -520,7 +591,6 @@ class hideleg_csr_t: public masked_csr_t {
csr_t_p mideleg;
};
class hgatp_csr_t: public basic_csr_t {
public:
hgatp_csr_t(processor_t* const proc, const reg_t addr);
@ -529,7 +599,6 @@ class hgatp_csr_t: public basic_csr_t {
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class tselect_csr_t: public basic_csr_t {
public:
tselect_csr_t(processor_t* const proc, const reg_t addr);
@ -537,7 +606,6 @@ class tselect_csr_t: public basic_csr_t {
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class tdata1_csr_t: public csr_t {
public:
tdata1_csr_t(processor_t* const proc, const reg_t addr);
@ -548,13 +616,26 @@ class tdata1_csr_t: public csr_t {
class tdata2_csr_t: public csr_t {
public:
tdata2_csr_t(processor_t* const proc, const reg_t addr, const size_t count);
tdata2_csr_t(processor_t* const proc, const reg_t addr);
virtual reg_t read() const noexcept override;
reg_t read(const size_t idx) const noexcept;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
private:
std::vector<reg_t> vals;
};
class tdata3_csr_t: public csr_t {
public:
tdata3_csr_t(processor_t* const proc, const reg_t addr);
virtual reg_t read() const noexcept override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class tinfo_csr_t: public csr_t {
public:
tinfo_csr_t(processor_t* const proc, const reg_t addr);
virtual reg_t read() const noexcept override;
protected:
virtual bool unlogged_write(const reg_t UNUSED val) noexcept override { return false; };
};
// For CSRs that are only writable from debug mode
@ -564,9 +645,6 @@ class debug_mode_csr_t: public basic_csr_t {
virtual void verify_permissions(insn_t insn, bool write) const override;
};
typedef std::shared_ptr<tdata2_csr_t> tdata2_csr_t_p;
class dpc_csr_t: public epc_csr_t {
public:
dpc_csr_t(processor_t* const proc, const reg_t addr);
@ -594,8 +672,7 @@ class dcsr_csr_t: public csr_t {
typedef std::shared_ptr<dcsr_csr_t> dcsr_csr_t_p;
class float_csr_t: public masked_csr_t {
class float_csr_t final: public masked_csr_t {
public:
float_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init);
virtual void verify_permissions(insn_t insn, bool write) const override;
@ -603,6 +680,7 @@ class float_csr_t: public masked_csr_t {
virtual bool unlogged_write(const reg_t val) noexcept override;
};
typedef std::shared_ptr<float_csr_t> float_csr_t_p;
// For a CSR like FCSR, that is actually a view into multiple
// underlying registers.
@ -620,7 +698,6 @@ class composite_csr_t: public csr_t {
const unsigned upper_lsb;
};
class seed_csr_t: public csr_t {
public:
seed_csr_t(processor_t* const proc, const reg_t addr);
@ -630,7 +707,6 @@ class seed_csr_t: public csr_t {
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class vector_csr_t: public basic_csr_t {
public:
vector_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init=0);
@ -645,7 +721,6 @@ class vector_csr_t: public basic_csr_t {
typedef std::shared_ptr<vector_csr_t> vector_csr_t_p;
// For CSRs shared between Vector and P extensions (vxsat)
class vxsat_csr_t: public masked_csr_t {
public:
@ -655,4 +730,59 @@ class vxsat_csr_t: public masked_csr_t {
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class hstateen_csr_t: public masked_csr_t {
public:
hstateen_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init, uint8_t index);
virtual reg_t read() const noexcept override;
virtual void verify_permissions(insn_t insn, bool write) const override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
protected:
uint8_t index;
};
class sstateen_csr_t: public hstateen_csr_t {
public:
sstateen_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init, uint8_t index);
virtual reg_t read() const noexcept override;
virtual void verify_permissions(insn_t insn, bool write) const override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class senvcfg_csr_t final: public masked_csr_t {
public:
senvcfg_csr_t(processor_t* const proc, const reg_t addr, const reg_t mask, const reg_t init);
virtual void verify_permissions(insn_t insn, bool write) const override;
};
class stimecmp_csr_t: public basic_csr_t {
public:
stimecmp_csr_t(processor_t* const proc, const reg_t addr, const reg_t imask);
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
private:
reg_t intr_mask;
};
class virtualized_stimecmp_csr_t: public virtualized_csr_t {
public:
virtualized_stimecmp_csr_t(processor_t* const proc, csr_t_p orig, csr_t_p virt);
virtual void verify_permissions(insn_t insn, bool write) const override;
};
class scountovf_csr_t: public csr_t {
public:
scountovf_csr_t(processor_t* const proc, const reg_t addr);
virtual void verify_permissions(insn_t insn, bool write) const override;
virtual reg_t read() const noexcept override;
protected:
virtual bool unlogged_write(const reg_t val) noexcept override;
};
class jvt_csr_t: public basic_csr_t {
public:
jvt_csr_t(processor_t* const proc, const reg_t addr, const reg_t init);
virtual void verify_permissions(insn_t insn, bool write) const override;
};
#endif

View file

@ -4,6 +4,8 @@
//
// Original Author: Zbigniew CHAMSKI <zbigniew.chamski@thalesgroup.com>
#define DECODE_MACRO_USAGE_LOGGED 1
#include "decode_macros.h"
#include "cvxif.h"
#include "trap.h"
#include <cstdlib>

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
#include <cassert>
#include "sim.h"
#include "simif.h"
#include "devices.h"
#include "debug_module.h"
#include "debug_defines.h"
#include "opcodes.h"
@ -31,26 +32,29 @@ static unsigned field_width(unsigned n)
///////////////////////// debug_module_t
debug_module_t::debug_module_t(sim_t *sim, const debug_module_config_t &config) :
nprocs(sim->nprocs()),
debug_module_t::debug_module_t(simif_t *sim, const debug_module_config_t &config) :
config(config),
program_buffer_bytes((config.support_impebreak ? 4 : 0) + 4*config.progbufsize),
debug_progbuf_start(debug_data_start - program_buffer_bytes),
debug_abstract_start(debug_progbuf_start - debug_abstract_size*4),
custom_base(0),
hartsellen(field_width(sim->nprocs())),
sim(sim),
// The spec lets a debugger select nonexistent harts. Create hart_state for
// them because I'm too lazy to add the code to just ignore accesses.
hart_state(1 << field_width(sim->nprocs())),
hart_array_mask(sim->nprocs()),
hart_state(1 << field_width(sim->get_cfg().max_hartid() + 1)),
hart_array_mask(sim->get_cfg().max_hartid() + 1),
rti_remaining(0)
{
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));
assert(nprocs <= 1024);
const unsigned max_procs = 1024;
if (sim->get_cfg().max_hartid() >= max_procs) {
fprintf(stderr, "Hart IDs must not exceed %u (%zu harts with max hart ID %zu requested)\n",
max_procs - 1, sim->get_cfg().nprocs(), sim->get_cfg().max_hartid());
exit(1);
}
program_buffer = new uint8_t[program_buffer_bytes];
@ -80,11 +84,8 @@ debug_module_t::~debug_module_t()
void debug_module_t::reset()
{
assert(sim->nprocs() > 0);
for (unsigned i = 0; i < sim->nprocs(); i++) {
processor_t *proc = sim->get_core(i);
if (proc)
proc->halt_request = proc->HR_NONE;
for (const auto& [hart_id, hart] : sim->get_harts()) {
hart->halt_request = hart->HR_NONE;
}
memset(&dmcontrol, 0, sizeof(dmcontrol));
@ -165,18 +166,18 @@ bool debug_module_t::load(reg_t addr, size_t len, uint8_t* bytes)
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;
}
);
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;
@ -203,23 +204,20 @@ bool debug_module_t::store(reg_t addr, size_t len, const uint8_t* bytes)
if (!hart_state[id].halted) {
hart_state[id].halted = true;
if (hart_state[id].haltgroup) {
for (unsigned i = 0; i < nprocs; i++) {
if (!hart_state[i].halted &&
hart_state[i].haltgroup == hart_state[id].haltgroup) {
processor_t *proc = sim->get_core(i);
proc->halt_request = proc->HR_GROUP;
for (const auto& [hart_id, hart] : sim->get_harts()) {
if (!hart_state[hart_id].halted &&
hart_state[hart_id].haltgroup == hart_state[id].haltgroup) {
hart->halt_request = hart->HR_GROUP;
// TODO: What if the debugger comes and writes dmcontrol before the
// halt occurs?
}
}
}
}
if (dmcontrol.hartsel == id) {
if (0 == (debug_rom_flags[id] & (1 << DEBUG_ROM_FLAG_GO))){
if (dmcontrol.hartsel == id) {
abstract_command_completed = true;
}
}
if (selected_hart_id() == id) {
if (0 == (debug_rom_flags[id] & (1 << DEBUG_ROM_FLAG_GO))) {
abstract_command_completed = true;
}
}
return true;
}
@ -269,23 +267,9 @@ uint32_t debug_module_t::read32(uint8_t *memory, unsigned int index)
return value;
}
processor_t *debug_module_t::processor(unsigned hartid) const
{
processor_t *proc = NULL;
try {
proc = sim->get_core(hartid);
} catch (const std::out_of_range&) {
}
return proc;
}
bool debug_module_t::hart_selected(unsigned hartid) const
{
if (dmcontrol.hasel) {
return hartid == dmcontrol.hartsel || hart_array_mask[hartid];
} else {
return hartid == dmcontrol.hartsel;
}
return hartid == selected_hart_id() || (dmcontrol.hasel && hart_array_mask[hartid]);
}
unsigned debug_module_t::sb_access_bits()
@ -318,13 +302,13 @@ void debug_module_t::sb_read()
reg_t address = ((uint64_t) sbaddress[1] << 32) | sbaddress[0];
try {
if (sbcs.sbaccess == 0 && config.max_sba_data_width >= 8) {
sbdata[0] = sim->debug_mmu->load_uint8(address);
sbdata[0] = sim->debug_mmu->load<uint8_t>(address);
} else if (sbcs.sbaccess == 1 && config.max_sba_data_width >= 16) {
sbdata[0] = sim->debug_mmu->load_uint16(address);
sbdata[0] = sim->debug_mmu->load<uint16_t>(address);
} else if (sbcs.sbaccess == 2 && config.max_sba_data_width >= 32) {
sbdata[0] = sim->debug_mmu->load_uint32(address);
sbdata[0] = sim->debug_mmu->load<uint32_t>(address);
} else if (sbcs.sbaccess == 3 && config.max_sba_data_width >= 64) {
uint64_t value = sim->debug_mmu->load_uint64(address);
uint64_t value = sim->debug_mmu->load<uint64_t>(address);
sbdata[0] = value;
sbdata[1] = value >> 32;
} else {
@ -340,13 +324,13 @@ 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 && config.max_sba_data_width >= 8) {
sim->debug_mmu->store_uint8(address, sbdata[0]);
sim->debug_mmu->store<uint8_t>(address, sbdata[0]);
} else if (sbcs.sbaccess == 1 && config.max_sba_data_width >= 16) {
sim->debug_mmu->store_uint16(address, sbdata[0]);
sim->debug_mmu->store<uint16_t>(address, sbdata[0]);
} else if (sbcs.sbaccess == 2 && config.max_sba_data_width >= 32) {
sim->debug_mmu->store_uint32(address, sbdata[0]);
sim->debug_mmu->store<uint32_t>(address, sbdata[0]);
} else if (sbcs.sbaccess == 3 && config.max_sba_data_width >= 64) {
sim->debug_mmu->store_uint64(address,
sim->debug_mmu->store<uint64_t>(address,
(((uint64_t) sbdata[1]) << 32) | sbdata[0]);
} else {
sbcs.error = 3;
@ -357,8 +341,8 @@ 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;
if (address >= DM_DATA0 && address < DM_DATA0 + abstractcs.datacount) {
unsigned i = address - DM_DATA0;
result = read32(dmdata, i);
if (abstractcs.busy) {
result = -1;
@ -372,8 +356,8 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value)
if (!abstractcs.busy && ((abstractauto.autoexecdata >> i) & 1)) {
perform_abstract_command();
}
} else if (address >= DMI_PROGBUF0 && address < DMI_PROGBUF0 + config.progbufsize) {
unsigned i = address - DMI_PROGBUF0;
} else if (address >= DM_PROGBUF0 && address < DM_PROGBUF0 + config.progbufsize) {
unsigned i = address - DM_PROGBUF0;
result = read32(program_buffer, i);
if (abstractcs.busy) {
result = -1;
@ -385,37 +369,37 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value)
} else {
switch (address) {
case DMI_DMCONTROL:
case DM_DMCONTROL:
{
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_HASEL, dmcontrol.hasel);
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);
result = set_field(result, DM_DMCONTROL_HALTREQ, dmcontrol.haltreq);
result = set_field(result, DM_DMCONTROL_RESUMEREQ, dmcontrol.resumereq);
result = set_field(result, DM_DMCONTROL_HARTSELHI,
dmcontrol.hartsel >> DM_DMCONTROL_HARTSELLO_LENGTH);
result = set_field(result, DM_DMCONTROL_HASEL, dmcontrol.hasel);
result = set_field(result, DM_DMCONTROL_HARTSELLO, dmcontrol.hartsel);
result = set_field(result, DM_DMCONTROL_HARTRESET, dmcontrol.hartreset);
result = set_field(result, DM_DMCONTROL_NDMRESET, dmcontrol.ndmreset);
result = set_field(result, DM_DMCONTROL_DMACTIVE, dmcontrol.dmactive);
}
break;
case DMI_DMSTATUS:
case DM_DMSTATUS:
{
dmstatus.allhalted = true;
dmstatus.allhalted = true;
dmstatus.anyhalted = false;
dmstatus.allrunning = true;
dmstatus.allrunning = true;
dmstatus.anyrunning = false;
dmstatus.allnonexistant = true;
dmstatus.allresumeack = true;
dmstatus.anyresumeack = false;
for (unsigned i = 0; i < nprocs; i++) {
if (hart_selected(i)) {
for (const auto& [hart_id, hart] : sim->get_harts()) {
if (hart_selected(hart_id)) {
dmstatus.allnonexistant = false;
if (hart_state[i].resumeack) {
if (hart_state[hart_id].resumeack) {
dmstatus.anyresumeack = true;
} else {
dmstatus.allresumeack = false;
}
if (hart_state[i].halted) {
if (hart_state[hart_id].halted) {
dmstatus.allrunning = false;
dmstatus.anyhalted = true;
} else {
@ -428,93 +412,91 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value)
// We don't allow selecting non-existant harts through
// hart_array_mask, so the only way it's possible is by writing a
// non-existant hartsel.
dmstatus.anynonexistant = (dmcontrol.hartsel >= nprocs);
dmstatus.anynonexistant = dmcontrol.hartsel >= sim->get_cfg().nprocs();
dmstatus.allunavail = false;
dmstatus.anyunavail = false;
dmstatus.allunavail = false;
dmstatus.anyunavail = false;
result = set_field(result, DMI_DMSTATUS_IMPEBREAK,
result = set_field(result, DM_DMSTATUS_IMPEBREAK,
dmstatus.impebreak);
result = set_field(result, DMI_DMSTATUS_ALLHAVERESET,
hart_state[dmcontrol.hartsel].havereset);
result = set_field(result, DMI_DMSTATUS_ANYHAVERESET,
hart_state[dmcontrol.hartsel].havereset);
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);
result = set_field(result, DM_DMSTATUS_ALLHAVERESET, selected_hart_state().havereset);
result = set_field(result, DM_DMSTATUS_ANYHAVERESET, selected_hart_state().havereset);
result = set_field(result, DM_DMSTATUS_ALLNONEXISTENT, dmstatus.allnonexistant);
result = set_field(result, DM_DMSTATUS_ALLUNAVAIL, dmstatus.allunavail);
result = set_field(result, DM_DMSTATUS_ALLRUNNING, dmstatus.allrunning);
result = set_field(result, DM_DMSTATUS_ALLHALTED, dmstatus.allhalted);
result = set_field(result, DM_DMSTATUS_ALLRESUMEACK, dmstatus.allresumeack);
result = set_field(result, DM_DMSTATUS_ANYNONEXISTENT, dmstatus.anynonexistant);
result = set_field(result, DM_DMSTATUS_ANYUNAVAIL, dmstatus.anyunavail);
result = set_field(result, DM_DMSTATUS_ANYRUNNING, dmstatus.anyrunning);
result = set_field(result, DM_DMSTATUS_ANYHALTED, dmstatus.anyhalted);
result = set_field(result, DM_DMSTATUS_ANYRESUMEACK, dmstatus.anyresumeack);
result = set_field(result, DM_DMSTATUS_AUTHENTICATED, dmstatus.authenticated);
result = set_field(result, DM_DMSTATUS_AUTHBUSY, dmstatus.authbusy);
result = set_field(result, DM_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,
case DM_ABSTRACTCS:
result = set_field(result, DM_ABSTRACTCS_CMDERR, abstractcs.cmderr);
result = set_field(result, DM_ABSTRACTCS_BUSY, abstractcs.busy);
result = set_field(result, DM_ABSTRACTCS_DATACOUNT, abstractcs.datacount);
result = set_field(result, DM_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);
case DM_ABSTRACTAUTO:
result = set_field(result, DM_ABSTRACTAUTO_AUTOEXECPROGBUF, abstractauto.autoexecprogbuf);
result = set_field(result, DM_ABSTRACTAUTO_AUTOEXECDATA, abstractauto.autoexecdata);
break;
case DMI_COMMAND:
case DM_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);
case DM_HARTINFO:
result = set_field(result, DM_HARTINFO_NSCRATCH, 1);
result = set_field(result, DM_HARTINFO_DATAACCESS, 1);
result = set_field(result, DM_HARTINFO_DATASIZE, abstractcs.datacount);
result = set_field(result, DM_HARTINFO_DATAADDR, debug_data_start);
break;
case DMI_HAWINDOWSEL:
case DM_HAWINDOWSEL:
result = hawindowsel;
break;
case DMI_HAWINDOW:
case DM_HAWINDOW:
{
unsigned base = hawindowsel * 32;
for (unsigned i = 0; i < 32; i++) {
unsigned n = base + i;
if (n < nprocs && hart_array_mask[n]) {
if (n < sim->get_cfg().nprocs() && hart_array_mask[sim->get_cfg().hartids()[n]]) {
result |= 1 << i;
}
}
}
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);
case DM_SBCS:
result = set_field(result, DM_SBCS_SBVERSION, sbcs.version);
result = set_field(result, DM_SBCS_SBREADONADDR, sbcs.readonaddr);
result = set_field(result, DM_SBCS_SBACCESS, sbcs.sbaccess);
result = set_field(result, DM_SBCS_SBAUTOINCREMENT, sbcs.autoincrement);
result = set_field(result, DM_SBCS_SBREADONDATA, sbcs.readondata);
result = set_field(result, DM_SBCS_SBERROR, sbcs.error);
result = set_field(result, DM_SBCS_SBASIZE, sbcs.asize);
result = set_field(result, DM_SBCS_SBACCESS128, sbcs.access128);
result = set_field(result, DM_SBCS_SBACCESS64, sbcs.access64);
result = set_field(result, DM_SBCS_SBACCESS32, sbcs.access32);
result = set_field(result, DM_SBCS_SBACCESS16, sbcs.access16);
result = set_field(result, DM_SBCS_SBACCESS8, sbcs.access8);
break;
case DMI_SBADDRESS0:
case DM_SBADDRESS0:
result = sbaddress[0];
break;
case DMI_SBADDRESS1:
case DM_SBADDRESS1:
result = sbaddress[1];
break;
case DMI_SBADDRESS2:
case DM_SBADDRESS2:
result = sbaddress[2];
break;
case DMI_SBADDRESS3:
case DM_SBADDRESS3:
result = sbaddress[3];
break;
case DMI_SBDATA0:
case DM_SBDATA0:
result = sbdata[0];
if (sbcs.error == 0) {
if (sbcs.readondata) {
@ -525,21 +507,20 @@ bool debug_module_t::dmi_read(unsigned address, uint32_t *value)
}
}
break;
case DMI_SBDATA1:
case DM_SBDATA1:
result = sbdata[1];
break;
case DMI_SBDATA2:
case DM_SBDATA2:
result = sbdata[2];
break;
case DMI_SBDATA3:
case DM_SBDATA3:
result = sbdata[3];
break;
case DMI_AUTHDATA:
case DM_AUTHDATA:
result = challenge;
break;
case DMI_DMCS2:
result = set_field(result, DMI_DMCS2_HALTGROUP,
hart_state[dmcontrol.hartsel].haltgroup);
case DM_DMCS2:
result = set_field(result, DM_DMCS2_GROUP, selected_hart_state().haltgroup);
break;
default:
result = 0;
@ -583,7 +564,7 @@ bool debug_module_t::perform_abstract_command()
bool write = get_field(command, AC_ACCESS_REGISTER_WRITE);
unsigned regno = get_field(command, AC_ACCESS_REGISTER_REGNO);
if (!hart_state[dmcontrol.hartsel].halted) {
if (!selected_hart_state().halted) {
abstractcs.cmderr = CMDERR_HALTRESUME;
return true;
}
@ -672,7 +653,7 @@ bool debug_module_t::perform_abstract_command()
write32(debug_abstract, i++, csrw(S0, CSR_DSCRATCH0));
}
} else if (regno >= 0x1020 && regno < 0x1040) {
} else if (regno >= 0x1020 && regno < 0x1040 && config.support_abstract_fpr_access) {
unsigned fprnum = regno - 0x1020;
if (write) {
@ -738,7 +719,7 @@ bool debug_module_t::perform_abstract_command()
write32(debug_abstract, i++, ebreak());
}
debug_rom_flags[dmcontrol.hartsel] |= 1 << DEBUG_ROM_FLAG_GO;
debug_rom_flags[selected_hart_id()] |= 1 << DEBUG_ROM_FLAG_GO;
rti_remaining = config.abstract_rti;
abstract_command_completed = false;
@ -753,14 +734,14 @@ 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)
if (!dmstatus.authenticated && address != DM_AUTHDATA &&
address != DM_DMCONTROL)
return false;
if (address >= DMI_DATA0 && address < DMI_DATA0 + abstractcs.datacount) {
unsigned i = address - DMI_DATA0;
if (address >= DM_DATA0 && address < DM_DATA0 + abstractcs.datacount) {
unsigned i = address - DM_DATA0;
if (!abstractcs.busy)
write32(dmdata, address - DMI_DATA0, value);
write32(dmdata, address - DM_DATA0, value);
if (abstractcs.busy && abstractcs.cmderr == CMDERR_NONE) {
abstractcs.cmderr = CMDERR_BUSY;
@ -771,8 +752,8 @@ bool debug_module_t::dmi_write(unsigned address, uint32_t value)
}
return true;
} else if (address >= DMI_PROGBUF0 && address < DMI_PROGBUF0 + config.progbufsize) {
unsigned i = address - DMI_PROGBUF0;
} else if (address >= DM_PROGBUF0 && address < DM_PROGBUF0 + config.progbufsize) {
unsigned i = address - DM_PROGBUF0;
if (!abstractcs.busy)
write32(program_buffer, i, value);
@ -784,112 +765,108 @@ bool debug_module_t::dmi_write(unsigned address, uint32_t value)
} else {
switch (address) {
case DMI_DMCONTROL:
case DM_DMCONTROL:
{
if (!dmcontrol.dmactive && get_field(value, DMI_DMCONTROL_DMACTIVE))
if (!dmcontrol.dmactive && get_field(value, DM_DMCONTROL_DMACTIVE))
reset();
dmcontrol.dmactive = get_field(value, DMI_DMCONTROL_DMACTIVE);
dmcontrol.dmactive = get_field(value, DM_DMCONTROL_DMACTIVE);
if (!dmstatus.authenticated || !dmcontrol.dmactive)
return true;
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.haltreq = get_field(value, DM_DMCONTROL_HALTREQ);
dmcontrol.resumereq = get_field(value, DM_DMCONTROL_RESUMEREQ);
dmcontrol.hartreset = get_field(value, DM_DMCONTROL_HARTRESET);
dmcontrol.ndmreset = get_field(value, DM_DMCONTROL_NDMRESET);
if (config.support_hasel)
dmcontrol.hasel = get_field(value, DMI_DMCONTROL_HASEL);
dmcontrol.hasel = get_field(value, DM_DMCONTROL_HASEL);
else
dmcontrol.hasel = 0;
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;
for (unsigned i = 0; i < nprocs; i++) {
if (hart_selected(i)) {
if (get_field(value, DMI_DMCONTROL_ACKHAVERESET)) {
hart_state[i].havereset = false;
dmcontrol.hartsel = get_field(value, DM_DMCONTROL_HARTSELHI) <<
DM_DMCONTROL_HARTSELLO_LENGTH;
dmcontrol.hartsel |= get_field(value, DM_DMCONTROL_HARTSELLO);
dmcontrol.hartsel = std::min(size_t(dmcontrol.hartsel), sim->get_cfg().nprocs() - 1);
for (const auto& [hart_id, hart] : sim->get_harts()) {
if (hart_selected(hart_id)) {
if (get_field(value, DM_DMCONTROL_ACKHAVERESET)) {
hart_state[hart_id].havereset = false;
}
processor_t *proc = processor(i);
if (proc) {
proc->halt_request = dmcontrol.haltreq ? proc->HR_REGULAR : proc->HR_NONE;
if (dmcontrol.haltreq) {
D(fprintf(stderr, "halt hart %d\n", i));
}
if (dmcontrol.resumereq) {
D(fprintf(stderr, "resume hart %d\n", i));
debug_rom_flags[i] |= (1 << DEBUG_ROM_FLAG_RESUME);
hart_state[i].resumeack = false;
}
if (dmcontrol.hartreset) {
proc->reset();
}
hart->halt_request = dmcontrol.haltreq ? hart->HR_REGULAR : hart->HR_NONE;
if (dmcontrol.haltreq) {
D(fprintf(stderr, "halt hart %d\n", hart_id));
}
if (dmcontrol.resumereq) {
D(fprintf(stderr, "resume hart %d\n", hart_id));
debug_rom_flags[hart_id] |= (1 << DEBUG_ROM_FLAG_RESUME);
hart_state[hart_id].resumeack = false;
}
if (dmcontrol.hartreset) {
hart->reset();
}
}
}
if (dmcontrol.ndmreset) {
for (size_t i = 0; i < sim->nprocs(); i++) {
processor_t *proc = sim->get_core(i);
proc->reset();
for (const auto& [hart_id, hart] : sim->get_harts()) {
hart->reset();
}
}
}
return true;
case DMI_COMMAND:
case DM_COMMAND:
command = value;
return perform_abstract_command();
case DMI_HAWINDOWSEL:
hawindowsel = value & ((1U<<field_width(nprocs))-1);
case DM_HAWINDOWSEL:
hawindowsel = value & ((1U<<field_width(hart_array_mask.size()))-1);
return true;
case DMI_HAWINDOW:
case DM_HAWINDOW:
{
unsigned base = hawindowsel * 32;
for (unsigned i = 0; i < 32; i++) {
unsigned n = base + i;
if (n < nprocs) {
hart_array_mask[n] = (value >> i) & 1;
if (n < sim->get_cfg().nprocs()) {
hart_array_mask[sim->get_cfg().hartids()[n]] = (value >> i) & 1;
}
}
}
return true;
case DMI_ABSTRACTCS:
abstractcs.cmderr = (cmderr_t) (((uint32_t) (abstractcs.cmderr)) & (~(uint32_t)(get_field(value, DMI_ABSTRACTCS_CMDERR))));
case DM_ABSTRACTCS:
abstractcs.cmderr = (cmderr_t) (((uint32_t) (abstractcs.cmderr)) & (~(uint32_t)(get_field(value, DM_ABSTRACTCS_CMDERR))));
return true;
case DMI_ABSTRACTAUTO:
case DM_ABSTRACTAUTO:
abstractauto.autoexecprogbuf = get_field(value,
DMI_ABSTRACTAUTO_AUTOEXECPROGBUF);
DM_ABSTRACTAUTO_AUTOEXECPROGBUF);
abstractauto.autoexecdata = get_field(value,
DMI_ABSTRACTAUTO_AUTOEXECDATA);
DM_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);
case DM_SBCS:
sbcs.readonaddr = get_field(value, DM_SBCS_SBREADONADDR);
sbcs.sbaccess = get_field(value, DM_SBCS_SBACCESS);
sbcs.autoincrement = get_field(value, DM_SBCS_SBAUTOINCREMENT);
sbcs.readondata = get_field(value, DM_SBCS_SBREADONDATA);
sbcs.error &= ~get_field(value, DM_SBCS_SBERROR);
return true;
case DMI_SBADDRESS0:
case DM_SBADDRESS0:
sbaddress[0] = value;
if (sbcs.error == 0 && sbcs.readonaddr) {
sb_read();
sb_autoincrement();
}
return true;
case DMI_SBADDRESS1:
case DM_SBADDRESS1:
sbaddress[1] = value;
return true;
case DMI_SBADDRESS2:
case DM_SBADDRESS2:
sbaddress[2] = value;
return true;
case DMI_SBADDRESS3:
case DM_SBADDRESS3:
sbaddress[3] = value;
return true;
case DMI_SBDATA0:
case DM_SBDATA0:
sbdata[0] = value;
if (sbcs.error == 0) {
sb_write();
@ -898,16 +875,16 @@ bool debug_module_t::dmi_write(unsigned address, uint32_t value)
}
}
return true;
case DMI_SBDATA1:
case DM_SBDATA1:
sbdata[1] = value;
return true;
case DMI_SBDATA2:
case DM_SBDATA2:
sbdata[2] = value;
return true;
case DMI_SBDATA3:
case DM_SBDATA3:
sbdata[3] = value;
return true;
case DMI_AUTHDATA:
case DM_AUTHDATA:
D(fprintf(stderr, "debug authentication: got 0x%x; 0x%x unlocks\n", value,
challenge + secret));
if (config.require_authentication) {
@ -919,10 +896,11 @@ bool debug_module_t::dmi_write(unsigned address, uint32_t value)
}
}
return true;
case DMI_DMCS2:
if (config.support_haltgroups && get_field(value, DMI_DMCS2_HGWRITE)) {
hart_state[dmcontrol.hartsel].haltgroup = get_field(value,
DMI_DMCS2_HALTGROUP);
case DM_DMCS2:
if (config.support_haltgroups &&
get_field(value, DM_DMCS2_HGWRITE) &&
get_field(value, DM_DMCS2_GROUPTYPE) == 0) {
selected_hart_state().haltgroup = get_field(value, DM_DMCS2_GROUP);
}
return true;
}
@ -936,3 +914,13 @@ void debug_module_t::proc_reset(unsigned id)
hart_state[id].halted = false;
hart_state[id].haltgroup = 0;
}
hart_debug_state_t& debug_module_t::selected_hart_state()
{
return hart_state[selected_hart_id()];
}
size_t debug_module_t::selected_hart_id() const
{
return sim->get_cfg().hartids().at(dmcontrol.hartsel);
}

View file

@ -3,24 +3,26 @@
#define _RISCV_DEBUG_MODULE_H
#include <set>
#include <vector>
#include "abstract_device.h"
#include "mmu.h"
class sim_t;
class simif_t;
class bus_t;
class processor_t;
typedef struct {
// Size of program_buffer in 32-bit words, as exposed to the rest of the
// world.
unsigned progbufsize;
unsigned max_sba_data_width;
bool require_authentication;
unsigned abstract_rti;
bool support_hasel;
bool support_abstract_csr_access;
bool support_haltgroups;
bool support_impebreak;
// Size of program_buffer in 32-bit words, as exposed to the rest of the
// world.
unsigned progbufsize;
unsigned max_sba_data_width;
bool require_authentication;
unsigned abstract_rti;
bool support_hasel;
bool support_abstract_csr_access;
bool support_abstract_fpr_access;
bool support_haltgroups;
bool support_impebreak;
} debug_module_config_t;
typedef struct {
@ -54,12 +56,12 @@ typedef struct {
} dmstatus_t;
typedef enum cmderr {
CMDERR_NONE = 0,
CMDERR_BUSY = 1,
CMDERR_NOTSUP = 2,
CMDERR_EXCEPTION = 3,
CMDERR_HALTRESUME = 4,
CMDERR_OTHER = 7
CMDERR_NONE = 0,
CMDERR_BUSY = 1,
CMDERR_NOTSUP = 2,
CMDERR_EXCEPTION = 3,
CMDERR_HALTRESUME = 4,
CMDERR_OTHER = 7
} cmderr_t;
typedef struct {
@ -108,7 +110,7 @@ class debug_module_t : public abstract_device_t
* abstract_rti is extra run-test/idle cycles that each abstract command
* takes to execute. Useful for testing OpenOCD.
*/
debug_module_t(sim_t *sim, const debug_module_config_t &config);
debug_module_t(simif_t *sim, const debug_module_config_t &config);
~debug_module_t();
void add_device(bus_t *bus);
@ -130,7 +132,6 @@ class debug_module_t : public abstract_device_t
private:
static const unsigned datasize = 2;
unsigned nprocs;
debug_module_config_t config;
// Actual size of the program buffer, which is 1 word bigger than we let on
// to implement the implicit ebreak at the end.
@ -144,11 +145,7 @@ class debug_module_t : public abstract_device_t
// functionality.
unsigned custom_base;
// We only support 1024 harts currently. More requires at least resizing
// the arrays below, and their corresponding special memory regions.
unsigned hartsellen = 10;
sim_t *sim;
simif_t *sim;
uint8_t debug_rom_whereto[4];
uint8_t debug_abstract[debug_abstract_size * 4];
@ -181,13 +178,15 @@ class debug_module_t : public abstract_device_t
uint32_t challenge;
const uint32_t secret = 1;
processor_t *processor(unsigned hartid) const;
bool hart_selected(unsigned hartid) const;
void reset();
bool perform_abstract_command();
bool abstract_command_completed;
unsigned rti_remaining;
size_t selected_hart_id() const;
hart_debug_state_t& selected_hart_state();
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,312 @@
// See LICENSE for license details.
#ifndef _RISCV_DECODE_MACROS_H
#define _RISCV_DECODE_MACROS_H
#include "config.h"
#include "decode.h"
#include "encoding.h"
#include "common.h"
#include "softfloat_types.h"
#include "specialize.h"
// helpful macros, etc
#define MMU (*p->get_mmu())
#define STATE (*p->get_state())
#define FLEN (p->get_flen())
#define CHECK_REG(reg) ((void) 0)
#define READ_REG(reg) (CHECK_REG(reg), STATE.XPR[reg])
#define READ_FREG(reg) STATE.FPR[reg]
#define RD READ_REG(insn.rd())
#define RS1 READ_REG(insn.rs1())
#define RS2 READ_REG(insn.rs2())
#define RS3 READ_REG(insn.rs3())
#define WRITE_RD(value) WRITE_REG(insn.rd(), value)
/* 0 : int
* 1 : floating
* 2 : vector reg
* 3 : vector hint
* 4 : csr
*/
#define WRITE_REG(reg, value) ({ \
reg_t wdata = (value); /* value may have side effects */ \
if (DECODE_MACRO_USAGE_LOGGED) STATE.log_reg_write[(reg) << 4] = {wdata, 0}; \
CHECK_REG(reg); \
STATE.XPR.write(reg, wdata); \
})
#define WRITE_FREG(reg, value) ({ \
freg_t wdata = freg(value); /* value may have side effects */ \
if (DECODE_MACRO_USAGE_LOGGED) STATE.log_reg_write[((reg) << 4) | 1] = wdata; \
DO_WRITE_FREG(reg, wdata); \
})
#define WRITE_VSTATUS STATE.log_reg_write[3] = {0, 0};
// 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)
// Zc* macros
#define RVC_R1S (Sn(insn.rvc_r1sc()))
#define RVC_R2S (Sn(insn.rvc_r2sc()))
#define SP READ_REG(X_SP)
#define RA READ_REG(X_RA)
// FPU macros
#define READ_ZDINX_REG(reg) (xlen == 32 ? f64(READ_REG_PAIR(reg)) : f64(STATE.XPR[reg] & (uint64_t)-1))
#define READ_FREG_H(reg) (p->extension_enabled(EXT_ZFINX) ? f16(STATE.XPR[reg] & (uint16_t)-1) : f16(READ_FREG(reg)))
#define READ_FREG_F(reg) (p->extension_enabled(EXT_ZFINX) ? f32(STATE.XPR[reg] & (uint32_t)-1) : f32(READ_FREG(reg)))
#define READ_FREG_D(reg) (p->extension_enabled(EXT_ZFINX) ? READ_ZDINX_REG(reg) : f64(READ_FREG(reg)))
#define FRS1 READ_FREG(insn.rs1())
#define FRS2 READ_FREG(insn.rs2())
#define FRS3 READ_FREG(insn.rs3())
#define FRS1_H READ_FREG_H(insn.rs1())
#define FRS1_F READ_FREG_F(insn.rs1())
#define FRS1_D READ_FREG_D(insn.rs1())
#define FRS2_H READ_FREG_H(insn.rs2())
#define FRS2_F READ_FREG_F(insn.rs2())
#define FRS2_D READ_FREG_D(insn.rs2())
#define FRS3_H READ_FREG_H(insn.rs3())
#define FRS3_F READ_FREG_F(insn.rs3())
#define FRS3_D READ_FREG_D(insn.rs3())
#define dirty_fp_state STATE.sstatus->dirty(SSTATUS_FS)
#define dirty_ext_state STATE.sstatus->dirty(SSTATUS_XS)
#define dirty_vs_state STATE.sstatus->dirty(SSTATUS_VS)
#define DO_WRITE_FREG(reg, value) (STATE.FPR.write(reg, value), dirty_fp_state)
#define WRITE_FRD(value) WRITE_FREG(insn.rd(), value)
#define WRITE_FRD_H(value) \
do { \
if (p->extension_enabled(EXT_ZFINX)) \
WRITE_REG(insn.rd(), sext_xlen((int16_t)((value).v))); \
else { \
WRITE_FRD(value); \
} \
} while (0)
#define WRITE_FRD_F(value) \
do { \
if (p->extension_enabled(EXT_ZFINX)) \
WRITE_REG(insn.rd(), sext_xlen((value).v)); \
else { \
WRITE_FRD(value); \
} \
} while (0)
#define WRITE_FRD_D(value) \
do { \
if (p->extension_enabled(EXT_ZFINX)) { \
if (xlen == 32) { \
uint64_t val = (value).v; \
WRITE_RD_PAIR(val); \
} else { \
WRITE_REG(insn.rd(), (value).v); \
} \
} else { \
WRITE_FRD(value); \
} \
} while (0)
#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->read(); \
if (rm > 4) throw trap_illegal_instruction(insn.bits()); \
rm; })
#define require_privilege(p) require(STATE.prv >= (p))
#define require_novirt() (unlikely(STATE.v) ? throw trap_virtual_instruction(insn.bits()) : (void) 0)
#define require_hs_qualified(cond) (STATE.v && !(cond) ? require_novirt() : require(cond))
#define require_privilege_hs_qualified(p) require_hs_qualified(STATE.prv >= (p))
#define require_rv64 require(xlen == 64)
#define require_rv32 require(xlen == 32)
#define require_extension(s) require(p->extension_enabled(s))
#define require_either_extension(A,B) require(p->extension_enabled(A) || p->extension_enabled(B));
#define require_impl(s) require(p->supports_impl(s))
#define require_fs require(STATE.sstatus->enabled(SSTATUS_FS))
#define require_fp STATE.fflags->verify_permissions(insn, false)
#define require_accelerator require(STATE.sstatus->enabled(SSTATUS_XS))
#define require_vector_vs require(STATE.sstatus->enabled(SSTATUS_VS))
#define require_vector(alu) \
do { \
require_vector_vs; \
require_extension('V'); \
require(!P.VU.vill); \
if (alu && !P.VU.vstart_alu) \
require(P.VU.vstart->read() == 0); \
WRITE_VSTATUS; \
dirty_vs_state; \
} while (0);
#define require_vector_novtype(is_log) \
do { \
require_vector_vs; \
require_extension('V'); \
if (is_log) \
WRITE_VSTATUS; \
dirty_vs_state; \
} while (0);
#define require_align(val, pos) require(is_aligned(val, pos))
#define require_noover(astart, asize, bstart, bsize) \
require(!is_overlapped(astart, asize, bstart, bsize))
#define require_noover_widen(astart, asize, bstart, bsize) \
require(!is_overlapped_widen(astart, asize, bstart, bsize))
#define require_vm do { if (insn.v_vm() == 0) require(insn.rd() != 0); } while (0);
#define require_envcfg(field) \
do { \
if (((STATE.prv != PRV_M) && (m##field == 0)) || \
((STATE.prv == PRV_U && !STATE.v) && (s##field == 0))) \
throw trap_illegal_instruction(insn.bits()); \
else if (STATE.v && ((h##field == 0) || \
((STATE.prv == PRV_U) && (s##field == 0)))) \
throw trap_virtual_instruction(insn.bits()); \
} while (0);
#define require_zcmp_pushpop \
do { \
require_extension(EXT_ZCMP); \
reg_t rlist = insn.rvc_rlist(); \
require(rlist >= 4); \
\
if (p->extension_enabled('E')) { \
require(rlist <= 6); \
} \
} while (0);
#define set_fp_exceptions ({ if (softfloat_exceptionFlags) { \
STATE.fflags->write(STATE.fflags->read() | 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(x, pos) (((reg_t)(x) << (64 - (pos))) >> (64 - (pos)))
#define zext_xlen(x) zext(x, 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)
class wait_for_interrupt_t {};
#define wfi() \
do { set_pc_and_serialize(npc); \
throw wait_for_interrupt_t(); \
} 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 invalid_pc(pc) ((pc) & 1)
/* Convenience wrappers to simplify softfloat code sequences */
#define isBoxedF16(r) (isBoxedF32(r) && ((uint64_t)((r.v[0] >> 16) + 1) == ((uint64_t)1 << 48)))
#define unboxF16(r) (isBoxedF16(r) ? (uint16_t)r.v[0] : defaultNaNF16UI)
#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)
inline float16_t f16(uint16_t v) { return { v }; }
inline float32_t f32(uint32_t v) { return { v }; }
inline float64_t f64(uint64_t v) { return { v }; }
inline float16_t f16(freg_t r) { return f16(unboxF16(r)); }
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(float16_t f) { return { ((uint64_t)-1 << 16) | f.v, (uint64_t)-1 }; }
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 F16_SIGN ((uint16_t)1 << 15)
#define F32_SIGN ((uint32_t)1 << 31)
#define F64_SIGN ((uint64_t)1 << 63)
#define fsgnj16(a, b, n, x) \
f16((f16(a).v & ~F16_SIGN) | ((((x) ? f16(a).v : (n) ? F16_SIGN : 0) ^ f16(b).v) & F16_SIGN))
#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; \
/* permissions check occurs in get_csr */ \
(which); })
/* For debug only. This will fail if the native machine's float types are not IEEE */
inline float to_f(float32_t f) { float r; memcpy(&r, &f, sizeof(r)); return r; }
inline double to_f(float64_t f) { double r; memcpy(&r, &f, sizeof(r)); return r; }
inline long double to_f(float128_t f) { long double r; memcpy(&r, &f, sizeof(r)); return r; }
// Vector macros
#define e8 8 // 8b elements
#define e16 16 // 16b elements
#define e32 32 // 32b elements
#define e64 64 // 64b elements
#define e128 128 // 128b elements
#define e256 256 // 256b elements
#define e512 512 // 512b elements
#define e1024 1024 // 1024b elements
#define vsext(x, sew) (((sreg_t)(x) << (64 - sew)) >> (64 - sew))
#define vzext(x, sew) (((reg_t)(x) << (64 - sew)) >> (64 - sew))
#define DEBUG_RVV 0
#if DEBUG_RVV
#define DEBUG_RVV_FP_VV \
printf("vfp(%lu) vd=%f vs1=%f vs2=%f\n", i, to_f(vd), to_f(vs1), to_f(vs2));
#define DEBUG_RVV_FP_VF \
printf("vfp(%lu) vd=%f vs1=%f vs2=%f\n", i, to_f(vd), to_f(rs1), to_f(vs2));
#define DEBUG_RVV_FMA_VV \
printf("vfma(%lu) vd=%f vs1=%f vs2=%f vd_old=%f\n", i, to_f(vd), to_f(vs1), to_f(vs2), to_f(vd_old));
#define DEBUG_RVV_FMA_VF \
printf("vfma(%lu) vd=%f vs1=%f vs2=%f vd_old=%f\n", i, to_f(vd), to_f(rs1), to_f(vs2), to_f(vd_old));
#else
#define DEBUG_RVV_FP_VV 0
#define DEBUG_RVV_FP_VF 0
#define DEBUG_RVV_FMA_VV 0
#define DEBUG_RVV_FMA_VF 0
#endif
#define DECLARE_XENVCFG_VARS(field) \
reg_t m##field = get_field(STATE.menvcfg->read(), MENVCFG_##field); \
reg_t s##field = get_field(STATE.senvcfg->read(), SENVCFG_##field); \
reg_t h##field = get_field(STATE.henvcfg->read(), HENVCFG_##field)
#endif

View file

@ -137,3 +137,16 @@ char* mem_t::contents(reg_t addr) {
}
return search->second + pgoff;
}
void mem_t::dump(std::ostream& o) {
const char empty[PGSIZE] = {0};
for (reg_t i = 0; i < sz; i += PGSIZE) {
reg_t ppn = i >> PGSHIFT;
auto search = sparse_memory_map.find(ppn);
if (search == sparse_memory_map.end()) {
o.write(empty, PGSIZE);
} else {
o.write(sparse_memory_map[ppn], PGSIZE);
}
}
}

View file

@ -4,12 +4,16 @@
#include "decode.h"
#include "mmio_plugin.h"
#include "abstract_device.h"
#include "abstract_interrupt_controller.h"
#include "platform.h"
#include <map>
#include <queue>
#include <vector>
#include <utility>
#include <cassert>
class processor_t;
class simif_t;
class bus_t : public abstract_device_t {
public:
@ -43,6 +47,7 @@ class mem_t : public abstract_device_t {
bool store(reg_t addr, size_t len, const uint8_t* bytes) { return load_store(addr, len, const_cast<uint8_t*>(bytes), true); }
char* contents(reg_t addr);
reg_t size() { return sz; }
void dump(std::ostream& o);
private:
bool load_store(reg_t addr, size_t len, uint8_t* bytes, bool store);
@ -53,22 +58,104 @@ class mem_t : public abstract_device_t {
class clint_t : public abstract_device_t {
public:
clint_t(std::vector<processor_t*>&, uint64_t freq_hz, bool real_time);
clint_t(simif_t*, uint64_t freq_hz, bool real_time);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
size_t size() { return CLINT_SIZE; }
void increment(reg_t inc);
uint64_t get_mtimecmp(reg_t hartid) { return mtimecmp[hartid]; }
uint64_t get_mtime() { return mtime; }
private:
typedef uint64_t mtime_t;
typedef uint64_t mtimecmp_t;
typedef uint32_t msip_t;
std::vector<processor_t*>& procs;
simif_t* sim;
uint64_t freq_hz;
bool real_time;
uint64_t real_time_ref_secs;
uint64_t real_time_ref_usecs;
mtime_t mtime;
std::vector<mtimecmp_t> mtimecmp;
std::map<size_t, mtimecmp_t> mtimecmp;
};
#define PLIC_MAX_DEVICES 1024
struct plic_context_t {
plic_context_t(processor_t* proc, bool mmode)
: proc(proc), mmode(mmode)
{}
processor_t *proc;
bool mmode;
uint8_t priority_threshold {};
uint32_t enable[PLIC_MAX_DEVICES/32] {};
uint32_t pending[PLIC_MAX_DEVICES/32] {};
uint8_t pending_priority[PLIC_MAX_DEVICES] {};
uint32_t claimed[PLIC_MAX_DEVICES/32] {};
};
class plic_t : public abstract_device_t, public abstract_interrupt_controller_t {
public:
plic_t(simif_t*, uint32_t ndev);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
void set_interrupt_level(uint32_t id, int lvl);
size_t size() { return PLIC_SIZE; }
private:
std::vector<plic_context_t> contexts;
uint32_t num_ids;
uint32_t num_ids_word;
uint32_t max_prio;
uint8_t priority[PLIC_MAX_DEVICES];
uint32_t level[PLIC_MAX_DEVICES/32];
uint32_t context_best_pending(const plic_context_t *c);
void context_update(const plic_context_t *context);
uint32_t context_claim(plic_context_t *c);
bool priority_read(reg_t offset, uint32_t *val);
bool priority_write(reg_t offset, uint32_t val);
bool pending_read(reg_t offset, uint32_t *val);
bool context_enable_read(const plic_context_t *context,
reg_t offset, uint32_t *val);
bool context_enable_write(plic_context_t *context,
reg_t offset, uint32_t val);
bool context_read(plic_context_t *context,
reg_t offset, uint32_t *val);
bool context_write(plic_context_t *context,
reg_t offset, uint32_t val);
};
class ns16550_t : public abstract_device_t {
public:
ns16550_t(class bus_t *bus, abstract_interrupt_controller_t *intctrl,
uint32_t interrupt_id, uint32_t reg_shift, uint32_t reg_io_width);
bool load(reg_t addr, size_t len, uint8_t* bytes);
bool store(reg_t addr, size_t len, const uint8_t* bytes);
void tick(void);
size_t size() { return NS16550_SIZE; }
private:
class bus_t *bus;
abstract_interrupt_controller_t *intctrl;
uint32_t interrupt_id;
uint32_t reg_shift;
uint32_t reg_io_width;
std::queue<uint8_t> rx_queue;
uint8_t dll;
uint8_t dlm;
uint8_t iir;
uint8_t ier;
uint8_t fcr;
uint8_t lcr;
uint8_t mcr;
uint8_t lsr;
uint8_t msr;
uint8_t scr;
void update_interrupt(void);
uint8_t rx_byte(void);
void tx_byte(uint8_t val);
int backoff_counter;
static const int MAX_BACKOFF = 16;
};
class mmio_plugin_device_t : public abstract_device_t {
@ -84,4 +171,26 @@ class mmio_plugin_device_t : public abstract_device_t {
void* user_data;
};
template<typename T>
void write_little_endian_reg(T* word, reg_t addr, size_t len, const uint8_t* bytes)
{
assert(len <= sizeof(T));
for (size_t i = 0; i < len; i++) {
const int shift = 8 * ((addr + i) % sizeof(T));
*word = (*word & ~(T(0xFF) << shift)) | (T(bytes[i]) << shift);
}
}
template<typename T>
void read_little_endian_reg(T word, reg_t addr, size_t len, uint8_t* bytes)
{
assert(len <= sizeof(T));
for (size_t i = 0; i < len; i++) {
const int shift = 8 * ((addr + i) % sizeof(T));
bytes[i] = word >> shift;
}
}
#endif

View file

@ -3,7 +3,9 @@
#ifndef _RISCV_DISASM_H
#define _RISCV_DISASM_H
#include "common.h"
#include "decode.h"
#include "isa_parser.h"
#include <string>
#include <sstream>
#include <algorithm>
@ -80,7 +82,7 @@ class disasm_insn_t
class disassembler_t
{
public:
disassembler_t(int xlen);
disassembler_t(const isa_parser_t *isa);
~disassembler_t();
std::string disassemble(insn_t insn) const;
@ -92,6 +94,8 @@ class disassembler_t
static const int HASH_SIZE = 255;
std::vector<const disasm_insn_t*> chain[HASH_SIZE+1];
void add_instructions(const isa_parser_t* isa);
const disasm_insn_t* probe_once(insn_t insn, size_t idx) const;
static const unsigned int MASK1 = 0x7f;

View file

@ -1,8 +1,10 @@
// See LICENSE for license details.
#include "config.h"
#include "dts.h"
#include "libfdt.h"
#include "platform.h"
#include <cassert>
#include <iostream>
#include <sstream>
#include <signal.h>
@ -13,6 +15,7 @@
std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
reg_t initrd_start, reg_t initrd_end,
const char* bootargs,
size_t pmpregions,
std::vector<processor_t*> procs,
std::vector<std::pair<reg_t, mem_t*>> mems)
{
@ -25,15 +28,16 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
" #size-cells = <2>;\n"
" compatible = \"ucbbar,spike-bare-dev\";\n"
" model = \"ucbbar,spike-bare\";\n"
" chosen {\n";
" chosen {\n"
" stdout-path = &SERIAL0;\n";
if (initrd_start < initrd_end) {
s << " linux,initrd-start = <" << (size_t)initrd_start << ">;\n"
" linux,initrd-end = <" << (size_t)initrd_end << ">;\n";
if (!bootargs)
bootargs = "root=/dev/ram console=hvc0 earlycon=sbi";
bootargs = "root=/dev/ram " DEFAULT_KERNEL_BOOTARGS;
} else {
if (!bootargs)
bootargs = "console=hvc0 earlycon=sbi";
bootargs = DEFAULT_KERNEL_BOOTARGS;
}
s << " bootargs = \"";
for (size_t i = 0; i < strlen(bootargs); i++) {
@ -54,10 +58,10 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
" 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"
" riscv,pmpregions = <16>;\n"
" riscv,pmpgranularity = <4>;\n"
" riscv,isa = \"" << procs[i]->get_isa().get_isa_string() << "\";\n"
" mmu-type = \"riscv," << (procs[i]->get_isa().get_max_xlen() <= 32 ? "sv32" : "sv57") << "\";\n"
" riscv,pmpregions = <" << pmpregions << ">;\n"
" riscv,pmpgranularity = <" << (1 << PMP_SHIFT) << ">;\n"
" clock-frequency = <" << cpu_hz << ">;\n"
" CPU" << i << "_intc: interrupt-controller {\n"
" #address-cells = <2>;\n"
@ -92,6 +96,35 @@ std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
" reg = <0x" << (clintbs >> 32) << " 0x" << (clintbs & (uint32_t)-1) <<
" 0x" << (clintsz >> 32) << " 0x" << (clintsz & (uint32_t)-1) << ">;\n"
" };\n"
" PLIC: plic@" << PLIC_BASE << " {\n"
" compatible = \"riscv,plic0\";\n"
" #address-cells = <2>;\n"
" interrupts-extended = <" << std::dec;
for (size_t i = 0; i < procs.size(); i++)
s << "&CPU" << i << "_intc 11 &CPU" << i << "_intc 9 ";
reg_t plicbs = PLIC_BASE;
reg_t plicsz = PLIC_SIZE;
s << std::hex << ">;\n"
" reg = <0x" << (plicbs >> 32) << " 0x" << (plicbs & (uint32_t)-1) <<
" 0x" << (plicsz >> 32) << " 0x" << (plicsz & (uint32_t)-1) << ">;\n"
" riscv,ndev = <0x" << PLIC_NDEV << ">;\n"
" riscv,max-priority = <0x" << ((1U << PLIC_PRIO_BITS) - 1) << ">;\n"
" #interrupt-cells = <1>;\n"
" interrupt-controller;\n"
" };\n"
" SERIAL0: ns16550@" << NS16550_BASE << " {\n"
" compatible = \"ns16550a\";\n"
" clock-frequency = <" << std::dec << (cpu_hz/insns_per_rtc_tick) << ">;\n"
" interrupt-parent = <&PLIC>;\n"
" interrupts = <" << std::dec << NS16550_INTERRUPT_ID;
reg_t ns16550bs = NS16550_BASE;
reg_t ns16550sz = NS16550_SIZE;
s << std::hex << ">;\n"
" reg = <0x" << (ns16550bs >> 32) << " 0x" << (ns16550bs & (uint32_t)-1) <<
" 0x" << (ns16550sz >> 32) << " 0x" << (ns16550sz & (uint32_t)-1) << ">;\n"
" reg-shift = <0x" << NS16550_REG_SHIFT << ">;\n"
" reg-io-width = <0x" << NS16550_REG_IO_WIDTH << ">;\n"
" };\n"
" };\n"
" htif {\n"
" compatible = \"ucb,htif0\";\n"
@ -143,7 +176,7 @@ std::string dts_compile(const std::string& dts)
close(dts_pipe[1]);
close(dtb_pipe[0]);
close(dtb_pipe[1]);
execlp(DTC, DTC, "-O", "dtb", 0);
execlp(DTC, DTC, "-O", "dtb", (char *)0);
std::cerr << "Failed to run " DTC ": " << strerror(errno) << std::endl;
exit(1);
}
@ -182,7 +215,6 @@ std::string dts_compile(const std::string& dts)
return dtb.str();
}
static int fdt_get_node_addr_size(void *fdt, int node, reg_t *addr,
unsigned long *size, const char *field)
{
@ -244,7 +276,6 @@ static int check_cpu_node(void *fdt, int cpu_offset)
return 0;
}
int fdt_get_offset(void *fdt, const char *field)
{
return fdt_path_offset(fdt, field);
@ -276,6 +307,64 @@ int fdt_parse_clint(void *fdt, reg_t *clint_addr,
return 0;
}
int fdt_parse_plic(void *fdt, reg_t *plic_addr, uint32_t *ndev,
const char *compatible)
{
int nodeoffset, len, rc;
const fdt32_t *ndev_p;
nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible);
if (nodeoffset < 0)
return nodeoffset;
rc = fdt_get_node_addr_size(fdt, nodeoffset, plic_addr, NULL, "reg");
if (rc < 0 || !plic_addr)
return -ENODEV;
ndev_p = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "riscv,ndev", &len);
if (!ndev || !ndev_p)
return -ENODEV;
*ndev = fdt32_to_cpu(*ndev_p);
return 0;
}
int fdt_parse_ns16550(void *fdt, reg_t *ns16550_addr,
uint32_t *reg_shift, uint32_t *reg_io_width,
const char *compatible)
{
int nodeoffset, len, rc;
const fdt32_t *reg_p;
nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible);
if (nodeoffset < 0)
return nodeoffset;
rc = fdt_get_node_addr_size(fdt, nodeoffset, ns16550_addr, NULL, "reg");
if (rc < 0 || !ns16550_addr)
return -ENODEV;
reg_p = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-shift", &len);
if (reg_shift) {
if (reg_p) {
*reg_shift = fdt32_to_cpu(*reg_p);
} else {
*reg_shift = NS16550_REG_SHIFT;
}
}
reg_p = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-io-width", &len);
if (reg_io_width) {
if (reg_p) {
*reg_io_width = fdt32_to_cpu(*reg_p);
} else {
*reg_io_width = NS16550_REG_IO_WIDTH;
}
}
return 0;
}
int fdt_parse_pmp_num(void *fdt, int cpu_offset, reg_t *pmp_num)
{
int rc;
@ -306,8 +395,10 @@ int fdt_parse_pmp_alignment(void *fdt, int cpu_offset, reg_t *pmp_align)
return 0;
}
int fdt_parse_mmu_type(void *fdt, int cpu_offset, char *mmu_type)
int fdt_parse_mmu_type(void *fdt, int cpu_offset, const char **mmu_type)
{
assert(mmu_type);
int len, rc;
const void *prop;
@ -318,7 +409,7 @@ int fdt_parse_mmu_type(void *fdt, int cpu_offset, char *mmu_type)
if (!prop || !len)
return -EINVAL;
strcpy(mmu_type, (char *)prop);
*mmu_type = (const char *)prop;
return 0;
}

View file

@ -10,6 +10,7 @@
std::string make_dts(size_t insns_per_rtc_tick, size_t cpu_hz,
reg_t initrd_start, reg_t initrd_end,
const char* bootargs,
size_t pmpregions,
std::vector<processor_t*> procs,
std::vector<std::pair<reg_t, mem_t*>> mems);
@ -21,7 +22,12 @@ int fdt_get_next_subnode(void *fdt, int node);
int fdt_parse_clint(void *fdt, reg_t *clint_addr,
const char *compatible);
int fdt_parse_plic(void *fdt, reg_t *plic_addr, uint32_t *ndev,
const char *compatible);
int fdt_parse_ns16550(void *fdt, reg_t *ns16550_addr,
uint32_t *reg_shift, uint32_t *reg_io_width,
const char *compatible);
int fdt_parse_pmp_num(void *fdt, int cpu_offset, reg_t *pmp_num);
int fdt_parse_pmp_alignment(void *fdt, int cpu_offset, reg_t *pmp_align);
int fdt_parse_mmu_type(void *fdt, int cpu_offset, char *mmu_type);
int fdt_parse_mmu_type(void *fdt, int cpu_offset, const char **mmu_type);
#endif

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
#include <fstream>
#include <iostream>
#include "internals.h"
#include "common.h"
//
// Used to model the cryptography extension entropy source.
@ -30,13 +30,12 @@ public:
// seed register
// ------------------------------------------------------------
void set_seed(reg_t val) {
void set_seed(reg_t UNUSED val) {
// Always ignore writes to seed.
// This CSR is strictly read only. It occupies a RW CSR address
// to handle the side-effect of the changing seed value on a read.
}
//
// The format of seed is described in Section 4.1 of
// the scalar cryptography specification.
@ -50,27 +49,27 @@ public:
// the bare minimum.
uint32_t return_status = OPST_ES16;
if(return_status == OPST_ES16) {
if (return_status == OPST_ES16) {
// Add some sampled entropy into the low 16 bits
uint16_t entropy = this -> get_two_random_bytes();
result |= entropy;
// Add some sampled entropy into the low 16 bits
uint16_t entropy = this -> get_two_random_bytes();
result |= entropy;
} else if(return_status == OPST_BIST) {
} else if (return_status == OPST_BIST) {
// Do nothing.
// Do nothing.
} else if(return_status == OPST_WAIT) {
} else if (return_status == OPST_WAIT) {
// Do nothing.
// Do nothing.
} else if(return_status == OPST_DEAD) {
} else if (return_status == OPST_DEAD) {
// Do nothing. Stay dead.
// Do nothing. Stay dead.
} else {
// Unreachable.
// Unreachable.
}
@ -93,25 +92,25 @@ public:
// Read two random bytes from the entropy source file.
uint16_t get_two_random_bytes() {
std::ifstream fh(this -> randomness_source, std::ios::binary);
std::ifstream fh(this -> randomness_source, std::ios::binary);
if(fh.is_open()) {
if (fh.is_open()) {
uint16_t random_bytes;
uint16_t random_bytes;
fh.read((char*)(&random_bytes), 2);
fh.read((char*)(&random_bytes), 2);
fh.close();
fh.close();
return random_bytes;
return random_bytes;
} else {
} else {
fprintf(stderr, "Could not open randomness source file:\n\t");
fprintf(stderr, "%s", randomness_source.c_str());
abort();
fprintf(stderr, "Could not open randomness source file:\n\t");
fprintf(stderr, "%s", randomness_source.c_str());
abort();
}
}
}

View file

@ -1,11 +1,12 @@
// See LICENSE for license details.
#include "config.h"
#include "processor.h"
#include "mmu.h"
#include "disasm.h"
#include "decode_macros.h"
#include <cassert>
#ifdef RISCV_ENABLE_COMMITLOG
static void commit_log_reset(processor_t* p)
{
p->get_state()->log_reg_write.clear();
@ -27,7 +28,7 @@ static void commit_log_print_value(FILE *log_file, int width, const void *data)
switch (width) {
case 8:
fprintf(log_file, "0x%01" PRIx8, *(const uint8_t *)data);
fprintf(log_file, "0x%02" PRIx8, *(const uint8_t *)data);
break;
case 16:
fprintf(log_file, "0x%04" PRIx16, *(const uint16_t *)data);
@ -59,11 +60,6 @@ static void commit_log_print_value(FILE *log_file, int width, uint64_t val)
commit_log_print_value(log_file, width, &val);
}
const char* processor_t::get_symbol(uint64_t addr)
{
return sim->get_symbol(addr);
}
static void commit_log_print_insn(processor_t *p, reg_t pc, insn_t insn)
{
FILE *log_file = p->get_log_file();
@ -122,10 +118,10 @@ static void commit_log_print_insn(processor_t *p, reg_t pc, insn_t insn)
if (!show_vec && (is_vreg || is_vec)) {
fprintf(log_file, " e%ld %s%ld l%ld",
p->VU.vsew,
(long)p->VU.vsew,
p->VU.vflmul < 1 ? "mf" : "m",
p->VU.vflmul < 1 ? (reg_t)(1 / p->VU.vflmul) : (reg_t)p->VU.vflmul,
p->VU.vl->read());
p->VU.vflmul < 1 ? (long)(1 / p->VU.vflmul) : (long)p->VU.vflmul,
(long)p->VU.vl->read());
show_vec = true;
}
@ -133,7 +129,7 @@ static void commit_log_print_insn(processor_t *p, reg_t pc, insn_t insn)
if (prefix == 'c')
fprintf(log_file, " c%d_%s ", rd, csr_name(rd));
else
fprintf(log_file, " %c%2d ", prefix, rd);
fprintf(log_file, " %c%-2d ", prefix, rd);
if (is_vreg)
commit_log_print_value(log_file, size, &p->VU.elt<uint8_t>(rd, 0));
else
@ -154,40 +150,34 @@ static void commit_log_print_insn(processor_t *p, reg_t pc, insn_t insn)
}
fprintf(log_file, "\n");
}
#else
static void commit_log_reset(processor_t* p) {}
static void commit_log_stash_privilege(processor_t* p) {}
static void commit_log_print_insn(processor_t* p, reg_t pc, insn_t insn) {}
#endif
inline void processor_t::update_histogram(reg_t pc)
{
#ifdef RISCV_ENABLE_HISTOGRAM
pc_histogram[pc]++;
#endif
if (histogram_enabled)
pc_histogram[pc]++;
}
// 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 inline reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch)
// These two functions are expected to be inlined by the compiler separately in
// the processor_t::step() loop. The logged variant is used in the slow path
static inline reg_t execute_insn_fast(processor_t* p, reg_t pc, insn_fetch_t fetch) {
return fetch.func(p, fetch.insn, pc);
}
static inline reg_t execute_insn_logged(processor_t* p, reg_t pc, insn_fetch_t fetch)
{
commit_log_reset(p);
commit_log_stash_privilege(p);
if (p->get_log_commits_enabled()) {
commit_log_reset(p);
commit_log_stash_privilege(p);
}
reg_t npc;
try {
npc = fetch.func(p, fetch.insn, pc);
if (npc != PC_SERIALIZE_BEFORE) {
#ifdef RISCV_ENABLE_COMMITLOG
if (p->get_log_commits_enabled()) {
commit_log_print_insn(p, pc, fetch.insn);
}
#endif
}
#ifdef RISCV_ENABLE_COMMITLOG
} catch (wait_for_interrupt_t &t) {
if (p->get_log_commits_enabled()) {
commit_log_print_insn(p, pc, fetch.insn);
@ -204,7 +194,6 @@ static inline reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch)
}
}
throw;
#endif
} catch(...) {
throw;
}
@ -215,7 +204,8 @@ static inline reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch)
bool processor_t::slow_path()
{
return debug || state.single_step != state.STEP_NONE || state.debug_mode;
return debug || state.single_step != state.STEP_NONE || state.debug_mode ||
log_commits_enabled || histogram_enabled || in_wfi || check_triggers_icount;
}
// fetch/decode/execute loop
@ -238,19 +228,18 @@ void processor_t::step(size_t n)
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++; \
}
if (unlikely(invalid_pc(pc))) { \
switch (pc) { \
case PC_SERIALIZE_BEFORE: state.serialized = true; break; \
case PC_SERIALIZE_AFTER: ++instret; break; \
default: abort(); \
} \
pc = state.pc; \
break; \
} else { \
state.pc = pc; \
instret++; \
}
try
{
@ -274,10 +263,24 @@ void processor_t::step(size_t n)
state.single_step = state.STEP_STEPPED;
}
if (!state.serialized && check_triggers_icount) {
auto match = TM.detect_icount_match();
if (match.has_value()) {
assert(match->timing == triggers::TIMING_BEFORE);
throw triggers::matched_t((triggers::operation_t)0, 0, match->action);
}
}
// debug mode wfis must nop
if (unlikely(in_wfi && !state.debug_mode)) {
throw wait_for_interrupt_t();
}
in_wfi = false;
insn_fetch_t fetch = mmu->load_insn(pc);
if (debug && !state.serialized)
disasm(fetch.insn);
pc = execute_insn(this, pc, fetch);
pc = execute_insn_logged(this, pc, fetch);
advance_pc();
}
}
@ -286,7 +289,7 @@ void processor_t::step(size_t n)
// Main simulation loop, fast path.
for (auto ic_entry = _mmu->access_icache(pc); ; ) {
auto fetch = ic_entry->data;
pc = execute_insn(this, pc, fetch);
pc = execute_insn_fast(this, pc, fetch);
ic_entry = ic_entry->next;
if (unlikely(ic_entry->tag != pc))
break;
@ -304,38 +307,26 @@ void processor_t::step(size_t n)
take_trap(t, pc);
n = instret;
if (unlikely(state.single_step == state.STEP_STEPPED)) {
// Trigger action takes priority over single step
auto match = TM.detect_trap_match(t);
if (match.has_value())
take_trigger_action(match->action, 0, state.pc);
else 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)
catch (triggers::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: {
trap_breakpoint trap(state.v, t.address);
take_trap(trap, pc);
break;
}
default:
abort();
}
take_trigger_action(t.action, t.address, pc);
}
catch(trap_debug_mode&)
{
enter_debug_mode(DCSR_CAUSE_SWBP);
}
catch (wait_for_interrupt_t &t)
{
@ -346,9 +337,14 @@ void processor_t::step(size_t n)
// allows us to switch to other threads only once per idle loop in case
// there is activity.
n = ++instret;
in_wfi = true;
}
state.minstret->bump(instret);
// Model a hart whose CPI is 1.
state.mcycle->bump(instret);
n -= instret;
}
}

View file

@ -15,7 +15,7 @@ class extension_t
virtual std::vector<disasm_insn_t*> get_disasms() = 0;
virtual const char* name() = 0;
virtual void reset() {};
virtual void set_debug(bool value) {};
virtual void set_debug(bool UNUSED value) {}
virtual ~extension_t();
void set_processor(processor_t* _p) { p = _p; }

View file

@ -4,6 +4,6 @@
// These conflict with Boost headers so can't be included from insn_template.h
#define P (*p)
#define require(x) do { if (unlikely(!(x))) throw trap_illegal_instruction(insn.bits()); } while (0)
#define require(x) (unlikely(!(x)) ? throw trap_illegal_instruction(insn.bits()) : (void) 0)
#endif

View file

@ -3,7 +3,9 @@
#include "insn_template.h"
#include "insn_macros.h"
reg_t rv32i_NAME(processor_t* p, insn_t insn, reg_t pc)
#define DECODE_MACRO_USAGE_LOGGED 0
reg_t fast_rv32i_NAME(processor_t* p, insn_t insn, reg_t pc)
{
#define xlen 32
reg_t npc = sext_xlen(pc + insn_length(OPCODE));
@ -13,7 +15,30 @@ reg_t rv32i_NAME(processor_t* p, insn_t insn, reg_t pc)
return npc;
}
reg_t rv64i_NAME(processor_t* p, insn_t insn, reg_t pc)
reg_t fast_rv64i_NAME(processor_t* p, insn_t insn, reg_t pc)
{
#define xlen 64
reg_t npc = sext_xlen(pc + insn_length(OPCODE));
#include "insns/NAME.h"
trace_opcode(p, OPCODE, insn);
#undef xlen
return npc;
}
#undef DECODE_MACRO_USAGE_LOGGED
#define DECODE_MACRO_USAGE_LOGGED 1
reg_t logged_rv32i_NAME(processor_t* p, insn_t insn, reg_t pc)
{
#define xlen 32
reg_t npc = sext_xlen(pc + insn_length(OPCODE));
#include "insns/NAME.h"
trace_opcode(p, OPCODE, insn);
#undef xlen
return npc;
}
reg_t logged_rv64i_NAME(processor_t* p, insn_t insn, reg_t pc)
{
#define xlen 64
reg_t npc = sext_xlen(pc + insn_length(OPCODE));
@ -26,7 +51,10 @@ reg_t rv64i_NAME(processor_t* p, insn_t insn, reg_t pc)
#undef CHECK_REG
#define CHECK_REG(reg) require((reg) < 16)
reg_t rv32e_NAME(processor_t* p, insn_t insn, reg_t pc)
#undef DECODE_MACRO_USAGE_LOGGED
#define DECODE_MACRO_USAGE_LOGGED 0
reg_t fast_rv32e_NAME(processor_t* p, insn_t insn, reg_t pc)
{
#define xlen 32
reg_t npc = sext_xlen(pc + insn_length(OPCODE));
@ -36,7 +64,30 @@ reg_t rv32e_NAME(processor_t* p, insn_t insn, reg_t pc)
return npc;
}
reg_t rv64e_NAME(processor_t* p, insn_t insn, reg_t pc)
reg_t fast_rv64e_NAME(processor_t* p, insn_t insn, reg_t pc)
{
#define xlen 64
reg_t npc = sext_xlen(pc + insn_length(OPCODE));
#include "insns/NAME.h"
trace_opcode(p, OPCODE, insn);
#undef xlen
return npc;
}
#undef DECODE_MACRO_USAGE_LOGGED
#define DECODE_MACRO_USAGE_LOGGED 1
reg_t logged_rv32e_NAME(processor_t* p, insn_t insn, reg_t pc)
{
#define xlen 32
reg_t npc = sext_xlen(pc + insn_length(OPCODE));
#include "insns/NAME.h"
trace_opcode(p, OPCODE, insn);
#undef xlen
return npc;
}
reg_t logged_rv64e_NAME(processor_t* p, insn_t insn, reg_t pc)
{
#define xlen 64
reg_t npc = sext_xlen(pc + insn_length(OPCODE));

View file

@ -1,9 +1,12 @@
// See LICENSE for license details.
#include "decode_macros.h"
#include "arith.h"
#include "mmu.h"
#include "softfloat.h"
#include "internals.h"
#include "specialize.h"
#include "tracer.h"
#include "p_ext_macros.h"
#include "v_ext_macros.h"
#include <assert.h>

View file

@ -10,16 +10,13 @@ uint8_t round_consts [10] = {
uint8_t enc_rcon = insn.rcon() ;
if(enc_rcon > 0xA) {
// Invalid opcode.
throw trap_illegal_instruction(0);
}
require(enc_rcon <= 0xA);
uint32_t temp = (RS1 >> 32) & 0xFFFFFFFF ;
uint8_t rcon = 0 ;
uint64_t result ;
if(enc_rcon != 0xA) {
if (enc_rcon != 0xA) {
temp = (temp >> 8) | (temp << 24); // Rotate right by 8
rcon = round_consts[enc_rcon];
}

View file

@ -1,5 +1,5 @@
uint8_t AES_ENC_SBOX[]= {
static uint8_t UNUSED AES_ENC_SBOX[]= {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
@ -34,7 +34,7 @@ uint8_t AES_ENC_SBOX[]= {
0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
};
uint8_t AES_DEC_SBOX[] = {
static uint8_t UNUSED AES_DEC_SBOX[] = {
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38,
0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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