一、單比特CDC傳輸1.1 慢到快
快時(shí)鐘域相比慢時(shí)鐘域采樣速度更快,也就是說從慢時(shí)鐘域來到快時(shí)鐘域的信號(hào)一定可以被采集到。既然快時(shí)鐘一定可以采集到慢時(shí)鐘分發(fā)的數(shù)據(jù),那么考慮的問題就只剩下如何保證采樣到的信號(hào)質(zhì)量!最常用的同步方法是雙級(jí)觸發(fā)器緩存法,俗稱延遲打拍法。信號(hào)從一個(gè)時(shí)鐘域進(jìn)入另一個(gè)時(shí)鐘域之前,將該信號(hào)用兩級(jí)觸發(fā)器連續(xù)緩存兩次,可有效降低因?yàn)闀r(shí)序不滿足而導(dǎo)致的亞穩(wěn)態(tài)問題。
具體如下圖所示:來自慢時(shí)鐘clk1的信號(hào)在clk2被多次采樣(信號(hào)在clk1持續(xù)一個(gè)時(shí)鐘周期,在clk2持續(xù)三個(gè)時(shí)鐘周期),如果只需要在clk2持續(xù)一個(gè)時(shí)鐘周期,可以采用邊沿檢測(cè)即可得到signal4;

1.2 快到慢
慢時(shí)鐘域相比快時(shí)鐘域采樣速度更慢,也就是說從快時(shí)鐘域來到慢時(shí)鐘域的信號(hào)極有可能被漏采。一般要求在接收時(shí)鐘域中采樣信號(hào)要保持三個(gè)時(shí)鐘邊沿的時(shí)間(也就是1.5倍的采樣時(shí)鐘周期)才會(huì)避免出現(xiàn)漏采。也就是快到慢跨時(shí)鐘域的核心是如何延長(zhǎng)信號(hào)長(zhǎng)度!
對(duì)于電平信號(hào)而言(一般電平信號(hào)持續(xù)時(shí)間足夠長(zhǎng)),信號(hào)長(zhǎng)度可以得到保證,所以正常采用兩級(jí)同步器采樣即可。
對(duì)于脈沖信號(hào)而言(一般脈沖信號(hào)持續(xù)時(shí)間很短),長(zhǎng)度難以得到保證,需要對(duì)信號(hào)進(jìn)行延長(zhǎng)。目前,常用延長(zhǎng)方法有兩種:
一是開環(huán)(無反饋)延長(zhǎng),在知道兩個(gè)時(shí)鐘頻率比的情況下,可以采用“快時(shí)鐘域脈寬擴(kuò)展+慢時(shí)鐘域延遲打拍”的方法進(jìn)行同步。
二是閉環(huán)(有反饋)延長(zhǎng),信號(hào)延長(zhǎng)的恢復(fù)位置由反饋信號(hào)決定,此方法實(shí)質(zhì)是通過相互握手的方式對(duì)窄脈沖信號(hào)進(jìn)行脈寬擴(kuò)展,這也是我們常說的“握手協(xié)議”。
然而,除了“握手協(xié)議”以外其他兩種方法都是有缺陷、有限制的,具體如下圖所示:


可以看到無論是電平還是脈沖信號(hào)使用起來都是有限制的,因?yàn)樗鼈儾捎玫亩际菬o反饋的開環(huán)設(shè)計(jì)(詳細(xì)可查看博文跨時(shí)鐘傳輸——單比特)。采用閉環(huán)的反饋設(shè)計(jì)可以避免這些問題,具體流程如下:

快時(shí)鐘域?qū)γ}沖信號(hào)進(jìn)行檢測(cè),檢測(cè)為高電平時(shí)輸出高電平信號(hào)req。
慢時(shí)鐘域?qū)鞎r(shí)鐘域的信號(hào)req進(jìn)行延遲打拍采樣。因?yàn)榇藭r(shí)的脈沖信號(hào)被快時(shí)鐘域保持拉高狀態(tài),延遲打拍肯定會(huì)采集到該信號(hào)。
慢時(shí)鐘域確認(rèn)采樣得到高電平信號(hào)req_r2后,拉高反饋信號(hào)ack再反饋給快時(shí)鐘域。
快時(shí)鐘域?qū)Ψ答佇盘?hào)ack進(jìn)行延遲打拍采樣得到ack_r0。如果檢測(cè)到反饋信號(hào)為高電平,證明慢時(shí)鐘域已經(jīng)接收到有效的高電平信號(hào),信號(hào)恢復(fù)原來狀態(tài)。


