更新于 

存储器建模

位单元

上一讲我们学习了存储器阵列,而存储器阵列是由位单元阵列组成的,每一个位单元只存储1位数据,每一个位单元与一个字线(wordline)和一个位线(bitline)相连,如下图:

读写数据主要是通过位线来完成的:

那么现在我们再来从整体上观察一下存储器阵列的结构:

可以得到以下几点规律:

  1. 字线与使能端类似
  2. 用于控制阵列中一行数据的读/写
  3. 对应着唯一的地址
  4. 同一时刻至多有一个字线位高电平
  5. 一个字线控制着一个数据字
  6. 当某个字线为高电平时,那么这行数据就要进行位修改或者位读取,接下来通过位线传入要修改的新的数据,或者读出位单元存储的位数据

我们要注意到由于每次只有一个字线处于高电平,因此虽然位线控制着一列位单元,但是实际上每次每一个位线上也只有一个位单元处于读/写状态,但是同一时刻,可能会有多个处于同一个字线的位单元处于读/写状态。

存储器的类型

这里实际上我们在《计算机组成原理》中介绍过,但是当时并没有从存储器阵列的角度去分析。这里我们会以存储器阵列的角度去介绍,首先我们回忆一下存储器的类型。

  • 随机访问存储器(RAM):易失的
    • 动态随机访问存储器(DRAM):计算机的主存
    • 静态随机访问存储器(SRAM):CPU中的高速缓存
  • 只读存储器(ROM):非易失的

ROM也是可以随机访问的,大多数现代ROM也已经支持的可读写功能。

DRAMvsSRAM

DRAM

DRAM是将数据存储在一个电容上,读操作后,存储的数据会被破坏,电容上的存储的电荷量会慢慢泄漏,因此为了维持数据的存在,需要频繁的进行刷新充电(读,然后写),所以被称为动态存储器。如下图:

SRAM

而SRAM是将数据存储在一个交叉耦合的反相器中,交叉耦合反相器具有很强的抗干扰能力,因此并不需要频繁的进行刷新,因此被称为静态存储器。

几种存储器的比较

存储器类型 每个位单元的晶体管数 成本 延迟
触发器 ≈20
SRAM 6 中等 中等
DRAM 1

ROM虽然也是存储器,他的读速度非常快,但是写速度很慢。

存储器端口

单端口是存储器中常见的端口,即一次性只能读/写。使用同一个端口来接受/输出数据,因此我们需要保证任何时刻,存储的端口只能处于读/写状态。

实现原理实际上就是使用了两个三态缓冲器,保证两个三态缓冲器的使能端任一时刻相反即可,这样即保证了每一次只有一个线路处于通路状态,也就保证了每次只能读入/输出数据。

寄存器文件

寄存器文件通常是一个小型的多端口SRAM存储器阵列,如下图就是一个32寄存器×32的3端口寄存器文件。

注意寄存器文件不是寄存器,他是一个寄存器堆,是CPU中多个寄存器组成的阵列,因此可以多路并发访问不同的寄存器

他有两个读端口A1/RD1和A2/RD2,还有一个写端口A3/WD3,地址线均是5位,可寻址2^5=32个寄存器。他可以同时读两个寄存器和写一个寄存器。

RAM建模

RAM是一个随机访问存储器,他需要接收地址,数据,clk时钟信号等,然后还要有输出端等,结构如下图:

那么我们接下来对他进行建模:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module ram #(parameter N=6,M=32)
(input logic clk,we,
input logic [N-1:0] adr,
input logic [M-1:0] din,
output logic [M-1:0] dout
);
//注意**是幂运算
//这里的声明mem可以看成是定义了一个mem数组
//他的每一个存储单元的数据是32位
//同时寻址范围是0~32
//因此是一个32×32的存储器阵列
//有32行字线和32列位线,每一个字线对应有32个位单元
logic [M-1:0] mem[2**N-1:0];
always_ff @(posedge clk)
if(we) mem[adr]<=din;
assign dout = mem[adr];
endmodule

上面的赋值语句当且仅当clk处于有效上升沿并且we为高电平时执行,并且注意虽然这里的赋值语句可以使用阻塞赋值,但是为了建模代码的规范,always_ff时序逻辑电路的建模中使用非阻塞赋值语句更好。

ROM建模

