手把手代码实现五级流水线CPU——第三篇:流水线控制逻辑

news2024/11/17 1:35:42

系列文章目录

第一篇:初级顺序流水线
第二篇:分支预测流水线


文章目录

  • 系列文章目录
  • 一、控制逻辑
  • 二、具体操作
    • 1.判断暂停
    • 2.控制冒险
    • 3.跳转问题
    • 4.实现
  • 代码


一、控制逻辑

通过暂停和插入气泡来动态调整流水线的状态
在这里插入图片描述

二、具体操作

在这里插入图片描述

1.判断暂停

  • 识别:
    指令在译码阶段读取寄存器时,通过读取寄存器的id值来分别与执行阶段、访存阶段以及写回阶段所执行指令的目的寄存器进行对比

     如果存在寄存器id值相等的情况,就说明指令之间存在数据相关,那么该指令就要在译码阶段等待
    
  • 方式:
    通过插入气泡来代替暂停的指令

     气泡不会改变寄存器、内存、条件码以及程序状态
    
  • 用途:

    解决数据冒险

     基于这种机制实现的流水线性能并不高,频繁暂停,降低流水线的吞吐量
    
  • 优化:

    直接将运算结果进行传递,数据转发,但对于访存指令,数据比较靠后,必须使用暂停再转发

     使得流水线不用暂停就可以处理大多数情况的数据冒险
    

2.控制冒险

  • 问题:
    由于需要每一次取值操作后,必须马上确定下一条指令的地址

     当取出指令为ret时,下一条指令需要从栈中读出,因此必须等到访存操作结束后才能确定下一条指令的地址
     当取到的指令是分支条件指令时,流水线无法立即进行立即判断是否进行跳转,通过执行阶段后才能确定跳转
    
  • 方式:
    暂停执行新指令

3.跳转问题

  • 策略:
    预测分支总是跳转或者总是不跳转

  • 解决方式:
    对于需要冲刷的第一条指令,在执行阶段插入气泡,对于第二条,在译码阶段插入气泡,还要取出跳转指令后面的指令

4.实现

  • 为每个流水线寄存器引入两个控制信号,分别为暂停信号和气泡信号

  • 当需要暂停时,将暂停信号设为 1,寄存器就会保持以前的状态,就实现指令阻塞在流水线的某个阶段中

  • 当需要插入气泡时,寄存器的状态会设置成某个固定的复位配置,复位配置等效于指令nop的状态

在这里插入图片描述

代码

#include<stdio.h>
#include<stdint.h>
#include<string.h>
/*
寄存器编号
*/
enum Reg {
	rax = 0,
	rcx = 1,
	rdx = 2,
	rbx = 3,
	rsp = 4,
	rbp = 5,
	rsi = 6,
	rdi = 7,
	r8 = 8,
	r9 = 9,
	r10 = 0xA,
	r11 = 0xB,
	r12 = 0xC,
	r13 = 0xD,
	r14 = 0xE,
	No_regisEer = 0xF,
};
const uint8_t PC_memory[32768] = { 0x30,0xf2,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00		//1  0x000 : irmovq  $9,   %rdx
								  ,0x30,0xf3,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00		//2  0x00a	: irmovq  $21,  %rbx
								  ,0x61,0x23												//3  0x014	: subq   %rdx,  %rbx
								  ,0x30,0xf4,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00		//4  0x016	: irmovq  $128, %rsp
								  ,0x40,0x43,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00		//5  0x020	: rmmovq %rsp,  100(%rbx)
								  ,0xa0,0x2f												//6  0x02a	: pushq  %rdx 
								  ,0xb0,0x0f												//7  0x02c	: popq   %rax  
								  ,0x73,0x37,0x00,0x00,0x00,0x00,0x00,0x00,0x00				//8  0x02e	: je done
								  ,0x80,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00				//9  0x037	: call proc
																							//10  0x040	: done:
								  ,0x00														//    0x040	:     halt
																							//11  0x041	: proc:
								  ,0x90 };													//    0x041	:     ret
