VHDL
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
C:\vunit
로 이동합니다. 다음 명령을 입력하십시오.python setup.py install
VUNIT_MODELSIM_PATH
:컴퓨터의 ModelSim에 대한 경로입니다.VUNIT_SIMULATOR
:모델심
아래 양식을 사용하여 예제 프로젝트와 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)가 있습니다.
일부 모터는 사용을 시작하기 전에 처음에 초기화가 필요합니다. 모터 시동 절차는 세 단계로 이루어집니다.
다음은 모터 시동 순서와 LED 표시등의 의미입니다.
튜토리얼의 이 부분은 다음 하위 섹션으로 구성됩니다.
각 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 테스트벤치를 생성하려면 테스트벤치 파일 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_start
및 motor_pkg
DESIGN_LIB
로 컴파일됩니다. .motor_start_tb
및 motor_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
, reset
및 led_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
:이 스위치는 예를 들어 오류를 확인하기 위해 테스트를 실행하지 않고 컴파일하려는 경우에 유용합니다.
종종 파도의 육안 검사가 필요합니다. 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 검사기 라이브러리를 사용하여 검증 엔지니어의 관점에서 더 많은 테스트 케이스를 개발할 것입니다.
이전 테스트 케이스와 달리 이 접근 방식은 테스트 케이스 내부에 드라이버가 있고 외부에 검사기가 있지만 테스트 케이스는 여전히 이를 제어합니다.
다음과 같은 확인 요구 사항이 있다고 가정해 보겠습니다.
DUT 섹션에서 우리는 다음을 알고 있습니다.
VUnit의 check_stable
을 사용하겠습니다. 확인 절차:
VUnit의 check_next
을 사용하겠습니다. 확인 절차:
check_stable :start_event
으로 시작하는 창 내에서 신호가 안정적인지 확인합니다. 신호 펄스 및 종료 end_event
신호 펄스.
check_next :start_event
이후에 여러 클록 사이클 후에 신호 ='1'인지 확인 신호 펄스.
start_event
및 end_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_event
및 end_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;
지연을 계산하기 위한 클록 주기와 클록 주기에서 각 스위치에 대해 원하는 지연을 지정하는 정수를 제공합니다.
clk_period
이후에 스위치를 켭니다. * sw_delay
).이제 다음과 같이 테스트 케이스 내에서 이 절차를 사용할 수 있습니다.
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에 대한 자체 검사 검사기를 설계하고 이를 위한 프로세스를 다음과 같이 사용합니다.
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';
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;
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_next
및 check_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은 또한 검증 엔지니어가 테스트벤치를 더 빠르고 쉽게 개발하고 실행하는 데 도움이 됩니다. 가장 중요한 것은 빠르고 가벼운 학습 곡선을 가지고 있다는 것입니다.
아래 양식을 사용하여 예제 프로젝트를 다운로드하십시오!
VHDL
촉각 센서와 관련하여 터치 스위치 회로는 가장 간단한 회로 중 하나입니다. 단순함에도 불구하고 이러한 놀라운 기술은 어디에나 있습니다. 예를 들어 터치 스크린은 디스플레이 상단에 있는 터치 스위치의 배열일 뿐입니다. 그들은 벽 스위치, 램프 및 공용 컴퓨터 터미널을 포함하여 다른 광범위한 응용 프로그램을 가지고 있습니다. 이 기사에서는 터치 스위치 회로에 대해 필요한 모든 정보를 제공합니다. 전기 또는 전자 장치에 현대적인 터치 기능을 추가하는 데 필요한 모든 것입니다. 터치 회로 그림 터치 스위치 회로는 어떻게 작동합니까
이 비디오는 저렴한 기성품 USB 카메라와 함께 Tormach ScanCAD 셰어웨어를 사용하여 공장을 2D 스캐닝 및 측정 시스템으로 전환하는 방법을 보여줍니다. CNC 스캐너 시작하기 이 비디오에서 CNC 기계공은 부품을 스캔한 다음 가공합니다.스캐닝 목적으로 사용할 수 있는 다양한 카메라에 대해 알려줍니다. Tormach CNC 스캐너 소개 Tormach CNC 스캐너는 PCNC를 고해상도 2D 광학 스캐너 및 리버스 엔지니어링 시스템으로 전환합니다. 이것은 도구 제작, 예술성, 수리 및 복원, 정밀 측정, 품질 보증