第4章:处理器体系结构(Y86-64 PIPE)
目录
- 模块划分
- 状态单元&&Y86-64的指令集及其编码
- fetch模块→硬件代码见ARCH:HCL
- decode模块
- execute模块
- memory模块
- write back模块
- 控制逻辑模块
- 完整python代码
📌😿写了一大半看书才发现书其实附有PIPE 的完整HCL代码的QWQQ。要认真看书。见书本p308提到ARCH:HCL。
链接 :
注意一下这个硬件代码里面少了一些判断,比如stat状态码之类的
书上是似乎没有流水线的完整图片的。主要分为5个模块写的,fetch(包含了PC select模块)、decode、execute、memory、write_back。其中write_back其实是和decode模块在一起的,这里分出来的write_back模块并不是真正的write_back模块,而是相当于存储write_back的输入的寄存器。也就是说decode模块其实是decode+write_back的实现。各个模块的划分已经在下面的图1图2图3图 4中标出来了。
除了这5个模块之外,还写了控制信号模块。
emmmmm写完模块之后不会拼装,听说python的threading可以实现并发,但是没有进一步写了。
模块划分
状态单元&&Y86-64的指令集及其编码
先全部放出来,后面写的时候需要查找。
程序员可见状态:
Y86-64指令集:
OPq、jXX、cmovXX指令集,在这些指令集中功能码不一样对应的操作也不一样。
寄存器标识符:
Stat状态码,只有Stat==1时是状态正常的。
fetch模块→硬件代码见ARCH:HCL
注意:下面的**fetch模块是不完整的。缺少了"Instr valid"/"Need valC"/"Need regids"以及f_stat**的判断。
predPC→p309(图中最下方的那个寄存器)
功能:pc的预测值仅用寄存器与取指部分隔离开来,故整合到fetch部分一同进行说明。相当于将fetch中PredictPC模块中的输出值作为输入直接进行输出。
def predPC(pred_pc_pre):
pred_pc = pred_pc_pre
return pred_pc
SelectPC(返回值为指令地址)
- 默认为pc的预测值
- 若执行到jXX指令且Cnd为1(发生跳转),应将修改为对应地址(M_valA)
- 若执行到return指令,应将地址设置为rsp存储值(W_valM)
instr_pc = pred_pc_pre
if M_code =='7' and M_Cnd != 1:
instr_pc = M_valA
elif W_code == '9':
instr_pc = W_valM
从指令中取出各值(代码位、功能位、寄存器、立即数、跳转地址)
instr_cur = instruction[instr_pc:instr_pc+20]#当前指令(最大长度20)
code = instr_cur[0]
func = instr_cur[1]
rA = instr_cur[2]
rB = instr_cur[3]
Dest = instruction[2:18]
imm = instruction[4:20]
注意:此处imm并不是严格存放“立即数”,而是存放指令中低八位字节的数据。 (irmov指令中该位置存放的数据为“内存地址”,但为了统一,在fetch的说明中统称imm存放数据为“立即数”)
更新valP(指向下一条指令的地址)
valP = instr_pc
#更新valP(根据当前指令长度)
if code == '0' or code == '1' or code == '9':
valP += 2
elif code == '2' or code == '6' or code == 'A' or code == 'B':
valP += 4
elif code == '3' or code == '4' or code == '5':
valP += 20
elif code == '7' or code == '8':
valP += 18
else:#非法指令
stat = 4
更新valC
- 若为jXX指令或return指令,valC应存放Dest(跳转地址)
- 若为mov指令,valC应存放imm(“立即数”)
- (没有一条指令同时拥有imm和Dest)
valC = 0
#更新valC为立即数或者跳转地址
if code == '7' or code == '8':
valC = int(trans(Dest),16)
elif code == '3' or code == '4' or code == '5':
valC = int(trans(imm),16)
if valC > 32767:#立即数应为补码形式
valC -= 65536
PC预测值
pred_pc = valP
if code == '8' or code == '7':
pred_pc = valC
- jXX指令和call指令对应的跳转地址为valC
- 其余指令的预测值均为valP
decode模块
dstE
功能:译码阶段中标号为 dstE的块根据来自流水线寄存器 D 中取 出的指
令的各个字段,产生寄存器文件 E 端口的寄存器 ID 。在 PIPE 的 HCL 描述中,得到
的信号 命名为 d_dstE。
硬件代码:
## What register should be used as the E destination?
word d_dstE = [
D_icode in { IRRMOVQ, IIRMOVQ, IOPQ} : D_rB;
D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
1 : RNONE; # Don’t write any register
];
python代码:
#写回的目的寄存器
dstE = 'F'
dstM = 'F' #直接设置为‘F’,等价于 RNONE
if code_pre == '2' or code_pre == '3' or code_pre == '6':
dstE = rB
elif code_pre == '8' or code_pre == '9' or code_pre == 'A' or code_pre == 'B':
dstE = '4'
if code_pre == '5' or code_pre == 'B':
dstM = rA
dstM
功能:What register should be used as the M destination?应该是决定memory阶段的寄存器ID。
硬件代码:
204 word d_dstM = [
205 D_icode in { IMRMOVQ, IPOPQ } : D_rA;
206 1 : RNONE; # Don’t write any register
207 ];
python代码:
#写回的目的寄存器
dstE = 'F'
dstM = 'F' #直接设置为‘F’,等价于 RNONE
if code_pre == '2' or code_pre == '3' or code_pre == '6':
dstE = rB
elif code_pre == '8' or code_pre == '9' or code_pre == 'A' or code_pre == 'B':
dstE = '4'
if code_pre == '5' or code_pre == 'B':
dstM = rA
srcA
功能:What register should be used as the A source?
硬件代码:
182 ## What register should be used as the A source?
183 word d_srcA = [
184 D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : D_rA;
185 D_icode in { IPOPQ, IRET } : RRSP;
186 1 : RNONE; # Don’t need register
187 ];
python代码:
#根据code获取rA rB寄存器并取值
srcA = 'F'
srcB = 'F'
if code_pre == '2' or code_pre == '4' or code_pre == '6' or code_pre == 'A':
srcA = rA
elif code_pre == '9' or code_pre == 'B':#需要rsp寄存器
srcA = '4'
if code_pre == '4' or code_pre == '5' or code_pre == '6':
srcB = rB
elif code_pre == '8' or code_pre == '9' or code_pre == 'A' or code_pre =='B':
srcB = '4'
srcB
功能:What register should be used as the B source?
硬件代码:
189 ## What register should be used as the B source?
190 word d_srcB = [
191 D_icode in { IOPQ, IRMMOVQ, IMRMOVQ } : D_rB;
192 D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
193 1 : RNONE; # Don’t need register
194 ];
python代码:
#根据code获取rA rB寄存器并取值
srcA = 'F'
srcB = 'F'
if code_pre == '2' or code_pre == '4' or code_pre == '6' or code_pre == 'A':
srcA = rA
elif code_pre == '9' or code_pre == 'B':#需要rsp寄存器
srcA = '4'
if code_pre == '4' or code_pre == '5' or code_pre == '6':
srcB = rB
elif code_pre == '8' or code_pre == '9' or code_pre == 'A' or code_pre =='B':
srcB = '4'
寄存器文件
主要就是分为两个方面。一个是读出,一个是写入。
硬件代码:
##这一块是读出
182 ## What register should be used as the A source?
183 word d_srcA = [
184 D_icode in { IRRMOVQ, IRMMOVQ, IOPQ, IPUSHQ } : D_rA;
185 D_icode in { IPOPQ, IRET } : RRSP;
186 1 : RNONE; # Don’t need register
187 ];
188
189 ## What register should be used as the B source?
190 word d_srcB = [
191 D_icode in { IOPQ, IRMMOVQ, IMRMOVQ } : D_rB;
192 D_icode in { IPUSHQ, IPOPQ, ICALL, IRET } : RRSP;
193 1 : RNONE; # Don’t need register
194 ];
##写入
就是在转发逻辑里实现了,不过是写python代码的时候分开写了
python代码:
#写入
#向寄存器写回
dict_r[W_dstM] = W_valM
dict_r[W_dstE] = W_valE
#读出
#根据code获取rA rB寄存器并取值
srcA = 'F'
srcB = 'F'
if code_pre == '2' or code_pre == '4' or code_pre == '6' or code_pre == 'A':
srcA = rA
elif code_pre == '9' or code_pre == 'B':#需要rsp寄存器
srcA = '4'
if code_pre == '4' or code_pre == '5' or code_pre == '6':
srcB = rB
elif code_pre == '8' or code_pre == '9' or code_pre == 'A' or code_pre =='B':
srcB = '4'
d_rvalA = dict_r[srcA]
d_rvalB = dict_r[srcB]
Sel+FwdA
功能:有两个功能。
1.它为后面的阶段将 valP 信号合并到 valA 信号,这样可以减少流
水线寄存器中状态的数量。合并信号 valA 和 valP 的依据是,只有 call 和跳转指令在后面的阶段中需要 valP的值,而这些指令并不需要从寄存器文件 A 端口中读出的值。这个选择是由该阶段的icode 信号来控制的。
2.它还实现了源操作数 valA 的转发逻辑。
硬件代码:
209 ## What should be the A value?
210 ## Forward into decode stage for valA
211 word d_valA = [
212 D_icode in { ICALL, IJXX } : D_valP; # Use incremented PC
213 d_srcA == e_dstE : e_valE; # Forward valE from execute
214 d_srcA == M_dstM : m_valM; # Forward valM from memory
215 d_srcA == M_dstE : M_valE; # Forward valE from memory
216 d_srcA == W_dstM : W_valM; # Forward valM from write back
217 d_srcA == W_dstE : W_valE; # Forward valE from write back
218 1 : d_rvalA; # Use value read from register file
219 ];
python代码:
#选择正确的valA
valA = d_rvalA
if(code_pre == '7' or code_pre == '8'):#跳转指令与调用函数指令
valA = valP
else:#数据冒险
if srcA == e_dstE:
valA = e_valE
elif srcA == M_dstM:
valA = m_valM
elif srcA == M_dstE:
valA = M_valE
elif srcA == W_dstM:
valA = W_valM
elif srcA == W_dstE:
valA = W_valE
Sel+FwdB
功能:略
硬件代码:
220
221 word d_valB = [
222 d_srcB == e_dstE : e_valE; # Forward valE from execute
223 d_srcB == M_dstM : m_valM; # Forward valM from memory
224 d_srcB == M_dstE : M_valE; # Forward valE from memory
225 d_srcB == W_dstM : W_valM; # Forward valM from write back
226 d_srcB == W_dstE : W_valE; # Forward valE from write back
227 1 : d_rvalB; # Use value read from register file
228 ];
python代码:
#选择正确的valB
valB = d_rvalB
if srcB == e_dstE:
valB = e_valE
elif srcB == M_dstM:
valB = m_valM
elif srcB == M_dstE:
valB = M_valE
elif srcB == W_dstM:
valB = W_valM
elif srcB == W_dstE:
valB = W_valE
execute模块
模块解释
ALUA→p280
功能:对进入ALU进行计算的值aluA的来源。
硬件代码:
word aluA = [
icode in {IRRMOVQ, IOPQ} : valA; #包含两个寄存器时,aluA为寄存器的值valA
icode in {IIRMOVQ, IRMMOVQ, IMRMOVQ} : valC; #当出现立即数、偏移量时,aluA为常数值
icode in {ICALL, IPUSHQ} : -8; #入栈需要将栈顶地址下移8字节
//但是由于写python程序的时候,存储器中的一个空字符代表是一个bit,故在写程序的时候是向下移动16
//注意分析就好
icode in {IRET, IPOPQ} : 8; #出栈需要将栈顶地址上移8字节
//同理
];
ALUB→p336
功能:对进入ALU进行计算的值aluB的来源。
硬件代码:
word aluB = [
icode in {IRMMOVQ, IMRMOVQ, IOPQ, ICALL, IPUSHQ, IRET, IPOPQ} : valB;
icode in {IRRMOVQ, IIRMOVQ} : 0;
];
ALUfun.→p280
功能:决定ALU是进行加法、减法、and、xor操作。对于OPq指令,由func决定。除此之外,如果是其他需要使用ALU的指令,一律设置成进行加法操作。
硬件代码:
word alufun = [
icode == IOPQ : ifun;
1 : ALUADD;
];
解释一下这两行硬件代码,刚开始以为是如果指令不是**OPq就把alufun设置为1,但是这是错的。首先检查第一行,如果指令是OPq→alufun = ifun,如果不是则检查第二行,1是一定为真的,故alufun = 0(执行加法操作)。**
问题: **OPq**进行加法操作和其他指令进行加法操作时,同样的运算,为什么alufun的值不一样?
注意到,OPq指令进行加法操作,alufun=0。而其他操作,则直接设置为加法操作,并且alufun=0。这是一个特别巧妙的设计。因为之前我们已经对aluA、aluB进行处理,除OPq的指令的多种运算需求被统一成了一种(也就是设置成了加法操作。)
ALU
功能:算术逻辑单元。
硬件代码:
书上没有,自己根据这张图写了一下。
#ALU
valE = 0
if ALUfun == 0:
valE = ALUA + ALUB
elif ALUfun == 1:
valE = ALUB-ALUA
elif ALUfun == 2:
valE = ALUB & ALUA
elif ALUfun == 3:
valE = ALUB^ALUA
CC→p273
功能:条件码寄存器(CC)有三个条件码位。ALU负责计算条件码的新值。当执行条件传送指令时,根据条件码和传送条件来计算决定是否更新目标寄存器。
硬件代码:
书上没有。代码也挺好写的,就是要注意有溢出情况的时候不能用未经过处理的值进行符号判断(但是ZF的判断应该是不受影响的,因为溢出应该是不可能为0的)。
cond→p280
功能:Cnd是分支信号。根据条件码和跳转类型来计算分支信号Cnd。
看见网上有博客,写了一些关于Cnd的内容。
cmovXX指令,可以将其看成是rrmovq和条件信号Cnd的组合.jXX在执行阶段会根据ifun和条件码来设置是否跳转Cnd。
所以这个模块主要是和**cmovXX、jXX有关。Cnd决定jXX是否跳转,决定cmovXX**是否进行传送。
硬件代码:
书上没有。自己写的代码。
#cond
#根据条件码和功能码来确定是否进行条件分支或者条件数据传输。
#它产生Cnd,用于设置条件传送的dstE,也用在条件分支的下移个PC逻辑来,
#对于其他指令,取决于指令的功能码和条件码的设置
#jXX #cmovXX
Cnd = 0
if func == '0':
Cnd = 1
elif func == '1':
if (SF^OF)|ZF:
Cnd = 1
elif func == '2':
if (SF^OF):
Cnd = 1
elif func == '3':
if ZF:
Cnd = 1
elif func == '4':
if not ZF:
Cnd = 1
elif func == '5':
if not (SF^OF):
Cnd = 1
elif func == '6':
if (not (SF^OF))&(not ZF):
Cnd = 1
dstE→p310
功能:该硬件模块生成了e_dstE信号,其传送到该篇笔记的第2张图,也就是用红字写了decode的图里,最后作用于Sel+FwdA模块。综上,dstE是参与到源操作数valA的转发逻辑。下面这张图是Sel+FwdA、Sel+FwdB的输入的信号量。
需要注意的是在写python程序的时候,需要按照这张图中信号量的排序从上到下对这些信号依次进行判断。举个栗子🦔:
1:rrmov eax ebx
2:rrmov ecx ebx
3:rrmov edx ebx
4:rrmov ebx r8
此时会发生数据冲突。如果想要进行处理,肯定是由下往上进行判断,此时肯定是第3行指令处于的阶段(比如在execute)要早于第2行处于的阶段。
硬件代码:
## Set dstE to RNONE in event of not-taken conditional move
word e_dstE = [
E_icode == IRRMOVO && !e_Cnd : RNONE
1 : E_dstE;
];
#dstE(自己写的 不知道对不对)
if code_pre == '2' and Cnd == 0:
#在执行cmovXX语句时,若Cnd为0,则不发生向寄存器内写值的操作
dstE = 'F'
else:
dstE = dstE_pre
e_dstE = dstE
memory模块
Addr
硬件代码:
271 ## Select memory address
272 word mem_addr = [
273 M_icode in { IRMMOVQ, IPUSHQ, ICALL, IMRMOVQ } : M_valE;
274 M_icode in { IPOPQ, IRET } : M_valA;
275 # Other instructions don’t need address
276 ];
python代码:
#选择地址:alu计算结果或rsp储存值
mem_address = valE_pre
if code_pre == '9' or code_pre == 'B':
mem_address = valA
Mem.read
硬件代码:
278 ## Set read control signal
279 bool mem_read = M_icode in { IMRMOVQ, IPOPQ, IRET };
python代码:
#数据在译码阶段求得
mem_data = valA
#读写内存的控制信号
mem_read = 0
mem_write = 0
if code_pre == '5' or code_pre == '9' or code_pre == 'B':
mem_read = 1
if code_pre == '4' or code_pre == '8' or code_pre == 'A':
mem_write = 1
Mem.write
硬件代码:
280
281 ## Set write control signal
282 bool mem_write = M_icode in { IRMMOVQ, IPUSHQ, ICALL };
python代码:
#数据在译码阶段求得
mem_data = valA
#读写内存的控制信号
mem_read = 0
mem_write = 0
if code_pre == '5' or code_pre == '9' or code_pre == 'B':
mem_read = 1
if code_pre == '4' or code_pre == '8' or code_pre == 'A':
mem_write = 1
数据寄存器
python代码:
#读写操作
valM = 0
if mem_read == 1:
if ' ' in stack[stack[mem_address-15:mem_address+1]]:#非法地址
stat = 3
valM = int(trans(stack[mem_address-15:mem_address+1]),16)
if mem_write == 1:
stack[mem_address-15:mem_address+1] = trans('0x{:08X}'.format(mem_data)[2:10])
m_valM =valM
write back模块
这个模块其实是当成一个寄存器在使用。
def write_back(stat,code,valE,valM,dstE,dstM):
W_code = code
W_valE = valE
W_valM = valM
W_dstE = dstE
W_dstM = dstM
return stat,W_code,W_valE,W_valM,W_dstE,W_dstM
控制逻辑模块
需要生成的信号量
F_stall
功能:
硬件代码:
bool F_stall =
# Conditions for a load/use hazard
E_icode in { IMRMOVQ, IPOPQ } &&
E_dstM in { d_srcA, d_srcB } ||
# Stalling at fetch while ret passes through pipeline
IRET in { D_icode, E_icode, M_icode };
SetCC→p280、p312、p339(但是其实已经直接在execute模块里写了)
功能:是否设置条件码(CC)。只希望执行OPq指令的时候才设置条件码,所以产生了一个信号set_cc来控制是否更新条件码寄存器(CC)。而在流水线处理器中,我们还需要查看m_stat和W_stat,如果异常,则任何对条件码的更新都会被禁止。
硬件代码:
bool set_cc = icode in {IOPQ}; //这是单周期的情况的代码
//流水线
//这个控制需要检查正在执行的指令的代码,还需要检查流水线中更后面阶段中的异常.
//##是否应该更新条件代码?
bool set cc=E_icode=IOPq&&
//#仅在正常操作过程中状态变化
!m_stat in {SADR,SINS,SHLT} && !W_stat in {SADR、SINS、SHLT};
W_stall
功能:
硬件代码:
M_bubble
功能:
硬件代码:
set_cc
功能:
硬件代码:
E_bubble
功能:
硬件代码:
D_bubble
功能:
硬件代码:
D_stall
功能:
硬件代码:
完整python代码
stack = ''.join([' ']*65538) #一个地址对应一位数据
dict_r={
'0':0,#rax
'1':0,#rcx
'2':0,#rdx
'3':0,#rbx
'4':61440,#rsp 0xf0000000
'5':0,#rbp
'6':0,#rsi
'7':0,#rdi
'8':0,#r8
'9':0,#r9
'A':0,#r10
'B':0,#r11
'C':0,#r12
'D':0,#r13
'E':0,#r14
'F':0}
ZF = 0
SF = 0
OF = 0
stat = 1#正常操作
instruction = []
instr_pc = 0
instr_pc_max = 0 #指令最大长度
fil = open('D:/learn/大二下/计算系统/实验4/test.txt','r')
instruction = fil.read()
instruction = instruction.replace('\n', '')
instr_pc_max = len(instruction)
#将小端格式储存的imm转换为正常的16进制字符串
def trans(imm):
result = [' ']*8
result[0:2] = imm[6:8]
result[2:4] = imm[4:6]
result[4:6] = imm[2:4]
result[6:8] = imm[0:2]
result = ''.join(result)
return result
def predPC(pred_pc_pre):
pred_pc = pred_pc_pre
return pred_pc
#fetch
def fetch(pred_pc_pre,M_code,M_Cnd,M_valA,W_code,W_valM):
global instruction
#f_pc
instr_pc = pred_pc_pre
if M_code =='7' and M_Cnd != 1:
instr_pc = M_valA
elif W_code == '9':
instr_pc = W_valM
instr_cur = instruction[instr_pc:instr_pc+20]#当前指令(最大长度20)
code = instr_cur[0]
func = instr_cur[1]
rA = instr_cur[2]
rB = instr_cur[3]
Dest = instruction[2:18]
imm = instruction[4:20]
valP = instr_pc
valC = instr_pc
#更新valP(根据当前指令长度)
if code == '0' or code == '1' or code == '9':
valP += 2
elif code == '2' or code == '6' or code == 'A' or code == 'B':
valP += 4
elif code == '3' or code == '4' or code == '5':
valP += 20
elif code == '7' or code == '8':
valP += 18
else:#非法指令
stat = 4
#更新valC为立即数或者跳转地址
if code == '7' or code == '8':
valC = int(trans(Dest),16)
elif code == '3' or code == '4' or code == '5':
valC = int(trans(imm),16)
if valC > 32767:#立即数应为补码形式
valC -= 65536
#pc的预测值
#f_predPC
pred_pc = valP
if code == '8' or code == '7':
pred_pc = valC
return stat,code,func,rA,rB,valC,valP,pred_pc
#decode
def decode(stat_pre,code_pre,func_pre,rA,rB,valC_pre,valP,e_dstE,e_valE,M_dstE,M_valE,M_dstM,m_valM,W_dstM,W_valM,W_dstE,W_valE):
stat = stat_pre
code = code_pre
func = func_pre
valC = valC_pre
#向寄存器写回
dict_r[W_dstM] = W_valM
dict_r[W_dstE] = W_valE
#写回的目的寄存器
dstE = 'F'
dstM = 'F'
if code_pre == '2' or code_pre == '3' or code_pre == '6':
dstE = rB
elif code_pre == '8' or code_pre == '9' or code_pre == 'A' or code_pre == 'B':
dstE = '4'
if code_pre == '5' or code_pre == 'B':
dstM = rA
#根据code获取rA rB寄存器并取值
srcA = 'F'
srcB = 'F'
if code_pre == '2' or code_pre == '4' or code_pre == '6' or code_pre == 'A':
srcA = rA
elif code_pre == '9' or code_pre == 'B':#需要rsp寄存器
srcA = '4'
if code_pre == '4' or code_pre == '5' or code_pre == '6':
srcB = rB
elif code_pre == '8' or code_pre == '9' or code_pre == 'A' or code_pre =='B':
srcB = '4'
d_rvalA = dict_r[srcA]
d_rvalB = dict_r[srcB]
#选择正确的valA
valA = d_rvalA
if(code_pre == '7' or code_pre == '8'):#跳转指令与调用函数指令
valA = valP
else:#数据冒险
if srcA == e_dstE:
valA = e_valE
elif srcA == M_dstM:
valA = m_valM
elif srcA == M_dstE:
valA = M_valE
elif srcA == W_dstM:
valA = W_valM
elif srcA == W_dstE:
valA = W_valE
#选择正确的valB
valB = d_rvalB
if srcB == e_dstE:
valB = e_valE
elif srcB == M_dstM:
valB = m_valM
elif srcB == M_dstE:
valB = M_valE
elif srcB == W_dstM:
valB = W_valM
elif srcB == W_dstE:
valB = W_valE
return stat,code,func,valC,valA,valB,dstE,dstM,srcA,srcB
#execute
def execute(stat_pre,code_pre,func,valC,valA_pre,valB,dstE_pre,dstM_pre,W_stat,m_stat):
#cnd == 1 -> jump
#stat,code,valA,dstM可以直接返回,dstE目前还不清楚
stat = stat_pre
code = code_pre
valA = valA_pre
dstM = dstM_pre
#SetCC
if code == '6': #一定要是OPq
if W_stat == 1 or m_stat == 1:
set_cc = True #书上set_cc是bool类型
#ALUA
if code =='2' or code == '6':
ALUA = valA_pre
elif code == '3' or code == '4' or code == '5':
ALUA = valC
elif code == '8' or code == 'A':
ALUA = -16
elif code == '9' or code == 'B':
ALUA = 16
#ALUB
if code == '4' or code == '5' or code == '6' or code == '8' or code == 'A' or code == '9' or code == 'B':
ALUB = valB
elif code =='2' or code =='3':
ALUB = 0
#注意先后顺序
#ALUfun.
#有这个模块的原因是:ALU通常作为加法器使用;但是对于OPq指令,希望使用func字段里编码的操作
if code == '6':
ALUfun = func#非常巧妙
else:
ALUfun = 0
#ALU
valE = 0
if ALUfun == 0:
valE = ALUA + ALUB
elif ALUfun == 1:
valE = ALUB-ALUA
elif ALUfun == 2:
valE = ALUB & ALUA
elif ALUfun == 3:
valE = ALUB^ALUA
e_valE = valE
#CC
if set_cc == True:
if valE == 0:
ZF =1
if valE >= 2**15-1:
OF = 1
valE = valE - 2**16
#处理溢出。处理之后再判断符号
if valE <= -2**15:
OF = 1
valE = valE + 2**16
#处理溢出。处理之后再判断符号
#valE_str = bin(valE).replace('0b','')
if valE < 0:
SF = 1
#注意先后顺序
#cond
#根据条件码和功能码来确定是否进行条件分支或者条件数据传输。
#它产生Cnd,用于设置条件传送的dstE,也用在条件分支的下移个PC逻辑来,
#对于其他指令,取决于指令的功能码和条件码的设置
#jXX #cmovXX
Cnd = 0
if func == '0':
Cnd = 1
elif func == '1':
if (SF^OF)|ZF:
Cnd = 1
elif func == '2':
if (SF^OF):
Cnd = 1
elif func == '3':
if ZF:
Cnd = 1
elif func == '4':
if not ZF:
Cnd = 1
elif func == '5':
if not (SF^OF):
Cnd = 1
elif func == '6':
if (not (SF^OF))&(not ZF):
Cnd = 1
#dstE(自己写的 不知道对不对)
if code_pre == '2' and Cnd == 0:#在执行cmovXX语句时,若Cnd为0,则不发生向寄存器内写值的操作
dstE = 'F'
else:
dstE = dstE_pre
e_dstE = dstE
return stat,code,Cnd,valE,valA,dstE,dstM,e_valE,e_dstE
#memory
def memory(stat_pre,code_pre,Cnd,valE_pre,valA,dstE_pre,dstM_pre):
code = code_pre
valE = valE_pre
dstE = dstE_pre
dstM = dstM_pre
M_code = code_pre
M_Cnd = Cnd
M_dstE = dstE_pre
M_dstM = dstM_pre
M_valA = valA
M_valE = valE_pre
stat = stat_pre
#选择地址:alu计算结果或rsp储存值
mem_address = valE_pre
if code_pre == '9' or code_pre == 'B':
mem_address = valA
#数据在译码阶段求得
mem_data = valA
#读写内存的控制信号
mem_read = 0
mem_write = 0
if code_pre == '5' or code_pre == '9' or code_pre == 'B':
mem_read = 1
if code_pre == '4' or code_pre == '8' or code_pre == 'A':
mem_write = 1
#读写操作
valM = 0
if mem_read == 1:
if ' ' in stack[stack[mem_address-15:mem_address+1]]:#非法地址
stat = 3
valM = int(trans(stack[mem_address-15:mem_address+1]),16)
if mem_write == 1:
stack[mem_address-15:mem_address+1] = trans('0x{:08X}'.format(mem_data)[2:10])
m_valM =valM
return stat,code,valE,valM,dstE,dstM,M_code,M_Cnd,m_valM,M_dstE,M_dstM,M_valA,M_valE
#write_back
def write_back(stat,code,valE,valM,dstE,dstM):
W_code = code
W_valE = valE
W_valM = valM
W_dstE = dstE
W_dstM = dstM
return stat,W_code,W_valE,W_valM,W_dstE,W_dstM
def Control_logical(W_stat, m_stat, M_icode, e_Cnd, E_dstM, E_icode, d_srcB, d_srcA, D_icode):
#是根据PIPE的HCL代码写的
# Should I stall or inject a bubble into Pipeline Register F
F_bubble = 0
F_stall = 0
if E_icode == '5' or E_icode == '6' :
if E_dstM == '' or E_dstM == '': # d_srcA d_srcB 怎么表示
F_stall = 1
elif D_icode == '9' or E_icode == '9' or M_icode == '9':
F_stall = 1
# Should I stall or inject a bubble into Pipeline Register D?
D_stall = 0
D_bubble = 0
if (E_icode == '5' or E_icode == '6') and (E_dstM == '' or E_dstM == ''): # d_srcA d_srcB 怎么表示
D_stall = 1
if E_icode == '7' and e_Cnd == 0:
D_bubble = 1
elif ((E_icode == '5' or E_icode == '6') and (E_dstM == '' or E_dstM == '')) == 0:
if D_icode == '9' or E_icode == '9' or M_icode == '9':
D_bubble = 1
# Should I stall or inject a bubble into Pipeline Register E?
E_stall = 0
E_bubble = 0
if E_icode == '7' and e_Cnd == 0:
E_bubble = 1
elif E_icode == '5' or E_icode == '6':
if E_dstM == '' and E_dstM == '': # d_srcA d_srcB 怎么表示
E_bubble = 1
# Should I stall or inject a bubble into Pipeline Register M?
M_stall = 0
M_bubble = 0
if m_stat != 1 or W_stat != 1:
M_bubble = 1
# Should I stall or inject a bubble into Pipeline Register W?
W_stall = 0
W_bubble = 0
if W_stat != 1:
W_stall = 1
#set_cc 和 CC模块放在execute里面实现了
return F_stall,D_stall,D_bubble,E_bubble,M_bubble,W_stall
代码没有经过测试集的检验。因为不会用python并发编程,所以最后实现的是SEQ