struct FReg {
	int predPC;
};
struct SelectPC {			//PC选择逻辑单元,预测与纠错
	int predpc;				//经过predicatePC单元,判断jxx,call跳转 还是 其他顺序执行PCinc单元(valp),预测
	int cnd;				//执行jxx valc的限制条件,默认跳转,cnd=0  顺序执行valp					,修正jxx
	int M_valA;				//通过selectA将valp放入valA里了										
	int M_icode;
	int W_valM;				//ret默认顺序执行valp,待从栈Datamemory中读地址后更新					,修正ret
	int W_icode;
};
struct F_DReg {
	int bubble;
	int stall;
	int stat;
	int icode;
	int ifuc;
	int rA;
	int rB;
	int valC;
	int valP;
};
struct D_EReg {
	int bubble;
	int stall;
	int stat;
	int icode;
	int ifuc;
	int valC;
	int valA;
	int valB;
	int dstE;
	int dstM;
	int srcA;
	int srcB;
};
struct Cache {
	int valid;
	int Tag;
	int Cache_block;
}Set[16];
struct Address {
	int Tag;
	int Set_index;
}Address;
struct FwdB {
	int register_B;
	int srcB;
	int E_dstE;
	int E_valE;
	int M_dstE;
	int M_valE;
	int M_dstM;
	int M_valM;
	int W_dstM;
	int W_valM;
	int W_dstE;
	int W_valE;
	int valB;
};
struct SelectA_and_FwdA {
	int register_A;
	int srcA;
	int valP;
	int E_dstE;
	int E_valE;
	int M_dstE;
	int M_valE;
	int M_dstM;
	int M_valM;
	int W_dstM;
	int W_valM;
	int W_dstE;
	int W_valE;
	int valA;
};
struct E_MReg {
	int bubble;
	int stall;
	int stat;
	char icode;
	int Cnd;
	int valE;
	int valA;
	int dstE;
	int dstM;
};
struct CC {
	int ZF;
	int SF;
	int OF;
};
struct M_WReg {
	int bubble;
	int stall;
	int stat;
	int icode;
	int valE;
	int valM;
	int dstE;
	int dstM;
};
struct Inst {
	int icode;
	int ifunct;
	int rA_id;
	int rB_id;
	int valC;
	int valP;
	int len;
};
struct Inst inst = { 0,0,0,0,0,0,0 };
struct F_DReg f_dReg = { 0,0,1,0,0,0,0,0,0 };
struct D_EReg d_eReg = { 0,0,1,0,0,0,0,0,0,0,0,0 };
struct E_MReg e_mReg = { 0,0,1,0,0,0,0,0,0 };
struct M_WReg m_wReg = { 0,0,1,0,0,0,0xF,0xF };
struct F_DReg f_dRegNew = { 0,0,1,0,0,0,0,0,0 };
struct D_EReg d_eRegNew = { 0,0,1,0,0,0,0,0,0,0,0,0 };
struct E_MReg e_mRegNew = { 0,0,1,0,0,0,0,0,0 };
struct M_WReg m_wRegNew = { 0,0,1,0,0,0,0xF,0xF };
int Register_file[16] = { 0 };
int Datamemory[32768] = { 0 };
struct CC cc = { 0 };
struct FReg fReg = { 0 };
struct SelectPC selectpc = { 0,1,0,0,0,0 };
struct FwdB fwdb = { 0 };
struct SelectA_and_FwdA sel_fwda = { 0 };
/*
1.用暂停来避免冒险
只要一条指令的源操作数会被流水线后面某个阶段中的指令产生,处理器就会通过将指令阻塞在解码阶段来避免数据冒险
2.用转发来避免冒险-----》不需要暂停,但与存储器读有关的指令,是在流水线较后面发生的,仍需暂停
如mr 0(%edx),%eax 指令后紧接着add %ebx,&eax ;add的%eax需要在译码阶段得到值,而mr最早到访存阶段才能转发
显然已经来不及,所以不得不先暂停后转发
3.暂停+转发
将解码阶段中的指令暂停一个周期,导致执行阶段中插入一个气泡,同时暂停,使得F和D状态不变

需要解决的控制逻辑:插入气泡
1.加载使用冒险:1个气泡
	在一条从存储器中读出一个值的指令和一条使用该值的指令之间,流水线必须暂停一个周期
2. ret:3个气泡
	流水线必须暂停直到ret指令到达写回阶段
3. 预测错误的分支:两个气泡
	在分支逻辑发现不应该选择分支之前(执行阶段才发现),分支目标处的几条指令已经进入流水线了。
	必须从流水线中去掉这些指令。
	此时第一条指令已经执行到译码阶段,第二条指令刚开始取指。


具体实现:
定义F_DReg 和F_DRegNew  D_EReg 和D_ERegNew   E_MReg 和E_MRegNew   M_WReg 和M_WRegNew
执行过程中:
	F:输入pc      输出 F_DRegNew;正常情况下需要更新  将F_DRegNew---->F_DReg,否则不变
	D:输入F_DReg  输出 D_ERegNew;
	E: 输入D_EReg  输出 E_MRegNew;
	M: 输入E_MReg  输出 M_WRegNew
	W: 输入M_WReg
每次执行完需要判断:
	需要暂停,就使用原状态F_DReg
	正常情况,每次执行完一个周期需要更新
		F_DReg = F_DRegNew

*/