1.3 單比特“握手協(xié)議”verilog代碼
verilog代碼
代碼語言:c
復(fù)制
//單比特快到慢“握手協(xié)議”
module cdc_sbit_handshake(
input aclk,//快時(shí)鐘
input arst_n,//快時(shí)鐘域復(fù)位信號(hào)
input signal_a,//快時(shí)鐘域信號(hào)
input bclk,//慢時(shí)鐘
input brst_n,//慢時(shí)鐘域復(fù)位信號(hào)
output signal_b//慢時(shí)鐘域輸出信號(hào)
);
//慢時(shí)鐘域信號(hào)展寬直至反饋信號(hào)回來再恢復(fù)
reg req;//寄存慢時(shí)鐘域展寬信號(hào)
reg ack_r0;//反饋信號(hào)
always@(posedge aclk or negedge arst_n) begin
if(!arst_n) begin
req <= 1'b0;
end
else if(signal_a) begin
req <= 1'b1;//信號(hào)展寬
end
else if(ack_r0) begin
req <= 1'b0; //反饋信號(hào)到來時(shí)恢復(fù)
end
end
//展寬信號(hào)跨時(shí)鐘同步至慢時(shí)鐘域
reg req_r0;
reg req_r1;
reg req_r2;
always@(posedge bclk or negedge brst_n) begin
if(!brst_n)begin
{req_r2,req_r1,req_r0} <= 3'b0;
end
else begin
{req_r2,req_r1,req_r0} <= ?{req_r1,req_r0,req};
end
end
//生成反饋信號(hào)并同步至快時(shí)鐘域
reg ack;
always@(posedge aclk or negedge arst_n) begin
if(!arst_n) begin
{ack_r0,ack} <= 2'b0;
end
else begin
{ack_r0,ack} <= ?{ack,req_r1};
end
end
//信號(hào)上升沿檢測(cè),讓輸出持續(xù)一個(gè)慢時(shí)鐘周期
assign signal_b = ~req_r2 & req_r1;
endmodule
Testbench
代碼語言:c
復(fù)制
`timescale 1ns/1ps//仿真時(shí)間單位1ns 仿真時(shí)間精度1ps
module cdc_sbit_handshake_tb;
//信號(hào)申明
reg aclk;
reg arst_n;
reg signal_a;
regbclk;
regbrst_n;
wiresignal_b;
//例化
cdc_sbit_handshake u_cdc_sbit_handshake(
.aclk(aclk),
.bclk(bclk),
.arst_n(arst_n),
.brst_n(brst_n),
.signal_a(signal_a),
.signal_b(signal_b)
);
//快時(shí)鐘域慢時(shí)鐘生成
always #5 aclk =~ aclk;
always #15 bclk =~ bclk;
//初始信號(hào)賦值與激勵(lì)
initial begin
signal_a = 0;
aclk = 0;
bclk = 0;
arst_n = 1;
brst_n = 1;
#15;
arst_n = 0;
brst_n = 0;
#15;
arst_n = 1;
brst_n = 1;
signal_a = 1;
#10;
signal_a = 0;
end
endmodule
仿真結(jié)果

二、多比特CDC傳輸
多比特為能不能使用二級(jí)同步器傳輸?使用格雷碼也不行嗎?什么情況下可以使用同步器加格雷碼跨時(shí)鐘傳輸?
先給結(jié)論:多比特信號(hào)不能用二級(jí)同步器跨時(shí)鐘傳輸,哪怕使用格雷碼大部分情況也不行,只有在格雷碼自增或自減順序變化才可以跨時(shí)鐘傳輸。對(duì)于多比特?cái)?shù)據(jù),在進(jìn)行傳輸時(shí)候會(huì)因?yàn)闀r(shí)序問題導(dǎo)致所有寄存器不會(huì)同時(shí)翻轉(zhuǎn)(不是不翻轉(zhuǎn),是不同時(shí)翻轉(zhuǎn)!),所以容易在跨時(shí)鐘傳輸?shù)臅r(shí)候出現(xiàn)中間態(tài)。使用格雷碼可以避免這種現(xiàn)象,但是當(dāng)格雷碼不是按計(jì)數(shù)順序變化(非順序變化相當(dāng)于每次變化不止一位),這同樣是不允許的,因?yàn)楦窭状a每次只有一位發(fā)生變化的前提是,數(shù)據(jù)是遞增或遞減的。比如異步FIFO中格雷碼可以通過二級(jí)同步器進(jìn)行CDC傳輸。
2.1 慢到快:MUX同步器法
慢到快這種情況在快時(shí)鐘接收端是一定能夠采樣得到的,但是根據(jù)上文可知,多比特不適合采用二級(jí)同步器直接傳輸采樣,因?yàn)樵趥鬏斶^程中有多位同時(shí)變化,那么有什么解決辦法呢?解決辦法是在傳輸過程中不變化!所以必須在寫入使能信號(hào)有效時(shí)傳輸!
傳輸非同步數(shù)據(jù)到接收時(shí)鐘域時(shí)配上一個(gè)同步的控制信號(hào),數(shù)據(jù)和控制信號(hào)被同時(shí)發(fā)送到接收時(shí)鐘域,同時(shí)控制信號(hào)在接收時(shí)鐘域使用兩級(jí)寄存器同步到接收時(shí)鐘域,使用此同步后的控制信號(hào)來加載數(shù)據(jù),這樣數(shù)據(jù)就可以在目的寄存器被安全加載。
具體代碼可參考鏈接:Verilog 跨時(shí)鐘域傳輸:慢到快
verilog代碼
代碼語言:c
復(fù)制
//同步模塊工作時(shí)鐘為 100MHz 的模塊
//異步數(shù)據(jù)對(duì)來自工作時(shí)鐘為 20MHz 的模塊
module delay_sample(
input rstn,
input clk1,
input [31:0] din,
input din_en,
input clk2,
output [31:0] dout,
output dout_en);
//sync din_en
reg [2:0] din_en_r ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn) din_en_r <= 3'b0 ;
else din_en_r <= {din_en_r[1:0], din_en} ;
end
wire din_en_pos = din_en_r[1] && !din_en_r[2] ;
//sync data
reg [31:0] dout_r ;
reg dout_en_r ;
always @(posedge clk2 or negedge rstn) begin
if (!rstn)
dout_r <= 'b0 ;
else if (din_en_pos)
dout_r <= din ;
end
//dout_en delay
always @(posedge clk2 or negedge rstn) begin
if (!rstn) dout_en_r <= 1'b0 ;
else dout_en_r <= din_en_pos ;
end
assign dout = dout_r ;
assign dout_en = dout_en_r ;
endmodule
時(shí)序結(jié)構(gòu)如下圖所示:

但如果慢時(shí)鐘域沒有數(shù)據(jù)使能信號(hào) din_en, 或數(shù)據(jù)使能信號(hào)一直有效,此時(shí)在快時(shí)鐘域?qū)?shù)據(jù)使能信號(hào)進(jìn)行上升沿檢測(cè)的方法將會(huì)失效。因?yàn)閿?shù)據(jù)使能信號(hào)一直有效,除了第一個(gè)數(shù)據(jù),快時(shí)鐘域?qū)o法檢測(cè)到后繼數(shù)據(jù)的傳輸時(shí)刻。
解決方法就是,在快時(shí)鐘域?qū)β龝r(shí)鐘信號(hào)的邊沿進(jìn)行檢測(cè)。
2.2 快到慢:握手協(xié)議
快到慢必然會(huì)伴隨著漏采的風(fēng)險(xiǎn),根據(jù)單比特CDC傳輸?shù)姆椒梢灾辣苊獾姆椒ň褪茄娱L(zhǎng)信號(hào)的長(zhǎng)度,所以需要帶寫入的使能信號(hào)對(duì)信號(hào)進(jìn)行延長(zhǎng)。此處任采用握手的方式,完全握手具體原理如下圖所示:


優(yōu)點(diǎn):可以解決快時(shí)鐘域向慢時(shí)鐘域過渡的問題,且其適用的范圍很廣。
缺點(diǎn):實(shí)現(xiàn)較為復(fù)雜,特別是其效率不高,在對(duì)設(shè)計(jì)性能要求較高的場(chǎng)合應(yīng)該慎用。

這一部分具體可以查看鏈接:FPGA學(xué)習(xí)筆記——跨時(shí)鐘域(CDC)設(shè)計(jì)之多bit信號(hào)同步
verilog代碼
代碼語言:c
復(fù)制
module data_driver(
input clk_a, //發(fā)送端時(shí)鐘信號(hào)
input rst_n, //復(fù)位信號(hào),低電平有效
input data_ack, //數(shù)據(jù)接收確人信號(hào)
input clk_b, //接收端時(shí)鐘信號(hào)
input rst_n, //復(fù)位信號(hào),低電平有效
input [3:0] data, //接收數(shù)據(jù)
input data_req, //請(qǐng)求接收信號(hào)
output reg data_ack//數(shù)據(jù)接收確人信號(hào)
);
/********************** 發(fā)送端 **********************/
reg [3:0] data;//發(fā)送數(shù)據(jù)
reg data_req ; //請(qǐng)求接收信號(hào)
reg [2:0] cnt_reg;
reg data_ack_sync1;
reg data_ack_sync2;
//計(jì)數(shù)
always@(posedge clk_a or negedge rst_n)
begin
if(!rst_n)
cnt_reg <= 3'd0;
else if(data_ack_sync1 && !data_ack_sync2 == 1'b1)
cnt_reg <= 3'd0;
else if(data_req == 1'b1)
cnt_reg <= cnt_reg;
else
cnt_reg <= cnt_reg + 1'b1;
end
//data_ack兩級(jí)同步
always@(posedge clk_a or negedge rst_n)
begin
if(!rst_n)
begin
data_ack_sync1 <= 1'b0;
data_ack_sync2 <= 1'b0;
end
else
begin
data_ack_sync1 <= data_ack;
data_ack_sync2 <= data_ack_sync1;
end
end
//請(qǐng)求接收信號(hào)
always@(posedge clk_a or negedge rst_n)
begin
if(!rst_n)
data_req <= 1'b0;
else if(cnt_reg == 3'd4)
data_req <= 1'b1;
else if(data_ack_sync2 == 1'b1)
data_req <= 1'b0;
else
data_req <= data_req;
end
//發(fā)送數(shù)據(jù)
always@(posedge clk_a or negedge rst_n)
begin
if(!rst_n)
data <= 4'd0;
else if(data == 4'd7 && data_ack_sync2 == 1'b1 && data_req == 1'b1 )
data <= 4'd0;
else
begin
if(data_ack_sync2 == 1'b1 && data_req == 1'b1 )
data <= data + 1'b1;
else
data <= data;
end
end
/********************** 接收端 **********************/
reg data_req_sync1;
reg data_req_sync2;
//data_req兩級(jí)同步
always@(posedge clk_b or negedge rst_n)
begin
if(!rst_n)
begin
data_req_sync1 <= 1'b0;
data_req_sync2 <= 1'b0;
end
else
begin
data_req_sync1 <= data_req;
data_req_sync2 <= data_req_sync1;
end
end
//數(shù)據(jù)接收確人信號(hào)
always@(posedge clk_b or negedge rst_n)
begin
if(!rst_n)
data_ack <= 1'b0;
else if(data_req_sync2 == 1'b1)
data_ack <= 1'b1;
else
data_ack <= 1'b0;
end
endmodule2.3 異步FIFO(慢到快和快到慢通殺)
關(guān)于異步FIFO具體可以看看這篇:異步FIFO設(shè)計(jì)原理與設(shè)計(jì)方法以及重要問題匯總(包含verilog代碼|Testbench|仿真結(jié)果),對(duì)異步FIFO介紹很詳細(xì)并且總結(jié)了若干重要問題。
FIFO 是一種“先進(jìn)先出隊(duì)列”,數(shù)據(jù)從一頭寫入,從另一頭讀出,讀出順序和寫入順序一模一樣。因?yàn)殛?duì)列空間有限,因此一般把隊(duì)列設(shè)計(jì)為環(huán)形。對(duì)于隊(duì)列來說,最重要的事情是不能在隊(duì)空的時(shí)候讀數(shù)、不能在隊(duì)滿的時(shí)候?qū)憯?shù)。一般通過比較讀寫指針來獲得“隊(duì)空”和“隊(duì)滿”信息。異步FIFO常常用在高速數(shù)據(jù)跨時(shí)鐘域的場(chǎng)景上。

異步FIFO主要由五部分組成:RAM、寫控制端、讀控制端、兩個(gè)時(shí)鐘同步端
雙端口RAM:此處為偽雙端口RAM進(jìn)行數(shù)據(jù)存儲(chǔ)與讀出,有兩組數(shù)據(jù)線、地址線、時(shí)鐘線。
寫控制端:寫指針與滿信號(hào)產(chǎn)生器,用于判斷是否可以寫入數(shù)據(jù),寫操作時(shí),寫使能有效且FIFO未滿。
讀控制端:讀指針與空信號(hào)產(chǎn)生器,用于判斷是否可以讀取數(shù)據(jù),讀操作時(shí),讀使能有效且FIFO未空。
兩個(gè)時(shí)鐘同步端:讀指針同步到寫指針域進(jìn)行“寫滿”判斷,寫指針同步到讀指針域進(jìn)行“讀空”判斷。
verilog代碼
代碼語言:c
復(fù)制
//深度為8,數(shù)據(jù)位寬為8的異步FIFO
module async_fifo #(
parameter DATA_DEPTH = 8,//深度為8
parameter DATA_WIDTH = 8,//數(shù)據(jù)位寬為8
parameter PTR_WIDTH = 3//讀寫指針位寬為3
)(
input [DATA_WIDTH - 1 : 0] wr_data, //寫數(shù)據(jù)
input wr_clk, //寫時(shí)鐘
input wr_rst_n,//寫時(shí)鐘復(fù)位
input wr_en,//寫使能
input rd_clk,//讀數(shù)據(jù)
input rd_rst_n,//讀時(shí)鐘復(fù)位
input rd_en,//讀使能
output reg fifo_full,//“滿”標(biāo)志位
output reg fifo_empty,//“空”標(biāo)志位
output reg [DATA_WIDTH - 1 : 0] rd_data //寫時(shí)鐘
);
/*-----------------------------------------------------------------
-----------------------------偽雙口RAM模塊--------------------------
------------------------------------------------------------------*/
//定義一個(gè)寬度為8,深度為DEPTH的8的RAM_FIFO
reg [DATA_WIDTH - 1 : 0] ram_fifo [DATA_DEPTH - 1 : 0];
//寫指針計(jì)數(shù)
reg [PTR_WIDTH : 0] wr_ptr; //信息位+地址位所以指針位寬為4
always@ (posedge wr_clk or negedge wr_rst_n) begin
if(!wr_rst_n) begin
wr_ptr <= 0;
end
else if(wr_en && !fifo_full) begin
wr_ptr <= wr_ptr + 1;
end
else begin
wr_ptr <= wr_ptr;
end
end
//RAM寫入數(shù)據(jù)
wire [PTR_WIDTH -1 : 0] wr_addr;
assign wr_addr = wr_ptr[PTR_WIDTH -1 : 0];//RAM寫數(shù)據(jù)只需要地址位不需要信息位,所以尋址地址位寬為3
always@ (posedge wr_clk or negedge wr_rst_n) begin
if(!wr_rst_n) begin
ram_fifo[wr_addr] <= 0;//復(fù)位
end
else if(wr_en && !fifo_full) begin
ram_fifo[wr_addr] <= wr_data;//數(shù)據(jù)寫入
end
else begin
ram_fifo[wr_addr] <= ram_fifo[wr_addr];//保持不變
end
end
//讀指針計(jì)數(shù)
reg [PTR_WIDTH : 0] rd_ptr;
always@ (posedge rd_clk or negedge rd_rst_n) begin
if(!rd_rst_n) begin
rd_ptr <= 0;
end
else if(rd_en && !fifo_empty) begin
rd_ptr <= rd_ptr + 1;
end
else begin
rd_ptr <= rd_ptr;
end
end
//RAM讀出數(shù)據(jù)
wire [PTR_WIDTH -1 : 0] rd_addr;
assign rd_addr = rd_ptr[PTR_WIDTH -1 : 0];//RAM讀數(shù)據(jù)只需要地址位不需要信息位,所以尋址地址位寬為3
always@ (posedge rd_clk or negedge rd_rst_n) begin
if(!rd_rst_n) begin
rd_data <= 0;//復(fù)位
end
else if(rd_en && !fifo_empty) begin
rd_data <= ram_fifo[rd_addr];//讀數(shù)據(jù)
end
else begin
rd_data <= rd_data;//保持不變
end
end
/*--------------------------------------------------------------------
------------------------讀寫指針(格雷碼)轉(zhuǎn)換與跨時(shí)鐘域同步模塊------
---------------------------------------------------------------------------------------*/
//讀寫指針轉(zhuǎn)換成格雷碼
wire [PTR_WIDTH : 0] wr_ptr_gray;
wire [PTR_WIDTH : 0] rd_ptr_gray;
assign wr_ptr_gray = wr_ptr ^ (wr_ptr >> 1);
assign rd_ptr_gray = rd_ptr ^ (rd_ptr >> 1);
//寫指針同步到讀時(shí)鐘域
//打兩拍
reg [PTR_WIDTH : 0] wr_ptr_gray_r1;
reg [PTR_WIDTH : 0] wr_ptr_gray_r2;
always@ (posedge rd_clk or negedge rd_rst_n) begin
if(!rd_rst_n) begin
wr_ptr_gray_r1 <= 0;
wr_ptr_gray_r2 <= 0;
end
else begin
wr_ptr_gray_r1 <= wr_ptr_gray;
wr_ptr_gray_r2 <= wr_ptr_gray_r1;
end
end
//讀指針同步到寫時(shí)鐘域
//打兩拍
reg [PTR_WIDTH : 0] rd_ptr_gray_r1;
reg [PTR_WIDTH : 0] rd_ptr_gray_r2;
always@ (posedge wr_clk or negedge wr_rst_n) begin
if(!wr_rst_n) begin
rd_ptr_gray_r1 <= 0;
rd_ptr_gray_r2 <= 0;
end
else begin
rd_ptr_gray_r1 <= rd_ptr_gray;
rd_ptr_gray_r2 <= rd_ptr_gray_r1;
end
end
/*--------------------------------------------------------------------------------------
--------------------------------------空滿信號(hào)判斷模塊-----------------------------------
---------------------------------------------------------------------------------------*/
//組合邏輯判斷寫滿
always@ (*) begin
if(!wr_rst_n) begin
fifo_full <= 0;
end
else if( wr_ptr_gray == { ~rd_ptr_gray_r2[PTR_WIDTH : PTR_WIDTH - 1],
rd_ptr_gray_r2[PTR_WIDTH - 2 : 0] }) begin
fifo_full <= 1;
end
else begin
fifo_full <= 0;
end
end
//組合邏輯判斷讀空
always@ (*) begin
if(!rd_rst_n) begin
fifo_empty <= 0;
end
else if(rd_ptr_gray == wr_ptr_gray_r2) begin
fifo_empty <= 1;
end
else begin
fifo_empty <= 0;
end
end
endmodule
Testbench
代碼語言:c
復(fù)制
`timescale 1ns/1ps;//仿真時(shí)間單位1ns 仿真時(shí)間精度1ps
module async_fifo_tb #(
parameter DATA_DEPTH = 8,
parameter DATA_WIDTH = 8,
parameter PTR_WIDTH = 3
);
//信號(hào)申明
reg [DATA_WIDTH - 1 : 0] wr_data;
reg wr_clk;
reg wr_rst_n;
reg wr_en;
reg rd_clk;
reg rd_rst_n;
reg rd_en;
wire fifo_full;
wire fifo_empty;
wire [DATA_WIDTH - 1 : 0] rd_data;
//例化
async_fifo u_async_fifo (
.wr_clk(wr_clk),
.rd_clk(rd_clk),
.wr_rst_n(wr_rst_n),
.rd_rst_n(rd_rst_n),
.wr_en(wr_en),
.rd_en(rd_en),
.wr_data(wr_data),
.rd_data(rd_data),
.fifo_empty(fifo_empty),
.fifo_full(fifo_full)
);
//讀寫時(shí)鐘信號(hào)生成
always #10 rd_clk = ~rd_clk;
always #5 wr_clk = ~wr_clk;
//信號(hào)初始化和賦值
initial begin
wr_clk = 0;
wr_rst_n = 1;
wr_en = 0;
rd_clk = 0;
rd_rst_n = 1;
rd_en = 0;
#10;
wr_rst_n = 0;
rd_rst_n = 0;
#10;
wr_rst_n = 1;
rd_rst_n = 1;
//only write
wr_en = 1;
rd_en = 0;
repeat(10) begin
@(negedge wr_clk) begin
wr_data = {$random}%30;
end
end
//only read
wr_en = 0;
rd_en = 1;
repeat(10) begin
@(negedge rd_clk);
end
rd_en =0;
//read and write
wr_en = 0;
rd_en = 0;
#80;
wr_en = 1;
rd_en = 1;
repeat(20) begin
@(negedge wr_clk) begin
wr_data = {$random}%30;
end
end
end
endmodule
仿真結(jié)果

