testbench常用语句

news/2024/12/22 14:24:07/

与可综合Verilog代码所不同的是,testbench Verilog是在计算机主机上的仿真器中执行的。testbench Verilog的许多构造与C语言相似,我们可在代码中包括复杂的语言结构和顺序语句的算法。

1 always块和initial块

Verilog有两种进程语句:always块和initial块。always块内的进程语句,可用来模拟抽象的电路。

出于模拟的目的,always块可以包括:用以指定与不同结构之间的传播延迟等同的时序结构;或等待指定事件的时序结构。敏感列表有时可忽略。比方说,我们用下面的代码片段来模拟时钟信号,该信号每20个时间单位在0~1间变换一次,且永远执行下去。

always

begin

clk=1;

#20;

clk=0;

#20);

end

initial块内也有进程语句,但是仅在仿真之初被执行。其简单语法如下:

initial

begin

进程语句;

end

initail块常用于设置变量的初始值。注意,initial块不可被综合。

2 进程语句

进程语句应用于initial块、always块、function和task之中。最常用的进程语句为:

· 阻塞赋值

· 非阻塞赋值

· if表达式

· case表达式

· 循环表达式

我们讨论过阻塞和阻塞赋值,if和case语句。

Verilog支持的循环结构有:for、while、repeat和forever。for循环的简单语法为:

for([initial_assignment]; [end_condition]; [step_assignment])

begin

[procedural_statements;]

end

举个例子,我们可以使用下面的语句来清除16位寄存器文件的内容:

integer i;

. . .

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

reg_file[i]=0;

注意当循环体内只有一条语句的话,begin和end限定词可以略去。

while循环的简单语法如下:

while([end_condition])

begin

[procedural_statements;]

end

循环体内的语句连续重复执行,直到达到指定的终止条件[end_condition]为止。比方说上面的清寄存器文件的操作可以使用while循环来描述:

integer i;

. . .

while(i<16)

begin

reg_file[i]=0;

i=i+1;

en

repeat循环的简单语法如下:

repeat([number])

begin

[procedual_statements;]

end

循环体内的语句被重复执行指定数次,该数可通过[number]来指定。比方说,我们可以将上面的操作替换为repeat循环:

integer i;

. . .

repeat(16)

begin

[procedural_statements;]

end

forever循环,正如其名,重复执行其主体直至仿真结束位置。循环体内常包括一定的时序控制结构,以致周期性推迟执行。比方说,我们换一种方式来描述时钟信号,该信号每10个时间单位翻转一次,且永远运行下去。

initial

begin

clk=1’b0;

forever

#10 clk=~clk;

end

3 时序控制

在testbench中,必须指定不同信号有效和无效或等待某事件或条件的时间。有三种时序控制结构:

· 时延控制:#[delay_time]

· 事件控制:@([event], [event], …]

· 等待语句:wait([boolean_expression])

此外还有一个编译器指令,`timescale,也与时序规范有关。

4 时延控制

时延控制使用#符号来指示,其后为延迟的时间单位数值。

如果时延控制放置在左手边,那么整条语句的执行都会被延迟。比方说,

. . .

#10 a=1’b0;

#5 y=a|b;

. . .

假设当前时间为t,上面的语句表示,a于t+10时刻得到0值;又过了5个时间单位后(即于t+15时刻)a|b表达式被计算,其结果被赋给y。

如果实验控制被放置在右手边,那么表达式将会被立即运算,但是延迟后再赋给左手边。如:

. . .

#10 a=1’b0;

y=#5 a|b;

. . .

a于t+10时刻得到0值;a|b表达式被立即运算(即在t+10时刻),但其结果却在t+15时刻才赋给y。

一般情况下,我们使用时延控制生成激励的方式来替代传播延迟的模拟。下面的格式使得代码显得更加直观。

. . .

a=1’b0;// a gets 0

#10; // the 0 value lasts 10 time units

a-1’b1;// a changes to 10

#5; // the 1 value lasts 5 time units

a=1’b0;// a changes to 0

#20 // the 0 value lasts 20 time units

. . .

5 事件控制

事件控制使用@符号来指示,其后为敏感列表,用于指定所需事件。其使用与always块内的事件类似。事件即敏感列表中的信号改变其值(信号跳变)的时刻。可加入posedge和negedge关键字以指定所需的跳变边沿(上升沿和下降沿)。在testbench中,直到指定事件发生,语句才可跳过延迟,继续执行。事件控制的一个常见应用为:使用时钟信号来同步激励的生成。比方说,下面的代码片段中,en信号被激活持续一个时钟周期。

localparam delta=1;

. . .

@(posedgeclk);// wait for the rising edge of clk

#delta; // wait for delta to avoid hold=time violation

en=1’b1; // assert en to 1

@(posedgeclk);// wait for the next rising edge of clk

#delta; // wait for delta to avoid hold-time violation

en=1’b0; // assert en to 0

换一种方式,我们可以在时钟信号的下降沿断言或解除断言en。

. . .

@(negedgeclk)// wait for the falling edge of clk

en=1’b1; // assert en to 1

@(negedgeclk)// wait for the next falling edge of clk

en=1’b0;

. . .

6 等待语句

wait语句用以等待指定条件。其简单语法如下:

wait[boolean_expression]

直到[boolean_expression]被计算为真,后面语句才可跳过延迟,继续执行。比方说,我们可以这样写代码:

wait(stateREAD && mem_ready1’b1) [statement_to_get_data];

我们也可以使用wait语句来延迟执行。比方说,我们可以等计数器数到15才激活某信号:

. . .

wait(counter==4’b1111);// wait until counter is 15

. . . // continue

wait语句有时很想事件控制。后者是等待某信号的跳变边沿,而前者是等待指定条件,有时可理解为电平敏感。

7 timescale指令

编译器指令用以控制编译和预处理verilog代码,他们通过重音符号()来指明。重音符号常位于键盘的左上角。与时间有关的指令是timescale指令

`timescale [time_unit] / [time_precision]

