一、异步FIFO1.异步FIFO的基本概念2.异步FIFO的作用3.异步FIFO的读/写指针4.异步FIFO空/满标志5.指针计数器的选择6.二进制与格雷码相互转换三、Spec(1)Function descripton(2)Feature list(3)Block Diagram(4)Interface description(5)Timing四、Design and Verification(1)RTL(2)Test bench(3)Analyse五、Result(1)Write(2)Read(3)Write&Read(4)假满假空现象异步FIFO测试点常见问题参考资料参考资料
一、异步FIFO
1.异步FIFO的基本概念
对于异步FIFO,主要是实现不同时钟域之间的数据交互。
与同步FIFO有着明显的区别,同步FIFO是使用一个时钟,读写在同一个时钟域内。而异步FIFO使用两个时钟,读/写在不同时钟域内,这个过程就涉及了跨时钟域处理的过程,跨时钟域又会产生亚稳态问题,所以这是异步FIFO设计的一个重点,与同步FIFO一样,通过空满标志衡量存储器的使用情况,那么在异步FIFO中,空满标志产生的条件和方式是什么呢,这也是设计的重点。

2.异步FIFO的作用
- 为什么需要异步FIFO? 用于在不同的时钟域(clock domain)之间安全地传输数据。而同步FIFO主要是解决数据传输速率匹配问题。
- 同步器(synchronizer)

对于跨时钟域之间的信号传输,需要进行同步(synchronize)处理;一般来讲,我们可以采用同步器(由2~3级FF组成)对
单bit的信号进行同步操作。注意,这里的打拍子是针对单bit信号而已的。那么为什么不能用简单的同步器(synchronizer)对数据总线 ( 大于1bit)进行同步呢?下面分析一下。

问※题
:那么对于多bit信号的跨时钟域同步,可采用异步FIFO或者双口RAM。
异步最经典的问题:
(1)在clk0域一个稳定信号,由于designer改变了设计,在ck1域以为仍然稳定,假设是enable=1,其实是变化了的,需要用synal同步。
(2)忽略了ck的频率,打两拍采信号(不同频率是否采到信号慢的采信号可能采不到)
(3)上升沿a&(~ady)下降沿(~a)&ady,沿出来之后触发信号变成电平方便检测。脉冲同步。
(4)同步一个bus,两个模块之间的data交互,要用到异步fifo。
3.异步FIFO的读/写指针
- 写指针(write pointer)
- ▷ 始终指向
下一次将要写入的数据的地址;
- ▷ 系统复位后(FIFO为空),写指针指向0地址;
- ▷ 每写入一笔数据,写指针地址加1;
- 读指针(read pointer)
- ▷ 始终指向当前要读出的数据的地址;
- ▷ 系统复位后(FIFO为空),读指针指向0地址;
- ▷ 此时的数据是无效的,因为还没有数据写入,空标志有效;
4.异步FIFO空/满标志

- 空标志(empty)
- ▷ 情形一,复位时,两指针都为0;
- ▷ 情形二,当读指针和写指针相等时,空标志=1;
- 满标志(full)
- ▷ 当写指针和读指针最高位不同,其他相等时,满标志=1;
- ▷ 例如,写入的速度快,写指针转了一圈(wrap around),又追上了读指针;
- 空满标志处理

▷ 把读、写指针都额外增加1bit,假如FIFO的深度为8,理论上指针位只需要[2:0]。为了能够正确甄别空、满,需要将指针都扩展到[3:0]。
▷ 其中额外引入的最高位[3],用于辅助甄别是否已经发生了回环(wrap around)的情形。当指针计满FIFO的深度,折回头重新开始时,最高位MSB加1,其它位清0。
▷ 如果读写指针的最高位不同,就意味着写指针速度快,并已经多完成一次回环。
▷ 如果两个指针的最高位相同,就意味着双方完成了相同次数的回环。
5.指针计数器的选择

- 普通二进制计数器(Binary counter)
1.在异步FIFO的设计中,读写两边要互相参考对方的指针,以便生成空、满标志;
2.数据同步问题:> 1 bit,从一个clock domain到另一个clock domain,由于亚稳态现象的出现,会导致数据出错; 极端情形:所有的数据位都变化;
3.解决办法:采用sample & hold机制,引入保持寄存器和握手机制,以确保接收端正确地采集到想要的数据,之后通知发送端,进行下一轮的数据传输;