//分支预测
void Select_PC() {
	if (selectpc.W_icode == 9) {	//ret生效
		printf("ret生效\n");
		selectpc.predpc = selectpc.W_valM;
		printf("selectpc.predpc =%d \n", selectpc.predpc);
	}
	else if (selectpc.W_icode == 7) {
			if (selectpc.cnd == 0) {	//跳转失败
				selectpc.predpc = selectpc.M_valA;
			}
	}
}
//取值阶段
int Predict_PC(int icode, int valc, int valp) {
	if (icode == 7 || icode == 8) {
		return valc;
	}
	else {
		return valp;
	}
}
int PC_inc(int len, int pc) {
	return len + pc;
}
int iValC(int valC_start, int valC_end) {
	int i, j, valc = 0;
	for (j = 0, i = valC_start; i <= valC_end; i++, j = j + 2)
	{
		valc += PC_memory[i] << j;
	}
	return valc;
}
void Align(int pc,int Need_regids, int Need_valC) {
	int valC_start;
	int valC_end;

	if (Need_regids == 0) {
		inst.rA_id = 0xF;
		inst.rB_id = 0xF;
		if (Need_valC) {	//无寄存器、有常数  如jxx、call
			valC_start = pc + 1;
			valC_end = pc + 8;
			inst.len = 1 + (valC_end - pc);
			inst.valC = iValC(valC_start, valC_end);
		}
		else {				//无寄存器、无常数  如halt、nop、ret
			inst.len = 1;
		}
	}
	if (Need_regids == 1) {
		inst.rA_id = 0xF;
		inst.rB_id = PC_memory[pc + 1] & 0x0F;
		if (Need_valC) {	//只有寄存器rB、有常数  如irmovq
			valC_start = pc + 2;
			valC_end = pc + 9;
			inst.len = 1 + (valC_end - pc);
			inst.valC = iValC(valC_start, valC_end);
		}
		//只有寄存器rB、无常数  此情况无
	}
	if (Need_regids == 2) {
		inst.rA_id = PC_memory[pc + 1] >> 4;
		inst.rB_id = 0xF;
		//只有寄存器rA、有常数  此情况无
		if (!Need_valC) {	//有寄存器rA、无常数  如pushq、popq
			inst.len = 2;
		}
	}
	if (Need_regids == 3) {
		inst.rA_id = PC_memory[pc + 1] >> 4;
		inst.rB_id = PC_memory[pc + 1] & 0x0F;
		if (Need_valC) {	//都有寄存器、有常数  如rrmovq、rmmovq、mrmovq
			valC_start = pc + 2;
			valC_end = pc + 9;
			inst.len = 1 + (valC_end - pc);
			inst.valC = iValC(valC_start, valC_end);
		}
		else {
			// 都有寄存器、无常数  如addq、subq、andq、xorq、rrmovq、rrmovq、cmovle、cmovl、cmove、cmovne、cmovge、cmovg
			inst.len = 2;
		}
	}

}
void Split(int pc) {
	int Need_valC;
	int Need_regids;					//0:都为空;1:rA为空;2:rB为空;3:都不为空
	uint8_t code = PC_memory[pc];
	uint8_t reg = PC_memory[pc + 1];
	inst.icode = (uint8_t)(code >> 4);
	inst.ifunct = code & 0x0F;
	if (inst.icode == 0 && inst.ifunct == 0) {
		Need_regids = 0;
		Need_valC = 0;
		Align(pc,Need_regids, Need_valC);
	}
	if (inst.icode == 1 && inst.ifunct == 0) {
		Need_valC = 0;
		Need_regids = 0;
		Align(pc,Need_regids, Need_valC);
	}
	if (inst.icode == 2) {
		Need_valC = 0;
		Need_regids = 3;
		Align(pc,Need_regids, Need_valC);
	}
	if (inst.icode == 3 && inst.ifunct == 0) {
		Need_valC = 1;
		Need_regids = 1;
		Align(pc, Need_regids, Need_valC);
	}
	if (inst.icode == 4 && inst.ifunct == 0) {
		Need_valC = 1;
		Need_regids = 3;
		Align(pc,Need_regids, Need_valC);
	}
	if (inst.icode == 5 && inst.ifunct == 0) {
		Need_valC = 1;
		Need_regids = 3;
		Align(pc, Need_regids, Need_valC);
	}
	if (inst.icode == 6) {
		Need_valC = 0;
		Need_regids = 3;
		Align(pc, Need_regids, Need_valC);
	}
	if (inst.icode == 7) {
		Need_valC = 1;
		Need_regids = 0;
		Align(pc,Need_regids, Need_valC);
	}
	if (inst.icode == 8 && inst.ifunct == 0) {
		Need_valC = 1;
		Need_regids = 0;
		Align(pc,Need_regids, Need_valC);
	}
	if (inst.icode == 9 && inst.ifunct == 0) {
		Need_valC = 0;
		Need_regids = 0;
		Align(pc,Need_regids, Need_valC);
	}
	if (inst.icode == 0xA && inst.ifunct == 0) {
		Need_valC = 0;
		Need_regids = 2;
		Align(pc, Need_regids, Need_valC);
	}
	if (inst.icode == 0xB && inst.ifunct == 0) {
		Need_valC = 0;
		Need_regids = 2;
		Align(pc, Need_regids, Need_valC);
	}
	if (!(inst.icode >= 0 && inst.icode <= 11)) {
		f_dReg.stat = 0;
	}
}
void Fetch(int pc) {
	Split(pc);
	inst.valP = PC_inc(inst.len, pc);
	fReg.predPC = Predict_PC(inst.icode, inst.valC, inst.valP);
	selectpc.predpc = fReg.predPC;
	f_dRegNew.bubble = 0;
	f_dRegNew.stall = 0;
	f_dRegNew.icode = inst.icode;
	f_dRegNew.ifuc = inst.ifunct;
	f_dRegNew.rA = inst.rA_id;
	f_dRegNew.rB = inst.rB_id;
	f_dRegNew.valC = inst.valC;
	f_dRegNew.valP = inst.valP;
}

