基于FPGA的信号发生器 的TESTBENCH(测试文本)程序怎么编

基于FPGA的信号发生器 的TESTBENCH(测试文本)程序怎么编,第1张

1.激励的设置

相应于被测试模块的输入激励设置为reg型,输出相应设置为wire类型,双向端口inout在测试中需要进行处理。

方法1:为双向端口设置中间变量inout_reg作为该inout的输出寄存,inout口在testbench中要定义为wire型变量,然后用输出使能控制传输方向。

eg:

inout [0:0] bi_dir_port;

wire [0:0] bi_dir_port;

reg [0:0] bi_dir_port_reg;

reg bi_dir_port_oe;

assign bi_dir_port=bi_dir_port_oebi_dir_port_reg:1'bz;

用bi_dir_port_oe控制端口数据方向,并利用中间变量寄存器改变其值。等于两个模块之间用inout双向口互连。往端口写(就是往模块里面输入)

方法2:使用force和release语句,这种方法不能准确反映双向端口的信号变化,但这种方法可以反映块内信号的变化。具体如示:

module test();

wire data_inout;

reg data_reg;

reg link;

#xx; //延时

force data_inout=1'bx; //强制作为输入端口

#xx;

release data_inout; //释放输入端口

endmodule

从文本文件中读取和写入向量

1)读取文本文件:用 $readmemb系统任务从文本文件中读取二进制向量(可以包含输入激励和输出期望值)。$readmemh 用于读取十六进制文件。例如:

reg [7:0] mem[1:256] // a 8-bit, 256-word 定义存储器mem

initial $readmemh ( "memdata", mem ) // 将dat文件读入寄存器mem中

initial $readmemh ( "memdata", mem, 128, 1 ) // 参数为寄存器加载数据的地址始终

2)输出文本文件:打开输出文件用$fopen 例如:

integer out_file; // out_file 是一个文件描述,需要定义为 integer类型

out_file = $fopen ( " cpudata " ); // cpudata 是需要打开的文件,也就是最终的输出文本

设计中的信号值可以通过$fmonitor, $fdisplay,

2 Verilog和Ncverilog命令使用库文件或库目录

ex) ncverilog -f runf -v lib/libv -y lib2 +libext+v //一般编译文件在runf中, 库文件在libv中,lib2目录中的v文件系统自动搜索

使用库文件或库目录,只编译需要的模块而不必全部编译

3.Verilog Testbench信号记录的系统任务:

1) SHM数据库可以记录在设计仿真过程中信号的变化 它只在probes有效的时间内记录你set probe on的信号的变化

ex) $shm_open("wavesshm"); //打开波形数据库

$shm_probe(top, "AS"); // set probe on "top",

第二个参数: A -- signals of the specific scrope

S -- Ports of the specified scope and below, excluding library cells

C -- Ports of the specified scope and below, including library cells

AS -- Signals of the specified scope and below, excluding library cells

AC -- Signals of the specified scope and below, including library cells

还有一个 M ,表示当前scope的memories, 可以跟上面的结合使用, "AM" "AMS" "AMC"

什么都不加表示当前scope的ports;

$shm_close //关闭数据库

2) VCD数据库也可以记录在设计仿真过程中信号的变化 它只记录你选择的信号的变化

ex) $dumpfile("filename"); //打开数据库

$dumpvars(1, topu1); //scope = topu1, depth = 1

第一个参数表示深度, 为0时记录所有深度; 第二个参数表示scope,省略时表当前的scope

$dumpvars; //depth = all scope = all

$dumpvars(0); //depth = all scope = current

$dumpvars(1, topu1); //depth = 1 scope = topu1

$dumpoff //暂停记录数据改变,信号变化不写入库文件中

$dumpon //重新恢复记录

3) Debussy fsdb数据库也可以记录信号的变化,它的优势是可以跟debussy结合,方便调试

如果要在ncverilog仿真时,记录信号, 首先要设置debussy:

a setenv LD_LIBRARY_PATH :$LD_LIBRARY_PATH