三、CDC的幾個(gè)重要問題(重要!!!)
多比特為能不能使用二級(jí)同步器傳輸?使用格雷碼也不行嗎?什么情況下可以使用同步器加格雷碼跨時(shí)鐘傳輸?
先給結(jié)論:多比特信號(hào)不能用二級(jí)同步器跨時(shí)鐘傳輸,哪怕使用格雷碼大部分情況也不行,只有在格雷碼自增或自減順序變化才可以跨時(shí)鐘傳輸。對(duì)于多比特?cái)?shù)據(jù),在進(jìn)行傳輸時(shí)候會(huì)因?yàn)闀r(shí)序問題導(dǎo)致所有寄存器不會(huì)同時(shí)翻轉(zhuǎn)(不是不翻轉(zhuǎn),是不同時(shí)翻轉(zhuǎn)!),所以容易在跨時(shí)鐘傳輸?shù)臅r(shí)候出現(xiàn)中間態(tài)。使用格雷碼可以避免這種現(xiàn)象,但是當(dāng)格雷碼不是按計(jì)數(shù)順序變化(非順序變化相當(dāng)于每次變化不止一位),這同樣是不允許的,因?yàn)楦窭状a每次只有一位發(fā)生變化的前提是,數(shù)據(jù)是遞增或遞減的。比如異步FIFO中格雷碼可以通過二級(jí)同步器進(jìn)行CDC傳輸。
慢到快使用打兩拍的前提是什么?先給結(jié)論:兩級(jí)同步器與慢時(shí)鐘域之間無組合邏輯,因?yàn)榻M合邏輯里存在競(jìng)爭(zhēng)冒險(xiǎn),從而導(dǎo)致毛刺產(chǎn)生。我們無法預(yù)先知道CLKB 的上升沿何時(shí)會(huì)到來,CLKB 采樣到的信號(hào)就無法預(yù)知。


