异步时钟的空、满信号生成:通过读、写时钟域的读、写指针比较生成
异步信号的比较问题:同步,通过二级寄存器实现,
这里的同步还存在两个问题:1)二进制指针的多位信号转变的异步同步,存在采样错的可能(无法保证同时刻电频反转),2)快到慢存在的漏采现象。
对问题1)采用格雷码编码再同步,再解码,实现多bit信号的异步同步
对问题2)漏采现象只会产生未满报满,未空报空的更保守操作,不影响正确性。
补充:异步信号需要从寄存器输出而不是逻辑输出;
总流程如下:
二进制转化格雷码:
module binary2gray_wireout #(
PTR=4)
(
input [PTR-1:0] binary_in,
output [PTR-1:0] gary_out
);
generate
genvar i;
for(i=0;i<(PTR-1);i=i+1)begin
gary_out[i] = binary_in[i]^binary_in[i+1];
end
endgenerate
assign gary_out[PTR-1] = binary_in[PTR-1];
endmodule
格雷码转换二进制:
module gray2binary_wireout #(
PTR=4)
(
input [PTR-1:0] gray_in,
output [PTR-1:0] binary_out
);
generate
genvar i;
for(i=0;i<(PTR-1);i=i+1)begin
binary_out[i] = gray_in[i]^binary_out[i+1];
end
endgenerate
assign binary_out[PTR-1] = gray_in[PTR-1];
endmodule
通过双端口RAM实现异步FIFO:
用vivodo示例一下,16的位宽,实际可以做可变位宽
module async_fifo #(
parameter PTR = 4,
DATA_WIDTH = 16,
DATA_DEPTH = 16
) (
input wr_clk,
input [DATA_WIDTH-1:0] wr_data,
input wr_en,
input wr_rst_n,
output reg full,
input rd_clk,
output [DATA_WIDTH-1:0] rd_data,
input rd_en,
input rd_rst_n,
output reg empty
);
//*******************generate ptr
localparam PTR_MAX = (DATA_DEPTH<<1)-1;
reg [PTR:0] wr_ptr,wr_ptr_nxt;
reg [PTR:0] rd_ptr,rd_ptr_nxt;
always@(*)begin
wr_ptr_nxt = wr_ptr ;
if(wr_en)begin
if(wr_ptr_nxt==PTR_MAX)
wr_ptr_nxt = 'd0;
else
wr_ptr_nxt = wr_ptr_nxt + 'd1;
end
end
always@(posedge wr_clk or negedge wr_rst_n)
if (wr_rst_n)
wr_ptr <= 'd0;
else
wr_ptr <= wr_ptr_nxt;
always@(*)begin
rd_ptr_nxt = rd_ptr ;
if(rd_en)begin
if(rd_ptr_nxt==PTR_MAX)
rd_ptr_nxt = 'd0;
else
rd_ptr_nxt = rd_ptr_nxt + 'd1;
end
end
always@(posedge rd_clk or negedge rd_rst_n)
if (rd_rst_n)
rd_ptr <= 'd0;
else
rd_ptr <= rd_ptr_nxt;
//**********************b2g
reg [PTR:0] wr_ptr_b2g;
reg [PTR:0] rd_ptr_b2g;
wire [PTR:0] wr_ptr_b2g_nxt;
wire [PTR:0] rd_ptr_b2g_nxt;
binary2gray_wireout #(
.PTR(PTR+1))
b2g_wr
(
.binary_in(wr_ptr),
.gray_out(wr_ptr_b2g_nxt)
);
binary2gray_wireout #(
.PTR(PTR+1))
b2g_rd
(
.binary_in(rd_ptr),
.gray_out(rd_ptr_b2g_nxt)
);
always@(posedge wr_clk or negedge wr_rst_n)
if (wr_rst_n)
wr_ptr_b2g <= 'd0;
else
wr_ptr_b2g <= wr_ptr_b2g_nxt;
always@(posedge rd_clk or negedge rd_rst_n)
if (rd_rst_n)
rd_ptr_b2g <= 'd0;
else
rd_ptr_b2g <= rd_ptr_b2g_nxt;
//********************************sync
reg [PTR:0]wr_ptr_sync2rd_1,wr_ptr_sync2rd_2;
reg [PTR:0]rd_ptr_sync2wr_1,rd_ptr_sync2wr_2;
always@(posedge rd_clk or negedge rd_rst_n)
if (rd_rst_n)begin
wr_ptr_sync2rd_1 <= 'd0;
wr_ptr_sync2rd_2 <= 'd0;
end
else begin
wr_ptr_sync2rd_1 <= wr_ptr_b2g;
wr_ptr_sync2rd_2 <= wr_ptr_sync2rd_1;
end
always@(posedge wr_clk or negedge wr_rst_n)
if (wr_rst_n)begin
rd_ptr_sync2wr_1 <= 'd0;
rd_ptr_sync2wr_2 <= 'd0;
end
else begin
rd_ptr_sync2wr_1 <= rd_ptr_b2g;
rd_ptr_sync2wr_2 <= rd_ptr_sync2wr_1;
end
//*******************************g2b
reg [PTR:0]rd_ptr_sync2wr_2_g2b;
reg [PTR:0]wr_ptr_sync2rd_2_r2b;
wire [PTR:0]rd_ptr_sync2wr_2_g2b_nxt;
wire [PTR:0]wr_ptr_sync2rd_2_r2b_nxt;
gray2binary_wireout #(
PTR+1
)rd2wr_g2b
(
.gray_in(rd_ptr_sync2wr_2),
.binary_out(rd_ptr_sync2wr_2_g2b_nxt)
);
gray2binary_wireout #(
PTR+1
)wr2rd_g2b
(
.gray_in(wr_ptr_sync2rd_2),
.binary_out(wr_ptr_sync2rd_2_r2b_nxt)
);
always@(posedge wr_clk or negedge wr_rst_n)
if (wr_rst_n)
rd_ptr_sync2wr_2_g2b <= 'd0;
else
rd_ptr_sync2wr_2_g2b <= rd_ptr_sync2wr_2_g2b_nxt;
always@(posedge rd_clk or negedge rd_rst_n)
if (rd_rst_n)
wr_ptr_sync2rd_2_r2b <= 'd0;
else
wr_ptr_sync2rd_2_r2b <= wr_ptr_sync2rd_2_r2b_nxt;
//*******************************full,empty
wire full_nxt,empty_nxt;
assign full_nxt = (rd_ptr_sync2wr_2_g2b[PTR]!=wr_ptr[PTR])&&(rd_ptr_sync2wr_2_g2b[PTR-1:0]==wr_ptr[PTR-1:0]);
assign empty_nxt = (wr_ptr_sync2rd_2_r2b==rd_ptr);
always@(posedge wr_clk or negedge wr_rst_n)
if (wr_rst_n)
full <= 'd0;
else
full <= full_nxt;
always@(posedge rd_clk or negedge rd_rst_n)
if (rd_rst_n)
empty <= 'd0;
else
empty <= empty_nxt;
//**********************************RAM
Dual_port_RAM Dual_port_RAM_U (
.clka(wr_clk), // input wire clka
.wea(wr_en), // input wire ena
.addra(wr_ptr[PTR-1:0]), // input wire [3 : 0] addra
.dina(wr_data), // input wire [15 : 0] douta
.clkb(rd_clk), // input wire clkb
.enb(rd_en), // input wire enb
.addrb(rd_ptr[PTR-1:0]), // input wire [3 : 0] addrb
.doutb(rd_data) // output wire [15 : 0] doutb
);
endmodule
ptr指针信号做了一位的拓宽,用于表示套圈,最高位相等而低位相同,视为套一整圈,即写满。实际满、空信号因为异步同步的时延,都是未满报满,未空报空。
格雷码解码到空满信号打了一拍,优化了延迟,(格雷到二进制逻辑延迟比较大,和行波进位加法类似,每一位计算需要等高一位的输出),不过vivado也会自动优化:
参考:Verilog高级数字系统设计技术与示例分析,Kishore Mishra,电子工业出版社。