顺序处理器SEQ的HCL代码分析

目录

为什么要分析 

F阶段

D阶段

E阶段

M阶段 

更新PC阶段


为什么要分析 

顺序处理器SEQ的HCL代码分析_第1张图片

本书提供了SEQ和PIPE的HCL描述,完整的研究它们是很值得的。本文来分析SEQ的HCL代码。分析的方式就是以下面中的几个指令(rrmovqretpushq)为例,跟着HCL控制逻辑的六个阶段走一遍。

顺序处理器SEQ的HCL代码分析_第2张图片

F阶段

确定icode和ifun,这个都一样,分析写在注释里了。

################ 取指令阶段     ###################################

# 确定指令码(即前四位)
word icode = [
    imem_error: INOP;  # 如果这条指令的icode取失败了,对于这个字节就什么都不做了,视为nop
    1: imem_icode;  
];

# 确定指令函数(即后四位,jXX、cmovXX和OPq才有区别,其余都默认为0)
word ifun = [
	imem_error: FNONE; # 如果这条指令的icode取失败了,ifun自然也取不出来,icode视为INOP,ifun理应为默认值,即0
	1: imem_ifun;
];

然后是确定指令是否有效,把你这个处理器能处理的全部指令放进集合:

bool instr_valid = icode in 
	{ INOP, IHALT, IRRMOVQ, IIRMOVQ, IRMMOVQ, IMRMOVQ,
	       IOPQ, IJXX, ICALL, IRET, IPUSHQ, IPOPQ };

对于rrmovq来说,需要寄存器参数、无需常数参数。所以在下面两个集合中,分别是:有 、无。

对于ret来说,不需要寄存器参数,不需要常数参数,所以是:无、无。

对于pushq来说,需要寄存器参数,不需要常数参数,所以是:有、无。

bool need_regids =
	icode in { IRRMOVQ, IOPQ, IPUSHQ, IPOPQ, 
		     IIRMOVQ, IRMMOVQ, IMRMOVQ };


bool need_valC =
	icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ, IJXX, ICALL };

D阶段和W阶段

接下看看译码阶段。

对于rrmovq来说,只有一个读源,是rA。所以若icode是IRRMOVQ,那么srcA=rA,srcB=RNONE(RNONE的值为0xF,表明不需要访问寄存器 

对于ret来说,有两个读源,读出来两个%rsp。我猜应该是为了后续代码简便所以弄了个备份。此处待补,我认为一个读源就可以实现ret。

对于pushq来说,有两个读源,是rA和%rsp(因为稍后%rsp要自减8,所以得先直到%rsp现在存的是几)。所以若icode是IPUSHQ,那么srcA=rA,srcB=RRSP。


################ 译码阶段(确定寄存器的两个读源和两个写目标) ######################

word srcA = [
	icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ  } : rA;
	icode in { IPOPQ, IRET } : RRSP;
	1 : RNONE; # Don't need register
];


word srcB = [
	icode in { IOPQ, IRMMOVQ, IMRMOVQ  } : rB;
	icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
	1 : RNONE;  # Don't need register
];

再看两个写的目标:

 rrmovq 就是把R[rB]的值改为R[rA],只有一个写目标,那为什么多了个“&& cnd”?

(提示:考虑rrmovq与cmov完全等价。)

其实,如果忽略条件传送指令,那么确实IRRMOVQ可以和下一行合并。至于如何考虑条件传送指令,这在练习4.24有解答。

ret 会把%rsp修改(自增8)

pushq会把%rsp修改(自减8)

word dstE = [
	icode in { IRRMOVQ } && Cnd : rB;
	icode in { IIRMOVQ, IOPQ} : rB;
	icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
	1 : RNONE;  # Don't write any register
];


word dstM = [
	icode in { IMRMOVQ, IPOPQ } : rA;
	1 : RNONE;  # Don't write any register
];
顺序处理器SEQ的HCL代码分析_第3张图片 如果忽略条件传送指令,那么确实IRRMOVQ可以和下一行合并 顺序处理器SEQ的HCL代码分析_第4张图片 如何考虑条件传送指令,这在练习4.24有解答。 顺序处理器SEQ的HCL代码分析_第5张图片 练习4.24的答案

