详解VHDL如何编写Testbench

1.概述

仿真测试平台文件(Testbench)是可以用来验证所设计的硬件模型正确性的 VHDL模型,它为所测试的元件提供了激励信号,可以以波形的方式显示仿真结果或把测试结果存储到文件中。这里所说的激励信号可以直接集成在测试平台文件中,也可以从外部文件中加载。

一般而言,编写 Testbench 进行测试主要有下面四个步骤

  • (1)实例化需要测试的设计(DUT,Design Under Test);
  • (2)产生模拟激励(波形);
  • (3)将产生的激励加入到被测试模块并观察其输出响应;
  • (4)将输出响应与期望进行比较,从而判断设计的正确性。

其中,输出响应可以以波形方式显示或存储测试结果到文件中。

2.Testbench程序基本结构

通常 Testbench 的基本结构包括库的调用、程序包的调用、空实体、结构体描述。在结构体描述中,一般包含有被测试元件的声明、局部信号声明、被测试元件例化、激励信号的产生,如图所示。与一般的 VHDL 程序不同的是,Testbench 里面的实体为空。
在这里插入图片描述

2.1 被测试元件的声明方式

先说一个被测实体vote7,代码如下:

library ieee;
use ieee.std_logic_1164.all;

entity vote7 is 
    port(
        vt      : in  std_logic_vector(6 downto 0);
        result  : out std_logic
    );
end entity vote7;

architecture rtl of vote7 is
begin
    process(vt)
        variable sum : integer range 0 to 7;
    begin
        sum := 0;
        for i in 0 to 6 loop
            if vt(i) = '1' then
                sum := sum + 1;
                if sum > 4 then
                    result <= '1';
                else
                    result <= '0';
                end if;
            end if;
        end loop;
    end process;
end architecture;

2.1.1 组件实例化

组件实例化是一种传统且常用的方法,特别适用于较早版本的VHDL(如VHDL-93)。该方法需要在测试平台中先声明一个组件,然后在架构中进行实例化。

步骤

  • 组件声明:在测试平台的架构声明部分(通常在architecture关键词之后)声明DUT的组件。

  • 实例化:在架构的主体部分使用component实例化DUT,并进行端口映射。
    示例代码

假设有一个被测实体vote7,其声明如下:

library ieee;
use ieee.std_logic_1164.all;

entity tb_vote7 is
end entity tb_vote7;

architecture Behavioral of tb_vote7 is

    -- 信号声明
    ……
    -- 组件声明
    component vote7
        port(
            vt      : in  std_logic_vector(6 downto 0);
            result  : out std_logic
        );
    end component;

begin

    -- DUT实例化
    DUT: vote7
        port map (
            vt      => vt,
            result  => result
        );
    -- 激励过程
   ……
end architecture Behavioral;

2.1.2 直接实体实例化

直接实体实例化(也称为架构实例化)是VHDL-2002及更高版本中引入的一种更简洁的实例化方式。它不需要提前声明组件,直接引用实体和架构即可。

优点

  • 简洁:无需组件声明,减少了代码冗余。
  • 灵活:可以直接指定要使用的实体和架构。

使用与前述相同的vote7实体,测试平台采用直接实体实例化的方法如下:

library ieee;
use ieee.std_logic_1164.all;

entity tb_vote7 is
end entity tb_vote7;

architecture Behavioral of tb_vote7 is

    -- 信号声明
    ……
begin
    -- DUT直接实例化
    DUT: entity work.vote7(rtl)
        port map (
            vt      => vt,
            result  => result
        );
    -- 激励过程
  	……
end architecture Behavioral;

代码解析

  • DUT实例化:使用entity work.vote7(rtl)指定实体vote7和其架构rtl,然后进行端口映射。
  • 无需组件声明:省略了组件的预先声明,代码更加简洁。

3.激励信号的产生

激励信号产生的方式一般有两种,一种是以一定的离散时间间隔产生激励信号,另一种是基于实体的状态产生激励信号。需要注意的是,在 Testbench 程序中一定要对所有的激励信号赋初始值。下面通过实例,讲述激励信号的产生方法。

3.1 时钟信号的产生

时钟信号属于周期性出现的信号,是同步设计中最重要的信号之一。如图所示,时钟信号分为两类,即占空比为50%的对称时钟信号与占空比不是 50%的非对称时钟信号。

在这里插入图片描述
Testbench 中产生时钟信号方式有两种,

  • 一种是使用并行的信号赋值语句;
  • 一种是使用process进程。

下面分别通过两个例子来说明如何用这两种方法来产生所需的时钟信号。

【例】用并行信号赋值语句产生如图所示的 clk1clk2clk3 信号。
在这里插入图片描述
观察上图,我们发现 clk1 为对称时钟信号,其初始值可以在信号定义时赋值;clk2clk3为非对称时钟信号,其起始值可以在语句中赋值。这两种信号的产生方式有所不同,相对而言对称时钟信号的产生相对简单一些。

并行信号赋值语句的实现如下:

signal clk1:std_logic := '0';
signal clk2:std_logic;
signal clk3:std_logic;
……
clk1 <= not clk1 after clk_period/2;
clk2 <= '0' after clk_period/4 when clk2 = '1' else
	    '1' after 3*clk_period/4 when clk2 = '0' else
	    '1';
clk3 <= '0' after clk_period/4 when clk3 = '1' else
		'1' after 3*clk_period/4 when clk3 = '0' else
		'0';
……

【例】使用 process 进程产生如图所示的clk1clk2 信号。
在这里插入图片描述
观察上图,可以发现 clk1 为对称时钟信号,clk2 为非对称时钟信号,但这两种信号用 process 进程实现的方法基本一致。

process 进程实现如下:

signal clk1:std_logic;
signal clk2:std_logic;
……
clk1_gen:process
	constant clk_period	:time	:= 40ns;--常量只在该进程中起作用
	begin
		clk1 <= '1';
		wait for clk_period/2;
		clk1 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可知可知不可知

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值