流水线CPU的实现

我一直觉得通过流水线CPU的设计之后的对流水线的理解是比较全面和完整的(事实上也是如此),但是有人提醒我要仔细看看,就再仔细阅读一下,从头到尾阐述这个过程。

顺序执行的处理器

要想理解什么是流水线CPU,首先要从最开始去了解一个单周期的CPU是怎么搭建起来的。

我们为我们的Y86设置了六个阶段(子程序),从每个阶段去完成相应的功能,到这里有人会疑惑为什么要分成六个阶段,我们的单周期CPU不是从一个周期就把他所需要做的事情完成了吗,怎么还是六个阶段。

最开始我被问到这个问题的时候也是晕了,难道说我们其实是六个小周期,但是从这方面来看就是我们理解的顺序出现了错误,他是使用了六个模块去完成相应的问题,而非六个不同的时序,在这里的阶段本就不是时序的意思,是因为我们主观去理解一个阶段的时候把它汉语中所隐含的时序强加进去了,所以与其说是六个阶段,不如说是六个模块来的更准确易懂一些,因为他虽然有先后,但是完全没有时序这个概念(在我们单周期的CPU中,我指的是对于一个周期内其讨论)。所以从另一个角度去理解,是不是我们实现了流水线CPU之后反过来回头才赋予了单周期CPU六个阶段的概念呢,真伪已经难以考证。

SEQ的时序

去讨论SEQ的时序,与其说是时序,不如说是去看清楚我们的读和写是在什么时候实现的,

我们在一个周期开始的时候去写我们需要更新的数据流,在一个周期结束的时候完成数据流的计算,并等待下一个周期的开始并写入相应的位置。

流水线的实现

前面叙述了单周期CPU的实现以及其时序,主要就是为了后面的流水线,我的脖子好痛不想分析这个地方了,因为我估计我分析了也没人看。

我们发现我们的CPU在进行工作时,因为六个模块互为关键路径,所以当一个模块在工作时,其他五个都在摸鱼,所以怎么能当一个资本家让CPU无🐟可摸呢,因此我们就想到了使用流水线,让一个模块工作的时候,其他的模块也在不停工作,这样就榨取了CPU的剩余价值,提高了吞吐率。但是我们为什么不划分无数个流水线,是因为只有六个模块吗,这显然是不对的,只要模块之间独立就可以无限进行流水线分割,而非人为规定的六个阶段,制约我们榨取剩余价值的是寄存器的存储速度,这就相当于工人需要睡觉,我们寄存器进行数据的存储也需要相应的时间,因此我们无法实现无限分割的流水线,所以我们在设计很深的流水线时,需要很小心,否则会承受很大的延迟反噬。

这里就将明白了为什么我们要划分流水线以及为什么我们不能给流水线分为尽可能多的阶段(果然在流水线使用阶段这个概念非常的漂亮),但是这时候我们就要考虑另一个问题了,我们使用单周期CPU虽然也涉及时序的问题,但是只是读和写的问题,我们可以保证数据流和指令流的正确性,我们能保证我们直接实现的流水线CPU时正确的吗。

我们为了考查是否正确,就要查看我们的CPU是否按照我们期待的想法准确的完成我们想让他做的事情,即是否流水线改变了系统的行为。答案是肯定的,因为我们的指令流和数据流在很大程度上依赖于前面的指令是否实现,比如我们要使用上一条指令中想要改变数值的寄存器,数据流就会出现问题,我们想要想要使用指令跳转,但是后面的指令尚未得到指令还在继续流水,这时候控制流就出现了问题,因此我们产生了概念数据相关,但是显然我说的更绝对一点,这样的事情是一定出现了问题,所以产生了更难以置信的后果,就是数据冲突和控制冲突。

我们想要去理解冲突,就要从时序去理解这件事情,首先要考虑为什么我们的单周期不会出错,为什么一定是对的,我们知道,我们的单周期CPU是一个周期完成了一条指令,然后去完成下一条指令,按顺序执行,因此与我们想让他完成的是一样的,一条一条执行,按照顺序完成,因此一定不会出错。从这里我们引申出来也可以理解,CPU对于我们来说也是一种抽象,是对于指令流的抽象(我也不知道这样理解对不对),我们不关心内部如何实现,只关心他是否变成了我们想要的样子,所以无论是顺序执行还是乱序执行,单发射还是多发射我们统统不关心。

又扯远了,那为什么流水线就错了呢,我们知道,时间是不能倒流的,我们使用流水线的时候,势必有多条指令在CPU内流水,因此一条指令到达了他的执行阶段时,前面的指令一定有还在流水的,因此若出现了我们前述的数据相关,不加处理势必就会产生数据冲突和控制冲突的情况,会导致系统出现不一致的行为。

这里再捎带一嘴控制相关,控制相关指的是诸如条件跳转指令会产生控制相关信号,通过反馈路径反馈给后面的指令,从而会导致改变系统的行为。

一个什么都不考虑的流水线实现

说到这里,我们还是从简单到复杂去考虑这个实际的问题,首先我们想要使用流水线去加速我们的CPU,而不去考虑他是否正确,我们把它分为六个阶段,然后六个阶段在流水时去完成自己相应的工作,显然这六个阶段需要处于不同的指令中才能够做到这件事情,因此我们需要对不同的阶段提供不同的数据流以及控制流,因此我们在流水线阶段之间加入了流水线寄存器去实现不同流的输入。

 我们可以看到,简单的插入流水线寄存器就能初步完成我们预想的功能。

