在上一篇文章里我們嘗試了第二種在靜態(tài)模塊中構(gòu)建可控隨機(jī)的方案,接下來我們延續(xù)這個思路,來嘗試編寫一個能夠模擬亞穩(wěn)態(tài)隨機(jī)恢復(fù)的單比特信號同步器,那么開始逐一的拆解并解決問題吧。要聲明一下,下面的代碼只是個人探究并沒有在實際工程中實踐過,因此難免有錯誤和漏洞。接口module名為bit_async,既然是單比特的同步器那么接口自然就是6根線,分別以i_*和o_*來標(biāo)記時鐘域:
module bit_async #(
//parameter
)( /*AUTOARG*/
// Outputs
o_data,
// Inputs
i_clk, i_rst_n, i_data, o_clk, o_rst_n
);
// ----------------------------------------------------------------
// Interface declare
// ----------------------------------------------------------------
input i_clk;
input i_rst_n;
input i_data;
input o_clk;
input o_rst_n;
output o_data;可控隨機(jī)第一步自然是做用來產(chǎn)生隨機(jī)數(shù)的function,思路就是另一篇博客中說的方式,通過%m來差異化每一個inst隨機(jī)時的urandom的種子,思路已經(jīng)在上一篇文章中說明了。將宏定義定義在tb.f最前面的define文件中:
`define module_urandom_define \
string path_str; \
initial path_str = $psprintf(path_str, "%m"); \
\
function integer urandom; \
integer seed, i; \
begin \
seed = $urandom(); \
for(i=path_str.len; i>=0; i=i-1)begin \
seed = seed ^ path_str.getc(i); \
seed = $urandom(seed); \
end \
urandom = $abs(seed); \
end \
endfunction \
function integer urandom_range(); \
input integer min, max; \
integer seed, i; \
begin \
seed = $urandom(); \
for(i=path_str.len; i>=0; i=i-1)begin \
seed = seed ^ path_str.getc(i); \
seed = $urandom(seed); \
end \
urandom_range = min + $abs(seed % (max - min)); \
end \
endfunction而后在bit_async中引用宏定義:// ----------------------------------------------------------------
// Wire declare
// ----------------------------------------------------------------
`module_urandom_define就可以正常的使用urandom()和urandom_range()兩個函數(shù)了。跳變檢查因為只有在輸入信號發(fā)生跳變時才會涉及到亞穩(wěn)態(tài)和亞穩(wěn)態(tài)的隨機(jī)恢復(fù)問題,所以需要識別信號跳變,在代碼里我做了信號i_data_hold和i_data_jump,i_data_jump是i_data_hold取反的結(jié)果,所以只需關(guān)注i_data_hold的邏輯就可以。
最簡單的思路,在i_clk時鐘前后兩拍的值一樣,就可以認(rèn)為輸入信號是穩(wěn)定的:
reg i_data_i_ff;
always @(posedge i_clk or negedge i_rst_n) begin
if(i_rst_n == 1'b0)begin
i_data_i_ff 但是這樣存在一個問題,即在慢打快的時候接受域的快時鐘會在多拍都看到信號跳轉(zhuǎn),比如下面這樣:
但是我們知道在信號進(jìn)行跨時鐘域同步時必須遵循寄存器輸出以及走線在快時鐘域一拍以內(nèi),因此不會出現(xiàn)一次跳變的信號在采樣時連續(xù)多拍都是不穩(wěn)定的信號。所以基于這個思路,我補(bǔ)充了一個條件,也就是說在o_clk下i_data如果連續(xù)兩拍數(shù)值一致,那么也認(rèn)為其保持不會產(chǎn)生亞穩(wěn)態(tài),這樣等價于在快時鐘下兩拍一致就可以:
// ----------------------------------------------------------------
// gain jump point
// ----------------------------------------------------------------
reg i_data_i_ff;
always @(posedge i_clk or negedge i_rst_n) begin
if(i_rst_n == 1'b0)begin
i_data_i_ff 亞穩(wěn)態(tài)恢復(fù)因為判斷跳變是在i_data輸入時進(jìn)行判斷的,所以亞穩(wěn)態(tài)隨機(jī)恢復(fù)的事情也是在o_clk打拍的第一拍來進(jìn)行的。簡單來說就是在打拍時判斷是否為i_data_jump狀態(tài),如果是則進(jìn)行隨機(jī)恢復(fù):
// ----------------------------------------------------------------
// i_data -> o_data_ff1 -> o_data_ff2
// ----------------------------------------------------------------
reg o_data_ff1, o_data_ff2;
always @(posedge o_clk or negedge o_rst_n) begin
if(o_rst_n == 1'b0)begin
o_data_ff1 = 5)begin
o_data_ff1 信號輸出嚴(yán)格來說,模塊應(yīng)該做成拍數(shù)可配置的,不過因為只是一個探索所以我就固定做成打兩拍了:
always @(posedge o_clk or negedge o_rst_n) begin
if(o_rst_n == 1'b0)begin
o_data_ff2 如此一來,單比特的跨時鐘域同步器就完成了。波形仿真嘗試仿真一下波形,在testbench里例化了4個bit_async,然后把o_data_ff1都拽出來看看。先是慢打快的場景:
以及快打慢的場景:
系列文章入口——
【芯片設(shè)計】SoC 101(一):緒論 | 【芯片設(shè)計】FIFO漫談(零)從無處不在的FIFO開始說起 | 【芯片設(shè)計】計算機(jī)體系結(jié)構(gòu)(一)虛擬內(nèi)存 | 【芯片設(shè)計】深入理解AMBA總線(零)緒論
| 【芯片設(shè)計】握手協(xié)議的介紹與時序說明 | 【芯片設(shè)計】復(fù)位那些小事 —— 復(fù)位消抖 | 【芯片設(shè)計】快速入門數(shù)字芯片設(shè)計(一)Introduction | 【芯片驗證】UVM源碼計劃(零)下定決心讀源碼前的自測環(huán)節(jié)
| 【芯片設(shè)計】異步電路碎碎念(一) 到底什么是異步電路
| 【芯片設(shè)計】從RTL到GDS(一):Introduction
| 其他文章鏈接——
【芯片驗證】sva_assertion: 15道助力飛升的斷言練習(xí) | 【芯片驗證】可能是RTL定向驗證的巔峰之作 | 【芯片驗證】RTL仿真中X態(tài)行為的傳播 —— 從xprop說起 | 【芯片驗證】年輕人的第一個systemVerilog驗證環(huán)境全工程與解析 |
【芯片設(shè)計】verilog中有符號數(shù)和無符號數(shù)的本質(zhì)探究
| 【芯片設(shè)計】論RTL中always語法的消失術(shù) | 【芯片設(shè)計】代碼即注釋,注釋即代碼 | 【芯片設(shè)計】700行代碼的risc處理器你確實不能要求太多了 |
入職芯片開發(fā)部門后,每天摸魚之外的時間我們要做些什么呢 | 如何計算系統(tǒng)的outstanding 和 burst length? | 芯片搬磚日!け扑缽(qiáng)迫癥的關(guān)鍵詞不對齊事件 | 熟人社會里,一群沒有社會價值的局外人 |
|