Verilog如何编写一个基础的Testbench

news2025/7/18 21:08:54

本文将讲述如何使用Verilog 编写一个基础的测试脚本(testbench)

在考虑一些关键概念之前,先来看看testbench的架构是什么样的。架构包括建模时间、initial块(initial block)和任务(task)。此文最后将以一个完整的testbench编写作为示例。

在使用verilog设计数字电路时,设计人员通常还会创建一个testbench来仿真代码以确保其按预期设计运行。设计人员可以使用多种语言构建testbench,其中最流行的是VHDL、Verilog和System verilog。

System Verilog 在行业中被广泛应用,可能是用于测试的最常用语言,但本文仅介绍 verilog 中testbench设计的基本原则。


1、Testbench的架构

testbench由不可综合的verilog 代码组成,这些代码生成被测设计的输入并验证被测设计的输出是否正确(输出是否符合预期)。

下图展示了一个基本testbench的典型架构。

  • 激励(stimulus block)是为 FPGA 设计生成的输入

  • 输出校验(output checker)检查被测模块的输出是否符合预期

  • 被测模块(design under test,DUT)即是编写的verilog模块,testbench的主要设计目的就是对其进行验证,以确保在特定输入下,其输出均与预期一致

对于较大规模的设计,激励和输出校验可以位于单独的文件中,也可以将所有这些不同的模块都包含在同一个文件中。


2、例化被测模块

编写testbench的第一步是创建一个 verilog 模块作为测试的顶层。

与讨论过的verilog module不同,在这种情况下,设计人员要创建的是一个没有输入和输出的模块。这是因为设计人员希望testbench模块是完全独立的(self contained)。

下面的代码片段展示了一个空模块的语法,这可以被用作testbench。

module <module_name> ();
    //在这里写testbench
endmodule : <module_name>

创建了一个testbench之后,必须例化被测设计,这可以将信号连接到被测设计以激励代码运行。

下面的代码片段展示了如何例化一个被测模块。

<module_name> # (
  //例化参数
  .<parameter_name> (<parameter_value>)
)
<instance_name> (
  //连接端口信号
  .<port_name> (<signal_name>),
  .<port_name> (signal_name>)
);

完成此操作后,就可以开始将激励写入testbench。激励包括时钟信号和复位信号,以及创建发送到testbench的测试数据。

为此,需要使用一些尚未学过的 verilog 结构----initial块(initial block)、foever循环(foever loop)和时间控制(time consuming)语句。