time_unit指定计时和延时的测量单位,time_precision则是指定仿真器的精度。

比方说,指令

`timescale10ns/1ns

则说明仿真单位为10ns,精度为1ns。当指定如下代码中的延时,

#5 y = a & b;

表明实际上的延时为50ns(即5*10ns)。

也可以指定小数形式的单位延时,比方说

#5.12345 y = a & b;

则说明实际延时为51.2345ns。因为精度是1ns, 所以在仿真中就取整为51ns。精度越少,仿真的准确性越高,但是会减慢仿真的速度。

time_unit和time_precision的数字部分可以为1、10和100,时间单元可以是s(秒)、ms(毫秒)、us(微秒)、ns(纳秒)和ps(皮秒)。

8 系统控制函数和任务

Verilog有一组预定义的系统函数,以$打头,执行与系统相关的操作,如仿真控制、文件读取等。下面我们讲一下一些常用的函数和任务。

数据类型转换函数

unsigned和unsigned和unsignedsigned函数执行介于无符号数和有符号数类型之间的转换。

仿真时间函数

仿真时间函数返回当前的仿真时间,如time、time、timestime和$realtime函数分别以64位整数、32位整数和实数的形式返回时间。

仿真控制任务

有两种仿真控制函数:finish和finish和finishstop。其中,finish任务用于终止仿真并跳出仿真器;finish任务用于终止仿真并跳出仿真器;finish任务用于终止仿真并跳出仿真器;stop任务则用于中止仿真。在Modelsim中,stop任务则是返回到交互模式。在开发流程中,我们有时会停在Modelsim环境中,来进一步编辑或测试波形,因此代码中使用的是stop任务则是返回到交互模式。在开发流程中,我们有时会停在Modelsim环境中,来进一步编辑或测试波形,因此代码中使用的是stop任务则是返回到交互模式。在开发流程中,我们有时会停在Modelsim环境中,来进一步编辑或测试波形,因此代码中使用的是stop。

显示任务

在Modelsim中,仿真的结果可以以波形的形式显示,也可以以文本的形式显示。四种主要的显示任务有display、display、displaywrite、strobe和strobe和strobemonitor,它们语法类似。在Modelsim中,文本是在控制面板显示的。

$display的语法与C语言中的打印函数类似。其简单语法为:

$display([format_string], [argument], [argument], …);

如:

$display(“at %d; signal x = %b”, $time, x);

其结果的形式如下:

at 5100; signal x = 00110001

最常用的转移符号有%d、%b、%o、%h、%c、%s和%g,对应分别为十进制、二进制、八进制、十六进制、字符、字符串和实数。

write任务几乎和write任务几乎和write任务几乎和diplay等同,除了其执行之后并不跳到下一行显示。而是一直显示在当前位置。显示下一行字符\n,必须手动添加,以创建一个行中断。

Verilog可结合time step的概念来塑造仿真延时。每个time step中可以发生很多活动。strobe与strobe与strobedisplay任务类似。代替立即执行的是,$strobe任务是在当前仿真的time step的结尾执行的。它可以规避由于竞争冒险造成的不匹配的数据显示。

monitor任务是非常通用的命令。鉴于monitor任务是非常通用的命令。鉴于monitor任务是非常通用的命令。鉴于displat、write、write、writestrobe任务是在一旦它们被执行的情况下才显示文本,monitor任务则是当其参数发生变化时即显示文本。monitor任务则是当其参数发生变化时即显示文本。monitor任务则是当其参数发生变化时即显示文本。monitor任务提供了简单的富有弹性的方式来跟踪仿真。比方说,我们可以在testbench中添加如下的代码:

initial

begin

$display(“time test_in0 test_in1 test_out”);

$monitot(“%d %b %b %b”,

        $time, test_in0, test_in1, test_out);

end

Modelsim的控制面板中显示的文本仿真结果如下(示例):

time test_in0 test_in1 test_out

0 00 00 1

200 01 00 0

400 01 11 0

. . .

文件I/O系统函数和任务

Veirlog提供一组用于访问外部数据文件的函数和任务。文件可以通过fopen和fopen和fopenfclose函数来打开和关闭。$fopen的语法为:

[mcd_names] = $fopen(“[file_name]”);

$fopen函数返回一个与文件相关的32位的多通道描述子。这个描述子我们可以认为是一个32位的标志,它代表一个文件(亦即一个通道)。最低位LSB保留,用以只是标准输出(console)。当使用函数调用的文件被成功打开,则返回的描述子的值得某位会被置一。例如,0…0010表示打开第一个文件,0…0100表示打开第二个文件,依次类推。若函数的返回值为0,则表示文件未能成功打开。

一旦某个文件被打开,我们就可以向其内写入数据。可用的四种显示系统任务为:fdisplay,fdisplay,fdisplayfwrite,fstrobe和fstrobe和fstrobefmonitor。这些任务的用法类似于先前的$display等,除了其第一参数为描述子以外。

$fdisplay([mcd_name], [format_string], …);

下面给出一个简单的代码片段。

integer log_file, both_file;

localparam con_file =32’h0000_0001;// console

initialbegin

log_file  = $fopenZ("my_log");if(log_file == 0)$display("Fail to open log file");// write consoleboth_file = log_file | con_file;// write to both console and log_file$fdisplay(both_file,"Simulation started");. . .// write to log_file only$fdisplay(log_file, ...);. . .// write to$fdisplay(both_file,"Simulation ended");$fclose(log_file);

end

注意我们可以通过对多个描述子进行位运算来创建一个描述子,比方说both_file变量。当both_file被使用时,就可以同时对console和log_file进行操作。

有两个任务可以从文件中载入数据,分别为:readmemb,readmemb,readmembreadmemh。这些任务假设外置文件中存储了memory-array的内容,然后读出这些内容存到一个变量中。readmemb,readmemb,readmembreadmemh所假设的文件格式分别为二进制和十六进制,相应地,它们的语法格式为:

$readmemb(“[file_name]”, [mem_variable]);

$readmemh(“{file_name]”, [mem_variable]);

下面的片段描述如何载入一个8x4的存储阵列:

reg[3:0] v_mem [0:7];

. . .

$readmemb(“vector.txt”, v_mem);

vector.txt应该包含八个4bit的使用空格分割的二进制数据。

有了文件操作函数和任务,就可以使用外部文件来指定测试模型,以及记录仿真结果。下面给出一个案例。

`timescale1ns/1ns

