接口
module名为bit_async,既然是单比特的同步器那么接口自然就是6根线,分别以i_*和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;
可控随机
第一步自然是做用来产生随机数的function,思路就是另一篇博客中说的方式,通过%m来差异化每一个inst随机时的urandom的种子,思路已经在上一篇文章中说明了。将宏定义定义在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
// ----------------------------------------------------------------
// Wire declare
// ----------------------------------------------------------------
`module_urandom_define
跳变检查
因为只有在输入信号发生跳变时才会涉及到亚稳态和亚稳态的随机恢复问题,所以需要识别信号跳变,在代码里我做了信号i_data_hold和i_data_jump,i_data_jump是i_data_hold取反的结果,所以只需关注i_data_hold的逻辑就可以。
最简单的思路,在i_clk时钟前后两拍的值一样,就可以认为输入信号是稳定的:
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 <= 1'b0;
end
else begin
i_data_i_ff <= i_data;
end
end
wire i_data_hold = (i_data == i_data_i_ff);
wire i_data_jump = !i_data_hold;
但是我们知道在信号进行跨时钟域同步时必须遵循寄存器输出以及走线在快时钟域一拍以内,因此不会出现一次跳变的信号在采样时连续多拍都是不稳定的信号。所以基于这个思路,我补充了一个条件,也就是说在o_clk下i_data如果连续两拍数值一致,那么也认为其保持不会产生亚稳态,这样等价于在快时钟下两拍一致就可以:
// ----------------------------------------------------------------
// 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 <= 1'b0;
end
else begin
i_data_i_ff <= i_data;
end
end
reg i_data_o_ff;
always @(posedge o_clk or negedge o_rst_n) begin
if(o_rst_n == 1'b0)begin
i_data_o_ff <= 1'b0;
end
else begin
i_data_o_ff <= i_data;
end
end
wire i_data_hold = (i_data == i_data_i_ff) || (i_data == i_data_o_ff);
wire i_data_jump = !i_data_hold;
亚稳态恢复
因为判断跳变是在i_data输入时进行判断的,所以亚稳态随机恢复的事情也是在o_clk打拍的第一拍来进行的。简单来说就是在打拍时判断是否为i_data_jump状态,如果是则进行随机恢复:
// ----------------------------------------------------------------
// 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 <= 1'b0;
end
else begin
if(i_data_jump)begin
if(urandom_range(1,10) >= 5)begin
o_data_ff1 <= !i_data;
end
else begin
o_data_ff1 <= i_data;
end
end
信号输出
严格来说,模块应该做成拍数可配置的,不过因为只是一个探索所以我就固定做成打两拍了:
always @(posedge o_clk or negedge o_rst_n) begin
if(o_rst_n == 1'b0)begin
o_data_ff2 <= 1'b0;
end
else begin
o_data_ff2 <= o_data_ff1;
end
end
assign o_data = o_data_ff2;
波形仿真
尝试仿真一下波形,在testbench里例化了4个bit_async,然后把o_data_ff1都拽出来看看。先是慢打快的场景:
以及快打慢的场景:
系列文章入口——
【芯片验证】sva_assertion: 15道助力飞升的断言练习 |
【芯片验证】可能是RTL定向验证的巅峰之作 |
【芯片验证】RTL仿真中X态行为的传播 —— 从xprop说起 |
【芯片验证】年轻人的第一个systemVerilog验证环境全工程与解析 |
【芯片设计】verilog中有符号数和无符号数的本质探究 |
【芯片设计】论RTL中always语法的消失术 |
【芯片设计】代码即注释,注释即代码 |
【芯片设计】700行代码的risc处理器你确实不能要求太多了 |
入职芯片开发部门后,每天摸鱼之外的时间我们要做些什么呢 |
如何计算系统的outstanding 和 burst length? |
芯片搬砖日常·逼死强迫症的关键词不对齐事件 |
熟人社会里,一群没有社会价值的局外人 |