3、Verilog 中的建模时间(Modelling Time

testbench代码和设计代码之间的主要区别是testbench并不需要被综合成实际电路,为此可以使用时间控制语句这种特殊结构。事实上,这对于创建测试激励至关重要。

在 Verilog 中有一个可用的结构----它能够对仿真进行延时。在 verilog 中使用 # 字符后跟多个时间单位来模拟延时。

例如,下面的 verilog 代码展示使用延时运算符等待 10 个时间单位。

#10

这里要注意的是代码末尾并没有分号

将延时语句写在与赋值相同的代码行中也很常见,这可以有效地行使调度功能,将信号的变化安排在延迟时间之后。下面的代码片段是此种情况的一个示例。

#10 a = 1'b1;    // 在10个时间单位后,a将被赋值为1

3.1、时间单位(Timescale )编译指令

在上一节已经讨论了十个时间单位的延时用法,但在设计人员真正定义所使用的时间单位之前,这样的讨论是毫无意义的。

为了指定在仿真期间所使用的时间单位,需要使用指定时间单位时间精度(分辨率)的 verilog 编译器指令。这只需要在testbench中运行该指令一次,而且应在模块外完成。

下面的代码片段展示用来在 verilog 中指定时间单位和精度的编译指令。

`timescale <unit_time> / <resolution>

<unit_time> 指定时间的单位,<resolution>则指定时间精度。

<resolution> 很重要,因为设计人员可以使用小数来指定 verilog 代码中的延时。例如,如果设计人员想要 10.5ns 的延迟,就可以简单地写为 #10.5。因此,编译指令中的 <resolution> 决定了可以实现最小时间的步长(即精度)。

此编译指令中的两个参数都采用时间类型,例如 1ps 或 1ns。


4、Verilog initial block初始块

initial 块中编写的任何代码都会在仿真开始时执行一次且仅执行一次

下面的 verilog 代码展示了initial 块的一般语法。

initial begin
  //这里写代码
end

与 always 块不同,在 initial 块中编写的 verilog 代码几乎是不可综合的,因此其几乎只被用于仿真。但是,在verilog RTL 中也可以使用initial块来初始化信号(几乎很少用)

为了更好地理解如何使用initial块在 verilog 中编写激励,请来看一个基本示例----假设现在想要测试一个基本的两输入与门,为此需要编写代码来生成所有可能的四种输入。此外还需要使用延时运算符以在生成不同的输入之间延迟一段时间。这很重要,因为这可以允许信号有时间来传播。

下面的 verilog 代码展示了在initial块中编写此测试的方法。

initial begin
  // 每隔10个时间单位就生成一个输入
  and_in = 2b'00;
  #10 and_in = 2b'01;
  #10 and_in = 2b'10;
  #10 and_in = 2b'11;
end

5、Verilog Foever 循环Loop

在 verilog testbench中可以使用一种重要的循环类型——foever循环

使用这个构造时,实际上是创建了一个无限的循环----这意味着创建了一段在仿真过程中将永远运行的代码。

下面的 verilog 代码展示了用来编写foever循环的一般语法。

forever begin
  // our code goes here
end

当用其他编程语言编写代码时,无限循环一般被视为应极力避免的严重错误。但是,verilog 与其他编程语言不同,编写 verilog 代码是在描述硬件而不是在编写软件。

因此,至少有一种情况是可以使用无限循环的----时钟信号。为此需要一种定期连续反转信号的方法,foever循环与此相当契合。

下面的 verilog 代码展示了如何使用foever循环在testbench中生成一个时钟。需要注意的是,所编写的任何循环都必须包含在过程块(procedural block)中或生成块(generate块)中。

initial begin
   clk = 1'b0;
   forever begin
     #1 clk = ~clk;
   end
end

6、Verilog 系统任务(System Tasks

在 verilog 中编写testbench时,有一些内置的任务和函数可以提供帮助。这些被统称为系统任务或系统函数,它们很容易被识别----总是以美元符号($)开头。

虽然有很多这样的系统任务可用,但是这三个是最常用的 :$display、$monitor 和 $time

6.1、$display

$display 是 verilog 中最常用的系统任务之一。设计人员可以使用它来输出一条消息,该消息在仿真时将会显示在控制台上。

$display的使用方式与C语言中的printf函数非常类似,这意味着设计人员可以轻松地在testbench中创建文本语句,并使用它们来显示有关仿真状态的信息。

设计人员还可以在字符串中使用特殊字符 (%) 来显示设计中的信号。这样做时,还必须使用一个格式字母来决定以何种格式显示变量。最常用的格式是 b(二进制)、d(十进制)和 h(十六进制)。设计人员还可以在这个格式代码前面加上一个数字来确定要显示的位数。

下面的 verilog 代码展示了 $display 系统任务的一般语法。此代码片段还包括一个示例用例。

//一般语法
$display(<string_to_display>, <variables_to_display);

//例子:分别用2进制、16进制和10进制来打印x的值
$display("x (bin) = %b, x (hex) = %h, x (decimal) = %d", x, x, x);

设计人员可以在 $display 系统任务中使用的不同格式的完整列表如下所示。

格式代码

描述

%b 或 %B

显示为二进制

%d 或 %D

显示为十进制

%h 或 %H

显示为十六进制

%o 或 %O

显示为八进制格式

%c 或 %C

显示为 ASCII 字符

%m 或 %M

显示模块的层级名称

%s 或 %S

显示为字符串

%t 或 %T

显示为时间

6.2、$monitor

$monitor 函数与 $display 函数非常相似,但它一般被用来监视testbench的信号值,这些信号中的任何一个改变状态,都会在终端打印一条消息。

所有的系统任务在使用时都会被综合工具忽略,因此甚至可以在verilog RTL 代码中使用 $monitor 语句,尽管这并不常见。

此系统任务的一般语法显示在下面的代码片段中。此代码片段还包括一个示例用例。

//一般语法
$monitor(<message_to_display>, <variables_to_display>);

//例子:监控信号in_a和in_b的值。其中任何一个发生变化都会立即在终端打印出两个信号的值
$monitor("in_a=%b, in_b=%b\n", in_a, in_b);

6.3、$time

在testbench中常用的最后一个系统任务是 $time 。这个系统任务可以用来获取当前的仿真时间。

在testbench中通常将 $time 与 $display 或 $monitor 一起使用,以便在打印的消息中显示具体仿真时间。

下面的 verilog 代码展示了如何一起使用 $time 和 $display 来打印信息。

$display("Current simulation time = %t", $time);    //打印当前仿真时间

7、Verilog testbench示例

接下来将为一个非常简单的电路构建一个testbench以检查其功能的正确性。

下面显示的电路是将用于此示例的电路,由一个简单的两输入与门以及一个寄存器组成。

7.1、创建一个testbench模块

testbench中做的第一件事就是声明一个空模块来写入代码。

下面的代码片段展示了此testbench的模块声明。请注意,最好让被测试设计的名称与testbench的名称保持相似。一般可以简单地将 _tb 或 _test 附加到被测设计名称的末尾。

module example_tb ();
  //在这里写测试代码
endmodule : example_tb

7.2、例化被测模块

现在只有一个空白的testbench模块,接下来需要例化要测试的设计模块。

下面的代码片段展示了如何例化被测模块,假设信号 clk、in_1、in_b 和 out_q 之前就已声明。

example_design dut (
    .clock (clk),
    .reset (reset),
    .a     (in_a),
    .b     (in_b),
    .q     (out_q)
);

7.3、生成时钟和复位信号

接下来要做的是在testbench中生成一个时钟和复位信号。可以在initial块中为时钟和复位信号编写代码,然后使用延时运算符来实现信号状态的变化。

对于时钟信号,可以使用 forever 关键字在仿真期间持续运行时钟信号。使用此语法将每 1 ns 进行一次反转,从而实现500MHz 的时钟频率----选择此频率纯粹是为了实现快速仿真,实际上FPGA 中的 500MHz 时钟速率很难实现,所以testbench的时钟频率应尽量与硬件时钟频率匹配。

下面的 verilog 代码展示了如何在testbench中生成时钟和复位信号。

//生成时钟信号
initial begin
    clk = 1'b0;
    forever #1 clk = ~clk;
end

//生成复位信号
initial begin
   reset = 1'b1;
    #10
   reset = 1'b0;
end

7.4、编写测试激励信号

最后一部分是编写测试激励。为了测试被测电路,需要依次生成四种可能输入中的每一种,然后需要等待一小段时间,让信号通过代码块传播。

为此,将为输入赋值,然后使用延时语句来通过 FPGA 进行传播。如果还想监控输入和输出的值,可以使用 $monitor这个系统任务来完成。

下面的代码片段展示了相关代码。

initial begin
    $monitor("time=%3d, in_a=%b, in_b=%b, q=%2b \n",
              $time, in_a, in_b, q);

    in_a = 1'b0;in_b = 1'b0;
    #20 in_a = 1'b1;
    #20 in_a = 1'b0;in_b = 1'b1;
    #20 in_a = 1'b1;
end

7.5、完整示例代码

下面的 verilog 代码展示了完整的testbench示例。

`timescale 1ns / 1ps    //时间单位1ns,精度1ps

module example_tb ();

//声明时钟和复位信号
reg clk;
reg reset;

//输入和输出信号
reg in_a;
reg in_b;
wire out_q;

//例化被测模块
example_design dut (
  .clock (clk),
  .reset (reset),
  .a     (in_a),
  .b     (in_b),
  .q     (out_q)
);

//生成时钟信号
initial begin
  clk = 1'b0;
  forever #1 clk = ~clk;
end

//生成复位信号
initial begin
 reset = 1'b1;
 #10 reset = 1'b0;
end

//生成测试激励信号
  initial begin
  $monitor("time=%3d, in_a=%b, in_b=%b, q=%2b \n",
            $time, in_a, in_b, q);
            
  in_a = 1'b0;
  in_b = 1'b0;
  #20
  in_a = 1'b1;
  #20
  in_a = 1'b0;
  in_b = 1'b1;
  #20
  in_a = 1'b1;
  end
  
endmodule

  • 📣您有任何问题,都可以在评论区和我交流📃!

  • 📣本文由 孤独的单刀 原创,首发于CSDN平台🐵,博客主页:wuzhikai.blog.csdn.net

  • 📣您的支持是我持续创作的最大动力!如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/395673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Stimulsoft Reports.WEB 23.1.8 完美Crack

Stimulsoft Reports.WEB 适用于 ASP.NET 和 .NET Core 的报告工具 Stimulsoft Reports.WEB 是一种跨平台报告工具&#xff0c;旨在在 Web 浏览器中创建、构建、显示、打印和导出报告。该产品包括将报告工具集成到基于 ASP.NET、ASP.NET MVC、.NET Core、Angular 和 Blazor 平台…

【算法设计-分治】递归与尾递归

文章目录1. 阶乘尾递归&#xff1a;递归的进一步优化2. 斐波那契数列3. 最大公约数&#xff08;GCD&#xff09;4. 上楼梯5. 汉诺塔&#xff08;1&#xff09;输出移动过程输出移动步数5. 汉诺塔&#xff08;2&#xff09;输出移动过程输出移动步数6. 杨辉三角形7. 完全二叉树1…

管理系统-学科列表-增删改查

查 1.布局2.定义api3.导入api4.进入页面就调用api5.获取数据6.存储并渲染7.与分页建立关联a.请求参数值要与分页组件绑定b.total值存储并绑定到分页组件c.页码改变与页容量改变都要请求api1.布局 <template><div><el-card><el-form :inline"true&q…

【Leetcode】反转链表 合并链表 相交链表 链表的回文结构

目录 一.【Leetcode206】反转链表 1.链接 2.题目再现 3.解法A&#xff1a;三指针法 二.【Leetcode21】合并两个有序链表 1.链接 2.题目再现 3.三指针尾插法 三.【Leetcode160】相交链表 1.链接 2.题目再现 3.解法 四.链表的回文结构 1.链接 2.题目再现 3.解法 一.…

【python爬虫】获取cookie和uid的方式

本文以微博网站为例。 一、获取cookie &#xff08;1&#xff09;在浏览器中输入“weibo.cn” &#xff08;2&#xff09;登录自己的账号。 &#xff08;3&#xff09;登录后&#xff0c;右键空白处点击【检查】 &#xff08;4&#xff09;点击【网络】或者【Network】 浏…

Sql执行流程与Redo log、 Undo log、 Bin log日志文件

文章目录Sql执行流程与日志文件Sql的执行流程Redo LogBin logUndo logSql执行流程与日志文件 Sql的执行流程 mysql的内部组件结构如下图所示 连接器 与客户端建立连接&#xff0c;检验登录密码&#xff0c;分配相应权限 查询缓存 执行sql语句时会先从这里找一下&#xff0c;…

雷电模拟器运行非常卡顿有效解决方法分享

雷电模拟器运行非常卡顿有效解决方法分享。有用户在电脑上开启雷电模拟器来使用的时候&#xff0c;遇到了软件使用非常卡顿的情况。那么这样的软件卡顿问题要怎么去进行处理呢&#xff1f;接下来我们一起来看看以下的解决方法教学吧。 雷电模拟器卡顿的解决方法 一、关闭360核晶…

华大(小华)HC32L130工程创建

一、我们先来认识一下华大驱动库包下的文件功能。注意&#xff0c;华大提供的包是没有M0内核标准文件的&#xff0c;&#xff08;HC32L130 是M0&#xff09;ST提供的驱动库包是有的&#xff0c;就是CORE文件夹。第一步&#xff1a;创建文件夹目录具体这个创建文件夹目录的含义可…

matlab基础到实战(1)

目录概述sin函数例子四则运算实数复数逻辑运算复数运算模幅角共轭向量二维向量定义序列生成向量向量索引方式加减乘除向量间运算加减乘法除法概述 MATLAB是美国MathWorks公司出品的商业数学软件&#xff0c;用于数据分析、无线通信、深度学习、图像处理与计算机视觉、信号处理…

基于阿里云物联网平台设计的实时图传系统_采用MQTT协议传输图像

一、项目功能介绍 当前基于MQTT协议设计了一个实时图传系统,通过这个项目来演示,两个MQTT设备如何互相订阅,进行消息流转。 在阿里云服务器上创建2个设备,分为为设备A和设备B;设备A负责采集本地摄像头画面上传,设备B负责接收设备A上传的数据然后解析显示出来。在阿里云服…

0106广度优先搜索和最短路径-无向图-数据结构和算法(Java)

1 单点最短路径 单点最短路径。 给定一幅图和一个起点s&#xff0c;回答“从s到给定目的顶点v是否存在一条路径&#xff1f;如果有&#xff0c;找出其中最短的那条&#xff08;所含边数最少&#xff09;。“等类似问题。 深度优先搜索在这个问题上没有什么作为&#xff0c;因为…

个人创业做什么好?哪些互联网创业项目可长期收益?

打工没前途&#xff0c;这是现在年轻人看透了的一个事情&#xff0c;日复一日地工作&#xff0c;不如自己创业挣钱&#xff0c;那么个人创业做什么好呢&#xff1f;哪些项目可以长期稳定收益呢&#xff1f; 一、高体力项目不要做&#xff1b; 创业项目有很多&#xff0c;尤其是…

RK3588平台开发系列讲解(同步与互斥篇)自旋锁介绍

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、自旋锁介绍二、自旋锁相关的函数1、普通场景2、进程上下文和下半部3、中断相关三、相关结构体四、函数实现1、初始化2、获取自旋锁沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇介绍自旋锁的使用和基…

Clickhouse数据去重

1. Hive去重 先以两个简单的sql启发我们的话题 select count(distinct id)from order_combine;select count(id) from (select id from order_combine group by id ) t;从执行日志当中我们可以看到二者的差异&#xff08;只摘取关键部分&#xff09; # distinctStage-Stage…

kube-scheduler

kube-scheduler&#xff0c;它是 k8s 的默认调度器&#xff0c;负责为新创建出来的 pod寻找一个最合适的节点&#xff0c;这里的“最合适”指两种最优解&#xff1a;从集群中的所有节点中找出的全局最优解&#xff0c;和从集群中的部分节点中找出的局部最优解。它们分别可以解决…

电气系统中防雷接地保护的综合解决方案

防雷接地保护是通过导体和接地&#xff0c;可靠地连接一种保护性接线方法&#xff0c;在该保护性接线方法中&#xff0c;可能会在绝缘材料损坏后或在其他情况下对电器的金属部分&#xff08;即与带电部分绝缘的金属结构部分&#xff09;进行绝缘。接地保护系统仅具有相线和零线…

VSCode使用技巧,代码编写效率提升2倍以上!

VSCode是一款开源免费的跨平台文本编辑器&#xff0c;它的可扩展性和丰富的功能使得它成为了许多程序员的首选编辑器。在本文中&#xff0c;我将分享一些VSCode的使用技巧&#xff0c;帮助您更高效地使用它。 1. 插件 VSCode具有非常丰富的插件生态系统&#xff0c;通过安装插…

大数据分析案例-基于逻辑回归算法构建微博评论情感分类模型

🤵‍♂️ 个人主页:@艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞👍🏻 收藏 📂加关注+ 喜欢大数据分析项目的小伙伴,希望可以多多支持该系列的其他文章 大数据分析案例合集…

【WEB前端进阶之路】 HTML 全路线学习知识点梳理(下)

前言 本文是HTML零基础小白学习系列的第三篇文章&#xff0c;点此阅读 上一篇文章 文章目录前言十五.HTML布局1.使用div元素添加网页布局2.使用table元素添加网页布局十六.HTML表单和输入1.文本域2.密码字段3.单选按钮4.复选框5.提交按钮十七.HTML框架1.iframe语法2.iframe设置…

java中如何判断map是否为空

java中判断map是否为空的方法是&#xff1a;利用isEmpty()函数来判断。函数介绍&#xff1a;isEmpty()是Java中用于判断某种容器是否有元素的系统库函数。如用来判断ArrayList&#xff0c;HashSet&#xff0c;HashMap是否有元素等。在Java中&#xff0c;可以用isEmpty();判断一…