Compare commits
	
		
			7 Commits
		
	
	
		
			94e46b9968
			...
			e68f9c573f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e68f9c573f | |||
| f38cc7d8b9 | |||
| 7af7e040da | |||
| 6e52af168b | |||
| bd0d15f3a2 | |||
| c78026b720 | |||
| edba497fa1 | 
| @@ -1,10 +1,12 @@ | |||||||
| cmake_minimum_required(VERSION 3.12) | cmake_minimum_required(VERSION 3.12) | ||||||
|  | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) | ||||||
| ############################################################################### | ############################################################################### | ||||||
| # | # | ||||||
| ############################################################################### | ############################################################################### | ||||||
| project(dbt-rise-tgc VERSION 1.0.0) | project(dbt-rise-tgc VERSION 1.0.0) | ||||||
|  |  | ||||||
| include(GNUInstallDirs) | include(GNUInstallDirs) | ||||||
|  | include(flink) | ||||||
|  |  | ||||||
| find_package(elfio QUIET) | find_package(elfio QUIET) | ||||||
| find_package(Boost COMPONENTS coroutine) | find_package(Boost COMPONENTS coroutine) | ||||||
| @@ -40,6 +42,9 @@ set(LIB_SOURCES | |||||||
| if(WITH_TCC) | if(WITH_TCC) | ||||||
| 	list(APPEND LIB_SOURCES src/vm/tcc/vm_tgc_c.cpp) | 	list(APPEND LIB_SOURCES src/vm/tcc/vm_tgc_c.cpp) | ||||||
| endif() | endif() | ||||||
|  | if(WITH_LLVM) | ||||||
|  | 	list(APPEND LIB_SOURCES src/vm/llvm/vm_tgc_c.cpp src/vm/llvm/fp_impl.cpp) | ||||||
|  | endif() | ||||||
|  |  | ||||||
| # library files | # library files | ||||||
| FILE(GLOB GEN_ISS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp) | FILE(GLOB GEN_ISS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src-gen/iss/arch/*.cpp) | ||||||
| @@ -67,10 +72,10 @@ if(TARGET RapidJSON OR TARGET RapidJSON::RapidJSON) | |||||||
| endif() | endif() | ||||||
|  |  | ||||||
| # Define the library | # Define the library | ||||||
| add_library(${PROJECT_NAME} ${LIB_SOURCES}) | add_library(${PROJECT_NAME} SHARED ${LIB_SOURCES}) | ||||||
|  |  | ||||||
| if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") | if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") | ||||||
|      target_compile_options(${PROJECT_NAME} PRIVATE -Wno-shift-count-overflow) |     target_compile_options(${PROJECT_NAME} PRIVATE -Wno-shift-count-overflow) | ||||||
| elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") | elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") | ||||||
|     target_compile_options(${PROJECT_NAME} PRIVATE /wd4293) |     target_compile_options(${PROJECT_NAME} PRIVATE /wd4293) | ||||||
| endif() | endif() | ||||||
| @@ -82,11 +87,9 @@ if(TARGET jsoncpp::jsoncpp) | |||||||
| else() | else() | ||||||
| 	target_link_libraries(${PROJECT_NAME} PUBLIC jsoncpp) | 	target_link_libraries(${PROJECT_NAME} PUBLIC jsoncpp) | ||||||
| endif() | endif() | ||||||
| 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) | target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-core) | ||||||
| else() |  | ||||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-core) |  | ||||||
| endif() |  | ||||||
| if(TARGET elfio::elfio) | if(TARGET elfio::elfio) | ||||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC elfio::elfio) |     target_link_libraries(${PROJECT_NAME} PUBLIC elfio::elfio) | ||||||
| else() | else() | ||||||
| @@ -101,6 +104,13 @@ if(TARGET RapidJSON::RapidJSON) | |||||||
| elseif(TARGET RapidJSON) | elseif(TARGET RapidJSON) | ||||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC RapidJSON) |     target_link_libraries(${PROJECT_NAME} PUBLIC RapidJSON) | ||||||
| endif() | endif() | ||||||
|  | if(WITH_LLVM) | ||||||
|  | 	target_compile_definitions(${PROJECT_NAME} PUBLIC ${LLVM_DEFINITIONS}) | ||||||
|  | 	target_include_directories(${PROJECT_NAME} PUBLIC ${LLVM_INCLUDE_DIRS}) | ||||||
|  | 	if(BUILD_SHARED_LIBS) | ||||||
|  | 		target_link_libraries( ${PROJECT_NAME} PUBLIC ${LLVM_LIBRARIES}) | ||||||
|  | 	endif() | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  |  | ||||||
| set_target_properties(${PROJECT_NAME} PROPERTIES | set_target_properties(${PROJECT_NAME} PROPERTIES | ||||||
| @@ -154,7 +164,7 @@ if(WITH_TCC) | |||||||
|     target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_TCC) |     target_compile_definitions(${PROJECT_NAME} PRIVATE WITH_TCC) | ||||||
| endif() | endif() | ||||||
| # Links the target exe against the libraries | # Links the target exe against the libraries | ||||||
| target_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc) | target_force_link_libraries(${PROJECT_NAME} PUBLIC dbt-rise-tgc) | ||||||
| if(TARGET Boost::program_options) | if(TARGET Boost::program_options) | ||||||
|     target_link_libraries(${PROJECT_NAME} PUBLIC Boost::program_options) |     target_link_libraries(${PROJECT_NAME} PUBLIC Boost::program_options) | ||||||
| else() | else() | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								cmake/flink.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								cmake/flink.cmake
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | # 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() | ||||||
| @@ -158,30 +158,81 @@ private: | |||||||
|     /**************************************************************************** |     /**************************************************************************** | ||||||
|      * start opcode definitions |      * start opcode definitions | ||||||
|      ****************************************************************************/ |      ****************************************************************************/ | ||||||
|     struct InstructionDesriptor { |     struct instruction_descriptor { | ||||||
|         size_t length; |         size_t length; | ||||||
|         uint32_t value; |         uint32_t value; | ||||||
|         uint32_t mask; |         uint32_t mask; | ||||||
|         typename arch::traits<ARCH>::opcode_e op; |         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){} | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{ |     decoding_tree_node* root {nullptr}; | ||||||
|  |     const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{ | ||||||
|          /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %> |          /* 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}},<%}%> |         {${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){ |     iss::status fetch_ins(virt_addr_t pc, uint8_t * data){ | ||||||
|         auto phys_pc = this->core.v2p(pc); |         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; |             if (this->core.read(phys_pc, 4, data) != iss::Ok)  return iss::Err; | ||||||
|         //} |  | ||||||
|         return iss::Ok; |         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) { | template <typename CODE_WORD> void debug_fn(CODE_WORD insn) { | ||||||
| @@ -208,16 +259,11 @@ constexpr size_t bit_count(uint32_t u) { | |||||||
| template <typename ARCH> | template <typename ARCH> | ||||||
| vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | ||||||
| : vm_base<ARCH>(core, core_id, cluster_id) { | : vm_base<ARCH>(core, core_id, cluster_id) { | ||||||
|     unsigned id=0; |     root = new decoding_tree_node(std::numeric_limits<uint32_t>::max()); | ||||||
|     for (auto instr : instr_descr) { |     for(auto instr:instr_descr){ | ||||||
|         auto quadrant = instr.value & 0x3; |         root->instrs.push_back(instr); | ||||||
|         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){ | inline bool is_count_limit_enabled(finish_cond_e cond){ | ||||||
| @@ -228,14 +274,6 @@ 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; |     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> | 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){ | 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; |     auto pc=start; | ||||||
| @@ -257,7 +295,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | |||||||
|         } else { |         } else { | ||||||
|             if (is_jump_to_self_enabled(cond) && |             if (is_jump_to_self_enabled(cond) && | ||||||
|                     (instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0' |                     (instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0' | ||||||
|             auto inst_id = decode_inst_id(instr); |             auto inst_id = decode_instr(root, instr); | ||||||
|             // pre execution stuff |             // pre execution stuff | ||||||
|              this->core.reg.last_branch = 0; |              this->core.reg.last_branch = 0; | ||||||
|             if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id)); |             if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id)); | ||||||
|   | |||||||
| @@ -30,10 +30,9 @@ | |||||||
|  * |  * | ||||||
|  *******************************************************************************/ |  *******************************************************************************/ | ||||||
| 
 | 
 | ||||||
|  | #include <iss/arch/${coreDef.name.toLowerCase()}.h> | ||||||
| #include <iss/debugger/gdb_session.h> | #include <iss/debugger/gdb_session.h> | ||||||
| #include <iss/debugger/server.h> | #include <iss/debugger/server.h> | ||||||
| #include <iss/arch/${coreDef.name.toLowerCase()}.h> |  | ||||||
| #include <iss/arch/riscv_hart_m_p.h> |  | ||||||
| #include <iss/iss.h> | #include <iss/iss.h> | ||||||
| #include <iss/llvm/vm_base.h> | #include <iss/llvm/vm_base.h> | ||||||
| #include <util/logging.h> | #include <util/logging.h> | ||||||
| @@ -111,7 +110,7 @@ protected: | |||||||
|     void gen_trap_check(BasicBlock *bb); |     void gen_trap_check(BasicBlock *bb); | ||||||
| 
 | 
 | ||||||
|     inline Value *gen_reg_load(unsigned i, unsigned level = 0) { |     inline Value *gen_reg_load(unsigned i, unsigned level = 0) { | ||||||
|         return this->builder.CreateLoad(get_reg_ptr(i), false); |         return this->builder.CreateLoad(this->get_typeptr(i), get_reg_ptr(i), false); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     inline void gen_set_pc(virt_addr_t pc, unsigned reg_num) { |     inline void gen_set_pc(virt_addr_t pc, unsigned reg_num) { | ||||||
| @@ -124,7 +123,7 @@ protected: | |||||||
|     // enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 }; |     // enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 }; | ||||||
|     enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 }; |     enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 }; | ||||||
|     enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 }; |     enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 }; | ||||||
|     enum { LUT_SIZE = 1 << util::bit_count(EXTR_MASK32), LUT_SIZE_C = 1 << util::bit_count(EXTR_MASK16) }; |     enum { LUT_SIZE = 1 << util::bit_count(static_cast<uint64_t>(EXTR_MASK32)), LUT_SIZE_C = 1 << util::bit_count(static_cast<uint64_t>(EXTR_MASK16)) }; | ||||||
| 
 | 
 | ||||||
|     using this_class = vm_impl<ARCH>; |     using this_class = vm_impl<ARCH>; | ||||||
|     using compile_func = std::tuple<continuation_e, BasicBlock *> (this_class::*)(virt_addr_t &pc, |     using compile_func = std::tuple<continuation_e, BasicBlock *> (this_class::*)(virt_addr_t &pc, | ||||||
| @@ -204,10 +203,10 @@ private: | |||||||
|      ****************************************************************************/ |      ****************************************************************************/ | ||||||
|     std::tuple<continuation_e, BasicBlock *> illegal_intruction(virt_addr_t &pc, code_word_t instr, BasicBlock *bb) { |     std::tuple<continuation_e, BasicBlock *> illegal_intruction(virt_addr_t &pc, code_word_t instr, BasicBlock *bb) { | ||||||
| 		this->gen_sync(iss::PRE_SYNC, instr_descr.size()); | 		this->gen_sync(iss::PRE_SYNC, instr_descr.size()); | ||||||
|         this->builder.CreateStore(this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::NEXT_PC), true), |         this->builder.CreateStore(this->builder.CreateLoad(this->get_typeptr(traits<ARCH>::NEXT_PC), get_reg_ptr(traits<ARCH>::NEXT_PC), true), | ||||||
|                                    get_reg_ptr(traits<ARCH>::PC), true); |                                    get_reg_ptr(traits<ARCH>::PC), true); | ||||||
|         this->builder.CreateStore( |         this->builder.CreateStore( | ||||||
|             this->builder.CreateAdd(this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::ICOUNT), true), |             this->builder.CreateAdd(this->builder.CreateLoad(this->get_typeptr(traits<ARCH>::ICOUNT), get_reg_ptr(traits<ARCH>::ICOUNT), true), | ||||||
|                                      this->gen_const(64U, 1)), |                                      this->gen_const(64U, 1)), | ||||||
|             get_reg_ptr(traits<ARCH>::ICOUNT), true); |             get_reg_ptr(traits<ARCH>::ICOUNT), true); | ||||||
|         pc = pc + ((instr & 3) == 3 ? 4 : 2); |         pc = pc + ((instr & 3) == 3 ? 4 : 2); | ||||||
| @@ -244,20 +243,21 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, | |||||||
|     // we fetch at max 4 byte, alignment is 2 |     // we fetch at max 4 byte, alignment is 2 | ||||||
|     enum {TRAP_ID=1<<16}; |     enum {TRAP_ID=1<<16}; | ||||||
|     code_word_t insn = 0; |     code_word_t insn = 0; | ||||||
|     const typename traits<ARCH>::addr_t upper_bits = ~traits<ARCH>::PGMASK; |     // const typename traits<ARCH>::addr_t upper_bits = ~traits<ARCH>::PGMASK; | ||||||
|     phys_addr_t paddr(pc); |     phys_addr_t paddr(pc); | ||||||
|     auto *const data = (uint8_t *)&insn; |     auto *const data = (uint8_t *)&insn; | ||||||
|     paddr = this->core.v2p(pc); |     paddr = this->core.v2p(pc); | ||||||
|     if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary |     //TODO: re-add page handling | ||||||
|         auto res = this->core.read(paddr, 2, data); | //    if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary | ||||||
|         if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | //        auto res = this->core.read(paddr, 2, data); | ||||||
|         if ((insn & 0x3) == 0x3) { // this is a 32bit instruction | //        if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | ||||||
|             res = this->core.read(this->core.v2p(pc + 2), 2, data + 2); | //        if ((insn & 0x3) == 0x3) { // this is a 32bit instruction | ||||||
|         } | //            res = this->core.read(this->core.v2p(pc + 2), 2, data + 2); | ||||||
|     } else { | //        } | ||||||
|  | //    } else { | ||||||
|         auto res = this->core.read(paddr, 4, data); |         auto res = this->core.read(paddr, 4, data); | ||||||
|         if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); |         if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | ||||||
|     } | //    } | ||||||
|     if (insn == 0x0000006f || (insn&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 |     // curr pc on stack | ||||||
|     ++inst_cnt; |     ++inst_cnt; | ||||||
| @@ -271,7 +271,7 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, | |||||||
| 
 | 
 | ||||||
| template <typename ARCH> void vm_impl<ARCH>::gen_leave_behavior(BasicBlock *leave_blk) { | template <typename ARCH> void vm_impl<ARCH>::gen_leave_behavior(BasicBlock *leave_blk) { | ||||||
|     this->builder.SetInsertPoint(leave_blk); |     this->builder.SetInsertPoint(leave_blk); | ||||||
|     this->builder.CreateRet(this->builder.CreateLoad(get_reg_ptr(arch::traits<ARCH>::NEXT_PC), false)); |     this->builder.CreateRet(this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::NEXT_PC), get_reg_ptr(arch::traits<ARCH>::NEXT_PC), false)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(uint16_t trap_id, uint16_t cause) { | template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(uint16_t trap_id, uint16_t cause) { | ||||||
| @@ -295,18 +295,18 @@ template <typename ARCH> void vm_impl<ARCH>::gen_wait(unsigned type) { | |||||||
| 
 | 
 | ||||||
| template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(BasicBlock *trap_blk) { | template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(BasicBlock *trap_blk) { | ||||||
|     this->builder.SetInsertPoint(trap_blk); |     this->builder.SetInsertPoint(trap_blk); | ||||||
|     auto *trap_state_val = this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::TRAP_STATE), true); |     auto *trap_state_val = this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::TRAP_STATE), get_reg_ptr(traits<ARCH>::TRAP_STATE), true); | ||||||
|     this->builder.CreateStore(this->gen_const(32U, std::numeric_limits<uint32_t>::max()), |     this->builder.CreateStore(this->gen_const(32U, std::numeric_limits<uint32_t>::max()), | ||||||
|                               get_reg_ptr(traits<ARCH>::LAST_BRANCH), false); |                               get_reg_ptr(traits<ARCH>::LAST_BRANCH), false); | ||||||
|     std::vector<Value *> args{this->core_ptr, this->adj_to64(trap_state_val), |     std::vector<Value *> args{this->core_ptr, this->adj_to64(trap_state_val), | ||||||
|                               this->adj_to64(this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::PC), false))}; |                               this->adj_to64(this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::PC), get_reg_ptr(traits<ARCH>::PC), false))}; | ||||||
|     this->builder.CreateCall(this->mod->getFunction("enter_trap"), args); |     this->builder.CreateCall(this->mod->getFunction("enter_trap"), args); | ||||||
|     auto *trap_addr_val = this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::NEXT_PC), false); |     auto *trap_addr_val = this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::NEXT_PC), get_reg_ptr(traits<ARCH>::NEXT_PC), false); | ||||||
|     this->builder.CreateRet(trap_addr_val); |     this->builder.CreateRet(trap_addr_val); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename ARCH> inline void vm_impl<ARCH>::gen_trap_check(BasicBlock *bb) { | template <typename ARCH> inline void vm_impl<ARCH>::gen_trap_check(BasicBlock *bb) { | ||||||
|     auto *v = this->builder.CreateLoad(get_reg_ptr(arch::traits<ARCH>::TRAP_STATE), true); |     auto *v = this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::TRAP_STATE), get_reg_ptr(arch::traits<ARCH>::TRAP_STATE), true); | ||||||
|     this->gen_cond_branch(this->builder.CreateICmp( |     this->gen_cond_branch(this->builder.CreateICmp( | ||||||
|                               ICmpInst::ICMP_EQ, v, |                               ICmpInst::ICMP_EQ, v, | ||||||
|                               ConstantInt::get(getContext(), APInt(v->getType()->getIntegerBitWidth(), 0))), |                               ConstantInt::get(getContext(), APInt(v->getType()->getIntegerBitWidth(), 0))), | ||||||
| @@ -323,3 +323,25 @@ std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreD | |||||||
| } | } | ||||||
| } // namespace llvm | } // namespace llvm | ||||||
| } // namespace iss | } // namespace iss | ||||||
|  | 
 | ||||||
