UVM异步验证

@[T


dirver的实现

关于UVM验证的项目与资料有点少,由于刚入门,想找一个简单的项目来做。决定从零开始搭建一个异步fifo验证环境,参考了很多大佬的资料(发现对着大佬的代码写下来环境跑不起来),用绿皮书+UVM实战两本书决定自己一步一步做一个吧。会按照自己做的顺序来更新组件,水平有限,有问题的地方还请指出,感谢。

1.异步fifo的设计:

异步FIFO的设计代码有很多,在这里不做赘述,直接将DUT+TB的代码贴出:数据16位,fifo存储深度4位:

DRAM模块:


 
 
  1. `timescale 1ns / 1ns
  2. module DPRAM #
  3. (
  4. parameter WIDTH = 16,
  5. parameter DEPTH = 16,
  6. parameter ADDR = 4
  7. )
  8. (
  9. input wrclk,
  10. input rdclk,
  11. input rd_rst_n,
  12. input wr_en,
  13. input rd_en,
  14. input [WIDTH -1: 0]wr_data,
  15. input [ADDR -1: 0]wr_addr,
  16. input [ADDR -1: 0]rd_addr,
  17. output reg [WIDTH -1: 0]rd_data
  18. );
  19. reg[WIDTH -1: 0]DPRAM[DEPTH -1: 0];
  20. always@(posedge wrclk) begin
  21. if(wr_en)
  22. DPRAM[wr_addr] <= wr_data;
  23. end
  24. always@(posedge rdclk or negedge rd_rst_n) begin
  25. if( !rd_rst_n)
  26. rd_data <= 'b0;
  27. else if(rd_en)
  28. rd_data <= DPRAM[rd_addr];
  29. end
  30. endmodule

FIFO模块:


 
 
  1. `timescale 1ns/1ns
  2. `include "DPRAM.v"
  3. module ASFIFO #
  4. (
  5. parameter WIDTH = 16, //数据总线宽度
  6. parameter PTR = 4 //fifo存储深度
  7. )
  8. (
  9. input wrclk,
  10. input rdclk,
  11. input wr _rst_n,
  12. input rd _rst_n,
  13. input wr _en,
  14. input rd_en,
  15. input [WIDTH-1:0]wr _data,
  16. output [WIDTH-1:0]rd_data,
  17. output reg wr _full,
  18. output reg rd_empty
  19. );
  20. // **** **** ***写时钟信号定义*** **** **** ****//
  21. reg[PTR:0] wr _bin;
  22. reg[PTR:0] wr_gray;
  23. reg[PTR:0] rd _gray_ff1;
  24. reg[PTR:0] rd _gray_ff2;
  25. reg[PTR:0] rd _bin_wr;
  26. // **** **** ***读时钟信号定义*** **** **** ****//
  27. reg[PTR:0] rd _bin;
  28. reg[PTR:0] rd_gray;
  29. reg[PTR:0] wr _gray_ff1;
  30. reg[PTR:0] wr _gray_ff2;
  31. reg[PTR:0] wr _bin_rd;
  32. integer i,j;
  33. // **** **** **DPRAM控制信号** **** **** **//
  34. wire dpram_wr_en;
  35. wire [PTR-1:0] dpram_wr_addr;
  36. wire [WIDTH-1:0] dpram_wr_data;
  37. wire dpram_rd_en;
  38. wire [PTR-1:0] dpram_rd_addr;
  39. wire [WIDTH-1:0] dpram_rd_data;
  40. //** **** **** **** **** ***写时钟域*** **** **** **** ****//
  41. //二进制写地址递增
  42. always@(posedge wrclk or posedge wr _rst_n)begin
  43. if(!wr _rst_n)begin
  44. wr_bin <= 'b0;
  45. end
  46. else if(wr_en == 1'b1 && wr_full == 1'b0)begin
  47. wr_bin <= wr_bin + 1'b1;
  48. end
  49. else begin
  50. wr_bin <= wr_bin;
  51. end
  52. end
  53. //*********二进制转换格雷码***********//
  54. //********写地址**********************//
  55. always@(posedge wrclk or posedge wr _rst_n)begin
  56. if(!wr _rst_n)begin
  57. wr_gray <= 'b0;
  58. end
  59. else begin
  60. wr_gray <= {wr_bin[PTR],wr_bin[PTR:1]^wr_bin[PTR-1:0]};
  61. end
  62. end
  63. // **** **** ***r2w*** **** **** **** **** *//
  64. //*************使用多个中间值打两拍gray>ff1>ff2最终两拍后取ff2 **** **** **** ***//
  65. always@(posedge
  66. or posedge wr_rst_n)begin
  67. if(!wr_rst_n)begin
  68. rd_gray_ff1 <= 'b0;
  69. rd_gray_ff2 <= 'b0;
  70. end
  71. else begin
  72. rd_gray_ff1 <= rd_gray;
  73. rd_gray_ff2 <= rd_gray_ff1;
  74. end
  75. end
  76. //*** **** ***rd_addr_bin 2 wr_addr_bin*** **** **** **** *//
  77. always@(*)begin
  78. rd _bin_wr[PTR] = rd _gray_ff2[PTR];
  79. for(i=PTR-1;i>=0;i=i-1)
  80. rd_bin_wr[i] = rd_bin_wr[i+1]^rd_gray_ff2[i];
  81. end
  82. // **** **** **写满** **** **** **** ***//
  83. always@(*)begin
  84. if(wr_bin[PTR]!=rd_bin_wr[PTR]&&(wr_bin[PTR-1:0]==rd_bin_wr[PTR-1:0]))
  85. wr_full = 1'b1;
  86. else
  87. wr_full = 1'b0;
  88. end
  89. //** **** **** **读时钟域** **** **** **** **** **//
  90. always@(posedge rdclk or posedge rd_rst_n)begin
  91. if(!rd_rst_n)begin
  92. rd_bin <= 'b0;
  93. end
  94. else if(rd_en == 1'b1 && rd _empty == 1'b0)begin
  95. rd_bin <= rd _bin + 1'b1;
  96. end
  97. else begin
  98. rd_bin <= rd _bin;
  99. end
  100. end
  101. //***********读地址**************//
  102. always@(posedge rdclk or posedge rd_rst _n)begin
  103. if(!rd_rst _n)begin
  104. rd_gray <= 'b0;
  105. end
  106. else begin
  107. rd_gray <= {rd_bin[PTR],rd_bin[PTR:1]^rd_bin[PTR-1:0]};
  108. end
  109. end
  110. always@(posedge rdclk or posedge rd_rst_n)begin
  111. if(!rd_rst_n)begin
  112. wr_gray_ff1 <= 'b0;
  113. wr_gray_ff2 <= 'b0;
  114. end
  115. else begin
  116. wr_gray_ff1 <= wr_gray;
  117. wr_gray _ff2 <= wr_gray _ff1;
  118. end
  119. end
  120. always@(*)begin
  121. wr_bin _rd[PTR] = wr_gray _ff2[PTR];
  122. for(j=PTR-1;j>=0;j=j-1)
  123. wr_bin _rd[j] = wr_bin _rd[j+1]^wr_gray _ff2[j];
  124. end
  125. always@(*)begin
  126. if(rd_bin == wr _bin_rd)
  127. rd_empty = 1'b1;
  128. else
  129. rd_empty = 1'b0;
  130. end
  131. DPRAM
  132. #(.WIDTH(16),.DEPTH(16),.ADDR(4))
  133. U_DPRAM
  134. (
  135. .wrclk(wrclk),
  136. .rdclk(rdclk),
  137. .rd_rst _n(rd_rst _n),
  138. .wr_en(dpram _wr_en),
  139. .rd_en(dpram_rd_en),
  140. .wr_data(dpram _wr_data),
  141. .rd_data(dpram_rd_data),
  142. .wr_addr(dpram _wr_addr),
  143. .rd_addr(dpram_rd_addr)
  144. );
  145. assign dpram_wr _en = (wr_en == 1'b1 && wr _full == 1'b0)? 1'b1:1'b0;
  146. assign dpram_rd _en = (rd_en == 1'b1 && rd _empty == 1'b0)? 1'b1:1'b0;
  147. assign dpram_wr _data = wr_data;
  148. assign rd_data = dpram_rd_data;
  149. assign dpram_wr _addr = wr_bin[PTR-1:0];
  150. assign dpram_rd_addr = rd_bin[PTR-1:0];
  151. endmodule

TB:


 
 
  1. `timescale 1ns/ 1ns
  2. `include "FIFO.v"
  3. module ASFIFO_tb;
  4. parameter WIDTH = 16;
  5. parameter PTR = 4 ;
  6. reg wrclk;
  7. reg wr_rst_n;
  8. reg[WIDTH- 1: 0] wr_data;
  9. reg wr_en;
  10. wire wr_full;
  11. reg rdclk;
  12. reg rd_rst_n;
  13. wire[WIDTH- 1: 0] rd_data;
  14. reg rd_en;
  15. wire rd_empty;
  16. reg init_done;
  17. reg[ 3: 0] cnt;
  18. initial begin
  19. wr_rst_n = 1;
  20. rd_rst_n = 1;
  21. wrclk = 0;
  22. rdclk = 0;
  23. wr_en = 0;
  24. rd_en = 0;
  25. wr_data = 'b0;
  26. init_done= 0;
  27. # 30 wr_rst_n = 0;
  28. rd_rst_n = 0;
  29. # 30 wr_rst_n = 1;
  30. rd_rst_n = 1;
  31. # 30 init_done = 1;
  32. end
  33. always
  34. # 2 wrclk = ~wrclk;
  35. always
  36. # 4 rdclk = ~rdclk;
  37. always@(*)begin
  38. if(init_done)begin
  39. if(wr_full) wr_en = 0;
  40. else wr_en = 1;
  41. end
  42. end
  43. always@(*)begin
  44. if(init_done)begin
  45. if(rd_empty) rd_en = 0;
  46. else rd_en = 1;
  47. end
  48. end
  49. always@(posedge wrclk)begin
  50. if(init_done)begin
  51. if(wr_full == 1 'b0)begin
  52. wr_data <= wr_data + 1;
  53. end
  54. else begin
  55. wr_data <= wr_data;
  56. end
  57. end
  58. else
  59. wr_data <= 'b0;
  60. end
  61. ASFIFO
  62. #(.WIDTH( 16),.PTR( 4))
  63. ASFIFO
  64. (
  65. .wrclk(wrclk),
  66. .rdclk(rdclk),
  67. .rd_rst_n(rd_rst_n),
  68. .wr_rst_n(wr_rst_n),
  69. .wr_en(wr_en),
  70. .rd_en(rd_en),
  71. .wr_data(wr_data),
  72. .rd_data(rd_data),
  73. .wr_full(wr_full),
  74. .rd_empty(rd_empty)
  75. );
  76. endmodule

2.搭建UVM验证环境

(1)top.tb的搭建:

这里的写法其实类似于tb,主要是我们将发送数据更改到了driver中,多个initial块并行执行,执行到$finish后跳出,这里我们将dirver例化,然后调用main_phase,执行发送随机data的task


 
 
  1. `timescale 1ns / 1ns
  2. `include "uvm_macros.svh"
  3. import uvm_pkg:: *;
  4. `include "fifo_driver.sv"
  5. module top_tb;
  6. parameter WIDTH = 16;
  7. parameter PTR = 4 ;
  8. reg wrclk;
  9. reg wr_rst_n;
  10. reg[WIDTH -1: 0] wr_data;
  11. reg wr_en;
  12. wire wr_full;
  13. reg rdclk;
  14. reg rd_rst_n;
  15. wire[WIDTH -1: 0] rd_data;
  16. reg rd_en;
  17. wire rd_empty;
  18. reg init_done;
  19. ASFIFO
  20. #(.WIDTH( 16),.PTR( 4))
  21. ASFIFO
  22. (
  23. .wrclk(wrclk),
  24. .rdclk(rdclk),
  25. .rd_rst_n(rd_rst_n),
  26. .wr_rst_n(wr_rst_n),
  27. .wr_en(wr_en),
  28. .rd_en(rd_en),
  29. .wr_data(wr_data),
  30. .rd_data(rd_data),
  31. .wr_full(wr_full),
  32. .rd_empty(rd_empty)
  33. );
  34. initial begin
  35. wrclk = 0;
  36. forever begin
  37. # 2 wrclk = ~wrclk;
  38. end
  39. end
  40. initial begin
  41. rdclk = 0;
  42. forever begin
  43. # 4 rdclk = ~rdclk;
  44. end
  45. end
  46. initial begin
  47. wr_rst_n = 1;
  48. rd_rst_n = 1;
  49. wr_en = 0;
  50. rd_en = 0;
  51. wr_data = 'b0;
  52. init_done= 0;
  53. #30 wr_rst_n = 0;
  54. rd_rst_n = 0;
  55. #30 wr_rst_n = 1;
  56. rd_rst_n = 1;
  57. #30 init_done = 1;
  58. end
  59. always@(*)begin
  60. if(init_done)begin
  61. if(wr_full) wr_en = 0;
  62. else wr_en = 1;
  63. end
  64. end
  65. always@(*)begin
  66. if(init_done)begin
  67. if(rd_empty) rd_en = 0;
  68. else rd_en = 1;
  69. end
  70. end
  71. initial begin
  72. fifo_driver drv;
  73. drv = new("drv",null);
  74. drv.main_phase(null);
  75. $finish();
  76. end
  77. endmodule

(2)fifo_driver的搭建

首先定义一个driver,比较简单,只需要一个new函数与main_phase(run_phase 中的一个phase)

这里考虑到要让线程同步进行,选用fork join可以解决这个问题,之前调试了半天,有一个bug就是init_done拉高前,wr_en拉高已经开始有wrdata了,但是rddata读取数据是从init_done拉高后一个cycle开始读的,用了fork join后完美解决。用forever 会导致这个sim无法finish 一直卡在第一个线程中。使用for循环来控制我们要发送多少个随机数据,然后把每一个发送的数据与发送时间打印出来,如果fifo已经满了 显示full,等全部发送完后,打印finished。逻辑比较简单,其实跟tb的区别也就是可以发送随机的数据。


 
 
  1. `ifndef MY_DRIVER__SV
  2. `define MY_DRIVER__SV
  3. import uvm_pkg::*;
  4. `include "uvm_macros.svh"
  5. class fifo_driver extends uvm_driver;
  6. function new(string name = "fifo_driver",uvm_component parent = null);
  7. super. new(name,parent);
  8. endfunction
  9. extern virtual task main_phase(uvm_phase phase);
  10. endclass
  11. task fifo_driver::main_phase(uvm_phase phase);
  12. fork
  13. //forever begin
  14. @(top_tb.init_done == 0)begin
  15. top_tb.wr_data <= 'b0;
  16. end
  17. @(top_tb.init_done == 1) begin
  18. //@(posedge top_tb.wrclk);
  19. for(int i = 0;i < 32;i++) begin
  20. @(posedge top_tb.wrclk);
  21. if(top_tb.wr_full == 0)begin
  22. top_tb.wr_data <= $urandom_range( 0, 255);
  23. `uvm_info( "fifo_driver",$sformatf( "%0d is driverd at %0t",top_tb.wr_data,$time), UVM_LOW)
  24. end
  25. else begin
  26. top_tb.wr_data <= top_tb.wr_data;
  27. `uvm_info( "fifo_driver", "fifo is full", UVM_LOW)
  28. end
  29. end
  30. end
  31. join
  32. `uvm_info( "fifo_driver", "drive is finished", UVM_LOW)
  33. endtask
  34. `endif

最后给各位附上波形图:

可见在90ns init_done拉高的下一个wrclk 开始写入随机数据

然后就先写这么多,后面会根据自己做的进度来慢慢更新的。实在是太菜了,调个driver调一天

OC](这里写自定义目录标题)

欢迎使用Markdown编辑器

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值