第一季】CH06_FPGA设计Verilog基础(三)

一个完整的设计,除了好的功能描述代码,对于程序的仿真验证是必不可少的。学会如何去验证自己所写的程序,即如何调试自己的程序是一件非常重要的事情。而RTL逻辑设计中,学会根据硬件逻辑来写测试程序,即Testbench是尤其重要的。Verilog测试平台是一个例化的待测(MUT)模块,重要的是给它施加激励并观测其输出。逻辑模块与其对应的测试平台共同组成仿真模型,应用这个模型可以测试该模块能否符合自己的设计要求。

编写TESTBENCH的目的是为了对使用硬件描述语言设计的电路进行仿真验证,测试设计电路的功能、性能与设计的预期是否相符。通常,编写测试文件的过程如下:

• 产生模拟激励(波形);

• 将产生的激励加入到被测试模块中并观察其响应;

• 将输出响应与期望值相比较。

6.1 完成的Test bench文件结构

通常,一个完整的测试文件其结构为

module Test_bench();//通常无输入无输出

信号或变量声明定义

逻辑设计中输入对应reg型

逻辑设计中输出对应wire型

使用initial或always语句产生激励

例化待测试模块

监控和比较输出响应

endmodule

6.2 时钟激励设计

下面列举出一些常用的封装子程序,这些是常用的写法,在很多应用中都能用到。

/*----------------------------------------------------------------

时钟激励产生方法一:50%占空比时钟

----------------------------------------------------------------*/

parameter ClockPeriod=10;

initial

begin

clk_i=0;

forever

#(ClockPeriod/2) clk_i=~clk_i;

end

/*----------------------------------------------------------------

时钟激励产生方法二:50%占空比时钟

----------------------------------------------------------------*/

initial

begin

clk_i=0;

always #(ClockPeriod/2) clk_i=~clk_i;

end

/*----------------------------------------------------------------

时钟激励产生方法四:产生固定数量的时钟脉冲

----------------------------------------------------------------*/

initial

begin

clk_i=0;

repeat(6)

#(ClockPeriod/2) clk_i=~clk_i;

end

/*----------------------------------------------------------------

时钟激励产生方法五:产生非占空比为50%的时钟

----------------------------------------------------------------*/

initial

begin

clk_i=0;

forever

begin

#((ClockPeriod/2)-2) clk_i=0;

#((ClockPeriod/2)+2) clk_i=1;

end

end

6.3 复位信号设计

/*----------------------------------------------------------------

复位信号产生方法一:异步复位

----------------------------------------------------------------*/

initial

begin

rst_n_i=1;

#100;

rst_n_i=0;

#100;

rst_n_i=1;

end

/*----------------------------------------------------------------

复位信号产生方法二:同步复位

----------------------------------------------------------------*/

initial

begin

rst_n_i=1;

@(negedge clk_i)

rst_n_i=0;

#100; //固定时间复位

repeat(10) @(negedge clk_i); //固定周期数复位

@(negedge clk_i)

rst_n_i=1;

end

/*----------------------------------------------------------------

复位信号产生方法三:复位任务封装

----------------------------------------------------------------*/

task reset;

input [31:0] reset_time; //复位时间可调,输入复位时间

RST_ING=0; //复位方式可调,低电平或高电平

begin

rst_n=RST_ING; //复位中

#reset_time; //复位时间

rst_n_i=~RST_ING; //撤销复位,复位结束

end

endtask

6.4 特殊信号设计

/*----------------------------------------------------------------

特殊激励信号产生描述一:输入信号任务封装

----------------------------------------------------------------*/

task i_data;

input [7:0] dut_data;

begin

@(posedge data_en); send_data=0;

@(posedge data_en); send_data=dut_data[0];

@(posedge data_en); send_data=dut_data[1];

@(posedge data_en); send_data=dut_data[2];

@(posedge data_en); send_data=dut_data[3];

@(posedge data_en); send_data=dut_data[4];

@(posedge data_en); send_data=dut_data[5];

@(posedge data_en); send_data=dut_data[6];

@(posedge data_en); send_data=dut_data[7];

@(posedge data_en); send_data=1;

#100;

end

endtask

//调用方法:i_data(8'hXX);

/*----------------------------------------------------------------

特殊激励信号产生描述二:多输入信号任务封装

----------------------------------------------------------------*/