- 格雷码计数器(Gray code counter):每次当从一个值变化到
相邻的一个值时,有且仅有一位发生变化;
由于格雷码的这种特性,我们就可以通过简单的synchronizer对指针(多位宽)进行同步操作了,而不用担心由于发生亚稳态而出现数据错误的情形;但是对于我们习惯了二进制码的风格,这种码易读性稍差;对于2的整数次幂的FIFO,采用格雷码计数器器; 接近2的整数次幂的FIFO, 采用接近2的幂次方格雷码修改实现;如果这两种都满足不了,就设计一种查找表的形式实现。所以,一般采用2的幂次方格雷码实现FIFO,会浪费一些地址空间,但可以简化控制电路;需要注意:格雷码计数器适用于地址范围空间为2的整数次幂的FIFO,例如8, 16, 32, 64…
6.二进制与格雷码相互转换
二进制到格雷码:二进制码字整体右移一位,再与原先的码字按位做异或操作。
- 二进制转格雷码

- 格雷码转二进制

三、Spec
(1)Function descripton
Asynchronization First in First out 通过控制两个不同时钟域的读/写操作,完成了两个时钟域之间数据的同步处理。
(2)Feature list
- 存储器采用宽度为16,深度为8的regs
- FIFO宽度、深度可配置
- 写时钟为3MHz,读时钟为2MHz
(3)Block Diagram

☛ 模块设计可细分为写入接口(Push Interface)、读出接口(Pop Interface)、同步器(sync)和存储介质RAM(regs_array)
(4)Interface description
Signal Name
Width
Direction
Description
(5)Timing
- Write timing

- Read Timing

