Testbench编写指南(1)基本组成与示例

对于小型设计来说,最好的测试方式便是使用TestBench和HDL仿真器来验证其正确性;testbench的主要作用有:

  • 实例化待测试的设计;
  • 对测试模式添加测试向量;
  • 将测试结果以波形窗口的可视化方式显示;
  • 将测试结果与期待的结果进行比较;

一般TestBench需要包含这些部分:实例化待测试设计、使用测试向量激励设计、将结果输出到终端或波形窗口便于可视化观察、比较实际结果和预期结果。下面是一个标准的HDL验证流程: 

 

FPGA设计必须采用Verilog中可综合的部分子集,但TestBench没有限制,任何行为级语法都可以使用,即不需要可综合!! 

下表介绍了testbench的基本组成部分:

 

本文将先介绍TestBench中基本的组成部分;


生成时钟信号 

使用系统时钟的设计在TestBench中必须要生成时钟信号,该功能实现起来也非常简单,示例代码如下:

// Declare a clock period constant.

Parameter ClockPeriod = 10;
// Clock Generation method 1:
initial begin
forever Clock = #(ClockPeriod / 2) ~ Clock;
end

// Clock Generation method 2:
initial begin
always #(ClockPeriod / 2) Clock = ~Clock;
end

生成测试激励 

只有给设计激励数据,才能得到验证结果;提供激励的方法有两种 :绝对时间激励和相对时间激励;

绝对时间激励以仿真时刻0为基准,给信号赋值,示例如下:

initial begin
    reset = 1;
    load = 0;
    count = 0;
    #100 reset = 0;
    #20 load = 1;
    #20 count = 1;//#’用于指定等待的延迟时间,之后才会执行下一个激励
end

 相对时间激励给信号一个初始值,直到某一事件发生后才触发激励赋值,示例如下:

always @ (posedge clk)
    tb_cnt <= tb_cnt + 1;

initial begin
    if (tb_cnt <= 5) begin
        reset = 1;
        load = 0;
        count = 0;
    end
    else begin
        reset = 0;
        load = 1;
        count = 1;
    end
end

 根据需要,可以同时使用两种方法;每一个initial块、always块之间都是并行工作的关系,但在initial块内部是顺序地处理事件;因此复杂的激励序列应该分散到多个initial或always块中,以提高代码可读性和可维护性;

 


显示结果 

Verilog中可以使用$display和$monitor系统任务来显示仿真结果,示例代码如下:

initial begin
    $timeformat(-9, 1, "ns", 12);
    $display("    Time clk rst ld sftRg data sel");
    $monitor("%t %b %b %b %b %b %b", $realtime,
            clock, reset, load, shiftreg, data, sel);
end

 $display会将双引号之间的文本输出到终端窗口;$monitor的输出为事件驱动型,如上例中$realtime变量用于触发信号列表的显示,%t表示$realtime以时间格式输出,%b表示其余值以二进制格式输出,其余还有%d、%h、%o等;

 


简单示例

 下面是一个简单的移位寄存器Verilog设计示例:

module shift_reg (clock, reset, load, sel, data, shiftreg);
input clock;
input reset;
input load;
input [1:0] sel;
input [4:0] data;
output [4:0] shiftreg;
reg [4:0] shiftreg;

always @ (posedge clock)
begin
    if (reset)
        shiftreg = 0;
    else if (load)
        shiftreg = data;
    else
        case (sel)
            2'b00 : shiftreg = shiftreg;
            2'b01 : shiftreg = shiftreg << 1;//左移
            2'b10 : shiftreg = shiftreg >> 1;//右移
            default : shiftreg = shiftreg;
        endcase
end
endmodule

下面给出上述设计的TestBench示例:

module testbench; // 申明TestBench名称
reg clock;
reg load;
reg reset; // 申明信号
wire [4:0] shiftreg;
reg [4:0] data;
reg [1:0] sel;

// 申明移位寄存器设计单元
shift_reg dut(.clock (clock),
    .load (load),
    .reset (reset),
    .shiftreg (shiftreg),
    .data (data),
    .sel (sel));

