void FE_stage() { /* only part of FE_stage function is implemented */ /* please complete the rest of FE_stage function */ if(FE_latch->stage_stall==false) //check if no pipeline stalled { Op* new_op = get_free_op(); //get a placeholder op out of the pool if(get_op(new_op)) //copy op from trace into the placeholder op { if((new_op->opcode>=OP_FMEM)&&(new_op->opcode<=OP_FCMO)){ float_count++; } if((new_op->opcode>=OP_IADD)&&(new_op->opcode<=OP_MM)){ integer_count++; } if((new_op->opcode>=OP_CF)&&(new_op->opcode<=OP_LDA)){ if(new_op->opcode==OP_CF) branch_count++; integer_count++; } if((new_op->opcode>=OP_LDA)&&(new_op->opcode<=OP_LD)){ load_count++; } if((new_op->opcode==OP_MM)||(new_op->opcode==OP_IMUL)){ multiple_count++; } if(new_op->opcode==OP_ST){ store_count++; } if((new_op->opcode == OP_CF) && new_op->cf_type==CF_CBR && use_bpred==true) { uint64_t pc = new_op->instruction_addr; bool predicted_dir=0; predicted_dir=bpred_access(branchpred,pc); bpred_update(branchpred,pc,predicted_dir,new_op->actually_taken); if(new_op->actually_taken != predicted_dir) { new_op->mispredicted_branch=true; } bpred_mispred_count=branchpred->mispred; bpred_okpred_count=branchpred->okpred; } FE_latch->op=new_op; FE_latch->op_valid=true; } else free_op(new_op); } // next_pc = pc + op->inst_size; // you need this code for building a branch predictor }
/* start simulation, program loaded, processor precise state initialized */ void sim_main(void) { md_inst_t inst; register md_addr_t addr, target_PC = 0; enum md_opcode op; register int is_write; int stack_idx; enum md_fault_type fault; fprintf(stderr, "sim: ** starting functional simulation w/ predictors **\n"); /* set up initial default next PC */ SET_NPC(CPC + sizeof(md_inst_t)); /* check for DLite debugger entry condition */ if (dlite_check_break(regs.regs_PC, /* no access */0, /* addr */0, 0, 0)) dlite_main(regs.regs_PC - sizeof(md_inst_t), regs.regs_PC, sim_num_insn, ®s, mem); while (TRUE) { /* maintain $r0 semantics */ regs.regs_R[MD_REG_ZERO] = 0; #ifdef TARGET_ALPHA regs.regs_F.d[MD_REG_ZERO] = 0.0; #endif /* get the next instruction to execute */ MD_FETCH_INST(inst, mem, regs.regs_PC); /* keep an instruction count */ sim_num_insn++; /* set default reference address and access mode */ addr = 0; is_write = FALSE; /* set default fault - none */ fault = md_fault_none; /* decode the instruction */ MD_SET_OPCODE(op, inst); /* execute the instruction */ switch (op) { #define DEFINST(OP,MSK,NAME,OPFORM,RES,FLAGS,O1,O2,I1,I2,I3) \ case OP: \ SYMCAT(OP,_IMPL); \ break; #define DEFLINK(OP,MSK,NAME,MASK,SHIFT) \ case OP: \ panic("attempted to execute a linking opcode"); #define CONNECT(OP) #define DECLARE_FAULT(FAULT) \ { fault = (FAULT); break; } #include "machine.def" default: panic("attempted to execute a bogus opcode"); } if (fault != md_fault_none) fatal("fault (%d) detected @ 0x%08p", fault, regs.regs_PC); if (MD_OP_FLAGS(op) & F_MEM) { sim_num_refs++; if (MD_OP_FLAGS(op) & F_STORE) is_write = TRUE; } if (MD_OP_FLAGS(op) & F_CTRL) { md_addr_t pred_PC; struct bpred_update_t update_rec; sim_num_branches++; if (pred) { /* get the next predicted fetch address */ pred_PC = bpred_lookup( pred, regs.regs_PC, // branch addr target_PC, // target addr op, // instruction opcode MD_IS_CALL(op), // call? MD_IS_RETURN(op), // return? &update_rec, // stack an update ptr &stack_idx); // stash return stack ptr /* valid address returned from branch predictor? */ if (!pred_PC) { /* no predicted taken target, attempt not taken target */ pred_PC = regs.regs_PC + sizeof(md_inst_t); } bpred_update( pred, CPC, // branch addr NPC, // resolved branch target NPC != CPC + sizeof(md_inst_t), // taken? pred_PC != CPC + sizeof(md_inst_t), // pred taken? pred_PC == NPC, // correct pred? op, // opcode &update_rec); // predictor update pointer } } /* check for DLite debugger entry condition */ if (dlite_check_break(NPC, is_write ? ACCESS_WRITE : ACCESS_READ, addr, sim_num_insn, sim_num_insn)) { dlite_main(regs.regs_PC, NPC, sim_num_insn, ®s, mem); } /* go to the next instruction */ UPDATE_PC /* finish early? */ if (max_insts && sim_num_insn >= max_insts) return; } }
void FE_stage() { if(stop_fetching) return; if(have_to_send_EOS) { if(sendEOS()) { stop_fetching = true; have_to_send_EOS = false; } return; } #if 0 if(FE_latch->op_valid || FE_latch->pipeline_stall_enabled) { /* Data inside the latch is valid and next stage is still using it. Or ID stage has enabled pipeline stalling because of a branch instruction. Do not fetch */ return; } /* This condition is rewritten for multithreading. See following statements. ~(a OR b) ===> ~a AND ~b */ #endif static UINT64 fetch_arbiter = 0; int stream_id = -1; Op *op; bool op_exists = false, stalled[HW_MAX_THREAD]; for(int i = 0 ; i < HW_MAX_THREAD ; i++) stalled[i] = true; /* Find next available empty queue slot to fill */ for(int i = 0 ; i < thread_count ; i++) { stream_id = fetch_arbiter++ % thread_count; if(!FE_latch->op_valid_thread[stream_id] && !FE_latch->pipeline_stall_enabled_thread[stream_id]) { stalled[stream_id] = false; op = get_free_op(); op_exists = get_op(op, stream_id); if(op_exists) break; else free_op(op); } } if(!op_exists) { /* No op fetched - this could be due to following : 1. all threads were stalled 2. some threads were stalled and others have run out of instructions 3. no instructions available to fetch */ // checking case 1 bool all_stalled = true; for(int i = 0 ; i < thread_count ; i++) { if(!stalled[i]) all_stalled = false; } if(all_stalled) return; // checking case 2 & 3 bool eois = true; // end of instruction streams for(int i = 0 ; i < thread_count ; i++) { if(!end_of_stream[i]) eois = false; } if(!eois) return; else { /* Must take actions for initiating simulator shut down */ // first it should be seen if there is some space in queue. // if no, then try to send in next cycle if(sendEOS()) stop_fetching = true; else have_to_send_EOS = true; return; } } /* If the op is an branch other than conditional branch, assume that it is predicted correctly, if the branch predictor is used */ // if(use_bpred && (op->cf_type >= CF_BR) && (op->cf_type < NUM_CF_TYPES) && (op->cf_type != CF_CBR)) // bpred_okpred_count++; /* Above 2 lines commented because its not the way solution is implemented */ /* If we are using branch predictor and type of opcode is conditional branch, get a prediction and update GHR and PHT */ if(use_bpred && (op->cf_type == CF_CBR)) { int prediction = bpred_access(branchpred, op->instruction_addr, op->thread_id); if(prediction == op->actually_taken) { bpred_okpred_count++; bpred_okpred_count_thread[op->thread_id]++; } else { bpred_mispred_count++; bpred_mispred_count_thread[op->thread_id]++; /* stall the pipeline if we mispredict */ FE_latch->pipeline_stall_enabled_thread[op->thread_id] = true;; FE_latch->stall_enforcer_thread[op->thread_id] = op->inst_id; } bpred_update(branchpred,op->instruction_addr,prediction,op->actually_taken, op->thread_id); } /* hwsim : get the instruction and pass to ID phase */ # if 0 /* Deprecated after adding MT support */ FE_latch->op = op; /* pass the op to ID stage */ FE_latch->op_valid = true; /* Mark it as valid */ #endif FE_latch->op_queue[op->thread_id] = op; FE_latch->op_valid_thread[op->thread_id] = true; }
/* start simulation, program loaded, processor precise state initialized */ void sim_main(void) { md_inst_t inst; register md_addr_t addr, target_PC; enum md_opcode op; register int is_write; int stack_idx; enum md_fault_type fault; int setPC, in_flow = FALSE, flow_index, nflow; struct md_uop_t flowtab[MD_MAX_FLOWLEN]; fprintf(stderr, "sim: ** starting functional simulation w/ predictors **\n"); /* set up initial default next PC */ regs.regs_NPC = regs.regs_PC + sizeof(md_inst_t); /* synchronize register files... */ regs.regs_R[MD_REG_PC] = regs.regs_PC; /* check for DLite debugger entry condition */ if (dlite_check_break(regs.regs_PC, /* no access */0, /* addr */0, 0, 0)) dlite_main(regs.regs_PC - sizeof(md_inst_t), regs.regs_PC, sim_num_insn, ®s, mem); while (TRUE) { /* maintain $r0 semantics */ #ifndef TARGET_ARM regs.regs_R[MD_REG_ZERO] = 0; #endif #ifdef TARGET_ALPHA regs.regs_F.d[MD_REG_ZERO] = 0.0; #endif /* TARGET_ALPHA */ /* set default reference address and access mode */ addr = 0; is_write = FALSE; /* set default fault - none */ fault = md_fault_none; if (!in_flow) { /* keep an instruction count */ sim_num_insn++; /* get the next instruction to execute */ MD_FETCH_INST(inst, mem, regs.regs_PC); /* decode the instruction */ MD_SET_OPCODE(op, inst); if (MD_OP_FLAGS(op) & F_CISC) { /* get instruction flow */ nflow = md_get_flow(op, inst, flowtab); if (nflow > 0) { in_flow = TRUE; flow_index = 0; } else fatal("could not locate CISC flow"); sim_num_uops += nflow; } else sim_num_uops++; } if (in_flow) { op = flowtab[flow_index].op; inst = flowtab[flow_index++].inst; if (flow_index == nflow) in_flow = FALSE; } if (op == NA) panic("bogus opcode detected @ 0x%08p", regs.regs_PC); if (MD_OP_FLAGS(op) & F_CISC) panic("CISC opcode decoded"); setPC = 0; regs.regs_R[MD_REG_PC] = regs.regs_PC; /* execute the instruction */ switch (op) { #define DEFINST(OP,MSK,NAME,OPFORM,RES,FLAGS,O1,O2,O3,I1,I2,I3,I4) \ case OP: \ SYMCAT(OP,_IMPL); \ break; #define DEFUOP(OP,NAME,OPFORM,RES,FLAGS,O1,O2,O3,I1,I2,I3,I4) \ case OP: \ SYMCAT(OP,_IMPL); \ break; #define DEFLINK(OP,MSK,NAME,MASK,SHIFT) \ case OP: \ panic("attempted to execute a linking opcode"); #define CONNECT(OP) #define DECLARE_FAULT(FAULT) \ { fault = (FAULT); break; } #include "machine.def" default: panic("attempted to execute a bogus opcode"); } if (fault != md_fault_none) fatal("fault (%d) detected @ 0x%08p", fault, regs.regs_PC); if (setPC != 0/* regs.regs_R[MD_REG_PC] != regs.regs_PC */) regs.regs_NPC = regs.regs_R[MD_REG_PC]; if (MD_OP_FLAGS(op) & F_MEM) { sim_num_refs++; if (MD_OP_FLAGS(op) & F_STORE) is_write = TRUE; } if (MD_OP_FLAGS(op) & F_CTRL) { md_addr_t pred_PC; struct bpred_update_t update_rec; sim_num_branches++; if (pred) { /* get the next predicted fetch address */ pred_PC = bpred_lookup(pred, /* branch addr */regs.regs_PC, /* target */target_PC, /* inst opcode */op, /* call? */MD_IS_CALL(op), /* return? */MD_IS_RETURN(op), /* stash an update ptr */&update_rec, /* stash return stack ptr */&stack_idx); /* valid address returned from branch predictor? */ if (!pred_PC) { /* no predicted taken target, attempt not taken target */ pred_PC = regs.regs_PC + sizeof(md_inst_t); } bpred_update(pred, /* branch addr */regs.regs_PC, /* resolved branch target */regs.regs_NPC, /* taken? */regs.regs_NPC != (regs.regs_PC + sizeof(md_inst_t)), /* pred taken? */pred_PC != (regs.regs_PC + sizeof(md_inst_t)), /* correct pred? */pred_PC == regs.regs_NPC, /* opcode */op, /* predictor update pointer */&update_rec); } } /* check for DLite debugger entry condition */ if (dlite_check_break(regs.regs_NPC, is_write ? ACCESS_WRITE : ACCESS_READ, addr, sim_num_insn, sim_num_insn)) dlite_main(regs.regs_PC, regs.regs_NPC, sim_num_insn, ®s, mem); /* go to the next instruction */ if (!in_flow) { regs.regs_PC = regs.regs_NPC; regs.regs_NPC += sizeof(md_inst_t); } /* finish early? */ if (max_insts && sim_num_insn >= max_insts) return; } }
/* start simulation, program loaded, processor precise state initialized */ void sim_main(void) { md_inst_t inst; register md_addr_t addr, target_PC; enum md_opcode op; register int is_write; int stack_idx; enum md_fault_type fault; int out1, out2, in1, in2, in3; /* register names */ int v_out1, v_out2, v_in1, v_in2, v_in3;/* register values */ int vl_out1, vl_out2, vl_in1, vl_in2, vl_in3; md_addr_t addr_dispatch; /* access_address */ int m_size; /* access_size */ FILE *stream; md_addr_t predicted_PC; fprintf(stderr, "sim: ** starting functional simulation w/ predictors **\n"); /* set up initial default next PC */ regs.regs_NPC = regs.regs_PC + sizeof(md_inst_t); /* check for DLite debugger entry condition */ if (dlite_check_break(regs.regs_PC, /* no access */0, /* addr */0, 0, 0)) dlite_main(regs.regs_PC - sizeof(md_inst_t), regs.regs_PC, sim_num_insn, ®s, mem); while (TRUE) { /* maintain $r0 semantics */ regs.regs_R[MD_REG_ZERO] = 0; #ifdef TARGET_ALPHA regs.regs_F.d[MD_REG_ZERO] = 0.0; #endif /* TARGET_ALPHA */ /* get the next instruction to execute */ mem_access(mem, Read, regs.regs_PC, &inst, sizeof(md_inst_t)); /* keep an instruction count */ sim_num_insn++; /* set default reference address and access mode */ addr = 0; is_write = FALSE; /* set default fault - none */ fault = md_fault_none; /* decode the instruction */ MD_SET_OPCODE(op, inst); /* execute the instruction */ switch (op) { #define DEFINST(OP,MSK,NAME,OPFORM,RES,CLASS,O1,O2,I1,I2,I3, \ VO1,LO1,VO2,LO2,ADDR,VI1,LI1,VI2,LI2,VI3,LI3,M_Size) \ case OP: \ in1 = I1; in2 = I2; in3 = I3; \ v_in1 = VI1; v_in2 = VI2; v_in3 = VI3; \ vl_in1 = LI1; vl_in2 = LI2; vl_in3 = LI3; \ SYMCAT(OP,_IMPL); \ out1 = O1; out2 = O2; \ v_out1 = VO1; v_out2 = VO2; \ vl_out1 = LO1; vl_out2 = LO2; \ addr_dispatch = ADDR; \ m_size = M_Size; \ break; #define DEFLINK(OP,MSK,NAME,MASK,SHIFT) \ case OP: \ panic("attempted to execute a linking opcode"); #define CONNECT(OP) #define DECLARE_FAULT(FAULT) \ { fault = (FAULT); break; } #include "operand.def" default: panic("attempted to execute a bogus opcode"); } if (fault != md_fault_none) fatal("fault (%d) detected @ 0x%08p", fault, regs.regs_PC); if (MD_OP_FLAGS(op) & F_MEM) { sim_num_refs++; if (MD_OP_FLAGS(op) & F_STORE) is_write = TRUE; } if (MD_OP_FLAGS(op) & F_CTRL) { md_addr_t pred_PC; struct bpred_update_t update_rec; sim_num_branches++; if (pred) { /* get the next predicted fetch address */ pred_PC = bpred_lookup(pred, /* branch addr */regs.regs_PC, /* target */target_PC, /* inst opcode */op, /* call? */MD_IS_CALL(op), /* return? */MD_IS_RETURN(op), /* stash an update ptr */&update_rec, /* stash return stack ptr */&stack_idx); /* valid address returned from branch predictor? */ if (!pred_PC) { /* no predicted taken target, attempt not taken target */ pred_PC = regs.regs_PC + sizeof(md_inst_t); } bpred_update(pred, /* branch addr */regs.regs_PC, /* resolved branch target */regs.regs_NPC, /* taken? */regs.regs_NPC != (regs.regs_PC + sizeof(md_inst_t)), /* pred taken? */pred_PC != (regs.regs_PC + sizeof(md_inst_t)), /* correct pred? */pred_PC == regs.regs_NPC, /* opcode */op, /* predictor update pointer */&update_rec); } predicted_PC = pred_PC; } /* ここから下を見ればどの変数に何が入っているか分かるはず。 */ stream = stdout; fprintf(stream, "############################################################ \n"); fprintf(stream, " --- trace count(%d) \n", (int)sim_num_insn); fprintf(sim_fd, "%s, inst: `", fprintf(stream, " opcode: %s, inst: `", MD_OP_NAME(op)); md_print_insn(inst, regs.regs_PC, stream); fprintf(stream, "'\n"); myfprintf(stream, " PC: 0x%08p, NPC: 0x%08p", regs.regs_PC, regs.regs_NPC); if(pred) myfprintf(stream, " (pred_PC: 0x%08p)\n", regs.regs_PC, regs.regs_NPC, predicted_PC); else myfprintf(stream, "\n"); fprintf(stream," | in(r%02d,r%02d,r%02d),out(r%02d,r%02d),\n", in1, in2, in3, out1, out2); fprintf(stream," | IN(%8x,%8x,%8x),OUT(%8x,%8x)addr(%8x)|\n", v_in1, v_in2, v_in3, v_out1, v_out2, addr_dispatch); /* ダブルワードだった場合,この変数がセットされる */ fprintf(stream," | in(%8x,%8x,%8x),out(%8x,%8x) |\n", vl_in1, vl_in2, vl_in3, vl_out1, vl_out2); /* machine.h をみるとopから命令の種類を判別する方法が分かる。以下は例 */ if (op == MD_NOP_OP) { fprintf(stream, " NOP instruction \n"); } else if (MD_OP_FLAGS(op) & F_MEM) { fprintf(stream, " MEMORY instruction \n"); if (MD_OP_FLAGS(op) & F_STORE) fprintf(stream, " store instruction \n"); else if (MD_OP_FLAGS(op) & F_LOAD) fprintf(stream, " load instruction \n"); fprintf(stream, " ld/st address is %010x\n", addr_dispatch); fprintf(stream, " ld/st size is %2d\n", m_size); /* ロードストアがバイト単位なのか,ワード単位なのか, ハーフワード単位なのかは,opを見て判断できる。 例えば, md_op2name[op] が "sh"ならハーフワードである。 しかし、これだと面倒なのでoperand.defを改良した方が良い かも知れない。これはおまかせ。 */ /* ロードストアの詳細に関しては,machine.def を参照。 例えば,store halfの場合,(machine.def をエディタで開いて "sh"と言う文字列をサーチすれば該当箇所が見つかる) 以下のようにかかれている。*/ /* #define SH_IMPL { half_t _src; enum md_fault_type _fault; _src = (half_t)(word_t)GPR(RT); WRITE_HALF(_src, GPR(BS) + OFS, _fault); if (_fault != md_fault_none) DECLARE_FAULT(_fault); } */ /*ここで、GPR(BS)はBS番レジスタの値 OFS はオフセットを示す。 そして,GPR(RT)はRT番レジスタの値を示す。 そしてWRITE_HALF(_src, GPR(BS) + OFS, _fault);は 値 _src をアドレス GPR(BS) + OFS に書き込む事を意味する。 書き込みのサイズは,halfワードである。*/ } else if (MD_OP_FLAGS(op) & F_CTRL) { fprintf(stream, " BRANCH instruction \n"); if (MD_IS_CALL(op)) fprintf(stream, " function call \n"); else if (MD_IS_RETURN(op)) fprintf(stream, " function return \n"); else if (MD_IS_INDIR(op)) fprintf(stream, " indirect jump \n"); else if ((MD_OP_FLAGS(op) & (F_CTRL|F_DIRJMP)) == (F_CTRL|F_DIRJMP)) fprintf(stream, " direct jump \n"); } else { fprintf(stream, " other instruction \n"); } /* この応用として,いきなりopからstoreを判別するには,以下のようにすれば良い*/ if ((MD_OP_FLAGS(op) & (F_MEM|F_STORE)) == (F_MEM|F_STORE)) { /*fprintf(stream, " store instruction \n");*/ } else if ((MD_OP_FLAGS(op) & (F_MEM|F_LOAD)) == (F_MEM|F_LOAD)) { /*fprintf(stream, " load instruction \n");*/ } /* ここまで */ /* check for DLite debugger entry condition */ if (dlite_check_break(regs.regs_NPC, is_write ? ACCESS_WRITE : ACCESS_READ, addr, sim_num_insn, sim_num_insn)) dlite_main(regs.regs_PC, regs.regs_NPC, sim_num_insn, ®s, mem); /* go to the next instruction */ regs.regs_PC = regs.regs_NPC; regs.regs_NPC += sizeof(md_inst_t); /* finish early? */ if (max_insts && sim_num_insn >= max_insts) return; } }