时序电路

小孩子 我们都是小青蛙 2018-06-04

点击蓝字,关注我们


关注

本系列主要是《数字设计和计算机体系结构》和一些相关书籍的读书笔记,并没有专门为读者的阅读体验认真做设计,敬请谅解(时间不多)。


时序电路是我们理解CPU工作的基础,理解了这个才能理解指令是如何执行的,为何有的指令执行的快,有的执行的慢,为什么需要指令流水线等等吧啦吧啦的,所以需要认真看一下。


锁存器(latch)

前边说时序电路中输入改变了,输出可能不立即改变,这是因为输出该电路的输出取决于当前的输出以及新的输入,这么说太绕了,不如直接上图:


这个电路输入是R、S,输出是Q和Q,可是看起来奇怪的是,输出Q和Q又作为整个电路的输入。虽然很奇怪,我们分析一下看:

  • 当R=1,S=1时,由于N1,N2两个元件都是或非门,所以只要输入是1,那么输出肯定是0,所以最后:Q=0,Q=0。

  • 当R=0,S=1时,由于S=1,所以Q=0,由于N1的两个输入都为0,所以Q=1,也就是说最后的输出为:Q=1,Q=0。

  • 当R=1,S=0时,由于R=1,所以Q=0,由于N2的两个输入都为0,所以Q=1,也就是说最后的输出为:Q=0,Q=1。

  • 当R=0,S=0时,由于N1的输入是0Q,现在我们并不知道Q的值是啥,所以N1的输出无法确定;同理,N2的结果也不能确定,但是Q或者Q的值肯定不是0就是1,现在假设一下:

    • 假设Q=0,N2的两个输入都是0,所以Q=1,现在N2的两个输入分别为0和1,所以Q=0,假设不冲突,这种结果成立。

    • 假设Q=1,N2的输入分别是0和1,所以Q=0,现在N2的两个输入都是0,所以Q=1,假设不冲突,这种结果成立。

    这就意味着:当我们把R、S都设置为0时,最后的输出和先前的输出保持一致,也就是说,虽然我们改变了输入,但是输出与原先保持一致。这个也就是所谓的记忆功能

综上所述,这个电路的真值表如下:

SRQQ
1100
0101
1010
00QpreQpre

我们对这个电路的期望是:

  • Q和Q互为反值。

  • 当把S置为1时,会让Q=1。

  • 当把R置为1时,会让Q=0。

  • 当把S、R置为0时,不改变Q的值。

我们不允许S、R同时为1的情况发生,因为这样会让Q和Q的值不是相反的,与我们预期不符。这个电路称为SR锁存器,因为它好像可以把Q的值锁住一样,画一个简化的图就是这样:


这个所谓的SR锁存器能帮我们保持1个二进制位的状态,S称为置位,也就是把S从0调到1时会把最后的输出设置为1;R表示复位,也就是把R从0调到1时会把最后的输出设置为0,当S、R都为0时,保持旧值不变。

带时钟的SR锁存器

这时候只要我们想改变SR锁存器中Q的值,只需要改变S、R的值就好,我们想让这个电路的输出Q在一段时间内是稳定的,也就是说即使我们改变S、R的值,也不会影响到最终Q的值。很容易想到在S、R的值都为0时最后的输出是稳定的,那我们只需要再加一条输入信号,在该输入信号为0时,无论怎么改变S、R的值,最终的输出Q都是稳定的,当该输入信号为1时,才可以通过改变S、R的值实现置位和复位操作,怎么在该输入信号为0时让S、R的值为0,在该输入信号为1时让S、R的值为自己的值呢?解决方案就是使用与门来进行连接,如图:


如图所示,我们用输入CLK来当作控制信号,当CLK=0时,无论R、S的值是什么,最后输入到SR锁存器的值都是0,所以最终的输出Q是稳定的;当CLK=1时,输入R、S才有效。

这样我们可以通过控制CLK信号来控制何时修改SR锁存器的输出Q,这个CLK信号也称为时钟信号。小小的一根时钟信号控制线,甚至为后边复杂的CPU设计打开了一扇窗,这个电路的简化示意图如下:

D锁存器

对于SR锁存器来说,虽然我们禁止S、R同时为1,但是你要是非得把它们设置成1那也没办法,如果想从根上避免掉这种人为的失误,我们需要从头来考虑一下我们的需求,我们就是想:在CLK=1时,可以通过修改输入来修改输出Q的值。但是我们没必要一定给对用户提供两个输入按钮,一个输入按钮就够了,当该输入为0时,将Q设置为0,当该输入为1时,将Q设置为1。但是这个有需要在CLK=0时保持原先的Q输出,我们可以这样修改一下加了时钟信号的SR锁存器:

这样,当D=0是,原先的S=1,R=0,当D=1时,原先的S=0,R=1,这样就不会出现R和S同时为1的情况了。当时钟为0时,原先的S=R=0,输出Q保持不变,皆大欢喜啦啦啦啦,画一个简化的电路图:

时钟是怎么产生的

让我们来看一个比较奇葩的电路:

这个奇葩电路没有输入,是由一个非门围成的一个环路,当n1处是0时,n2便输出1,然后n1立即变成了0,接着n2又变成了1 … 这也就意味着电路的输出一直在0和1之间变化,这样的电路我们称之为振荡器

我们知道每个元件都有它自己的传播延迟,不同的元件可能制造工艺不同,电源电压不同、或者温度不同而造成不同的传播延迟,因此这样的振荡器输出变化的频率很难预测,所以如果我们需要更稳定的时钟频率,就需要用一些更高端的方式产生固定的时钟频率。通常为了得到固定频率的振荡器,通常使用石英晶体来制作石英晶体振荡器,石英就是二氧化硅,我们平常说的石英表就是利用石英来产生连续稳定的振动频率来驱动我们的表来计时。这石英产生固定稳定的频率的原理是个啥呢?说实话,我找了些资料瞅了一会,没看懂,等我看懂了再写篇文章哈~ 现在只要知道我们能自动生成这样输出的电路就好了:

如图,输出电压以Tc为周期,电压从0->1的过程称为上升沿,从1->0的过程称为下降沿。

D触发器

上边介绍的D锁存器中,在时钟信号CLK保持为1时,我们就可以通过控制D的输入来改变输出Q,这种在时钟信号CLK保持为1时可以修改输出的方式我们称之为电平触发,我们期望一种只在时钟从0到1这个变化的过程中使输出改变的方式,而在时钟信号CLK保持为1时,再修改D的输入是不会造成输出Q的变化的。为了做到这个,设计了这个电路:

如图所示,我们这里用到了两个D锁存器,左边的那个直接和输入D连接的称为主锁存器,后边那个称为从锁存器。我们看一下这个电路是怎样工作的:

  • 当CLK=0时,主锁存器导通,数据其实是到达了主锁存器的Q输出。但是从锁存器不导通,所以无法输出到从锁存器的Q输出。

  • 当CLK=1时,从锁存器导通,刚刚在主锁存器输出Q处的数据会进入到达从锁存器的Q输出。但是此时主锁存器是不导通的,如果输入D发生改变,是不能到达主锁存器的输出Q的。

所以,只有在时钟信号CLK从0-1的瞬间才能实现最终输出的改变,当时钟信号CLK保持1不变的时候,D的改变是进入不到主锁存器的,也就是不会影响最终的输出。我们把时钟从0-1的变化称为上升沿,相应的,从1-0的变化称为时钟下降沿。这样只在时钟上升沿将输入D复制到最终输出Q,其他时间最终输出Q保持不变的这个电路称为D触发器(flip-flop),或者也可以称为主从触发器边沿触发器

这个D触发器已经是有点儿复杂了,我们再来个简图:

注意其中的时钟信号CLK输入下边我们加了个小尖头,这个表示只有在时钟上升沿才能把D的值复制到Q。由于这个玩意儿我们之后会极其高频率的使用到,这个电路符号还是有点儿大,我们再简化一下(本质功能就是在时钟上升沿把输入D的值修改到Q,Q的值和Q是相反的,我们不在乎):

寄存器

因为一个D触发器可以保存1位二进制数据,如果我们多弄些D触发器,让它们受一个时钟信号控制,就像这样:

这样的话,一次时钟的上升沿就可以把4位二进制数存到对应的触发器中,我们把这玩意儿称为寄存器。很容易的,我们可以弄8位、16位、32位、64位的寄存器,只是保存的二进制位数多些,本质上的原理是相同的。为了给这玩意儿画个简单的示意图,如果有4个触发器,我们得画4根线,16个触发器,得画16根线,64个触发器,得画64根线,这样画的人会累死,看的人也会累死,所以我们简单的在线上写个数字代表有几根线,就像这样:

这样就表示一个4位寄存器,输入D3:0代表4个输入,分别是是D3、D2、D1和D0,Q3:0代表4个输出,分别是是Q3、Q2、Q1和Q0

同步时序电路

非稳态电路

像我们上边提到的这个电路:

输出的状态永远不会稳定,我们城这样的电路为非稳态电路

竞态条件

来看这个电路:


当CLK=D=1时,n1=1,n2=0,所以最终输出Q=1。当CLK从1下降到0时,这时n1、n2的改变顺序可能导致不同的输出有两条线可能改变Q:


  • n1先改变,此时n1=0,n2=0,Q先变为0,然后n2改变,因为此时与n2相连的两个输入分别是CLK=1,Q=0,所以最后输出n2=0,最终Q=0就确定了。

  • n2先改变,因为与n2相连的两个输入分别是CLK=1,Q=1,所以n2=1,所以最终Q=1。

可见,当改变时钟信号时,n1,n2改变的顺序会对最终输出产生影响,这个我们称之为竞态条件。

引入同步时序电路

我们有时候非常不想在电路中产生非稳态电路和竞态条件,但是有时候不小心自己就给造了一个,这种风险随着电路规模的增大而增大。我们回过头来分析一下非稳态电路和竞态条件产生的原因,都是因为电路中包含了不正确的环路所造成的,而我们之前介绍的组合电路是没有这个问题的,所以为了避免产生非稳态电路和竞态条件,我们可以使用寄存器来断开电路中出现的环路,剩下的电路全部都是组合电路,这时只要时钟足够慢,那么可以在一个时钟周期中使组合电路中的变化全部存储到寄存器中(因为只在时钟的上升沿改变寄存器的值),在下一个时钟周期开始后,寄存器的值才会传播到下一个组合电路,同时接收上一个组合电路传过来的新值。这个过程就好像很多人在接力跑一样,如下图:

每个寄存器我们用小黑块表示,组合电路用不同颜色的长方形表示,长方形的长度代表这段信号经过这个组合电路需要的时间,信号就像是运动员一样在这个图上跑,时钟信号就像是裁判,裁判每一吹一次哨子就像是时钟的上升沿一样,信号就沿着组合电路进行传播。但是这个接力赛是有要求的:

  1. 裁判每吹一次哨子,所有的信号往前前进一个组合电路。

  2. 不管信号跑得有多快(通过组合电路所需要的时间),到达下一个寄存器之前都必须停下来,等待下一次吹哨。

所以整体看起来就是,每当时钟上升沿到来,所有的信号往前前进一下,非常有节奏感。当然,这样的同步时序电路的要求也是明显的,就是在两次吹哨的间隔中,所有的信号都已经到达它前方的寄存器,或者说所有的组合电路都已经到达了一个稳定状态,所以时钟周期的大小(两次吹哨的时间间隔)取决于信号跑的最慢的那个组合电路(就是图中的组合电路2)。当然,时钟周期长一点没关系,你哨子24小时吹一下都可以,反正信号都已经到达寄存器那里了,只不过多等一会而已~

总结一下同步时许电路的特点:

  • 电路元件只能包含寄存器或者组合电路

  • 至少有一个元件是寄存器

  • 所有寄存器接受同一个时钟信号

  • 每个环路至少包含一个寄存器

当然与此相对的就是异步电路,这种电路不用受哨子影响,所以跑起来更快,但是设计起来更复杂,更困难,所以我们现在主流的CPU都是采用这种同步时序电路。

如何确定时钟周期长短

我们以下边这个电路为例:

信号从D1处传播到Q2处需要2个时钟周期:

  • 第1个时钟周期:D1 -> Q1 -> D2

  • 第2个时钟周期:D2 -> Q2

那我们就看一下这个D1 -> Q1 -> D2路线都在哪些地方花费了时间,把这个时间加起来就是最短的时钟周期时间。

  1. 从D1 -> Q1,也就是当时钟周期上升时,输入的值需要花费多长时间才能通过R1寄存器,这个就涉及到寄存器的传播延迟了。

  2. 从Q1到D2,这个部分是通过组合逻辑需要的时间,也就是该组合逻辑的传播延迟。

  3. 信号到达D2就完了么?在下一个时钟周期的上升沿就可以被传播到Q2了么?不不不,由于寄存器内部电路限制,在时钟周期上升前需要将D2的之保持一段时间,这个时间称为建立时间

我们根据上边3点画个图:

如图所示,一个时间周期的时间由这三部分组成

  • tqcq:指的是寄存器R1的传播延迟,也就是当时钟上升沿发生时,信号从D1稳定传播到Q1所花的时间。

  • tqd:指的是信号从Q1经过组合逻辑后到达D2的传播延迟时间。

  • tsetup:指的是寄存器R2的建立时间。

所以,最小时钟周期就是这么计算的:

Tc ≤ tqcq + tqd + tsetup

寄存器的输入除了需要在时钟上升沿到来之前要保持一段时间,也就是我们所说的建立时间之外,在时钟上升沿到达之后也可能需要使输入保持稳定一段时间,这个时间称之为保持时间,建立时间和保持时间都是寄存器的固有属性,至于为什么要有这个时间,可以参考这篇文章:Understanding the basics of setup and hold time,其实是和寄存器,或者说触发器的实现有关,这里我们就不多唠叨了。

因为寄存器需要保持时间,还以上图中的例子为例,当第二次时钟上升沿到来时,新的D1值将按照D1-> Q1 -> D2的顺序传播,这时为了使R2保持足够的保持时间,我们的图可以这样修改一下:

其中:

  • tccq:指的是寄存器R1的最小延迟,也就是当时钟上升沿发生时,信号从D1最快传播到Q1所花的时间。

  • tcd:指的是信号从Q1经过组合逻辑后到达D2的最小延迟时间。

这时,我们为了让寄存器R2有足够的保持时间,也就是th时间(维持D2输入在时钟上升沿后th时间内保持不变),必须满足这样的条件:

th ≤ tccq + tcd

也就是寄存器和组合电路的最小延迟别太小,如果太小的话,那么在小于寄存器的保持时间的时间内新的信号就已经到达D2,就不能满足R2的保持时间,这样就可能造成最终输出Q2的不稳定。

    觉得不错,分享给更多人看到
    我们都是小青蛙 热门文章:

    java并发编程之原子性操作    阅读/点赞 : 0/0

    我们都是小青蛙,呱呱呱呱呱    阅读/点赞 : 0/0

    活跃性(死锁、饥饿、活锁)    阅读/点赞 : 0/0

    指令重排序    阅读/点赞 : 0/0

    InnoDB的Buffer Pool简介    阅读/点赞 : 0/0

    一些比较重要的数字电路模块    阅读/点赞 : 0/0

    抽象的艺术    阅读/点赞 : 0/0

    内存可见性    阅读/点赞 : 0/0

    存储程序(二)之存储函数简介    阅读/点赞 : 0/0

    线程间通信(下)    阅读/点赞 : 0/0

    我们都是小青蛙 微信二维码

    我们都是小青蛙 微信二维码