【芯片验证】异步电路碎碎念(十二)基于静态随机函数的单比特跨时钟同步器

乐活   2024-11-22 12:13   北京  
在上一篇文章里我们尝试了第二种在静态模块中构建可控随机的方案,接下来我们延续这个思路,来尝试编写一个能够模拟亚稳态随机恢复的单比特信号同步器,那么开始逐一的拆解并解决问题吧。要声明一下,下面的代码只是个人探究并没有在实际工程中实践过,因此难免有错误和漏洞。

接口

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
而后在bit_async中引用宏定义:
// ----------------------------------------------------------------
// Wire declare
// ----------------------------------------------------------------
`module_urandom_define
就可以正常的使用urandom()和urandom_range()两个函数了。

跳变检查

因为只有在输入信号发生跳变时才会涉及到亚稳态和亚稳态的随机恢复问题,所以需要识别信号跳变,在代码里我做了信号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都拽出来看看。先是慢打快的场景:

以及快打慢的场景:

系列文章入口——

【芯片设计】SoC 101(一):绪论
【芯片设计】FIFO漫谈(零)从无处不在的FIFO开始说起
【芯片设计】计算机体系结构(一)虚拟内存

【芯片设计】深入理解AMBA总线(零)绪论

【芯片设计】握手协议的介绍与时序说明
【芯片设计】复位那些小事 —— 复位消抖
【芯片设计】快速入门数字芯片设计(一)Introduction
【芯片验证】UVM源码计划(零)下定决心读源码前的自测环节
【芯片设计】异步电路碎碎念(一) 到底什么是异步电路
【芯片设计】从RTL到GDS(一):Introduction
其他文章链接——
【芯片验证】sva_assertion: 15道助力飞升的断言练习
【芯片验证】可能是RTL定向验证的巅峰之作
【芯片验证】RTL仿真中X态行为的传播 —— 从xprop说起
【芯片验证】年轻人的第一个systemVerilog验证环境全工程与解析
【芯片设计】verilog中有符号数和无符号数的本质探究
【芯片设计】论RTL中always语法的消失术
【芯片设计】代码即注释,注释即代码
【芯片设计】700行代码的risc处理器你确实不能要求太多了
入职芯片开发部门后,每天摸鱼之外的时间我们要做些什么呢
如何计算系统的outstanding 和 burst length?
芯片搬砖日常·逼死强迫症的关键词不对齐事件
熟人社会里,一群没有社会价值的局外人


芯时代青年
专心数字前端全流程,芯时代有为青年的自我修养
 最新文章