【Verilog 教程】6.6Verilog 仿真激励

news2025/4/6 11:19:22

关键词:testbench,仿真,文件读写
Verilog 代码设计完成后,还需要进行重要的步骤,即逻辑功能仿真。仿真激励文件称之为 testbench,放在各设计模块的顶层,以便对模块进行系统性的例化调用进行仿真。

毫不夸张的说,对于稍微复杂的 Verilog 设计,如果不进行仿真,即便是经验丰富的老手,99.9999% 以上的设计都不会正常的工作。不能说仿真比设计更加的重要,但是一般来说,仿真花费的时间会比设计花费的时间要多。有时候,考虑到各种应用场景,testbench 的编写也会比 Verilog 设计更加的复杂。所以,数字电路行业会具体划分设计工程师和验证工程师。

下面,对 testbench 做一个简单的学习。

testbench 结构划分
testbench 一般结构如下:

在这里插入图片描述
其实 testbench 最基本的结构包括信号声明、激励和模块例化。

根据设计的复杂度,需要引入时钟和复位部分。当然更为复杂的设计,激励部分也会更加复杂。根据自己的验证需求,选择是否需要自校验和停止仿真部分。

当然,复位和时钟产生部分,也可以看做激励,所以它们都可以在一个语句块中实现。也可以拿自校验的结果,作为结束仿真的条件。

实际仿真时,可以根据自己的个人习惯来编写 testbench,这里只是做一份个人的总结。

testbench 仿真举例
前面的章节中,已经写过很多的 testbench。其实它们的结构也都大致相同。

下面,我们举一个数据拼接的简单例子,对 testbench 再做一个具体的分析。

一个 2bit 数据拼接成 8bit 数据的功能模块描述如下:

实例

