EDN首页   博客首页 用户登陆  |  注册
aaa
发表于 2009/9/18 20:15:02

2

关于投票

《ModelSim SE Command Reference》笔记1

ModelSim SE Command Reference

 

命令:.main clear

说明:.main clear”命令清除清除脚本。该行为如同:File > Transcript > Clear Transcript menu

 

命令:.wave.tree interrupt

说明:该命令将停止wave窗口的波形绘制,对于终止一个需要很长时间显示完整波形的wlf文件很有用。

 

命令:.wave.tree zoomfull

说明:该命令将满屏显示wave窗口的波形,包括从0时刻到最后仿真终止时刻的波形。同时该命令将返回当前显示的范围值。

如:

.wave.tree zoomfull

# {0 ps} {69321210 ns}

 

命令:.wave.tree zoomin <参数>

说明:该命令对wave窗口的波形进行放大,<参数>值代表以当前wave窗口波形的中心为基准进行放大的倍数。同时该命令将返回当前显示的范围值。

如:

.wave.tree zoomin 2

# {17330302500 ps} {51990907500 ps}

.wave.tree zoomin 5

# {31194544500 ps} {38126665500 ps}

 

命令:.wave.tree zoomout <参数>

说明:该命令对wave窗口的波形进行缩小,<参数>值代表以当前wave窗口波形的中心为基准进行缩小的倍数。同时该命令将返回当前显示的范围值。

如:

.wave.tree zoomin 5

# {31194544500 ps} {38126665500 ps}

.wave.tree zoomin 5

# {33967392900 ps} {35353817100 ps}

.wave.tree zoomout 5

# {31194544500 ps} {38126665500 ps}

 

命令:.wave.tree zoomrange <参数1> <参数2>

说明:该命令能够使wave窗口的波形显示<参数1><参数2>值范围内的波形。参数后最好带上相应的时间单位,如ms,us,ns,ps等。同时该命令将返回当前显示的范围值。

如:

.wave.tree zoomrange 100us 200us

# {100 us} {200 us}

.wave.tree zoomrange 100 200

# {100 ps} {200 ps}

 

命令:.wave.tree zoomlast

说明:该命令将使得波形显示范围和上一次操作前的一致,相当于“Ctrl+Z”操作,同时该命令将返回当前显示的范围值。该命令在需要多次反复观察几个信号的时候是很实用的。

如:

.wave.tree zoomin 2

# {17330302500 ps} {51990907500 ps}

.wave.tree zoomin 5

# {31194544500 ps} {38126665500 ps}

.wave.tree zoomlast

# {17330302500 ps} {51990907500 ps}

系统分类: CPLD/FPGA  |  用户分类: WritingTestbench  |  标签: ModelSim SE Command Reference  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(595) | 回复(3)

发表于 2009/4/8 18:33:39

7

关于投票

Writing Testbench——结构化Testbench

Writing Testbench——结构化Testbench

 

         特权同学为testbench的结构化以及可重用性设计发愁许久,终于在《writing testbench》一书的ch6章节里找到了答案,可谓柳暗花明又一村。原来Testbench还是可以做到可重用化的设计。下面以特权同学常用模块做一个结构化可重用的示例。

 

这是假设的待验证模块的顶层:

module prj_top(clk,rst_n,dsp_addr,dsp_data,dsp_rw……);

         input clk;

         input rst_n;

         input[23:0] dsp_addr;

         input dsp_rw;

         inout[15:0] dsp_data;

……

……

……

endmodule

 

这是testbench的顶层:

module tf_prj_top;

 

/*这个例化适用于被例化文件(这里是print_task.v)不对待验证模块接口进行控制*/

//print_task.v里包含常用信息打印任务封装

print_task         print();

 

/*这个例化适用于被例化文件需要对待验证模块接口进行控制,和通常RTL设计中例化方法时一样的*/

//sys_ctrl_task.v里包含系统时钟产生单元和系统复位任务

sys_ctrl_task   sys_ctrl(

                                                        .clk(clk),

                                                        .rst_n(rst_n)

                                               );

                                              

//dsp_ctrl_task.v包含DSP读写控制模拟

dsp_ctrl_task  dsp_ctrl(

                                                        .dsp_rw(DSP_RW),

                                                        .dsp_addr(dsp_addr),

                                                        .dsp_data(dsp_data),

                                                        ……

                                               );               

 

