산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Industrial programming >> VHDL

VUnit 시작하기

VUnit은 현재 사용 가능한 가장 인기 있는 오픈 소스 VHDL 검증 프레임워크 중 하나입니다. Python 테스트 스위트 러너와 전용 VHDL 라이브러리를 결합하여 테스트 벤치를 자동화합니다.

이 무료 VUnit 튜토리얼을 제공하기 위해 VHDLwhiz는 컴퓨터에서 다운로드하여 실행할 수 있는 간단한 VUnit 예제 프로젝트를 포함하여 이 기사의 나머지 부분을 담당하는 Ahmadmunthar Zaklouta를 모집합니다.

아흐마드에게 말을 전합시다!

이 튜토리얼은 설계 검증 프로세스에서 VUnit 프레임워크를 사용하는 방법을 보여 주는 것을 목표로 합니다. VUnit 설정, VUnit 테스트벤치 생성, VUnit 검사 라이브러리 사용, ModelSim에서 VUnit 테스트 실행 과정을 안내합니다. 또한 몇 가지 검증 기술을 보여줍니다.

개요

이 문서에는 여러 스크린샷이 포함되어 있습니다. 이미지를 클릭하면 커집니다!

사이드바를 사용하여 개요 탐색 이 자습서의 경우 아래로 스크롤하고 모바일 장치를 사용하는 경우 오른쪽 상단 모서리에 있는 팝업 탐색 버튼을 클릭합니다.

요구사항

이 튜토리얼에서는 이 소프트웨어가 Windows 시스템에 설치되어 있다고 가정합니다.

또한 기본적인 VHDL 지식과 ModelSim 기술이 있다고 가정합니다.

설치

git clone --recurse-submodules https://github.com/VUnit/vunit.git
 
python setup.py install

예제 프로젝트 다운로드

아래 양식을 사용하여 예제 프로젝트와 VHDL 코드를 다운로드할 수 있습니다.

C:\vunit_tutorial에 Zip 압축을 풉니다. .

소개

VUnit은 테스트 기반 워크플로 "조기 자주 테스트"와 테스트 실행 자동화 및 관리를 위한 도구 상자를 제공하여 검증 프로세스를 용이하게 하는 HDL용 테스트 프레임워크입니다. 광범위하고 풍부한 기능을 제공하지만 사용하고 적용하기 쉬운 고급 프레임워크입니다. 완전히 오픈 소스이며 기존 테스트 방법론에 쉽게 통합할 수 있습니다.

VUnit은 두 가지 주요 구성 요소로 구성됩니다.

VHDL 부분은 아래 다이어그램과 같이 6개의 라이브러리로 구성됩니다. 이 튜토리얼에서는 로깅 및 검사 라이브러리를 사용합니다.

테스트 중인 디자인

이 튜토리얼에 사용된 디자인, 이름은 motor_start입니다. , 특정 모터에 대한 시동 절차를 구현하고 모터 상태를 나타내는 3개의 LED를 구동합니다.

인터페이스는 입력 레코드 motor_start_in으로 구성됩니다. 3개의 요소(입력으로 3개의 스위치) 및 출력 레코드 motor_start_out 포함 3개의 요소(출력으로 RED, YELLOW, GREEN 3개의 LED)가 있습니다.

일부 모터는 사용을 시작하기 전에 처음에 초기화가 필요합니다. 모터 시동 절차는 세 단계로 이루어집니다.

  1. 구성 로드
  2. 보정
  3. 회전

시작 순서

다음은 모터 시동 순서와 LED 표시등의 의미입니다.

  1. switch_1을 켭니다.
  2. RED_LED가 5번 깜박이기 시작합니다.
  3. RED_LED가 깜박임을 멈추고 계속 켜져 있을 때까지 기다립니다.
