前言
因为后面我想做一些其他的实验,需要一个用于仿真的模拟亚稳态的单比特同步模块,需要满足几个需求:
1.在第一拍出发跨异步亚稳态,即发生信号跳变时,同步器第一拍的结果随机为0或者为1;
2.同步器例化多份,每一个inst亚稳态的恢复值随机,横向之间互相没有关系;
3.同一个inst,输入信号跳变,每次亚稳态恢复的值随机,纵向之间相互没有关系;
4.亚稳态恢复结果能够被seed控制,稳定复现;
其实这几个需求,核心还是之前在另外一个博文里探讨过的内容,如何在静态模块中做可控制的随机:
【验证小白】静态模块module/interface中的$urandom可控随机探索
这次实际是一次具体的使用吧。
代码
每次有信号跳变时,需要在RTL中触发一次随机,将同步器的第一拍输出随机为0或者1(实际就是把打三拍之后的亚稳态恢复值,聚在第一拍模拟完成)。
第一步,做用来产生随机数的function,思路就是另一篇博客中说的方式,通过%m来差异化每一个inst随机时的%urandom的种子;
1. /*for module random ctrl by seed*/ 2. string path_str; 3. initial path_str = $psprintf(path_str, "%m"); 4. 5. function integer urandom_range(); 6. input integer min, max; 7. integer seed, i; 8. begin 9. seed = $urandom(); 10. for(i=path_str.len; i>=0; i=i-1)begin 11. seed = seed ^ path_str.getc(i); 12. seed = $urandom(seed); 13. end 14. urandom_range = min + $abs(seed % (max - min)); 15. end 16. endfunction 17. /*end*/
第二步,判定输入信号是否跳变,我的做法就是,在i_clk和o_clk分别做一个i_sync_ff(i_sync_ff为实际的跨异步信号)的打拍i_sync_ff1/o_sync_org0,当i_sync_ff与i_sync_ff1保持一致或i_sync_ff与o_sync_org0保持一致时就认为i_sync_ff稳定了足够长的时间,不会触发亚稳态的问题(分别对应快转慢和慢转快);
1. reg o_sync_org0; 2. always @(posedge o_clk or negedge o_rst_n) begin 3. if(o_rst_n == 1'b0)begin 4. o_sync_org0 <= 1'b0; 5. end 6. else begin 7. o_sync_org0 <= i_sync_ff; 8. end 9. end 10. wire i_sync_hold = (i_sync_ff1 == i_sync_ff0) | (o_sync_org0 == i_sync_ff0);
第三步,在判定信号跳变时,触发亚稳态问题以及随机恢复;
1. reg o_sync_ff0; 2. always @(posedge o_clk or negedge o_rst_n) begin 3. if(o_rst_n == 1'b0)begin 4. o_sync_ff0 <= 1'b0; 5. end 6. else begin 7. if(i_sync_hold)begin 8. o_sync_ff0 <= i_sync_ff; 9. end 10. else begin 11. if(urandom_range(1,10) >= 5)begin 12. o_sync_ff0 <= ~i_sync_ff; 13. end 14. else begin 15. o_sync_ff0 <= i_sync_ff; 16. end 17. end 18. end 19. end
第四步,把o_sync_ff0再打两拍,作为同步器的最终输出;
1. reg o_sync_ff1, o_sync_ff2; 2. always @(posedge o_clk or negedge o_rst_n) begin 3. if(o_rst_n == 1'b0)begin 4. o_sync_ff1 <= 1'b0; 5. o_sync_ff2 <= 1'b0; 6. end 7. else begin 8. o_sync_ff1 <= o_sync_ff0; 9. o_sync_ff2 <= o_sync_ff1; 10. end 11. end 12. 13. assign o_sync = o_sync_ff2;
第五步,把o_sync_org0也打两拍输出,作为完全没有亚稳态情况下的参考输出;
1. reg o_sync_org1, o_sync_org2; 2. always @(posedge o_clk or negedge o_rst_n) begin 3. if(o_rst_n == 1'b0)begin 4. o_sync_org1 <= 1'b0; 5. o_sync_org2 <= 1'b0; 6. end 7. else begin 8. o_sync_org1 <= o_sync_org0; 9. o_sync_org2 <= o_sync_org1; 10. end 11. end 12. assign o_sync_org = o_sync_org2;
第六步,搭一个简易定向验证环境,测一下各种功能是否能出现,这个就请参考下文吧:
【验证小白】编译、仿真与波形 —— 基于VCS的通用superbench平台搭建
OK,模拟亚稳态的单比特三拍同步器就完成了,仿真一下波形看看各种情况是否有触发;
波形
电平信号跳变为1时,亚稳态恢复为错误值,导致晚一拍采样到 1;
电平信号跳变为0时,亚稳态恢复为错误值,导致晚一拍采样到 0;
电平信号跳变为0时,亚稳态恢复为正确值;
电平信号跳变为1时,亚稳态恢复为正确值;
脉冲信号,被完全正确采样;
脉冲信号,跳变为1时亚稳态恢复为错误值0,跳变为0时亚稳态恢复为正确值0,亚稳态导致这个脉冲被吃掉了;
脉冲输入过窄,导致这个脉冲被吃掉了;
上升沿亚稳态恢复正常,下降沿恢复异常;
上升沿 恢复异常,下降沿恢复异常;
波形看着好像情况是比较完备的哈,我准备先用着如果后面发现问题再说吧~~~