moduleeq2_file_tb;

// signal declaration

reg[1:0] test_in0, test_in1;

wiretest_out;

integer log_file, console_file, out_file;

reg[3:0] v_mem [0:7];

integer i;

// instantiate the circuit under test

eq2_sop eq2_sop_inst (

.a(test_in0),.b(test_in1),.aeqb(test_out)

);

initialbegin

// setup output fillog_file = $fopen("eqlog.txt"if(!log_file) $fdisplay("Cannot open log file");console_file =32'h0000_0001;out_file = log_file | console_file;// read test vector#readmemb("vector.txt", v_mem);   // test generator iterating throught 8 pattensfor(i=0; i<8; i=i+1)begin{test_in0, test_in1} = v_mem[i];end// stop simulation$fclose(log_file);$stop;

end

// text display

initialbegin

$fdisplay(out_file,"   time    test_in0    test_in1    test_out");$fdisplay(out_file,"           (a)         (b)         (aeqb)");$fmoitor(out_file,"    d    %b          %b          %b",$time, test_in0, test_in1, test_out);

end

endmodule

指定的test pattern为4位二进制格式,存储在vector.txt中。文件内容为:

1

2

3

4

5

6

7

8

00_00

01_00

01_11

10_10

10_00

11_11

11_01

00_10

