vhdl 튜토리얼
vhdl 시작하기
수색…
비고
VHDL은 VHSIC (초고속 집적 회로) HDL (Hardware Description Language)의 복합 약어입니다. 하드웨어 설명 언어로서 주로 회로를 설명하거나 모델링하는 데 사용됩니다. VHDL은 동시 동작을 모델링 할 때 도입 된 모호성을 제거하는 실행 모델과 함께 동시 및 순차 동작을 쉽게 설명하는 언어 구조를 제공하므로 회로를 설명하는 데 이상적인 언어입니다.
VHDL은 일반적으로 시뮬레이션과 합성의 두 가지 상황에서 해석됩니다. 합성을 위해 해석 될 때, 코드는 모델링 된 동등한 하드웨어 요소로 변환 (합성)됩니다. 일반적으로 VHDL의 하위 집합 만 합성 중에 사용할 수 있으며 지원되는 언어 구성은 표준화되지 않습니다. 이것은 사용 된 합성 엔진과 대상 하드웨어 장치의 기능입니다. VHDL이 시뮬레이션을 위해 해석 될 때, 모든 언어 구성은 하드웨어 동작을 모델링하는데 이용 가능합니다.
버전
번역 | 출시일 |
---|---|
IEEE 1076-1987 | 1988-03-31 |
IEEE 1076-1993 | 1994-06-06 |
IEEE 1076-2000 | 2000-01-30 |
IEEE 1076-2002 | 2002-05-17 |
IEEE 1076c-2007 | 2007-09-05 |
IEEE 1076-2008 | 2009-01-26 |
설치 또는 설정
VHDL 프로그램을 시뮬레이션하거나 합성 할 수 있습니다. 시뮬레이션은 다른 프로그래밍 언어의 대부분의 실행과 유사합니다. 합성은 VHDL 프로그램을 논리 게이트 네트워크로 변환합니다. 많은 VHDL 시뮬레이션 및 합성 툴은 상용 EDA (Electronic Design Automation) 제품군의 일부입니다. Verilog, SystemVerilog 또는 SystemC와 같은 다른 하드웨어 설명 언어 (HDL)도 자주 처리합니다. 일부 무료 및 오픈 소스 응용 프로그램이 있습니다.
VHDL 시뮬레이션
GHDL 은 아마도 가장 성숙한 무료 오픈 소스 VHDL 시뮬레이터 일 것이다. 사용 된 백엔드에 따라 gcc
, llvm
또는 mcode
세 가지 종류가 있습니다. 다음 예제는 GHDL ( mcode
버전)과 Mentor Graphics의 상용 HDL 시뮬레이터 인 mcode
GNU / Linux 운영 체제에서 사용하는 방법을 보여줍니다. 상황은 다른 도구 및 다른 운영 체제와 매우 유사합니다.
안녕하세요 세계
다음을 포함하는 hello_world.vhd
파일을 작성하십시오.
-- File hello_world.vhd
entity hello_world is
end entity hello_world;
architecture arc of hello_world is
begin
assert false report "Hello world!" severity note;
end architecture arc;
VHDL 컴파일 유닛은 혼자 컴파일 할 수있는 완전한 VHDL 프로그램입니다. 엔티티 는 디지털 회로의 외부 인터페이스, 즉 입력 및 출력 포트를 설명하는 데 사용되는 VHDL 컴파일 단위입니다. 이 예에서 entity
이름은 hello_world
이며 비어 있습니다. 우리가 모델링하는 회로는 블랙 박스이며 입력이없고 출력도 없습니다. 아키텍처 는 또 다른 유형의 컴파일 단위입니다. 그것들은 항상 entity
와 연관되어 있으며 디지털 회로의 동작을 설명하는 데 사용됩니다. 한 엔티티 는 엔티티 의 동작을 설명하기 위해 하나 이상의 아키텍처 를 가질 수 있습니다. 이 예에서 엔티티 는 하나의 VHDL 문만 포함하는 arc
라는 아키텍처 하나와 연결됩니다.
assert false report "Hello world!" severity note;
이 문장은 시뮬레이션의 시작 부분에서 실행되어 Hello world!
인쇄합니다 Hello world!
메시지를 표준 출력에 표시합니다. 더 이상 할 일이 없으므로 시뮬레이션이 종료됩니다. 우리가 쓴 VHDL 소스 파일에는 두 개의 컴파일 단위가 들어 있습니다. 두 개의 서로 다른 파일로 구분할 수는 있지만 서로 다른 파일로 분할 할 수는 없습니다. 컴파일 단위는 하나의 소스 파일에 완전히 포함되어야합니다. 이 아키텍처는 논리 게이트로 직접 변환 할 수있는 기능을 설명하지 않기 때문에 합성 할 수 없습니다.
분석하고 GHDL로 프로그램을 실행하십시오 :
$ mkdir gh_work
$ ghdl -a --workdir=gh_work hello_world.vhd
$ ghdl -r --workdir=gh_work hello_world
hello_world.vhd:6:8:@0ms:(assertion note): Hello world!
gh_work
디렉토리는 GHDL이 생성하는 파일을 저장하는 곳입니다. 이것은 --workdir=gh_work
옵션이 말하는 것입니다. 분석 단계에서는 구문의 정확성을 검사하고 소스 파일에서 발견 된 편집 단위를 설명하는 텍스트 파일을 생성합니다. 실행 단계는 실제로 프로그램을 컴파일, 링크 및 실행합니다. GHDL의 mcode
버전에서는 바이너리 파일이 생성되지 않습니다. 프로그램은 우리가 시뮬레이션 할 때마다 다시 컴파일됩니다. gcc
또는 llvm
버전은 다르게 작동합니다. ghdl -r
은 ghdl -a
처럼 VHDL 소스 파일의 이름을 사용하지 않고 컴파일 단위의 이름을 사용합니다. 우리의 경우에는 entity
의 이름을 전달합니다. 연결된 architecture
가 하나뿐이므로 시뮬레이션 할 모델을 지정할 필요가 없습니다.
Modelsim 사용 :
$ vlib ms_work
$ vmap work ms_work
$ vcom hello_world.vhd
$ vsim -c hello_world -do 'run -all; quit'
...
# ** Note: Hello world!
# Time: 0 ns Iteration: 0 Instance: /hello_world
...
vlib
, vmap
, vcom
및 vsim
은 vmap
에서 제공하는 네 가지 명령입니다. vlib
은 생성 된 파일이 저장 될 디렉토리 ( ms_work
)를 만듭니다. vmap
은 vlib
로 작성된 디렉토리를 논리 이름 ( work
)과 연관시킵니다. vcom
은 VHDL 소스 파일을 컴파일하고 기본적으로 결과를 work
논리 이름과 연관된 디렉토리에 저장합니다. 마지막으로 vsim
은 프로그램을 시뮬레이트하고 GHDL과 동일한 출력을 생성합니다. vsim
요구하는 것은 소스 파일이 아니라 이미 컴파일 된 컴파일 유닛의 이름입니다. -c
옵션은 시뮬레이터가 기본 그래픽 사용자 인터페이스 (GUI) 모드 대신 명령 행 모드에서 실행되도록 지시합니다. -do
옵션은 디자인을로드 한 후 실행할 TCL 스크립트를 전달하는 데 사용됩니다. TCL은 EDA 도구에서 자주 사용되는 스크립팅 언어입니다. -do
옵션의 값은 파일의 이름 -do
예의 경우 TCL 명령 문자열 일 수 있습니다. run -all; quit
또는 영원히 영원히 지속되는 경우 - - 다음 종료 자연스럽게 끝날 때까지 시뮬레이션을 실행하기 위해 시뮬레이터를 지시합니다.
동기 카운터
-- File counter.vhd
-- The entity is the interface part. It has a name and a set of input / output
-- ports. Ports have a name, a direction and a type. The bit type has only two
-- values: '0' and '1'. It is one of the standard types.
entity counter is
port(
clock: in bit; -- We are using the rising edge of CLOCK
reset: in bit; -- Synchronous and active HIGH
data: out natural -- The current value of the counter
);
end entity counter;
-- The architecture describes the internals. It is always associated
-- to an entity.
architecture sync of counter is
-- The internal signals we use to count. Natural is another standard
-- type. VHDL is not case sensitive.
signal current_value: natural;
signal NEXT_VALUE: natural;
begin
-- A process is a concurrent statement. It is an infinite loop.
process
begin
-- The wait statement is a synchronization instruction. We wait
-- until clock changes and its new value is '1' (a rising edge).
wait until clock = '1';
-- Our reset is synchronous: we consider it only at rising edges
-- of our clock.
if reset = '1' then
-- <= is the signal assignment operator.
current_value <= 0;
else
current_value <= next_value;
end if;
end process;
-- Another process. The sensitivity list is another way to express
-- synchronization constraints. It (approximately) means: wait until
-- one of the signals in the list changes and then execute the process
-- body. Sensitivity list and wait statements cannot be used together
-- in the same process.
process(current_value)
begin
next_value <= current_value + 1;
end process;
-- A concurrent signal assignment, which is just a shorthand for the
-- (trivial) equivalent process.
data <= current_value;
end architecture sync;
안녕 세상
고전적인 "Hello world!"를 인쇄하는 방법은 여러 가지가 있습니다. VHDL의 메시지. 가장 단순한 것은 아마도 다음과 같습니다.
-- File hello_world.vhd
entity hello_world is
end entity hello_world;
architecture arc of hello_world is
begin
assert false report "Hello world!" severity note;
end architecture arc;
동기식 카운터를위한 시뮬레이션 환경
시뮬레이션 환경
VHDL 설계 (Test Under Test 또는 DUT)에 대한 시뮬레이션 환경은 최소한 다음과 같은 또 다른 VHDL 설계입니다.
- DUT의 입력 및 출력 포트에 해당하는 신호를 선언합니다.
- DUT를 인스턴스화하고 포트를 선언 된 신호에 연결합니다.
- DUT의 입력 포트에 연결된 신호를 구동하는 프로세스를 인스턴스화합니다.
선택적으로, 시뮬레이션 환경은 인터페이스상의 트래픽 생성기, 통신 프로토콜을 점검하는 모니터, DUT 출력의 자동 검증기 등과 같이 DUT 이외의 다른 설계를 인스턴스화 할 수 있습니다.
시뮬레이션 환경이 분석되고 정교화되고 실행됩니다. 대부분의 시뮬레이터는 관찰 할 신호 세트를 선택하고, 그래픽 파형을 플롯하고, 소스 코드에 중단 점을 넣고, 소스 코드에서 단계를 수행 할 수있는 가능성을 제공합니다.
이론적으로 시뮬레이션 환경은 견고한 비 회귀 테스트로 사용할 수 있어야합니다. 즉, DUT 사양의 위반을 자동으로 감지하고, 유용한 오류 메시지를보고하고, DUT 기능을 합리적인 범위로 보장해야합니다. 이러한 시뮬레이션 환경을 사용할 수 있으면 시뮬레이션 트레이스에 대한 지루하고 오류가 발생하기 쉬운 육안 검사 없이도 DUT의 모든 변경 사항에 대해 재실행하여 기능적으로 올바른지 확인할 수 있습니다.
실제로 이상적인 시뮬레이션 환경을 설계하는 것은 쉽지 않습니다. 종종 DUT 자체를 설계하는 것보다 어렵거나 더 어렵습니다.
이 예제에서는 동기 카운터 예제에 대한 시뮬레이션 환경을 제시합니다. GHDL 및 Modelsim 을 사용하여 실행하는 방법과 GHDL이있는 GTKWave 및 Modelsim 이 내장 된 파형 뷰어를 사용하여 그래픽 파형을 관찰하는 방법을 보여줍니다. 그런 다음 시뮬레이션의 흥미로운면을 논의합니다 : 어떻게 멈추게할까요?
동기식 카운터의 첫 번째 시뮬레이션 환경
동기식 카운터에는 2 개의 입력 포트와 1 개의 출력 포트가 있습니다. 매우 간단한 시뮬레이션 환경은 다음과 같습니다.
-- File counter_sim.vhd
-- Entities of simulation environments are frequently black boxes without
-- ports.
entity counter_sim is
end entity counter_sim;
architecture sim of counter_sim is
-- One signal per port of the DUT. Signals can have the same name as
-- the corresponding port but they do not need to.
signal clk: bit;
signal rst: bit;
signal data: natural;
begin
-- Instantiation of the DUT
u0: entity work.counter(sync)
port map(
clock => clk,
reset => rst,
data => data
);
-- A clock generating process with a 2ns clock period. The process
-- being an infinite loop, the clock will never stop toggling.
process
begin
clk <= '0';
wait for 1 ns;
clk <= '1';
wait for 1 ns;
end process;
-- The process that handles the reset: active from beginning of
-- simulation until the 5th rising edge of the clock.
process
begin
rst <= '1';
for i in 1 to 5 loop
wait until rising_edge(clk);
end loop;
rst <= '0';
wait; -- Eternal wait. Stops the process forever.
end process;
end architecture sim;
GHDL로 시뮬레이션
GHDL로 이것을 컴파일하고 시뮬레이션 해보자.
$ mkdir gh_work
$ ghdl -a --workdir=gh_work counter_sim.vhd
counter_sim.vhd:27:24: unit "counter" not found in 'library "work"'
counter_sim.vhd:50:35: no declaration for "rising_edge"
그러면 오류 메시지가 우리에게 두 가지 중요한 것을 알려줍니다.
- GHDL 분석기는 우리 설계가
counter
라는 엔티티를 인스턴스화했지만이 엔티티가 라이브러리work
에서 발견되지 않음을 발견했습니다.counter_sim
앞에counter
를 컴파일하지 않았기 때문입니다. 엔티티를 인스턴스화하는 VHDL 디자인을 컴파일 할 때 맨 아래 레벨은 항상 최상위 레벨보다 먼저 컴파일해야합니다 (계층 적 디자인은 하향식으로 컴파일 될 수 있지만 엔티티가 아닌component
를 인스턴스화하는 경우에만 컴파일해야합니다). - 디자인에서 사용하는
rising_edge
함수는 정의되어 있지 않습니다. 이는이 기능이 VHDL 2008에 도입되었으며 GHDL에이 버전의 언어 사용을 말하지 않았기 때문입니다 (기본적으로 VHDL 1987 구문을 허용하는 VHDL 1993 사용).
두 가지 오류를 수정하고 시뮬레이션을 시작하십시오.
$ ghdl -a --workdir=gh_work --std=08 counter.vhd counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
^C
분석 및 시뮬레이션에는 --std=08
옵션이 필요합니다. 소스 파일이 아닌 counter_sim
, architecture sim
에 대한 시뮬레이션을 시작했습니다.
우리의 시뮬레이션 환경은 절대로 끝나지 않는 프로세스 (시계를 생성하는 프로세스)를 가지고 있기 때문에 시뮬레이션이 멈추지 않고 수동으로 중단해야합니다. 대신에 --stop-time
옵션을 사용하여 중지 시간을 지정할 수 있습니다.
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim --stop-time=60ns
ghdl:info: simulation stopped by --stop-time
그렇듯이, 시뮬레이션은 DUT의 동작에 대해 많이 알려주지 않습니다. 신호의 값 변경을 파일에 덤프하자.
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim --stop-time=60ns --vcd=counter_sim.vcd
Vcd.Avhpi_Error!
ghdl:info: simulation stopped by --stop-time
(오류 메시지를 무시하십시오, 이것은 GHDL에서 고쳐야 할 결과이며 아무런 결과가 없습니다). counter_sim.vcd
파일이 만들어졌습니다. 시뮬레이션 중에 VCD (ASCII) 형식의 모든 신호 변경 사항을 포함합니다. GTKWave는 해당 그래픽 파형을 보여줍니다 :
$ gtkwave counter_sim.vcd
카운터가 예상대로 작동하는 것을 볼 수 있습니다.
Modelsim으로 시뮬레이션하기
원리는 Modelsim과 정확히 같습니다.
$ vlib ms_work
...
$ vmap work ms_work
...
$ vcom -nologo -quiet -2008 counter.vhd counter_sim.vhd
$ vsim -voptargs="+acc" 'counter_sim(sim)' -do 'add wave /*; run 60ns'
vsim
전달 된 -voptargs="+acc"
옵션에 유의하십시오. 시뮬레이터가 data
신호를 최적화하지 못하게하여 파형에서 볼 수 있습니다.
정상적으로 종료되는 시뮬레이션
두 시뮬레이터 모두 우리는 결코 끝나지 않는 시뮬레이션을 중단 시키거나 전용 옵션으로 정지 시간을 지정해야했습니다. 이것은별로 편리하지 않습니다. 대부분의 경우 시뮬레이션의 종료 시간은 예측하기가 어렵습니다. 시뮬레이션 조건의 VHDL 코드 내부에서 특정 조건에 도달하면 (예 : 카운터의 현재 값이 20에 도달하는 경우) 시뮬레이션을 중지하는 것이 훨씬 더 좋습니다. 리셋을 처리하는 프로세스 :
process
begin
rst <= '1';
for i in 1 to 5 loop
wait until rising_edge(clk);
end loop;
rst <= '0';
loop
wait until rising_edge(clk);
assert data /= 20 report "End of simulation" severity failure;
end loop;
end process;
data
가 20과 다른 한 시뮬레이션은 계속됩니다. data
가 20에 도달하면 시뮬레이션은 오류 메시지와 함께 충돌합니다.
$ ghdl -a --workdir=gh_work --std=08 counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
counter_sim.vhd:90:24:@51ns:(assertion failure): End of simulation
ghdl:error: assertion failed
from: process work.counter_sim(sim2).P1 at counter_sim.vhd:90
ghdl:error: simulation failed
시뮬레이션 환경 만 다시 컴파일 했으므로 변경된 유일한 디자인이며 최상위 레벨입니다. counter.vhd
만 수정했다면 counter.vhd
가 변경 되었기 때문에 counter.vhd
- counter_sim.vhd
를 counter.vhd
의존하기 때문에 다시 컴파일해야했습니다.
오류 메시지와 함께 시뮬레이션 충돌이 매우 우아하지 않습니다. 시뮬레이션 메시지를 자동으로 분석하여 자동 비 회귀 테스트가 통과되었는지 여부를 결정할 때 문제가 될 수 있습니다. 더 좋고 훨씬 더 우아한 해결책은 조건에 도달했을 때 모든 프로세스를 중지하는 것입니다. 예를 들어 boolean
End of Simulation (EOF) 신호를 추가 eof
작업을 수행 할 수 있습니다. 기본적으로 시뮬레이션 시작시 false
로 초기화됩니다. 우리의 프로세스 중 하나는 시뮬레이션을 끝낼 시간이되면 true
설정합니다. 다른 모든 프로세스는이 신호를 모니터하고 그것이 true
이 될 때 wait
.
signal eos: boolean;
...
process
begin
clk <= '0';
wait for 1 ns;
clk <= '1';
wait for 1 ns;
if eos then
report "End of simulation";
wait;
end if;
end process;
process
begin
rst <= '1';
for i in 1 to 5 loop
wait until rising_edge(clk);
end loop;
rst <= '0';
for i in 1 to 20 loop
wait until rising_edge(clk);
end loop;
eos <= true;
wait;
end process;
$ ghdl -a --workdir=gh_work --std=08 counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
counter_sim.vhd:120:24:@50ns:(report note): End of simulation
마지막으로 중요한 것은 표준 패키지 env
와 함께 선언 된 stop
및 finish
절차가 포함 된 VHDL 2008에 도입 된 더 나은 솔루션입니다.
use std.env.all;
...
process
begin
rst <= '1';
for i in 1 to 5 loop
wait until rising_edge(clk);
end loop;
rst <= '0';
for i in 1 to 20 loop
wait until rising_edge(clk);
end loop;
finish;
end process;
$ ghdl -a --workdir=gh_work --std=08 counter_sim.vhd
$ ghdl -r --workdir=gh_work --std=08 counter_sim sim
simulation finished @49ns
신호 대 변수, VHDL의 시뮬레이션 의미에 대한 간략한 개요
이 예제는 VHDL 언어의 가장 기본적인 측면 중 하나 인 시뮬레이션 의미를 다룹니다. VHDL 초보자를 대상으로하며 많은 세부 사항이 생략 된 간단한보기 (연기 된 프로세스, VHDL 절차 인터페이스, 공유 변수 ...)를 제공합니다. 실제 완전한 의미 체계에 관심이있는 독자는 LRM (Language Reference Manual)을 참조해야합니다.
신호 및 변수
대부분의 고전적인 명령형 프로그래밍 언어는 변수를 사용합니다. 그것들은 값 컨테이너입니다. 대입 연산자는 변수에 값을 저장하는 데 사용됩니다.
a = 15;
현재 변수에 저장된 값을 읽고 다른 명령문에서 사용할 수 있습니다.
if(a == 15) { print "Fifteen" }
VHDL은 또한 변수를 사용하며 대부분의 명령형 언어에서와 똑같은 역할을합니다. 그러나 VHDL은 또 다른 종류의 가치 컨테이너 인 신호를 제공합니다. 신호는 값을 저장하고 할당 및 읽을 수도 있습니다. 신호에 저장할 수있는 값의 유형은 변수와 거의 같습니다.
그렇다면 두 가지 종류의 가치 컨테이너가있는 이유는 무엇입니까? 이 질문에 대한 대답은 필수적이며 언어의 핵심입니다. 변수와 신호의 차이를 이해하는 것이 VHDL에서 무엇인가를 프로그래밍하기 전에해야 할 첫 번째 일입니다.
이러한 차이점을 구체적인 예를 들어 설명하겠습니다.
참고 : 다음 코드 스 니펫은 모두 프로세스의 일부입니다. 프로세스가 어떤 것인지 나중에 알 수 있습니다.
tmp := a;
a := b;
b := tmp;
변수 a
와 b
바꿉니다. 이 3 개 개의 명령어를 실행 한 후, 새로운 콘텐츠 a
오래 콘텐츠 인 b
반대로하고. 대부분의 프로그래밍 언어 에서처럼 세 번째 임시 변수 ( tmp
)가 필요합니다. 변수 대신에 신호를 교환하고 싶다면 다음과 같이 작성하십시오 :
r <= s;
s <= r;
또는:
s <= r;
r <= s;
동일한 결과와 세 번째 임시 신호가 필요하지 않습니다!
참고 : VHDL 신호 할당 연산자
<=
는 변수 할당 연산자:=
와 다릅니다.
print
서브 프로그램이 해당 매개 변수의 10 진수 표현을 print
한다고 가정하는 두 번째 예제를 살펴 보겠습니다. a
가 정수 변수이고 현재 값이 15이면 다음을 실행합니다.
a := 2 * a;
a := a - 5;
a := a / 5;
print(a);
인쇄 할 것입니다 :
5
디버거에서이 단계를 단계별로 실행하면 초기 값 15에서 30, 25 및 마지막으로 5 a
바뀌는 값을 볼 수 있습니다.
그러나 s
가 정수 신호이고 현재 값이 15이면 다음을 실행합니다.
s <= 2 * s;
s <= s - 5;
s <= s / 5;
print(s);
wait on s;
print(s);
인쇄 할 것입니다 :
15
3
우리는 디버거에서 단계로이 단계를 실행하면 우리는 어떤 값 변경 볼 수 없습니다 s
때까지 wait
명령. 또한 s
의 최종 값은 15, 30, 25 또는 5가 아니라 3이됩니다!
이 이상한 동작은 디지털 하드웨어의 근본적인 병렬 특성 때문입니다. 다음 섹션에서 살펴 보겠습니다.
병행
VHDL은 하드웨어 설명 언어 (HDL)로, 사실상 병렬입니다. VHDL 프로그램은 병렬로 실행되는 순차 프로그램 모음입니다. 이러한 순차 프로그램을 프로세스라고합니다.
P1: process
begin
instruction1;
instruction2;
...
instructionN;
end process P1;
P2: process
begin
...
end process P2;
프로세스는 모델링하는 하드웨어와 마찬가지로 끝이 없습니다. 무한 루프입니다. 마지막 명령을 실행 한 후 실행은 첫 번째 명령으로 계속됩니다.
하나의 형식 또는 다른 병렬 처리를 지원하는 프로그래밍 언어와 마찬가지로, 스케줄러는 VHDL 시뮬레이션 중에 실행할 프로세스를 결정할 책임이 있습니다. 또한이 언어는 프로세스 간 통신 및 동기화를위한 특정 구성을 제공합니다.
스케줄링
스케줄러는 모든 프로세스의 목록을 유지 관리하며, 각각에 대해 running
, run-able
또는 suspended
될 수있는 현재 상태를 기록합니다. running
상태에는 현재 실행 된 프로세스가 하나만 있습니다. 현재 실행중인 프로세스가 wait
명령을 실행하지 않는 한, 실행중인 프로세스가 계속 실행되고 다른 프로세스가 실행되지 않도록합니다. VHDL 스케줄러는 선점 형이 아닙니다. 자체를 일시 중단하고 다른 프로세스를 실행시키는 것은 각 프로세스의 책임입니다. 이것은 VHDL 초보자가 종종 겪게되는 문제 중 하나입니다. 자유 실행 프로세스입니다.
P3: process
variable a: integer;
begin
a := s;
a := 2 * a;
r <= a;
end process P3;
주 : 변수
a
는 국지적으로 선언되고 신호s
와r
은 상위 레벨에서 선언됩니다. VHDL 변수는 선언하고 다른 프로세스에서 볼 수없는 프로세스의 로컬 변수입니다. 또 다른 프로세스는 a라는 변수를 선언 할 수도a
,P3
의 변수와 같은 변수가 아닐 수도 있습니다.
스케줄러가 P3
프로세스를 다시 시작하자마자 시뮬레이션이 멈추고 시뮬레이션의 현재 시간이 더 이상 진행되지 않으며이를 막을 수있는 유일한 방법은 시뮬레이션을 중단하거나 중단하는 것입니다. 그 이유는 P3
가 wait
문을 가지고 있지 않기 때문에 3 개의 명령을 반복하여 running
상태를 영원히 유지할 수 있기 때문입니다. 다른 과정은 이제까지이 경우에도 실행할 수있는 기회를 부여되지 않습니다 run-able
.
wait
문을 포함하는 프로세스조차도 같은 문제를 일으킬 수 있습니다.
P4: process
variable a: integer;
begin
a := s;
a := 2 * a;
if a = 16 then
wait on s;
end if;
r <= a;
end process P4;
참고 : VHDL 항등 연산자는
=
입니다.
신호 s
의 값이 3 인 동안 프로세스 P4
가 재개되면, a = 16
조건이 결코 참이되지 않기 때문에 영원히 실행됩니다.
VHDL 프로그램에 그러한 병리학 적 과정이 없다고 가정합시다. 실행중인 프로세스가 wait
명령을 실행하면 즉시 실행이 일시 중단되고 스케줄러는 suspended
상태로 만듭니다. wait
명령은 프로세스가 다시 run-able
하게되는 조건도 전달합니다. 예:
wait on s;
신호 s
값이 바뀔 때까지 나를 멈추게하는 것을 의미한다. 이 조건은 스케줄러에 의해 기록됩니다. 스케줄러는 그 사이에 다른 프로세스를 선택 run-able
,에 넣 running
상태를 생성하고 실행한다. run-able
모든 프로세스가 실행되고 일시 중단 될 때까지 동일한 작업이 반복됩니다.
중요 사항 : 여러 프로세스가
run-able
가능할 때 VHDL 표준은 스케줄러가run-able
프로세스를 선택하는 방법을 지정하지 않습니다. 결과적으로 시뮬레이터, 버전, 운영체제 등에 따라 동일한 VHDL 모델의 두 시뮬레이션이 한꺼번에 다른 선택을하고 실행할 다른 프로세스를 선택할 수 있습니다. 이 선택이 시뮬레이션 결과에 영향을 미쳤다면 VHDL이 비 결정적이라고 말할 수 있습니다. 비결정론은 일반적으로 바람직하지 않으므로 비 결정적 상황을 피하는 것은 프로그래머의 책임입니다. 다행히 VHDL이이를 처리하고 신호가 그림으로 들어오는 곳입니다.
신호 및 프로세스 간 통신
VHDL은 두 가지 특정 특성을 사용하여 비결정론을 피합니다.
- 프로세스는 신호를 통해서만 정보를 교환 할 수 있습니다.
signal r, s: integer; -- Common to all processes
...
P5: process
variable a: integer; -- Different from variable a of process P6
begin
a := s + 1;
r <= a;
a := r + 1;
wait on s;
end process P5;
P6: process
variable a: integer; -- Different from variable a of process P5
begin
a := r + 1;
s <= a;
wait on r;
end process P6;
참고 : VHDL의 코멘트에서 연장
--
라인의 끝.
- VHDL 신호의 값은 프로세스 실행 중에 변경되지 않습니다.
신호가 할당 될 때마다 할당 된 값은 스케줄러에 의해 기록되지만 신호의 현재 값은 변경되지 않습니다. 할당 된 직후에 새로운 가치를 취하는 변수의 또 다른 주요 차이점입니다.
위의 프로세스 P5
의 실행을 살펴보고 스케줄러에 의해 재개 될 때 a=5
, s=1
및 r=0
이라고 가정합니다. 명령 실행 후 a := s + 1;
, 변수 a
의 값이 변경되어 2 (1 + 1)이됩니다. 다음 명령어를 실행할 때 r <= a;
그것은 r
할당 된 a
(2)의 새로운 값입니다. 그러나 r
신호 인의 현재 값 r
그래서 여전히 0이고, 실행시, a := r + 1;
직감이 말할 때 변수 a
는 (즉시) 값 1 (0 + 1)이 아니라 3 (2 + 1)이됩니다.
신호 r
때 정말 새로운 가치가 걸릴까요? 스케줄러가 실행 가능한 모든 프로세스를 실행하면 모두 일시 중단됩니다. 이는 또한 하나의 델타 사이클 이후 로 언급됩니다. 스케줄러는 신호에 할당 된 모든 값을보고 신호 값을 실제로 업데이트합니다. VHDL 시뮬레이션은 실행 단계와 신호 업데이트 단계의 교대입니다. 실행 단계에서 신호 값이 고정됩니다. 기호 적으로, 우리는 실행 단계와 다음 신호 업데이트 단계 사이에 델타 가 경과했다고 말합니다. 이것은 실시간이 아닙니다. 델타 주기에는 물리적 지속 기간이 없습니다.
지연된 신호 업데이트 메커니즘 덕분에 VHDL이 결정적입니다. 프로세스는 신호와 만 통신 할 수 있으며 프로세스가 실행되는 동안 신호는 변경되지 않습니다. 따라서 프로세스 실행 순서는 중요하지 않습니다. 외부 환경 (신호)은 실행 중에 변경되지 않습니다. 초기 상태가 P5.a=5
, P6.a=10
, s=17
, r=0
이고 프로세스가 P5
먼저 실행하고 P6
다음으로 실행하기로 결정한 프로세스 P5
및 P6
의 이전 예제에서이를 보여 P6.a=10
. 다음 표는 두 변수의 값, 각 프로세스의 각 명령어를 실행 한 후의 신호의 현재 값과 다음 값을 보여줍니다.
프로세스 / 지시 | P5.a | P6.a | s.current | s.next | r.current | r.next |
---|---|---|---|---|---|---|
초기 상태 | 5 | 10 | 17 | 0 | ||
P5 / a := s + 1 | 18 | 10 | 17 | 0 | ||
P5 / r <= a | 18 | 10 | 17 | 0 | 18 | |
P5 / a := r + 1 | 1 | 10 | 17 | 0 | 18 | |
P5 / wait on s | 1 | 10 | 17 | 0 | 18 | |
P6 / a := r + 1 | 1 | 1 | 17 | 0 | 18 | |
P6 / s <= a | 1 | 1 | 17 | 1 | 0 | 18 |
P6 / wait on r | 1 | 1 | 17 | 1 | 0 | 18 |
신호 업데이트 후 | 1 | 1 | 1 | 18 |
동일한 초기 조건에서 스케줄러가 P6
먼저 실행하고 P5
다음으로 실행하도록 결정하면 :
프로세스 / 지시 | P5.a | P6.a | s.current | s.next | r.current | r.next |
---|---|---|---|---|---|---|
초기 상태 | 5 | 10 | 17 | 0 | ||
P6 / a := r + 1 | 5 | 1 | 17 | 0 | ||
P6 / s <= a | 5 | 1 | 17 | 1 | 0 | |
P6 / wait on r | 5 | 1 | 17 | 1 | 0 | |
P5 / a := s + 1 | 18 | 1 | 17 | 1 | 0 | |
P5 / r <= a | 18 | 1 | 17 | 1 | 0 | 18 |
P5 / a := r + 1 | 1 | 1 | 17 | 1 | 0 | 18 |
P5 / wait on s | 1 | 1 | 17 | 1 | 0 | 18 |
신호 업데이트 후 | 1 | 1 | 1 | 18 |
보시다시피 두 프로세스를 실행 한 후에 결과는 실행 순서에 관계없이 동일합니다.
이 반 직관적 인 신호 할당 의미론은 VHDL 초보자가 자주 접하는 두 번째 유형의 문제입니다. 하나의 델타주기만큼 지연되어 할당이 명백하게 작동하지 않습니다. 프로세스를 실행하는 경우 P5
이후 단계별 디버거하여, r
18가 할당되었으며 a
할당 된 r + 1
, 하나의 값을 기대할 수 a
19이지만 디버거 완고 말한다 r=0
및 a=1
...
참고 : 같은 실행 단계에서 동일한 신호를 여러 번 지정할 수 있습니다. 이 경우 신호의 다음 값을 결정하는 것은 마지막 할당입니다. 다른 임무는 아무런 효과가 없습니다.
우리의 이해를 점검 할 시간입니다 : 우리의 가장 첫 번째 교환 예로 돌아가서 그 이유를 이해해보십시오 :
process
begin
---
s <= r;
r <= s;
---
end process;
세 번째 임시 신호가 필요없이 실제로 신호 r
과 s
를 교환합니다. 이유는 무엇입니까?
process
begin
---
r <= s;
s <= r;
---
end process;
엄격하게 동일 할 것입니다. s
가 정수 신호이고 현재 값이 15이고, 우리가 다음을 실행하는 이유를 이해하십시오.
process
begin
---
s <= 2 * s;
s <= s - 5;
s <= s / 5;
print(s);
wait on s;
print(s);
---
end process;
신호 s
두 가지 첫 번째 할당은 효과가 없습니다. 왜 s
가 마침내 3으로 지정되고 두 인쇄 된 값이 15와 3인지 설명합니다.
물리적 시간
하드웨어를 모델링하기 위해 어떤 작업에 의해 취해지는 물리적 인 시간을 모델링하는 것이 매우 유용합니다. 다음은 VHDL에서이 작업을 수행하는 방법의 예입니다. 이 예제는 동기식 카운터를 모델링하며 컴파일되고 시뮬레이션 될 수있는 완전 독립형 VHDL 코드입니다.
-- File counter.vhd
entity counter is
end entity counter;
architecture arc of counter is
signal clk: bit; -- Type bit has two values: '0' and '1'
signal c, nc: natural; -- Natural (non-negative) integers
begin
P1: process
begin
clk <= '0';
wait for 10 ns; -- Ten nano-seconds delay
clk <= '1';
wait for 10 ns; -- Ten nano-seconds delay
end process P1;
P2: process
begin
if clk = '1' and clk'event then
c <= nc;
end if;
wait on clk;
end process P2;
P3: process
begin
nc <= c + 1 after 5 ns; -- Five nano-seconds delay
wait on c;
end process P3;
end architecture arc;
프로세스 P1
에서 우리는 지금까지 보았던 것처럼 신호 값이 바뀔 때까지 기다리지 않고 주어진 시간 동안 기다리기 위해 wait
명령을 사용하지 않습니다. 이 프로세스는 클럭 생성기를 모델링합니다. 신호 clk
는 우리 시스템의 클록이며주기 20 ns (50 MHz)로 주기적이며 듀티 사이클을가집니다.
프로세스 P2
는 clk
의 상승 에지가 방금 발생한 경우 입력 nc
의 값을 출력 c
할당 한 다음 clk
의 다음 값 변경을 기다리는 레지스터를 모델링합니다.
프로세스 P3
은 입력 c
의 값을 1 씩 증가시켜 5 ns의 물리적 지연으로 출력 nc
...에 할당하는 증분 P3
모델링합니다. 그런 다음 입력 값 c
변경 될 때까지 기다립니다. 이것은 또한 새로운 것입니다. 지금까지 우리는 항상 다음과 같이 신호를 할당했습니다.
s <= value;
이전 절에서 설명한 이유로 우리는 암시 적으로 다음과 같이 번역 할 수 있습니다.
s <= value; -- after delta
이 소형 디지털 하드웨어 시스템은 다음 그림으로 나타낼 수 있습니다.
물리적 인 시간의 소개와 델타 에서 측정 된 상징적 인 시간을 가지고 있다는 것을 알기 때문에, 우리는 T+D
를 나타낼 2 차원 시간을 갖게됩니다. 여기서 T
는 나노 초 단위로 측정 된 물리적 시간이고 D
는 숫자입니다. 델타 (물리적 기간 없음).
전체 그림
VHDL 시뮬레이션의 중요한 측면 중 하나는 아직 논의하지 않았지만 실행 단계가 끝나면 모든 프로세스가 suspended
상태입니다. 우리는 비공식적으로 스케쥴러가 할당 된 신호의 값을 업데이트한다고 말했습니다. 그러나 우리의 동기식 카운터의 예에서는 clk
, c
및 nc
신호를 동시에 업데이트해야합니까? 육체적 인 지연은 어떨까요? 그리고 suspended
상태의 모든 프로세스와 run-able
상태의 프로세스가 모두 다음에 어떻게됩니까?
완전한 (그러나 단순화 된) 시뮬레이션 알고리즘은 다음과 같습니다.
- 초기화
- 현재 시간
Tc
를 0 + 0 (0 ns, 0 델타 - 사이클)로 설정 - 모든 신호를 초기화하십시오.
-
wait
문에서 일시 중단 될 때까지 각 프로세스를 실행하십시오.- 신호 할당의 값과 지연을 기록하십시오.
- 프로세스가 재개되는 조건을 기록하십시오 (지연 또는 신호 변경).
-
Tn
을 다음 중 가장 초기 값으로 계산합니다.-
wait for <delay>
일시 중단 된 프로세스의 재개 시간. - 다음에 신호 값이 변경 될 때.
-
- 현재 시간
- 시뮬레이션주기
-
Tc=Tn
. - 업데이트해야 할 신호.
- 업데이트 된 신호 중 하나의 값 변경을 기다리고 있던 모든 프로세스를
run-able
상태로 만듭니다. -
wait for <delay>
명령문wait for <delay>
와 이력서 시간이Tc
모든 프로세스를run-able
상태로 만듭니다. - 실행 가능한 모든 프로세스를 일시 중단 할 때까지 실행하십시오.
- 신호 할당의 값과 지연을 기록하십시오.
- 프로세스가 재개되는 조건을 기록하십시오 (지연 또는 신호 변경).
-
Tn
을 다음 중 가장 초기 값으로 계산합니다.-
wait for <delay>
일시 중단 된 프로세스의 재개 시간. - 다음에 신호 값이 변경 될 때.
-
-
Tn
이 무한대이면 시뮬레이션을 중지합니다. 그렇지 않으면 새로운 시뮬레이션 사이클을 시작하십시오.
-
수동 시뮬레이션
결론을 맺으려면 위의 동기식 카운터에서 간략 시뮬레이션 알고리즘을 수동으로 연습 해 보겠습니다. 우리는 여러 프로세스가 실행 가능할 때 순서가 P3
> P2
> P1
이되도록 임의로 결정합니다. 다음 표는 초기화 및 첫 번째 시뮬레이션 사이클 동안 시스템 상태의 발전을 나타냅니다. 각 신호에는 현재 값이 표시되는 자체 열이 있습니다. 신호 할당이 실행되면 예약 된 값이 현재 값에 추가됩니다 (예 : 현재 값이 a
이고 다음 값이 시간 T+D
(실제 시간과 델타주기)에서 b
일 때 a/b@T+D
) . 마지막 세 열은 일시 중단 된 프로세스를 다시 시작하는 조건을 나타냅니다 (변경해야하는 신호의 이름 또는 프로세스가 다시 시작될 시간).
초기화 단계 :
운영 | Tc | Tn | clk | c | nc | P1 | P2 | P3 |
---|---|---|---|---|---|---|---|---|
현재 시간 설정 | 0 + 0 | |||||||
모든 신호 초기화 | 0 + 0 | '0' | 0 | 0 | ||||
P3/nc<=c+1 after 5 ns | 0 + 0 | '0' | 0 | 0 / 1 + 5 + 0 | ||||
P3/wait on c | 0 + 0 | '0' | 0 | 0 / 1 + 5 + 0 | c | |||
P2/if clk='1'... | 0 + 0 | '0' | 0 | 0 / 1 + 5 + 0 | c | |||
P2/end if | 0 + 0 | '0' | 0 | 0 / 1 + 5 + 0 | c | |||
P2/wait on clk | 0 + 0 | '0' | 0 | 0 / 1 + 5 + 0 | clk | c | ||
P1/clk<='0' | 0 + 0 | '0'/ '0'@ 0 + 1 | 0 | 0 / 1 + 5 + 0 | clk | c | ||
P1/wait for 10 ns | 0 + 0 | '0'/ '0'@ 0 + 1 | 0 | 0 / 1 + 5 + 0 | 10 + 0 | clk | c | |
다음에 계산하기 | 0 + 0 | 0 + 1 | '0'/ '0'@ 0 + 1 | 0 | 0 / 1 + 5 + 0 | 10 + 0 | clk | c |
시뮬레이션 사이클 # 1
운영 | Tc | Tn | clk | c | nc | P1 | P2 | P3 |
---|---|---|---|---|---|---|---|---|
현재 시간 설정 | 0 + 1 | '0'/ '0'@ 0 + 1 | 0 | 0 / 1 + 5 + 0 | 10 + 0 | clk | c | |
신호 업데이트 | 0 + 1 | '0' | 0 | 0 / 1 + 5 + 0 | 10 + 0 | clk | c | |
다음에 계산하기 | 0 + 1 | 5 + 0 | '0' | 0 | 0 / 1 + 5 + 0 | 10 + 0 | clk | c |
참고 : 첫 번째 시뮬레이션 사이클 중에는 3 개의 프로세스 중 어느 것도 재개 조건이 충족되지 않아 실행 단계가 없습니다.
P2
의 값 변경을 기다리고 있습니다clk
와에 거래되고있다clk
하지만 이전 및 새 값이 동일한만큼,이 값을 변경하지 않습니다.
시뮬레이션 사이클 # 2
운영 | Tc | Tn | clk | c | nc | P1 | P2 | P3 |
---|---|---|---|---|---|---|---|---|
현재 시간 설정 | 5 + 0 | '0' | 0 | 0 / 1 + 5 + 0 | 10 + 0 | clk | c | |
신호 업데이트 | 5 + 0 | '0' | 0 | 1 | 10 + 0 | clk | c | |
다음에 계산하기 | 5 + 0 | 10 + 0 | '0' | 0 | 1 | 10 + 0 | clk | c |
참고 : 다시 한 번 실행 단계가 없습니다.
nc
변경되지만 과정에서 대기하지 않습니다nc
.
시뮬레이션 사이클 # 3
운영 | Tc | Tn | clk | c | nc | P1 | P2 | P3 |
---|---|---|---|---|---|---|---|---|
현재 시간 설정 | 10 + 0 | '0' | 0 | 1 | 10 + 0 | clk | c | |
신호 업데이트 | 10 + 0 | '0' | 0 | 1 | 10 + 0 | clk | c | |
P1/clk<='1' | 10 + 0 | '0'/ '1'@ 10 + 1 | 0 | 1 | clk | c | ||
P1/wait for 10 ns | 10 + 0 | '0'/ '1'@ 10 + 1 | 0 | 1 | 20 + 0 | clk | c | |
다음에 계산하기 | 10 + 0 | 10 + 1 | '0'/ '1'@ 10 + 1 | 0 | 1 | 20 + 0 | clk | c |
시뮬레이션 사이클 # 4
운영 | Tc | Tn | clk | c | nc | P1 | P2 | P3 |
---|---|---|---|---|---|---|---|---|
현재 시간 설정 | 10 + 1 | '0'/ '1'@ 10 + 1 | 0 | 1 | 20 + 0 | clk | c | |
신호 업데이트 | 10 + 1 | '1' | 0 | 1 | 20 + 0 | clk | c | |
P2/if clk='1'... | 10 + 1 | '1' | 0 | 1 | 20 + 0 | c | ||
P2/c<=nc | 10 + 1 | '1' | 0 / 1 @ 10 + 2 | 1 | 20 + 0 | c | ||
P2/end if | 10 + 1 | '1' | 0 / 1 @ 10 + 2 | 1 | 20 + 0 | c | ||
P2/wait on clk | 10 + 1 | '1' | 0 / 1 @ 10 + 2 | 1 | 20 + 0 | clk | c | |
다음에 계산하기 | 10 + 1 | 10 + 2 | '1' | 0 / 1 @ 10 + 2 | 1 | 20 + 0 | clk | c |
시뮬레이션 사이클 # 5
운영 | Tc | Tn | clk | c | nc | P1 | P2 | P3 |
---|---|---|---|---|---|---|---|---|
현재 시간 설정 | 10 + 2 | '1' | 0 / 1 @ 10 + 2 | 1 | 20 + 0 | clk | c | |
신호 업데이트 | 10 + 2 | '1' | 1 | 1 | 20 + 0 | clk | c | |
P3/nc<=c+1 after 5 ns | 10 + 2 | '1' | 1 | 1 / 2 @ 15 + 0 | 20 + 0 | clk | ||
P3/wait on c | 10 + 2 | '1' | 1 | 1 / 2 @ 15 + 0 | 20 + 0 | clk | c | |
다음에 계산하기 | 10 + 2 | 15 + 0 | '1' | 1 | 1 / 2 @ 15 + 0 | 20 + 0 | clk | c |
참고 :
nc
업데이트가15+2
로 예정되어 있다고 생각할 수 있으며15+0
예약했습니다. 0이 아닌 물리적 지연 (여기서는5 ns
)을 현재 시간 (10+2
)에 추가하면 델타 사이클이 사라집니다. 실제로, 델타 사이클은 서로 다른 시뮬레이션 시간T+0
,T+1
...을 동일한 물리적 시간T
로 구별하는 데만 유용합니다. 실제 시간이 변경 되 자마자 델타주기를 재설정 할 수 있습니다.
시뮬레이션 사이클 # 6
운영 | Tc | Tn | clk | c | nc | P1 | P2 | P3 |
---|---|---|---|---|---|---|---|---|
현재 시간 설정 | 15 + 0 | '1' | 1 | 1 / 2 @ 15 + 0 | 20 + 0 | clk | c | |
신호 업데이트 | 15 + 0 | '1' | 1 | 2 | 20 + 0 | clk | c | |
다음에 계산하기 | 15 + 0 | 20 + 0 | '1' | 1 | 2 | 20 + 0 | clk | c |
참고 : 다시 한 번 실행 단계가 없습니다.
nc
변경되지만 과정에서 대기하지 않습니다nc
.