task more_input;

input [7:0] a;

input [7:0] b;

input [31:0] times;

output [8:0] c;

begin

repeat(times) //等待times个时钟上升沿

@(posedge clk_i)

c=a+b; //时钟上升沿a,b相加

end

endtask

//调用方法:more_input(x,y,t,z);  //按声明顺序

/*----------------------------------------------------------------

双向信号描述一:inout在testbench中定义为wire型变量

----------------------------------------------------------------*/

//为双向端口设置中间变量inout_reg作为inout的输出寄存,其中inout变

//量定义为wire型,使用输出使能控制传输方向

//inout bir_port;

wire bir_port;

reg bir_port_reg;

reg bi_port_oe;

assign bi_port=bi_port_oe ? bir_port_reg : 1'bz;

/*----------------------------------------------------------------

双向信号描述二:强制force

----------------------------------------------------------------*/

//当双向端口作为输出口时,不需要对其进行初始化,而只需开通三态门

//当双向端口作为输入时,只需要对其初始化并关闭三态门,初始化赋值需

//使用wire型数据,通过force命令来对双向端口进行输入赋值

//assign dinout=(!en) din :16'hz; 完成双向赋值

initial

begin

force dinout=20;

#200

force dinout=dinout-1;

end

/*----------------------------------------------------------------

特殊激励信号产生描述三:输入信号产生,一次SRAM写信号产生

----------------------------------------------------------------*/

initial

begin

cs_n=1; //片选无效

wr_n=1; //写使能无效

rd_n=1; //读使能无效

addr=8'hxx; //地址无效

data=8'hzz; //数据无效

#100;

cs_n=0; //片选有效

wr_n=0; //写使能有效

addr=8'hF1; //写入地址

data=8'h2C; //写入数据

#100;

cs_n=1;

wr_n=1;

#10;

addr=8'hxx;

data=8'hzz;

end

/*----------------------------------------------------------------

Testbench中@与wait

----------------------------------------------------------------*/

//@使用沿触发

//wait语句都是使用电平触发

initial

begin

start=1'b1;

wait(en=1'b1);

#10;

start=1'b0;

end

6.5 仿真控制语句及系统任务描述

/*----------------------------------------------------------------

仿真控制语句及系统任务描述

----------------------------------------------------------------*/

$stop     //停止运行仿真,modelsim中可继续仿真

$stop(n) //带参数系统任务,根据参数0,1或2不同,输出仿真信息

$finish   //结束运行仿真,不可继续仿真

$finish(n)  //带参数系统任务,根据参数0,1或2不同,输出仿真信息

//0:不输出任何信息

//1:输出当前仿真时刻和位置

//2:输出当前仿真时刻、位置和仿真过程中用到的memory以及CPU时间的统计

$random //产生随机数

$random % n //产生范围-n到n之间的随机数

{$random} % n //产生范围0到n之间的随机数

/*----------------------------------------------------------------

仿真终端显示描述

----------------------------------------------------------------*/

$monitor //仿真打印输出,大印出仿真过程中的变量,使其终端显示

/*

$monitor($time,,,"clk=%d reset=%d out=%d",clk,reset,out);

*/

$display //终端打印字符串,显示仿真结果等

/*

$display(” Simulation start ! ");

$display(” At time %t,input is %b%b%b,output is %b",$time,a,b,en,z);

*/

$time //返回64位整型时间

$stime //返回32位整型时间

$realtime //实行实型模拟时间

/*----------------------------------------------------------------

文本输入方式:$readmemb/$readmemh

----------------------------------------------------------------*/

//激励具有复杂的数据结构

//verilog提供了读入文本的系统函数

$readmemb/$readmemh("<数据文件名>",<存储器名>);

$readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>);

$readmemb/$readmemh("<数据文件名>",<存储器名>,<起始地址>,<结束地址>);

$readmemb:/*读取二进制数据,读取文件内容只能包含:空白位置,注释行,二进制数

数据中不能包含位宽说明和格式说明,每个数字必须是二进制数字。*/

$readmemh:/*读取十六进制数据,读取文件内容只能包含:空白位置,注释行,十六进制数

数据中不能包含位宽说明和格式说明,每个数字必须是十六进制数字。*/