注意:“_”只起连接数字,分隔开容易区分位数的作用,和verilog其他地方的用法一致。

上面的文件被载入到二维的v_mem变量中。仿真的结果被写入console和log_file中。log_file的内容为:

time test_in0 test_in1 test_out

    (a)         (b)         (aeqb)

0 00 00 1

200 01 00 0

400 01 11 0

600 10 10 1

800 10 11 0

1000 11 00 1

1200 11 01 0

1400 00 10 0

log_file为一般的文本文件,可使用其他文本编辑器编辑。

9 用户自定义函数和任务

待续

转载自:http://blog.sina.com.cn/s/blog_78699cbf01016mvt.html


http://www.ppmy.cn/news/22640.html

相关文章

Python爬虫教你爬取视频信息

大家好&#xff0c;我是拉斯&#xff0c;今天分享一个爬取某音视频的一个小案例&#xff0c;大家一起学习 目录前言基本环境配置爬取目标视频获取视频链接1.查看网页源代码2.抓包工具捕捉下载视频(以mp4格式进行保存)获取其他信息并打印(作者名&#xff0c;作品名&#xff0c;…

Go异步任务解决方案 Asynq

今天为大家介绍一个Go处理异步任务的解决方案&#xff1a;Asynq&#xff0c;是一个 Go 库&#xff0c;用于排队任务并与 worker 异步处理它们。它由Redis提供支持&#xff0c;旨在实现可扩展且易于上手。 一、概述 Asynq 是一个 Go 库&#xff0c;用于对任务进行排队并与工作人…

软件测试-python-UnitTest-笔记

UnitTestunittest是Python单元测试框架&#xff0c;类似于JUnit框架。unittest中有4个重要的概念&#xff1a;test fixture, test case, test suite, test runnerTestcase&#xff1a;一个TestCase的实例就是一个测试用例。什么是测试用例呢&#xff1f;就是一个完整的测试流程…

RPC框架整体架构

RPC就是把拦截到的方法参数&#xff0c;转成可以在网络中传输的二进制&#xff0c;并保证在服务提供方能正确地还原出语义&#xff0c;最终实现像调用本地一样地调用远程的目的。 1 RPC架构 RPC本质是远程调用&#xff0c;就要通过网络来传输数据。考虑到可靠性&#xff0c;一…

决策树算法分析天气、周末和促销活动对销量的影响

决策树算法分析天气、周末和促销活动对销量的影响 作者&#xff1a;AOAIYI 创作不易&#xff0c;觉得文章不错或能帮助到你&#xff0c;点赞收藏评论一下哦 文章目录决策树算法分析天气、周末和促销活动对销量的影响一、实验目的二、实验原理三、实验环境四、实验内容五、实验步…

QT+ OpenGL 变换

文章目录QT OpenGL变换向量的运算矩阵矩阵与向量相乘代码实现QT OpenGL 本篇完整工程见gitee:QTOpenGL 对应点的tag&#xff0c;由turbolove提供技术支持&#xff0c;您可以关注博主或者私信博主。 变换 我们需要改变物体的位置 现有解决办法&#xff08;每一帧&#xff0c…

YB菜菜的机器学习自学之路(八)——基于keras的初级深度学习框架

YB菜菜的机器学习自学之路&#xff08;八&#xff09;——基于keras的初级深度学习框架前提说明1. 训练集和测试集2. mnist数据集简单介绍3.基于keras框架&#xff0c;利用全链接层搭建深度学习网络对MNIST训练3.1 数据导入与one-hot编码3.2 创建模型3.3创建神经元3.3.1 输入层…

产品原型不应该有过多的视觉设计元素

​写在前面&#xff1a; 日常中会接触较多的学习产品经理知识的小伙伴&#xff0c;学习是为从事这个岗位或者技能提升做准备。在与小伙伴接触的过程中发现&#xff0c;有小部分人的观点是产品原型不重要&#xff0c;甚至可以不学不做。 作为产品经理&#xff0c;产品原型制作是…