数据冒险

我们首先去考虑数据冒险的情况,我们在最开始的讨论发现,我们的数据返回机制已经出现问题了,很有可能我们返回的时候导致了系统行为的改变。

我们去看一串指令流实例:

可以清楚的看到,只有第一段程序实现了我们预先想要达到的结果。 

用暂停来避免数据冒险

暂停是避免冒险的一种常用手段,暂停时,处理器会停止流水线中一条或多条指令,直到冒险条件不再满足,让一条指令停在译码阶段直到产生他的源操作数的指令通过了写回阶段,因此,其后的指令也应该暂停在取指阶段,直到暂停结束。

我们使用插入气泡来实现暂停(暂停和气泡不是一个东西,气泡是把流水线寄存器清空,而暂停时维持流水线寄存器的状态,我们只是说插入气泡来实现暂停,并没有说暂停和气泡是一个东西),气泡不会改变任何状态,而只会阻止被暂停的指令流水进入下一个阶段。因此我们查看上文讨论过的程序发现,我们需要产生三条nop指令,虽然实现起来非常容易,但是带来的副作用就是我们暂停了三个周期,这是很大的代价。

注意暂停条件:

源寄存器:当前指令的srcA和srcB都处于译码阶段;

目的寄存器:dstE域和dstM域,处于执行、访存和写回阶段的指令;

特例:ID为(0xF)的寄存器不需要暂停:表示无寄存器操作数,表示失败的条件和移动;

用转发来避免数据冒险

我们在前一段已经提过了,如果想要执行一条源操作数还在流水线上的指令,就需要其通过写回阶段才能进行。但是代价就是暂停了三个周期,我们考虑,与其暂停直到其写回阶段,为什么不能直接使用其执行阶段产生的结果呢,我们简单地将指令生成的值直接传递到译码阶段,不止是在执行阶段、在访存阶段中有对寄存器未进行的写时,我们都需要使用数据转发技术,因为这样不仅可以提升流水线的吞吐率,更可以保证指令流行为的正确性。

因此我们去考虑转发源和转发目的就可以知道,

五个转发源:e_valE,m_valM,M_valE,W_valM,W_valE

转发目的:val_A,val_B

在这里还要涉及多重转发的选择,在转发是如果同时获得W、M、E流水线寄存器的转发,我们应该选择最早的流水线阶段获取的匹配值。

通过上述讨论,我们就在译码阶段从E、M、W流水线寄存器中添加额外的反馈路径实现了转发,在译码阶段中,我们创建逻辑块来从valA和valB的多来源中进行选择。

加载/使用冒险

在一个周期中需要的值,等到几个周期之后的访存阶段才能读取,这种数据冒险,完全无法通过转发规避,因为我们不能改变事件,完成一个根本没有完成的事件,对于这种冒险的处理方式,我们使用指令暂停一个周期,然后就可以获取从访存阶段转发的加载值。

  1. 将指令暂停在取指和译码阶段
  2. 在执行阶段注入气泡

 

控制相关

我们在时钟周期都发射一条指令,因此每次取出指令之后我们就要马上确定下一条指令,如果是一条条件指令,我们就要在几个周期之后才知道具体的分支选择,如果是ret,我们会错误的流水几条指令之后才能确定返回地址,类似的情况还有call和jmp。

因此我们对于控制流的产生,都是使用预测下一个PC的值的策略,对于大部分的指令来说是完全可靠的(我最开始也非常迷惑,为什么对大部分的指令都是可靠的,直到我发现,他所说的预测PC值就是单纯的取出下一条指令时),这样产生的指令流势必大部分时间都是正确的,因此我们只需要考虑如何应对预测错误时的情况,去处理小部分的特殊时刻。

预测策略

非转移指令:预测PC为valP,永远可靠;

调用指令或无条件转移指令:预测PC为valC(调用的入口地址或转移目的地址),永远可靠;

条件转移指令:预测PC为valC(转移目的地址),如果分支被选中则预测正确(成功率大约60%)

返回指令:不进行预取(不进行预取,只暂停)

处理预测错误

在我们上述的预测策略下,控制冒险只会发生在ret指令和跳转指令。而且,后一种情况只有在条件跳转方向预测错误时才会造成麻烦。

ret指令

当ret经过流水线时,暂停在取指阶段(浪费三个时钟周期):当处于译码、执行、访存阶段。

在译码阶段注入气泡,当到达写回阶段释放暂停。

检测分支预测错误

作为预测分支时,取出两条目标指令(浪费两个时钟周期);

当预测错误时取消:

  1. 在执行阶段检测到未选择该分支;
  2. 在紧跟的指令周期中,将处于执行和译码阶段的指令用气泡替换掉;
  3. 此时没有出现副作用;

特殊控制情况

控制组合 

ret+分支预测错误

PC选择逻辑将会使用M_valM。

ret+加载/使用冒险

加载/使用冒险应该有优先选择权;

ret指令应该被保持在译码阶段以推迟一个周期。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值