虽然ROM在现代的计算机中也实现了写操作,但是在我们的408学习中,一般还是认为ROM是不能进行写操作的,只能进行读操作。接下来我们同样对ROM进行建模,由于这里我们只进行读操作的建模,而读操作可以随时进行读取,并不需要等待有效沿,因此就是一个最简单的组合逻辑模块:

1
2
3
4
5
6
7
8
9
10
11
12
module rom(input logic [1:0] adr,
input logic [2:0] dout
);
//只有读出数据的操作,没有写入修改数据操作
always_comb
case(adr)
2'b00:dout<=3'b011;
2'b01:dout<=3'b110;
2'b10:dout<=3'b100;
2'b11:dout<=3'b010;
endcase
endmodule

时序

接下来我们讨论一下时序问题,我们前学习到了D触发器只有在时钟的有效沿(上升沿/下降沿)才对输入D进行采样,并赋值给Q,因此在采样的时刻,D必须处于一个稳定的状态,保持为0或者1。这个过程就如同照相,只有在被拍摄的物体静止不动时才能获得清晰的图像,Q想要得到一个明确的电平信号,那么就要求在采样D信号时,D需要保持稳定在一个确定的状态。如果在采样的过程中,D未处于稳定的状态,那么就会产生亚稳态。

输入时序约束

  • 建立时间(Setup time):t_setup=在时钟有效边沿到来前信号所需要稳定的时间
  • 保持时间(Hold time):t_hold=在时钟有效边沿到来后在采样时输入信号需要保持稳定的时间
  • 孔径时间(Aperture time):t_a=在时钟边沿附近输入信号需要维持的总时间

很明显有以下规律:

ta=tsetup+tholdt_a=t_{setup}+t_{hold}

输出时序约束

前面我们讲到的仅仅是在clk有效时对采样信号D的时序约束要求,但是采样完成以后赋值给Q还有一定的时间。这里的输出时序同样需要加上约束:

  • 传播延迟(Propagation delay):t_pcq=时钟有效边沿到达后到Q最终稳定所需要的最长时间
  • 最小延迟(Contamination delay):t_ccq=时钟有效边沿到达后到Q开始改变所需要的最短时间

动态约束

我们在学习了几个有关约束的概念以后,需要添加动态约束,首先同步时序电路中,输入必须在时钟有效边沿附近的孔径时间内保持稳定。即输入信号必须

  1. 在时钟有效边沿到达前,至少要稳定t_setup
  2. 同时在时钟有效边沿到达后,至少要稳定t_hold

系统时序

时钟周期Tc是指两个时钟上升沿(下降沿)之间的间隔

fc=1/Tc,表示时钟频率f_c=1/T_c,表示时钟频率

那么提高时钟频率(也就是缩短时钟周期)就可以增加数字系统在单位时间完成的工作量,但是频率不能无限制的增加。

如上图所示,两个寄存器间的延迟具有最小和最大延迟,这些延迟由其中的电路元件的延迟所决定。很明显D2想要变化,首先需要Q1改变完成并稳定,因此T_c有一个最小值,同样的也会有一个最大值。

建立时间约束

建立时间约束由路径R1至R2间的最大延迟所决定:

  • 寄存器的传播延迟t_pcq
  • 组合逻辑电路的传播延迟t_pd

我们通过上图可以容易得到结论,寄存器R2的输入信号D2必须在下一个时钟上升沿的t_setup时间前稳定。

同时D2稳定至少需要t_pd+t_pcq。因此有以下约束公式:

{Tc>=tpcq+tpd+tsetuptpd<=Tc(tpcq+tsetup)\begin{cases} T_c>=t_{pcq}+t_pd+t_{setup}\\ t_{pd}<=T_c-(t_{pcq}+t_{setup}) \end{cases}

tpd<=Tc(tpcq+tsetup)t_{pd}<=T_c-(t_{pcq}+t_{setup})

上式就被成为建立时间约束或者最大延迟约束,即上式限制了我们在设计组合逻辑电路的最大延迟。

在商业设计中:

  • Tc是由研发总监和市场部提出的,以确保产品的竞争性
  • 制造商确定触发器的传播延迟t_pcq和建立时间t_setup
  • t_pcq+t_setup被称为时序开销,由芯片的生产工艺决定
  • 而通常,只有t_pd即组合逻辑电路的最大时间延迟是我们设计人员可以控制的变量,因此我们在设计组合逻辑电路时要保证设计出来的t_pd小于建立时间约束

保持时间约束

保持时间约束由路径R1至R2间的最短延迟所决定:

  • 寄存器的最小延迟t_ccq
  • 组合逻辑电路的最小延迟t_cd

寄存器R2的输入信号必须在时钟上升沿后至少稳定t_hold,也就是D2稳定的时间必须小于变化值传来的时间,毕竟他要改变值了,不能继续hold维持原先的值了,因此规律是:

{thold<=tccq+tcdtcd>=tholdtccq\begin{cases} t_{hold}<=t_{ccq}+t_{cd}\\ t_{cd}>=t_{hold}-t_{ccq} \end{cases}

tcd>=tholdtccqt_{cd}>=t_{hold}-t_{ccq}

上式被称为保持时间约束或者最小延迟约束,其限制了我们设计的组合逻辑电路的最小延迟,因此组合逻辑电路延迟不能太大也不能太小。下面我们介绍一种特殊的情况,触发器背靠背相连:

此时触发器之间没有组合逻辑电路,因此t_cd=0,那么如果要不违反保持时间约束,需要保证

thold<=tccqt_{hold}<=t_{ccq}

因此一个可靠的触发器,保持时间要比最小延迟短。这样才能保证变化的值不会在D2还在稳定的状态时就抵达。

  1. 在实际应用中,经常将触发器设计成t_hold=0,来保证保持时间约束在各种情况下都可以满足
  2. 教材中如非特别注明,后面的讨论会忽略保持时间约束
  3. 保持时间约束又非常重要,如果一旦违反保持时间约束,必须重新设计电路,这一点与建立时间约束不同,如果违反了建立时间约束,可以通过调整时钟周取Tc来修正,但是保持时间约束无法这样修正
  4. 因此一旦违反了保持时间约束后果很严重
思考:建立时间约束和保持时间约束的关系以及由来?

可能我们会有点懵,不知道建立时间约束和保持时间约束的由来,下面我们来总结一下两个D触发器的工作原理。

如上图,两个D触发器有一个CLK控制,并且R2的输入信号是R1输出信号经过组合逻辑电路进行计算加工得到的。因此在第一个CLK上升沿抵达后Q1会更新,然后会用组合逻辑电路进行计算得到D2,我们需要保证在第二个CLK上升沿抵达前D2已经稳定得到了Q1根据组合逻辑电路计算出来的新信号值,因此有一个建立时间延迟。同时在第一个CLK抵达后第二个CLK抵达前,Q1很快就会变化完成并且经过组合逻辑电路计算得到了新的修改值可以传递给D2了,但是D2并不是得到修改值后马上就能变化,他需要保证维持一个t_hold来保证R2的输出Q2可以正确得到D2的值,当Q2稳定得到D2的值以后D2才能变化更改为新的值,因此新的值不能计算抵达的太快,他需要晚于t_hold,因此也就是保持时间约束。两者共同限制了组合逻辑电路延迟不能过长也不能过短。

时序分析

我们前面学习了时序问题的约束条件,那么接下来我们来以一道例题学习一下时序分析。如下图:

根据题干我们可以知道

{tpd=35ps3=105pstcd=25ps\begin{cases} t_{pd}=35ps*3=105ps\\ t_{cd}=25ps \end{cases}

因此

{Tc>=tpcq+tpd+tsetup=50+105+60=215pstcd=25ps,tholdtccq=7030=40ps\begin{cases} T_c>=t_{pcq}+t_{pd}+t_{setup}=50+105+60=215ps\\ t_{cd}=25ps,t_{hold}-t_{ccq}=70-30=40ps \end{cases}

我们得到最大时钟周期至少为215ps,而t_cd=25ps小于要求的40ps,也就是说延迟太小了,违反了保持时间约束。

思考:如何修改电路使其不违反保持时间约束?

我们只需要将t_cd提升即可,我们发现之前的最短路径是只经过一个门,因此出现t_cd=25ps。我们可以修改电路为使其至少要经过两个门,因此在只经过一个门的电路上增加一个缓冲器门,如下图:

缓冲器就是仅仅减缓了信号传递的速度,这样t_cd就至少为50ps>40ps了,因此也就不违反保持时间约束了。