/*当地址出现在数据文件中,格式为@hh...h,地址与数字之间不允许空白位置,

可出现多个地址*/

module

reg [7:0] memory[0:3];//声明8个8位存储单元

integer i;

initial

begin

$readmemh("mem.dat",memory);//读取系统文件到存储器中的给定地址

//显示此时存储器内容

for(i=0;i<4;i=i+1)

$display("Memory[%d]=%h",i,memory[i]);

end

endmodule

/*mem.dat文件内容

@001

AB CD

@003

A1

*/

//仿真输出为

Memory[0] = xx;

Memory[1] = AB;

Memory[2] = CD;

Memory[3] = A1;

6.6加法器的仿真测试文件编写

上面只例举了常用的testbench写法,在工程应用中基本能够满足我们需求,至于其他更为复杂的testbench写法,大家可参考其他书籍或资料。

这里提出以下几点建议供大家参考:

• 封装有用且常用的testbench,testbench中可以使用task或function对代码进行封装,下次利用时灵活调用即可;

• 如果待测试文件中存在双向信号(inout)需要注意,需要一个reg变量来表示输入,一个wire变量表示输出;

• 单个initial语句不要太复杂,可分开写成多个initial语句,便于阅读和修改;

• Testbench说到底是依赖PC软件平台,必须与自身设计的硬件功能想搭配。

下面具体看一段程序:

module add(a,b,c,d,e);// 模块接口

input [5:0] a; // 输入信号a

input [5:0] b; // 输入信号b

input [5:0] c; // 输入信号a

input [5:0] d; // 输入信号b

output[7:0] e; // 求和输出信号

wire [6:0]outa1,outa2; // 定义输出网线型

assign e = outa2+outa1; // 把两部分输出结果合并

/*

通常,我们模块的调用写法如下:

被调用的模块名字- 自定义的名字- 括号内信号

这里比如括号内的信号,.ina(ina1)

这种写法最常用,信号的顺序可以调换

另外还有一种写法没可以直接这样写

adder u1 (ina1,inb1,outa1);

这种写法必须确保信号的顺序一致,这种写法几乎没有人采用

*/

adder u1 (.ina(a),.inb(b),.outa(outa1)); // 调用adder 模块,自定义名字为u1

adder u2 (.ina(c),.inb(d),.outa(outa2)); // 调用adder 模块,自定义名字为u2

endmodule

//adder 子模块

module adder(ina,inb,outa );// 模块接口

input [5:0] ina; // ina-输入信号

input [5:0] inb; // inb-输入信号

output [6:0] outa; // outa-输入信号

assign outa = ina + inb; // 求和

endmodule // 模块结束

仿真文件:

`timescale 1ns / 1ps

module add_tb();

reg [5:0] a;

reg [5:0] b;

reg [5:0] c;

reg [5:0] d;

wire[7:0] e;

reg [5:0] i; //中间变量

// 调用被仿真模块模块

add uut (.a(a), .b(b),.c(c),.d(d),.e(e));

initial begin // initial 是仿真用的初始化关键词

a=0;b=0;c=0;d=0; // 必须初始化输入信号

for(i=1;i<31;i=i+1) begin

#10 ;

a = i;

b = i;

c = i;

d = i;

end // 给是输入信号a 赋值

end

initial begin

$monitor($time,,,"%d + %d + %d + %d ={%d}",a,b,c,d,e); // 信号打印输出

#500 $finish;

end

endmodule

【第一季】CH06_FPGA设计Verilog基础(三)的更多相关文章

  1. 【第一季】CH05_FPGA设计Verilog基础(二)Enter a post title

    [第一季]CH05_FPGA设计Verilog基础(二) 5.1状态机设计 状态机是许多数字系统的核心部件,是一类重要的时序逻辑电路.通常包括三个部分:一是下一个状态的逻辑电路,二是存储状态机当前状态 ...

  2. 【第一季】CH04_FPGA设计Verilog基础(一)Enter a post title

    [第一季]CH04_FPGA设计Verilog基础(一) 4.1 Verilog HDL 代码规范 u 项目构架设计 项目的构架用于团队的沟通,以及项目设计的全局把控 u 接口时序设计规范 模块和模块 ...

  3. 《java入门第一季》二维数组三个案例详解

    案例一:遍历二维数组 /* 需求:二维数组遍历 外循环控制的是二维数组的长度,其实就是一维数组的个数行数. 内循环控制的是一维数组的长度,每一行,一维数组元素分别的个数. */ class Array ...

  4. 灵书妙探第一季/全集Castle迅雷下载

    第一季 Castle Season 1 (2009)看点:ABC电视台2009年开播的一部罪案剧,讲述一位罪案小说家Richard Castle帮助纽约警察局凶杀组破案的故事.凶案组女警探Kate B ...

  5. 无耻之徒(美版)第一季/全集Shameless US迅雷下载

    第一季 Shameless Season 1 (2011)看点:本以为美版<无耻之徒>(Shameless)是小众剧(诸多儿童不宜),但是试播集98.2万的收视人次竟然创下了Showtim ...

  6. Hadoop 2.x从零基础到挑战百万年薪第一季

    鉴于目前大数据Hadoop 2.x被企业广泛使用,在实际的企业项目中需要更加深入的灵活运用,并且Hadoop 2.x是大数据平台处理 的框架的基石,尤其在海量数据的存储HDFS.分布式资源管理和任务调 ...

  7. 【第一季】CH09_FPGA多路分频器设计

    [第一季]CH09_FPGA多路分频器设计 在第七节的学习中,笔者带大家通过一个入门必学的流水灯实验实现,快速掌握了VIVADO基于FPGA开发板的基本流程.考虑到很多初学者并没有掌握好Vivado ...

  8. QQ聊天界面的布局和设计(IOS篇)-第一季

    我写的源文件整个工程会再第二季中发上来~,存在百度网盘, 感兴趣的童鞋, 可以关注我的博客更新,到时自己去下载~.喵~~~ QQChat Layout - 第一季 一.准备工作 1.将假数据messa ...

  9. Asp.Net MVC3 简单入门第一季(三)详解Controller之Filter

    前言 前面两篇写的比较简单,刚开始写这个系列的时候我面向的对象是刚开始接触Asp.Net MVC的朋友,所以写的尽量简单.所以写的没多少技术含量.把这些技术总结出来,然后一简单的方式让更多的人很好的接 ...

随机推荐

  1. Editor

    E. Editor 我们把"("用1表示,")"用-1表示,其余字母用0表示,这样形成的一个数组,我们求出它的前缀和sum[],只有当\(sum[n]==0\) ...

  2. League of Leesins

    C - League of Leesins 首先找到每一串数字的头和尾两个数字,这两个数字有一个特点,就是它们在输入数据的时候都只会出现一次.我们在输出的时候用头和尾做第一数都可以. 然后第二个数只会 ...

  3. vue实现购物清单列表添加删除

    vue实现购物清单列表添加删除 一.总结 一句话总结: 基础的v-model操作,以及数组的添加(push)删除(splice)操作 1.checkbox可以绑定数组,也可以直接绑定值? 绑定数组就是 ...

  4. Spring事务知识点

    事务的传播属性 PROPAGATION_REQUIRED 如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中.// 最常用,@Transactional注解默认 PROPAGA ...

  5. <JavaScript> 关于闭包和this对象

    1.this指向windows是如何得出的 var name = "The Window"; var object = { name : "My Object" ...

  6. react对字符串转义成html并渲染

    <div dangerouslySetInnerHTML={{__html: "字符串内容"}} />  

  7. c++ Container print

    template<typename Container>void PrintContents(const Container& con) { Container::const_it ...

  8. iOS自适应行高方法及问题

    最近一周被项目的动态高度虐的很惨,感觉浪费了很多时间,但是值得高兴的是对动态高度的使用掌握了好多方法,并且知道了方法之间的区别和优缺点. 1.最常用的: UITableView+FDTemplateL ...

  9. swift 第十四课 可视化view: @IBDesignable 、@IBInspectable

    以前应objctiew-c 写项目的时候,就知道有这两个关键字,现在用swift了.用法稍作改变,基本用法还是一致的 虽然使用这个之后,有时候会报错的非常的莫名其妙----(其实还是自己技术不够牛…… ...

  10. The input file should be UTF8 without a byte-order-mark(BOM)

    byte-order-mark = (BOM) 在unicode诸编码中,字节顺序标记-BOM被用于标记编码高低位的顺序. .BOM是一个特殊的unicode字符.早期标准定义其为“零长度.非断行的空 ...