四、Design and Verification
(1)RTL
//-- modified by xlinxdu, 2022/05/17
module async_fifo
#(
parameter DATA_WIDTH = 16 ,
parameter FIFO_DEPTH = 8 ,
parameter PTR_WIDTH = 4 ,
parameter ADDR_DEPTH = $clog2(FIFO_DEPTH)
)
(
//reset signal
input wire wr_rst_n_i,
input wire rd_rst_n_i,
//write interface
input wire wr_clk_i ,
input wire wr_en_i ,
input wire [DATA_WIDTH-1:0] wr_data_i,
//read interface
input wire rd_clk_i ,
input wire rd_en_i ,
output reg [DATA_WIDTH-1:0] rd_data_o,
//flag
output reg full_o ,
output reg empty_o
);
//-- memery
reg [DATA_WIDTH-1:0] regs_array [FIFO_DEPTH-1:0] ;
//-- memery addr
wire [ADDR_DEPTH-1:0] wr_addr ;
wire [ADDR_DEPTH-1:0] rd_addr ;
//-- write poiter,write poiter of gray and sync
reg [PTR_WIDTH -1:0] wr_ptr ;
wire [PTR_WIDTH -1:0] gray_wr_ptr ;
reg [PTR_WIDTH -1:0] gray_wr_ptr_d1 ;
reg [PTR_WIDTH -1:0] gray_wr_ptr_d2 ;
//-- read poiter,read poiter of gray and sync
reg [PTR_WIDTH -1:0] rd_ptr ;
wire [PTR_WIDTH -1:0] gray_rd_ptr ;
reg [PTR_WIDTH -1:0] gray_rd_ptr_d1 ;
reg [PTR_WIDTH -1:0] gray_rd_ptr_d2 ;
/*-----------------------------------------------\
-- write poiter and bin->gray --
\-----------------------------------------------*/
always @ (posedge wr_clk_i or negedge wr_rst_n_i) begin
if (!wr_rst_n_i) begin
wr_ptr <= {(PTR_WIDTH){1'b0}};
end
else if (wr_en_i && !full_o) begin
wr_ptr <= wr_ptr + 1'b1;
end
end
assign gray_wr_ptr = wr_ptr ^ (wr_ptr >> 1'b1);
/*-----------------------------------------------\
-- gray_wr_prt sync --
\-----------------------------------------------*/
always @ (posedge wr_clk_i or negedge wr_rst_n_i) begin
if (!wr_rst_n_i) begin
gray_wr_ptr_d1 <= {(PTR_WIDTH){1'b0}};
gray_wr_ptr_d2 <= {(PTR_WIDTH){1'b0}};
end
else begin
gray_wr_ptr_d1 <= gray_wr_ptr ;
gray_wr_ptr_d2 <= gray_wr_ptr_d1;
end
end
/*-----------------------------------------------\
-- read poiter and bin->gray --
\-----------------------------------------------*/
always @ (posedge rd_clk_i or negedge rd_rst_n_i) begin
if (!rd_rst_n_i) begin
rd_ptr <= {(PTR_WIDTH){1'b0}};
end
else if (rd_en_i && !empty_o) begin
rd_ptr <= rd_ptr + 1'b1;
end
end
assign gray_rd_ptr = rd_ptr ^ (rd_ptr >> 1'b1);
/*-----------------------------------------------\
-- gray_rd_ptr sync --
\-----------------------------------------------*/
always @ (posedge rd_clk_i or negedge rd_rst_n_i) begin
if (!rd_rst_n_i) begin
gray_rd_ptr_d1 <= {(PTR_WIDTH){1'b0}};
gray_rd_ptr_d2 <= {(PTR_WIDTH){1'b0}};
end
else begin
gray_rd_ptr_d1 <= gray_rd_ptr ;
gray_rd_ptr_d2 <= gray_rd_ptr_d1;
end
end
/*-----------------------------------------------\
-- full flag and empty flag --
\-----------------------------------------------*/
assign full_o = (gray_wr_ptr == {~gray_rd_ptr_d2[PTR_WIDTH-1],gray_rd_ptr_d2[PTR_WIDTH-2:0]})? 1'b1 : 1'b0;
assign empty_o = (gray_rd_ptr == gray_wr_ptr_d2)? 1'b1 : 1'b0;
/*-----------------------------------------------\
-- write addr and read addr --
\-----------------------------------------------*/
assign wr_addr = wr_ptr[PTR_WIDTH-2:0];
assign rd_addr = rd_ptr[PTR_WIDTH-2:0];
/*-----------------------------------------------\
-- write operation --
\-----------------------------------------------*/
integer [PTR_WIDTH-1:0] i;
always @ (posedge wr_clk_i or negedge wr_rst_n_i) begin
if (!wr_rst_n_i) begin
for(i=0;i<FIFO_DEPTH;i=i+1)begin
regs_array[i] <= {(DATA_WIDTH){1'b0}};
end
end
else if (wr_en_i && !full_o) begin
regs_array[wr_addr] <= wr_data_i;
end
end
/*-----------------------------------------------\
-- read operation --
\-----------------------------------------------*/
always @ (posedge rd_clk_i or negedge rd_rst_n_i) begin
if (!rd_rst_n_i) begin
rd_data_o <= {(DATA_WIDTH){1'b0}};
end
else if (rd_en_i && !empty_o) begin
rd_data_o <= regs_array[rd_addr];
end
end
endmodule
(2)Test bench
//-- modified by xlinxdu, 2022/05/17
module tb_async_fifo;
reg rst_n_i ;
reg wr_clk_i ;
reg wr_en_i ;
reg [15:0] wr_data_i;
reg rd_clk_i ;
reg rd_en_i ;
wire [15:0] rd_data_o;
reg full_o ;
reg empty_o ;
initial begin
rst_n_i = 1;
wr_clk_i = 0;
wr_en_i = 0;
wr_data_i= 16'b0;
rd_clk_i = 0;
rd_en_i = 0;
# 1 rst_n_i = 0;# 2 rst_n_i = 1;end
initial begin
#20 wr_en_i = 1;
rd_en_i = 0;
#40 wr_en_i = 0;
rd_en_i = 1;
#30 wr_en_i = 1 ;
rd_en_i = 0 ;
#13 rd_en_i = 1 ;#10
repeat(100)
begin
#5 wr_en_i = {$random}%2 ;
rd_en_i = {$random}%2 ;
end
end
always#1.5 wr_clk_i = ~wr_clk_i ;
always#1 rd_clk_i = ~rd_clk_i ;
always#3 wr_data_i = {$random}%16'hFF;
async_fifo u_async_fifo(
.wr_rst_n_i(rst_n_i ),
.wr_clk_i (wr_clk_i ),
.wr_en_i (wr_en_i ),
.wr_data_i (wr_data_i),
.rd_rst_n_i(rst_n_i ),
.rd_clk_i (rd_clk_i ),
.rd_en_i (rd_en_i ),
.rd_data_o (rd_data_o),
.full_o (full_o ),
.empty_o (empty_o )
);
initial begin
#1000 $finish ;$fsdbDumpfile("async.fsdb");
$fsdbDumpvars ;
$fsdbDumpMDA ;
end
endmodule
(3)Analyse
bug1:可以看到本该同步到异时钟域的格雷码写指针和格雷码读指针,却在自己的时钟域下打拍,导致后面产生的空满标志错误。

定位到读/写指针格雷码同步代码块
/*-----------------------------------------------\
-- gray_wr_prt sync --
\-----------------------------------------------*/
always @ (posedge wr_clk_i or negedge wr_rst_n_i) begin
if (!wr_rst_n_i) begin
gray_wr_ptr_d1 <= {(PTR_WIDTH){1'b0}};
gray_wr_ptr_d2 <= {(PTR_WIDTH){1'b0}};
end
else begin
gray_wr_ptr_d1 <= gray_wr_ptr ;
gray_wr_ptr_d2 <= gray_wr_ptr_d1;
end
end
/*-----------------------------------------------\
-- gray_rd_ptr sync --
\-----------------------------------------------*/
always @ (posedge rd_clk_i or negedge rd_rst_n_i) begin
if (!rd_rst_n_i) begin
gray_rd_ptr_d1 <= {(PTR_WIDTH){1'b0}};
gray_rd_ptr_d2 <= {(PTR_WIDTH){1'b0}};
end
else begin
gray_rd_ptr_d1 <= gray_rd_ptr ;
gray_rd_ptr_d2 <= gray_rd_ptr_d1;
end
end
123456789101112131415161718192021222324252627两处更改了对应的时钟沿触发条件
/*-----------------------------------------------\
-- gray_rd_prt sync --
\-----------------------------------------------*/
always @ (posedge wr_clk_i or negedge wr_rst_n_i) begin
if (!wr_rst_n_i) begin
gray_rd_ptr_d1 <= {(PTR_WIDTH){1'b0}};
gray_rd_ptr_d2 <= {(PTR_WIDTH){1'b0}};
end
else begin
gray_rd_ptr_d1 <= gray_rd_ptr ;
gray_rd_ptr_d2 <= gray_rd_ptr_d1;
end
end
/*-----------------------------------------------\
-- gray_wr_ptr sync --
\-----------------------------------------------*/
always @ (posedge rd_clk_i or negedge rd_rst_n_i) begin
if (!rd_rst_n_i) begin
gray_wr_ptr_d1 <= {(PTR_WIDTH){1'b0}};
gray_wr_ptr_d2 <= {(PTR_WIDTH){1'b0}};
end
else begin
gray_wr_ptr_d1 <= gray_wr_ptr ;
gray_wr_ptr_d2 <= gray_wr_ptr_d1;
end
end

bug2:在只读情况下,写满之后,没有产生满标志。

定位到空满标志代码块
/*-----------------------------------------------\
-- full flag and empty flag --
\-----------------------------------------------*/
assign full_o = (gray_wr_ptr == {~gray_rd_ptr_d2[PTR_WIDTH-1],gray_rd_ptr_d2[PTR_WIDTH-2:0]})? 1'b1 : 1'b0;
1234更改满标志的条件,因为在这里是比较格雷码,和原来二进制比较不同,二进制是高位取反,然后所有位相等即产生满标志。格雷码是最高位和次高位取反,然后所有位相同,即产生满标志。
/*-----------------------------------------------\
-- full flag and empty flag --
\-----------------------------------------------*/
assign full_o = (gray_wr_ptr == {~gray_rd_ptr_d2[PTR_WIDTH-1:PTR_WIDTH-2],gray_rd_ptr_d2[PTR_WIDTH-3:0]})? 1'b1 : 1'b0;
1234
五、Result
(1)Write

在只读阶段,读使能,写指针和写地址开始递增,并将data_i依次写入regs_array。在地址增加到写满了8个地址(0~7)后,写指针继续增加(回环,表示第二圈了),写地址回到第一个地址,并产生满标志。(写指针总指向将要写入的地址,前面有说)
(2)Read

在只读阶段,这个阶段regs_array已经存满,开始读出数据,读指针和读地址开始递增,当读到regs_array的第八个存储地址(0~7)后,读指针继续增加(回环,表示第二圈了),读地址回到第一个地址,并产生空标志。(读指针总指向将要读出的地址,前面有说)
(3)Write&Read

在读/写同时使能的时候,写操作依次往存储单元里面写入新数据,读操作依次往存储单元里读出数据,结果见上图。
(4)假满假空现象

由上图标号1可以知道,在满标志拉低时刻之前,其实regs_array是一个假满的状态,因为这时刻之前,已经读出了三个数据了;由上图标号2可以知道,读空的时候,产生了空标志,其实regs_array是一个假空的状态,因为这时刻之前,写操作已经写入了两个数据。
※原因:因为在格雷码指针同步的时候,格雷码指针信号在两个时钟域之间存在两个时钟周期的延时,但这并不会影响FIFO的正常工作逻辑。
异步FIFO测试点
- 同时读写,读写数据正确检查
- FIFO满标志位检查
- FIFO空标志位检查
- 写过程中发生写复位,写数据和FIFO满标志位被清空
- 读过程中发生读复位,读数据和读空标志位被清空
- 读写时钟相位相同,异步FIFO能正常工作
- 读写时钟相位不同,异步FIFO能正常工作
- 写时钟等与读时钟,异步FIFO能正常工作
- 写时钟快于读时钟,异步FIFO能正常工作
- 写时钟慢于读时种异步FIFO能正常工作
- 写过程中发生写复位以后,异步FIFO能继续正常工作
- 读过程中发生读复位以后,异步FIFO能继续正常工作
- FIFO满以后,继续往FIFO写数据,异步FIFO不会被卡死,数据被读走以后,异步步FIFO能继续正常工作。
异步FIFO设计:
`timescale 1ns / 1ps
//
// Company:
// Engineer: zhangsy
//
// Create Date: 2019/08/05 11:58:12
// Design Name:
// Module Name: asynfifo
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module asynfifo#(
parameter data_depth = 4,
parameter data_width = 4,
parameter ptr_width = 2
)
(
input w_clk,r_clk,
input write,read,
input rst_n,
input [data_width-1:0] data_in,
output [data_width-1:0] data_out,
output valid,
output full,
output emty
);
///
//name enable
//write H
//read H
//rst_n L
//valid H
//full H
//emty H
/
reg [data_width-1:0]fifo [data_depth-1:0];
reg [ptr_width:0] r_ptr;
wire [ptr_width:0]r_ptr_g;
reg [ptr_width:0] w_ptr;
wire [ptr_width:0] w_ptr_g;
//****************update r_ptr************
always @ (posedge r_clk)
if(!rst_n)
r_ptr <= 2'd0;
else if(read && !emty )
r_ptr <= r_ptr + 1'b1;
assign r_ptr_g = (r_ptr >>1) ^r_ptr;
//****************update w_ptr************
always @ (posedge w_clk)
if(!rst_n)
w_ptr <= 2'd0;
else if(write && !full )
w_ptr <= w_ptr + 1'b1;
assign w_ptr_g = (w_ptr >>1) ^w_ptr;
//---------------------------------read-------------
//******************synchronization w_ptr***********
reg[ptr_width:0] w_reg1,w_reg2;
wire [ptr_width:0] w_gray;
reg [data_width-1:0] dout;
reg valid_r;
always @ (posedge r_clk)
if(!rst_n)
{w_reg1,w_reg2} <= 2'b00;
else
{w_reg1,w_reg2} <= {w_ptr,w_reg1};
always @(posedge r_clk)
if(!rst_n)
begin
dout <= 'h0;
valid_r <= 1'b0;
end
else if(read && !emty)
begin
dout <= fifo[r_ptr[ptr_width-1-:ptr_width]];
valid_r <= 1'b1;
end
else
begin
dout <= 'h0;
valid_r <= 1'b0;
end
//---------------------------------write-------------
//******************synchronization r_ptr***********
reg[ptr_width:0] r_reg1,r_reg2;
wire [ptr_width:0] r_gray;
always @ (posedge w_clk)
if(!rst_n)
{r_reg1,r_reg2} <= 2'b00;
else
{r_reg1,r_reg2} <= {r_ptr,r_reg1};
always @ (posedge w_clk)
if(write && !full)
begin
fifo[w_ptr[ptr_width-1-:ptr_width]] <= data_in;
end
//---------------gray----------
assign r_gray = (r_reg2 >> 1) ^r_reg2;
assign w_gray = (w_reg2 >> 1) ^w_reg2;
assign full = ({~w_ptr_g[ptr_width:ptr_width-1],w_ptr_g[ptr_width-2:0]}== r_gray);
assign emty = (w_gray == r_ptr_g);
assign data_out = dout;
assign valid = valid_r;
endmodule常见问题
同步复位与异步复位原理
同步复位原理:同步复位只有在时钟沿到来时复位信号才起作用,则复位信号持续的时间应该超过一个时钟周期才能保证系统复位。
异步复位原理:异步复位只要有复位信号系统马上复位,因此异步复位抗干扰能力差,有些噪声也能使系统复位,因此有时候显得不够稳定,要想设计一个好的复位最好使用异步复位同步释放。
同步复位与异步复位的区别主要看是否有时钟信号参与。异步复位不需要时钟参与,一旦信号有效立即执行复位操作;同步信号需要时钟参与,只有有效的时钟信号出现,复位信号才有效。
一、同步复位与异步复位的特点:
同步复位:顾名思义,同步复位就是指复位信号只有在时钟上升沿到来时,才能有效。否则,无法完成对系统的复位工作。用Verilog描述如下:
always @ (posedge clk) begin
if (!Rst_n)
....
end异步复位:它是指无论时钟沿是否到来,只要复位信号有效,就对系统进行复位。用Verilog描述如下:
always @ (posedge clk or negedge Rst_n) begin
if (!Rst_n)
....
end二、同步复位和异步复位的优缺点分析:
1、总的来说,同步复位的优点大概有3条:
a、有利于仿真器的仿真。
b、可以使所设计的系统成为100%的同步时序电路,这便大大有利于时序分析,而且综合出来的fmax一般较高。
c、因为他只有在时钟有效电平到来时才有效,所以可以滤除高于时钟频率的毛刺。他的缺点也有不少,主要有以下几条:
a、复位信号的有效时长必须大于时钟周期,才能真正被系统识别并完成复位任务。同时还要考虑,诸如:clk skew,组合逻辑路径延时,复位延时等因素。
b、由于大多数的逻辑器件的目标库内的DFF都只有异步复位端口,所以,倘若采用同步复位的话,综合器就会在寄存器的数据输入端口插入组合逻辑,这样就会耗费较多的逻辑资源。
2、对于异步复位来说,他的优点也有三条,都是相对应的
a、大多数目标器件库的dff都有异步复位端口,因此采用异步复位可以节省资源。
b、设计相对简单。
c、异步复位信号识别方便,而且可以很方便的使用FPGA的全局复位端口GSR。
缺点:
a、在复位信号释放(release)的时候容易出现问题。具体就是说:倘若复位释放时恰恰在时钟有效沿附近,就很容易使寄存器输出出现亚稳态,从而导致亚稳态。
b、复位信号容易受到毛刺的影响。
下面我用verilog来演示下同步复位与异步复位。同步复位的verilog程序如下:
moduleD_FF
(
//Inputports
SYSCLK,
RST_B,
A,
//Outputports
B
);
//=========================================
//Inputandoutputdeclaration
//=========================================
inputSYSCLK;
inputRST_B;
input A;
outputB;
//=========================================
//Wireandregdeclaration
//=========================================
wireSYSCLK;
wireRST_B;
wireA;
regB;
//=========================================
//Logic
//=========================================
always@(posedgeSYSCLK)
begin
if(!RST_B)
B《=1‘b0;
else
B《=A;
end
endmodule综合后的RTL级电路图如下:

异步复位的verilog程序如下:
moduleD_FF
(
//Inputports
SYSCLK
,RST_B,
A,
//Outputports
B
);
//=========================================
//Inputandoutputdeclaration
//=========================================
input SYSCLK;
input RST_B;
input A;
output B;
//=========================================
//Wireandregdeclaration
//=========================================
wireSYSCLK;
wireRST_B;
wireA;
regB;
//=========================================
//Logic
//=========================================
always@(posedgeSYSCLKornegedgeRST_B)
begin
if(!RST_B)
B《=1‘b0;
else
B《=A;
end
endmodule综合后的RTL级电路图如下:

通过比较显然异步复位消耗的逻辑资源要少些,因此通常的设计中都采用异步复位的方式,需要进一步的学习的话,可以再研究下其它的资料。
三、总结:
所以说,一般都推荐使用异步复位,同步释放的方式,而且复位信号低电平有效。这样就可以两全其美了。


