手把手代码实现五级流水线CPU——第一篇:初级顺序流水线

news2024/11/17 13:43:12

文章目录

  • 指令系统
  • 编码格式
  • 一、基础:顺序结构
      • 1.取值阶段:
      • 2.译码阶段
      • 3.执行阶段
      • 4.访存阶段
      • 5.写回阶段
      • 6.更新PC阶段
      • 详细硬件结构
      • 指令在各个阶段完成的操作
  • C代码实现


指令系统

在这里插入图片描述

编码格式

在这里插入图片描述

一、基础:顺序结构

1.取值阶段:

  • 根据icode还可以判断当前指令是否包含寄存器指示符字节(Need regids);以及是否包含常数字节(Need valC),由此计算出指令的长度,进而计算下一条地址

  • 当need_regids等于0时,表示这条指令没有寄存器指示符,rA和rB置为0xF;当指令中只含有一个寄存器操作数时,同样另一个字段置为0xF

  • 当need_regids等于0时,第1个字节到第8个表示常数字段valC,1时,2~9

2.译码阶段

  • 产生寄存器ID值,需要指令代码icode和rA、rB读取寄存器的数据,rA用于pop(call、ret)指令指针值

3.执行阶段

  • ALU根据指令功能(ifun)来判断对输入的操作数进行何种运算,都会产生条件码信号,但对于计算内存引用地址以及栈操作时,不必设置条件码,通过设置setCC,可根据icode来控制是否要更新条件码寄存器CC;标号为cond的硬件单元会根据指令功能和条件码寄存器产生一个cnd信号
  • 对于跳转指令,如果cnd=1,执行跳转,否则不跳转

4.访存阶段

  • 就是从内存中读数据或者将数据写入内存中,根据信号计算状态码Stat
    读控制块,写控制块,产生内存地址和输入数据的控制块

5.写回阶段

  • 将数据写入寄存器文件
    两个写端口分别为M和E,对应的地址输入为dstE和dstM。当执行条件传送指令(cmov)时,写入操作还需根据执行阶段计算出的cnd信号;当不满足条件时,可以将目的寄存器设置为0xF来禁止写入寄存器文件

6.更新PC阶段

  • call指令
    常数字段
  • ret指令
    内存(栈)中读出返回地址
  • jxx跳转指令
    满足cnd,常数字段;否则,与其他一样,加上当前指令长度

详细硬件结构

在这里插入图片描述

指令在各个阶段完成的操作

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
更多指令操作请查找:Y86指令集

C代码实现

#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		//0x000 : irmovq  $9,   %rdx
								  ,0x30,0xf3,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00		//0x00a	: irmovq  $21,  %rbx
								  ,0x61,0x23												//0x014	: subq   %rdx,  %rbx
								  ,0x30,0xf4,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00		//0x016	: irmovq  $128, %rsp
								  ,0x40,0x43,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00		//0x020	: rmmovq %rsp,  100(%rbx)
								  ,0xa0,0x2f												//0x02a	: pushq  %rdx 
								  ,0xb0,0x0f												//0x02c	: popq   %rax  
								  ,0x73,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00				//0x02e	: je done
								  ,0x80,0x41,0x03,0x00,0x00,0x00,0x00,0x00,0x00				//0x037	: call proc
	//0x040	: done:
,0x00														//0x040	:     halt
//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 stat;
	int icode;
	int ifuc;
	int rA;
	int rB;
	int valC;
	int valP;
};
struct D_EReg {
	int stat;
	int icode;
	int ifuc;
	int valC;
	int valA;
	int valB;
	int dstE;
	int dstM;
	int srcA;
	int srcB;
};

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 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 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;
};

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(struct SelectPC* selectpc) {
	if (selectpc->W_icode == 9) {	//ret生效
		selectpc->predpc = selectpc->W_valM;
	}
	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,struct Inst* inst,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, struct Inst* inst, struct F_DReg* f_dReg) {
	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,inst, Need_regids, Need_valC);
	}
	if (inst->icode == 1 && inst->ifunct == 0) {
		Need_valC = 0;
		Need_regids = 0;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 2) {
		Need_valC = 0;
		Need_regids = 3;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 3 && inst->ifunct == 0) {
		Need_valC = 1;
		Need_regids = 1;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 4 && inst->ifunct == 0) {
		Need_valC = 1;
		Need_regids = 3;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 5 && inst->ifunct == 0) {
		Need_valC = 1;
		Need_regids = 3;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 6) {
		Need_valC = 0;
		Need_regids = 3;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 7) {
		Need_valC = 1;
		Need_regids = 0;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 8 && inst->ifunct == 0) {
		Need_valC = 1;
		Need_regids = 0;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 9 && inst->ifunct == 0) {
		Need_valC = 0;
		Need_regids = 0;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 0xA && inst->ifunct == 0) {
		Need_valC = 0;
		Need_regids = 2;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (inst->icode == 0xB && inst->ifunct == 0) {
		Need_valC = 0;
		Need_regids = 2;
		Align(pc, inst, Need_regids, Need_valC);
	}
	if (!(inst->icode >= 0 && inst->icode <= 11)) {
		f_dReg->stat = 0;
	}
}
void Fetch(struct F_DReg* f_dReg,int pc){
	struct Inst inst = {0,0,0,0,0,0,0};
	Split(pc,&inst, f_dReg);
	inst.valP = PC_inc(inst.len, pc);
	fReg.predPC = Predict_PC(inst.icode, inst.valC, inst.valP);
	selectpc.predpc = selectpc.predpc;
	f_dReg->icode = inst.icode;
	f_dReg->ifuc = inst.ifunct;
	f_dReg->rA = inst.rA_id;
	f_dReg->rB = inst.rB_id;
	f_dReg->valC = inst.valC;
	f_dReg->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(struct SelectA_and_FwdA* sel_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(struct FwdB* 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(struct F_DReg f_dReg, struct D_EReg* d_eReg, int* Register_file) {
	d_eReg->stat = f_dReg.stat;
	d_eReg->icode = f_dReg.icode;
	d_eReg->valC = f_dReg.valC;
	d_eReg->ifuc = f_dReg.ifuc;
	if (f_dReg.icode == 0 || f_dReg.icode == 1) {
		;
	}
	if (f_dReg.icode == 2) {	//rr
		d_eReg->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_eReg->valA = sel_fwda.valA;

		d_eReg->srcB = f_dReg.rB;
		d_eReg->valB = 0;

		d_eReg->dstE = f_dReg.rB;		//将结果valE写回寄存器rB
	}
	if (f_dReg.icode == 3) {	//ir
		d_eReg->srcA = f_dReg.rA;
		d_eReg->srcB = f_dReg.rB;
		d_eReg->valB = 0;
		d_eReg->dstE = f_dReg.rB;		//将ALU结果valE写回寄存器rB
	}
	if (f_dReg.icode == 4) {	//rm  rA,D(rB)  不需要写回寄存器
		d_eReg->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_eReg->valA = sel_fwda.valA;

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


		d_eReg->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_eReg->valA = sel_fwda.valA;

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

		d_eReg->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(&sel_fwda);
		d_eReg->valA = sel_fwda.valA;
	}
	if (f_dReg.icode == 8) {	//call
		d_eReg->srcA = 4;

		sel_fwda.srcA = 4;
		sel_fwda.register_A = Register_file[f_dReg.rA];
		SelectA_and_FwdA(&sel_fwda);
		d_eReg->valA = sel_fwda.valA;

		fwdb.srcB = 4;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB(&fwdb);
		d_eReg->valB = fwdb.valB;

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

		d_eReg->srcB = 4;
		fwdb.srcB = 4;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB(&fwdb);
		d_eReg->valB = fwdb.valB;

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

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

		d_eReg->srcB = 4;
		fwdb.srcB = 4;
		fwdb.register_B = Register_file[f_dReg.rB];
		FwdB(&fwdb);
		d_eReg->valB = fwdb.valB;

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

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

		d_eReg->dstE = 4;		//将ALU结果valE写回寄存器sp
		d_eReg->dstM = f_dReg.rA;		//将Datamemory结果valM写回寄存器rA
	}
}
//执行阶段
void setCC(struct CC* cc, 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(struct CC cc, 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;
	}
}
void Excecute(struct D_EReg d_eReg, struct E_MReg* e_mReg) {
	int ALU_A;
	int ALU_B;
	e_mReg->stat = d_eReg.stat;
	e_mReg->icode = d_eReg.icode;
	e_mReg->valA = d_eReg.valA;
	e_mReg->dstM = d_eReg.dstM;
	int op = 1;//1:add,2:sub,3:and,4:xor
	if (d_eReg.icode == 0) {//				不提供数据转发
		e_mReg->dstE = 0xF;
	}
	if (d_eReg.icode == 1) {//				不提供数据转发
		e_mReg->dstE = 0xF;
	}
	if (d_eReg.icode == 2) {//rr		如果条件满足才提供数据转发
		ALU_A = d_eReg.valA;
		ALU_B = d_eReg.valB;
		op = 1;
		e_mReg->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_mReg->Cnd = 1;
		}
		if (d_eReg.ifuc == 1) {
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 2) {
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 3) {
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 4) {
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 5) {
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 6) {
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (e_mReg->Cnd) {
			e_mReg->dstE = d_eReg.dstE;
			fwdb.E_dstE = d_eReg.dstE;
			sel_fwda.E_dstE = d_eReg.dstE;
		}
		else {
			e_mReg->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_mReg->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_mReg->dstE = d_eReg.dstE;
	}
	if (d_eReg.icode == 4) {//rm			不提供数据转发
		op = 1;
		ALU_A = d_eReg.valC;
		ALU_B = d_eReg.valB;
		e_mReg->valE = ALU(ALU_A, ALU_B, op);
		e_mReg->dstE = 0xF;
	}
	if (d_eReg.icode == 5) {//mr			只有访存后才数据转发
		op = 1;
		ALU_A = d_eReg.valC;
		ALU_B = d_eReg.valB;
		e_mReg->valE = ALU(ALU_A, ALU_B, op);
		e_mReg->dstE = d_eReg.dstE;
	}
	if (d_eReg.icode == 6) {//Opq rr		提供数据转发
		ALU_A = d_eReg.valA;
		ALU_B = d_eReg.valB;
		e_mReg->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_mReg->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(&cc, ALU_A, ALU_B, e_mReg->valE, op);
	}
	if (d_eReg.icode == 7) {//jxx			不提供数据转发
		if (d_eReg.ifuc == 0) {				//jmp
			e_mReg->Cnd = 1;
		}
		if (d_eReg.ifuc == 1) {				//jle  (SF ^ OF) | ZF
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 2) {				//jl   SF ^ OF
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 3) {				//je   ZF
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 4) {				//jne  ~ZF
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 5) {				//jge   ~(SF ^ OF)
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
		if (d_eReg.ifuc == 6) {				//jg    ~(SF ^ OF) & ~ZF
			e_mReg->Cnd = Cond(cc, d_eReg.ifuc);
		}
	}
	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_mReg->valE = ALU(ALU_A, ALU_B, op);
		e_mReg->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_mReg->valE = ALU(ALU_A, ALU_B, op);
		e_mReg->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 == 0xB) {	//pop  valB=R[%rsp]   提供数组转发  1.执行阶段valB=R[%rsp] 2.访存后 valM
		ALU_B = d_eReg.valB;
		ALU_A = 4;
		op = 1;
		e_mReg->valE = ALU(ALU_A, ALU_B, op);
		e_mReg->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_mReg->Cnd;
	selectpc.M_icode = e_mReg->icode;
}
//访存阶段
void DataMemory(int addr, int Data, int* Datamemory, int MemoryControl, struct M_WReg* m_wReg) {
	if (MemoryControl == 1) {
		m_wReg->valM = Datamemory[addr];
	}
	else if (MemoryControl == 2) {
		Datamemory[addr] = Data;
	}
}
void MemoryAccess(struct E_MReg e_mReg, struct M_WReg* m_wReg) {
	int MemoryControl = -1;	//0:不读不写;1:读有效,输入地址输出valM;2:写有效,输入地址数据
	int Addr = -1;
	int Data = -1;
	m_wReg->icode = e_mReg.icode;
	m_wReg->stat = e_mReg.stat;
	m_wReg->valE = e_mReg.valE;
	m_wReg->dstE = e_mReg.dstE;
	m_wReg->dstM = e_mReg.dstM;
	if (e_mReg.icode == 0 || e_mReg.icode == 1 || e_mReg.icode == 2) {
		MemoryControl = 0;
	}
	if (e_mReg.icode == 3 || e_mReg.icode == 8 || e_mReg.icode == 0xA) {	//rm push M[valE] = valA
		MemoryControl = 2;
		Addr = e_mReg.valE;
		Data = e_mReg.valA;
	}
	if (e_mReg.icode == 5) {	//mr
		MemoryControl = 1;
		Addr = e_mReg.valE;
	}
	if (e_mReg.icode == 9 || e_mReg.icode == 0xB) {	//ret pop M[valE] = valA
		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, m_wReg);
	if (e_mReg.icode == 5 || e_mReg.icode == 0xB) {
		fwdb.M_valM = m_wReg->valM;
		fwdb.M_dstM = e_mReg.dstM;
		sel_fwda.M_valM = m_wReg->valM;
		sel_fwda.M_dstM = e_mReg.dstM;
	}
	selectpc.M_valA = e_mReg.valA;
}
//写回阶段
void writeBack(struct M_WReg m_wReg) {
	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;
	selectpc.W_icode = m_wReg.icode;
}

//主函数
int main() {
	int i = 5;
	struct F_DReg f_dReg = { 1,0,0,0,0,0,0 };
	struct D_EReg d_eReg = { 1,0,0,0,0,0,0,0,0,0 };
	struct E_MReg e_mReg = { 1,0,0,0,0,0,0 };
	struct M_WReg m_wreg = { 1,0,0,0,0xF,0xF };

	while (i--) {
		if (!selectpc.predpc) {
			Select_PC(&selectpc);
		}
		Fetch(&f_dReg, selectpc.predpc);
		printf("取指阶段-------------------\n");
		printf("f_dReg.icode = %d\n", f_dReg.icode);
		printf("f_dReg.ifun = %d\n", f_dReg.ifuc);
		printf("f_dReg.rA = %d\n", f_dReg.rA);
		printf("f_dReg.rB = %d\n", f_dReg.rB);
		printf("f_dReg.valC = %d\n", f_dReg.valC);

		Decode(f_dReg, &d_eReg, &Register_file);
		printf("译码阶段-------------------\n");
		printf("d_eReg.icode = %d\n", d_eReg.icode);
		printf("d_eReg.ifun = %d\n", d_eReg.ifuc);
		printf("d_eReg.srcA = %d\n", d_eReg.srcA);
		printf("d_eReg.valA = %d\n", d_eReg.valA);
		printf("d_eReg.srcB = %d\n", d_eReg.srcB);
		printf("d_eReg.valB = %d\n", d_eReg.valB);
		printf("d_eReg.dstE = %d\n", d_eReg.dstE);
		printf("d_eReg.dstM = %d\n", d_eReg.dstM);
		printf("d_eReg.valC = %d\n", d_eReg.valC);

		Excecute(d_eReg, &e_mReg);
		printf("执行阶段--------------------\n");
		printf("e_mReg->valE = %d\n", e_mReg.valE);
		printf("e_mReg->dstE = %d\n", e_mReg.dstE);

		MemoryAccess(e_mReg, &m_wreg);
		printf("访存阶段----------------------\n");
		writeBack(m_wreg);
		printf("写回阶段----------------------\n");
		printf("rbx = %d\n", Register_file[3]);
	}


	return 0;
}

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

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

相关文章

【FPGA开发】Verilog 基础

写在前面&#xff1a;本章将对 Verilog 进行简要介绍&#xff0c;并对其基本特性进行讲解说明。之后&#xff0c;我们将按步骤演示如何使用 Vivado 创建简单项目。手动实践部分将根据我们提供的 .v 和 .tb 代码&#xff0c;跟着步骤跑出 Simulation 结果即可。 Ⅰ. Verilog 基础…

Odoo 16 企业版手册 - 库存管理之产品追溯

产品追溯 Odoo提供的产品可追溯性功能将有助于跟踪和跟踪产品的每个组件。在库存移动的每个阶段跟踪产品对于控制所有操作是必要的。为了确保有效监控库存的走势&#xff0c;批号和序列号发挥着重要作用。从制造过程到交付操作&#xff0c;产品可追溯性将保持适当的跟踪&#x…

Mixlab 的自我介绍

‍‍‍‍2022在探索元宇宙落地过程中&#xff0c;走过不少弯路&#xff0c;本着 “孵化” 的初心&#xff0c;我们将继续探索面向未来的社区模式。1 / Mixlab 无界社区社区即服务&#xff0c;以此作为基础&#xff0c;孵化各种形态的产品/服务。在2022的白皮书记录了我们做社区…

数据结构与算法—链表之单链表

文章目录链表单链表结构和特点创建添加修改删除2023年的第一篇文章在开发过程中&#xff0c;选择合适的数据结构是很重要的&#xff0c;可以快速处理数据的存储及使用问题。计划有时间慢慢系统的学习《数据结构与算法》&#xff0c;看看视频&#xff0c;练习实践&#xff0c;最…

国产FPGA应用--易灵思Programming Mode完全解析

本文介绍易灵思的几种配置模式&#xff0c;方便大家参考。 一、易灵思下载模式&#xff1a; 二、下载模式选择&#xff1a; 1、SPI Active mode 时序图如下&#xff1a; 2、SPI Passive Mode 时序图如下&#xff1a; SPI Active using JTAG Bridge 实际项目中&#xff0c;SPI…

锥度张力控制(收卷应用)

收卷、放卷应用系列文章可以参看下面的文章链接: 变频器简单张力控制(线缆收放卷应用)_RXXW_Dor的博客-CSDN博客_收放卷应用张力控制的开闭环算法,可以查看专栏的其它文章,链接地址如下:PLC张力控制(开环闭环算法分析)_RXXW_Dor的博客-CSDN博客。https://blog.csdn.ne…

excel筛选技巧:不用函数公式也能快速多对多查找

说到excel中的筛选&#xff0c;想必大家早已是了如指掌&#xff0c;不过增强版的筛选&#xff0c;你听说过吗&#xff1f;它可比普通的筛选厉害多了&#xff0c;不仅能实现excel中的一对多查找&#xff0c;就连复杂的多对多查找也不在话下&#xff01; 其实是使用公式还是用其…

原子性 以及悲观锁, 乐观锁

1. 前言 今天这篇文章要详细的说说&#xff0c;什么叫原子性&#xff0c;以及如果不是原子性的话&#xff0c;怎么能保证原子性。 2. 原子性 先说下并发编程的三大特性&#xff1a;可见性, 有序性, 原子性。 无论是在什么语言&#xff0c;原子性都是非常重要的&#xff0c;既然…

elasticsearch 的基本操作多维度分享

目录 一、索引操作 二、映射操作 三、文档操作 elasticsearch 的基本操作多维度分享此篇正式分享&#xff0c;具体包括索引、映射、文档的相关处理&#xff0c;模拟生成环境&#xff0c;通过DSL语句和java的高级REST形式全方位展示给大家&#xff1b; 一、索引操作 1、创建…

2023超好用的Mac清理优化工具CleanMyMacX

CleanMymac X Mac版本&#xff0c;以一种全面的方式扫描Mac系统以允许垃圾隐藏&#xff0c;您只需要轻松单击左鼠标按钮即可清洁数字G的垃圾&#xff0c;这是如此简单。立即提高您的MAC速度。为Apple System计算机建造可以帮助用户清理多种类型的垃圾和其他恶意束&#xff0c;提…

Sentinel 控制台安装与详解

Sentinel 控制台包含如下功能: 查看机器列表以及健康情况&#xff1a;收集 Sentinel 客户端发送的心跳包&#xff0c;用于判断机器是否在线。监控 (单机和集群聚合)&#xff1a;通过 Sentinel 客户端暴露的监控 API&#xff0c;定期拉取并且聚合应用监控信息&#xff0c;最终可…

AIGC + 任意应用情景组合,从技术层面给了大家体验不同领域的创作的机会

还在为学技术的时候面对一大堆教程苦恼&#xff1f;画画、剪辑、建模 ... 啥啥啥都想学 &#x1f92f;AIGC 来解决&#xff01;&#xff01;每个人都有机会当五分钟艺术家&#xff01;AIGC 究竟有多强大&#xff1f;简单用一个公式来概况 AIGC 的强大之处&#xff0c;就是 AIG…

Harbor 镜像仓库

目录 一、Harboar概述 1.1 什么是 Harbor 1.2 Harbor优势 1.3 Harbor构成 1.4 Harbor的误区 二、Harbor 安装&#xff08;http&#xff09; 2.1 两种方式 2.2 具体安装步骤 2.2.1 先安装Docker和Docker Compose 2.2.2 下载Harbor 2.2.3 harbor.yml 的hostname 2.2.…

梳理一下我在2022年读过的23本书

作者 | gongyouliu编辑 | gongyouliu2022年我一共看了23本书&#xff0c;比自己之前定的目标——每年看36本书——少了不少。今天特意花大半天时间写一篇文章来整理一下今年看的书&#xff0c;梳理一下自己的思路&#xff0c;也算是留下一份记录。这份书单也希望给大家作为参考…

从这两道题重新理解,JS的this、作用域、闭包、对象

日常开发中&#xff0c;我们经常用到this。例如用Jquery绑定事件时&#xff0c;this指向触发事件的DOM元素&#xff1b;编写Vue、React组件时&#xff0c;this指向组件本身。对于新手来说&#xff0c;常会用一种意会的感觉去判断this的指向。以至于当遇到复杂的函数调用时&…

spring、mybatis、spring-mybatis、springboot-mybatis的配置文件

第一个Spring程序 第一个Mybatis程序 第一个Spring-Mybatis程序 第一个SpringBoot-Mybatis程序 1. Spring程序配置文件 beans.xml&#xff1a;/resources <!--注册一个Bean--><bean id"hello" class"com.kuang.pojo.Hello"><property nam…

03 I2C

特点 两根线 SCL SDA同步、半双工带数据应答支持总线挂载多设备 一主多从 多主多从 两个功能&#xff1a; 读取外设模块指定位置的寄存器写外设模块指定位置的寄存器一个完整的通讯协议一定是在软件和硬件上同时定义的。 硬件 所有的I2C设备的时钟线与数据线都要连在一起设备…

普元技术专家2022历史文章合集

大家好&#xff0c;新年快乐。‍献上2022年历史文章合集&#xff0c;方便你翻阅过去一年里的精华文章。【第一辑】企业服务总线&#xff08;ESB&#xff09;基于ESB的企业服务集成平台建设之道企业服务总线建设之道的探索与研究零代码能力干掉80%开发工作&#xff1a;普元ESB 8…

论文综述——Event-Event Relation Extraction using Probabilistic Box Embedding

Event-Event Relation Extraction using Probabilistic Box Embedding1 任务介绍2 Box Embedding3 BERE模型4 实验5 总结1 任务介绍 事件关系抽取&#xff1a;文本中包含多个事件e1,e2,…,en&#xff0c;识别每个事件对(ei,ej)之间的关系r(ei,ej) 子事件(Subevent)关系抽取&a…

(一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>

一、工作环境及项目简介 立创EDA&#xff1a;硬件原理图及PCB绘制。 全志F1C200S&#xff1a;F1C100S内置32MB DDR1内存&#xff0c;F1C200S内置64MB DDR1内存。 原理图&#xff1a;参考开源项目&#xff0c;详见墨云&#xff0c; 详见peng-zhihui。 核心板&#xff1a;四层…