跨越時(shí)鐘域
FPGA設(shè)計(jì)可以使用多個(gè)時(shí)鐘。每個(gè)時(shí)鐘在FPGA內(nèi)部形成一個(gè)“時(shí)鐘域”,如果在另一個(gè)時(shí)鐘域中需要在一個(gè)時(shí)鐘域中生成的信號(hào),則需要格外小心。
本文引用地址:http://www.ex-cimer.com/article/202312/454268.htm跨時(shí)鐘域1-信號(hào)
假設(shè) clkB 域中需要來自 clkA 域的信號(hào)。 它需要“同步”到 clkB 域,因此我們要構(gòu)建一個(gè)同步器設(shè)計(jì),它從 clkA 域獲取一個(gè)信號(hào),并在 clkB 域中創(chuàng)建一個(gè)新信號(hào)。
在第一種設(shè)計(jì)中,我們假設(shè)與 clkA 和 clkB 時(shí)鐘速度相比,“信號(hào)輸入”變化緩慢。
您需要做的就是使用兩個(gè)觸發(fā)器將信號(hào)從 clkA 移動(dòng)到 clkB。
module Signal_CrossDomain(
input clkA,
// we actually don't need clkA in that example, but it is here for completeness as we'll need it in further examples input SignalIn_clkA,
input clkB,
output SignalOut_clkB
);
// We use a two-stages shift-register to synchronize SignalIn_clkA to the clkB clock domain
reg [1:0] SyncA_clkB;
always @(posedge clkB) SyncA_clkB[0] <= SignalIn_clkA; // notice that we use clkB
always @(posedge clkB) SyncA_clkB[1] <= SyncA_clkB[0]; // notice that we use clkB
assign SignalOut_clkB = SyncA_clkB[1]; // new signal synchronized to (=ready to be used in) clkB domain
endmodule
例如,在以下波形中,您可以看到慢速移動(dòng)的信號(hào)被兩個(gè)觸發(fā)器同步(和延遲)到 clkB 域:
跨時(shí)鐘域 2 - 標(biāo)志
另一個(gè)時(shí)鐘域的標(biāo)志
如果需要跨越時(shí)鐘域的信號(hào)只是一個(gè)脈沖(即它只持續(xù)一個(gè)時(shí)鐘周期),我們稱之為“標(biāo)志”。 以前的設(shè)計(jì)通常不起作用(標(biāo)志可能會(huì)丟失,或者看到時(shí)間過長,具體取決于所使用的時(shí)鐘的比例)。
我們?nèi)匀幌M褂猛狡鳎撏狡鬟m用于標(biāo)志。
訣竅是將標(biāo)志轉(zhuǎn)換為電平變化,這樣可以更容易地跨越時(shí)鐘域。
module Flag_CrossDomain(
input clkA,
input FlagIn_clkA, // this is a one-clock pulse from the clkA domain
input clkB,
output FlagOut_clkB // from which we generate a one-clock pulse in clkB domain
);
reg FlagToggle_clkA;
always @(posedge clkA) FlagToggle_clkA <= FlagToggle_clkA ^ FlagIn_clkA; // when flag is asserted, this signal toggles (clkA domain)
reg [2:0] SyncA_clkB;
always @(posedge clkB) SyncA_clkB <= {SyncA_clkB[1:0], FlagToggle_clkA}; // now we cross the clock domains
assign FlagOut_clkB = (SyncA_clkB[2] ^ SyncA_clkB[1]); // and create the clkB flag
endmodule
現(xiàn)在,如果您希望 clkA 域收到確認(rèn)(clkB 收到標(biāo)志),只需添加繁忙信號(hào)即可。
module FlagAck_CrossDomain(
input clkA,
input FlagIn_clkA,
output Busy_clkA,
input clkB,
output FlagOut_clkB
);
reg FlagToggle_clkA;
always @(posedge clkA) FlagToggle_clkA <= FlagToggle_clkA ^ (FlagIn_clkA & ~Busy_clkA);
reg [2:0] SyncA_clkB;
always @(posedge clkB) SyncA_clkB <= {SyncA_clkB[1:0], FlagToggle_clkA};
reg [1:0] SyncB_clkA;
always @(posedge clkA) SyncB_clkA <= {SyncB_clkA[0], SyncA_clkB[2]};
assign FlagOut_clkB = (SyncA_clkB[2] ^ SyncA_clkB[1]);
assign Busy_clkA = FlagToggle_clkA ^ SyncB_clkA[1];
endmodule
跨越時(shí)鐘域 3 - 任務(wù)
在另一個(gè)時(shí)鐘域中完成任務(wù)
如果 clkA 域中有任務(wù)需要在 clkB 域中完成,則可以使用以下設(shè)計(jì)。
這是一種方法
module TaskAck_CrossDomain(
input clkA,
input TaskStart_clkA,
output TaskBusy_clkA, TaskDone_clkA,
input clkB,
output TaskStart_clkB, TaskBusy_clkB,
input TaskDone_clkB
);
reg FlagToggle_clkA, FlagToggle_clkB, Busyhold_clkB;
reg [2:0] SyncA_clkB, SyncB_clkA;
always @(posedge clkA) FlagToggle_clkA <= FlagToggle_clkA ^ (TaskStart_clkA & ~TaskBusy_clkA);
always @(posedge clkB) SyncA_clkB <= {SyncA_clkB[1:0], FlagToggle_clkA};
assign TaskStart_clkB = (SyncA_clkB[2] ^ SyncA_clkB[1]);
assign TaskBusy_clkB = TaskStart_clkB | Busyhold_clkB;
always @(posedge clkB) Busyhold_clkB <= ~TaskDone_clkB & TaskBusy_clkB;
always @(posedge clkB) if(TaskBusy_clkB & TaskDone_clkB) FlagToggle_clkB <= FlagToggle_clkA;
always @(posedge clkA) SyncB_clkA <= {SyncB_clkA[1:0], FlagToggle_clkB};
assign TaskBusy_clkA = FlagToggle_clkA ^ SyncB_clkA[2];
assign TaskDone_clkA = SyncB_clkA[2] ^ SyncB_clkA[1];
endmodule
下面是一個(gè)匹配的仿真波形
跨越時(shí)鐘域 4 - 數(shù)據(jù)總線
數(shù)據(jù)總線到另一個(gè)時(shí)鐘域
為了將數(shù)據(jù)總線(2 位寬或更高)從一個(gè)時(shí)鐘域移動(dòng)到另一個(gè)時(shí)鐘域,我們可以使用多種技術(shù)。
這里有一些想法。
格雷碼:如果數(shù)據(jù)總線是單調(diào)計(jì)數(shù)器(即僅遞增或遞減),我們可以將其轉(zhuǎn)換為格雷碼,該格雷碼具有跨時(shí)鐘域的能力(在某些時(shí)序條件下)。
數(shù)據(jù)凍結(jié):如果數(shù)據(jù)總線是非單調(diào)的,則使用標(biāo)志向另一個(gè)域發(fā)出信號(hào)以捕獲該值(當(dāng)它在源時(shí)鐘域中凍結(jié)時(shí))。
數(shù)據(jù)突發(fā):如果數(shù)據(jù)總線有許多需要跨越時(shí)鐘域的連續(xù)值,請使用異步 FIFO,從源時(shí)鐘域推送值,然后從另一個(gè)域讀回值。
評論