<올 시작="4">
  • 이제 switch_2를 켤 수 있습니다.
  • switch_2가 켜져 있으면 YELLOW_LED가 5클록 주기 후에 계속 켜지기 시작하여 10클록 주기 동안 지속됩니다. 그 후 YELLOW_LED와 RED_LED가 꺼집니다.
  • <올 시작="6">
  • 이제 모터를 사용할 준비가 되었습니다. switch_3이 켜질 때마다 switch_3이 꺼질 때까지 GREEN_LED가 계속 켜집니다.
  • 이 시퀀스를 위반하면 모든 스위치가 꺼지고 시퀀스를 다시 시작할 수 있을 때까지 3개의 LED가 모두 계속 켜집니다.

    테스트벤치 개발

    튜토리얼의 이 부분은 다음 하위 섹션으로 구성됩니다.

    1. Python 실행 스크립트 설정
    2. VUnit 스켈레톤 설정
    3. 테스트벤치 설정

    Python 실행 스크립트 run.py 설정

    각 VUnit 프로젝트에는 최상위 python 스크립트 run.py가 필요합니다. 프로젝트의 진입점 역할을 하며 모든 VHDL 디자인, 테스트벤치 및 라이브러리 소스를 지정합니다.

    이 파일은 일반적으로 프로젝트 디렉토리에 있습니다. 이 튜토리얼에서 사용하는 디렉토리 트리는 아래와 같습니다.

    run.py에서 파일에서 다음 세 가지 작업을 수행해야 합니다.

    1 – 이 파일이 있는 경로를 가져오고 디자인 및 테스트벤치 파일의 경로를 지정합니다.

    # ROOT
    ROOT = Path(__file__).resolve().parent
    # Sources path for DUT
    DUT_PATH = ROOT / "design"
    # Sources path for TB
    TEST_PATH = ROOT / "testbench"
    

    2 – VUnit 인스턴스를 생성합니다.
    이렇게 하면 VUnit 인스턴스가 생성되고 VU라는 변수에 할당됩니다. . 그런 다음 VU을 사용할 수 있습니다. 라이브러리 및 다양한 작업을 생성합니다.

    # create VUnit instance
    VU = VUnit.from_argv()
    VU.enable_location_preprocessing()
    

    3 – 라이브러리를 만들고 여기에 VHDL 소스를 추가합니다.
    저는 디자인 부분과 테스트벤치 부분을 분리하는 것을 좋아합니다. 따라서 design_lib 두 개의 라이브러리를 만듭니다. 및 tb_lib .

    # create design library
    design_lib = VU.add_library("design_lib")
    # add design source files to design_lib
    design_lib.add_source_files([DUT_PATH / "*.vhdl"])
    
    # create testbench library
    tb_lib = VU.add_library("tb_lib")
    # add testbench source files to tb_lib
    tb_lib.add_source_files([TEST_PATH / "*.vhdl"])
    

    파일의 나머지 부분은 ModelSim이 wave.do를 사용하기 위한 구성입니다. 파일이 있는 경우.

    참고: 여기서는 *.vhdl를 사용합니다. 확대. *.vhd를 사용하는 경우 수정해야 할 수도 있습니다. .

    이 작업 구조가 마음에 들면 이 파일을 전혀 변경할 필요가 없습니다. 새 프로젝트를 시작할 때마다 프로젝트 디렉토리에 복사하기만 하면 됩니다. 그러나 다른 디렉토리 구조를 선호하는 경우 작업 구조에 맞게 경로를 수정해야 합니다.

    이제 이 파일을 사용할 때마다 VUnit은 프로젝트에서 VUnit 테스트벤치를 자동으로 검색하고, 컴파일 순서를 결정하고, 라이브러리를 만들고 소스를 컴파일하고, 선택적으로 모든 또는 특정 테스트 케이스로 시뮬레이터를 실행합니다.

    대단하지 않아? 😀

    VUnit 골격 설정

    VUnit 테스트벤치를 생성하려면 테스트벤치 파일 motor_start_tb에 특정 코드를 추가해야 합니다. , 이 하위 섹션에 설명된 대로.

    1 – 다음과 같이 라이브러리를 추가합니다.

    먼저 VUnit 라이브러리 VUNIT_LIB를 추가해야 합니다. 컨텍스트:VUNIT_CONTEXT , 따라서 다음과 같이 VUnit 기능에 액세스할 수 있습니다.

    LIBRARY VUNIT_LIB;
    CONTEXT VUNIT_LIB.VUNIT_CONTEXT;

    둘째, 디자인 및 테스트벤치 라이브러리 DESIGN_LIB를 추가해야 합니다. 및 TB_LIB 다음과 같이 DUT 및 패키지에 액세스할 수 있습니다.

    LIBRARY DESIGN_LIB;
    USE DESIGN_LIB.MOTOR_PKG.ALL;
    
    LIBRARY TB_LIB;
    USE TB_LIB.MOTOR_TB_PKG.ALL;
    

    DUT에는 두 개의 패키지가 있습니다. 하나는 디자인용:motor_pkg 다른 하나는 테스트벤치 요소 motor_tb_pkg에 대한 것입니다. . 이것은 일반적으로 큰 프로젝트가 구조화되는 방식이기 때문에 내가 만든 사소한 패키지입니다. VUnit이 이를 어떻게 처리하는지 보여주고 싶습니다.

    • motor_startmotor_pkg DESIGN_LIB로 컴파일됩니다. .
    • motor_start_tbmotor_tb_pkg TB_LIB으로 컴파일됩니다. .

    2 – 다음과 같이 엔티티에 러너 구성을 추가합니다.

    ENTITY motor_start_tb IS
    
      GENERIC(runner_cfg : string := runner_cfg_default);
    
    END ENTITY motor_start_tb;
    

    runner_cfg 파이썬 테스트 러너가 테스트벤치를 제어할 수 있도록 하는 일반 상수입니다. 즉, 파이썬 환경에서 테스트를 실행할 수 있습니다. 이 제네릭은 필수이며 변경되지 않습니다.

    3 – 다음과 같이 테스트벤치에 VUnit 테스트벤치 스켈레톤을 추가합니다.

    ARCHITECTURE tb OF motor_start_tb IS
      test_runner : PROCESS
      BEGIN
        -- setup VUnit
        test_runner_setup(runner, runner_cfg);
    
        test_cases_loop : WHILE test_suite LOOP
        
          -- your testbench test cases here
        
        END LOOP test_cases_loop;
        
        test_runner_cleanup(runner); -- end of simulation
      END PROCESS test_runner;
      
    END ARCHITECTURE tb;
    

    test_runner 테스트벤치의 주요 제어 프로세스입니다. 항상 test_runner_setup 프로시저로 시작합니다. test_runner_cleanup 절차로 끝납니다. . 시뮬레이션은 이 두 절차 사이에 있습니다. test_cases_loop 모든 테스트 사례가 발생하는 테스트 슈트입니다.

    테스트 케이스를 생성하기 위해 VUnit의 run을 사용합니다. 다음과 같이 If 문 내에서 함수:

    IF run("test_case_name") THEN
      -- test case code here
    
    ELSIF run("test_case_name") THEN
      -- test case code here
    
    END IF;
    

    그런 다음 run 호출에서 지정한 이름으로 단순히 호출하여 Python 환경에서 전체 또는 특정 테스트 케이스를 실행할 수 있습니다. .

    테스트벤치 설정

    여기에서 아래와 같이 DUT와 통신하는 데 필요한 신호를 추가하는 것으로 시작합니다.

    --------------------------------------------------------------------------
    -- TYPES, RECORDS, INTERNAL SIGNALS, FSM, CONSTANTS DECLARATION.
    --------------------------------------------------------------------------
    -- CONSTANTS DECLARATION --
    -- simulation constants
    CONSTANT C_CLK_PERIOD : time := 10 ns;
    
    -- INTERNAL SIGNALS DECLARATION --
    -- DUT interface
    SIGNAL clk             : STD_LOGIC := '0';
    SIGNAL reset           : STD_LOGIC := '1';
    SIGNAL motor_start_in  : MOTOR_START_IN_RECORD_TYPE := 
      (switch_1 => '0', switch_2 => '0', switch_3 => '0');
    
    SIGNAL motor_start_out : MOTOR_START_OUT_RECORD_TYPE;
    
    -- simulation signals
    SIGNAL led_out : STD_LOGIC_VECTOR(2 DOWNTO 0) := (OTHERS => '0');
    

    참고: 초기 값으로 신호를 초기화하는 것이 좋습니다.

    다음으로 DUT를 다음과 같이 인스턴스화합니다.

    --------------------------------------------------------------------------
    -- DUT INSTANTIATION.
    --------------------------------------------------------------------------
    motor_start_tb_inst : ENTITY DESIGN_LIB.motor_start(rtl)
      PORT MAP( 
        clk             => clk, 
        reset           => reset,
        motor_start_in  => motor_start_in,
        motor_start_out => motor_start_out
      ); 

    참고: 레코드에 입력 포트와 출력 포트를 그룹화했습니다. 엔터티와 인스턴스화를 덜 복잡하게 만들기 때문에 대규모 프로젝트에서 이점을 발견했습니다.

    마지막으로 드라이브 clk , resetled_out 여기에 표시된 대로:

    --------------------------------------------------------------------------
    -- SIGNAL DEFINITION OF UNUSED OUTPUT PORTS AND FIXED SIGNALS.
    --------------------------------------------------------------------------
    led_out(0) <= motor_start_out.red_led;
    led_out(1) <= motor_start_out.yellow_led; 
    led_out(2) <= motor_start_out.green_led; 
    
    --------------------------------------------------------------------------
    -- CLOCK AND RESET.
    --------------------------------------------------------------------------
    clk   <= NOT clk after C_CLK_PERIOD / 2;
    reset <= '0' after 5 * (C_CLK_PERIOD / 2);
    

    테스트 케이스 개발

    이제 DUT로 돌아가서 몇 가지 테스트 케이스를 개발하여 실제 작업을 시작하겠습니다. 두 가지 시나리오를 제시하겠습니다.

    설계 엔지니어 시나리오: 디자이너의 입장에서 검증은 디자이너 자신이 하는 것입니다. 일반적으로 개발 단계에서 발생하는 이 시나리오에서 디자이너는 코드에 액세스할 수 있습니다. 이 시나리오는 VUnit이 "조기 그리고 자주 테스트"하는 데 어떻게 도움이 되는지 보여줍니다.

    인증 엔지니어 시나리오 :디자인(DUT)은 블랙박스로 취급됩니다. 우리는 외부 인터페이스와 테스트 요구 사항만 알고 있습니다.

    다음 세 가지 확인 기술도 살펴보겠습니다.

    • 테스트 케이스 내의 드라이버 및 검사기.
    • 테스트 케이스 내의 드라이버 및 제어된 검사기
    • 테스트 케이스 내 드라이버 및 자체 검사 검사기

    첫 번째 기술부터 시작하여 이 기사 뒷부분의 마지막 두 가지 방법으로 돌아가 보겠습니다.

    테스트 케이스 내의 드라이버 및 검사기

    이것은 가장 직접적인 접근 방식입니다. 드라이버와 검사기는 테스트 케이스 자체 내부에 있으며 테스트 케이스 코드 내에서 구동 및 확인 작업을 구현합니다.

    다음과 같이 RED_LED 기능을 개발했다고 가정해 보겠습니다.

    WHEN SWITCH_1_ON =>
      IF (motor_start_in.switch_1 = '0' OR
          motor_start_in.switch_2 = '1' OR
          motor_start_in.switch_3 = '1') THEN
        state = WAIT_FOR_SWITCHES_OFF;
      ELSIF (counter = 0) THEN
        led_s.red_led <= '1';
        state         <= WAIT_FOR_SWITCH_2;
      ELSE
        led_s.red_led <= NOT led_s.red_led;
      END IF;
    

    이제 나머지 기능을 개발하기 전에 디자인을 확인하려고 합니다.

    이를 위해 VUnit의 run를 사용합니다. test_suite 내부의 함수 아래와 같이 switch_1을 켤 때의 출력을 확인하기 위한 테스트 케이스를 생성합니다.

    IF run("switch_1_on_output_check") THEN
      info("------------------------------------------------------------------");
      info("TEST CASE: switches_off_output_check");
      info("------------------------------------------------------------------");
      -- code for your test case here.
    

    이렇게 하면 "switch_1_on_output_check"라는 테스트 케이스가 생성됩니다.

    참고: info 스크립트 화면과 터미널에 인쇄하는 로깅 라이브러리의 VUnit 프로시저입니다. 테스트 결과를 표시하는 데 사용할 것입니다.

    이제 이 테스트 케이스에 대한 코드를 작성합니다. 이를 위해 VUnit의 검사 하위 프로그램을 사용할 것입니다.

    switch_1이 켜진 후 RED_LED가 5번 깜박임을 알고 있으므로 VHDL For 루프를 만들고 내부에서 검사를 수행합니다.

    check 절차는 우리가 제공하는 특정 매개변수를 확인합니다. 많은 변형이 있으며 여기에서는 데모 목적으로 몇 가지를 사용했습니다.

    check(expr => motor_start_out.red_led = '1', 
          msg  => "Expect red_led to be ON '1'");
    

    여기에서 시뮬레이션 시간의 이 시점에서 RED_LED가 '1'인지 테스트하고 콘솔에 메시지를 인쇄합니다.

    # 35001 ps - check - PASS - red_led when switch_1 on (motor_start_tb.vhdl:192)
    

    참고 통과인지 오류인지, 이 검사가 발생한 타임스탬프와 함께 이 검사가 있는 파일 이름 및 줄 번호를 알려줍니다.

    또 다른 방법은 check_false를 사용하는 것입니다. 절차. 여기서는 YELLOW_LED가 '0'인지 확인하는 데 사용합니다.

    check_false(expr => ??motor_start_out.yellow_led, 
                msg  => result("for yellow_led when switch_1 on"));
    

    여기서는 VUnit의 result을 사용합니다. 메시지를 개선하는 기능. 출력물은 다음과 같습니다.

    # 35001 ps - check - PASS - False check passed for yellow_led when switch_1 on 
    #                           (motor_start_tb.vhdl:193)
    

    참고 수표 유형에 대한 추가 정보를 인쇄합니다. "거짓 수표 통과".

    또 다른 방법은 check_equal을 사용하는 것입니다. . 여기서는 GREEN_LED가 '0'인지 테스트하는 데 사용합니다.

    check_equal(got      => motor_start_out.green_led, 
                expected => '0',
                msg      => result("for green_led when switch_1 on"));
    

    여기에서 비교를 위해 추가 매개변수 '0'을 제공했습니다. 결과 출력물은 다음과 같습니다.

    # 35001 ps - check - PASS - Equality check passed for green_led when switch_1 on - 
    #                           Got 0. (motor_start_tb.vhdl:194)
    

    이제 우리는 한 클럭 사이클 후에 RED_LED가 꺼지고 다른 LED도 꺼진 상태를 유지한다는 것을 알고 있습니다. check_equal를 사용할 수 있습니다. 아래와 같이 모두 동시에 확인하려면:

    check_equal(led_out, STD_LOGIC_VECTOR'("000"), 
                result("for led_out when switch_1 on"), warning);
    

    참고 한정자 STD_LOGIC_VECTOR'("000") 사용 , 따라서 값이 절차에 대해 모호하지 않습니다. 또한 이 검사의 수준을 WARNING으로 지정했습니다. 즉, 이 검사가 실패하면 오류가 발생하는 대신 경고가 발생합니다. 출력은 다음과 같습니다.

    #  45001 ps - check - PASS - Equality check passed for led_out when switch_1 on - 
    #                            Got 000 (0). (motor_start_tb.vhdl:197)
    

    다음은 전체 테스트 사례의 코드입니다.

    WAIT UNTIL reset <= '0';
    WAIT UNTIL falling_edge(clk);
    motor_start_in.switch_1 <= '1';  -- turn on switch_1
    FOR i IN 0 TO 4 LOOP
      WAIT UNTIL rising_edge(clk);
      WAIT FOR 1 ps;
      check(expr => motor_start_out.red_led = '1', 
            msg  => "Expect red_led to be ON '1'");
    
     check_false(expr => ??motor_start_out.yellow_led, 
                 msg  => result("for yellow_led when switch_1 on"));
    
     check_equal(got      => motor_start_out.green_led, 
                 expected => '0',
                 msg      => result("for green_led when switch_1 on"));   
    
      WAIT UNTIL rising_edge(clk);
      WAIT FOR 1 ps;
      check_equal(led_out, STD_LOGIC_VECTOR'("000"), 
                  result("for led_out when switch_1 on"), warning);
    END LOOP;
    info("===== TEST CASE FINISHED =====");
    

    테스트 케이스 실행

    이제 테스트 케이스를 실행할 시간입니다. 터미널이나 시뮬레이터 GUI에서 테스트를 실행할 수 있습니다.

    터미널에서 테스트 실행

    그렇게 하려면 먼저 새 터미널(CMD, PowerShell, Windows 터미널)을 열고 vunit_tutorial으로 이동합니다. run.py 디렉토리 파일이 있습니다.

    테스트 케이스를 실행하려면 다음을 입력하십시오.

    python .\run.py *switch_1_on_output_check
    

    VUnit은 모든 VHDL 파일을 올바른 순서로 컴파일하고 구문 분석하여 VUnit 테스트 벤치를 찾습니다. 그런 다음 VUnit은 해당 파일을 살펴보고 run을 검색합니다. "switch_1_on_output_check" 테스트 케이스 이름으로 함수를 실행합니다.

    참고: 전체 이름과 일치하도록 테스트 케이스 앞에 * 와일드카드 기호를 넣습니다.

    tb_lib.motor_start_tb.switch_1_on_output_check
    

    VUnit 명령줄 인터페이스가 와일드카드를 허용하기 때문에 그렇게 할 수 있습니다.

    시뮬레이션 후 결과 출력은 다음과 같습니다.

    Starting tb_lib.motor_start_tb.switch_1_on_output_check
    Output file: C:\vunit_tutorial\vunit_out\test_output\tb_lib.motor_start_tb.
    switch_1_on_output_check_6df3cd7bf77a9a304e02d3e25d028a92fc541cf5\output.txt
    pass (P=1 S=0 F=0 T=1) tb_lib.motor_start_tb.switch_1_on_output_check (1.1 seconds)
    
    ==== Summary ==========================================================
    pass tb_lib.motor_start_tb.switch_1_on_output_check (1.1 seconds)
    =======================================================================
    pass 1 of 1
    =======================================================================
    Total time was 1.1 seconds
    Elapsed time was 1.1 seconds
    =======================================================================
    All passed!
    

    하나의 테스트가 실행되었고 통과했음을 알 수 있습니다.

    참고 VUnit이 vunit_out을 만들었습니다. 프로젝트 디렉토리의 폴더. 이 폴더 안에 test_output이라는 폴더가 있습니다. 테스트에 대한 보고서가 있습니다.

    위에서 각 검사에 대한 세부 정보 없이 최종 테스트 결과만 얻었지만 VUnit 명령줄 도구는 테스트 실행을 위한 여러 스위치를 제공합니다. 시뮬레이션 중에 진행되는 작업에 대한 자세한 정보를 얻으려면 자세한 정보 표시 스위치 -v를 사용할 수 있습니다. :

    python .\run.py *switch_1_on_output_check -v
    

    자세한 출력은 다음과 같습니다.

    다른 유용한 스위치는 다음과 같습니다.

    -l, --list :모든 테스트 케이스를 나열합니다.

    --clean :먼저 출력 폴더를 삭제한 후 테스트를 실행합니다.

    --compile :이 스위치는 예를 들어 오류를 확인하기 위해 테스트를 실행하지 않고 컴파일하려는 경우에 유용합니다.

    시뮬레이터 GUI에서 테스트 실행

    종종 파도의 육안 검사가 필요합니다. VUnit은 GUI 스위치 -g를 사용하여 시뮬레이터에서 테스트를 실행하는 자동화된 방법을 제공합니다. . VUnit은 모든 컴파일을 수행하고 요청된 테스트 케이스로 ModelSim(우리가 구성한 시뮬레이터)을 시작하고 wave.do 파일을 사용할 수 있습니다.

    python .\run.py *switch_1_on_output_check -g
    

    이제 VUnit은 이 특정 테스트 사례에 대해 ModelSim을 시작하고 파형 창을 열고 이미 motor_start_tb_wave.do가 있으므로 신호를 추가합니다. 파도 내부 폴더.

    참고: 원하는 대로 파형을 사용자 지정할 수 있지만 waves 안에 파형 형식 파일을 저장해야 합니다. 이 이름 규칙이 testbench_file_name_wave.do인 폴더 VUnit이 ModelSim을 시작할 때 로드할 수 있도록 합니다.

    오류를 발견한 후 코드를 변경하고 이 테스트 케이스를 다시 실행하려고 한다고 가정합니다. 이 경우 ModelSim의 스크립트 창에 vunit_restart를 입력할 수 있습니다. , 그러면 VUnit이 시뮬레이션을 다시 컴파일하고 다시 시작하고 다시 실행합니다.

    테스트 케이스 내의 드라이버 및 제어된 검사기

    지금까지 VUnit 테스트벤치를 설정하고 테스트 케이스를 개발 및 실행하는 방법을 배웠습니다. 이 섹션에서는 다른 검증 접근 방식과 VUnit 검사기 라이브러리를 사용하여 검증 엔지니어의 관점에서 더 많은 테스트 케이스를 개발할 것입니다.

    이전 테스트 케이스와 달리 이 접근 방식은 테스트 케이스 내부에 드라이버가 있고 외부에 검사기가 있지만 테스트 케이스는 여전히 이를 제어합니다.

    다음과 같은 확인 요구 사항이 있다고 가정해 보겠습니다.

    • switch_1이 ON이고 RED_LED가 켜져 있는 동안 switch_2를 ON한 후 출력을 확인합니다.

    DUT 섹션에서 우리는 다음을 알고 있습니다.

    • 스위치_2를 켜면 YELLOW_LED가 10클록 주기 동안 5클록 주기 후에 계속 켜져 있고 그 후에 YELLOW_LED와 RED_LED가 꺼집니다.

    VUnit의 check_stable을 사용하겠습니다. 확인 절차:

    • YELLOW_LED는 처음부터 switch_2가 켜질 때까지 '0'입니다.
    • YELLOW_LED는 10클럭 사이클 동안 '1'입니다.

    VUnit의 check_next을 사용하겠습니다. 확인 절차:

      • YELLOW_LED는 switch_2를 켠 후 5클록 주기 후 '1'입니다.

    check_stable :start_event으로 시작하는 창 내에서 신호가 안정적인지 확인합니다. 신호 펄스 및 종료 end_event 신호 펄스.

    check_next :start_event 이후에 여러 클록 사이클 후에 신호 ='1'인지 확인 신호 펄스.

    start_eventend_event 신호는 테스트 케이스에서 제어됩니다.

    먼저 check_stable에 필요한 신호를 추가합니다. 및 check_next 절차는 다음과 같습니다.

    -- VUnit signals
    SIGNAL enable                  : STD_LOGIC := '0';
    -- for yellow_led
    SIGNAL yellow_next_start_event : STD_LOGIC := '0';
    SIGNAL yellow_low_start_event  : STD_LOGIC := '0';
    SIGNAL yellow_low_end_event    : STD_LOGIC := '0';
    SIGNAL yellow_high_start_event : STD_LOGIC := '0';
    SIGNAL yellow_high_end_event   : STD_LOGIC := '0';
    

    그런 다음 test_suite 안에 새 테스트 케이스를 만듭니다. VUnit의 run 사용 기능:

    ELSIF run("switch_2_on_output_check") THEN
      info("------------------------------------------------------------------");
      info("TEST CASE: switch_2_on_output_check");
      info("------------------------------------------------------------------");
    

    start_event를 생성합니다. check_stable과 함께 사용하기 위한 YELLOW_LED의 낮은 상태 절차는 다음과 같습니다.

    WAIT UNTIL reset <= '0';
    -- out of reset
    enable <= '1';
    pulse_high(clk, yellow_low_start_event);
    WAIT FOR C_CLK_PERIOD * 3;
    

    enable 신호는 check_stable를 활성화합니다. 및 check_next 절차를 처음부터 활성화하고자 합니다.

    pulse_high motor_tb_pkg의 간단한 절차입니다. 다음 상승 클록 에지를 기다리고 한 클록 사이클 동안 신호를 펄스합니다. 이 경우 yellow_ low_start_event입니다. .

    이제 switch_1을 켜고 RED_LED가 지속적으로 켜질 때까지 기다린 다음 switch_2를 ON으로 켭니다.

    -- turn ON switch_1
    motor_start_in.switch_1 <= '1';
    -- wait until RED_LED finished
    WAIT FOR C_CLK_PERIOD * 12;
    -- turn ON switch_2
    motor_start_in.switch_2 <= '1';
    

    이제 우리는 5개의 클럭 사이클 후에 YELLOW_LED가 '1'이 된다는 것을 압니다. 따라서 start_event YELLOW_LED가 check_next와 함께 사용하려면 절차:

    -- after 5 clk cycles YELLOW_LED will light
    -- next_start_event for YELLOW_LED high
    pulse_high(clk, yellow_next_start_event); -- 1st clk cycle
    

    마찬가지로 end_event check_stable과 함께 사용할 YELLOW_LED의 낮은 상태 절차:

    WAIT FOR C_CLK_PERIOD * 3;
    
    -- end event for YELLOW_LED low
    pulse_high(clk, yellow_low_end_event);  -- 5th clk cycle
    

    이제 YELLOW_LED는 10클럭 사이클 동안 하이일 것입니다. 따라서 start_event를 생성합니다. check_stable에 대한 YELLOW_LED의 높은 상태 절차:

    -- YELLOW_LED is high for 10 clk cycles
    -- start event for YELLOW_LED high
    yellow_high_start_event <= '1';
    WAIT UNTIL rising_edge(clk); -- 1st clk cycle
    yellow_high_start_event <= '0';
    

    여기서는 pulse_high을 사용하지 않았습니다. 펄스가 다음 하강 에지가 아니라 지금 발생하기를 원하기 때문입니다.

    그리고 end_event을 생성합니다. check_stable에 대한 YELLOW_LED의 높은 상태 다음과 같이 8 클록 주기 후:

    WAIT FOR C_CLK_PERIOD * 8;
    -- end event for YELLOW_LED
    pulse_high(clk, yellow_high_end_event); -- 10th clk cycle
    

    이것으로 테스트 케이스가 종료됩니다. 다음과 같은 프로세스 후에 검사기 프로시저에 대한 호출만 추가하면 됩니다.

    ----------------------------------------------------------------------
    -- Related YELLOW_LED check
    ----------------------------------------------------------------------
    -- check that YELLOW_LED is low from start until switch_2 is ON
    check_stable(clock       => clk, 
                 en          => enable, 
                 start_event => yellow_low_start_event, 
                 end_event   => yellow_low_end_event, 
                 expr        => motor_start_out.yellow_led, 
                 msg         => result("YELLOW_LED Low before switch_2"),
                 active_clock_edge => rising_edge,
                 allow_restart     => false);
    
    -- check that YELLOW_LED is high after switch_2 is ON
    check_next(clock       => clk,
               en          => enable, 
               start_event => yellow_next_start_event, 
               expr        => motor_start_out.yellow_led, 
               msg         => result("for yellow_led is high after 5 clk"),
               num_cks     => 5, 
               allow_overlapping   => false, 
               allow_missing_start => true);
    
    -- check that YELLOW_LED is high after for 10 clk cycle
    check_stable(clk, enable, yellow_high_start_event, yellow_high_end_event,
                 motor_start_out.yellow_led, 
                 result("for YELLOW_LED High after switch_2"));
    

    참고: allow_restart check_stable의 매개변수 새 start_event end_event 전에 발생 .

    참고: check_next에 5를 넣습니다. YELLOW_LED가 yellow_next_start_event의 5개 클록 주기 후에 높기 때문에 절차 .

    참고: check_next의 마지막 두 매개변수

    • allow_overlapping :두 번째 start_event 허용 첫 번째 expr 앞에는 '1'이 있습니다.
    • allow_missing_start :start_event 없이 expr이 '1'이 되도록 허용 .

    이제 다음과 같이 명령줄에서 테스트 케이스를 실행할 수 있습니다.

    python .\run.py *switch_2_on_output_check -v
    

    결과는 다음과 같습니다.

    다음과 같이 시뮬레이터 GUI에서 테스트 케이스를 실행할 수 있습니다.

    python .\run.py *switch_2_on_output_check -g
    

    이 파형의 결과:

    참고:start_eventend_event check_stable에 대한 신호 창에 포함되고 모니터링되는 신호는 start_event='1'일 때 참조됩니다. .

    이 테스트 케이스에서 우리는 테스트 케이스에서 검사 작업을 가져왔지만 테스트 케이스에서 제어했습니다. 또한 RED_LED 기능을 확인하지 않았다는 점에 유의하세요.

    테스트 케이스 내 드라이버 및 자체 검사 검사기

    이전 접근 방식의 한 가지 단점은 가독성이 떨어진다는 것입니다. 테스트 케이스에는 start_event과 같이 테스트나 기능과 관련이 없거나 흥미롭지 않은 정보가 포함되어 있습니다. 및 end_event 신호. 우리는 이 모든 세부 사항을 숨기고 테스트 케이스에 드라이버만 포함하고 자체 검사 검사기를 만들고 싶습니다.

    먼저 드라이버를 설계해 보겠습니다.

    VHDL의 절차는 이에 대한 좋은 후보입니다. VHDL 절차를 사용하는 방법을 모르는 경우 이 자습서를 확인하십시오.

    다음은 switch_driver 절차입니다. motor_tb_pkg에서 .

    PROCEDURE switch_driver(
      SIGNAL switches     : OUT MOTOR_START_IN_RECORD_TYPE;
      CONSTANT clk_period : IN TIME;
      CONSTANT sw1_delay  : IN INTEGER;
      CONSTANT sw2_delay  : IN INTEGER;
      CONSTANT sw3_delay  : IN INTEGER) IS
    BEGIN
      IF (sw1_delay = 0) THEN
        WAIT FOR clk_period * sw1_delay;
        switches.switch_1 <= '1';
      ELSIF (sw1_delay = -1) THEN
        switches.switch_1 <= '0';
      END IF;
      IF (sw2_delay = 0) THEN
        WAIT FOR clk_period * sw2_delay;
        switches.switch_2 <= '1';
      ELSIF (sw2_delay = -1) THEN
        switches.switch_2 <= '0';
      END IF;
      IF (sw3_delay = 0) THEN
        WAIT FOR clk_period * sw3_delay;
        switches.switch_3 <= '1';
      ELSIF (sw3_delay = -1) THEN
        switches.switch_3 <= '0';
      END IF;
    END PROCEDURE switch_driver;
    

    지연을 계산하기 위한 클록 주기와 클록 주기에서 각 스위치에 대해 원하는 지연을 지정하는 정수를 제공합니다.

    • 자연값(>=0)은 다음을 의미합니다. (clk_period 이후에 스위치를 켭니다. * sw_delay ).
    • 값 -1은 스위치를 끕니다.
    • 다른 모든 음수 값은 아무 것도 하지 않음을 의미합니다.

    이제 다음과 같이 테스트 케이스 내에서 이 절차를 사용할 수 있습니다.

    switch_driver(motor_start_in,C_CLK_PERIOD,3,12,20);
    

    이렇게 하면 3클록 주기 후에 switch_1이 켜지고 12클록 주기 후에 switch_2가 켜지고 마지막으로 20클록 주기 후에 switch_3이 켜집니다.

    지금까지 VUnit의 기본 검사기를 사용했습니다. VUnit은 사용자 지정 검사기를 가질 수 있는 가능성을 제공합니다. 이것은 검사가 많은 큰 테스트 케이스가 있고 검사에 대한 통계를 갖고 싶거나 검사를 나머지 테스트벤치와 혼동하고 싶지 않을 때 유용합니다.

    RED_LED에 대한 사용자 지정 검사기를 만들고 실패한 로그 수준을 WARNING으로 설정합니다.

    CONSTANT RED_CHECKER : checker_t := new_checker("red_led_checker", WARNING);
    

    그리고 Setup VUnit 섹션에서 RED_CHECKER에 대한 검사 통과 로깅을 활성화합니다.

    show(get_logger(RED_CHECKER), display_handler, pass);
    

    이제 자체 검사 검사기(또는 모니터)로 이동해 보겠습니다. 먼저 RED_LED에 대한 자체 검사 검사기를 설계하고 이를 위한 프로세스를 다음과 같이 사용합니다.

    • switch_1이 켜질 때까지 기다렸다가 start_event을 추가하세요. 모든 LED 낮음:
    red_monitor_process : PROCESS
    BEGIN
      WAIT UNTIL reset = '0';
      pulse_high(clk, led_low_start_event);
      WAIT UNTIL motor_start_in.switch_1 = '1';
    
    • RED_LED 깜박임에 대한 첫 번째 테스트 사례에서 동일한 FOR LOOP를 사용하고 start_event을 추가합니다. RED_LED 높음:
    -- RED_LED is blinking
    FOR i IN 0 TO 4 LOOP
      IF (motor_start_in.switch_1 = '1' AND
          motor_start_in.switch_2 = '0' AND
          motor_start_in.switch_3 = '0') THEN
    
        WAIT UNTIL rising_edge(clk);
        WAIT FOR 1 ps;
        check(RED_CHECKER, motor_start_out.red_led = '1', 
              result("for red_led blink high"));
    
        WAIT UNTIL rising_edge(clk);
        WAIT FOR 1 ps;
        check(RED_CHECKER, motor_start_out.red_led = '0',
              result("for red_led blink low"));
    
        -- RED_LED is constantly lighting start event
        IF (i = 4) THEN -- finish blinking
          WAIT UNTIL rising_edge(clk);
          WAIT FOR 1 ps;
          check(RED_CHECKER, motor_start_out.red_led = '1',
                result("for red_led blink low"));
          pulse_high(clk, red_high_start_event);
        END IF;
      ELSE
      -- perform check for error (All led high until all switches are off)
      END IF;
    END LOOP;
    
    • 이제 switch_2가 켜질 때까지 기다린 다음 end_event을 추가합니다. RED_LED 높음:
      IF (motor_start_in.switch_2 /= '1') THEN
        WAIT UNTIL motor_start_in.switch_2 = '1';
      END IF;
      WAIT UNTIL rising_edge(clk);
      WAIT FOR C_CLK_PERIOD * 14;
      -- RED_LED is constantly lighting end event
      pulse_high(clk, red_high_end_event);
    END PROCESS red_monitor_process;
    
    • 이제 check_stable를 추가합니다. RED_LED 높음 및 낮음 절차:
    -- check that RED_LED is low from start until switch_1 is ON
    -- Note the use of motor_start_in.switch_1 as end_event
    check_stable(RED_CHECKER, clk, enable, led_low_start_event, 
                 motor_start_in.switch_1, motor_start_out.red_led,
                 result("RED_LED low before switch_1"));
    
    -- check that RED_LED is low after switch_2 is ON
    check_stable(RED_CHECKER, clk, enable, red_high_start_event, 
                 red_high_end_event, motor_start_out.red_led,
                 result("RED_LED high after switch_1"));
    

    다음 테스트 케이스로 이것을 테스트해 봅시다:

    ELSIF run("red_led_output_self-check") THEN
      info("---------------------------------------------------------------");
      info("TEST CASE: red_led_output_self-check");
      info("---------------------------------------------------------------");
      WAIT UNTIL reset = '0';
      -- turn switch_1 on after 3 clk cycles and switch_2 after 13 clk cycles
      switch_driver(motor_start_in,C_CLK_PERIOD,3,13,-1);
    
      WAIT FOR C_CLK_PERIOD * 3; -- dummy wait
      info("===== TEST CASE FINISHED =====");
    

    다음과 같이 시뮬레이션을 실행합니다.

    python .\run.py *red_led* -v
    

    결과는 다음과 같습니다.

    참고 검사기가 이제 red_led_checker .

    우리는 YELLOW_LED에 대한 자체 검사 검사기를 설계하기 위해 동일한 접근 방식을 따를 수 있지만 이것은 독자를 위한 연습으로 남겨둘 것입니다. 그러나 check를 사용하여 GREEN_LED 기능을 확인하는 다음 다른 방법을 보여 드리겠습니다. , check_stable , check_nextcheck_equal 절차.

    처음부터 switch_3이 처음 켜질 때까지 GREEN_LED가 꺼져 있는지 확인하려면 check_stable을 사용하면 됩니다. 절차:

    -- check that GREEN_LED is low from start until switch_3 is ON.
    check_stable(clk, enable, led_low_start_event, 
                 motor_start_in.switch_3, motor_start_out.green_led,
                 result("GREEN_LED low before switch_3"));
    

    switch_3이 켜져 있을 때 GREEN_LED가 켜져 있는지 확인하는 한 가지 방법은 check_next을 사용하는 것입니다. 절차. start_event로 switch_3을 사용하여 호출합니다. , num_cks에 1 클럭 주기 할당 , 겹침 허용:

    -- check that GREEN_LED is high using check_next
    check_next(clock       => clk, 
               en          => green_next_en,
               start_event => motor_start_in.switch_3,
               expr        => motor_start_out.green_led,
               msg         => result("for green_led high using check_next"),
               num_cks     => 1,
               allow_overlapping   => true,
               allow_missing_start => false);
    

    중복을 허용했기 때문에 이 절차는 switch_3이 '1'일 때 클럭의 모든 상승 에지에서 트리거됩니다. t는 한 클럭 주기 후에 GREEN_LED가 '1'일 것으로 예상하고 이것이 우리가 원하는 것입니다.

    GREEN_LED 기능을 테스트하는 또 다른 방법은 클럭 버전의 check을 사용하는 것입니다. 이 프로시저에 대해 활성화로 switch_3의 지연된 버전이 있는 프로시저:

    switch_3_delayed <= motor_start_in.switch_3'delayed(C_CLK_PERIOD + 1 ps)
                        AND NOT enable;
    check(clock => clk,
          en    => switch_3_delayed,
          expr  => motor_start_out.green_led,
          msg   => result("for green_led high using delayed"));
    

    switch_3_delayed switch_3의 1 clock cycle 지연 신호입니다. 따라서 이 절차는 switch_3이 '1'이 된 후 항상 1 클럭 주기를 활성화하고 절차가 활성화되면 GREEN_LED가 '1'인지 확인합니다.

    switch_3_delayed 신호는 하나의 클럭 사이클만큼 지연된 버전 switch_3입니다. 따라서 이 절차는 switch_3이 '1'이 된 후 항상 한 클럭 주기를 활성화합니다. 활성화되면 절차는 GREEN_LED가 '1'인지 확인합니다.

    참고: switch_3_delayed에서 AND NOT 활성화 프로세스 접근 방식을 사용하지 않을 때 이 신호를 마스킹하는 것입니다.

    마지막으로 VHDL While 루프와 함께 전용 프로세스를 사용하여 GREEN_LED 검사를 수행할 수 있습니다.

    green_monitor_process : PROCESS
    BEGIN
      WAIT UNTIL enable = '1' AND 
                 motor_start_in.switch_1 = '1' AND
                 motor_start_in.switch_2 = '1' AND
                 motor_start_in.switch_3 = '1';
      WHILE motor_start_in.switch_3 = '1' LOOP
        WAIT UNTIL rising_edge(clk);
        WAIT FOR 1 ps;
        check_equal(led_out, STD_LOGIC_VECTOR'("100"), 
                    result("for led_out when switch_3 on"));
      END LOOP;
    END PROCESS green_monitor_process;
    

    이제 다음과 같이 GREEN_LED에 대한 테스트 케이스를 개발합니다.

    ELSIF run("switch_3_on_output_check") THEN
      info("-------------------------------------------------------------");
      info("TEST CASE: switch_3_on_output_check");
      info("-------------------------------------------------------------");
      info("check using a clocked check PROCEDURES");
      WAIT UNTIL reset = '0';
      switch_driver(motor_start_in,C_CLK_PERIOD,3,12,20);
      WAIT FOR C_CLK_PERIOD * 5;
      switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,-1);
      WAIT FOR C_CLK_PERIOD * 3; -- dummy wait
      info("-------------------------------------------------------------");
      
      info("check using check_next PROCEDURES");
      green_next_en <= '1';
      switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,10);
      WAIT FOR C_CLK_PERIOD * 5;
      switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,-1);
      WAIT FOR C_CLK_PERIOD * 3; -- dummy wait
      green_next_en <= '0';
      info("-------------------------------------------------------------");
      
      info("check using check_equal process");
      enable <= '1';
      switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,10);
      WAIT FOR C_CLK_PERIOD * 5;
      switch_driver(motor_start_in,C_CLK_PERIOD,-2,-2,-1);
      WAIT FOR C_CLK_PERIOD * 10; -- dummy wait
      info("===== TEST CASE FINISHED =====");
    

    테스트 케이스를 작성한 후 실행하여 다른 결과를 확인할 수 있습니다.

    자체 검사 방식의 테스트 케이스는 VHDL에 대한 지식이 없는 엔지니어라도 훨씬 더 읽기 쉽습니다.

    testbench 파일에는 다른 테스트 케이스가 있습니다. 다음 명령을 사용하여 시작할 수 있습니다.

    python .\run.py *motor_start_tb* --list
    

    콘솔에 출력되는 출력은 다음과 같습니다.

    tb_lib.motor_start_tb.switches_off_output_check
    tb_lib.motor_start_tb.switch_1_on_output_check
    tb_lib.motor_start_tb.switch_2_on_output_check
    tb_lib.motor_start_tb.red_led_output_self-check
    tb_lib.motor_start_tb.switch_3_on_output_check
    tb_lib.motor_start_tb.switch_2_error_output_check
    Listed 6 tests
    

    다음을 입력하여 모든 테스트 사례를 실행할 수 있습니다.

    python .\run.py
    

    결과는 다음과 같습니다.

    ==== Summary =============================================================
    pass tb_lib.motor_start_tb.switch_1_on_output_check    (0.4 seconds)
    pass tb_lib.motor_start_tb.switch_2_on_output_check    (0.4 seconds)
    pass tb_lib.motor_start_tb.red_led_output_self-check   (0.4 seconds)
    pass tb_lib.motor_start_tb.switch_3_on_output_check    (0.4 seconds)
    fail tb_lib.motor_start_tb.switches_off_output_check   (0.9 seconds)
    fail tb_lib.motor_start_tb.switch_2_error_output_check (0.4 seconds)
    ==========================================================================
    pass 4 of 6
    fail 2 of 6
    ==========================================================================
    Total time was 2.9 seconds
    Elapsed time was 2.9 seconds
    ==========================================================================
    Some failed!
    

    요약

    VUnit 프레임워크는 테스트 실행 자동화를 위한 고급 기능과 테스트벤치 개발을 위한 풍부한 라이브러리를 제공합니다. 또한 설계 엔지니어가 초기에 자주 설계를 테스트할 수 있습니다.

    VUnit은 또한 검증 엔지니어가 테스트벤치를 더 빠르고 쉽게 개발하고 실행하는 데 도움이 됩니다. 가장 중요한 것은 빠르고 가벼운 학습 곡선을 가지고 있다는 것입니다.

    여기서 어디로 가야 하나요

    • VUnit 문서
    • VUnit 블로그
    • VUnit gitter

    아래 양식을 사용하여 예제 프로젝트를 다운로드하십시오!


  • VHDL

    1. 코드 준비 컨테이너:클라우드에서 프로세스 자동화 도구 시작하기
    2. 세라믹 3D 프린팅 시작하기
    3. 기본 염료에 대해 알아보기!
    4. TJBot 시작하기
    5. RAK 831 Lora Gateway 및 RPi3 시작하기
    6. RAK831 LoRa 게이트웨이 및 RPi3 시작하기
    7. IoT 비즈니스 시작하기
    8. AI 보험 시작하기:입문 가이드
    9. Arduino 튜토리얼 01:시작하기
    10. My.Cat.com 시작하기