module  data_consolidation
    (
        input           clk ,
        input           rstn ,
        input [1:0]     din ,          //data in
        input           din_en ,
        output [7:0]    dout ,
        output          dout_en        //data out
     );

   // data shift and counter
    reg [7:0]            data_r ;
    reg [1:0]            state_cnt ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            state_cnt     <= 'b0 ;
            data_r        <= 'b0 ;
        end
        else if (din_en) begin
            state_cnt     <= state_cnt + 1'b1 ;    //数据计数
            data_r        <= {data_r[5:0], din} ;  //数据拼接
        end
        else begin
            state_cnt <= 'b0 ;
        end
    end
    assign dout          = data_r ;

    // data output en
    reg                  dout_en_r ;
    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            dout_en_r       <= 'b0 ;
        end
        //计数为 3 且第 4 个数据输入时,同步输出数据输出使能信号
        else if (state_cnt == 2'd3 & din_en) begin  
            dout_en_r       <= 1'b1 ;
        end
        else begin
            dout_en_r       <= 1'b0 ;
        end
    end
    //这里不直接声明dout_en为reg变量,而是用相关寄存器对其进行assign赋值
    assign dout_en       = dout_en_r;

endmodule

对应的 testbench 描述如下,增加了文件读写的语句:

实例

`timescale 1ns/1ps

   //============== (1) ==================
   //signals declaration
module test ;
    reg          clk;
    reg          rstn ;
    reg [1:0]    din ;
    reg          din_en ;
    wire [7:0]   dout ;
    wire         dout_en ;

    //============== (2) ==================
    //clock generating
    real         CYCLE_200MHz = 5 ; //
    always begin
        clk = 0 ; #(CYCLE_200MHz/2) ;
        clk = 1 ; #(CYCLE_200MHz/2) ;
    end

    //============== (3) ==================
    //reset generating
    initial begin
        rstn      = 1'b0 ;
        #8 rstn      = 1'b1 ;
    end

    //============== (4) ==================
    //motivation
    int          fd_rd ;
    reg [7:0]    data_in_temp ;  //for self check
    reg [15:0]   read_temp ;     //8bit ascii data, 8bit \n
    initial begin
        din_en    = 1'b0 ;        //(4.1)
        din       = 'b0 ;
        open_file("../tb/data_in.dat", "r", fd_rd); //(4.2)
        wait (rstn) ;    //(4.3)
        # CYCLE_200MHz ;

        //read data from file
        while (! $feof(fd_rd) ) begin  //(4.4)
            @(negedge clk) ;
            $fread(read_temp, fd_rd);
            din    = read_temp[9:8] ;
            data_in_temp = {data_in_temp[5:0], din} ;
            din_en = 1'b1 ;
        end

        //stop data
        @(posedge clk) ;  //(4.5)
        #2 din_en = 1'b0 ;
    end

    //open task
    task open_file;
        input string      file_dir_name ;
        input string      rw ;
        output int        fd ;

        fd = $fopen(file_dir_name, rw);
        if (! fd) begin
            $display("--- iii --- Failed to open file: %s", file_dir_name);
        end
        else begin
            $display("--- iii --- %s has been opened successfully.", file_dir_name);
        end
    endtask

    //============== (5) ==================
    //module instantiation
    data_consolidation    u_data_process
    (
      .clk              (clk),
      .rstn             (rstn),
      .din              (din),
      .din_en           (din_en),
      .dout             (dout),
      .dout_en          (dout_en)
     );

    //============== (6) ==================
    //auto check
    reg  [7:0]           err_cnt ;
    int                  fd_wr ;

    initial begin
        err_cnt   = 'b0 ;
        open_file("../tb/data_out.dat", "w", fd_wr);
        forever begin
            @(negedge clk) ;
            if (dout_en) begin
                $fdisplay(fd_wr, "%h", dout);
            end
        end
    end

    always @(posedge clk) begin
        #1 ;
        if (dout_en) begin
            if (data_in_temp != dout) begin
                err_cnt = err_cnt + 1'b1 ;
            end
        end
    end

    //============== (7) ==================
    //simulation finish
    always begin
        #100;
        if ($time >= 10000)  begin
            if (!err_cnt) begin
                $display("-------------------------------------");
                $display("Data process is OK!!!");
                $display("-------------------------------------");
            end
            else begin
                $display("-------------------------------------");
                $display("Error occurs in data process!!!");
                $display("-------------------------------------");
            end
            #1 ;
            $finish ;
        end
    end

endmodule // test

仿真结果如下。由图可知,数据整合功能的设计符合要求:加粗样式
在这里插入图片描述
testbench 具体分析
1)信号声明

testbench 模块声明时,一般不需要声明端口。因为激励信号一般都在 testbench 模块内部,没有外部信号。

声明的变量应该能全部对应被测试模块的端口。当然,变量不一定要与被测试模块端口名字一样。但是被测试模块输入端对应的变量应该声明为 reg 型,如 clk,rstn 等,输出端对应的变量应该声明为 wire 型,如 dout,dout_en。

2)时钟生成

生成时钟的方式有很多种,例如以下两种生成方式也可以借鉴。

实例

initial clk = 0 ;
always #(CYCLE_200MHz/2) clk = ~clk;

initial begin
    clk = 0 ;
    forever begin
        #(CYCLE_200MHz/2) clk = ~clk;
    end
end      

需要注意的是,利用取反方法产生时钟时,一定要给 clk 寄存器赋初值。

利用参数的方法去指定时间延迟时,如果延时参数为浮点数,该参数不要声明为 parameter 类型。例如实例中变量 CYCLE_200MHz 的值为 2.5。如果其变量类型为 parameter,最后生成的时钟周期很可能就是 4ns。当然,timescale 的精度也需要提高,单位和精度不能一样,否则小数部分的时间延迟赋值也将不起作用。

3)复位生成

复位逻辑比较简单,一般赋初值为 0,再经过一段小延迟后,复位为 1 即可。

这里大多数的仿真都是用的低有效复位。

4)激励部分

激励部分该产生怎样的输入信号,是根据被测模块的需要来设计的。

本次实例中:

(4.1) 对被测模块的输入信号进行一个初始化,防止不确定值 X 的出现。激励数据的产生,我们需要从数据文件内读入。
(4.2) 处利用一个 task 去打开一个文件,只要指定文件存在,就可以得到一个不为 0 的句柄信号 fp_rd。fp_rd 指定了文件数据的起始地址。
(4.3) 的操作是为了等待复位后,系统有一个安全稳定的可测试状态。
(4.4) 开始循环读数据、给激励。在时钟下降沿送出数据,是为了被测试模块能更好的在上升沿采样数据。
利用系统任务 $fread ,通过句柄信号 fd_rd 将读取的 16bit 数据变量送入到 read_temp 缓存。

输入数据文件前几个数据截图如下。因为 $fread 只能读取 2 进制文件,所以输入文件的第一行对应的 ASCII 码应该是 330a,所以我们想要得到文件里的数据 3,应该取变量 read_temp 的第 9 到第 8bit 位的数据。
在这里插入图片描述
信号 data_in_temp 是对输入数据信号的一个紧随的整合,后面校验模块会以此为参考,来判断仿真是否正常,模块设计是否正确。

(4.5) 选择在时钟上升沿延迟 2 个周期后停止输入数据,是为了被测试模块能够正常的采样到最后一个数据使能信号,并对数据进行正常的整合。
当数据量相对较少时,可以利用 Verilog 中的系统任务 $readmemh 来按行直接读取 16 进制数据。保持文件 data_in.dat 内数据和格式不变,则该激励部分可以描述为:

实例

reg [1:0] data_mem [39:0] ;
reg [7:0] data_in_temp ; //for self check
integer k1 ;
initial begin
din_en = 1’b0 ;
din = 'b0 ;
$readmemh(“…/tb/data_in.dat”, data_mem);
wait (rstn) ;
# CYCLE_200MHz ;

//read data from file
for(k1=0; k1<40; k1=k1+1)  begin
    @(negedge clk) ;
    din    = data_mem[k1] ;
    data_in_temp = {data_in_temp[5:0], din} ;
    din_en = 1'b1 ;
end

//stop data
@(posedge clk) ;
#2 din_en = 1'b0 ;

end

5)模块例化

这里利用 testbench 开始声明的信号变量,对被测试模块进行例化连接。

6)自校验

如果设计比较简单,完全可以通过输入、输出信号的波形来确定设计是否正确,此部分完全可以删除。如果数据很多,有时候拿肉眼观察并不能对设计的正确性进行一个有效判定。此时加入一个自校验模块,会大大增加仿真的效率。

实例中,我们会在数据输出使能 dout_en 有效时,对输出数据 dout 与参考数据 read_temp(激励部分产生)做一个对比,并将对比结果置于信号 err_cnt 中。最后就可以通过观察 err_cnt 信号是否为 0 来直观的对设计的正确性进行判断。

当然如实例中所示,我们也可以将数据写入到对应文件中,利用其他方式做对比。

7)结束仿真

如果我们不加入结束仿真部分,仿真就会无限制的运行下去,波形太长有时候并不方便分析。Verilog 中提供了系统任务 $finish 来停止仿真。

停止仿真之前,可以将自校验的结果,通过系统任务 $display 在终端进行显示。

文件读写选项
用于打开文件的系统任务 $fopen 格式如下:

fd = $fopen("<name_of_file>", "mode")

和 C 语言类似,打开方式的选项 “mode” 意义如下:
在这里插入图片描述

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

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

相关文章

对象存储,从单机到分布式的演进

关于数据存储的相关知识,请大家关注“数据存储张”,各大平台同名。 通过《什么是云存储?从对象存储说起》我们对对象存储的历史、概念和基本使用有了一个大概的认识。而且我们以Minio为例,通过单机部署的模式实际操作了一下对象存储的GUI,感受了一下对象存储的用法。 在上…

word中使用latex多行公式,矩阵公式

\eqarray{H& [h(x_1)^T,\cdots,h(x_N)^T]^T \\ & [\matrix{g(w_1 x_1b_1) & \cdots & g(w_L x_1b_L) \\ \vdots & \ddots & \vdots \\ g(w_1 x_Nb_1) & \cdots & g(w_L x_Nb_L)}]_{N \times L}}&的引起的那条竖线可以通过backspace或者del…

人工智能AI 全栈体系(七)

第一章 神经网络是如何实现的 神经网络不仅仅可以处理图像&#xff0c;同样也可以处理文本。由于处理图像讲起来比较形象&#xff0c;更容易理解&#xff0c;所以基本是以图像处理为例讲解的。 七、词向量 图像处理之所以讲起来比较形象&#xff0c;是因为图像的基本元素是像…

VB6.0开发文件管理小数据库-基于ACCESS

今天的客户也是小客户&#xff0c;需要对文件的一些操作记录在数据库里面&#xff0c;这里采用的数据库ACCCESS&#xff0c;用的是VB自带的可视化数据管理器创建的mdb数据库文件。实现了数据的增删改查。简单方便。基本可以用这套代码模板实现大部分的数据库功能了。想研发或学…

自定义ElementPlus主题颜色

构建工具采用Vite CSS预处理器采用Sass 一.准备定制化的样式文件 1.安装Sass npm i sass -D 2.创建好文件目录 3.书写样式 ElementPlus默认样式. //index.scss/* 只需要重写你需要的即可 */ forward element-plus/theme-chalk/src/common/var.scss with ($colors: (prim…

腾讯云秒杀活动是什么?如何参与?

腾讯云是国内知名的云计算服务提供商之一&#xff0c;为了吸引更多的用户&#xff0c;腾讯云会不定期地推出各种各样的优惠活动&#xff0c;其中最受大家欢迎的就是“腾讯云秒杀活动”。本文将为大家详细介绍腾讯云秒杀活动参与方式以及购买攻略。 一、腾讯云秒杀活动是什么&am…

【操作系统】处理机调度的基本概念和三个层次、进程调度的时机和方式、调度器、闲逛线程

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 操作系统 一、处理机调度基本概念二、处理机…

【论文阅读】UniDiffuser: Transformer+Diffusion 用于图、文互相推理

而多模态大模型将能够打通各种模态能力&#xff0c;实现任意模态之间转化&#xff0c;被认为是通用式生成模型的未来发展方向。 最近看到不少多模态大模型的工作&#xff0c;有医学、金融混合&#xff0c;还有CV&NLP。 今天介绍&#xff1a; One Transformer Fits All Di…

七、装饰者模式

这里写自定义目录标题 1、项目需求2、解决方案13、解决方案24、装饰者模式定义5、装饰者模式原理6、装饰者模式解决上述需求7、装饰者模式在jdk应用的源码分析 1、项目需求 2、解决方案1 3、解决方案2 可以控制类的数量&#xff0c;不至于造成很多的类在增加或者删除调料种类…

100万级连接,石墨文档WebSocket网关如何架构?

说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;很多小伙伴拿到一线互联网企业如阿里、网易、有赞、希音、百度、滴滴的面试资格。 最近&#xff0c;尼恩指导一个小伙伴简历&#xff0c;写了一个《高并发网关项目》&#xff0c;此项目帮这个小伙拿到 字节/阿里/…

自学WEB后端05-Node.js后端服务链接数据库redis

嘿&#xff0c;亲爱的小伙伴们&#xff01;&#x1f604; 今天我要给大家分享一个超级方便且高效的 NoSQL 类型数据库——Redis&#xff01;&#x1f4a1; 它可不是一般的关系型数据库哦&#xff0c;而是以键值对形式存储数据的内存数据库。&#x1f4da; 快跟着我一起来学习如…

GEO生信数据挖掘(一)数据集下载和初步观察

检索到目标数据集后&#xff0c;开始数据挖掘&#xff0c;本文以阿尔兹海默症数据集GSE1297为例 目录 GEOquery 简介 安装并加载GEOquery包 getGEO函数获取数据&#xff08;联网下载&#xff09; 更换下载数据源 对数据集进行初步观察处理 GEOquery 简介 GEOquery是一个…

基于springboot+vue的旅游系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

如何看待程序员这个职业?

程序员作为高薪职业&#xff0c;主要是指从事程序开发、程序维护的专业人员。一般将程序员分为程序设计人员和程序编码人员&#xff0c;但两者的界限并不非常清楚&#xff0c;特别是在中国。软件从业人员分为初级程序员、中级程序员、高级程序员&#xff08;现为软件设计师&…

基于体系结构-架构真题2022(四十一)

给定关系模式R&#xff08;U,F&#xff09;&#xff0c;其中U为属性集&#xff0c;F是U上的一组函数依赖&#xff0c;那么函数依赖的公理系统中分解规则是指&#xff08;&#xff09;为F所蕴含。 解析&#xff1a; 伪传递是x到y&#xff0c;wy到z&#xff0c;则xw到z 传递是z…

【性能测试】jmeter连接数据库jdbc

一、下载第三方工具包驱动数据库   1. 因为JMeter本身没有提供链接数据库的功能&#xff0c;所以我们需要借助第三方的工具包来实现。 &#xff08;有这个jar包之后&#xff0c;jmeter可以发起jdbc请求&#xff0c;没有这个jar包&#xff0c;也有jdbc取样器&#xff0c;但不能…

服务断路器_Resilience4j信号量隔离实现

POM引入依赖 <dependency><groupId>io.github.resilience4j</groupId><artifactId>resilience4j-bulkhead</artifactId><version>1.7.0</version> </dependency>信号量隔离修改YML文件 resilience4j:#信号量隔离bulkhead:ins…

【数据结构】八大排序算法-代码实现+复杂度分析+稳定性分析+总结

文章目录 关于稳定性插入排序直接插入排序希尔排序 选择排序直接选择排序堆排序 交换排序冒泡排序快速排序hoare版本挖坑法两路划分 快排致命点三路划分小区间优化 快排非递归 归并排序非递归版本 计数排序-鸽巢原理绝对映射相对映射 插入排序和选择排序的对比总结 关于稳定性 …

UNITY—2D游戏制作入门!

Unity作为当今最流行的游戏引擎之一&#xff0c;受到各大厂商的喜爱。 像是炉石传说&#xff0c;以及逃离塔克夫&#xff0c;都是由unity引擎开发制作。 作为初学者的我们&#xff0c;虽然无法直接做出完成度那么高的作品&#xff0c;但每一个伟大的目标&#xff0c;都有一个…

机柜PDU产品采购与安装指南——TOWE精选

机柜PDU指的是Power Distribution Unit&#xff0c;即电源分配单元。它是一种电子设备&#xff0c;通常用于为数据中心、服务器机房等设施中的计算机和其他设备提供电力&#xff0c;是各行业数据中心“标配”构成部分&#xff0c;以确保服务器等用电设备的安全和稳定运行。 数据…