【CSAPP】第4章:处理器体系结构(Y86-64 PIPE)

284 阅读18分钟

第4章:处理器体系结构(Y86-64 PIPE)

目录

📌😿写了一大半看书才发现书其实附有PIPE 的完整HCL代码的QWQQ。要认真看书。见书本p308提到ARCH:HCL。

链接 :

waside-hcl.pdf (cmu.edu)

注意一下这个硬件代码里面少了一些判断,比如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可以实现并发,但是没有进一步写了。

模块划分

图1:fetch和PC select

微信图片_20230404195231_iPw3iJztmk.png

微信图片_20230404195231_iPw3iJztmk.png

图4:memory和write back(不是真的write back)

状态单元&&Y86-64的指令集及其编码

先全部放出来,后面写的时候需要查找。

程序员可见状态:

image_m7k-W6jIQ_.png

Y86-64指令集:

image_rbDR7rxcnu.png

OPq、jXX、cmovXX指令集,在这些指令集中功能码不一样对应的操作也不一样。

image_m7k-W6jIQ_.png

寄存器标识符:

image_2hOSd0wpiN.png Stat状态码,只有Stat==1时是状态正常的。

image_HFYSfeGAnd.png

fetch模块→硬件代码见ARCH:HCL

注意:下面的**fetch模块是不完整的。缺少了"Instr valid"/"Need valC"/"Need regids"以及f_stat**的判断。

图1:fetch和PC select

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,但是这是错的。首先检查第一行,如果指令是OPqalufun = ifun,如果不是则检查第二行,1是一定为真的,故alufun = 0(执行加法操作)。**​

问题: **​OPq**进行加法操作和其他指令进行加法操作时,同样的运算,为什么alufun的值不一样?

注意到,OPq指令进行加法操作,alufun=0。而其他操作,则直接设置为加法操作,并且alufun=0。这是一个特别巧妙的设计。因为之前我们已经对aluA、aluB进行处理,除OPq的指令的多种运算需求被统一成了一种(也就是设置成了加法操作。)

ALU

功能:算术逻辑单元。

硬件代码:

书上没有,自己根据这张图写了一下。

image_m7k-W6jIQ_.png

#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

所以这个模块主要是和**cmovXXjXX有关。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+FwdASel+FwdB的输入的信号量。

image_wDsAMYQ23f.png

需要注意的是在写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模块

图4:memory和write back(不是真的write back)

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

控制逻辑模块

Snipaste_2023-04-05_14-01-56_wR-cmecTtj.png

需要生成的信号量

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_statW_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