//译码阶段
/*
1.不是所以指令都需要数据转发,而且数据转发阶段也不同:(不需要回写寄存器的指令,不需要转发)
		执行阶段:(ALU计算结果)						  valE:	 3irmov 6opq 2rrmov(有条件) Apush Bpop 8call 9ret(注意:回写valE到SP寄存器)
		访存前:	 (对寄存器写入端口E还没有进行写入的数据)valE: 就是执行阶段的指令
		访存后有:(内存的输出数据)					  valM: 5mrmov(有点晚) Bpop (从Datememory中读出数据后才回写)
		写回阶段:(对寄存器写入端口E还没进行写入的数据)  valE:就是执行阶段的指令
				 (对寄存器写入端口M还没有进行写入的数据)valM: 就是访存后指令
2.rB寄存器作为源寄存器时,需要与rB作为目的寄存器匹配验证的指令很少。有指令:
		opq: rA,rB->rB
		mrmov:D(rB),rA
3.rA寄存器作为源寄存器时,需要与rA作为目的寄存器匹配验证。rA源寄存器有指令:
		特别注意:jxx和call指令要经过selectA,选出的是valP,不是来自寄存器的ra值,所以不需要匹配验证
		mr指令也不需要
*/
//数据转发
void SelectA_and_FwdA() {

	if (sel_fwda.srcA == 0xF) {
		sel_fwda.valA = sel_fwda.valP;
	}
	else {
		if (sel_fwda.srcA == sel_fwda.E_dstE) {
			sel_fwda.valA = sel_fwda.E_valE;
		}
		else if (sel_fwda.srcA == sel_fwda.M_dstE) {
			sel_fwda.valA = sel_fwda.M_valE;
		}
		else if (sel_fwda.srcA == sel_fwda.M_dstM) {
			sel_fwda.valA = sel_fwda.M_valE;
		}
		else if (sel_fwda.srcA == sel_fwda.W_dstE) {
			sel_fwda.valA = sel_fwda.W_valE;
		}
		else if (sel_fwda.srcA == sel_fwda.W_dstM) {
			sel_fwda.valA = sel_fwda.W_valM;
		}
		else {
			sel_fwda.valA = sel_fwda.register_A;
		}
	}

}
void FwdB() {
	if (fwdb.srcB == fwdb.E_dstE) {
		fwdb.valB = fwdb.E_valE;
	}
	else if (fwdb.srcB == fwdb.M_dstE) {
		fwdb.valB = fwdb.M_valE;
	}
	else if (fwdb.srcB == fwdb.M_dstM) {
		fwdb.valB = fwdb.M_valE;
	}
	else if (fwdb.srcB == fwdb.W_dstE) {
		fwdb.valB = fwdb.W_valE;
	}
	else if (fwdb.srcB == fwdb.W_dstM) {
		fwdb.valB = fwdb.W_valM;
	}
	else {
		fwdb.valB = fwdb.register_B;
	}
}
void Decode(int Register_file[]) {
	if (f_dReg.stall) {
		printf("Decode: Stall\n");
		//调整pc=pc-inst.len
		inst.valP = inst.valP - inst.len;
		printf("跳出译码阶段\n");
		return;
	}
	if (f_dReg.bubble) {
		printf("Decode: Bubble\n");
		d_eRegNew.bubble = 1;
	}
	printf("进入译码阶段\n");
	d_eRegNew.stall = 0;
	d_eRegNew.stat = f_dReg.stat;
	d_eRegNew.icode = f_dReg.icode;
	d_eRegNew.valC = f_dReg.valC;
	d_eRegNew.ifuc = f_dReg.ifuc;
	if (f_dReg.icode == 0 || f_dReg.icode == 1) {
		;
	}
	if (f_dReg.icode == 2) {	//rr
		d_eRegNew.srcA = f_dReg.rA;

		sel_fwda.srcA = f_dReg.rA;
		sel_fwda.register_A = Register_file[f_dReg.rA];
		SelectA_and_FwdA(&sel_fwda);
		d_eRegNew.valA = sel_fwda.valA;

		d_eRegNew.srcB = f_dReg.rB;
		d_eRegNew.valB = 0;

		d_eRegNew.dstE = f_dReg.rB;		//将结果valE写回寄存器rB
	}
	if (f_dReg.icode == 3) {	//ir
		d_eRegNew.srcA = f_dReg.rA;
		d_eRegNew.srcB = f_dReg.rB;
		d_eRegNew.valB = 0;
		d_eRegNew.dstE = f_dReg.rB;		//将ALU结果valE写回寄存器rB
	}
	if (f_dReg.icode == 4) {	//rm  rA,D(rB)  不需要写回寄存器
		d_eRegNew.srcA = f_dReg.rA;

		sel_fwda.srcA = f_dReg.rA;
		sel_fwda.register_A = Register_file[f_dReg.rA];
		SelectA_and_FwdA();
		d_eRegNew.valA = sel_fwda.valA;

		d_eRegNew.srcB = f_dReg.rB;  //对rB去寄存器取基地址
		fwdb.srcB = f_dReg.rB;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB();
		d_eRegNew.valB = fwdb.valB;
	}
	if (f_dReg.icode == 5) {	//mr  D(rB),rA   需要验证
		d_eRegNew.srcB = f_dReg.rB;		//对rB去寄存器取基地址
		fwdb.srcB = f_dReg.rB;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB(&fwdb);
		d_eRegNew.valB = fwdb.valB;


		d_eRegNew.dstM = f_dReg.rA;		//将Datamemory结果valM写回寄存器rA
	}
	if (f_dReg.icode == 6) {	//OPq rA,rB		 需要验证
		sel_fwda.srcA = f_dReg.rA;
		sel_fwda.register_A = Register_file[f_dReg.rA];
		SelectA_and_FwdA(&sel_fwda);
		d_eRegNew.valA = sel_fwda.valA;

		fwdb.srcB = f_dReg.rB;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB(&fwdb);
		d_eRegNew.valB = fwdb.valB;

		d_eRegNew.dstE = f_dReg.rB;		//将ALU结果valE写回寄存器rB
	}
	if (f_dReg.icode == 7) {	//jxx     
		sel_fwda.srcA = f_dReg.rA;
		sel_fwda.register_A = Register_file[f_dReg.rA];
		SelectA_and_FwdA();
		d_eRegNew.valA = sel_fwda.valA;
	}
	if (f_dReg.icode == 8) {	//call
		d_eRegNew.srcA = 4;
		sel_fwda.srcA = 4;
		sel_fwda.register_A = Register_file[f_dReg.rA];
		SelectA_and_FwdA();
		d_eRegNew.valA = sel_fwda.valA;

		fwdb.srcB = 4;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB();
		d_eRegNew.valB = fwdb.valB;

		d_eRegNew.dstE = 4;		//将ALU结果valE写回寄存器sp
	}
	if (f_dReg.icode == 9) {	//ret
		d_eRegNew.srcA = 4;				//对sp去寄存器取数据 
		sel_fwda.srcA = 4;
		sel_fwda.register_A = Register_file[f_dReg.rA];
		SelectA_and_FwdA();
		d_eRegNew.valA = sel_fwda.valA;

		d_eRegNew.srcB = 4;
		fwdb.srcB = 4;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB();
		d_eRegNew.valB = fwdb.valB;

		d_eRegNew.dstE = 4;		//将ALU结果valE写回寄存器sp
	}
	if (f_dReg.icode == 0xA) {//push
		d_eRegNew.srcA = f_dReg.rA;

		sel_fwda.srcA = f_dReg.rA;		//对rA去寄存器取数据
		sel_fwda.register_A = Register_file[f_dReg.rA];
		SelectA_and_FwdA();
		d_eRegNew.valA = sel_fwda.valA;

		d_eRegNew.srcB = 4;
		fwdb.srcB = 4;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB();
		d_eRegNew.valB = fwdb.valB;

		d_eRegNew.dstE = 4;		//将ALU结果valE写回寄存器sp
	}
	if (f_dReg.icode == 0xB) {//popq
		d_eRegNew.srcA = 4;				//对sp去寄存器取数据 
		sel_fwda.srcA = 4;
		sel_fwda.register_A = Register_file[f_dReg.rA];
		SelectA_and_FwdA();
		d_eRegNew.valA = sel_fwda.valA;

		d_eRegNew.srcB = 4;				//对sp去寄存器取数据
		fwdb.srcB = 4;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB();
		d_eRegNew.valB = fwdb.valB;

		d_eRegNew.dstE = 4;		//将ALU结果valE写回寄存器sp
		d_eRegNew.dstM = f_dReg.rA;		//将Datamemory结果valM写回寄存器rA
	}
}
//执行阶段
void setCC(int a, int b, int result, int op) {
	if (op == 1) {	//OF=1   两个同符号数相加(正数+正数 或 负数+负数),结果符号与其相反。
		if ((a >= 0 && b >= 0) && result < 0) {
			cc.OF = 1;
		}
		else if ((a < 0 && b < 0) && result >= 0) {
			cc.OF = 1;
		}
		else {
			cc.OF = 0;
		}
	}
	if (op == 2) {	//OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
		if ((a >= 0 && b < 0) && result >= 0) {
			cc.OF = 1;
		}
		else if ((a < 0 && b >= 0) && result < 0) {
			cc.OF = 1;
		}
		else {
			cc.OF = 0;
		}
	}
	if (op == 3 || op == 4) {
		cc.OF = 0;
	}
	if (result < 0) {
		cc.SF = 1;
	}
	else {
		cc.SF = 0;
	}
	if (result) {
		cc.ZF = 1;
	}
	else {
		cc.ZF = 0;
	}
}
int ALU(int alu_a, int alu_b, int op) {
	if (op == 1)
		return alu_a + alu_b;
	else if (op == 2)
		return alu_a - alu_b;
	else if (op == 2)
		return alu_a & alu_b;
	else
		return alu_a ^ alu_b;
}
int Cond(int ifunc) {
	if (ifunc == 1) {
		return (cc.SF ^ cc.OF) | cc.ZF;
	}
	if (ifunc == 2) {
		return cc.SF ^ cc.OF;
	}
	if (ifunc == 3) {
		return cc.ZF;
	}
	if (ifunc == 4) {
		return ~cc.ZF;
	}
	if (ifunc == 5) {
		return ~(cc.SF ^ cc.OF);
	}
	if (ifunc == 6) {
		return ~(cc.SF ^ cc.OF) & ~cc.ZF;
	}
	return 1;
}
void Excecute() {
	int ALU_A;
	int ALU_B;
	if (d_eReg.stall) {
		printf("Execute: Stall\n");
		e_mRegNew.bubble = 1; \
			return;
	}
	if (d_eReg.bubble) {
		printf("Execute: Bubble\n");
		e_mRegNew.bubble = 1;
	}
	e_mRegNew.stat = d_eReg.stat;
	e_mRegNew.icode = d_eReg.icode;
	e_mRegNew.valA = d_eReg.valA;
	e_mRegNew.dstM = d_eReg.dstM;
	int op = 1;//1:add,2:sub,3:and,4:xor
	if (d_eReg.icode == 0) {//				不提供数据转发
		e_mRegNew.dstE = 0xF;
	}
	if (d_eReg.icode == 1) {//				不提供数据转发
		e_mRegNew.dstE = 0xF;
	}
	if (d_eReg.icode == 2) {//rr		如果条件满足才提供数据转发
		ALU_A = d_eReg.valA;
		ALU_B = d_eReg.valB;
		op = 1;
		e_mRegNew.valE = ALU(ALU_A, ALU_B, op);
		fwdb.E_valE = ALU(ALU_A, ALU_B, op);
		sel_fwda.E_valE = ALU(ALU_A, ALU_B, op);
		if (d_eReg.ifuc == 0) {
			e_mRegNew.Cnd = 1;
		}
		if (d_eReg.ifuc == 1) {
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 2) {
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 3) {
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 4) {
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 5) {
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 6) {
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (e_mRegNew.Cnd) {
			e_mRegNew.dstE = d_eReg.dstE;
			fwdb.E_dstE = d_eReg.dstE;
			sel_fwda.E_dstE = d_eReg.dstE;
		}
		else {
			e_mRegNew.dstE = 0xF;
			fwdb.E_dstE = 0xF;
			sel_fwda.E_dstE = 0xF;
		}
	}
	if (d_eReg.icode == 3) {//ir			 提供数据转发
		op = 1;
		ALU_A = d_eReg.valC;
		ALU_B = d_eReg.valB;
		printf("执行立即数加法\n");
		e_mRegNew.valE = ALU(ALU_A, ALU_B, op);
		fwdb.E_valE = ALU(ALU_A, ALU_B, op);
		sel_fwda.E_valE = ALU(ALU_A, ALU_B, op);
		fwdb.E_dstE = d_eReg.dstE;
		sel_fwda.E_dstE = d_eReg.dstE;
		e_mRegNew.dstE = d_eReg.dstE;
	}
	if (d_eReg.icode == 4) {//rm			不提供数据转发
		op = 1;
		ALU_A = d_eReg.valC;
		ALU_B = d_eReg.valB;
		e_mRegNew.valE = ALU(ALU_A, ALU_B, op);
		e_mRegNew.dstE = 0xF;
	}
	if (d_eReg.icode == 5) {//mr			只有访存后才数据转发
		op = 1;
		ALU_A = d_eReg.valC;
		ALU_B = d_eReg.valB;
		e_mRegNew.valE = ALU(ALU_A, ALU_B, op);
		e_mRegNew.dstE = d_eReg.dstE;
		f_dRegNew.stall = 2;
		d_eRegNew.stall = 2;
		e_mRegNew.bubble = 1;
	}
	if (d_eReg.icode == 6) {//Opq rr		提供数据转发
		ALU_A = d_eReg.valA;
		ALU_B = d_eReg.valB;
		e_mRegNew.dstE = d_eReg.dstE;
		if (d_eReg.ifuc == 0) {
			op = 1;
		}
		if (d_eReg.ifuc == 1) {
			op = 2;
		}
		if (d_eReg.ifuc == 2) {
			op = 3;
		}
		if (d_eReg.ifuc == 3) {
			op = 4;
		}
		e_mRegNew.valE = ALU(ALU_A, ALU_B, op);
		fwdb.E_valE = ALU(ALU_A, ALU_B, op);
		fwdb.E_dstE = d_eReg.dstE;
		fwdb.E_valE = ALU(ALU_A, ALU_B, op);
		fwdb.E_dstE = d_eReg.dstE;
		sel_fwda.E_valE = ALU(ALU_A, ALU_B, op);
		sel_fwda.E_dstE = d_eReg.dstE;
		setCC(ALU_A, ALU_B, e_mRegNew.valE, op);
	}
	if (d_eReg.icode == 7) {//jxx			不提供数据转发
		if (d_eReg.ifuc == 0) {				//jmp
			e_mRegNew.Cnd = 1;
		}
		if (d_eReg.ifuc == 1) {				//jle  (SF ^ OF) | ZF
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 2) {				//jl   SF ^ OF
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 3) {				//je   ZF
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 4) {				//jne  ~ZF
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 5) {				//jge   ~(SF ^ OF)
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 6) {				//jg    ~(SF ^ OF) & ~ZF
			e_mRegNew.Cnd = Cond(d_eReg.ifuc);
		}
		f_dRegNew.bubble = 1;
		d_eRegNew.bubble = 1;
	}
	if (d_eReg.icode == 8 || d_eReg.icode == 0xA) {	//call push   valB=R[%rsp]   提供数组转发
		ALU_B = d_eReg.valB;
		ALU_A = -4;							//-8
		op = 1;
		e_mRegNew.valE = ALU(ALU_A, ALU_B, op);
		e_mRegNew.dstE = d_eReg.dstE;
		fwdb.E_valE = ALU(ALU_A, ALU_B, op);
		fwdb.E_dstE = d_eReg.dstE;
		sel_fwda.E_valE = ALU(ALU_A, ALU_B, op);
		sel_fwda.E_dstE = d_eReg.dstE;
	}
	if (d_eReg.icode == 9) {	//ret   valB=R[%rsp]  提供数组转发给fwda
		ALU_B = d_eReg.valB;
		ALU_A = 4;
		op = 1;
		e_mRegNew.valE = ALU(ALU_A, ALU_B, op);
		e_mRegNew.dstE = d_eReg.dstE;
		fwdb.E_valE = ALU(ALU_A, ALU_B, op);
		fwdb.E_dstE = d_eReg.dstE;
		sel_fwda.E_valE = ALU(ALU_A, ALU_B, op);
		sel_fwda.E_dstE = d_eReg.dstE;
		f_dRegNew.bubble = 1;
		d_eRegNew.bubble = 1;
		e_mRegNew.bubble = 1;
	}
	if (d_eReg.icode == 0xB) {	//pop  valB=R[%rsp]   提供数组转发  1.执行阶段valB=R[%rsp] 2.访存后 valM
		ALU_B = d_eReg.valB;
		ALU_A = 4;
		op = 1;
		e_mRegNew.valE = ALU(ALU_A, ALU_B, op);
		e_mRegNew.dstE = d_eReg.dstE;
		fwdb.E_valE = ALU(ALU_A, ALU_B, op);
		fwdb.E_dstE = d_eReg.dstE;
		sel_fwda.E_valE = ALU(ALU_A, ALU_B, op);
		sel_fwda.E_dstE = d_eReg.dstE;
	}
	selectpc.cnd = e_mRegNew.Cnd;
	selectpc.M_icode = e_mRegNew.icode;
	e_mRegNew.bubble = 0;
	e_mRegNew.stall = 0;
}
//访存阶段
void DataMemory(int addr, int Data, int* Datamemory, int MemoryControl) {
	if (MemoryControl == 1) {
		Address.Set_index = addr &0x0F;
		Address.Tag = addr & 0xFF;
		if (Set[Address.Set_index].valid && Set[Address.Set_index].Tag == Address.Tag) {
			m_wRegNew.valM = Set[Address.Set_index].Cache_block;
		}
		else {
			Set[Address.Set_index].valid = 1;
			Set[Address.Set_index].Tag = Address.Tag;
			Set[Address.Set_index].Cache_block = Datamemory[addr];
			m_wRegNew.valM = Set[Address.Set_index].Cache_block;
		}
	}
	else if (MemoryControl == 2) {		//写穿透
		Address.Set_index = addr &0x0F;
		Address.Tag = addr &0xFF;
		Datamemory[addr] = Data;
		Set[Address.Set_index].valid = 1;
		Set[Address.Set_index].Tag = Address.Tag;
		Set[Address.Set_index].Cache_block = Data;
	}
}
void MemoryAccess() {
	int MemoryControl = -1;	//0:不读不写;1:读有效,输入地址输出valM;2:写有效,输入地址数据
	int Addr = -1;
	int Data = -1;
	if (e_mReg.stall) {
		printf("Execute: Stall\n");
		return;
	}
	if (e_mReg.bubble) {
		printf("Execute: Bubble\n");
		m_wRegNew.bubble = 1;
	}
	m_wRegNew.icode = e_mReg.icode;
	m_wRegNew.stat = e_mReg.stat;
	m_wRegNew.valE = e_mReg.valE;
	m_wRegNew.dstE = e_mReg.dstE;
	m_wRegNew.dstM = e_mReg.dstM;
	if (e_mReg.icode == 0 || e_mReg.icode == 1 || e_mReg.icode == 2 || e_mReg.icode == 3) {
		MemoryControl = 0;
	}
	if (e_mReg.icode == 4 || e_mReg.icode == 0xA) {	//rm push M[valE] = valA
		MemoryControl = 2;
		Addr = e_mReg.valE;
		Data = e_mReg.valA;
	}
	if (e_mReg.icode == 8) {	//call M[valE] = valP
		MemoryControl = 2;
		Addr = e_mReg.valE;
		//		Data = e_mReg.valA;
		Data = f_dReg.valP;
	}
	if (e_mReg.icode == 5) {	//mr
		MemoryControl = 1;
		Addr = e_mReg.valE;

	}
	if (e_mReg.icode == 0xB) {	//pop M[valE] = valA
		MemoryControl = 1;
		Addr = e_mReg.valA;
	}
	if (e_mReg.icode == 9 ) {	//ret valP = M[valE]
		MemoryControl = 1;
		Addr = e_mReg.valA;
	}
	//数据传递
	if (e_mReg.icode == 2 || e_mReg.icode == 3 || e_mReg.icode == 6 || e_mReg.icode == 8 || e_mReg.icode == 9
		|| e_mReg.icode == 0xA || e_mReg.icode == 0xB) {
		fwdb.M_valE = e_mReg.valE;
		fwdb.M_dstE = e_mReg.dstE;
		sel_fwda.M_valE = e_mReg.valE;
		sel_fwda.M_dstE = e_mReg.dstE;
	}
	DataMemory(Addr, Data, Datamemory, MemoryControl);
	if (e_mReg.icode == 5 || e_mReg.icode == 0xB) {
		fwdb.M_valM = m_wRegNew.valM;
		fwdb.M_dstM = e_mReg.dstM;
		sel_fwda.M_valM = m_wRegNew.valM;
		sel_fwda.M_dstM = e_mReg.dstM;
	}
	selectpc.M_valA = e_mReg.valA;
	m_wRegNew.bubble = 0;
	m_wRegNew.stall = 0;
}
//写回阶段
void writeBack() {
	if (m_wReg.stall) {
		printf("WriteBack: stall\n");
		return;
	}
	if (m_wReg.bubble) {
		printf("WriteBack: Bubble\n");
		return;
	}
	if (m_wReg.dstE != 0xF)
		Register_file[m_wReg.dstE] = m_wReg.valE;
	if (m_wReg.dstM != 0xF)
		Register_file[m_wReg.dstM] = m_wReg.valM;
	if (m_wReg.icode == 2 || m_wReg.icode == 3 || m_wReg.icode == 6 || m_wReg.icode == 8 || m_wReg.icode == 9
		|| m_wReg.icode == 0xA || m_wReg.icode == 0xB) {
		fwdb.M_valE = m_wReg.valE;
		fwdb.M_dstE = m_wReg.dstE;
		sel_fwda.M_valE = m_wReg.valE;
		sel_fwda.M_dstE = m_wReg.dstE;
	}
	if (m_wReg.icode == 5 || m_wReg.icode == 0xB) {
		fwdb.M_valM = m_wReg.valM;
		fwdb.M_dstM = m_wReg.dstM;
		sel_fwda.M_valM = m_wReg.valM;
		sel_fwda.M_dstM = m_wReg.dstM;
	}
	selectpc.W_valM = m_wReg.valM;
	printf("selectpc.W_valM=%d\n", selectpc.W_valM);
	selectpc.W_icode = m_wReg.icode;
	printf("selectpc.W_icode=%d\n", selectpc.W_icode);
}
void initCache() {
	for (int i = 0; i < 16; i++)
	{
		Set[i].valid = 0;
	}
}
void test_cache() {
	for (int i = 0; i < 16; i++)
	{
		if (Set[i].valid) {
			printf("Set[%d].Cache_block:%d\n",i, Set[i].Cache_block);
		}
	}
}
//主函数
int main() {
	int i = 11;
	initCache();
	while (i--) {
		printf("第%d条指令开始执行", 11 - i);
		printf("开始预测\n");
		Select_PC();
		printf("PC = %d\n", selectpc.predpc);
		Fetch(selectpc.predpc);
		if (!f_dReg.stall) {
			f_dReg = f_dRegNew;
		}
		else {
			f_dReg.stall--;
		}
		Decode(&Register_file);
		if (!d_eReg.stall) {
			d_eReg = d_eRegNew;
		}
		else {
			d_eReg.stall--;
		}
		Excecute();
		e_mReg = e_mRegNew;
		MemoryAccess();
		m_wReg = m_wRegNew;
		writeBack();
		printf("Datamemory[%d]=%d\n", Register_file[4], Datamemory[Register_file[4]]);
		memset(&f_dRegNew, 0, sizeof(f_dRegNew));
		memset(&d_eRegNew, 0, sizeof(d_eRegNew));
		memset(&e_mRegNew, 0, sizeof(e_mRegNew));
		memset(&m_wRegNew, 0, sizeof(m_wRegNew));
	}
	printf("Datamemory[124]=%d", Datamemory[124]);
	//test_cache();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/134071.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL高级 索引【索引使用索引设计原则】

目录 1&#xff1a;索引使用 1.1&#xff1a;验证索引效率 1.2&#xff1a;最左前缀法则 1.3&#xff1a;范围查询&#xff08;存在索引失效的情况&#xff09; 1.4&#xff1a;索引失效情况 1.4.1&#xff1a;索引列运算&#xff08;索引会失效&#xff09; 1.4.2&…

第三十五讲:无线局域网基础知识

1. IEEE 802.11协议 802.11无线标准家族包括802.11a/b/g/n/ac五个标准理论上可以提供高达每秒1Gbit的数据传输能力标准定义了如何使用免授权2.4 GHz 和 5GHz 频带的电磁波进行信号传输。 802.11无线标准家族 802.11a 802.11b 802.11g 802.11n 802.11ac 工作频段 5GHz 2…

servelt的cookie操作

Cookie对象 Cookie是浏览器提供的一种技术&#xff0c;通过服务器的程序能将一些只须保存在客户端&#xff0c;或者在客户端进行处理的数据&#xff0c;放在本地的计算机上&#xff0c;不需要通过网络传输&#xff0c;因而提高网页处理的效率&#xff0c;并且能够减少服务器的…

Allegro如果通过CNS Show命令查看走线的阻抗操作指导

Allegro如果通过CNS Show命令查看走线的阻抗操作指导 Allegro可以通过CNS show的命令快速查看走线的阻抗,省去通过规则管理器查看的时间,如下图 具体操作如下 选择Display命令选择Constraint

使用资源绑定器获取属性配置文件中的内容(读取属性配置文件最简单的方法)

package com.javase.reflect;import java.util.ResourceBundle;/*** java.util包下提供了一个资源绑定器&#xff0c;便于获取属性配置文件中的内容&#xff0c;使用这种方式的时候&#xff0c;* 属性配置文件必须放在类路径下。该文件的文件名必须是 "*.properties&…

2022年度总结|我的CSDN成长历程

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 周榜第一&#xff0c;诚信互投 我正在参加年度博客之星评选&#xff0c;麻烦…

Leetcode:150. 逆波兰表达式求值(C++)

目录 问题描述&#xff1a; 实现代码和解析&#xff1a; 原理思路&#xff1a; 问题描述&#xff1a; 给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 示例 1&#xff1a; 输入&a…

Selenium用法详解【从入门到实战】【JAVA爬虫】

目录 简介 selenium安装 java使用 浏览器控制 修改窗口大小 窗口最大化 窗口全屏显示 浏览器前进&后退 浏览器刷新 浏览器打开新标签页 浏览器窗口切换 关闭标签页 关闭浏览器 浏览器页面截图 其他操作 简介 Selenium是一个用于Web应用程序测试的工具。Seleniu…

Redis发布订阅和事务实现原理

Redis发布订阅和事务实现原理发布订阅实现频道订阅与退订频道模式订阅与退订发送消息事务事务队列执行事务WATCH命令实现ACID原子性一致性隔离性持久性发布订阅 Redis的发布订阅由PUBLISH&#xff0c;SUBSCRIBE&#xff0c;PSUBSCRIBE等命令组成,例子如下: redis中我们还可以通…

三菱FX5U系列PLC通过简单CPU通信功能实现以太网通信的具体方法示例

三菱FX5U系列PLC通过简单CPU通信功能实现以太网通信的具体方法示例 对于三菱FX5U系列PLC,只需对CPU模块进行简单的参数设置,即可实现在指定时间内与指定软元件进行数据收发的功能。以1:1的方式设置通信对象(传送源)和通信对象(传送目标),在指定的通信对象之间进行数据的…

windows下TensorFlow-GPU 的安装教程

文章目录安装环境一. 查看自己的GPU版本是否支持cuda二 .安装CUDA三. 安装cuDNN安装环境 Anaconda: 4.10.1python: 3.8.8tensorflow-gpu: 2.5.0cuda: 11.4.0cudnn: 8.2.2.26 一. 查看自己的GPU版本是否支持cuda 打开显卡的控制面板&#xff0c;查看显卡是否支持cuda 查看te…

物联网-初步探索lua

初步探索lua 在物联网行业中&#xff0c;存在各种协议&#xff1b;比如在电控和云端进行通信的时候需要对功能进行解码和编码&#xff1b;当云端下发到设备的时候需要将Json格式的命令转换成电控码&#xff1b;当电控进行上报或者返回的时候&#xff0c;需要将16进制的电控码转…

(黑马C++)L06 重载与继承

一、关系运算符重载 以重载等于号运算符为例&#xff1a; #include<string> #include <iostream> using namespace std;class Person { public:Person(string Name, int age) {this->m_Name Name;this->m_Age age;}public:string m_Name;int m_Age; };bo…

SD存储卡接口规范介绍

SD存储卡简介 SD卡高度集成闪存&#xff0c;具备串行和随机存取能力。可以通过专用优化速度的串行接口访问&#xff0c;数据传输可靠。接口允许几个卡垛叠&#xff0c;通过他们的外部连接。接口完全符合最新的消费者标准&#xff0c;叫做SD卡系统标准&#xff0c;由SD卡系统规范…

用javascript分类刷leetcode14.排序算法(图文视频讲解)

常见排序算法复杂度 n^2除nlogn在不同数据规模下的结果 常见排序算法 算法可视化来源&#xff1a;http://visualgo.net/ 冒泡排序&#xff1a;时间复杂度O(n^2) 比较相邻元素&#xff0c;如果第一个比第二个大&#xff0c;则交换他们一轮下来&#xff0c;可以保证最后一个数…

Android入门第56天-在Android里使用OKHttp多线程下载文件并展示其进度

简介 OkHttp是一个神器。OkHttp分为异步、同步两种调用。今天我们就会基于OkHttp的异步调用实现一个多线程并行下载文件并以进度条展示总进度的实用例子。当然这不是我们的Android里使用OkHttp的最终目标&#xff0c;我们最终在下一篇中会在今天这一课的基础上加入“断点续传”…

【我在异世界学Linux】认识冯诺依曼体系结构

文章目录一、冯诺依曼体系结构是什么二、冯诺依曼为什么要这么设计&#xff1f;三、内存是怎么提高效率的呢&#xff1f;解释&#xff1a;程序要运行&#xff0c;必须加载到内存四、和QQ好友聊天的时候&#xff0c;数据是怎么流向的&#xff1f;一、冯诺依曼体系结构是什么 冯诺…

教你使用Java开发一款简单的扫雷小游戏 附实例代码

相信很多小伙伴都知道也玩过扫雷游戏,本篇文章将和大家分享一篇关于如何使用Java来实现一款简单的扫雷小游戏,这有助于大家对于Java相关知识的学习有一定的参考价值,下面是详情内容。 简介 学了几周的Java,闲来无事,写个乞丐版的扫雷,加强一下Java基础知识。 编写过程…

树莓派4b串口配置

从树莓派的相关资料我们可以看到&#xff0c;树莓派有两个串口可以使用&#xff0c;一个是硬件串口&#xff08;/dev/ttyAMA0&#xff09;,另一个是mini串口&#xff08;/dev/ttyS0&#xff09;。硬件串口有单独的波特率时钟源&#xff0c;性能好&#xff0c;稳定性强&#xff…

【Java寒假打卡】Java基础-接口

【Java寒假打卡】Java基础-接口接口的介绍接口的定义和特点接口中的成员特点JDK8 接口中的成员特点JDK9 接口中的成员特点类和接口的关系接口的介绍 &emsp&#xff1b;当一个类中的所有方法都是抽象方法的时候&#xff0c;我们就可以将其定义为接口&#xff0c;接口也是一…