initial begin   // 建立时钟
           clock = 0;
           forever #50 clock = ~clock;
        end

initial begin   // 提供激励
             reset = 1;
             data = 5'b00000;
             load = 0;
             sel = 2'b00;
             #200
             reset = 0;
             load = 1;
             #200
             data = 5'b00001;
             #100
             sel = 2'b01;
             load = 0;
             #200
             sel = 2'b10;
             #1000 $stop;
        end

initial begin   // 打印结果到终端
             $timeformat(-9,1,"ns",12);
             $display(" Time Clk Rst Ld SftRg Data Sel");
             $monitor("%t %b %b %b %b %b %b", $realtime,
             clock, reset, load, shiftreg, data, sel);
        end
endmodule

 TestBench中包括实例化设计、建立时钟、提供激励、终端显示几个部分;每个initial块之间都从0时刻开始并行执行;$stop用来指示仿真器停止TestBench仿真(建议每个TestBench中都有至少一个$stop);$monitor会在终端以ASCII格式打印监测结果;

 


设计规则

下面给出一些编写TestBench的基本设计规则:

  •  了解仿真器特性:不同的仿真器由不同的特性、能力和性能差异,可能会产生不同的仿真结果。仿真器可分为两类:(1).基于事件,当输入、信号或门的值改变时调度仿真器事件,有最佳的时序仿真表现;(2).基于周期,在每个时钟周期优化组合逻辑和分析结果,比前者更快且内存利用效率高,但时序仿真结果不准确。即使是基于事件的仿真器,在调度事件时采用不同的算法也会影响到仿真性能(比如同一仿真时刻发生了多个事件,仿真器需要按一定的序列依次调度每个事件);了解仿真器特性有一定必要,但目前最常用的ModelSim、Vivado Simulator等仿真器已经非常强大;
  • 避免使用无限循环:仿真器调度事件时,会增加CPU和内存的使用率,仿真进程也会变慢;因此除非迫不得已(比如利用forever生成时钟信号),尽量不要使用无限循环;
  • 将激励分散到多个逻辑块中:Verilog中的每个initial块都是并行的,相对于仿真时刻0开始运行;将不相关的激励分散到独立的块中,在编写、维护和更新testbench代码时会更有效率;
  • 避免显示不重要的数据:对于大型设计来说,会有超过10万个事件和大量的信号,显示大量数据会极度拖慢仿真速度;因此最好的做法是每隔N个时钟周期显示重要信号的数据,以保证足够的仿真速度;
     

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TestBench编写是验证设计正确性的重要步骤。以下是一些编写TestBench指南: 1. 了解仿真器特性:不同的仿真器具有不同的特性和性能差异,可能会导致不同的仿真结果。了解仿真器的工作原理和特点对于编写高效的TestBench很重要。 2. 设计时钟信号:时钟信号是设计中最重要的信号之一,需要在TestBench中生成一个稳定的时钟信号。这可以通过使用initial块和一些逻辑来实现。 3. 提供激励:TestBench需要提供适当的激励来测试设计。这可以通过调用设计的输入端口或使用测试向量来完成。 4. 终端显示:为了方便观察和验证结果,TestBench可以将设计的输出结果显示在终端或波形窗口上。这可以使用$display或$monitor命令来实现。 5. 遵循设计规则:编写TestBench时,需要遵循一些设计规则。例如,了解仿真器的特性和性能差异,避免使用无限循环等。 6. 分散激励:为了提高编写、维护和更新TestBench代码的效率,可以将不相关的激励分散到独立的逻辑块中。 7. 避免显示不重要的数据:对于大型设计,显示大量的数据会拖慢仿真速度。建议只显示重要的信号数据,并且可以设置显示间隔以保持足够的仿真速度。 以上是编写TestBench的一些基本指南,根据实际情况可能还会有其他特定的要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Testbench编写指南(1)基本组成示例](https://blog.csdn.net/FPGADesigner/article/details/82021647)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值