(path for debpliso file (/share/PLI/nc_xl//nc_loadpli1))

b while invoking ncverilog use the +ncloadpli1 option

ncverilog -f runf +debug +ncloadpli1=debpli:deb_PLIPtr

fsdb数据库文件的记录方法,是使用$fsdbDumpfile和$fsdbDumpvars系统函数,使用方法参见VCD

注意: 在用ncverilog的时候,为了正确地记录波形,要使用参数: "+access+rw", 否则没有读写权限

在记录信号或者波形时需要指出被记录信号的路径,如:tbmoduleu1clk

………………………………………………………………………………………………………

关于信号记录的系统任务的说明:

在testbench中使用信号记录的系统任务,就可以将自己需要的部分的结果以及波形文件记录下来(可采用sigalscan工具查看),适用于对较大的系统进行仿真,速度快,优于全局仿真。使用简单,在testbench中添加:initial begin

$shm_open("wavesshm");

$shm_probe("要记录信号的路径“,”AS“);

#10000

$shm_close; 即可。

4 ncverilog编译的顺序: ncverilog file1 file2

有时候这些文件存在依存关系,如在file2中要用到在file1中定义的变量,这时候就要注意其编译的顺序是

从后到前,就先编译file2然后才是file2

5 信号的强制赋值force

首先, force语句只能在过程语句中出现,即要在initial 或者 always 中间 去除force 用 release 语句

initial begin force sig1 = 1'b1; ; release sig1; end

force可以对wire赋值,这时整个net都被赋值; 也可以对reg赋值

6.加载测试向量时,避免在时钟的上下沿变化

为了模拟真实器件的行为,加载测试向量时,避免在时钟的上下沿变化,而是在时钟的上升沿延时一个时间单位后,加载的测试向量发生变化。如:

assign #5 c=a^b

……

@(posedge clk) #(01`cycle) A=1;

//testbench的波形输出

module top;

initial

begin

$dumpfile("/topvcd"); //存储波形的文件名和路径,一般是vcd格式

$dumpvars(1,top); //存储top这一层的所有信号数据

$dumpvars(2,topu1); //存储topu1之下两层的所有数据信号(包含topu1这一层)

$dumpvars(3,topu2); //存储topu2之下三层的所有数据信号(包含topu2这一层)

$dumpvars(0,topu3); //存储topu3之下所有层的所有数据信号

end

endmodule

//产生随机数,seed是种子

$random(seed);

ex: din <= $random(20);

//仿真时间,为unsigned型的64位数据

$time

ex:

time condition_happen_time;

condition_happen_time = $time;

$monitor($time,"data output = %d", dout);

//参数

parameter para1 = 10,

para2 = 20,

para3 = 30;

//显示任务

$display();

//监视任务

$monitor();

//延迟模型

specify

//describ pin-to-pin delay

endspecify

ex:

module nand_or(Y,A,B,C);

input A,B,C;

output Y;

AND2 #02 (N,A,B);

OR2 #01 (Y,C,N);

specify

(A->Y) = 02;

(B->Y) = 03;

(C->Y) = 01;

endspecify

endmodule

//时间刻度

`timescale 单位时间/时间精确度

//文件I/O

1打开文件

integer file_id;

file_id = fopen("file_path/file_name");

2写入文件

//$fmonitor只要有变化就一直记录

$fmonitor(file_id, "%format_char", parameter);

eg:$fmonitor(file_id, "%m: %t in1=%d o1=%h", $time, in1, o1);

//$fwrite需要触发条件才记录

$fwrite(file_id, "%format_char", parameter);

//$fdisplay需要触发条件才记录

$fdisplay(file_id, "%format_char", parameter);

$fstrobe();

3读取文件

integer file_id;

file_id = $fread("file_path/file_name", "r");

4关闭文件

$fclose(fjile_id);

5由文件设定存储器初值

$readmemh("file_name", memory_name"); //初始化数据为十六进制

$readmemb("file_name", memory_name"); //初始化数据为二进制

//仿真控制

$finish(parameter); //parameter = 0,1,2

$stop(parameter);

//读入SDF文件

$sdf_annotate("sdf_file_name", module_instance, "scale_factors");

//module_instance: sdf文件所对应的instance名

//scale_factors:针对timming delay中的最小延时min,典型延迟typ,最大延时max调整延迟参数

//generate语句,在Verilog-2001中定义用于表达重复性动作

//必须事先声明genvar类型变量作为generate循环的指标

eg:

genvar i;

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

begin

assign = din[i] = i % 2;

end

endgenerate

//资源共享

always @(A or B or C or D)

sum = sel (A+B):(C+D);

//上面例子使用两个加法器和一个MUX,面积大

//下面例子使用一个加法器和两个MUX,面积小

always @(A or B or C or D)

begin

tmp1 = sel A:C;

tmp2 = sel B:D;

end

always @(tmp1 or tmp2)

sum = tmp1 + tmp2;

模板:

module testbench; //定义一个没有输入输出的module

reg …… //将DUT的输入定义为reg类型

……

wire…… //将DUT的输出定义为wire类型

……

//在这里例化DUT

initial

begin

…… //在这里添加激励(可以有多个这样的结构)

end

always…… //通常在这里定义时钟信号

initial

//在这里添加比较语句(可选)

end

initial

//在这里添加输出语句(在屏幕上显示仿真结果)

end

endmodule

一下介绍一些书写Testbench的技巧:

1如果激励中有一些重复的项目,可以考虑将这些语句编写成一个task,这样会给书写和仿真带来很大方便。例如,一个存储器的testbench的激励可以包含write,read等task。

2如果DUT中包含双向信号(inout),在编写testbench时要注意。需要一个reg变量来表示其输入,还需要一个wire变量表示其输出

首先纠正下千年同志的回答:

你的第一点,请看清楚楼主两个always块第一个是grade,第二个是grade1,grade2分别表示个位十位;

第二点:脉冲信号不能做判断条件这句,我拍这胸脯告诉你,绝对可以。。只有你用时序逻辑,在时钟沿检测脉冲信号就可以,只有保证脉冲宽度大于一个时钟周期就绝对能检测到。

第三点:楼主确实有这么点不完善,不过有些时候可以预先确知某些信号同时发生也可以不考虑的。

最后的你建议,先学C再改verilog,个人意见非常不赞同。。很多软件工程师特别喜欢把写软件哦思维带到硬件描述语言中来。。调用个函数啦。。写个for,写个while什么,最后还问为啥综合报错。。。除非你是专门做测试的用systemC或者systemverilog。that's all。

有点偏题了,说楼主的问题:

你的错误:第一点,最明显的地方是 第二个always块里面,你同时用了阻塞赋值和非阻塞赋值语句,这是典型错误。时序逻辑的always块里面都用<=,组合逻辑的always块都用=。

第二,第二个语句中X,Y两个变量的赋值的表达式根本没办法正确综合,一般%这个运算符号只能出现在仿真里(也很少用)除法/只能对2的整数幂进行相除,根本不能除以10,因为这里除法的本质就是除以2^n就等同于讲寄存器的值左移n位;请问这里10等于2的几次幂啊?!

第三个错误,第一个always块,作为组合逻辑,要把所有的敏感变量加进敏感变量列表里,而且组合逻辑你就不用边缘检测posedge 了,直接always@(add or sub or grade)就可以了。

第四个错误。。你的这个组合逻辑的always块,else 语句写在else if 语句前面,这是一种很诡异的写法,能综合个什么东西出来完全想不出来。

所有,我给你的建议是,你这段程序直接推翻重新来写,全部使用标准的时序逻辑的写法,

就是你第二段“always@(posedge clk or negedge RSTn)beign……end”这种写法,它可以保证你的所有电路行为都是发生在时钟上升沿或异步复位下降沿。

其次,每个信号第一if的分支语句一定是要if (!RSTn),即RSTn为低电平时整个加法器复位。这样做的好处在于:由于所有FPGA器件自动上电的瞬间,内部各个信号的初始态都是低电平,可以保证系统有一个自动复位的过程,所有按照这样写的寄存器都会有一个初始态。而避免了不定态X。

最后,建议你四个bit位宽的个位寄存器做成一个模为10的计数器(可以加可以减,具体靠判断sub,add哪一个信号为高)。。每次计数器满时就将十位的那个寄存器加一,而当个位寄存器为零且又检测到sub减信号时,则把个位寄存器置为4‘b1001(9);将十位寄存器也减一;这样就可以实现总累加值从0~99的加减法器了 。

以上就是关于基于FPGA的信号发生器 的TESTBENCH(测试文本)程序怎么编全部的内容,包括:基于FPGA的信号发生器 的TESTBENCH(测试文本)程序怎么编、求指导一个verilog编写的FPGA加减法程序、等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zz/10105888.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-05
下一篇 2023-05-05

发表评论

登录后才能评论

评论列表(0条)

保存