by yang
最近准备数字IC岗时复习整理的知识点,参考了比较火的fpga面试题,和一些相关知识。主要是写着自己看着方便的,有很多不严谨的地方,有些地方有参考链接,那些博客写的都很好。
Part.3
同步与异步
1:同步电路与异步电路
2:多时域设计中,不同时钟域数据如何交换? ★
3:同步时序电路 & 异步时序电路 如何实现延时?
4:非同源时钟如何同步化?
同步与异步
1:同步电路与异步电路
同步时序电路:用各种触发器实现,主要信号与OUT都是一个时钟沿经过各种触发器触发的。所以只有时钟脉冲到来才能改变电路的状态,所以不论IN如何变化,状态表中的每个状态都是稳定的。
异步时序电路:用组合逻辑电路(与非门)实现,电路中没有统一的时钟,典型的有FIFO/RAM读写信号、地址译码器等。主要信号与OUT 不是某个时钟信号驱动FF产生的。
核心区别在于是否所有触发器都与唯一时钟脉冲同步。always后面是变量还是沿。比如一个触发器的输出连接到另一个触发器的时钟端去触发就是异步。
同步时序很好避免毛刺,异步时序电路的最大缺点是容易产生毛刺。同步电路的问题在于时钟偏差Clock skew
.
比如a+b=c.如果ab不同步,会有毛刺。
PS:阻塞赋值生成组合逻辑,非阻塞生成时序逻辑。
2:多时域设计中,不同时钟域数据如何交换? ★
不同的时钟域之间信号通信时需要进行同步处理,这样可以防止新时钟域中第一级触发器的亚稳态信号对下级逻辑造成影响。
- 当单个信号跨时钟域时,可以采用两级触发器(一位同步器)来同步;
- 数据流或地址总线跨时钟域时可以采用异步FIFO(或双口RAM)来实现时钟同步;
- 多位数据可以采用保持寄存器加握手信号的方法(多数据,控制信号,地址信号)。
3:同步时序电路 & 异步时序电路 如何实现延时?
异步电路一般是通过插入1个buffer、两级非门等,但这是不适合同步电路实现延时的。
在同步电路中的延时,一般通过时序控制实现,作为电路逻辑进行设计。
1)对于比较大的和特殊要求的延时,一般通过高速时钟产生计数器,通过计数器来控制延时;
2)对于比较小的延时,可以通过D触发器打一拍,延时了一个时钟周期,而且完成了信号与时钟的初次同步,在输入信号采样和增加时序约束余量中使用。
像#5 这种语句是“行为级代码描述”,仿真可以,电路综合会被忽略。
4:非同源时钟如何同步化?
当系统中有两个或两个以上非同源时钟的时候,数据的建立和保持时间很难得到保证。
我们可以使用带使能端的 D 触发器,并引入一个高频时钟(频率高于系统中的所有源时钟) ,便可以达到使系统中所有源时钟同步的效果。
例:系统时钟设计:
系统有两个不同源时钟,一个为 3MHz,一个为 5MHz,不同的触发器使用不同的时钟。为了使系统稳定,假设我们引入一个 20MHz 时钟,那么这个 20MHz 的时钟怎么才能将 3M和 5M 时钟同步化呢?
20M 的高频时钟将作为系统时钟,输入到所有触发器的的时钟端。3M_EN 和 5M_EN将控制所有触发器的使能端。即原来接 3M 时钟的触发器,接 20M 时钟,同时 3M_EN 将控制该触发器使能,原接 5M 时钟的触发器,也接 20M 时钟,同时 5M_EN 将控制该触发器使能。这样我们就可以将任何非同源时钟同步化。
异步信号输入总是无法满足数据的建立保持时间, 所以建议大家把所有异步输入都先经过双触发器进行同步化。
FIFO问题 ★★
深入理解FIFO
FIFOFirst In First Out
是一种先进先出的数据缓存器,与普通存储器的区别是没有外部读写地址线,这样使用起来非常简单,但缺点就是只能顺序写入数据,顺序的读出数据, 其数据地址由内部读写指针自动加1完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。
FIFO一般用于不同时钟域之间的数据传输,或不同宽度的数据接口相连。F根据FIFO读写时钟域是否相同,可以将FIFO分为同步FIFO和异步FIFO。
FIFO的常见参数:
- 宽度:即FIFO一次读写操作的数据位;
- 深度:指的是FIFO可以存储多少个N位的数据(如果宽度为N)。
- 满标志,空标志,读时钟,写时钟
- 读指针:总是指向下一个将要被写入的单元,复位时,指向第1个单元(编号为0)
- 写指针:总是指向当前要被读出的数据,复位时,指向第1个单元(编号为0)
FIFO设计的难点在于怎样判断FIFO的空/满状态。异步FIFO需要解决跨时钟域传递为了保证数据正确的写入或读出,而不发生溢出或读空的状态出现,必须保证FIFO在满的情况下,不能进行写操作。在空的状态下不能进行读操作。怎样判断FIFO的满/空就成了FIFO设计的核心问题。
空/满检测: 当读写指针相等时~
- FIFO 空:如果是复位或读操作引起(当读指针读出FIFO中最后一个字后,追赶上了写指针时)
- FIFO 满:如果是写操作引起
- 还可以用多加一位extra bit来记录折回,如果extra也一样代表空,不一样代表写满
写操作无条件清除空标志;读操作无条件清除满标志。
而异步FIFO不同时间域,将指针同步到其它时钟域时,用两级FF传递格雷码地址防止亚稳态,gray码可以保证即使亚稳态数据不准,也能正确判断空满,需要一个always块专门进行bin2gray的转换。gray码每次只变一个寄存器一位,这样仅仅1bit可能产生亚稳态。采错最多错到上一位去,不会有影响。
6个模块实现:顶层;双口RAM;2个跨域同步指针模块;空判断逻辑;满判断逻辑;
- 根据异步FIFO的设计架构,归纳以下设计步骤:
写时钟域:
(1)根据写使能wr_en和写满标志位wr_full产生二进制写指针
(2)根据二进制写指针产生双端口RAM的写地址
(3)由二进制写指针转换成格雷码写指针
(4)对格雷码读指针在写时钟域中进行两级同步得同步后格雷码读指针
(5)同步后格雷码读指针转化成同步后二进制读指针
(6)步骤(3)与步骤(4)比较得写满标志位wr_full
(7)步骤(1)与步骤(5)相减得指示写FIFO的数据量
读时钟域:
(8)根据读使能rd_en和读空标志位rd_empty产生二进制读指针
(9)根据二进制读指针产生双端口RAM的读地址
(10)由二进制读指针转换成格雷码读指针
(11)对格雷码写指针在读时钟域中进行两级同步得同步后格雷码写指针
(12)同步后格雷码写指针转化成同步后二进制写指针
(13)步骤(10)与步骤(11)比较得读空标志位rd_empty
(14)步骤(8)与步骤(12)相减得指示读FIFO的数据量
关键代码:
reg[DATA_SIZE-1:0] mem_name[RAM_Depth-1:0]; //RAM深度=2^ADDR_WIDTH
assign rdata = Mem[raddr];
assign rempty = (rgraynext == rq2_wptr); //指针是否相等
assign wfull = (wgraynext == {~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]}); // ??
[1]异步fifo的verilog实现
[2]步骤参考
[3] 三种同步FIFO的实现方法(verilog实现)
例:用verilog/vhdl写一个fifo控制器(包括空,满,半满信号)?
八个always模块实现,两个用于读写FIFO,两个用于产生头地址head和尾地址tail,一个产生counter计数,剩下三个根据counter的值产生空,满,半满信号产生空,满,半满信号。
例:用系统任务$readmemb初始化memory.mem:
reg[DATA_SIZE-1:0] mem_name[RAM_Depth-1:0]; //RAM深度=2^ADDR_WIDTH
$readmemb("init.dat",mem_name)