Compare commits
	
		
			1 Commits
		
	
	
		
			7af7e040da
			...
			feature/is
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| a6c7b1427e | 
@@ -1,49 +1,45 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.12)
 | 
			
		||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
 | 
			
		||||
###############################################################################
 | 
			
		||||
#
 | 
			
		||||
###############################################################################
 | 
			
		||||
project(dbt-rise-tgc VERSION 1.0.0)
 | 
			
		||||
 | 
			
		||||
include(GNUInstallDirs)
 | 
			
		||||
include(flink)
 | 
			
		||||
 | 
			
		||||
find_package(elfio QUIET)
 | 
			
		||||
find_package(Boost COMPONENTS coroutine)
 | 
			
		||||
find_package(jsoncpp)
 | 
			
		||||
 | 
			
		||||
if(TARGET tcc::tcc)
 | 
			
		||||
  set(WITH_TCC ON)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(WITH_LLVM)
 | 
			
		||||
    if(DEFINED ENV{LLVM_HOME})
 | 
			
		||||
        find_path (LLVM_DIR LLVM-Config.cmake $ENV{LLVM_HOME}/lib/cmake/llvm)
 | 
			
		||||
    endif()
 | 
			
		||||
    find_package(LLVM QUIET CONFIG)
 | 
			
		||||
    if(LLVM_FOUND)
 | 
			
		||||
    endif(DEFINED ENV{LLVM_HOME})
 | 
			
		||||
    find_package(LLVM REQUIRED CONFIG)
 | 
			
		||||
    message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
 | 
			
		||||
    message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
 | 
			
		||||
    llvm_map_components_to_libnames(llvm_libs support core mcjit x86codegen x86asmparser)
 | 
			
		||||
	else()
 | 
			
		||||
		find_package(LLVM REQUIRED LLVMSupport LLVMCore LLVMMCJIT LLVMX86CodeGen LLVMX86AsmParser)
 | 
			
		||||
	endif()
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
#Mac needed variables (adapt for your needs - http://www.cmake.org/Wiki/CMake_RPATH_handling#Mac_OS_X_and_the_RPATH)
 | 
			
		||||
#set(CMAKE_MACOSX_RPATH ON)
 | 
			
		||||
#set(CMAKE_SKIP_BUILD_RPATH FALSE)
 | 
			
		||||
#set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
 | 
			
		||||
#set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
 | 
			
		||||
#set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
 | 
			
		||||
 | 
			
		||||
add_subdirectory(softfloat)
 | 
			
		||||
 | 
			
		||||
set(LIB_SOURCES 
 | 
			
		||||
    src/iss/plugin/instruction_count.cpp
 | 
			
		||||
	src/iss/arch/tgc_c.cpp
 | 
			
		||||
	src/vm/tcc/vm_tgc_c.cpp
 | 
			
		||||
	src/vm/interp/vm_tgc_c.cpp
 | 
			
		||||
	src/vm/fp_functions.cpp
 | 
			
		||||
)
 | 
			
		||||
if(WITH_TCC)
 | 
			
		||||
	list(APPEND LIB_SOURCES src/vm/tcc/vm_tgc_c.cpp)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# library files
 | 
			
		||||
if(TARGET ${CORE_NAME}_cpp)
 | 
			
		||||
    list(APPEND LIB_SOURCES ${${CORE_NAME}_OUTPUT_FILES})
 | 
			
		||||
else()
 | 
			
		||||
    FILE(GLOB GEN_ISS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp)
 | 
			
		||||
    FILE(GLOB GEN_VM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/interp/vm_*.cpp)
 | 
			
		||||
    list(APPEND LIB_SOURCES ${GEN_ISS_SOURCES} ${GEN_VM_SOURCES})
 | 
			
		||||
@@ -52,24 +48,33 @@ foreach(FILEPATH ${GEN_ISS_SOURCES})
 | 
			
		||||
        string(TOUPPER ${CORE} CORE)
 | 
			
		||||
        list(APPEND LIB_DEFINES CORE_${CORE})
 | 
			
		||||
    endforeach()
 | 
			
		||||
message("Core defines are ${LIB_DEFINES}")
 | 
			
		||||
 | 
			
		||||
if(WITH_LLVM)
 | 
			
		||||
	FILE(GLOB LLVM_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/llvm/vm_*.cpp)
 | 
			
		||||
	list(APPEND LIB_SOURCES ${LLVM_GEN_SOURCES})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(WITH_TCC)
 | 
			
		||||
	FILE(GLOB TCC_GEN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/tcc/vm_*.cpp)
 | 
			
		||||
	list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES})
 | 
			
		||||
    message("Defines are ${LIB_DEFINES}")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(TARGET RapidJSON OR TARGET RapidJSON::RapidJSON)
 | 
			
		||||
    list(APPEND LIB_SOURCES src/iss/plugin/cycle_estimate.cpp src/iss/plugin/pctrace.cpp)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(WITH_LLVM)
 | 
			
		||||
	FILE(GLOB LLVM_GEN_SOURCES
 | 
			
		||||
	    ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/vm/llvm/vm_*.cpp
 | 
			
		||||
	)
 | 
			
		||||
	list(APPEND LIB_SOURCES ${LLVM_GEN_SOURCES})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(WITH_TCC)
 | 
			
		||||
	FILE(GLOB TCC_GEN_SOURCES
 | 
			
		||||
	    ${CMAKE_CURRENT_SOURCE_DIR}/src/vm/tcc/vm_*.cpp
 | 
			
		||||
	)
 | 
			
		||||
	list(APPEND LIB_SOURCES ${TCC_GEN_SOURCES})
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Define the library
 | 
			
		||||
add_library(${PROJECT_NAME} ${LIB_SOURCES})
 | 
			
		||||
# list code gen dependencies
 | 
			
		||||
if(TARGET ${CORE_NAME}_cpp)
 | 
			
		||||
    add_dependencies(${PROJECT_NAME} ${CORE_NAME}_cpp)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
 | 
			
		||||
     target_compile_options(${PROJECT_NAME} PRIVATE -Wno-shift-count-overflow)
 | 
			
		||||
@@ -84,8 +89,8 @@ if(TARGET jsoncpp::jsoncpp)
 | 
			
		||||
else()
 | 
			
		||||
	target_link_libraries(${PROJECT_NAME} PUBLIC jsoncpp)
 | 
			
		||||
endif()
 | 
			
		||||
if(BUILD_SHARED_LIBS)
 | 
			
		||||
    target_force_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-core)
 | 
			
		||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND BUILD_SHARED_LIBS)
 | 
			
		||||
    target_link_libraries(${PROJECT_NAME} PUBLIC -Wl,--whole-archive dbt-rise-core -Wl,--no-whole-archive)
 | 
			
		||||
else()
 | 
			
		||||
    target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-core)
 | 
			
		||||
endif()
 | 
			
		||||
@@ -156,7 +161,7 @@ if(WITH_TCC)
 | 
			
		||||
    target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_TCC)
 | 
			
		||||
endif()
 | 
			
		||||
# Links the target exe against the libraries
 | 
			
		||||
target_force_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc)
 | 
			
		||||
target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc)
 | 
			
		||||
if(TARGET Boost::program_options)
 | 
			
		||||
    target_link_libraries(${PROJECT_NAME} PUBLIC Boost::program_options)
 | 
			
		||||
else()
 | 
			
		||||
@@ -181,13 +186,10 @@ install(TARGETS tgc-sim
 | 
			
		||||
###############################################################################
 | 
			
		||||
if(TARGET scc-sysc)
 | 
			
		||||
	project(dbt-rise-tgc_sc VERSION 1.0.0)
 | 
			
		||||
	set(LIB_SOURCES 
 | 
			
		||||
    add_library(${PROJECT_NAME} 
 | 
			
		||||
    	src/sysc/core_complex.cpp
 | 
			
		||||
    	src/sysc/register_tgc_c.cpp
 | 
			
		||||
    )
 | 
			
		||||
	FILE(GLOB GEN_SC_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/sysc/register_*.cpp)
 | 
			
		||||
	list(APPEND LIB_SOURCES ${GEN_SC_SOURCES})
 | 
			
		||||
    add_library(${PROJECT_NAME} ${LIB_SOURCES})
 | 
			
		||||
    target_compile_definitions(${PROJECT_NAME} PUBLIC WITH_SYSTEMC)
 | 
			
		||||
    target_compile_definitions(${PROJECT_NAME} PRIVATE CORE_${CORE_NAME})
 | 
			
		||||
    foreach(F IN LISTS TGC_SOURCES)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
# according to https://github.com/horance-liu/flink.cmake/tree/master
 | 
			
		||||
# SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
 | 
			
		||||
include(CMakeParseArguments)
 | 
			
		||||
 | 
			
		||||
function(target_do_force_link_libraries target visibility lib)
 | 
			
		||||
  if(MSVC)
 | 
			
		||||
    target_link_libraries(${target} ${visibility} "/WHOLEARCHIVE:${lib}")
 | 
			
		||||
  elseif(APPLE)
 | 
			
		||||
    target_link_libraries(${target} ${visibility} -Wl,-force_load ${lib})
 | 
			
		||||
  else()
 | 
			
		||||
    target_link_libraries(${target} ${visibility} -Wl,--whole-archive ${lib} -Wl,--no-whole-archive)
 | 
			
		||||
  endif()
 | 
			
		||||
endfunction()
 | 
			
		||||
 | 
			
		||||
function(target_force_link_libraries target)
 | 
			
		||||
  cmake_parse_arguments(FLINK
 | 
			
		||||
    ""
 | 
			
		||||
    ""
 | 
			
		||||
    "PUBLIC;INTERFACE;PRIVATE"
 | 
			
		||||
    ${ARGN}
 | 
			
		||||
  )
 | 
			
		||||
  
 | 
			
		||||
  foreach(lib IN LISTS FLINK_PUBLIC)
 | 
			
		||||
    target_do_force_link_libraries(${target} PUBLIC ${lib})
 | 
			
		||||
  endforeach()
 | 
			
		||||
 | 
			
		||||
  foreach(lib IN LISTS FLINK_INTERFACE)
 | 
			
		||||
    target_do_force_link_libraries(${target} INTERFACE ${lib})
 | 
			
		||||
  endforeach()
 | 
			
		||||
  
 | 
			
		||||
  foreach(lib IN LISTS FLINK_PRIVATE)
 | 
			
		||||
    target_do_force_link_libraries(${target} PRIVATE ${lib})
 | 
			
		||||
  endforeach()
 | 
			
		||||
endfunction()
 | 
			
		||||
@@ -55,7 +55,7 @@ def byteSize(int size){
 | 
			
		||||
    return 128;
 | 
			
		||||
}
 | 
			
		||||
def getCString(def val){
 | 
			
		||||
    return val.toString()+'ULL'
 | 
			
		||||
    return val.toString()
 | 
			
		||||
}
 | 
			
		||||
%>
 | 
			
		||||
#ifndef _${coreDef.name.toUpperCase()}_H_
 | 
			
		||||
 
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 * Copyright (C) 2023 MINRES Technologies GmbH
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright notice,
 | 
			
		||||
 *    this list of conditions and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 | 
			
		||||
 *    this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
 *    and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * 3. Neither the name of the copyright holder nor the names of its contributors
 | 
			
		||||
 *    may be used to endorse or promote products derived from this software
 | 
			
		||||
 *    without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 | 
			
		||||
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
 * POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include "iss_factory.h"
 | 
			
		||||
#include <iss/arch/${coreDef.name.toLowerCase()}.h>
 | 
			
		||||
#include <iss/arch/riscv_hart_m_p.h>
 | 
			
		||||
#include <iss/arch/riscv_hart_mu_p.h>
 | 
			
		||||
#include "sc_core_adapter.h"
 | 
			
		||||
#include "core_complex.h"
 | 
			
		||||
 | 
			
		||||
namespace iss {
 | 
			
		||||
namespace interp {
 | 
			
		||||
using namespace sysc;
 | 
			
		||||
volatile std::array<bool, 2> ${coreDef.name.toLowerCase()}_init = {
 | 
			
		||||
        iss_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|interp", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
 | 
			
		||||
            auto* cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            auto* cpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::${coreDef.name.toLowerCase()}>>(cc);
 | 
			
		||||
            return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::${coreDef.name.toLowerCase()}*>(cpu), gdb_port)}};
 | 
			
		||||
        }),
 | 
			
		||||
        iss_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|interp", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
 | 
			
		||||
            auto* cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            auto* cpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::${coreDef.name.toLowerCase()}>>(cc);
 | 
			
		||||
            return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::${coreDef.name.toLowerCase()}*>(cpu), gdb_port)}};
 | 
			
		||||
        })
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
#if defined(WITH_TCC)
 | 
			
		||||