僅僅通過簡(jiǎn)單的同步器同步有可能是不安全的,那么如何傳遞兩個(gè)同時(shí)需要的信號(hào)(b_load和b_en)?
將b_load和b_en信號(hào)在b_clk時(shí)鐘域中合并成一個(gè)信號(hào)b_lden,然后同步至a_clk中。若果不能合并,比如譯碼信息則加入一個(gè)控制信號(hào),等兩個(gè)信號(hào)穩(wěn)定了再采樣!

四、總結(jié)(重要)
采樣中“快到慢”與“慢到快”在考慮問題時(shí)有什么區(qū)別?慢到快:只需要考慮亞穩(wěn)態(tài)問題.
快到慢:除亞穩(wěn)態(tài)問題外,還需考慮慢時(shí)鐘的采樣速率問題。因?yàn)楦鶕?jù)采樣定理,采樣頻率低于信號(hào)最高頻率2倍的時(shí)候,是無法完整采樣的。
CDC傳輸方法總結(jié):?jiǎn)伪忍兀?/p>
慢到快只考慮亞穩(wěn)態(tài)問題,采用延遲打拍法;
快到慢還需要考慮慢時(shí)鐘采樣速度,但是只要延長(zhǎng)信號(hào)長(zhǎng)度即可。常用方法為電平同步器、脈沖同步器、握手協(xié)議。其中,握手協(xié)議限制較為靈活,但握手信號(hào)需要在兩個(gè)時(shí)鐘域來回傳遞導(dǎo)致延時(shí)很大,所以握手協(xié)議是以犧牲效率為代價(jià)保證信號(hào)傳遞質(zhì)量。
多比特:
慢到快:只考慮亞穩(wěn)態(tài)問題,采用延遲打拍法。為需要傳輸?shù)臄?shù)據(jù)配上一個(gè)同步的控制使能信號(hào),數(shù)據(jù)和控制信號(hào)被同時(shí)發(fā)送到接收時(shí)鐘域,使用此同步后的控制信號(hào)來加載數(shù)據(jù)(控制信號(hào)有效表示數(shù)據(jù)穩(wěn)定不變化從而避免傳輸出錯(cuò)),這樣數(shù)據(jù)就可以在目的寄存器被安全加載。這種方法我們稱為MUX同步器法/多周期路徑同步法(意思都差不多)。
快到慢:因?yàn)榭紤]時(shí)鐘采樣速度,所以需要延長(zhǎng)(使能信號(hào))信號(hào)長(zhǎng)度。最常用的還是“握手協(xié)議”,將使能信號(hào)同步后再加載多比特?cái)?shù)據(jù)。
處理多比特?cái)?shù)據(jù)跨時(shí)鐘傳輸,最常用還是異步FIFO,
一來異步FIFO同時(shí)適用快到慢和慢到快兩種CDC傳輸;
二來也能更好地滿足數(shù)據(jù)流具有較快的傳輸速度要求。
-
傳輸
+關(guān)注
關(guān)注
0文章
157瀏覽量
28519 -
脈沖信號(hào)
+關(guān)注
關(guān)注
6文章
408瀏覽量
38629 -
時(shí)鐘域
+關(guān)注
關(guān)注
0文章
53瀏覽量
10056
原文標(biāo)題:跨時(shí)鐘域傳輸總結(jié)
文章出處:【微信號(hào):gh_9d70b445f494,微信公眾號(hào):FPGA設(shè)計(jì)論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
跨時(shí)鐘域設(shè)計(jì)之控制信號(hào)傳輸工作原理
跨時(shí)鐘域控制信號(hào)傳輸設(shè)計(jì)方案
關(guān)于跨時(shí)鐘域信號(hào)的處理方法
如何利用FPGA設(shè)計(jì)一個(gè)跨時(shí)鐘域的同步策略?
CDC跨時(shí)鐘域處理及相應(yīng)的時(shí)序約束
fpga跨時(shí)鐘域通信時(shí),慢時(shí)鐘如何讀取快時(shí)鐘發(fā)送過來的數(shù)據(jù)?
請(qǐng)問雙口RAM能用來進(jìn)行跨時(shí)鐘域傳輸數(shù)據(jù)嗎?
如何處理跨時(shí)鐘域這些基礎(chǔ)問題
一文解析跨時(shí)鐘域傳輸
評(píng)論