refactored interpreter backend structure
This commit is contained in:
parent
b37ef973de
commit
521f40a3d6
|
@ -94,7 +94,7 @@ protected:
|
||||||
|
|
||||||
inline const char *name(size_t index){return traits::reg_aliases.at(index);}
|
inline const char *name(size_t index){return traits::reg_aliases.at(index);}
|
||||||
|
|
||||||
compile_func decode_inst(code_word_t instr) ;
|
typename arch::traits<ARCH>::opcode_e decode_inst_id(code_word_t instr);
|
||||||
virt_addr_t execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit) override;
|
virt_addr_t execute_inst(finish_cond_e cond, virt_addr_t start, uint64_t icount_limit) override;
|
||||||
|
|
||||||
// some compile time constants
|
// some compile time constants
|
||||||
|
@ -114,7 +114,7 @@ protected:
|
||||||
struct instruction_pattern {
|
struct instruction_pattern {
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
compile_func opc;
|
typename arch::traits<ARCH>::opcode_e id;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::array<std::vector<instruction_pattern>, 4> qlut;
|
std::array<std::vector<instruction_pattern>, 4> qlut;
|
||||||
|
@ -201,74 +201,14 @@ private:
|
||||||
size_t length;
|
size_t length;
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
compile_func op;
|
typename arch::traits<ARCH>::opcode_e op;
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::array<InstructionDesriptor, ${instructions.size}> instr_descr = {{
|
const std::array<InstructionDesriptor, ${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} */
|
{${instr.length}, ${instr.encoding}, ${instr.mask}, arch::traits<ARCH>::opcode_e::${instr.instruction.name}},<%}%>
|
||||||
{${instr.length}, ${instr.encoding}, ${instr.mask}, &this_class::__${generator.functionName(instr.name)}},<%}%>
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
/* instruction definitions */<%instructions.eachWithIndex{instr, idx -> %>
|
|
||||||
/* instruction ${idx}: ${instr.name} */
|
|
||||||
compile_ret_t __${generator.functionName(instr.name)}(virt_addr_t& pc, code_word_t instr){
|
|
||||||
// pre execution stuff
|
|
||||||
if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, ${idx});
|
|
||||||
<%instr.fields.eachLine{%>${it}
|
|
||||||
<%}%>if(this->disass_enabled){
|
|
||||||
/* generate console output when executing the command */
|
|
||||||
<%instr.disass.eachLine{%>${it}
|
|
||||||
<%}%>
|
|
||||||
}
|
|
||||||
auto* PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC]);
|
|
||||||
auto* NEXT_PC = reinterpret_cast<uint${addrDataWidth}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::NEXT_PC]);
|
|
||||||
auto* trap_state = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::TRAP_STATE]);
|
|
||||||
// used registers<%instr.usedVariables.each{ k,v->
|
|
||||||
if(v.isArray) {%>
|
|
||||||
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}0]);<% }else{ %>
|
|
||||||
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}]);
|
|
||||||
<%}}%>// calculate next pc value
|
|
||||||
*NEXT_PC = *PC + ${instr.length/8};
|
|
||||||
// execute instruction
|
|
||||||
try {
|
|
||||||
<%instr.behavior.eachLine{%>${it}
|
|
||||||
<%}%>} catch(...){}
|
|
||||||
// post execution stuff
|
|
||||||
process_spawn_blocks();
|
|
||||||
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, ${idx});
|
|
||||||
// trap check
|
|
||||||
if(*trap_state!=0){
|
|
||||||
super::core.enter_trap(*trap_state, pc.val, instr);
|
|
||||||
} else {
|
|
||||||
(*reinterpret_cast<uint64_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::ICOUNT]))++;
|
|
||||||
(*reinterpret_cast<uint64_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::INSTRET]))++;
|
|
||||||
}
|
|
||||||
(*reinterpret_cast<uint64_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::CYCLE]))++;
|
|
||||||
pc.val=*NEXT_PC;
|
|
||||||
return pc;
|
|
||||||
}
|
|
||||||
<%}%>
|
|
||||||
/****************************************************************************
|
|
||||||
* end opcode definitions
|
|
||||||
****************************************************************************/
|
|
||||||
compile_ret_t illegal_intruction(virt_addr_t &pc, code_word_t instr) {
|
|
||||||
this->do_sync(PRE_SYNC, static_cast<unsigned>(arch::traits<ARCH>::opcode_e::MAX_OPCODE));
|
|
||||||
uint32_t* PC = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC]);
|
|
||||||
uint32_t* NEXT_PC = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::NEXT_PC]);
|
|
||||||
*NEXT_PC = *PC + ((instr & 3) == 3 ? 4 : 2);
|
|
||||||
raise(0, 2);
|
|
||||||
// post execution stuff
|
|
||||||
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, static_cast<unsigned>(arch::traits<ARCH>::opcode_e::MAX_OPCODE));
|
|
||||||
auto* trap_state = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::TRAP_STATE]);
|
|
||||||
// trap check
|
|
||||||
if(*trap_state!=0){
|
|
||||||
super::core.enter_trap(*trap_state, pc.val, instr);
|
|
||||||
}
|
|
||||||
pc.val=*NEXT_PC;
|
|
||||||
return pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static constexpr typename traits::addr_t upper_bits = ~traits::PGMASK;
|
//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);
|
||||||
|
@ -307,6 +247,7 @@ 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;
|
||||||
for (auto instr : instr_descr) {
|
for (auto instr : instr_descr) {
|
||||||
auto quadrant = instr.value & 0x3;
|
auto quadrant = instr.value & 0x3;
|
||||||
qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
|
qlut[quadrant].push_back(instruction_pattern{instr.value, instr.mask, instr.op});
|
||||||
|
@ -327,31 +268,74 @@ inline bool is_jump_to_self_enabled(finish_cond_e cond){
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ARCH>
|
template <typename ARCH>
|
||||||
typename vm_impl<ARCH>::compile_func vm_impl<ARCH>::decode_inst(code_word_t instr){
|
typename arch::traits<ARCH>::opcode_e vm_impl<ARCH>::decode_inst_id(code_word_t instr){
|
||||||
for(auto& e: qlut[instr&0x3]){
|
for(auto& e: qlut[instr&0x3]){
|
||||||
if(!((instr&e.mask) ^ e.value )) return e.opc;
|
if(!((instr&e.mask) ^ e.value )) return e.id;
|
||||||
}
|
}
|
||||||
return &this_class::illegal_intruction;
|
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){
|
||||||
// we fetch at max 4 byte, alignment is 2
|
// we fetch at max 4 byte, alignment is 2
|
||||||
code_word_t insn = 0;
|
code_word_t instr = 0;
|
||||||
auto *const data = (uint8_t *)&insn;
|
auto *const data = (uint8_t *)&instr;
|
||||||
auto pc=start;
|
auto pc=start;
|
||||||
|
|
||||||
|
auto* PC = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::PC]);
|
||||||
|
auto* NEXT_PC = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::NEXT_PC]);
|
||||||
|
auto* trap_state = reinterpret_cast<uint32_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::TRAP_STATE]);
|
||||||
|
auto* icount = reinterpret_cast<uint64_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::ICOUNT]);
|
||||||
|
auto* instret = reinterpret_cast<uint64_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::INSTRET]);
|
||||||
|
|
||||||
while(!this->core.should_stop() &&
|
while(!this->core.should_stop() &&
|
||||||
!(is_count_limit_enabled(cond) && this->core.get_icount() >= icount_limit)){
|
!(is_count_limit_enabled(cond) && this->core.get_icount() >= icount_limit)){
|
||||||
auto res = fetch_ins(pc, data);
|
if(fetch_ins(pc, data)!=iss::Ok){
|
||||||
if(res!=iss::Ok){
|
|
||||||
this->do_sync(POST_SYNC, std::numeric_limits<unsigned>::max());
|
this->do_sync(POST_SYNC, std::numeric_limits<unsigned>::max());
|
||||||
pc.val = super::core.enter_trap(std::numeric_limits<uint64_t>::max(), pc.val, 0);
|
pc.val = super::core.enter_trap(std::numeric_limits<uint64_t>::max(), pc.val, 0);
|
||||||
} else {
|
} else {
|
||||||
if (is_jump_to_self_enabled(cond) &&
|
if (is_jump_to_self_enabled(cond) &&
|
||||||
(insn == 0x0000006f || (insn&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 f = decode_inst(insn);
|
auto inst_id = decode_inst_id(instr);
|
||||||
auto old_pc = pc.val;
|
// pre execution stuff
|
||||||
pc = (this->*f)(pc, insn);
|
if(this->sync_exec && PRE_SYNC) this->do_sync(PRE_SYNC, static_cast<unsigned>(inst_id));
|
||||||
|
switch(inst_id){<%instructions.eachWithIndex{instr, idx -> %>
|
||||||
|
case arch::traits<ARCH>::opcode_e::${instr.name}: {
|
||||||
|
<%instr.fields.eachLine{%>${it}
|
||||||
|
<%}%>if(this->disass_enabled){
|
||||||
|
/* generate console output when executing the command */
|
||||||
|
<%instr.disass.eachLine{%>${it}
|
||||||
|
<%}%>
|
||||||
|
}
|
||||||
|
// used registers<%instr.usedVariables.each{ k,v->
|
||||||
|
if(v.isArray) {%>
|
||||||
|
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}0]);<% }else{ %>
|
||||||
|
auto* ${k} = reinterpret_cast<uint${nativeTypeSize(v.type.size)}_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::${k}]);
|
||||||
|
<%}}%>// calculate next pc value
|
||||||
|
*NEXT_PC = *PC + ${instr.length/8};
|
||||||
|
// execute instruction
|
||||||
|
try {
|
||||||
|
<%instr.behavior.eachLine{%>${it}
|
||||||
|
<%}%>} catch(...){}
|
||||||
|
}
|
||||||
|
break;<%}%>
|
||||||
|
default: {
|
||||||
|
*NEXT_PC = *PC + ((instr & 3) == 3 ? 4 : 2);
|
||||||
|
raise(0, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// post execution stuff
|
||||||
|
process_spawn_blocks();
|
||||||
|
if(this->sync_exec && POST_SYNC) this->do_sync(POST_SYNC, 65);
|
||||||
|
// trap check
|
||||||
|
if(*trap_state!=0){
|
||||||
|
super::core.enter_trap(*trap_state, pc.val, instr);
|
||||||
|
} else {
|
||||||
|
(*icount)++;
|
||||||
|
(*instret)++;
|
||||||
|
}
|
||||||
|
(*reinterpret_cast<uint64_t*>(this->regs_base_ptr+arch::traits<ARCH>::reg_byte_offsets[arch::traits<ARCH>::CYCLE]))++;
|
||||||
|
pc.val=*NEXT_PC;
|
||||||
this->core.reg.PC = this->core.reg.NEXT_PC;
|
this->core.reg.PC = this->core.reg.NEXT_PC;
|
||||||
this->core.reg.trap_state = this->core.reg.pending_trap;
|
this->core.reg.trap_state = this->core.reg.pending_trap;
|
||||||
}
|
}
|
||||||
|
@ -359,7 +343,7 @@ typename vm_base<ARCH>::virt_addr_t vm_impl<ARCH>::execute_inst(finish_cond_e co
|
||||||
return pc;
|
return pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mnrv32
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreDef.name.toLowerCase()} *core, unsigned short port, bool dump) {
|
std::unique_ptr<vm_if> create<arch::${coreDef.name.toLowerCase()}>(arch::${coreDef.name.toLowerCase()} *core, unsigned short port, bool dump) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue