时序逻辑电路的输出是由上一刻的输出和这一刻的输入共同决定,也就是说时序电路带有记忆。

锁存器和触发器

双稳态电路

例如Q是1,则经过L1后变为0,因此$\bar{Q}$ 为0,然后又给L2输入,再回到Q还是1。所以Q始终为1,$\bar{Q}$始终为0,因此称为双稳态电路。

这是它的等价形式,注意这张图是有问题的,L1和L2要变换位置。

SR锁存器

上面的双稳态电路不方便输入,因此SR锁存器在双稳态电路上做了改进。

如图,添加了R和S两个输入端,当R和S为下列值时,Q输出为

R(reset) S(set) $Q^+$(Q下一时刻状态)
0 0 Q(保持)
0 1 1(置位)
1 0 0(复位)
1 1 x

主要是二者都为1的情况。二者都为1时,Q和$\bar{Q}$都是0,这就违背了Q和$\bar{Q}$的本意。

并且在二者变成0(保持状态)之后,如果是同时变成0,那么Q和$\bar{Q}$先同时变成1,然后同时变成0,这样来回震荡。

当然同时改变时不可能的,如果R先变成0,那么Q为1,$\bar{Q}$为0。反之Q为0,$\bar{Q}$为1.

因此二者同时为1时会造成电路不稳定,因此二者同时为一是禁止的。

D锁存器

  • CLK=0, $Q^+$ = Q,也就是锁存器的值不会变化,锁存器是不透明的
  • CLK=1, $Q^+$ = D, 这时候可以把它当成一根导线,锁存器是透明的(没有作用)。

这是他的原理图,他在SR锁存器上做了一些补充,避免了因为S和R同时为1导致的一些奇奇怪怪的现象。

D触发器

D触发器时在CLK从0到1的瞬间对D进行采样,然后一直保持这个状态,知道下一次从0到1才会重新改变。

当CLK=0时,主锁存器开启,也就是说D的状态就是N1的状态,但是从锁存器关闭,总的输出还是上一时刻的输出。

CLK=1,从0到1的一瞬间把主锁存器关闭,也就是说N1的状态就是D最后时刻的状态,然后从锁存器打开,N1的状态直接输出

寄存器

寄存器是由一个共享的CLK控制多个D触发器组成。

带使能端的触发器

EN=0时,使能端处于保持状态(也就是CLK=0的状态)。EN=1时,不影响正常工作。

如图是两种使能端的实现方式。看起来第二种方法要好,但实际上第一种方法更优。因为第二种方法会影响CLK工作(不同的与门所需时间不同),可能导致原本的同步电路变成异步电路(如上面的寄存器)

带复位或置位功能的触发器

当复位为1时,触发器被置0, 复位为0时不影响电路正常工作。

复位分为两种形式:

  • 同步复位: 只在上升沿复位
  • 异步复位: 只要reset位置1就进行复位


如图是一种同步复位的实现方式。

而置位就是将触发器置为1,和复位大致相同。

同步时序电路

同步时序电路中有一个时钟输入,时钟的上升沿表示电路发生改变的时间。当前状态称为现态,下一个状态称为次态

同步时序电路的组成规则

  • 电路中的模块是寄存器或组合逻辑电路
  • 模块中至少包含一个寄存器
  • 所有寄存器都共用一个信号
  • 电路的每个环路都至少包含一个寄存器

例如是同步时序电路,而

不是时序逻辑电路,因为它是锁存器不是触发器

两种常见的同步时序电路: 有限状态机和流水线

有限状态机

这两种有限状态机都受时钟信号的控制,寄存器前面是用来决定下一个状态的,它由输入和当前状态共同决定。而触发器后面就是处理信号模块。

moore机的输出信号不由输入信号直接决定,并且它只有在时钟上升沿才会发生改变。

mealy机的输出信号直接由输入信号决定,并且它在任意时刻输出都有可能改变。

例如: 交通信号灯的设计

输入: 两个交通传感器 $T_A , T_B$.当有人经过时返回True

输出,两个交通信号灯$L_A , L_B$.他们都有红绿黄三种状态

有一个5s的时钟,当时钟到达时,根据传感器的情况进行改变。此外还有一个复位键。

  • 画状态转换图.如果当前是绿灯并且有行人通过不做改变,如果没有行人通过就变成黄灯,5s后变成红灯

  • 画出状态转换图纸后,将其转换为状态转换表
现态 输入 次态
S1 S0 Ta Tb S1' S0'
0 0 0 x 0 1
0 0 1 x 0 0
0 1 x x 1 0
1 0 x 0 1 1
1 0 x 1 1 0
1 1 x x 0 0

我们认为00是S0状态,01是S1状态,以此类推。也就是说四种状态需要两个二进制位,需要两个触发器。

同理,我们可以画出输出的状态转换表

由于输出有红绿黄三种状态,所以同样需要两位输出表示状态。用00表示绿,01表示黄,10表示红。

  • 化简

当我们把真值表画出来后,就可以用卡诺图或者直接进行化简了。

例2:

一个蜗牛在一条写满0和1的纸带上爬行。当它爬过的最后两位是01时,蜗牛会对着你微笑。请设计一个Mealy型状态机来模拟蜗牛的行为。

  • 状态转换图

除号左边是当前输入,右边是输出。例如在状态S0时输入0,输出为0,如果输入一直是0那么转到状态S1.

从电路导出状态机

上图是一个开锁器的电路,我们需要根据这个电路导出状态转换图。

  • 首先得出次态方程和输出方程:
  • 之后根据方程写出状态转换表

可以看到其他三个状态中没有任何一个次态会转到11,也就是11状态不可达,因此可以直接把11状态删去

因此一共有三个状态00, 01, 10,然后根据状态转换表在这三种状态中进行转换即可

时序问题

在时钟信号到来时有一段时间内输入信号是不可以改变的,贸然改变可能会导致如前面那样的不稳定状态。

  • 建立时间: 在时钟信号到来前输入信号需要稳定的时间
  • 保持时间: 在时钟信号到来后需要保持稳定的时间。
  • 孔径时间: 也就是总的需要保持稳定的时间,等于建立时间+保持时间

  • 最小延迟($t_{ccq}$): 在时钟有效沿到达后后面的器件开始改变的最小时间
  • 传播延迟($t_{pcq}$): 改变的最大时间

首先$t{pcq}$是寄存器$R_1$的传播延迟. $t{pd}$可以看成中间的组合逻辑电路的传播延迟,而最后的$t_{setup}$是建立时间。

也就是说一个时钟周期的时间最少为$t{pcq} + t{pd} + t{setup}$,假设时钟周期为$T_c$,即$t{pd} \le Tc - (t{pcq} + t_{setup})$

由于$Tc$是由研发总监和市场部提出,而$t{pcq}和t{setup}$是由采购的硬件决定,这两个都没有办法改变,因此只有缩短$t{pd}$才能保证没有不稳定状态。

这里指的是传播延迟,可能存在寄存器和组合电路的最小延迟之和小于保持时间,这会导致还没有过保持时间但是D2已经开始改变。因此

时序逻辑建模(always_ff)

always过程块的结构如下

always @(sensitivity list)
statements;
@后面的是敏感事件列表,只有触发敏感事件才会激活always块

always_ff用来表示触发器,例如:

module flop ( input   logic          clk, 
input logic [3:0] d,
output logic [3:0] q);
always_ff @(posedge clk)
q <= d;
endmodule

它的输出为:

可以看到,通过这种建模结果会有寄存器出现。

  • posedge: 表示这个变量会在上升沿触发
  • <=: 非阻塞性赋值,也就是说这些语句都是并发的,会一起执行。这就需要通过寄存器进行同步了,例如下面这个例子
module syncgood( input  logic  clk,
input logic d,
output logic q);
logic n1;
always_ff @(posedge clk) begin
n1 <= d; // 非阻塞赋值
q <= n1; // 非阻塞赋值
end
endmodule

输出为

我们可以把它当成一个次态方程,每一次触发时右边的值都会同步的赋值给左边的值。

而如果不使用非阻塞性赋值

module syncbad(input  logic clk,
input logic d,
output logic q);
logic n1;
always_ff @(posedge clk) begin
n1 = d; // 阻塞赋值
q = n1; // 阻塞赋值
end
endmodule

我们也可以添加reset位

module flopr ( input   logic          clk,
input logic reset,
input logic [3:0] d,
output logic [3:0] q);
always_ff @(posedge clk)
if (reset) q <= 4'b0;//同步复位
else q <= d;
endmodule

module flopr ( input logic clk,
input logic reset,
input logic [3:0] d,
output logic [3:0] q);
always_ff @(posedge clk, posedge reset)//异步复位
if (reset) q <= 4'b0;
else q <= d;
endmodule

时序逻辑建模的应用

移位计数器

功能:在每一个时钟内,每一个寄存器的内容都向右移动一位,最前面的位移入$S_{out}$

作用: 可以把并行变成串行,可以用在网络传输中。

我们可以对他稍作改进,可以一次性输入这么多位

$D0 , D_1 , … , D{N-1}$是输入,而Load决定是否需要输入。每过一个时钟周期所有寄存器都会接受前一个的输入。建模语句为:

module shiftreg #(parameter N=8)
( input logic clk,
input logic reset, load,
input logic sin,
input logic [N-1: 0] q
output logic [N-1: 0] q
output logic sout );
always_ff @(posedge clk, posedge reset)
if (reset) q <= 0;
else if (load) q <= d;
else q <= { q[N-2 : 0], sin };
assign sout = q[N-1];
endmodule

有限状态机的建模

这是一个典型的moore型状态机,它的建模语句为

module divideby3FSM ( input  logic  clk, 
input logic reset,
output logic q);
typedef enum logic [1:0] {S0, S1, S2} statetype;
statetype [1:0] state, nextstate;
// 寄存器
always_ff @ (posedge clk, posedge reset)
if (reset) state <= S0;
else state <= nextstate;
// 次态逻辑
always_comb
case (state)
S0: nextstate = S1;
S1: nextstate = S2;
S2: nextstate = S0;
default: nextstate = S0;
endcase
// 输出逻辑
assign q = (state == S0);
endmodule

输出逻辑和寄存器并列是因为输出是时时刻刻受到寄存器的输出影响的,只要寄存器输出改变,他就会改变

存储器阵列

Address是地址总线,有n位最多可以取$2^n$个数据。而Data是数据总线,表示一次可以取多少位数据。

例如Address=10, 那么我们可以取100,或者将101写入地址10处。

将上述逻辑展开就可以得到如图的结构了。其中字线如果为1就表示这条线可以读取或写入。其中每一个存储单元可以是DRAM存储单元或SRAM存储单元

DRAM
SRAM

此外,如果想要同时实现读写的功能,可以在数据接口处使用