/*这里的端口例化需要注意的时,原来被测试模块的outputreg,如果被底层的例化模块所控制,那么这个reg要改为wire类型进行定义,而底层模块要将其定义为reg*/

         wire clk;

         wire rst_n;

         wire[23:0] dsp_addr;

         wire dsp_rw;

         wire[15:0] dsp_data;

         ……

 

//例化待验证工程顶层

prj_top              uut(

.clk(clk),

.rst_n(rst_n),

.dsp_addr(dsp_addr),

.dsp_data(dsp_data),

.dsp_rw(dsp_rw),

……

);

 

/*注意下面调用底层模块的任务的方式,例如sys_ctrl表示上面例化的sys_ctrl_task.vsys_reset是例化文件中的一个任务,用”.”做分割*/

Initial begin

         sys_ctrl.sys_reset(32’d1000);                   //系统复位1000ns

         #1000;

         dsp_ctrl.task_dsp_write(SELECT_STRB0,24'h000001,16’h00ff);   //DSP写任务调用

         #1000;

         dsp_ctrl.task_dsp_read(SELECT_STRB0,24'h000008,dsp_rd_data);     //DSP读任务调用

……

print.terminate;

end

 

endmodule

 

//调用层1

module print_task;

 

//----------------------------------------------------------------------//

//常用信息打印任务封装

//----------------------------------------------------------------------//           

//警告信息打印任务

task warning;

         input[80*8:1] msg;

         begin

                   $write("WARNING at %t : %s",$time,msg);

         end

endtask

 

//错误信息打印任务

task error;

         input[80*8:1] msg;

         begin

                   $write("ERROR at %t : %s",$time,msg);

         end

endtask

 

//致命错误打印并停止仿真任务

task fatal;

         input[80*8:1] msg;

         begin

                   $write("FATAL at %t : %s",$time,msg);

                   $write("Simulation false\n");

                   $stop;

         end

endtask

 

//完成仿真任务

task terminate;       

         begin

                   $write("Simulation Successful\n");

                   $stop;               

         end

endtask   

                  

endmodule

 

 

//调用层2

module sys_ctrl_task(

                                               clk,rst_n

                                               );

 

output reg clk; //时钟信号

output reg rst_n;     //复位信号

 

parameter      PERIOD   = 20;                   //时钟周期,单位ns

parameter      RST_ING          = 1'b0;      //有效复位值,默认低电平复位                                    

 

//----------------------------------------------------------------------//

//系统时钟信号产生

//----------------------------------------------------------------------//           

initial begin

         clk = 0;

         forever

                   #(PERIOD/2) clk = ~clk;

end

                

//----------------------------------------------------------------------//

//系统复位任务封装

//----------------------------------------------------------------------//           

task sys_reset;

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

         begin

                   rst_n = RST_ING;              //复位中

                   #reset_time;                      //复位时间

                   rst_n = ~RST_ING;            //撤销复位

         end

endtask            

                  

                  

endmodule

 

//调用层3

module dsp_ctrl_task(

                                               dsp_rw,dsp_strb0,dsp_strb1,dsp_iostrb,

                                               dsp_addr,dsp_data

                                               );

 

output reg dsp_rw;          //DSP读写信号,低--写,高--

output reg dsp_strb0;     //DSP存储空间STRB0选通信号

output reg dsp_strb1;     //DSP存储空间STRB1选通信号

output reg dsp_iostrb;    //DSP存储空间IOSTRB选通信号

output reg [23:0] dsp_addr;    //DSP地址总线

 

inout wire [15:0] dsp_data;     //DSP数据总线

                  

//print_task.v里包含常用信息打印任务封装

print_task         print();              

                  

//----------------------------------------------------------------------//

//模拟DSP读写任务封装

//----------------------------------------------------------------------//  

//DSP地址空间选择//

parameter        SELECT_STRB0         = 2'd1,

                                     SELECT_STRB1         = 2'd2,

                                     SELECT_IOSTRB       = 2'd3;

                                    

reg[15:0] dsp_data_reg; //DSP数据总线寄存器

 

assign dsp_data = dsp_rw ? 16'hzz : dsp_data_reg;

 

reg rd_flag;      //任务忙标志位,用于防止同时调用该任务

reg wr_flag;     //任务忙标志位,用于防止同时调用该任务

 

initial begin

         rd_flag = 0;       //DSP读任务不忙

         wr_flag = 0;      //DSP写任务不忙 

         //DSP信号接口初始化

         dsp_rw = 1;

         dsp_data_reg = 16'hzzzz;

         dsp_addr = 24'hzzzzzz;

         dsp_strb0 = 1;

         dsp_strb1 = 1;

         dsp_iostrb = 1;

end

 

reg h1;     //DSP时钟模拟,h1DSP指令周期

initial begin

         h1 = 1'b0;

         forever

         #20 h1 = ~h1;

end

 

//模拟 DSPFPGA任务

task task_dsp_read;

         input[1:0] tcs;           //片选输入

         input[23:0] taddr;    //地址输入

         output[15:0] tdata; //数据读出

         begin

                   ……

         end

endtask

 

//模拟DSPFPGA任务

task task_dsp_write;

         input[1:0] tcs;           //片选输入

         input[23:0] taddr;    //地址输入

         input[15:0] tdata;    //数据写入

         begin

                   ……

         end

endtask            

                  

endmodule

 

 

 

系统分类: CPLD/FPGA  |  用户分类: WritingTestbench  |  标签: Writing Testbench 结构化  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(1631) | 回复(1)

发表于 2009/4/1 21:22:48

3

关于投票

Writing testbench——文件调用

Writing testbench——文件调用

 

       最近在做一个FPGA工程的测试工作,接口比较繁杂。Testbench做起来更多的是感受到用Verilog做行为级设计的局限性。比如做一个CPU的读写时序,其实很难做到两个工程的直接便捷的移植,这也在无形中加大了测试人员的工作量。下面只是随笔,随便谈谈写testbench时自己对多个文件调用的一些实践看法,当然如果特权同学提出的一些看法和技巧有待改进的地方,还望高人指点。

       对于一个顶层的testbench,对待测试的工程做例化是必须的。而如果想把这个顶层testbench对被测试工程的输入信号分给多个文件的testbench来完成,即希望把我们的testbench分几个文件来完成(单纯的只是把一个文件分为多个文件),那似乎是不可能的事。至少特权同学目前在工程实践中是无法实现这一构想。

       一般的RTL级设计中可以使用`include “xxx.v”来包含定义了参数的头文件。而《writing testbench》一书中提出在testbench也能如此使用,即在xxx.v文件中定义了一些task,然后在顶层testbench直接调用这些task,但是工程实践上却无法通过modelsim的编译,不知道是否是因为我用的5.7SE版本太老不支持的缘故。而对于这样的不设计被测试工程信号的task,可以使用模块例化的方法来达到这个目的,即xxx  uuu();这种形式就可以在顶层testbench中使用xxx.v里的一些tast,function等,不过使用时必须加上uuu.(例如我要调用xxx.v里的名为a的认为,那么我应该写成uuu.a)。

       此外,个人感觉testbench的编写很难做到所谓的分层或者说是模块化,只能是凌乱一片,有时还真很难有头绪。而且对于像特权同学现在接手的这种功能繁多,而且信号交织的逻辑设计的验证,还是尽量做单元化的测试比较可行。所谓单元化,就是使用一个完成的工程编译后的网表作为测试对象,但是具体测试时,一个功能一个功能的去添加激励,然后测试这个功能的仿真验证结果是否达到要求。

      说白了,写testbench有时就像对单片机编程,一方面你可以做一个task(类比为单片机中直接控制IO口时序的子函数)把被测试工程的一些输入信号的操作进行封装,然后在initial或者always里调用这些task(类比为上层结构里直接调用这些底层驱动的单片机子函数)。对于单片机软件开发人员来说,不仅做软件,又要对硬件接口时序熟悉。而对testbench设计者而言,何尝不是。但是一个testbench里又充满了并行和顺序的陷阱,对于设计者更是一个挑战。做得不细心或是功底不到家,往往会让测试者感觉到底是做测试还是被测试。

 

 

系统分类: CPLD/FPGA  |  用户分类: WritingTestbench  |  标签: Writing testbench 文件调用  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(923) | 回复(0)

发表于 2009/3/19 20:27:22

2

关于投票

Writing testbench——防止同时调用task

Writing testbench——防止同时调用task

 

       Testben使用的是硬件语言,而其所依赖的环境却是基于PC的软件平台。这也就决定了其独特的代码风格。有时的的确确是以一个软件式的顺序方式在给待测试硬件代码做测试,但是写出来的testbench代码中却时常布满了并行执行的陷阱。这给硬件测试者带来了不少麻烦,既然我们选择了verilog,那么就得好好领会它在硬件测试环境下的特殊性。或者说,我们应该掌握一些常用的技巧来避免这些问题,让我们的testbench更高效的执行。

下面就是一个task使用的常见冲突以及解决办法。

 

task write;

input [7:0] wadd;

input [7:0] wdat;

begin

ad_dt <= wadd;

ale <= 1’bl;

rw <= 1’bl;

@ (posedge rdy) ;

ad_dt <= wdat;

ale <= 1’b0;

@ (negedge rdy);

end

endtask

 

initial write(8’h5A, 8’h00);

initial write(8’hAD, 8’h34);

 

       上面的task实现了往存储器的指定地址写入指定数据的功能。由于verilog中的alwaysinitial在实际执行中都是并行工作的,也就很有可能出现上面两个initial同时进行task调用,同时需要写存储器的情况。那样会出现什么后果呢?可想而知,这是我们不希望看到的。

       那么如何解决这样的问题呢?看下面改进后的代码。

 

task write;

input [7:0] wadd;

input [7:0] wdat;

reg in_use;

begin

if (in_use === 1b1) $stop;

in_use = 1b1;

ad_dt <= wadd;

ale <= 1’b1;

rw <= 1’b1;

@ (posedge rdy);

ad_dt <= wdat;

ale <= 1’b0;

@ (negedge rdy);

in_use = 1b0;

end

endtask

 

       粗体部分就是加入了检错机制,用in_use作为task已被调用的标志信号,从而避免其它的调用。

 

 

 

系统分类: CPLD/FPGA  |  用户分类: WritingTestbench  |  标签: Writing testbench task  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(1016) | 回复(0)

发表于 2009/3/14 16:53:02

4

关于投票

Testbench——读写紊乱状态

Testbench——HDL的并行性

 

       为什么C不能取代verilogVHDL作为硬件描述语言?因为C缺少了硬件描述最基本的三个思想:连通性(Connectivity),时间性(Time)和并行性(Concurrency)。

       连通性是指使用一个简单并相互连接的模块来描述设计的能力,原理图设计工具就是连通性完美的支持工具。

       时间性是指表现设计状态演进的时间变化的能力,这个能力不同于衡量一个代码执行运行多久的时间。

       并行性是指描述同时发生相互独立的行为的能力。

 

 

Testbench——读写紊乱状态

       在同一时刻对同一个寄存器进行读写容易发生紊乱状态。以下的例子,第一个always块对count操作(写),第二个always却要显示它。那么会出现什么状态呢?

 

module rw_race(clk);

 

input clk;

integer count;

 

always @ (posedge clk)

begin

count = count + 1;

end

 

always @ (posedge clk)

begin

$write("Count is equal to %0d\n", count);

end

 

endmodule

 

       由于testbench基于计算机,那么处理的时候也是分时复用的,从而这两个always块会先后执行。也就是说会出现两种情况,这里假设count在执行前为10,若先执行第一个块,那么后第二个块执行的结果显示count=11;若先执行第二个块再执行第一个块,显示的结果count=10

往往这样的紊乱状态不是我们希望看到的,很可能会给我们的测试工作带来许多不必要的麻烦。那么,有什么解决办法呢?

 

module rw_race(clk);

 

input clk;

integer count;

 

always @ (posedge clk)

begin

count <= count + 1;

end

 

always @ (posedge clk)

begin

$write(“Count is equal to %0d\n", count);

end

 

endmodule

 

       采用非阻塞赋值语句后,这个紊乱的状态就会得到解决。在第一个alwayscount增加的同时第二个always块也在执行,那么最后显示的count值是count1之前的数值。

       再看下面的例子。

 

module rw_race;

 

wire [7:0] out;

assign out = count + 1;

 

integer count;

initial

begin

count = 0;

$write("Out = %b\n", out);

end

 

endmodule

 

       会得到什么结果呢?这取决于你所使用的仿真器和命令行。一般的,Verilog-XL会输出”xxxxxxxx”,而VCS则会认为是”00000001”。那么如何改进呢?

 

module rw_race;

 

wire [7:0] out, tmp;

assign #1 out = tmp - 1;

assign #3 tmp = count + 1;

 

integer count;

initial

begin

count = 0;

#4;         // "out" will be 0 or x’s.

$write("Out = %b\n", out);

end

 

endmodule

 

       这些都是一个好的testbench应该注意的细节。

 

 

系统分类: CPLD/FPGA  |  用户分类: WritingTestbench  |  标签: Testbench  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(1022) | 回复(0)

发表于 2009/3/6 19:08:15

1

关于投票

Testbench——封装有用的子程序

Testbench——封装有用的子程序

 

moduel display_report();

//封装一些做测试时有用的报告显示

//包括任务errorwarningfatalterminate

 

//显示warning报告,同时包含显示当前时间和警告内容(由用户输入)

task warning;

       input [80*8:1] msg;

       begin

              $write("WARNING at %t: %s", $time, msg);

       end

endtask

 

//显示error报告,同时包含显示当前时间和错误内容(由用户输入)

task error;

       input [80*8:1] msg;

       begin

              $write("-ERROR- at %t: %s", $time, msg);

       end

endtask

 

//显示fatal报告,同时包含显示当前时间和致命内容(由用户输入)

task fatal;

       input [80*8:1] msg;

       begin

              $write("*FATAL* at %t: %s", $time, msg);

       terminate;

       end

endtask

 

//显示warning报告,同时包含显示当前时间和结束信息(该任务自动生成)

task terminate;

       begin

              $write("Simulation completed\n");

              $finish;

       end

endtask

 

endmodule

 

 

//使用上面封装好的task

module  testcase();

 

//包含已经编写好的display_report.v ,后面就可以调用其封装好的task

`include " display_report.v"

 

……

 

initial  begin

if (...)  error("Unexpected response\n");    //调用error任务

……

terminate;           //调用terminate任务

end

 

……

 

endmodule

 

 

module testcase;

 

initial

begin

if (...) syslog.error("Unexpected response");

syslog.terminate;

end

 

endmodule

 

 

       特权同学平时都是写testbenchModelSim5.7se下进行仿真测试。但是很可惜上面的方式无法通过。也许是不同测试软件测试环境的差异吧,归根到底应该是它们对于行为级硬件语言的编译执行程度不同。不过这里提出的子程序封装的概念还是很受益的,这样封装好的task对于以后的testbench随时都可以拿来用,免去繁杂的重复劳动。

系统分类: CPLD/FPGA  |  用户分类: WritingTestbench  |  标签: Testbench 封装 子程序  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(551) | 回复(0)

发表于 2009/3/3 19:06:32

0

关于投票

Testbench——关于变量的定义

Testbench——关于变量的定义:

 

       在编写testbench时,关于变量的定义常犯的错误就是将一个定义的全局变量应用到了两个不同的always块中(如例1所示),那么由于这两个always块独立并行的工作机制,很可能会导致意想不到的后果。

1

integer i;

 

always  begin

for (i = 0; i < 32; i = i + 1) begin

              ………

end

end

 

always  begin

for (i = 0; i < 32; i = i + 1) begin

              ……

end

end

 

       实际上,在verilog中(编写testbench时),如果在begin end之间定义了always的块名,那么你可以如例2一样申明变量。这样两个always块里的变量i就互不相关,也就不会产生不可预料的结果了。

2

always

begin:  block_l

integer i;

for (i = 0; i < 32; i = i + 1) begin

……

end

end

 

always

begin:  block_2

integer i;

for (i = 15; i >= 0; i = i - 1) begin

……

end

end

 

       除此以外,在verilog中的functiontask 支持类似上面的局部变量定义。

 

系统分类: CPLD/FPGA  |  用户分类: WritingTestbench  |  标签: Testbench 变量  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(712) | 回复(0)

发表于 2009/3/2 18:22:36

1

关于投票

Testbench——关于注释

Testbench——关于注释

 

1.        好的注释可以改善代码可维护性

注释的主要目的就是为了显著改善代码的可维护性。

2.        过时或错误的注释带来了(代码查看者)思维的混乱,这远比没有注释更糟糕。

代码注释时一个最大的共同错误是,注释描述的只是代码本身所做的功能。

例如:addr <= addr+1’b1;             //addr自增1

这句注释不会带来任何读者(代码查看者)需要的信息,我们应该在注释里告诉别人一些他们不熟悉的设计中和该语句相关的信息。

例如:(我们假定该工程是FPGAMCU通信)

addr <= addr+1’b1;           //MCU写入一个字节数据后,addr自增1

//以供MCU读取下一个字节数据

3.        注释代码时,你应该假定读者是一个有经验的工程师,他熟悉verilog语言本身,但是对于你的工程并不熟悉。最理想的情况是,抛开你的源代码,单从你的注释就能明白你所要实现的功能。

系统分类: CPLD/FPGA  |  用户分类: WritingTestbench  |  标签: 注释 verilog testbench  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(627) | 回复(1)

Total , Page /