译码阶段分析完成。

E阶段

接下来看执行阶段。

对于 rrmovq,要在该阶段执行的内容是valE=R[rA]。则第一个参数是valA(就是R[srcA],srcA就是rA),第二个参数是0,做加法,结果存到变量valE中。

对于ret,要在该阶段执行的内容是valE=R[%rsp]+8。则第一个参数是立即数8,第二个参数是valA(%rsp的值),做加法。

对于pushq,要在该阶段执行的内容是valE=R[%rsp]-8。则第一个参数是立即数-8,第二个参数是valA(%rsp的值),做加法。

################ 执行阶段   ###################################

# 给计算单元的第一个参数赋值
word aluA = [
	icode in { IRRMOVQ, IOPQ } : valA;
	icode in { IIRMOVQ, IRMMOVQ, IMRMOVQ } : valC;
	icode in { ICALL, IPUSHQ } : -8;
	icode in { IRET, IPOPQ } : 8;
	# Other instructions don't need ALU
];

# 给计算单元的第二个参数赋值
word aluB = [
	icode in { IRMMOVQ, IMRMOVQ, IOPQ, ICALL, 
		      IPUSHQ, IRET, IPOPQ } : valB;
	icode in { IRRMOVQ, IIRMOVQ } : 0;
	# Other instructions don't need ALU
];

下面这两个变量很简单,只有OPq的四个指令的两个参数之间所做的运算是需要自定义的,其他一律都是加法。且只有OPq指令执行完后更新条件码。

# 确定计算单元给两个参数做什么运算
word alufun = [
	icode == IOPQ : ifun;
	1 : ALUADD;
];

# 该指令是否更新条件码?
bool set_cc = icode in { IOPQ };

 M阶段 

访存只干一件事:要么把valE写进内存某处,要么将内存某处的东西写入变量valM。

究竟是读还是写,与究竟在处理哪个指令有关。

对于 rrmovq,无需访存,既不读也不写。

对于ret,需要从内存(栈)中得到返回位置,所以是读。

对于pushq,压栈,是写。

################ 访存阶段    ###################################

## Set read control signal
bool mem_read = icode in { IMRMOVQ, IPOPQ, IRET };

## Set write control signal
bool mem_write = icode in { IRMMOVQ, IPUSHQ, ICALL };

刚刚提到的“某处”,就是mem_addr变量,其值就取上文出现过的变量(如valA、valB、valC、valE等) 。

对于ret,从rsp所指向的内存位置读,所以mem_addr=valA。

对于pushq,在rsp所指向的内存位置开始写,所以mem_addr=valE。写什么?valA

## Select memory address
word mem_addr = [
	icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : valE;
	icode in { IPOPQ, IRET } : valA;
	# Other instructions don't need address
];

## Select memory input data
word mem_data = [
	# Value from register
	icode in { IRMMOVQ, IPUSHQ } : valA;
	# Return PC
	icode == ICALL : valP;
	# Default: Don't write anything
];

此处补充一个小特例

4.15答案

 访存阶段的最后还要设置Stat,Stat是干什么的,看书:

## Determine instruction status
word Stat = [
	imem_error || dmem_error : SADR;
	!instr_valid: SINS;
	icode == IHALT : SHLT;
	1 : SAOK;
];
Stat的用途以及计算方法

 更新PC阶段

这是最后一个阶段, Program Counter Update,看我的注释即可:

################ Program Counter Update ############################

# 即找出来下一条指令在哪取

word new_pc = [
	# 如果该条指令是call,那么下一条指令的地址就是call后的那个立即数,即valC
	icode == ICALL : valC;

	# 如果该条指令是条件跳转指令,那么在分支函数=1时跳转到valC,否则是默认
	icode == IJXX && Cnd : valC;

	# 如果该条指令是返回,那么下一条指令的地址是刚刚从栈中取出的地址valM
	icode == IRET : valM;

	# 默认值:紧邻的下一条指令地址,即valP
	1 : valP;
];


# 本文件结束

分析完毕。

你可能感兴趣的:(《CS:APP》的实验,处理器体系结构,SEQ,顺序处理器,Y86-64,HCL硬件描述语言,CSAPP,深入理解计算机系统)