namespace tcc {
 | 
			
		||||
volatile std::array<bool, 2> ${coreDef.name.toLowerCase()}_init = {
 | 
			
		||||
        core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|tcc", [](unsigned gdb_port, void* data) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            auto* cpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::${coreDef.name.toLowerCase()}>>(cc);
 | 
			
		||||
            return {cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::${coreDef.name.toLowerCase()}*>(cpu), gdb_port)}};
 | 
			
		||||
        }),
 | 
			
		||||
        core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|tcc", [](unsigned gdb_port, void* data) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            auto* cpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::${coreDef.name.toLowerCase()}>>(cc);
 | 
			
		||||
            return {cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::${coreDef.name.toLowerCase()}*>(cpu), gdb_port)}};
 | 
			
		||||
        })
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
@@ -158,81 +158,30 @@ private:
 | 
			
		||||
    /****************************************************************************
 | 
			
		||||
     * start opcode definitions
 | 
			
		||||
     ****************************************************************************/
 | 
			
		||||
    struct instruction_descriptor {
 | 
			
		||||
    struct InstructionDesriptor {
 | 
			
		||||
        size_t length;
 | 
			
		||||
        uint32_t value;
 | 
			
		||||
        uint32_t mask;
 | 
			
		||||
        typename arch::traits<ARCH>::opcode_e op;
 | 
			
		||||
    };
 | 
			
		||||
    struct decoding_tree_node{
 | 
			
		||||
        std::vector<instruction_descriptor> instrs;
 | 
			
		||||
        std::vector<decoding_tree_node*> children;
 | 
			
		||||
        uint32_t submask = std::numeric_limits<uint32_t>::max();
 | 
			
		||||
        uint32_t value;
 | 
			
		||||
        decoding_tree_node(uint32_t value) : value(value){}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    decoding_tree_node* root {nullptr};
 | 
			
		||||
    const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{
 | 
			
		||||
    const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{
 | 
			
		||||
         /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
 | 
			
		||||
        {${instr.length}, ${instr.encoding}, ${instr.mask}, arch::traits<ARCH>::opcode_e::${instr.instruction.name}},<%}%>
 | 
			
		||||
    }};
 | 
			
		||||
 | 
			
		||||
    //static constexpr typename traits::addr_t upper_bits = ~traits::PGMASK;
 | 
			
		||||
    iss::status fetch_ins(virt_addr_t pc, uint8_t * data){
 | 
			
		||||
        auto phys_pc = this->core.v2p(pc);
 | 
			
		||||
        //if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
 | 
			
		||||
        //    if (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err;
 | 
			
		||||
        //    if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction
 | 
			
		||||
        //        if (this->core.read(this->core.v2p(pc + 2), 2, data + 2) != iss::Ok) return iss::Err;
 | 
			
		||||
        //} else {
 | 
			
		||||
            if (this->core.read(phys_pc, 4, data) != iss::Ok)  return iss::Err;
 | 
			
		||||
        //}
 | 
			
		||||
        return iss::Ok;
 | 
			
		||||
    }
 | 
			
		||||
    void populate_decoding_tree(decoding_tree_node* root){
 | 
			
		||||
        //create submask
 | 
			
		||||
        for(auto instr: root->instrs){
 | 
			
		||||
            root->submask &= instr.mask;
 | 
			
		||||
        }
 | 
			
		||||
        //put each instr according to submask&encoding into children
 | 
			
		||||
        for(auto instr: root->instrs){
 | 
			
		||||
            bool foundMatch = false;
 | 
			
		||||
            for(auto child: root->children){
 | 
			
		||||
                //use value as identifying trait
 | 
			
		||||
                if(child->value == (instr.value&root->submask)){
 | 
			
		||||
                    child->instrs.push_back(instr);
 | 
			
		||||
                    foundMatch = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if(!foundMatch){
 | 
			
		||||
                decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask);
 | 
			
		||||
                child->instrs.push_back(instr);
 | 
			
		||||
                root->children.push_back(child);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        root->instrs.clear();
 | 
			
		||||
        //call populate_decoding_tree for all children
 | 
			
		||||
        if(root->children.size() >1)
 | 
			
		||||
            for(auto child: root->children){
 | 
			
		||||
                populate_decoding_tree(child);      
 | 
			
		||||
            }
 | 
			
		||||
        else{
 | 
			
		||||
            //sort instrs by value of the mask, this works bc we want to have the least restrictive one last
 | 
			
		||||
            std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) {
 | 
			
		||||
            return instr1.mask > instr2.mask;
 | 
			
		||||
            }); 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    typename arch::traits<ARCH>::opcode_e  decode_instr(decoding_tree_node* node, code_word_t word){
 | 
			
		||||
        if(!node->children.size()){
 | 
			
		||||
            if(node->instrs.size() == 1) return node->instrs[0].op;
 | 
			
		||||
            for(auto instr : node->instrs){
 | 
			
		||||
                if((instr.mask&word) == instr.value) return instr.op;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            for(auto child : node->children){
 | 
			
		||||
                if (child->value == (node->submask&word)){
 | 
			
		||||
                    return decode_instr(child, word);
 | 
			
		||||
                }  
 | 
			
		||||
            }  
 | 
			
		||||
        }
 | 
			
		||||
        return arch::traits<ARCH>::opcode_e::MAX_OPCODE;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename CODE_WORD> void debug_fn(CODE_WORD insn) {
 | 
			
		||||
@@ -259,11 +208,16 @@ constexpr size_t bit_count(uint32_t u) {
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
 | 
			
		||||
: vm_base<ARCH>(core, core_id, cluster_id) {
 | 
			
		||||
    root = new decoding_tree_node(std::numeric_limits<uint32_t>::max());
 | 
			
		||||
    unsigned id=0;
 | 
			
		||||
    for (auto instr : instr_descr) {
 | 
			
		||||
        root->instrs.push_back(instr);
 | 
			
		||||
        auto quadrant = instr.value & 0x3;
 | 
			
		||||
        qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
 | 
			
		||||
    }
 | 
			
		||||
    for(auto& lut: qlut){
 | 
			
		||||
        std::sort(std::begin(lut), std::end(lut), [](instruction_pattern const& a, instruction_pattern const& b){
 | 
			
		||||
            return bit_count(a.mask) > bit_count(b.mask);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    populate_decoding_tree(root);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool is_count_limit_enabled(finish_cond_e cond){
 | 
			
		||||
@@ -274,6 +228,14 @@ inline bool is_jump_to_self_enabled(finish_cond_e cond){
 | 
			
		||||
    return (cond & finish_cond_e::JUMP_TO_SELF) == finish_cond_e::JUMP_TO_SELF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
typename arch::traits<ARCH>::opcode_e vm_impl<ARCH>::decode_inst_id(code_word_t instr){
 | 
			
		||||
    for(auto& e: qlut[instr&0x3]){
 | 
			
		||||
        if(!((instr&e.mask) ^ e.value )) return e.id;
 | 
			
		||||
    }
 | 
			
		||||
    return arch::traits<ARCH>::opcode_e::MAX_OPCODE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit){
 | 
			
		||||
    auto pc=start;
 | 
			
		||||
@@ -295,7 +257,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
 | 
			
		||||
        } else {
 | 
			
		||||
            if (is_jump_to_self_enabled(cond) &&
 | 
			
		||||
                    (instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
 | 
			
		||||
            auto inst_id = decode_instr(root, instr);
 | 
			
		||||
            auto inst_id = decode_inst_id(instr);
 | 
			
		||||
            // pre execution stuff
 | 
			
		||||
             this->core.reg.last_branch = 0;
 | 
			
		||||
            if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id));
 | 
			
		||||
@@ -342,7 +304,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
 | 
			
		||||
    return pc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace ${coreDef.name.toLowerCase()}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreDef.name.toLowerCase()} *core, unsigned short port, bool dump) {
 | 
			
		||||
@@ -358,14 +320,14 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD
 | 
			
		||||
#include <iss/arch/riscv_hart_mu_p.h>
 | 
			
		||||
namespace iss {
 | 
			
		||||
namespace {
 | 
			
		||||
volatile std::array<bool, 2> dummy = {
 | 
			
		||||
        core_factory::instance().register_creator<core_factory::CPP>("${coreDef.name.toLowerCase()}|m_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
std::array<bool, 2> dummy = {
 | 
			
		||||
        core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::${coreDef.name.toLowerCase()}>();
 | 
			
		||||
		    auto vm = new interp::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false);
 | 
			
		||||
		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
 | 
			
		||||
            return {cpu_ptr{cpu}, vm_ptr{vm}};
 | 
			
		||||
        }),
 | 
			
		||||
        core_factory::instance().register_creator<core_factory::CPP>("${coreDef.name.toLowerCase()}|mu_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
        core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::${coreDef.name.toLowerCase()}>();
 | 
			
		||||
		    auto vm = new interp::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false);
 | 
			
		||||
		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
 | 
			
		||||
@@ -374,3 +336,8 @@ volatile std::array<bool, 2> dummy = {
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
	bool* get_${coreDef.name.toLowerCase()}_interp_creators() {
 | 
			
		||||
		return iss::dummy.data();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -120,7 +120,57 @@ protected:
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // some compile time constants
 | 
			
		||||
    // enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 };
 | 
			
		||||
    enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 };
 | 
			
		||||
    enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 };
 | 
			
		||||
    enum { LUT_SIZE = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK32)), LUT_SIZE_C = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK16)) };
 | 
			
		||||
 | 
			
		||||
    std::array<compile_func, LUT_SIZE> lut;
 | 
			
		||||
 | 
			
		||||
    std::array<compile_func, LUT_SIZE_C> lut_00, lut_01, lut_10;
 | 
			
		||||
    std::array<compile_func, LUT_SIZE> lut_11;
 | 
			
		||||
 | 
			
		||||
    std::array<compile_func *, 4> qlut;
 | 
			
		||||
 | 
			
		||||
    std::array<const uint32_t, 4> lutmasks = {{EXTR_MASK16, EXTR_MASK16, EXTR_MASK16, EXTR_MASK32}};
 | 
			
		||||
 | 
			
		||||
    void expand_bit_mask(int pos, uint32_t mask, uint32_t value, uint32_t valid, uint32_t idx, compile_func lut[],
 | 
			
		||||
                         compile_func f) {
 | 
			
		||||
        if (pos < 0) {
 | 
			
		||||
            lut[idx] = f;
 | 
			
		||||
        } else {
 | 
			
		||||
            auto bitmask = 1UL << pos;
 | 
			
		||||
            if ((mask & bitmask) == 0) {
 | 
			
		||||
                expand_bit_mask(pos - 1, mask, value, valid, idx, lut, f);
 | 
			
		||||
            } else {
 | 
			
		||||
                if ((valid & bitmask) == 0) {
 | 
			
		||||
                    expand_bit_mask(pos - 1, mask, value, valid, (idx << 1), lut, f);
 | 
			
		||||
                    expand_bit_mask(pos - 1, mask, value, valid, (idx << 1) + 1, lut, f);
 | 
			
		||||
                } else {
 | 
			
		||||
                    auto new_val = idx << 1;
 | 
			
		||||
                    if ((value & bitmask) != 0) new_val++;
 | 
			
		||||
                    expand_bit_mask(pos - 1, mask, value, valid, new_val, lut, f);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline uint32_t extract_fields(uint32_t val) { return extract_fields(29, val >> 2, lutmasks[val & 0x3], 0); }
 | 
			
		||||
 | 
			
		||||
    uint32_t extract_fields(int pos, uint32_t val, uint32_t mask, uint32_t lut_val) {
 | 
			
		||||
        if (pos >= 0) {
 | 
			
		||||
            auto bitmask = 1UL << pos;
 | 
			
		||||
            if ((mask & bitmask) == 0) {
 | 
			
		||||
                lut_val = extract_fields(pos - 1, val, mask, lut_val);
 | 
			
		||||
            } else {
 | 
			
		||||
                auto new_val = lut_val << 1;
 | 
			
		||||
                if ((val & bitmask) != 0) new_val++;
 | 
			
		||||
                lut_val = extract_fields(pos - 1, val, mask, new_val);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return lut_val;
 | 
			
		||||
    }
 | 
			
		||||
    template<unsigned W, typename U, typename S = typename std::make_signed<U>::type>
 | 
			
		||||
    inline S sext(U from) {
 | 
			
		||||
        auto mask = (1ULL<<W) - 1;
 | 
			
		||||
@@ -132,23 +182,14 @@ private:
 | 
			
		||||
    /****************************************************************************
 | 
			
		||||
     * start opcode definitions
 | 
			
		||||
     ****************************************************************************/
 | 
			
		||||
    struct instruction_descriptor {
 | 
			
		||||
    struct InstructionDesriptor {
 | 
			
		||||
        size_t length;
 | 
			
		||||
        uint32_t value;
 | 
			
		||||
        uint32_t mask;
 | 
			
		||||
        compile_func op;
 | 
			
		||||
    };
 | 
			
		||||
    struct decoding_tree_node{
 | 
			
		||||
        std::vector<instruction_descriptor> instrs;
 | 
			
		||||
        std::vector<decoding_tree_node*> children;
 | 
			
		||||
        uint32_t submask = std::numeric_limits<uint32_t>::max();
 | 
			
		||||
        uint32_t value;
 | 
			
		||||
        decoding_tree_node(uint32_t value) : value(value){}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    decoding_tree_node* root {nullptr};
 | 
			
		||||
 | 
			
		||||
    const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{
 | 
			
		||||
    const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{
 | 
			
		||||
         /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %>
 | 
			
		||||
        /* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */
 | 
			
		||||
        {${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%>
 | 
			
		||||
@@ -186,64 +227,11 @@ private:
 | 
			
		||||
        vm_impl::gen_trap_check(tu);
 | 
			
		||||
        return BRANCH;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    //decoding functionality
 | 
			
		||||
 | 
			
		||||
    void populate_decoding_tree(decoding_tree_node* root){
 | 
			
		||||
        //create submask
 | 
			
		||||
        for(auto instr: root->instrs){
 | 
			
		||||
            root->submask &= instr.mask;
 | 
			
		||||
        }
 | 
			
		||||
        //put each instr according to submask&encoding into children
 | 
			
		||||
        for(auto instr: root->instrs){
 | 
			
		||||
            bool foundMatch = false;
 | 
			
		||||
            for(auto child: root->children){
 | 
			
		||||
                //use value as identifying trait
 | 
			
		||||
                if(child->value == (instr.value&root->submask)){
 | 
			
		||||
                    child->instrs.push_back(instr);
 | 
			
		||||
                    foundMatch = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if(!foundMatch){
 | 
			
		||||
                decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask);
 | 
			
		||||
                child->instrs.push_back(instr);
 | 
			
		||||
                root->children.push_back(child);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        root->instrs.clear();
 | 
			
		||||
        //call populate_decoding_tree for all children
 | 
			
		||||
        if(root->children.size() >1)
 | 
			
		||||
            for(auto child: root->children){
 | 
			
		||||
                populate_decoding_tree(child);      
 | 
			
		||||
            }
 | 
			
		||||
        else{
 | 
			
		||||
            //sort instrs by value of the mask, this works bc we want to have the least restrictive one last
 | 
			
		||||
            std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) {
 | 
			
		||||
            return instr1.mask > instr2.mask;
 | 
			
		||||
            }); 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    compile_func decode_instr(decoding_tree_node* node, code_word_t word){
 | 
			
		||||
        if(!node->children.size()){
 | 
			
		||||
            if(node->instrs.size() == 1) return node->instrs[0].op;
 | 
			
		||||
            for(auto instr : node->instrs){
 | 
			
		||||
                if((instr.mask&word) == instr.value) return instr.op;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            for(auto child : node->children){
 | 
			
		||||
                if (child->value == (node->submask&word)){
 | 
			
		||||
                    return decode_instr(child, word);
 | 
			
		||||
                }  
 | 
			
		||||
            }  
 | 
			
		||||
        }
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename CODE_WORD> void debug_fn(CODE_WORD instr) {
 | 
			
		||||
    volatile CODE_WORD x = instr;
 | 
			
		||||
    instr = 2 * x;
 | 
			
		||||
template <typename CODE_WORD> void debug_fn(CODE_WORD insn) {
 | 
			
		||||
    volatile CODE_WORD x = insn;
 | 
			
		||||
    insn = 2 * x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); }
 | 
			
		||||
@@ -251,11 +239,14 @@ template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); }
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
 | 
			
		||||
: vm_base<ARCH>(core, core_id, cluster_id) {
 | 
			
		||||
    root = new decoding_tree_node(std::numeric_limits<uint32_t>::max());
 | 
			
		||||
    qlut[0] = lut_00.data();
 | 
			
		||||
    qlut[1] = lut_01.data();
 | 
			
		||||
    qlut[2] = lut_10.data();
 | 
			
		||||
    qlut[3] = lut_11.data();
 | 
			
		||||
    for (auto instr : instr_descr) {
 | 
			
		||||
        root->instrs.push_back(instr);
 | 
			
		||||
        auto quantrant = instr.value & 0x3;
 | 
			
		||||
        expand_bit_mask(29, lutmasks[quantrant], instr.value >> 2, instr.mask >> 2, 0, qlut[quantrant], instr.op);
 | 
			
		||||
    }
 | 
			
		||||
    populate_decoding_tree(root);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
@@ -263,19 +254,30 @@ std::tuple<continuation_e>
 | 
			
		||||
vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, tu_builder& tu) {
 | 
			
		||||
    // we fetch at max 4 byte, alignment is 2
 | 
			
		||||
    enum {TRAP_ID=1<<16};
 | 
			
		||||
    code_word_t instr = 0;
 | 
			
		||||
    code_word_t insn = 0;
 | 
			
		||||
    // const typename traits::addr_t upper_bits = ~traits::PGMASK;
 | 
			
		||||
    phys_addr_t paddr(pc);
 | 
			
		||||
    auto *const data = (uint8_t *)&insn;
 | 
			
		||||
    paddr = this->core.v2p(pc);
 | 
			
		||||
    auto res = this->core.read(paddr, 4, reinterpret_cast<uint8_t*>(&instr));
 | 
			
		||||
//    if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
 | 
			
		||||
//        auto res = this->core.read(paddr, 2, data);
 | 
			
		||||
//        if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
 | 
			
		||||
//        if ((insn & 0x3) == 0x3) { // this is a 32bit instruction
 | 
			
		||||
//            res = this->core.read(this->core.v2p(pc + 2), 2, data + 2);
 | 
			
		||||
//        }
 | 
			
		||||
//    } else {
 | 
			
		||||
        auto res = this->core.read(paddr, 4, data);
 | 
			
		||||
        if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
 | 
			
		||||
    if (instr == 0x0000006f || (instr&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
 | 
			
		||||
//    }
 | 
			
		||||
    if (insn == 0x0000006f || (insn&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
 | 
			
		||||
    // curr pc on stack
 | 
			
		||||
    ++inst_cnt;
 | 
			
		||||
    auto f = decode_instr(root, instr);
 | 
			
		||||
    auto lut_val = extract_fields(insn);
 | 
			
		||||
    auto f = qlut[insn & 0x3][lut_val];
 | 
			
		||||
    if (f == nullptr) {
 | 
			
		||||
        f = &this_class::illegal_intruction;
 | 
			
		||||
    }
 | 
			
		||||
    return (this->*f)(pc, instr, tu);
 | 
			
		||||
    return (this->*f)(pc, insn, tu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(tu_builder& tu, uint16_t trap_id, uint16_t cause) {
 | 
			
		||||
@@ -299,7 +301,7 @@ template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(tu_builder& tu) {
 | 
			
		||||
    tu("return *next_pc;");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace ${coreDef.name.toLowerCase()}
 | 
			
		||||
} // namespace mnrv32
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreDef.name.toLowerCase()} *core, unsigned short port, bool dump) {
 | 
			
		||||
@@ -315,14 +317,14 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD
 | 
			
		||||
#include <iss/arch/riscv_hart_mu_p.h>
 | 
			
		||||
namespace iss {
 | 
			
		||||
namespace {
 | 
			
		||||
volatile std::array<bool, 2> dummy = {
 | 
			
		||||
        core_factory::instance().register_creator<core_factory::CPP>("${coreDef.name.toLowerCase()}|m_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
std::array<bool, 2> dummy = {
 | 
			
		||||
        core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::${coreDef.name.toLowerCase()}>();
 | 
			
		||||
		    auto vm = new tcc::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false);
 | 
			
		||||
		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
 | 
			
		||||
            return {cpu_ptr{cpu}, vm_ptr{vm}};
 | 
			
		||||
        }),
 | 
			
		||||
        core_factory::instance().register_creator<core_factory::CPP>("${coreDef.name.toLowerCase()}|mu_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
        core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|mu_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::${coreDef.name.toLowerCase()}>();
 | 
			
		||||
		    auto vm = new tcc::${coreDef.name.toLowerCase()}::vm_impl<arch::${coreDef.name.toLowerCase()}>(*cpu, false);
 | 
			
		||||
		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
 | 
			
		||||
@@ -331,3 +333,8 @@ volatile std::array<bool, 2> dummy = {
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
	bool* get_${coreDef.name.toLowerCase()}_tcc_creators() {
 | 
			
		||||
		return iss::dummy.data();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								src-gen/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								src-gen/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,2 @@
 | 
			
		||||
/iss
 | 
			
		||||
/vm
 | 
			
		||||
/sysc
 | 
			
		||||
@@ -53,7 +53,7 @@ template <> struct traits<tgc_c> {
 | 
			
		||||
    static constexpr std::array<const char*, 36> reg_aliases{
 | 
			
		||||
        {"ZERO", "RA", "SP", "GP", "TP", "T0", "T1", "T2", "S0", "S1", "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "S2", "S3", "S4", "S5", "S6", "S7", "S8", "S9", "S10", "S11", "T3", "T4", "T5", "T6", "PC", "NEXT_PC", "PRIV", "DPC"}};
 | 
			
		||||
 | 
			
		||||
    enum constants {MISA_VAL=1073746180ULL, MARCHID_VAL=2147483651ULL, XLEN=32ULL, INSTR_ALIGNMENT=2ULL, RFS=32ULL, fence=0ULL, fencei=1ULL, fencevmal=2ULL, fencevmau=3ULL, CSR_SIZE=4096ULL, MUL_LEN=64ULL};
 | 
			
		||||
    enum constants {MISA_VAL=1073746180, MARCHID_VAL=2147483651, XLEN=32, INSTR_ALIGNMENT=2, RFS=32, fence=0, fencei=1, fencevmal=2, fencevmau=3, CSR_SIZE=4096, MUL_LEN=64};
 | 
			
		||||
 | 
			
		||||
    constexpr static unsigned FP_REGS_SIZE = 0;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -80,17 +80,9 @@ class core_factory {
 | 
			
		||||
public:
 | 
			
		||||
    static core_factory & instance() { static core_factory bf; return bf; }
 | 
			
		||||
 | 
			
		||||
    bool register_creator(const std::string & className, create_fn const& fn) {
 | 
			
		||||
        registry[className] = fn;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    bool register_creator(const std::string &, create_fn const&);
 | 
			
		||||
 | 
			
		||||
    base_t create(std::string const& className, unsigned gdb_port=0, void* init_data=nullptr) const {
 | 
			
		||||
        registry_t::const_iterator regEntry = registry.find(className);
 | 
			
		||||
        if (regEntry != registry.end())
 | 
			
		||||
            return regEntry->second(gdb_port, init_data);
 | 
			
		||||
        return {nullptr, nullptr};
 | 
			
		||||
    }
 | 
			
		||||
    base_t create(const std::string &, unsigned gdb_port=0, void* init_data=nullptr) const;
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> get_names() {
 | 
			
		||||
        std::vector<std::string> keys{registry.size()};
 | 
			
		||||
@@ -101,6 +93,18 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
inline bool core_factory::register_creator(const std::string & className, create_fn const& fn) {
 | 
			
		||||
    registry[className] = fn;
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline core_factory::base_t core_factory::create(const std::string &className, unsigned gdb_port, void* data) const {
 | 
			
		||||
    registry_t::const_iterator regEntry = registry.find(className);
 | 
			
		||||
    if (regEntry != registry.end())
 | 
			
		||||
        return regEntry->second(gdb_port, data);
 | 
			
		||||
    return {nullptr, nullptr};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* _ISS_FACTORY_H_ */
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@
 | 
			
		||||
#include <boost/program_options.hpp>
 | 
			
		||||
#include "iss/arch/tgc_mapper.h"
 | 
			
		||||
#ifdef WITH_LLVM
 | 
			
		||||
#include <iss/llvm/jit_init.h>
 | 
			
		||||
#include <iss/llvm/jit_helper.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include <iss/log_categories.h>
 | 
			
		||||
#include "iss/plugin/cycle_estimate.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -37,11 +37,10 @@
 | 
			
		||||
#include <iss/debugger/target_adapter_if.h>
 | 
			
		||||
#include <iss/iss.h>
 | 
			
		||||
#include <iss/vm_types.h>
 | 
			
		||||
#include "iss_factory.h"
 | 
			
		||||
#ifndef WIN32
 | 
			
		||||
#include <iss/plugin/loader.h>
 | 
			
		||||
#endif
 | 
			
		||||
#include "sc_core_adapter_if.h"
 | 
			
		||||
#include "core_complex.h"
 | 
			
		||||
#include <iss/arch/tgc_mapper.h>
 | 
			
		||||
#include <scc/report.h>
 | 
			
		||||
#include <util/ities.h>
 | 
			
		||||
@@ -86,9 +85,136 @@ using namespace sc_core;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
iss::debugger::encoder_decoder encdec;
 | 
			
		||||
 | 
			
		||||
std::array<const char, 4> lvl = {{'U', 'S', 'H', 'M'}};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<typename PLAT>
 | 
			
		||||
class core_wrapper_t : public PLAT {
 | 
			
		||||
public:
 | 
			
		||||
    using reg_t       = typename arch::traits<typename PLAT::core>::reg_t;
 | 
			
		||||
    using phys_addr_t = typename arch::traits<typename PLAT::core>::phys_addr_t;
 | 
			
		||||
    using heart_state_t = typename PLAT::hart_state_type;
 | 
			
		||||
    core_wrapper_t(core_complex *owner)
 | 
			
		||||
    : owner(owner) { }
 | 
			
		||||
 | 
			
		||||
    uint32_t get_mode() { return this->reg.PRIV; }
 | 
			
		||||
 | 
			
		||||
    inline void set_interrupt_execution(bool v) { this->interrupt_sim = v?1:0; }
 | 
			
		||||
 | 
			
		||||
    inline bool get_interrupt_execution() { return this->interrupt_sim; }
 | 
			
		||||
 | 
			
		||||
    heart_state_t &get_state() { return this->state; }
 | 
			
		||||
 | 
			
		||||
    void notify_phase(iss::arch_if::exec_phase p) override {
 | 
			
		||||
        if (p == iss::arch_if::ISTART)
 | 
			
		||||
            owner->sync(this->instr_if.get_total_cycles());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sync_type needed_sync() const override { return PRE_SYNC; }
 | 
			
		||||
 | 
			
		||||
    void disass_output(uint64_t pc, const std::string instr) override {
 | 
			
		||||
        if (!owner->disass_output(pc, instr)) {
 | 
			
		||||
            std::stringstream s;
 | 
			
		||||
            s << "[p:" << lvl[this->reg.PRIV] << ";s:0x" << std::hex << std::setfill('0')
 | 
			
		||||
              << std::setw(sizeof(reg_t) * 2) << (reg_t)this->state.mstatus << std::dec << ";c:"
 | 
			
		||||
              << this->reg.icount + this->cycle_offset << "]";
 | 
			
		||||
            SCCDEBUG(owner->name())<<"disass: "
 | 
			
		||||
                << "0x" << std::setw(16) << std::right << std::setfill('0') << std::hex << pc << "\t\t" << std::setw(40)
 | 
			
		||||
                << std::setfill(' ') << std::left << instr << s.str();
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    status read_mem(phys_addr_t addr, unsigned length, uint8_t *const data) override {
 | 
			
		||||
        if (addr.access && access_type::DEBUG)
 | 
			
		||||
            return owner->read_mem_dbg(addr.val, length, data) ? Ok : Err;
 | 
			
		||||
        else {
 | 
			
		||||
            return owner->read_mem(addr.val, length, data, is_fetch(addr.access)) ? Ok : Err;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    status write_mem(phys_addr_t addr, unsigned length, const uint8_t *const data) override {
 | 
			
		||||
        if (addr.access && access_type::DEBUG)
 | 
			
		||||
            return owner->write_mem_dbg(addr.val, length, data) ? Ok : Err;
 | 
			
		||||
        else {
 | 
			
		||||
            auto res = owner->write_mem(addr.val, length, data) ? Ok : Err;
 | 
			
		||||
            // clear MTIP on mtimecmp write
 | 
			
		||||
            if (addr.val == 0x2004000) {
 | 
			
		||||
                reg_t val;
 | 
			
		||||
                this->read_csr(arch::mip, val);
 | 
			
		||||
                if (val & (1ULL << 7)) this->write_csr(arch::mip, val & ~(1ULL << 7));
 | 
			
		||||
            }
 | 
			
		||||
            return res;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    status read_csr(unsigned addr, reg_t &val) override {
 | 
			
		||||
#ifndef CWR_SYSTEMC
 | 
			
		||||
        if((addr==arch::time || addr==arch::timeh) && owner->mtime_o.get_interface(0)){
 | 
			
		||||
            uint64_t time_val;
 | 
			
		||||
            bool ret = owner->mtime_o->nb_peek(time_val);
 | 
			
		||||
            if (addr == iss::arch::time) {
 | 
			
		||||
                val = static_cast<reg_t>(time_val);
 | 
			
		||||
            } else if (addr == iss::arch::timeh) {
 | 
			
		||||
                if (sizeof(reg_t) != 4) return iss::Err;
 | 
			
		||||
                val = static_cast<reg_t>(time_val >> 32);
 | 
			
		||||
            }
 | 
			
		||||
            return ret?Ok:Err;
 | 
			
		||||
#else
 | 
			
		||||
		if((addr==arch::time || addr==arch::timeh)){
 | 
			
		||||
			uint64_t time_val = owner->mtime_i.read();
 | 
			
		||||
			if (addr == iss::arch::time) {
 | 
			
		||||
				val = static_cast<reg_t>(time_val);
 | 
			
		||||
			} else if (addr == iss::arch::timeh) {
 | 
			
		||||
				if (sizeof(reg_t) != 4) return iss::Err;
 | 
			
		||||
				val = static_cast<reg_t>(time_val >> 32);
 | 
			
		||||
			}
 | 
			
		||||
			return Ok;
 | 
			
		||||
#endif
 | 
			
		||||
        } else {
 | 
			
		||||
            return PLAT::read_csr(addr, val);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void wait_until(uint64_t flags) override {
 | 
			
		||||
        SCCDEBUG(owner->name()) << "Sleeping until interrupt";
 | 
			
		||||
        while(this->reg.pending_trap == 0 && (this->csr[arch::mip] & this->csr[arch::mie]) == 0) {
 | 
			
		||||
            sc_core::wait(wfi_evt);
 | 
			
		||||
        }
 | 
			
		||||
        PLAT::wait_until(flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void local_irq(short id, bool value) {
 | 
			
		||||
        reg_t mask = 0;
 | 
			
		||||
        switch (id) {
 | 
			
		||||
        case 3: // SW
 | 
			
		||||
            mask = 1 << 3;
 | 
			
		||||
            break;
 | 
			
		||||
        case 7: // timer
 | 
			
		||||
            mask = 1 << 7;
 | 
			
		||||
            break;
 | 
			
		||||
        case 11: // external
 | 
			
		||||
            mask = 1 << 11;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            if(id>15) mask = 1 << id;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (value) {
 | 
			
		||||
            this->csr[arch::mip] |= mask;
 | 
			
		||||
            wfi_evt.notify();
 | 
			
		||||
        } else
 | 
			
		||||
            this->csr[arch::mip] &= ~mask;
 | 
			
		||||
        this->check_interrupt();
 | 
			
		||||
        if(value)
 | 
			
		||||
            SCCTRACE(owner->name()) << "Triggering interrupt " << id << " Pending trap: " << this->reg.pending_trap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    core_complex *const owner;
 | 
			
		||||
    sc_event wfi_evt;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int cmd_sysc(int argc, char *argv[], debugger::out_func of, debugger::data_func df,
 | 
			
		||||
             debugger::target_adapter_if *tgt_adapter) {
 | 
			
		||||
    if (argc > 1) {
 | 
			
		||||
@@ -128,9 +254,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    void reset(uint64_t addr){vm->reset(addr);}
 | 
			
		||||
    inline void start(){vm->start();}
 | 
			
		||||
    inline std::pair<uint64_t, bool> load_file(std::string const& name){
 | 
			
		||||
        iss::arch_if* cc = cpu->get_arch_if();
 | 
			
		||||
        return cc->load_file(name);};
 | 
			
		||||
    inline std::pair<uint64_t, bool> load_file(std::string const& name){ return cpu->load_file(name);};
 | 
			
		||||
 | 
			
		||||
    std::function<unsigned(void)> get_mode;
 | 
			
		||||
    std::function<uint64_t(void)> get_state;
 | 
			
		||||
@@ -138,35 +262,45 @@ public:
 | 
			
		||||
    std::function<void(bool)> set_interrupt_execution;
 | 
			
		||||
    std::function<void(short, bool)> local_irq;
 | 
			
		||||
 | 
			
		||||
    void create_cpu(std::string const& type, std::string const& backend, unsigned gdb_port, uint32_t hart_id){
 | 
			
		||||
        auto & f = sysc::iss_factory::instance();
 | 
			
		||||
        if(type.size()==0 || type == "?") {
 | 
			
		||||
            std::cout<<"Available cores: "<<util::join(f.get_names(), ", ")<<std::endl;
 | 
			
		||||
            sc_core::sc_stop();
 | 
			
		||||
        } else if (type.find('|') != std::string::npos) {
 | 
			
		||||
            std::tie(cpu, vm) = f.create(type+"|"+backend);
 | 
			
		||||
        } else {
 | 
			
		||||
            auto base_isa = type.substr(0, 5);
 | 
			
		||||
            if(base_isa=="tgc_d" || base_isa=="tgc_e") {
 | 
			
		||||
                std::tie(cpu, vm) = f.create(type + "|mu_p_clic_pmp|" + backend, gdb_port, owner);
 | 
			
		||||
            } else {
 | 
			
		||||
                std::tie(cpu, vm) = f.create(type + "|m_p|" + backend, gdb_port, owner);
 | 
			
		||||
    template<typename PLAT>
 | 
			
		||||
    std::tuple<cpu_ptr, vm_ptr> create_core(std::string const& backend, unsigned gdb_port, uint32_t hart_id){
 | 
			
		||||
        auto* lcpu = new core_wrapper_t<PLAT>(owner);
 | 
			
		||||
        lcpu->set_mhartid(hart_id);
 | 
			
		||||
        get_mode = [lcpu]() { return lcpu->get_mode(); };
 | 
			
		||||
        get_state = [lcpu]() { return lcpu->get_state().mstatus.backing.val; };
 | 
			
		||||
        get_interrupt_execution = [lcpu]() { return lcpu->get_interrupt_execution(); };
 | 
			
		||||
        set_interrupt_execution = [lcpu](bool b) { return lcpu->set_interrupt_execution(b); };
 | 
			
		||||
        local_irq = [lcpu](short s, bool b) { return lcpu->local_irq(s, b); };
 | 
			
		||||
        if(backend == "interp")
 | 
			
		||||
            return {cpu_ptr{lcpu}, vm_ptr{iss::interp::create(static_cast<typename PLAT::core*>(lcpu), gdb_port)}};
 | 
			
		||||
#ifdef WITH_LLVM
 | 
			
		||||
        if(backend == "llvm")
 | 
			
		||||
            return {cpu_ptr{lcpu}, vm_ptr{iss::llvm::create(lcpu, gdb_port)}};
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef WITH_TCC
 | 
			
		||||
        if(backend == "tcc")
 | 
			
		||||
    s        return {cpu_ptr{lcpu}, vm_ptr{iss::tcc::create(lcpu, gdb_port)}};
 | 
			
		||||
#endif
 | 
			
		||||
        return {nullptr, nullptr};
 | 
			
		||||
    }
 | 
			
		||||
        }
 | 
			
		||||
        if(!cpu ){
 | 
			
		||||
            SCCFATAL() << "Could not create cpu for isa " << type << " and backend " <<backend;
 | 
			
		||||
        }
 | 
			
		||||
        if(!vm ){
 | 
			
		||||
            SCCFATAL() << "Could not create vm for isa " << type << " and backend " <<backend;
 | 
			
		||||
        }
 | 
			
		||||
        auto* sc_cpu_if = reinterpret_cast<sc_core_adapter_if*>(cpu.get());
 | 
			
		||||
        sc_cpu_if->set_mhartid(hart_id);
 | 
			
		||||
        get_mode = [sc_cpu_if]() { return sc_cpu_if->get_mode(); };
 | 
			
		||||
        get_state = [sc_cpu_if]() { return sc_cpu_if->get_state(); };
 | 
			
		||||
        get_interrupt_execution = [sc_cpu_if]() { return sc_cpu_if->get_interrupt_execution(); };
 | 
			
		||||
        set_interrupt_execution = [sc_cpu_if](bool b) { return sc_cpu_if->set_interrupt_execution(b); };
 | 
			
		||||
        local_irq = [sc_cpu_if](short s, bool b) { return sc_cpu_if->local_irq(s, b); };
 | 
			
		||||
 | 
			
		||||
    void create_cpu(std::string const& type, std::string const& backend, unsigned gdb_port, uint32_t hart_id){
 | 
			
		||||
        CREATE_CORE(tgc_c)
 | 
			
		||||
#ifdef CORE_TGC_B
 | 
			
		||||
        CREATE_CORE(tgc_b)
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CORE_TGC_D
 | 
			
		||||
        CREATE_CORE(tgc_d)
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CORE_TGC_D_XRB_MAC
 | 
			
		||||
        CREATE_CORE(tgc_d_xrb_mac)
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef CORE_TGC_D_XRB_NN
 | 
			
		||||
        CREATE_CORE(tgc_d_xrb_nn)
 | 
			
		||||
#endif
 | 
			
		||||
        {
 | 
			
		||||
            LOG(ERR) << "Illegal argument value for core type: " << type << std::endl;
 | 
			
		||||
        }
 | 
			
		||||
        auto *srv = debugger::server<debugger::gdb_session>::get();
 | 
			
		||||
        if (srv) tgt_adapter = srv->get_target();
 | 
			
		||||
        if (tgt_adapter)
 | 
			
		||||
@@ -179,7 +313,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    core_complex * const owner;
 | 
			
		||||
    vm_ptr vm{nullptr};
 | 
			
		||||
    sc_cpu_ptr cpu{nullptr};
 | 
			
		||||
    cpu_ptr cpu{nullptr};
 | 
			
		||||
    iss::debugger::target_adapter_if *tgt_adapter{nullptr};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -500,5 +634,5 @@ bool core_complex::write_mem_dbg(uint64_t addr, unsigned length, const uint8_t *
 | 
			
		||||
    gp.set_streaming_width(length);
 | 
			
		||||
    return dbus->transport_dbg(gp) == length;
 | 
			
		||||
}
 | 
			
		||||
} /* namespace tgfs */
 | 
			
		||||
} /* namespace SiFive */
 | 
			
		||||
} /* namespace sysc */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,88 +0,0 @@
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 * Copyright (C) 2021 MINRES Technologies GmbH
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright notice,
 | 
			
		||||
 *    this list of conditions and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 | 
			
		||||
 *    this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
 *    and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * 3. Neither the name of the copyright holder nor the names of its contributors
 | 
			
		||||
 *    may be used to endorse or promote products derived from this software
 | 
			
		||||
 *    without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 | 
			
		||||
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
 * POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 | 
			
		||||
#ifndef _ISS_FACTORY_H_
 | 
			
		||||
#define _ISS_FACTORY_H_
 | 
			
		||||
 | 
			
		||||
#include <iss/iss.h>
 | 
			
		||||
#include "sc_core_adapter_if.h"
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace sysc {
 | 
			
		||||
 | 
			
		||||
using sc_cpu_ptr = std::unique_ptr<sc_core_adapter_if>;
 | 
			
		||||
using vm_ptr= std::unique_ptr<iss::vm_if>;
 | 
			
		||||
 | 
			
		||||
class iss_factory {
 | 
			
		||||
public:
 | 
			
		||||
    using base_t = std::tuple<sc_cpu_ptr, vm_ptr>;
 | 
			
		||||
    using create_fn = std::function<base_t(unsigned, void*) >;
 | 
			
		||||
    using registry_t = std::unordered_map<std::string, create_fn> ;
 | 
			
		||||
 | 
			
		||||
    iss_factory() = default;
 | 
			
		||||
    iss_factory(const iss_factory &) = delete;
 | 
			
		||||
    iss_factory & operator=(const iss_factory &) = delete;
 | 
			
		||||
 | 
			
		||||
    static iss_factory & instance() { static iss_factory bf; return bf; }
 | 
			
		||||
 | 
			
		||||
    bool register_creator(const std::string & className, create_fn const& fn) {
 | 
			
		||||
        registry[className] = fn;
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    base_t create(std::string const& className, unsigned gdb_port=0, void* init_data=nullptr) const {
 | 
			
		||||
        registry_t::const_iterator regEntry = registry.find(className);
 | 
			
		||||
        if (regEntry != registry.end())
 | 
			
		||||
            return regEntry->second(gdb_port, init_data);
 | 
			
		||||
        return {nullptr, nullptr};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> get_names() {
 | 
			
		||||
        std::vector<std::string> keys{registry.size()};
 | 
			
		||||
        std::transform(std::begin(registry), std::end(registry), std::begin(keys), [](std::pair<std::string, create_fn> const& p){
 | 
			
		||||
            return p.first;
 | 
			
		||||
        });
 | 
			
		||||
        return keys;
 | 
			
		||||
    }
 | 
			
		||||
private:
 | 
			
		||||
    registry_t registry;
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* _ISS_FACTORY_H_ */
 | 
			
		||||
@@ -1,36 +1,14 @@
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
 * Copyright (C) 2023 MINRES Technologies GmbH
 | 
			
		||||
 * All rights reserved.
 | 
			
		||||
/*
 | 
			
		||||
 * register_tgc_c.cpp
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright notice,
 | 
			
		||||
 *    this list of conditions and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 | 
			
		||||
 *    this list of conditions and the following disclaimer in the documentation
 | 
			
		||||
 *    and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 * 3. Neither the name of the copyright holder nor the names of its contributors
 | 
			
		||||
 *    may be used to endorse or promote products derived from this software
 | 
			
		||||
 *    without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 | 
			
		||||
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
			
		||||
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
			
		||||
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 | 
			
		||||
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 | 
			
		||||
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 | 
			
		||||
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 | 
			
		||||
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 | 
			
		||||
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 | 
			
		||||
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 | 
			
		||||
 * POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 *
 | 
			
		||||
 *******************************************************************************/
 | 
			
		||||
 *  Created on: Jul 5, 2023
 | 
			
		||||
 *      Author: eyck
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "iss_factory.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <iss/factory.h>
 | 
			
		||||
#include <iss/arch/tgc_c.h>
 | 
			
		||||
#include <iss/arch/riscv_hart_m_p.h>
 | 
			
		||||
#include <iss/arch/riscv_hart_mu_p.h>
 | 
			
		||||
@@ -38,35 +16,18 @@
 | 
			
		||||
#include "core_complex.h"
 | 
			
		||||
 | 
			
		||||
namespace iss {
 | 
			
		||||
namespace interp {
 | 
			
		||||
using namespace sysc;
 | 
			
		||||
volatile std::array<bool, 2> tgc_init = {
 | 
			
		||||
        iss_factory::instance().register_creator("tgc_c|m_p|interp", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
 | 
			
		||||
            auto* cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            auto* cpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::tgc_c>>(cc);
 | 
			
		||||
            return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::tgc_c*>(cpu), gdb_port)}};
 | 
			
		||||
namespace {
 | 
			
		||||
volatile std::array<bool, 2> dummy = {
 | 
			
		||||
        core_factory::instance().register_creator("tgc_c|m_p|interp", [](unsigned gdb_port, void* data) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            arch::tgc_c* lcpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::tgc_c>>(cc);
 | 
			
		||||
            return {cpu_ptr{lcpu}, vm_ptr{interp::create(lcpu, gdb_port)}};
 | 
			
		||||
        }),
 | 
			
		||||
        iss_factory::instance().register_creator("tgc_c|mu_p|interp", [](unsigned gdb_port, void* data) -> iss_factory::base_t {
 | 
			
		||||
            auto* cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            auto* cpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::tgc_c>>(cc);
 | 
			
		||||
            return {sysc::sc_cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::tgc_c*>(cpu), gdb_port)}};
 | 
			
		||||
        core_factory::instance().register_creator("tgc_c|mu_p|interp", [](unsigned gdb_port, void* data) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            arch::tgc_c* lcpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::tgc_c>>(cc);
 | 
			
		||||
            return {cpu_ptr{lcpu}, vm_ptr{interp::create(lcpu, gdb_port)}};
 | 
			
		||||
        })
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
#if defined(WITH_TCC)
 | 
			
		||||
namespace tcc {
 | 
			
		||||
volatile std::array<bool, 2> tgc_init = {
 | 
			
		||||
        core_factory::instance().register_creator("tgc_c|m_p|tcc", [](unsigned gdb_port, void* data) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            auto* cpu = new sc_core_adapter<arch::riscv_hart_m_p<arch::tgc_c>>(cc);
 | 
			
		||||
            return {cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::tgc_c*>(cpu), gdb_port)}};
 | 
			
		||||
        }),
 | 
			
		||||
        core_factory::instance().register_creator("tgc_c|mu_p|tcc", [](unsigned gdb_port, void* data) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto cc = reinterpret_cast<sysc::tgfs::core_complex*>(data);
 | 
			
		||||
            auto* cpu = new sc_core_adapter<arch::riscv_hart_mu_p<arch::tgc_c>>(cc);
 | 
			
		||||
            return {cpu_ptr{cpu}, vm_ptr{create(static_cast<arch::tgc_c*>(cpu), gdb_port)}};
 | 
			
		||||
        })
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,14 +11,14 @@
 | 
			
		||||
 | 
			
		||||
#include <scc/report.h>
 | 
			
		||||
#include <util/ities.h>
 | 
			
		||||
#include "sc_core_adapter_if.h"
 | 
			
		||||
#include "core_complex.h"
 | 
			
		||||
#include <iss/iss.h>
 | 
			
		||||
#include <iss/vm_types.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
namespace sysc {
 | 
			
		||||
 | 
			
		||||
template<typename PLAT>
 | 
			
		||||
class sc_core_adapter : public PLAT, public sc_core_adapter_if {
 | 
			
		||||
class sc_core_adapter : public PLAT {
 | 
			
		||||
public:
 | 
			
		||||
    using reg_t       = typename iss::arch::traits<typename PLAT::core>::reg_t;
 | 
			
		||||
    using phys_addr_t = typename iss::arch::traits<typename PLAT::core>::phys_addr_t;
 | 
			
		||||
@@ -26,17 +26,13 @@ public:
 | 
			
		||||
    sc_core_adapter(sysc::tgfs::core_complex *owner)
 | 
			
		||||
    : owner(owner) { }
 | 
			
		||||
 | 
			
		||||
    iss::arch_if* get_arch_if() override { return this;}
 | 
			
		||||
    uint32_t get_mode() { return this->reg.PRIV; }
 | 
			
		||||
 | 
			
		||||
    void set_mhartid(unsigned id) override { PLAT::set_mhartid(id); }
 | 
			
		||||
    inline void set_interrupt_execution(bool v) { this->interrupt_sim = v?1:0; }
 | 
			
		||||
 | 
			
		||||
    uint32_t get_mode() override { return this->reg.PRIV; }
 | 
			
		||||
    inline bool get_interrupt_execution() { return this->interrupt_sim; }
 | 
			
		||||
 | 
			
		||||
    void set_interrupt_execution(bool v) override { this->interrupt_sim = v?1:0; }
 | 
			
		||||
 | 
			
		||||
    bool get_interrupt_execution() override { return this->interrupt_sim; }
 | 
			
		||||
 | 
			
		||||
    uint64_t get_state() override { return this->state.mstatus.backing.val; }
 | 
			
		||||
    heart_state_t &get_state() { return this->state; }
 | 
			
		||||
 | 
			
		||||
    void notify_phase(iss::arch_if::exec_phase p) override {
 | 
			
		||||
        if (p == iss::arch_if::ISTART)
 | 
			
		||||
@@ -117,7 +113,7 @@ public:
 | 
			
		||||
        PLAT::wait_until(flags);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void local_irq(short id, bool value) override {
 | 
			
		||||
    void local_irq(short id, bool value) {
 | 
			
		||||
        reg_t mask = 0;
 | 
			
		||||
        switch (id) {
 | 
			
		||||
        case 3: // SW
 | 
			
		||||
@@ -147,5 +143,6 @@ private:
 | 
			
		||||
    sysc::tgfs::core_complex *const owner;
 | 
			
		||||
    sc_core::sc_event wfi_evt;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif /* _SYSC_SC_CORE_ADAPTER_H_ */
 | 
			
		||||
 
 | 
			
		||||
@@ -1,31 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * sc_core_adapter.h
 | 
			
		||||
 *
 | 
			
		||||
 *  Created on: Jul 5, 2023
 | 
			
		||||
 *      Author: eyck
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _SYSC_SC_CORE_ADAPTER_IF_H_
 | 
			
		||||
#define _SYSC_SC_CORE_ADAPTER_IF_H_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <scc/report.h>
 | 
			
		||||
#include <util/ities.h>
 | 
			
		||||
#include "core_complex.h"
 | 
			
		||||
#include <iss/iss.h>
 | 
			
		||||
#include <iss/vm_types.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
 | 
			
		||||
namespace sysc {
 | 
			
		||||
struct sc_core_adapter_if {
 | 
			
		||||
    virtual iss::arch_if* get_arch_if() = 0;
 | 
			
		||||
    virtual void set_mhartid(unsigned) = 0;
 | 
			
		||||
    virtual uint32_t get_mode() = 0;
 | 
			
		||||
    virtual uint64_t get_state() = 0;
 | 
			
		||||
    virtual bool get_interrupt_execution() = 0;
 | 
			
		||||
    virtual void set_interrupt_execution(bool v) = 0;
 | 
			
		||||
    virtual void local_irq(short id, bool value) = 0;
 | 
			
		||||
    virtual ~sc_core_adapter_if() = default;
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
#endif /* _SYSC_SC_CORE_ADAPTER_IF_H_ */
 | 
			
		||||
@@ -152,22 +152,14 @@ private:
 | 
			
		||||
    /****************************************************************************
 | 
			
		||||
     * start opcode definitions
 | 
			
		||||
     ****************************************************************************/
 | 
			
		||||
    struct instruction_descriptor {
 | 
			
		||||
    struct InstructionDesriptor {
 | 
			
		||||
        size_t length;
 | 
			
		||||
        uint32_t value;
 | 
			
		||||
        uint32_t mask;
 | 
			
		||||
        typename arch::traits<ARCH>::opcode_e op;
 | 
			
		||||
    };
 | 
			
		||||
    struct decoding_tree_node{
 | 
			
		||||
        std::vector<instruction_descriptor> instrs;
 | 
			
		||||
        std::vector<decoding_tree_node*> children;
 | 
			
		||||
        uint32_t submask = std::numeric_limits<uint32_t>::max();
 | 
			
		||||
        uint32_t value;
 | 
			
		||||
        decoding_tree_node(uint32_t value) : value(value){}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    decoding_tree_node* root {nullptr};
 | 
			
		||||
    const std::array<instruction_descriptor, 87> instr_descr = {{
 | 
			
		||||
    const std::array<InstructionDesriptor, 87> instr_descr = {{
 | 
			
		||||
         /* entries are: size, valid value, valid mask, function ptr */
 | 
			
		||||
        {32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, arch::traits<ARCH>::opcode_e::LUI},
 | 
			
		||||
        {32, 0b00000000000000000000000000010111, 0b00000000000000000000000001111111, arch::traits<ARCH>::opcode_e::AUIPC},
 | 
			
		||||
@@ -258,61 +250,18 @@ private:
 | 
			
		||||
        {16, 0b0000000000000000, 0b1111111111111111, arch::traits<ARCH>::opcode_e::DII},
 | 
			
		||||
    }};
 | 
			
		||||
 | 
			
		||||
    //static constexpr typename traits::addr_t upper_bits = ~traits::PGMASK;
 | 
			
		||||
    iss::status fetch_ins(virt_addr_t pc, uint8_t * data){
 | 
			
		||||
        auto phys_pc = this->core.v2p(pc);
 | 
			
		||||
        //if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
 | 
			
		||||
        //    if (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err;
 | 
			
		||||
        //    if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction
 | 
			
		||||
        //        if (this->core.read(this->core.v2p(pc + 2), 2, data + 2) != iss::Ok) return iss::Err;
 | 
			
		||||
        //} else {
 | 
			
		||||
            if (this->core.read(phys_pc, 4, data) != iss::Ok)  return iss::Err;
 | 
			
		||||
        //}
 | 
			
		||||
        return iss::Ok;
 | 
			
		||||
    }
 | 
			
		||||
    void populate_decoding_tree(decoding_tree_node* root){
 | 
			
		||||
        //create submask
 | 
			
		||||
        for(auto instr: root->instrs){
 | 
			
		||||
            root->submask &= instr.mask;
 | 
			
		||||
        }
 | 
			
		||||
        //put each instr according to submask&encoding into children
 | 
			
		||||
        for(auto instr: root->instrs){
 | 
			
		||||
            bool foundMatch = false;
 | 
			
		||||
            for(auto child: root->children){
 | 
			
		||||
                //use value as identifying trait
 | 
			
		||||
                if(child->value == (instr.value&root->submask)){
 | 
			
		||||
                    child->instrs.push_back(instr);
 | 
			
		||||
                    foundMatch = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if(!foundMatch){
 | 
			
		||||
                decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask);
 | 
			
		||||
                child->instrs.push_back(instr);
 | 
			
		||||
                root->children.push_back(child);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        root->instrs.clear();
 | 
			
		||||
        //call populate_decoding_tree for all children
 | 
			
		||||
        if(root->children.size() >1)
 | 
			
		||||
            for(auto child: root->children){
 | 
			
		||||
                populate_decoding_tree(child);      
 | 
			
		||||
            }
 | 
			
		||||
        else{
 | 
			
		||||
            //sort instrs by value of the mask, this works bc we want to have the least restrictive one last
 | 
			
		||||
            std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) {
 | 
			
		||||
            return instr1.mask > instr2.mask;
 | 
			
		||||
            }); 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    typename arch::traits<ARCH>::opcode_e  decode_instr(decoding_tree_node* node, code_word_t word){
 | 
			
		||||
        if(!node->children.size()){
 | 
			
		||||
            if(node->instrs.size() == 1) return node->instrs[0].op;
 | 
			
		||||
            for(auto instr : node->instrs){
 | 
			
		||||
                if((instr.mask&word) == instr.value) return instr.op;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            for(auto child : node->children){
 | 
			
		||||
                if (child->value == (node->submask&word)){
 | 
			
		||||
                    return decode_instr(child, word);
 | 
			
		||||
                }  
 | 
			
		||||
            }  
 | 
			
		||||
        }
 | 
			
		||||
        return arch::traits<ARCH>::opcode_e::MAX_OPCODE;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename CODE_WORD> void debug_fn(CODE_WORD insn) {
 | 
			
		||||
@@ -339,11 +288,16 @@ constexpr size_t bit_count(uint32_t u) {
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
 | 
			
		||||
: vm_base<ARCH>(core, core_id, cluster_id) {
 | 
			
		||||
    root = new decoding_tree_node(std::numeric_limits<uint32_t>::max());
 | 
			
		||||
    unsigned id=0;
 | 
			
		||||
    for (auto instr : instr_descr) {
 | 
			
		||||
        root->instrs.push_back(instr);
 | 
			
		||||
        auto quadrant = instr.value & 0x3;
 | 
			
		||||
        qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
 | 
			
		||||
    }
 | 
			
		||||
    for(auto& lut: qlut){
 | 
			
		||||
        std::sort(std::begin(lut), std::end(lut), [](instruction_pattern const& a, instruction_pattern const& b){
 | 
			
		||||
            return bit_count(a.mask) > bit_count(b.mask);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    populate_decoding_tree(root);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline bool is_count_limit_enabled(finish_cond_e cond){
 | 
			
		||||
@@ -354,6 +308,14 @@ inline bool is_jump_to_self_enabled(finish_cond_e cond){
 | 
			
		||||
    return (cond & finish_cond_e::JUMP_TO_SELF) == finish_cond_e::JUMP_TO_SELF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
typename arch::traits<ARCH>::opcode_e vm_impl<ARCH>::decode_inst_id(code_word_t instr){
 | 
			
		||||
    for(auto& e: qlut[instr&0x3]){
 | 
			
		||||
        if(!((instr&e.mask) ^ e.value )) return e.id;
 | 
			
		||||
    }
 | 
			
		||||
    return arch::traits<ARCH>::opcode_e::MAX_OPCODE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit){
 | 
			
		||||
    auto pc=start;
 | 
			
		||||
@@ -375,7 +337,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
 | 
			
		||||
        } else {
 | 
			
		||||
            if (is_jump_to_self_enabled(cond) &&
 | 
			
		||||
                    (instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
 | 
			
		||||
            auto inst_id = decode_instr(root, instr);
 | 
			
		||||
            auto inst_id = decode_inst_id(instr);
 | 
			
		||||
            // pre execution stuff
 | 
			
		||||
             this->core.reg.last_branch = 0;
 | 
			
		||||
            if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id));
 | 
			
		||||
@@ -2673,7 +2635,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
 | 
			
		||||
    return pc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace tgc_c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
std::unique_ptr<vm_if> create<arch::tgc_c>(arch::tgc_c *core, unsigned short port, bool dump) {
 | 
			
		||||
@@ -2689,7 +2651,7 @@ std::unique_ptr<vm_if> create<arch::tgc_c>(arch::tgc_c *core, unsigned short por
 | 
			
		||||
#include <iss/arch/riscv_hart_mu_p.h>
 | 
			
		||||
namespace iss {
 | 
			
		||||
namespace {
 | 
			
		||||
volatile std::array<bool, 2> dummy = {
 | 
			
		||||
std::array<bool, 2> dummy = {
 | 
			
		||||
        core_factory::instance().register_creator("tgc_c|m_p|interp", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::tgc_c>();
 | 
			
		||||
		    auto vm = new interp::tgc_c::vm_impl<arch::tgc_c>(*cpu, false);
 | 
			
		||||
@@ -2705,3 +2667,8 @@ volatile std::array<bool, 2> dummy = {
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
	bool* get_tgc_c_interp_creators() {
 | 
			
		||||
		return iss::dummy.data();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -120,7 +120,57 @@ protected:
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // some compile time constants
 | 
			
		||||
    // enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 };
 | 
			
		||||
    enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 };
 | 
			
		||||
    enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 };
 | 
			
		||||
    enum { LUT_SIZE = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK32)), LUT_SIZE_C = 1 << util::bit_count(static_cast<uint32_t>(EXTR_MASK16)) };
 | 
			
		||||
 | 
			
		||||
    std::array<compile_func, LUT_SIZE> lut;
 | 
			
		||||
 | 
			
		||||
    std::array<compile_func, LUT_SIZE_C> lut_00, lut_01, lut_10;
 | 
			
		||||
    std::array<compile_func, LUT_SIZE> lut_11;
 | 
			
		||||
 | 
			
		||||
    std::array<compile_func *, 4> qlut;
 | 
			
		||||
 | 
			
		||||
    std::array<const uint32_t, 4> lutmasks = {{EXTR_MASK16, EXTR_MASK16, EXTR_MASK16, EXTR_MASK32}};
 | 
			
		||||
 | 
			
		||||
    void expand_bit_mask(int pos, uint32_t mask, uint32_t value, uint32_t valid, uint32_t idx, compile_func lut[],
 | 
			
		||||
                         compile_func f) {
 | 
			
		||||
        if (pos < 0) {
 | 
			
		||||
            lut[idx] = f;
 | 
			
		||||
        } else {
 | 
			
		||||
            auto bitmask = 1UL << pos;
 | 
			
		||||
            if ((mask & bitmask) == 0) {
 | 
			
		||||
                expand_bit_mask(pos - 1, mask, value, valid, idx, lut, f);
 | 
			
		||||
            } else {
 | 
			
		||||
                if ((valid & bitmask) == 0) {
 | 
			
		||||
                    expand_bit_mask(pos - 1, mask, value, valid, (idx << 1), lut, f);
 | 
			
		||||
                    expand_bit_mask(pos - 1, mask, value, valid, (idx << 1) + 1, lut, f);
 | 
			
		||||
                } else {
 | 
			
		||||
                    auto new_val = idx << 1;
 | 
			
		||||
                    if ((value & bitmask) != 0) new_val++;
 | 
			
		||||
                    expand_bit_mask(pos - 1, mask, value, valid, new_val, lut, f);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline uint32_t extract_fields(uint32_t val) { return extract_fields(29, val >> 2, lutmasks[val & 0x3], 0); }
 | 
			
		||||
 | 
			
		||||
    uint32_t extract_fields(int pos, uint32_t val, uint32_t mask, uint32_t lut_val) {
 | 
			
		||||
        if (pos >= 0) {
 | 
			
		||||
            auto bitmask = 1UL << pos;
 | 
			
		||||
            if ((mask & bitmask) == 0) {
 | 
			
		||||
                lut_val = extract_fields(pos - 1, val, mask, lut_val);
 | 
			
		||||
            } else {
 | 
			
		||||
                auto new_val = lut_val << 1;
 | 
			
		||||
                if ((val & bitmask) != 0) new_val++;
 | 
			
		||||
                lut_val = extract_fields(pos - 1, val, mask, new_val);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return lut_val;
 | 
			
		||||
    }
 | 
			
		||||
    template<unsigned W, typename U, typename S = typename std::make_signed<U>::type>
 | 
			
		||||
    inline S sext(U from) {
 | 
			
		||||
        auto mask = (1ULL<<W) - 1;
 | 
			
		||||
@@ -132,23 +182,14 @@ private:
 | 
			
		||||
    /****************************************************************************
 | 
			
		||||
     * start opcode definitions
 | 
			
		||||
     ****************************************************************************/
 | 
			
		||||
    struct instruction_descriptor {
 | 
			
		||||
    struct InstructionDesriptor {
 | 
			
		||||
        size_t length;
 | 
			
		||||
        uint32_t value;
 | 
			
		||||
        uint32_t mask;
 | 
			
		||||
        compile_func op;
 | 
			
		||||
    };
 | 
			
		||||
    struct decoding_tree_node{
 | 
			
		||||
        std::vector<instruction_descriptor> instrs;
 | 
			
		||||
        std::vector<decoding_tree_node*> children;
 | 
			
		||||
        uint32_t submask = std::numeric_limits<uint32_t>::max();
 | 
			
		||||
        uint32_t value;
 | 
			
		||||
        decoding_tree_node(uint32_t value) : value(value){}
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    decoding_tree_node* root {nullptr};
 | 
			
		||||
 | 
			
		||||
    const std::array<instruction_descriptor, 87> instr_descr = {{
 | 
			
		||||
    const std::array<InstructionDesriptor, 87> instr_descr = {{
 | 
			
		||||
         /* entries are: size, valid value, valid mask, function ptr */
 | 
			
		||||
        /* instruction LUI, encoding '0b00000000000000000000000000110111' */
 | 
			
		||||
        {32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, &this_class::__lui},
 | 
			
		||||
@@ -3095,64 +3136,11 @@ private:
 | 
			
		||||
        vm_impl::gen_trap_check(tu);
 | 
			
		||||
        return BRANCH;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    //decoding functionality
 | 
			
		||||
 | 
			
		||||
    void populate_decoding_tree(decoding_tree_node* root){
 | 
			
		||||
        //create submask
 | 
			
		||||
        for(auto instr: root->instrs){
 | 
			
		||||
            root->submask &= instr.mask;
 | 
			
		||||
        }
 | 
			
		||||
        //put each instr according to submask&encoding into children
 | 
			
		||||
        for(auto instr: root->instrs){
 | 
			
		||||
            bool foundMatch = false;
 | 
			
		||||
            for(auto child: root->children){
 | 
			
		||||
                //use value as identifying trait
 | 
			
		||||
                if(child->value == (instr.value&root->submask)){
 | 
			
		||||
                    child->instrs.push_back(instr);
 | 
			
		||||
                    foundMatch = true;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if(!foundMatch){
 | 
			
		||||
                decoding_tree_node* child = new decoding_tree_node(instr.value&root->submask);
 | 
			
		||||
                child->instrs.push_back(instr);
 | 
			
		||||
                root->children.push_back(child);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        root->instrs.clear();
 | 
			
		||||
        //call populate_decoding_tree for all children
 | 
			
		||||
        if(root->children.size() >1)
 | 
			
		||||
            for(auto child: root->children){
 | 
			
		||||
                populate_decoding_tree(child);      
 | 
			
		||||
            }
 | 
			
		||||
        else{
 | 
			
		||||
            //sort instrs by value of the mask, this works bc we want to have the least restrictive one last
 | 
			
		||||
            std::sort(root->children[0]->instrs.begin(), root->children[0]->instrs.end(), [](const instruction_descriptor& instr1, const instruction_descriptor& instr2) {
 | 
			
		||||
            return instr1.mask > instr2.mask;
 | 
			
		||||
            }); 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    compile_func decode_instr(decoding_tree_node* node, code_word_t word){
 | 
			
		||||
        if(!node->children.size()){
 | 
			
		||||
            if(node->instrs.size() == 1) return node->instrs[0].op;
 | 
			
		||||
            for(auto instr : node->instrs){
 | 
			
		||||
                if((instr.mask&word) == instr.value) return instr.op;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else{
 | 
			
		||||
            for(auto child : node->children){
 | 
			
		||||
                if (child->value == (node->submask&word)){
 | 
			
		||||
                    return decode_instr(child, word);
 | 
			
		||||
                }  
 | 
			
		||||
            }  
 | 
			
		||||
        }
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename CODE_WORD> void debug_fn(CODE_WORD instr) {
 | 
			
		||||
    volatile CODE_WORD x = instr;
 | 
			
		||||
    instr = 2 * x;
 | 
			
		||||
template <typename CODE_WORD> void debug_fn(CODE_WORD insn) {
 | 
			
		||||
    volatile CODE_WORD x = insn;
 | 
			
		||||
    insn = 2 * x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); }
 | 
			
		||||
@@ -3160,11 +3148,14 @@ template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); }
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id)
 | 
			
		||||
: vm_base<ARCH>(core, core_id, cluster_id) {
 | 
			
		||||
    root = new decoding_tree_node(std::numeric_limits<uint32_t>::max());
 | 
			
		||||
    qlut[0] = lut_00.data();
 | 
			
		||||
    qlut[1] = lut_01.data();
 | 
			
		||||
    qlut[2] = lut_10.data();
 | 
			
		||||
    qlut[3] = lut_11.data();
 | 
			
		||||
    for (auto instr : instr_descr) {
 | 
			
		||||
        root->instrs.push_back(instr);
 | 
			
		||||
        auto quantrant = instr.value & 0x3;
 | 
			
		||||
        expand_bit_mask(29, lutmasks[quantrant], instr.value >> 2, instr.mask >> 2, 0, qlut[quantrant], instr.op);
 | 
			
		||||
    }
 | 
			
		||||
    populate_decoding_tree(root);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH>
 | 
			
		||||
@@ -3172,19 +3163,30 @@ std::tuple<continuation_e>
 | 
			
		||||
vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, tu_builder& tu) {
 | 
			
		||||
    // we fetch at max 4 byte, alignment is 2
 | 
			
		||||
    enum {TRAP_ID=1<<16};
 | 
			
		||||
    code_word_t instr = 0;
 | 
			
		||||
    code_word_t insn = 0;
 | 
			
		||||
    // const typename traits::addr_t upper_bits = ~traits::PGMASK;
 | 
			
		||||
    phys_addr_t paddr(pc);
 | 
			
		||||
    auto *const data = (uint8_t *)&insn;
 | 
			
		||||
    paddr = this->core.v2p(pc);
 | 
			
		||||
    auto res = this->core.read(paddr, 4, reinterpret_cast<uint8_t*>(&instr));
 | 
			
		||||
//    if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary
 | 
			
		||||
//        auto res = this->core.read(paddr, 2, data);
 | 
			
		||||
//        if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
 | 
			
		||||
//        if ((insn & 0x3) == 0x3) { // this is a 32bit instruction
 | 
			
		||||
//            res = this->core.read(this->core.v2p(pc + 2), 2, data + 2);
 | 
			
		||||
//        }
 | 
			
		||||
//    } else {
 | 
			
		||||
        auto res = this->core.read(paddr, 4, data);
 | 
			
		||||
        if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val);
 | 
			
		||||
    if (instr == 0x0000006f || (instr&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
 | 
			
		||||
//    }
 | 
			
		||||
    if (insn == 0x0000006f || (insn&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0'
 | 
			
		||||
    // curr pc on stack
 | 
			
		||||
    ++inst_cnt;
 | 
			
		||||
    auto f = decode_instr(root, instr);
 | 
			
		||||
    auto lut_val = extract_fields(insn);
 | 
			
		||||
    auto f = qlut[insn & 0x3][lut_val];
 | 
			
		||||
    if (f == nullptr) {
 | 
			
		||||
        f = &this_class::illegal_intruction;
 | 
			
		||||
    }
 | 
			
		||||
    return (this->*f)(pc, instr, tu);
 | 
			
		||||
    return (this->*f)(pc, insn, tu);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(tu_builder& tu, uint16_t trap_id, uint16_t cause) {
 | 
			
		||||
@@ -3208,7 +3210,7 @@ template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(tu_builder& tu) {
 | 
			
		||||
    tu("return *next_pc;");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace tgc_c
 | 
			
		||||
} // namespace mnrv32
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
std::unique_ptr<vm_if> create<arch::tgc_c>(arch::tgc_c *core, unsigned short port, bool dump) {
 | 
			
		||||
@@ -3216,7 +3218,7 @@ std::unique_ptr<vm_if> create<arch::tgc_c>(arch::tgc_c *core, unsigned short por
 | 
			
		||||
    if (port != 0) debugger::server<debugger::gdb_session>::run_server(ret, port);
 | 
			
		||||
    return std::unique_ptr<vm_if>(ret);
 | 
			
		||||
}
 | 
			
		||||
} // namespace tcc
 | 
			
		||||
} // namesapce tcc
 | 
			
		||||
} // namespace iss
 | 
			
		||||
 | 
			
		||||
#include <iss/factory.h>
 | 
			
		||||
@@ -3224,19 +3226,24 @@ std::unique_ptr<vm_if> create<arch::tgc_c>(arch::tgc_c *core, unsigned short por
 | 
			
		||||
#include <iss/arch/riscv_hart_mu_p.h>
 | 
			
		||||
namespace iss {
 | 
			
		||||
namespace {
 | 
			
		||||
volatile std::array<bool, 2> dummy = {
 | 
			
		||||
std::array<bool, 2> dummy = {
 | 
			
		||||
        core_factory::instance().register_creator("tgc_c|m_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto* cpu = new iss::arch::riscv_hart_m_p<iss::arch::tgc_c>();
 | 
			
		||||
            auto* vm = new tcc::tgc_c::vm_impl<arch::tgc_c>(*cpu, false);
 | 
			
		||||
		    auto vm = new tcc::tgc_c::vm_impl<arch::tgc_c>(*cpu, false);
 | 
			
		||||
		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
 | 
			
		||||
            return {cpu_ptr{cpu}, vm_ptr{vm}};
 | 
			
		||||
        }),
 | 
			
		||||
        core_factory::instance().register_creator("tgc_c|mu_p|tcc", [](unsigned port, void*) -> std::tuple<cpu_ptr, vm_ptr>{
 | 
			
		||||
            auto* cpu = new iss::arch::riscv_hart_mu_p<iss::arch::tgc_c>();
 | 
			
		||||
            auto* vm = new tcc::tgc_c::vm_impl<arch::tgc_c>(*cpu, false);
 | 
			
		||||
		    auto vm = new tcc::tgc_c::vm_impl<arch::tgc_c>(*cpu, false);
 | 
			
		||||
		    if (port != 0) debugger::server<debugger::gdb_session>::run_server(vm, port);
 | 
			
		||||
            return {cpu_ptr{cpu}, vm_ptr{vm}};
 | 
			
		||||
        })
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
	bool* get_tgc_c_tcc_creators() {
 | 
			
		||||
		return iss::dummy.data();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user