|  | #include <iss/factory.h> | ||||||
|  | #include <iss/arch/riscv_hart_m_p.h> | ||||||
|  | #include <iss/arch/riscv_hart_mu_p.h> | ||||||
|  | namespace iss { | ||||||
|  | namespace { | ||||||
|  | volatile std::array<bool, 2> dummy = { | ||||||
|  |         core_factory::instance().register_creator("${coreDef.name.toLowerCase()}|m_p|llvm", [](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 llvm::${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("${coreDef.name.toLowerCase()}|mu_p|llvm", [](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 llvm::${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}}; | ||||||
|  |         }) | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | } | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| {  |  | ||||||
| 	"${coreDef.name}" : [<%instructions.eachWithIndex{instr,index -> %>${index==0?"":","} |  | ||||||
| 		{ |  | ||||||
| 			"name"  : "${instr.name}", |  | ||||||
| 			"size"  : ${instr.length}, |  | ||||||
| 			"delay" : ${generator.hasAttribute(instr.instruction, com.minres.coredsl.coreDsl.InstrAttribute.COND)?[1,1]:1} |  | ||||||
| 		}<%}%> |  | ||||||
| 	] |  | ||||||
| } |  | ||||||
| @@ -1,223 +0,0 @@ | |||||||
| /******************************************************************************* |  | ||||||
|  * Copyright (C) 2017, 2018 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. |  | ||||||
|  * |  | ||||||
|  *******************************************************************************/ |  | ||||||
|  |  | ||||||
| <%  |  | ||||||
| import com.minres.coredsl.coreDsl.Register |  | ||||||
| import com.minres.coredsl.coreDsl.RegisterFile |  | ||||||
| import com.minres.coredsl.coreDsl.RegisterAlias |  | ||||||
| def getTypeSize(size){ |  | ||||||
| 	if(size > 32) 64 else if(size > 16) 32 else if(size > 8) 16 else 8 |  | ||||||
| } |  | ||||||
| def getOriginalName(reg){ |  | ||||||
|     if( reg.original instanceof RegisterFile) { |  | ||||||
|     	if( reg.index != null ) { |  | ||||||
|         	return reg.original.name+generator.generateHostCode(reg.index) |  | ||||||
|         } else { |  | ||||||
|         	return reg.original.name |  | ||||||
|         } |  | ||||||
|     } else if(reg.original instanceof Register){ |  | ||||||
|         return reg.original.name |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| def getRegisterNames(){ |  | ||||||
| 	def regNames = [] |  | ||||||
|  	allRegs.each { reg ->  |  | ||||||
| 		if( reg instanceof RegisterFile) { |  | ||||||
| 			(reg.range.right..reg.range.left).each{ |  | ||||||
|     			regNames+=reg.name.toLowerCase()+it |  | ||||||
|             } |  | ||||||
|         } else if(reg instanceof Register){ |  | ||||||
|     		regNames+=reg.name.toLowerCase() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return regNames |  | ||||||
| } |  | ||||||
| def getRegisterAliasNames(){ |  | ||||||
| 	def regMap = allRegs.findAll{it instanceof RegisterAlias }.collectEntries {[getOriginalName(it), it.name]} |  | ||||||
|  	return allRegs.findAll{it instanceof Register || it instanceof RegisterFile}.collect{reg -> |  | ||||||
| 		if( reg instanceof RegisterFile) { |  | ||||||
| 			return (reg.range.right..reg.range.left).collect{ (regMap[reg.name]?:regMap[reg.name+it]?:reg.name.toLowerCase()+it).toLowerCase() } |  | ||||||
|         } else if(reg instanceof Register){ |  | ||||||
|     		regMap[reg.name]?:reg.name.toLowerCase() |  | ||||||
|         } |  | ||||||
|  	}.flatten() |  | ||||||
| } |  | ||||||
| %> |  | ||||||
| #ifndef _${coreDef.name.toUpperCase()}_H_ |  | ||||||
| #define _${coreDef.name.toUpperCase()}_H_ |  | ||||||
|  |  | ||||||
| #include <array> |  | ||||||
| #include <iss/arch/traits.h> |  | ||||||
| #include <iss/arch_if.h> |  | ||||||
| #include <iss/vm_if.h> |  | ||||||
|  |  | ||||||
| namespace iss { |  | ||||||
| namespace arch { |  | ||||||
|  |  | ||||||
| struct ${coreDef.name.toLowerCase()}; |  | ||||||
|  |  | ||||||
| template <> struct traits<${coreDef.name.toLowerCase()}> { |  | ||||||
|  |  | ||||||
| 	constexpr static char const* const core_type = "${coreDef.name}"; |  | ||||||
|      |  | ||||||
|   	static constexpr std::array<const char*, ${getRegisterNames().size}> reg_names{ |  | ||||||
|  		{"${getRegisterNames().join("\", \"")}"}}; |  | ||||||
|   |  | ||||||
|   	static constexpr std::array<const char*, ${getRegisterAliasNames().size}> reg_aliases{ |  | ||||||
|  		{"${getRegisterAliasNames().join("\", \"")}"}}; |  | ||||||
|  |  | ||||||
|     enum constants {${coreDef.constants.collect{c -> c.name+"="+c.value}.join(', ')}}; |  | ||||||
|  |  | ||||||
|     constexpr static unsigned FP_REGS_SIZE = ${coreDef.constants.find {it.name=='FLEN'}?.value?:0}; |  | ||||||
|  |  | ||||||
|     enum reg_e {<% |  | ||||||
|      	allRegs.each { reg ->  |  | ||||||
|     		if( reg instanceof RegisterFile) { |  | ||||||
|     			(reg.range.right..reg.range.left).each{%> |  | ||||||
|         ${reg.name}${it},<% |  | ||||||
|                 } |  | ||||||
|             } else if(reg instanceof Register){ %> |  | ||||||
|         ${reg.name},<%   |  | ||||||
|             } |  | ||||||
|         }%> |  | ||||||
|         NUM_REGS, |  | ||||||
|         NEXT_${pc.name}=NUM_REGS, |  | ||||||
|         TRAP_STATE, |  | ||||||
|         PENDING_TRAP, |  | ||||||
|         MACHINE_STATE, |  | ||||||
|         LAST_BRANCH, |  | ||||||
|         ICOUNT<%  |  | ||||||
|      	allRegs.each { reg ->  |  | ||||||
|     		if(reg instanceof RegisterAlias){ def aliasname=getOriginalName(reg)%>, |  | ||||||
|         ${reg.name} = ${aliasname}<% |  | ||||||
|             } |  | ||||||
|         }%> |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     using reg_t = uint${regDataWidth}_t; |  | ||||||
|  |  | ||||||
|     using addr_t = uint${addrDataWidth}_t; |  | ||||||
|  |  | ||||||
|     using code_word_t = uint${addrDataWidth}_t; //TODO: check removal |  | ||||||
|  |  | ||||||
|     using virt_addr_t = iss::typed_addr_t<iss::address_type::VIRTUAL>; |  | ||||||
|  |  | ||||||
|     using phys_addr_t = iss::typed_addr_t<iss::address_type::PHYSICAL>; |  | ||||||
|  |  | ||||||
|  	static constexpr std::array<const uint32_t, ${regSizes.size}> reg_bit_widths{ |  | ||||||
|  		{${regSizes.join(",")}}}; |  | ||||||
|  |  | ||||||
|     static constexpr std::array<const uint32_t, ${regOffsets.size}> reg_byte_offsets{ |  | ||||||
|     	{${regOffsets.join(",")}}}; |  | ||||||
|  |  | ||||||
|     static const uint64_t addr_mask = (reg_t(1) << (XLEN - 1)) | ((reg_t(1) << (XLEN - 1)) - 1); |  | ||||||
|  |  | ||||||
|     enum sreg_flag_e { FLAGS }; |  | ||||||
|  |  | ||||||
|     enum mem_type_e { ${allSpaces.collect{s -> s.name}.join(', ')} }; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| struct ${coreDef.name.toLowerCase()}: public arch_if { |  | ||||||
|  |  | ||||||
|     using virt_addr_t = typename traits<${coreDef.name.toLowerCase()}>::virt_addr_t; |  | ||||||
|     using phys_addr_t = typename traits<${coreDef.name.toLowerCase()}>::phys_addr_t; |  | ||||||
|     using reg_t =  typename traits<${coreDef.name.toLowerCase()}>::reg_t; |  | ||||||
|     using addr_t = typename traits<${coreDef.name.toLowerCase()}>::addr_t; |  | ||||||
|  |  | ||||||
|     ${coreDef.name.toLowerCase()}(); |  | ||||||
|     ~${coreDef.name.toLowerCase()}(); |  | ||||||
|  |  | ||||||
|     void reset(uint64_t address=0) override; |  | ||||||
|  |  | ||||||
|     uint8_t* get_regs_base_ptr() override; |  | ||||||
|     /// deprecated |  | ||||||
|     void get_reg(short idx, std::vector<uint8_t>& value) override {} |  | ||||||
|     void set_reg(short idx, const std::vector<uint8_t>& value) override {} |  | ||||||
|     /// deprecated |  | ||||||
|     bool get_flag(int flag) override {return false;} |  | ||||||
|     void set_flag(int, bool value) override {}; |  | ||||||
|     /// deprecated |  | ||||||
|     void update_flags(operations op, uint64_t opr1, uint64_t opr2) override {}; |  | ||||||
|  |  | ||||||
|     inline uint64_t get_icount() { return reg.icount; } |  | ||||||
|  |  | ||||||
|     inline bool should_stop() { return interrupt_sim; } |  | ||||||
|  |  | ||||||
|     inline uint64_t stop_code() { return interrupt_sim; } |  | ||||||
|  |  | ||||||
|     inline phys_addr_t v2p(const iss::addr_t& addr){ |  | ||||||
|         if (addr.space != traits<${coreDef.name.toLowerCase()}>::MEM || addr.type == iss::address_type::PHYSICAL || |  | ||||||
|                 addr_mode[static_cast<uint16_t>(addr.access)&0x3]==address_type::PHYSICAL) { |  | ||||||
|             return phys_addr_t(addr.access, addr.space, addr.val&traits<${coreDef.name.toLowerCase()}>::addr_mask); |  | ||||||
|         } else |  | ||||||
|             return virt2phys(addr); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     virtual phys_addr_t virt2phys(const iss::addr_t& addr); |  | ||||||
|  |  | ||||||
|     virtual iss::sync_type needed_sync() const { return iss::NO_SYNC; } |  | ||||||
|  |  | ||||||
|     inline uint32_t get_last_branch() { return reg.last_branch; } |  | ||||||
|  |  | ||||||
| protected: |  | ||||||
|     struct ${coreDef.name}_regs {<% |  | ||||||
|      	allRegs.each { reg ->  |  | ||||||
|     		if( reg instanceof RegisterFile) { |  | ||||||
|     			(reg.range.right..reg.range.left).each{%> |  | ||||||
|         uint${generator.getSize(reg)}_t ${reg.name}${it} = 0;<% |  | ||||||
|                 } |  | ||||||
|             } else if(reg instanceof Register){ %> |  | ||||||
|         uint${generator.getSize(reg)}_t ${reg.name} = 0;<% |  | ||||||
|             } |  | ||||||
|         }%> |  | ||||||
|         uint${generator.getSize(pc)}_t NEXT_${pc.name} = 0; |  | ||||||
|         uint32_t trap_state = 0, pending_trap = 0, machine_state = 0, last_branch = 0; |  | ||||||
|         uint64_t icount = 0; |  | ||||||
|     } reg; |  | ||||||
|  |  | ||||||
|     std::array<address_type, 4> addr_mode; |  | ||||||
|      |  | ||||||
|     uint64_t interrupt_sim=0; |  | ||||||
| <% |  | ||||||
| def fcsr = allRegs.find {it.name=='FCSR'} |  | ||||||
| if(fcsr != null) {%> |  | ||||||
| 	uint${generator.getSize(fcsr)}_t get_fcsr(){return reg.FCSR;} |  | ||||||
| 	void set_fcsr(uint${generator.getSize(fcsr)}_t val){reg.FCSR = val;}		 |  | ||||||
| <%} else { %> |  | ||||||
| 	uint32_t get_fcsr(){return 0;} |  | ||||||
| 	void set_fcsr(uint32_t val){} |  | ||||||
| <%}%> |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| } |  | ||||||
| }             |  | ||||||
| #endif /* _${coreDef.name.toUpperCase()}_H_ */ |  | ||||||
| @@ -1,107 +0,0 @@ | |||||||
| /******************************************************************************* |  | ||||||
|  * Copyright (C) 2017, 2018 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. |  | ||||||
|  * |  | ||||||
|  *******************************************************************************/ |  | ||||||
|  <%  |  | ||||||
| import com.minres.coredsl.coreDsl.Register |  | ||||||
| import com.minres.coredsl.coreDsl.RegisterFile |  | ||||||
| import com.minres.coredsl.coreDsl.RegisterAlias |  | ||||||
| def getOriginalName(reg){ |  | ||||||
|     if( reg.original instanceof RegisterFile) { |  | ||||||
|     	if( reg.index != null ) { |  | ||||||
|         	return reg.original.name+generator.generateHostCode(reg.index) |  | ||||||
|         } else { |  | ||||||
|         	return reg.original.name |  | ||||||
|         } |  | ||||||
|     } else if(reg.original instanceof Register){ |  | ||||||
|         return reg.original.name |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| def getRegisterNames(){ |  | ||||||
| 	def regNames = [] |  | ||||||
|  	allRegs.each { reg ->  |  | ||||||
| 		if( reg instanceof RegisterFile) { |  | ||||||
| 			(reg.range.right..reg.range.left).each{ |  | ||||||
|     			regNames+=reg.name.toLowerCase()+it |  | ||||||
|             } |  | ||||||
|         } else if(reg instanceof Register){ |  | ||||||
|     		regNames+=reg.name.toLowerCase() |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return regNames |  | ||||||
| } |  | ||||||
| def getRegisterAliasNames(){ |  | ||||||
| 	def regMap = allRegs.findAll{it instanceof RegisterAlias }.collectEntries {[getOriginalName(it), it.name]} |  | ||||||
|  	return allRegs.findAll{it instanceof Register || it instanceof RegisterFile}.collect{reg -> |  | ||||||
| 		if( reg instanceof RegisterFile) { |  | ||||||
| 			return (reg.range.right..reg.range.left).collect{ (regMap[reg.name]?:regMap[reg.name+it]?:reg.name.toLowerCase()+it).toLowerCase() } |  | ||||||
|         } else if(reg instanceof Register){ |  | ||||||
|     		regMap[reg.name]?:reg.name.toLowerCase() |  | ||||||
|         } |  | ||||||
|  	}.flatten() |  | ||||||
| } |  | ||||||
| %> |  | ||||||
| #include "util/ities.h" |  | ||||||
| #include <util/logging.h> |  | ||||||
| #include <iss/arch/${coreDef.name.toLowerCase()}.h> |  | ||||||
| #include <cstdio> |  | ||||||
| #include <cstring> |  | ||||||
| #include <fstream> |  | ||||||
|  |  | ||||||
| using namespace iss::arch; |  | ||||||
|  |  | ||||||
| constexpr std::array<const char*, ${getRegisterNames().size}>    iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_names; |  | ||||||
| constexpr std::array<const char*, ${getRegisterAliasNames().size}>    iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_aliases; |  | ||||||
| constexpr std::array<const uint32_t, ${regSizes.size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_bit_widths; |  | ||||||
| constexpr std::array<const uint32_t, ${regOffsets.size}> iss::arch::traits<iss::arch::${coreDef.name.toLowerCase()}>::reg_byte_offsets; |  | ||||||
|  |  | ||||||
| ${coreDef.name.toLowerCase()}::${coreDef.name.toLowerCase()}() { |  | ||||||
|     reg.icount = 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ${coreDef.name.toLowerCase()}::~${coreDef.name.toLowerCase()}() = default; |  | ||||||
|  |  | ||||||
| void ${coreDef.name.toLowerCase()}::reset(uint64_t address) { |  | ||||||
|     for(size_t i=0; i<traits<${coreDef.name.toLowerCase()}>::NUM_REGS; ++i) set_reg(i, std::vector<uint8_t>(sizeof(traits<${coreDef.name.toLowerCase()}>::reg_t),0)); |  | ||||||
|     reg.PC=address; |  | ||||||
|     reg.NEXT_PC=reg.PC; |  | ||||||
|     reg.trap_state=0; |  | ||||||
|     reg.machine_state=0x3; |  | ||||||
|     reg.icount=0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uint8_t *${coreDef.name.toLowerCase()}::get_regs_base_ptr() { |  | ||||||
| 	return reinterpret_cast<uint8_t*>(®); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ${coreDef.name.toLowerCase()}::phys_addr_t ${coreDef.name.toLowerCase()}::virt2phys(const iss::addr_t &pc) { |  | ||||||
|     return phys_addr_t(pc); // change logical address to physical address |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @@ -120,57 +120,7 @@ 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> |     template<unsigned W, typename U, typename S = typename std::make_signed<U>::type> | ||||||
|     inline S sext(U from) { |     inline S sext(U from) { | ||||||
|         auto mask = (1ULL<<W) - 1; |         auto mask = (1ULL<<W) - 1; | ||||||
| @@ -182,14 +132,23 @@ private: | |||||||
|     /**************************************************************************** |     /**************************************************************************** | ||||||
|      * start opcode definitions |      * start opcode definitions | ||||||
|      ****************************************************************************/ |      ****************************************************************************/ | ||||||
|     struct InstructionDesriptor { |     struct instruction_descriptor { | ||||||
|         size_t length; |         size_t length; | ||||||
|         uint32_t value; |         uint32_t value; | ||||||
|         uint32_t mask; |         uint32_t mask; | ||||||
|         compile_func op; |         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){} | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{ |     decoding_tree_node* root {nullptr}; | ||||||
|  |  | ||||||
|  |     const std::array<instruction_descriptor, ${instructions.size}> instr_descr = {{ | ||||||
|          /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %> |          /* entries are: size, valid value, valid mask, function ptr */<%instructions.each{instr -> %> | ||||||
|         /* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */ |         /* instruction ${instr.instruction.name}, encoding '${instr.encoding}' */ | ||||||
|         {${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%> |         {${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%> | ||||||
| @@ -227,11 +186,64 @@ private: | |||||||
|         vm_impl::gen_trap_check(tu); |         vm_impl::gen_trap_check(tu); | ||||||
|         return BRANCH; |         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 insn) { | template <typename CODE_WORD> void debug_fn(CODE_WORD instr) { | ||||||
|     volatile CODE_WORD x = insn; |     volatile CODE_WORD x = instr; | ||||||
|     insn = 2 * x; |     instr = 2 * x; | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); } | template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); } | ||||||
| @@ -239,14 +251,11 @@ template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); } | |||||||
| template <typename ARCH> | template <typename ARCH> | ||||||
| vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | ||||||
| : vm_base<ARCH>(core, core_id, cluster_id) { | : vm_base<ARCH>(core, core_id, cluster_id) { | ||||||
|     qlut[0] = lut_00.data(); |     root = new decoding_tree_node(std::numeric_limits<uint32_t>::max()); | ||||||
|     qlut[1] = lut_01.data(); |     for(auto instr:instr_descr){ | ||||||
|     qlut[2] = lut_10.data(); |         root->instrs.push_back(instr); | ||||||
|     qlut[3] = lut_11.data(); |  | ||||||
|     for (auto instr : instr_descr) { |  | ||||||
|         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> | template <typename ARCH> | ||||||
| @@ -254,30 +263,19 @@ std::tuple<continuation_e> | |||||||
| vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, tu_builder& tu) { | 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 |     // we fetch at max 4 byte, alignment is 2 | ||||||
|     enum {TRAP_ID=1<<16}; |     enum {TRAP_ID=1<<16}; | ||||||
|     code_word_t insn = 0; |     code_word_t instr = 0; | ||||||
|     // const typename traits::addr_t upper_bits = ~traits::PGMASK; |  | ||||||
|     phys_addr_t paddr(pc); |     phys_addr_t paddr(pc); | ||||||
|     auto *const data = (uint8_t *)&insn; |  | ||||||
|     paddr = this->core.v2p(pc); |     paddr = this->core.v2p(pc); | ||||||
| //    if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary |     auto res = this->core.read(paddr, 4, reinterpret_cast<uint8_t*>(&instr)); | ||||||
| //        auto res = this->core.read(paddr, 2, data); |     if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | ||||||
| //        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 & 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 (insn == 0x0000006f || (insn&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0' |  | ||||||
|     // curr pc on stack |     // curr pc on stack | ||||||
|     ++inst_cnt; |     ++inst_cnt; | ||||||
|     auto lut_val = extract_fields(insn); |     auto f = decode_instr(root, instr); | ||||||
|     auto f = qlut[insn & 0x3][lut_val]; |  | ||||||
|     if (f == nullptr) { |     if (f == nullptr) { | ||||||
|         f = &this_class::illegal_intruction; |         f = &this_class::illegal_intruction; | ||||||
|     } |     } | ||||||
|     return (this->*f)(pc, insn, tu); |     return (this->*f)(pc, instr, tu); | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(tu_builder& tu, uint16_t trap_id, uint16_t cause) { | template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(tu_builder& tu, uint16_t trap_id, uint16_t cause) { | ||||||
|   | |||||||
| @@ -152,14 +152,22 @@ private: | |||||||
|     /**************************************************************************** |     /**************************************************************************** | ||||||
|      * start opcode definitions |      * start opcode definitions | ||||||
|      ****************************************************************************/ |      ****************************************************************************/ | ||||||
|     struct InstructionDesriptor { |     struct instruction_descriptor { | ||||||
|         size_t length; |         size_t length; | ||||||
|         uint32_t value; |         uint32_t value; | ||||||
|         uint32_t mask; |         uint32_t mask; | ||||||
|         typename arch::traits<ARCH>::opcode_e op; |         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){} | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     const std::array<InstructionDesriptor, 87> instr_descr = {{ |     decoding_tree_node* root {nullptr}; | ||||||
|  |     const std::array<instruction_descriptor, 87> instr_descr = {{ | ||||||
|          /* entries are: size, valid value, valid mask, function ptr */ |          /* entries are: size, valid value, valid mask, function ptr */ | ||||||
|         {32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, arch::traits<ARCH>::opcode_e::LUI}, |         {32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, arch::traits<ARCH>::opcode_e::LUI}, | ||||||
|         {32, 0b00000000000000000000000000010111, 0b00000000000000000000000001111111, arch::traits<ARCH>::opcode_e::AUIPC}, |         {32, 0b00000000000000000000000000010111, 0b00000000000000000000000001111111, arch::traits<ARCH>::opcode_e::AUIPC}, | ||||||
| @@ -250,9 +258,9 @@ private: | |||||||
|         {16, 0b0000000000000000, 0b1111111111111111, arch::traits<ARCH>::opcode_e::DII}, |         {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){ |     iss::status fetch_ins(virt_addr_t pc, uint8_t * data){ | ||||||
|         auto phys_pc = this->core.v2p(pc); |         auto phys_pc = this->core.v2p(pc); | ||||||
|  |         //TODO: re-add page handling | ||||||
|         //if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary |         //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 (this->core.read(phys_pc, 2, data) != iss::Ok) return iss::Err; | ||||||
|         //    if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction |         //    if ((data[0] & 0x3) == 0x3) // this is a 32bit instruction | ||||||
| @@ -262,6 +270,56 @@ private: | |||||||
|         //} |         //} | ||||||
|         return iss::Ok; |         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) { | template <typename CODE_WORD> void debug_fn(CODE_WORD insn) { | ||||||
| @@ -288,16 +346,11 @@ constexpr size_t bit_count(uint32_t u) { | |||||||
| template <typename ARCH> | template <typename ARCH> | ||||||
| vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | ||||||
| : vm_base<ARCH>(core, core_id, cluster_id) { | : vm_base<ARCH>(core, core_id, cluster_id) { | ||||||
|     unsigned id=0; |     root = new decoding_tree_node(std::numeric_limits<uint32_t>::max()); | ||||||
|     for (auto instr : instr_descr) { |     for(auto instr:instr_descr){ | ||||||
|         auto quadrant = instr.value & 0x3; |         root->instrs.push_back(instr); | ||||||
|         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){ | inline bool is_count_limit_enabled(finish_cond_e cond){ | ||||||
| @@ -308,14 +361,6 @@ 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; |     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> | 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){ | 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; |     auto pc=start; | ||||||
| @@ -337,7 +382,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co | |||||||
|         } else { |         } else { | ||||||
|             if (is_jump_to_self_enabled(cond) && |             if (is_jump_to_self_enabled(cond) && | ||||||
|                     (instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0' |                     (instr == 0x0000006f || (instr&0xffff)==0xa001)) throw simulation_stopped(0); // 'J 0' or 'C.J 0' | ||||||
|             auto inst_id = decode_inst_id(instr); |             auto inst_id = decode_instr(root, instr); | ||||||
|             // pre execution stuff |             // pre execution stuff | ||||||
|              this->core.reg.last_branch = 0; |              this->core.reg.last_branch = 0; | ||||||
|             if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id)); |             if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id)); | ||||||
|   | |||||||
| @@ -111,7 +111,7 @@ protected: | |||||||
|     void gen_trap_check(BasicBlock *bb); |     void gen_trap_check(BasicBlock *bb); | ||||||
|  |  | ||||||
|     inline Value *gen_reg_load(unsigned i, unsigned level = 0) { |     inline Value *gen_reg_load(unsigned i, unsigned level = 0) { | ||||||
|         return this->builder.CreateLoad(get_reg_ptr(i), false); |         return this->builder.CreateLoad(this->get_typeptr(i), get_reg_ptr(i), false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     inline void gen_set_pc(virt_addr_t pc, unsigned reg_num) { |     inline void gen_set_pc(virt_addr_t pc, unsigned reg_num) { | ||||||
| @@ -124,7 +124,7 @@ protected: | |||||||
|     // enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 }; |     // enum { MASK16 = 0b1111110001100011, MASK32 = 0b11111111111100000111000001111111 }; | ||||||
|     enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 }; |     enum { MASK16 = 0b1111111111111111, MASK32 = 0b11111111111100000111000001111111 }; | ||||||
|     enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 }; |     enum { EXTR_MASK16 = MASK16 >> 2, EXTR_MASK32 = MASK32 >> 2 }; | ||||||
|     enum { LUT_SIZE = 1 << util::bit_count(EXTR_MASK32), LUT_SIZE_C = 1 << util::bit_count(EXTR_MASK16) }; |     enum { LUT_SIZE = 1 << util::bit_count(static_cast<uint64_t>(EXTR_MASK32)), LUT_SIZE_C = 1 << util::bit_count(static_cast<uint64_t>(EXTR_MASK16)) }; | ||||||
|  |  | ||||||
|     using this_class = vm_impl<ARCH>; |     using this_class = vm_impl<ARCH>; | ||||||
|     using compile_func = std::tuple<continuation_e, BasicBlock *> (this_class::*)(virt_addr_t &pc, |     using compile_func = std::tuple<continuation_e, BasicBlock *> (this_class::*)(virt_addr_t &pc, | ||||||
| @@ -4042,10 +4042,10 @@ private: | |||||||
|      ****************************************************************************/ |      ****************************************************************************/ | ||||||
|     std::tuple<continuation_e, BasicBlock *> illegal_intruction(virt_addr_t &pc, code_word_t instr, BasicBlock *bb) { |     std::tuple<continuation_e, BasicBlock *> illegal_intruction(virt_addr_t &pc, code_word_t instr, BasicBlock *bb) { | ||||||
| 		this->gen_sync(iss::PRE_SYNC, instr_descr.size()); | 		this->gen_sync(iss::PRE_SYNC, instr_descr.size()); | ||||||
|         this->builder.CreateStore(this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::NEXT_PC), true), |         this->builder.CreateStore(this->builder.CreateLoad(this->get_typeptr(traits<ARCH>::NEXT_PC), get_reg_ptr(traits<ARCH>::NEXT_PC), true), | ||||||
|                                    get_reg_ptr(traits<ARCH>::PC), true); |                                    get_reg_ptr(traits<ARCH>::PC), true); | ||||||
|         this->builder.CreateStore( |         this->builder.CreateStore( | ||||||
|             this->builder.CreateAdd(this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::ICOUNT), true), |             this->builder.CreateAdd(this->builder.CreateLoad(this->get_typeptr(traits<ARCH>::ICOUNT), get_reg_ptr(traits<ARCH>::ICOUNT), true), | ||||||
|                                      this->gen_const(64U, 1)), |                                      this->gen_const(64U, 1)), | ||||||
|             get_reg_ptr(traits<ARCH>::ICOUNT), true); |             get_reg_ptr(traits<ARCH>::ICOUNT), true); | ||||||
|         pc = pc + ((instr & 3) == 3 ? 4 : 2); |         pc = pc + ((instr & 3) == 3 ? 4 : 2); | ||||||
| @@ -4082,20 +4082,21 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, | |||||||
|     // we fetch at max 4 byte, alignment is 2 |     // we fetch at max 4 byte, alignment is 2 | ||||||
|     enum {TRAP_ID=1<<16}; |     enum {TRAP_ID=1<<16}; | ||||||
|     code_word_t insn = 0; |     code_word_t insn = 0; | ||||||
|     const typename traits<ARCH>::addr_t upper_bits = ~traits<ARCH>::PGMASK; |     // const typename traits<ARCH>::addr_t upper_bits = ~traits<ARCH>::PGMASK; | ||||||
|     phys_addr_t paddr(pc); |     phys_addr_t paddr(pc); | ||||||
|     auto *const data = (uint8_t *)&insn; |     auto *const data = (uint8_t *)&insn; | ||||||
|     paddr = this->core.v2p(pc); |     paddr = this->core.v2p(pc); | ||||||
|     if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary |     //TODO: re-add page handling | ||||||
|         auto res = this->core.read(paddr, 2, data); | //    if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary | ||||||
|         if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | //        auto res = this->core.read(paddr, 2, data); | ||||||
|         if ((insn & 0x3) == 0x3) { // this is a 32bit instruction | //        if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | ||||||
|             res = this->core.read(this->core.v2p(pc + 2), 2, data + 2); | //        if ((insn & 0x3) == 0x3) { // this is a 32bit instruction | ||||||
|         } | //            res = this->core.read(this->core.v2p(pc + 2), 2, data + 2); | ||||||
|     } else { | //        } | ||||||
|  | //    } else { | ||||||
|         auto res = this->core.read(paddr, 4, data); |         auto res = this->core.read(paddr, 4, data); | ||||||
|         if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); |         if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | ||||||
|     } | //    } | ||||||
|     if (insn == 0x0000006f || (insn&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 |     // curr pc on stack | ||||||
|     ++inst_cnt; |     ++inst_cnt; | ||||||
| @@ -4109,7 +4110,7 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, | |||||||
|  |  | ||||||
| template <typename ARCH> void vm_impl<ARCH>::gen_leave_behavior(BasicBlock *leave_blk) { | template <typename ARCH> void vm_impl<ARCH>::gen_leave_behavior(BasicBlock *leave_blk) { | ||||||
|     this->builder.SetInsertPoint(leave_blk); |     this->builder.SetInsertPoint(leave_blk); | ||||||
|     this->builder.CreateRet(this->builder.CreateLoad(get_reg_ptr(arch::traits<ARCH>::NEXT_PC), false)); |     this->builder.CreateRet(this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::NEXT_PC), get_reg_ptr(arch::traits<ARCH>::NEXT_PC), false)); | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(uint16_t trap_id, uint16_t cause) { | template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(uint16_t trap_id, uint16_t cause) { | ||||||
| @@ -4133,18 +4134,18 @@ template <typename ARCH> void vm_impl<ARCH>::gen_wait(unsigned type) { | |||||||
|  |  | ||||||
| template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(BasicBlock *trap_blk) { | template <typename ARCH> void vm_impl<ARCH>::gen_trap_behavior(BasicBlock *trap_blk) { | ||||||
|     this->builder.SetInsertPoint(trap_blk); |     this->builder.SetInsertPoint(trap_blk); | ||||||
|     auto *trap_state_val = this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::TRAP_STATE), true); |     auto *trap_state_val = this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::TRAP_STATE), get_reg_ptr(traits<ARCH>::TRAP_STATE), true); | ||||||
|     this->builder.CreateStore(this->gen_const(32U, std::numeric_limits<uint32_t>::max()), |     this->builder.CreateStore(this->gen_const(32U, std::numeric_limits<uint32_t>::max()), | ||||||
|                               get_reg_ptr(traits<ARCH>::LAST_BRANCH), false); |                               get_reg_ptr(traits<ARCH>::LAST_BRANCH), false); | ||||||
|     std::vector<Value *> args{this->core_ptr, this->adj_to64(trap_state_val), |     std::vector<Value *> args{this->core_ptr, this->adj_to64(trap_state_val), | ||||||
|                               this->adj_to64(this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::PC), false))}; |                               this->adj_to64(this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::PC), get_reg_ptr(traits<ARCH>::PC), false))}; | ||||||
|     this->builder.CreateCall(this->mod->getFunction("enter_trap"), args); |     this->builder.CreateCall(this->mod->getFunction("enter_trap"), args); | ||||||
|     auto *trap_addr_val = this->builder.CreateLoad(get_reg_ptr(traits<ARCH>::NEXT_PC), false); |     auto *trap_addr_val = this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::NEXT_PC), get_reg_ptr(traits<ARCH>::NEXT_PC), false); | ||||||
|     this->builder.CreateRet(trap_addr_val); |     this->builder.CreateRet(trap_addr_val); | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename ARCH> inline void vm_impl<ARCH>::gen_trap_check(BasicBlock *bb) { | template <typename ARCH> inline void vm_impl<ARCH>::gen_trap_check(BasicBlock *bb) { | ||||||
|     auto *v = this->builder.CreateLoad(get_reg_ptr(arch::traits<ARCH>::TRAP_STATE), true); |     auto *v = this->builder.CreateLoad(this->get_typeptr(arch::traits<ARCH>::TRAP_STATE), get_reg_ptr(arch::traits<ARCH>::TRAP_STATE), true); | ||||||
|     this->gen_cond_branch(this->builder.CreateICmp( |     this->gen_cond_branch(this->builder.CreateICmp( | ||||||
|                               ICmpInst::ICMP_EQ, v, |                               ICmpInst::ICMP_EQ, v, | ||||||
|                               ConstantInt::get(getContext(), APInt(v->getType()->getIntegerBitWidth(), 0))), |                               ConstantInt::get(getContext(), APInt(v->getType()->getIntegerBitWidth(), 0))), | ||||||
| @@ -4161,3 +4162,25 @@ std::unique_ptr<vm_if> create<arch::tgc_c>(arch::tgc_c *core, unsigned short por | |||||||
| } | } | ||||||
| } // namespace llvm | } // namespace llvm | ||||||
| } // namespace iss | } // namespace iss | ||||||
|  |  | ||||||
|  | #include <iss/factory.h> | ||||||
|  | #include <iss/arch/riscv_hart_m_p.h> | ||||||
|  | #include <iss/arch/riscv_hart_mu_p.h> | ||||||
|  | namespace iss { | ||||||
|  | namespace { | ||||||
|  | volatile std::array<bool, 2> dummy = { | ||||||
|  |         core_factory::instance().register_creator("tgc_c|m_p|llvm", [](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 llvm::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|llvm", [](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 llvm::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}}; | ||||||
|  |         }) | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | } | ||||||
|   | |||||||
| @@ -120,57 +120,7 @@ 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> |     template<unsigned W, typename U, typename S = typename std::make_signed<U>::type> | ||||||
|     inline S sext(U from) { |     inline S sext(U from) { | ||||||
|         auto mask = (1ULL<<W) - 1; |         auto mask = (1ULL<<W) - 1; | ||||||
| @@ -182,14 +132,23 @@ private: | |||||||
|     /**************************************************************************** |     /**************************************************************************** | ||||||
|      * start opcode definitions |      * start opcode definitions | ||||||
|      ****************************************************************************/ |      ****************************************************************************/ | ||||||
|     struct InstructionDesriptor { |     struct instruction_descriptor { | ||||||
|         size_t length; |         size_t length; | ||||||
|         uint32_t value; |         uint32_t value; | ||||||
|         uint32_t mask; |         uint32_t mask; | ||||||
|         compile_func op; |         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){} | ||||||
|  |     }; | ||||||
|  |  | ||||||
|     const std::array<InstructionDesriptor, 87> instr_descr = {{ |     decoding_tree_node* root {nullptr}; | ||||||
|  |  | ||||||
|  |     const std::array<instruction_descriptor, 87> instr_descr = {{ | ||||||
|          /* entries are: size, valid value, valid mask, function ptr */ |          /* entries are: size, valid value, valid mask, function ptr */ | ||||||
|         /* instruction LUI, encoding '0b00000000000000000000000000110111' */ |         /* instruction LUI, encoding '0b00000000000000000000000000110111' */ | ||||||
|         {32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, &this_class::__lui}, |         {32, 0b00000000000000000000000000110111, 0b00000000000000000000000001111111, &this_class::__lui}, | ||||||
| @@ -493,14 +452,14 @@ private: | |||||||
|             this->gen_raise_trap(tu, 0,  2); |             this->gen_raise_trap(tu, 0,  2); | ||||||
|         } |         } | ||||||
|         else{ |         else{ | ||||||
|         	auto new_pc = tu.assignment(tu.ext((tu.bitwise_and((tu.add(tu.load(rs1+ traits::X0, 0),tu.constant((int16_t)sext<12>(imm),16))),tu.constant(~ 0x1,8))),32,true),32); |         	auto new_pc = tu.assignment(tu.ext((tu.bitwise_and((tu.add(tu.load(rs1+ traits::X0, 0),tu.constant((int16_t)sext<12>(imm),16))),tu.constant(~0x1,8))),32,true),32); | ||||||
|         	tu.open_if(tu.srem(new_pc,tu.constant(static_cast<uint32_t>(traits:: INSTR_ALIGNMENT),32))); |         	tu.open_if(tu.srem(new_pc,tu.constant(static_cast<uint32_t>(traits:: INSTR_ALIGNMENT),32))); | ||||||
|         	this->gen_raise_trap(tu, 0,  0); |         	this->gen_raise_trap(tu, 0,  0); | ||||||
|         	tu.open_else(); |         	tu.open_else(); | ||||||
|         	if(rd!= 0) { |         	if(rd!= 0) { | ||||||
|         	    tu.store(rd + traits::X0,tu.ext((tu.add(tu.ext(cur_pc_val,32,false),tu.constant( 4,8))),32,true)); |         	    tu.store(rd + traits::X0,tu.ext((tu.add(tu.ext(cur_pc_val,32,false),tu.constant( 4,8))),32,true)); | ||||||
|         	} |         	} | ||||||
|         	auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and(new_pc,tu.constant(~ 0x1,8)),32); |         	auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and(new_pc,tu.constant(~0x1,8)),32); | ||||||
|         	tu.store(traits::NEXT_PC, PC_val_v); |         	tu.store(traits::NEXT_PC, PC_val_v); | ||||||
|         	tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); |         	tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); | ||||||
|         	tu.close_scope(); |         	tu.close_scope(); | ||||||
| @@ -1962,7 +1921,7 @@ private: | |||||||
|         else{ |         else{ | ||||||
|         	auto xrd = tu.assignment(tu.read_mem(traits::CSR, csr, 32),32); |         	auto xrd = tu.assignment(tu.read_mem(traits::CSR, csr, 32),32); | ||||||
|         	if(zimm!= 0) { |         	if(zimm!= 0) { | ||||||
|         	    tu.write_mem(traits::CSR, csr, tu.bitwise_and(xrd,tu.constant(~ ((uint32_t)zimm),32))); |         	    tu.write_mem(traits::CSR, csr, tu.bitwise_and(xrd,tu.constant(~((uint32_t)zimm),32))); | ||||||
|         	} |         	} | ||||||
|         	if(rd!= 0) { |         	if(rd!= 0) { | ||||||
|         	    tu.store(rd + traits::X0,xrd); |         	    tu.store(rd + traits::X0,xrd); | ||||||
| @@ -2163,13 +2122,13 @@ private: | |||||||
|         	auto divisor = tu.assignment(tu.ext(tu.load(rs2+ traits::X0, 0),32,false),32); |         	auto divisor = tu.assignment(tu.ext(tu.load(rs2+ traits::X0, 0),32,false),32); | ||||||
|         	if(rd!= 0){ tu.open_if(tu.icmp(ICmpInst::ICMP_NE,divisor,tu.constant( 0,8))); |         	if(rd!= 0){ tu.open_if(tu.icmp(ICmpInst::ICMP_NE,divisor,tu.constant( 0,8))); | ||||||
|         	auto MMIN = tu.assignment(tu.constant(((uint32_t)1)<<(static_cast<uint32_t>(traits:: XLEN)-1),32),32); |         	auto MMIN = tu.assignment(tu.constant(((uint32_t)1)<<(static_cast<uint32_t>(traits:: XLEN)-1),32),32); | ||||||
|         	tu.open_if(tu.logical_and(tu.icmp(ICmpInst::ICMP_EQ,tu.load(rs1+ traits::X0, 0),MMIN),tu.icmp(ICmpInst::ICMP_EQ,divisor,tu.constant(- 1,8)))); |         	tu.open_if(tu.logical_and(tu.icmp(ICmpInst::ICMP_EQ,tu.load(rs1+ traits::X0, 0),MMIN),tu.icmp(ICmpInst::ICMP_EQ,divisor,tu.constant(-1,8)))); | ||||||
|         	tu.store(rd + traits::X0,MMIN); |         	tu.store(rd + traits::X0,MMIN); | ||||||
|         	tu.open_else(); |         	tu.open_else(); | ||||||
|         	tu.store(rd + traits::X0,tu.ext((tu.sdiv(dividend,divisor)),32,true)); |         	tu.store(rd + traits::X0,tu.ext((tu.sdiv(dividend,divisor)),32,true)); | ||||||
|         	tu.close_scope(); |         	tu.close_scope(); | ||||||
|         	tu.open_else(); |         	tu.open_else(); | ||||||
|         	tu.store(rd + traits::X0,tu.constant((uint32_t)- 1,32)); |         	tu.store(rd + traits::X0,tu.constant((uint32_t)-1,32)); | ||||||
|         	tu.close_scope(); |         	tu.close_scope(); | ||||||
|         	} |         	} | ||||||
|         } |         } | ||||||
| @@ -2208,7 +2167,7 @@ private: | |||||||
|         	} |         	} | ||||||
|         	tu.open_else(); |         	tu.open_else(); | ||||||
|         	if(rd!=0) { |         	if(rd!=0) { | ||||||
|         	    tu.store(rd + traits::X0,tu.constant((uint32_t)- 1,32)); |         	    tu.store(rd + traits::X0,tu.constant((uint32_t)-1,32)); | ||||||
|         	} |         	} | ||||||
|         	tu.close_scope(); |         	tu.close_scope(); | ||||||
|         } |         } | ||||||
| @@ -2243,7 +2202,7 @@ private: | |||||||
|         else{ |         else{ | ||||||
|         	tu.open_if(tu.icmp(ICmpInst::ICMP_NE,tu.load(rs2+ traits::X0, 0),tu.constant( 0,8))); |         	tu.open_if(tu.icmp(ICmpInst::ICMP_NE,tu.load(rs2+ traits::X0, 0),tu.constant( 0,8))); | ||||||
|         	auto MMIN = tu.assignment(tu.constant( 1<<(static_cast<uint32_t>(traits:: XLEN)-1),8),32); |         	auto MMIN = tu.assignment(tu.constant( 1<<(static_cast<uint32_t>(traits:: XLEN)-1),8),32); | ||||||
|         	tu.open_if(tu.logical_and(tu.icmp(ICmpInst::ICMP_EQ,tu.load(rs1+ traits::X0, 0),MMIN),tu.icmp(ICmpInst::ICMP_EQ,tu.ext(tu.load(rs2+ traits::X0, 0),32,false),tu.constant(- 1,8)))); |         	tu.open_if(tu.logical_and(tu.icmp(ICmpInst::ICMP_EQ,tu.load(rs1+ traits::X0, 0),MMIN),tu.icmp(ICmpInst::ICMP_EQ,tu.ext(tu.load(rs2+ traits::X0, 0),32,false),tu.constant(-1,8)))); | ||||||
|         	if(rd!=0) { |         	if(rd!=0) { | ||||||
|         	    tu.store(rd + traits::X0,tu.constant( 0,8)); |         	    tu.store(rd + traits::X0,tu.constant( 0,8)); | ||||||
|         	} |         	} | ||||||
| @@ -2955,7 +2914,7 @@ private: | |||||||
|         gen_set_pc(tu, pc, traits::NEXT_PC); |         gen_set_pc(tu, pc, traits::NEXT_PC); | ||||||
|         tu.open_scope(); |         tu.open_scope(); | ||||||
|         if(rs1&&rs1<static_cast<uint32_t>(traits:: RFS)) { |         if(rs1&&rs1<static_cast<uint32_t>(traits:: RFS)) { | ||||||
|             auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and(tu.load(rs1%static_cast<uint32_t>(traits:: RFS)+ traits::X0, 0),tu.constant(~ 0x1,8)),32); |             auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and(tu.load(rs1%static_cast<uint32_t>(traits:: RFS)+ traits::X0, 0),tu.constant(~0x1,8)),32); | ||||||
|             tu.store(traits::NEXT_PC, PC_val_v); |             tu.store(traits::NEXT_PC, PC_val_v); | ||||||
|             tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); |             tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); | ||||||
|         } |         } | ||||||
| @@ -3043,7 +3002,7 @@ private: | |||||||
|         else{ |         else{ | ||||||
|         	auto new_pc = tu.assignment(tu.load(rs1+ traits::X0, 0),32); |         	auto new_pc = tu.assignment(tu.load(rs1+ traits::X0, 0),32); | ||||||
|         	tu.store(1 + traits::X0,tu.ext((tu.add(tu.ext(cur_pc_val,32,false),tu.constant( 2,8))),32,true)); |         	tu.store(1 + traits::X0,tu.ext((tu.add(tu.ext(cur_pc_val,32,false),tu.constant( 2,8))),32,true)); | ||||||
|         	auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and(new_pc,tu.constant(~ 0x1,8)),32); |         	auto PC_val_v = tu.assignment("PC_val", tu.bitwise_and(new_pc,tu.constant(~0x1,8)),32); | ||||||
|         	tu.store(traits::NEXT_PC, PC_val_v); |         	tu.store(traits::NEXT_PC, PC_val_v); | ||||||
|         	tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); |         	tu.store(traits::LAST_BRANCH, tu.constant(2U, 2)); | ||||||
|         } |         } | ||||||
| @@ -3136,11 +3095,64 @@ private: | |||||||
|         vm_impl::gen_trap_check(tu); |         vm_impl::gen_trap_check(tu); | ||||||
|         return BRANCH; |         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 insn) { | template <typename CODE_WORD> void debug_fn(CODE_WORD instr) { | ||||||
|     volatile CODE_WORD x = insn; |     volatile CODE_WORD x = instr; | ||||||
|     insn = 2 * x; |     instr = 2 * x; | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); } | template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); } | ||||||
| @@ -3148,14 +3160,11 @@ template <typename ARCH> vm_impl<ARCH>::vm_impl() { this(new ARCH()); } | |||||||
| template <typename ARCH> | template <typename ARCH> | ||||||
| vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | vm_impl<ARCH>::vm_impl(ARCH &core, unsigned core_id, unsigned cluster_id) | ||||||
| : vm_base<ARCH>(core, core_id, cluster_id) { | : vm_base<ARCH>(core, core_id, cluster_id) { | ||||||
|     qlut[0] = lut_00.data(); |     root = new decoding_tree_node(std::numeric_limits<uint32_t>::max()); | ||||||
|     qlut[1] = lut_01.data(); |     for(auto instr:instr_descr){ | ||||||
|     qlut[2] = lut_10.data(); |         root->instrs.push_back(instr); | ||||||
|     qlut[3] = lut_11.data(); |  | ||||||
|     for (auto instr : instr_descr) { |  | ||||||
|         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> | template <typename ARCH> | ||||||
| @@ -3163,11 +3172,10 @@ std::tuple<continuation_e> | |||||||
| vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, tu_builder& tu) { | 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 |     // we fetch at max 4 byte, alignment is 2 | ||||||
|     enum {TRAP_ID=1<<16}; |     enum {TRAP_ID=1<<16}; | ||||||
|     code_word_t insn = 0; |     code_word_t instr = 0; | ||||||
|     // const typename traits::addr_t upper_bits = ~traits::PGMASK; |  | ||||||
|     phys_addr_t paddr(pc); |     phys_addr_t paddr(pc); | ||||||
|     auto *const data = (uint8_t *)&insn; |  | ||||||
|     paddr = this->core.v2p(pc); |     paddr = this->core.v2p(pc); | ||||||
|  |     //TODO: re-add page handling | ||||||
| //    if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary | //    if ((pc.val & upper_bits) != ((pc.val + 2) & upper_bits)) { // we may cross a page boundary | ||||||
| //        auto res = this->core.read(paddr, 2, data); | //        auto res = this->core.read(paddr, 2, data); | ||||||
| //        if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | //        if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | ||||||
| @@ -3175,18 +3183,17 @@ vm_impl<ARCH>::gen_single_inst_behavior(virt_addr_t &pc, unsigned int &inst_cnt, | |||||||
| //            res = this->core.read(this->core.v2p(pc + 2), 2, data + 2); | //            res = this->core.read(this->core.v2p(pc + 2), 2, data + 2); | ||||||
| //        } | //        } | ||||||
| //    } else { | //    } else { | ||||||
|         auto res = this->core.read(paddr, 4, data); |         auto res = this->core.read(paddr, 4, reinterpret_cast<uint8_t*>(&instr)); | ||||||
|         if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); |         if (res != iss::Ok) throw trap_access(TRAP_ID, pc.val); | ||||||
| //    } | //    } | ||||||
|     if (insn == 0x0000006f || (insn&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0' |     if (instr == 0x0000006f || (instr&0xffff)==0xa001) throw simulation_stopped(0); // 'J 0' or 'C.J 0' | ||||||
|     // curr pc on stack |     // curr pc on stack | ||||||
|     ++inst_cnt; |     ++inst_cnt; | ||||||
|     auto lut_val = extract_fields(insn); |     auto f = decode_instr(root, instr); | ||||||
|     auto f = qlut[insn & 0x3][lut_val]; |  | ||||||
|     if (f == nullptr) { |     if (f == nullptr) { | ||||||
|         f = &this_class::illegal_intruction; |         f = &this_class::illegal_intruction; | ||||||
|     } |     } | ||||||
|     return (this->*f)(pc, insn, tu); |     return (this->*f)(pc, instr, tu); | ||||||
| } | } | ||||||
|  |  | ||||||
| template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(tu_builder& tu, uint16_t trap_id, uint16_t cause) { | template <typename ARCH> void vm_impl<ARCH>::gen_raise_trap(tu_builder& tu, uint16_t trap_id, uint16_t cause) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user