최근 수정 시각 : 2024-12-17 13:27:20

베릴로그


'''[[전기전자공학과|전기·전자공학
{{{#!wiki style="font-family: Times New Roman, serif; font-style: Italic; display: inline;"
]]'''
{{{#!wiki style="margin:0 -10px -5px; min-height: 26px; word-break:keep-all"
{{{#!folding [ 펼치기 · 접기 ]
{{{#!wiki style="margin:-6px -1px -11px"
<colbgcolor=#009><colcolor=#fff> 학문 기반 학문
물리학 ( 전자기학 ( 회로이론 · 전자 회로 · 논리 회로) · 양자역학 · 물리화학 · 열역학 · 응집물질물리학) · 화학
연관 학문
수학 ( 공업수학 · 수치해석학 · 위상수학 · 미분방정식 · 대수학 ( 환론 · 표현론) · 선형대수학 · 이론 컴퓨터 과학 · 컴퓨터공학 ( 프로그래밍 언어 ( HDL · VHDL · C · C++ · Java · 파이썬 · 베릴로그)) · 재료공학 · 제어 이론
공식 · 법칙 전자기 유도 · 가우스 법칙 · 비오-사바르 법칙 · 무어의 법칙 · 키르히호프의 법칙 · 맥스웰 방정식 · 로런츠 힘 · 앙페르 법칙 · 드모르간 법칙 · 페르미 준위 · 중첩의 원리
이론 · 연구 반도체 ( P형 반도체 · N형 반도체) · 디스플레이 · 논리 회로 ( 보수기 · 가산기 · 플립플롭 · 논리 연산) · 전자 회로 · RLC 회로 · 역률 · DSP · 히스테리시스 곡선 · 휘트스톤 브리지 · 임베디드 시스템
용어 클럭 · ASIC · CPU 관련 ( BGA · 마이크로아키텍처 · GPS · C-DRX · 소켓) · 전계강도계 · 축전기 · CMCI · 전송선 · 양공 · 도핑 · 이미터 · 컬렉터 · 베이스 · 시퀀스
전기 · 전자
관련 정보
제품
스마트폰 · CPU · GPU ( 그래픽 카드) · ROM · RAM · SSD · HDD · MPU · CCD · eMMC · USB · UFS · LCD · LED · OLED · AMOLED · IoT · 와이파이 · 스마트 홈 · 마그네트론 · 마이크 · 스피커 · 배터리
소자
집적 회로 · 다이오드 · 진공관 · 트랜지스터 ( BJT · FET · JFET · MOSFET · T-FT) · CMOS · IGBT · 저항기 · 태양전지 · 연산 증폭기 · 사이리스터 · GTO · 레지스터 · 펠티어 소자 · 벅컨버터
자격증
전기 계열 기능사
전기기능사 · 철도전기신호기능사
기사
전기기사 · 전기산업기사 · 전기공사기사 · 전기공사산업기사 · 전기철도기사 · 전기철도산업기사 · 철도신호기사 · 철도신호산업기사
기능장 및 기술사
전기기능장 · 건축전기설비기술사 · 발송배전기술사 · 전기응용기술사 · 전기안전기술사 · 철도신호기술사 · 전기철도기술사
전자 계열 기능사
전자기기기능사 · 전자계산기기능사 · 전자캐드기능사
기사
전자기사 · 전자산업기사 · 전자계산기기사 · 전자계산기제어산업기사
기능장 및 기술사
전자기기기능장 · 전자응용기술사
기타 기능사
신재생에너지발전설비기능사(태양광)
기사
소방설비기사 · 신재생에너지발전설비기사(태양광) · 로봇소프트웨어개발기사 · 로봇하드웨어개발기사 · 로봇기구개발기사
}}}}}}}}}


[[컴퓨터공학|컴퓨터 과학 & 공학
Computer Science & Engineering
]]
[ 펼치기 · 접기 ]
||<tablebgcolor=#fff,#1c1d1f><tablecolor=#373a3c,#ddd><colbgcolor=#0066DC><colcolor=white> 기반 학문 || 수학( 해석학 · 이산수학 · 수리논리학 · 선형대수학 · 미적분학 · 미분방정식 · 대수학( 환론 · 범주론) · 정수론) · 이론 컴퓨터 과학 · 암호학 · 전자공학 · 언어학( 형태론 · 통사론 · 의미론 · 화용론 · 음운론) · 인지과학 ||
하드웨어 구성 SoC · CPU · GPU( 그래픽 카드 · GPGPU) · ROM · RAM · SSD · HDD · 참조: 틀:컴퓨터 부품
기술 기계어 · 어셈블리어 · C/ C++ · C# · Java · Python · BIOS · 절차적 프로그래밍 · 객체 지향 프로그래밍 · 해킹 · ROT13 · 일회용 비밀번호 · 사물인터넷 · 와이파이 · GPS · 임베디드 · 인공신경망 · OpenGL · EXIF · 마이크로아키텍처 · ACPI · UEFI · NERF · gRPC · 리버스 엔지니어링 · HCI · UI · UX · 대역폭 · DBMS · NoSQL · 해시( SHA · 브루트 포스 · 레인보우 테이블 · salt · 암호화폐) · RSA 암호화 · 하드웨어 가속
연구

기타
논리 회로( 보수기 · 가산기 · 논리 연산 · 불 대수 · 플립플롭) · 정보이론 · 임베디드 시스템 · 운영 체제 · 데이터베이스 · 프로그래밍 언어{ 컴파일러( 어셈블러 · JIT) · 인터프리터 · 유형 이론 · 파싱 · 링커 · 난해한 프로그래밍 언어} · 메타데이터 · 기계학습 · 빅데이터 · 폰노이만 구조 · 양자컴퓨터 · 행위자 모델 · 인코딩( 유니코드 · MBCS) · 네트워크 · 컴퓨터 보안 · OCR · 슈퍼컴퓨터 · 튜링 머신 · FPGA · 딥러닝 · 컴퓨터 구조론 · 컴퓨터 비전 · 컴퓨터 그래픽스 · 인공지능 · 시간 복잡도( 최적화) · 소프트웨어 개발 방법론 · 디자인 패턴 · 정보처리이론 · 재귀 이론 · 자연어 처리( 기계 번역 · 음성인식) · 버전 ( 버전 관리 시스템 · Git · GitHub)

하드웨어 기술 언어(HDL)
베릴로그 VHDL

1. 개요2. 특징3. 역사4. 문법
4.1. 모듈4.2. 논리값4.3. 상수4.4. 자료형4.5. 할당문
4.5.1. 연속 할당문4.5.2. 절차형 할당문
4.5.2.1. initial 블록4.5.2.2. always 블록4.5.2.3. 타이밍 제어
4.6. 조건문4.7. 반복문4.8. 모듈 포트4.9. 모듈 인스턴스4.10. 생성문4.11. 시스템 태스크(system tasks)
5. 예제
5.1. 조합 회로(combinational circuit)
5.1.1. 인코더
5.2. 순차 회로(sequential logic)
6. 여담
6.1. 자주 사용되는 용어6.2. SystemVerilog
7. 관련 책과 자료8. 관련 문서

1. 개요

베릴로그(Verilog)는 ASIC이나 FPGA 등을 포함한 디지털 회로 및 시스템의 설계, 검증 그리고 구현에 쓰이는 하드웨어 기술 언어(HDL; Hardware Description Language)다. 베릴로그 HDL라고도 부르지만, 이 경우 VHDL과 헷갈리기 때문에 짧게 베릴로그라고만 부른다. [1]

VHDL은 과거에는 많이 사용되었지만 최근에는 주로 회사에서 베릴로그를 사용하고, 학교나 연구실에서는 주로[2] VHDL을 사용한다. 통계적으로는 유럽을 제외하면 베릴로그의 점유율이 VHDL을 압도하였다.

2. 특징

Ada 기반인 VHDL과 달리 C언어와 비슷한 문법을 가진 것이 특징이다. 'if'나 'for', 'while' 같은 제어 구조나 연산자 등이 거의 유사하여, C언어에 익숙한 사용자들이 쉽게 접근할 수 있다. [3]

하지만 베릴로그가 C언어와 문법이 비슷하다고 C언어 프로그래밍 하듯이 베릴로그 코드를 작성하면 엉뚱한 코드가 나오게 되며 실제 하드웨어 상에서는 동작을 안할 가능성이 있다. 베릴로그는 실제적인 하드웨어를 기술하고 만드는 것에 목적을 둔 언어이기에, 현실의 하드웨어로는 도저히 구현이 어려운 구문이라면 논리적으로는 문제가 없더라도 합성이 되지 않는다.

테스트벤치와 시뮬레이션은 잘 돌아가도 합성이 안 되는 경우도 있다. 시뮬레이션은 작성한 코드를 합성한 뒤 하드웨어로 구현한 뒤 수행되는 것이 아니라, 소프트웨어 위에서만 해석하여 수행되는 것이기 때문이다. 따라서 어떤 문법이 합성이 가능하고 어떤 문법이 테스트벤치 및 시뮬레이션에서만 사용되는지 숙지해야 한다.[4]

예를 들어서, for 문 같은 반복문은 하드웨어적인 구현이 힘들거나 합성 툴에 따라서 완전히 다른 결과물이 나올수도 있으며, 비트 수가 큰 가산기 등 조합회로가 과다하게 쓰이는 회로인 경우에는 FPGA 내부의 자원(논리게이트, 메모리 등)을 너무 많이 차지해버려서 하드웨어적으로 구현하는 것이 비효율적일 수 있다.

HDL에 입문하는 사람들은 하드웨어에 대한 충분한 지식을 갖고, 작성한 베릴로그 코드가 어떻게 하드웨어로 합성될지에 대한 감각을 키워나가야 한다.

3. 역사

1983년 Gateway Design Automation사에서 근무하던 Phil Moorby가 Verilog-XL이라는 논리 시뮬레이터와 함께 모의시험용 언어로 개발한 것이 시초이다. 이후 회사가 Cadence Design Systems[5]에 인수합병되어 업계표준 HDL로 자리를 잡았다.

이후 1995년 IEEE 1364-1995 표준이 만들어졌고, 2001년 기존 단점을 보완하고 개선하여 IEEE 1364-2001 표준(Verilog-2001)으로 개정되었다.

4. 문법

4.1. 모듈

베릴로그의 기본 단위는 모듈(module)이다. module이라는 키워드를 모듈이 시작하는 위치에 endmodule 키워드는 모듈이 끝나는 위치에 작성한다.

모듈은 크게 모듈 선언부, 신호 선언부, 동작 선언부 3가지로 구성된다.
  • 모듈 선언부: 모듈의 이름, input 및 output 포트 등을 선언함
  • 신호 선언부: 모듈 내부에서 사용될 자료형인 wire, reg 등을 선언함
  • 동작 선언부: always, assign 등을 사용하여 모듈의 기능, 동작, 구조 등을 선언함

/* 모듈 선언부 */
// 모듈의 이름, 입출력 포트, parameter 등을 선언함 //
module my_and_module (a, b, c); 
    /* 신호 선언부 */
    // wire, reg, parameter 등 선언함
    input wire a, b;
    output wire c;

    /* 동작 선언부 */
    // assign, always, initial, 하위 모듈 인스턴스 등 선언함
    assign c = a & b;
endmodule

4.2. 논리값

베릴로그는 0, 1, x, z의 4가지 논리값을 지원한다. x는 논리 0과 논리 1이 충돌하여 논리값을 모르는(unknown) 상태를 나타내며, z는 회로가 끊어진 상태를 나타낸다.
논리값
0 0 또는 거짓
1 1 또는 참
x unknown
z high impedance

4.3. 상수

상수는 아래와 같은 형식을 사용해서 bit 수와 진수를 지정한 뒤 표현할 수 있다.
[bit 수]'[진수][숫자]
  • bit 수를 생략할 시 그 수는 32 bit로 간주한다.
  • 진수는 16진수('h, 'H), 10진수('d, 'D), 8진수('o, 'O), 2진수('b, 'B)를 나타낼 수 있으며, 대소문자 구별이 없다.

429       // 32 bit 10진수 429
'hf12     // 32 bit 16진수 f12
8'Ha12    //  8 bit 16진수 a12
32'd2020  // 32 bit 10진수 2020
16'o20    // 16 bit  8진수 20
4'b0110   //  4 bit  2진수 0110

===# 연산자 #===
연산자 비고
+ 덧셈
- 뺄셈
* 곱셈
/ 나눗셈
% 나머지
** 제곱
< 미만
> 초과
<= 이하
>= 이상
== 일치
!= 불일치
=== 일치 (x, z까지 고려하여 비교)
!== 불일치 (x, z까지 고려하여 비교)
&& 논리 and 결과 값은 1 bit의 1(참) 또는 0(거짓)
\|\| 논리 or 결과 값은 1 bit의 1(참) 또는 0(거짓)
! 논리 not 결과 값은 1 bit의 1(참) 또는 0(거짓)
& 비트 and 피연산자의 bit 수만큼 결과 값을 유지함
| 비트 or 피연산자의 bit 수만큼 결과 값을 유지함
^ 비트 xor 피연산자의 bit 수만큼 결과 값을 유지함
~& 비트 nand 피연산자의 bit 수만큼 결과 값을 유지함
~| 비트 nor 피연산자의 bit 수만큼 결과 값을 유지함
~^ 비트 xnor 피연산자의 bit 수만큼 결과 값을 유지함
~ 비트 not 피연산자의 bit 수만큼 결과 값을 유지함
<< 논리 왼쪽 시프트 비어있는 bit는 0으로 채움
>> 논리 오른쪽 시프트 비어있는 bit는 0으로 채움
<<< 산술 왼쪽 시프트 부호 bit를 유지하면서 시프트 연산을 수행함
>>> 산술 오른쪽 시프트 부호 bit를 유지하면서 시프트 연산을 수행함
? : 3항 연산자 (조건 연산자) a ? b : c의 형태 사용하며, a값이 참(1)이면 b를, 거짓(0)이면 c를 선택함
{} 결합 예를 들어서, {4'b1000, 2'b11}은 6'b1000_11과 동일함
{{}} 반복 예를 들어서, {3{4'b1101}}은 12'b1101_1101_1101과 동일함

4.4. 자료형

베릴로그에는 수많은 자료형이 있지만, 그중에서도 net 자료인 wire와 variable 자료형인 reg를 주로 사용한다.

wire 자료형은 값을 저장하지 않으며, 하드웨어적인 연결을 나타낼 때 사용한다. reg 자료형은 값을 저장할 수 있으며, 주로 순차회로를 구현하기 위해 사용한다. 다만 reg 자료형은 조합회로를 구현할 때도 사용할 수 있는데, 이때는 값을 저장하는 것이 아니라 특정 논리 조건에 따라서 출력을 결정하는데 사용한다.

wire와 reg 자료형은 대괄호 기호를 사용하여 범위 지정을 하면 다중 bit의 벡터 또는 배열을 선언할 수 있다.

wire        w1;            //      1 bit wire
reg         reg_a, reg_b;  //      1 bit reg
reg   [7:0] reg_c;         //      8 bit reg (벡터)
wire [15:0] d_in;          //     16 bit wire (벡터)
reg   [7:0] reg_d [0:255]; //  8*256 bit reg (2차원 배열)

4.5. 할당문

할당문은 객체 값을 주는 방법이다. wire 자료형에 assign 문을 사용하여 값을 할당하는 연속(continuous) 할당문과 reg 자료형에 blocking(=) 또는 non-blocking(<=) 할당문을 사용하여 값을 할당하는 절차형(procedular) 할당문이 있다.

4.5.1. 연속 할당문

연속 할당문은 wire 자료형에 assign 문을 사용하여 값을 할당하는 방법이다. 우변 수식의 값에 변화가 발생했을 때, 좌변으로 값을 할당한다.

연속 할당문은 조합 회로를 모델링할 때 사용한다.

wire wire_a;
assign wire_a = a & b; // assign 문을 사용한 연속 할당

wire wire_b = a & b; // wire 자료형 선언과 동시에 할당문 선언

4.5.2. 절차형 할당문

절차형 할당문은 initial, always와 같은 블록(block) 내부에서 사용된다.
4.5.2.1. initial 블록
initial 블록 내부의 코드는 한번만 실행된다. initial 블록은 합성이 안되므로 테스트벤치에서만 사용한다.

reg reg_a;
initial begin
    reg_a = 1'b0;
end

4.5.2.2. always 블록
always 블록은 합성이 가능하며, always 블록 내부의 코드는 항상 실행된다.

always 블록은 조합 회로 또는 순차 회로를 모델링할 때 사용한다. 조합 회로를 모델링할 때는 blocking 할당문(=)을, 순차 회로를 모델링할 때는 non-blocking 할당문(<=)을 사용한다. blocking 할당문을 사용한 조합 회로는 우변 값이 변하는[6] 즉시 좌변 값을 업데이트한다. non-blocking 할당문을 사용한 순차 회로는 좌변에 아직 업데이트되지 않고 남아있는 이전의 우변 값을 참조한다.
  • 순차 회로를 모델링할 때, always 블록에서는 non-blocking 할당문(<=)을 사용할 것
  • 조합 회로를 모델링할 때, always 블록에서는 blocking 할당문(=)을 사용할 것
  • 동일한 always 블록 내에서는 non-blocking 할당문(<=)과 blocking 할당문(=)을 혼용해서 사용하지 않을 것

module top (
    input  wire clk, rst_n,
    input  wire a, b,
    output reg  q
);

reg y;

// blocking 할당문을 사용하여 조합 회로를 모델링
always @(*) begin
    y = a ^ b;
end

// non-blocking 할당문을 사용하여 순차 회로를 모델링
always @(posedge clk) begin
    if(!rst_n) q <= 1'b0;
    else       q <= y;
end

endmodule
4.5.2.3. 타이밍 제어
절차형 할당문에서는 지연(delay)와 이벤트(event)에 대한 타이밍 제어 방법을 제공한다.

기호 #으로 지연을 지정한다. 지연을 가지는 절차형 할당문은 지정된 지연 값만큼 그 코드의 실행이 지연된다. 지연은 합성이 불가능한 코드이므로 테스트벤치에서만 사용다.

reg_a = 1'b1; // 지연 10 이후 할당문 실행

기호 @로 이벤트를 제어한다. 절차형 할당문의 코드는 선언된 이벤트의 발생에 동기화되어 실행된다. @는 합성이 가능한 코드이다.

괄호 안에 있는 신호들은 이벤트 제어 신호이며 sensitivity list라고 불린다. 기호 *는 모든 입력 신호를 감지하겠다는 의미이다. or 또는 기호 ,를 사용하여 다수의 이벤트에 의해 실행되는 절차형 할당문을 모델링할 수 있다. 상승 에지(posedge; positive edge)는 신호가 [math(0 \to 1)]로 변하는 시점을, 하강 에지(negedge; negative edge)는 신호가 [math(0 \to 1)]로 변하는 시점을 의미한다.

예를 들어서, always @(posedge clk)라고 작성했다면, 이 always 블록 내부의 신호들은 클럭(clk) 신호의 상승에지에 동기화되어 값을 업데이트한다는 의미를 가진.

always @(*) // 모든 입력 신호를 감지
always @(a) // a 값이 변할때마다 감지 (a가 0에서 1로 또는 1에서 0으로 변할때 모두 감지)
always @(posedge clk) // clk의 상승에지를 감지
always @(posedge clk, negedge rst_n) // clk의 상승에지 또는 rst_n의 하강에지를 감지

4.6. 조건문

베릴로그에서 조건문은 assign 및 3항 연산자를 사용한 방법, if-else를 사용한 방법과 case를 사용한 방법 3가지가 있다. 조건문을 사용할 때 래치(latch)가 발생하지 않도록 모든 조건에 대해서 조건문을 작성해야 한다.

다음 예제들은 조건문을 사용하여 멀티플렉서(mux)를 구현한 것이다. 래치 발생을 피하기 위해서 sel의 모든 조건(2'b00 ~ 2'b11)에 대해서 조건문을 작성했으며, 그 외에 조건에서는 x가 할당되도록 작성했다.

====# 3항 연산자 #====
module mux (
    input  wire       a,
    input  wire       b,
    input  wire       c,
    input  wire       d,
    input  wire [1:0] sel,
    output wire       out
);

assign out = (sel == 2'b00) ? a :
             (sel == 2'b01) ? b :
             (sel == 2'b10) ? c :
             (sel == 2'b11) ? d : x;

endmodule


====# if-else #====
module mux (
    input  wire       a,
    input  wire       b,
    input  wire       c,
    input  wire       d,
    input  wire [1:0] sel,
    output wire       out
);

always @(*) begin
         if(sel == 2'b00) out = a;
    else if(sel == 2'b01) out = b;
    else if(sel == 2'b10) out = c;
    else if(sel == 2'b11) out = d;
    else                  out = x;
end

endmodule


====# case #====
module mux (
    input  wire       a,
    input  wire       b,
    input  wire       c,
    input  wire       d,
    input  wire [1:0] sel,
    output wire       out
);

always @(*) begin
    case(sel)
        2'b00:   out = a;
        2'b01:   out = b;
        2'b10:   out = c;
        2'b11:   out = d;
        default: out = x;
    endcase
end

endmodule

4.7. 반복문

베릴로그에서 조건문은 for, while, repeat 그리고 forever를 사용하는 방법 4가지가 있다. forever 문은 합성이 지원되지 않으므로 테스트벤치 및 시뮬레이션에서만 사용된다.

초심자의 경우 테스트벤치 작성을 위한 경우 말고는 반복문 사용을 지양하는 것이 좋다. 반복문의 무분별한 오용은 하드웨어의 합성 과정에서 문제를 일으킬 가능성이 높다.[7] 따라서 설계할 반복문이 하드웨어로 합성 가능한 구조인지 잘 따져가면서 사용하는게 중요하다. 반복문이 다이나믹하다면 당연히 합성 불가능하고, 생각하기 힘든 다양한 함정들이 존재한다. 단순한 규칙적인 구조를 기술하는 수준이 아니라면, 루프끼리 복잡한 상호 작용이 있는 구조를 기교를 부려서 기술하는 것은 지양하는 것이 좋다.

4.8. 모듈 포트

모듈 포트는 외부 신호와 모듈 내부를 연결한다. 포트의 종류로는 방향에 따라서 input(입력), output(출력) 그리고 inout(양방향)이 있다. 모듈 포트를 선언하는 방법에는 여러가지가 있다.

module my_module (a, b, c);
    input a; // 자료형을 명시하지 않으면 wire로 할당됨
    input wire b;
    output reg c;

module my_module (
    input a, // bit 수를 명시하지 않으면 1 bit로 할당됨
    input wire [2:0] b,
    output reg c
);

4.9. 모듈 인스턴스

어떤 모듈 내부에서 다른 하위 모듈을 인스턴스하고 포트 매핑(mapping)을 해서 회로를 모델링할 수 있다. 포트 매핑을 하기 위해서는 wire 선언을 반드시 한다.

module top_module;

wire [7:0] d, e, r; // 포트 매핑을 위한 wire 선언은 필수다
submodule instance_1 ( 
    .a(d), // submodule의 포트 이름이 a인 경우
    .b(e),
    .c( ), // 사용하지 않는 포트는 빈 괄호로 두거나 아예 포트 이름을 생략함
    .q(r)
);

endmodule

4.10. 생성문

베릴로그에서 생성문은 generate-for를 사용하여 반복적인 구조를 생성하거나 generate-if를 사용하여 파라미화된 구조를 생성할 때 사용한다. 회로의 동작을 모델링할 때 일반적으로 사용되는 for 반복문이나 if 조건문과는 달리 generate를 사용한 생성문은 회로의 구조를 모델링할 때 사용하는 것에 차이점이 있다.

====# generate-for #====
베릴로그에서 generate-for은 하드웨어 구조가 반복될 때 코드를 간결하게 작성하기 위해 사용한다.

아래 예제는 8개의 XOR 게이트를 생성하는 예제이며, 두 코드 모두 회로 동작은 동일하다.

module my_xor #(
   parameter WIDTH=8
)
(
   input  wire [WIDTH-1:0] a,
   input  wire [WIDTH-1:0] b,
   output wire [WIDTH-1:0] out
);

genvar i;
generate
   for(i = 0; i < WIDTH; i = i + 1) begin
      assign out[i] = a[i] ^ b[i];
   end
endgenerate

endmodule


module my_xor #(
   parameter WIDTH=8
)
(
   input  wire [WIDTH-1:0] a,
   input  wire [WIDTH-1:0] b,
   output wire [WIDTH-1:0] out
);

assign out[0] = a[0] ^ b[0];
assign out[1] = a[1] ^ b[1];
assign out[2] = a[2] ^ b[2];
assign out[3] = a[3] ^ b[3];
assign out[4] = a[4] ^ b[4];
assign out[5] = a[5] ^ b[5];
assign out[6] = a[6] ^ b[6];
assign out[7] = a[7] ^ b[7];

endmodule


====# generate-if #====
베릴로그에서 generate-if 문은 특정 조건에 따라 하드웨어 구조를 선택적으로 생성할 때 사용한다. 주로 파라미터(parameter)나 상수 값에 따라서 하드웨어를 구성할 때 사용한다.

아래 예제는 파라미터인 MODE 값에 따라 덧셈, 곱셈, AND 연산 중 하나를 수행하는 회로가 생성되도록 설계하였다.

module mode_selector #(
   parameter MODE = 0
)
(
   input  wire [7:0] a
   input  wire [7:0] b,
   output wire [7:0] result
);
   generate
           if (MODE == 0) assign result = a + b;
      else if (MODE == 1) assign result = a * b;
      else                assign result = a & b;
   endgenerate
endmodule

4.11. 시스템 태스크(system tasks)

간단한 I/O 기능을 제공하는 함수들로 주로 시뮬레이션 과정에서 활용한다. 다른 함수와 구분하기 위해 모든 시스템 태스크는 $를 접두사로 사용한다.
  • $display
    문자열과 지정한 신호의 값을 화면에 출력한다. 자동으로 개행된다.
    $display("Hello World!");
    $display("%b, %d", a, b);
  • $write
    기능은 $display와 동일하나 개행 없이 출력한다.
  • $monitor
    지정한 신호의 값이 바뀌면 화면에 출력한다.
  • $stop
    시뮬레이션을 중단한다.
    $finish와 달리 control이 여전히 simulator에 있으며 simulator가 Interative Mode가 된다.
  • $finish
    시뮬레이션을 종료한다.
    $stop과 달리 control이 OS로 돌아온다.
  • $time
    현재의 시뮬레이션 시간
    $display($time);
  • $random
    랜덤값을 반환한다.
    $display($random);

5. 예제

5.1. 조합 회로(combinational circuit)

5.1.1. 인코더

인코더와 같이 조합 회로를 설계할 때는 always 문을 사용하는 방법과 assign 문을 사용하는 방법 2가지가 있다. 여기서 always 문에서 값을 할당할 때는 reg 자료형을, assign 문에서 값을 할당할 때는 wire 자료형을 사용하는 것에 유의해야 한다.
=====# always 문 사용 #=====
always 문 스타일로 4*2 인코더를 모델링한 코드이다.

always 문으로 조합회로를 기술할 때는 sensitivity list에 *(모든 신호)를 사용한다. *를 사용하지 않을 시, 합성 전 시뮬레이션 결과와 합성 후의 결과가 다를 수 있기 때문이다.

래치(latch) 생성을 피하기 위해서, 조건문에서 else 사용한다. 모든 경우에 대해서 조건문을 작성하지 않으면 래치가 생성된다. 래치는 조합 회로가 아닐뿐더러 글리치에 취약하고, 정적 타이밍 분석(STA; Static Timing Analysis)에도 불리하게 작용되기 때문이다.

module encoder (
   input  wire [3:0] x,
   output reg  [1:0] y // output y의 자료형이 wire가 아니라 reg임에 유의
);

   always @(*) begin
      if     (x == 4'b0001) y = 2'b00; // non-blocking 할당문(<=)이 아닌, blocking 할당문(=)을 사용함에 유의
      else if(x == 4'b0010) y = 2'b01;
      else if(x == 4'b0100) y = 2'b10;
      else if(x == 4'b1000) y = 2'b11;
      else                  y = 2'bxx; // latch 생성을 피하기 위해서 대신 else 사용함
   end

endmodule


=====# assign 문 사용 #=====
assign 문 스타일로 4*2 인코더를 모델링한 코드이다.

module encoder (
   input  wire [3:0] x,
   output wire [1:0] y  // output y의 자료형이 reg가 아니라 wire임에 유의
);

   assign y[0] = (!x[3] && !x[2] &&  x[1] && !x[0]) || (x[3] && !x[2] && !x[1] && !x[0]);
   assign y[1] = (!x[3] &&  x[2] && !x[1] && !x[0]) || (x[3] && !x[2] && !x[1] && !x[0]);

endmodule

5.2. 순차 회로(sequential logic)

====# D 플립플롭 #====
클럭의 상승에지에 동작하는 D 플립플롭을 모델링한 코드이다. 순차 회로를 구현할 때는 non-blockinig 할당문인 <=를 사용해야 한다. [8]

module d_flip_flop (
   input  wire clk,
   input  wire d,
   output reg  q,
   output reg  q_bar
);

   always @(posedge clk) begin
      q     <= d;
      q_bar <= ~d;
   end

endmodule


====# 다운 카운터 #====
8 bit 다운 카운터를 모델링한 코드이다. 입력 신호 load가 1이면 카운트 값 cnt는 8'b1111_1111(십진수로 255)로 값이 할당되며, load가 0이면 카운트 값이 1씩 작아지도록 설계되었다. 그리고 카운트 값 cnt는 출력 port인 Q와 연결되어 모듈 바깥으로 값이 출력된다.

module down_counter #(
   parameter WIDTH = 8
)
(
   input  wire             clk,
   input  wire             load,
   output wire [WIDTH-1:0] Q
);

   reg [WIDTH-1:0] cnt;

   always @(posedge clk) begin
      if(load) cnt <= {WIDTH{1’b1}}; // 8'b1111_1111
      else     cnt <= cnt - 1’b1;
   end

   assign Q = cnt;

endmodule

6. 여담

6.1. 자주 사용되는 용어

  • 시뮬레이션: 디자인을 실제 하드웨어를 제작하지 않고, 소프트웨어 환경에서 설계된 하드웨어의 동작을 가상으로 구현하고 분석하는 과정.
    • 테스트벤치(testbench): 디자인의 기능 및 성능을 검증하기 위해 사용되는 시뮬레이션 환경. 테스트벤치에서 입력 신호를 생성하고, 출력 결과를 관찰하거나 비교하여 동작을 확인함. 디자인과 마찬가지 테스트벤치 또한 HDL로 작성함.
    • DUT(Design Under Test): 테스트 대상이 되는 디자인.
  • 합성(synthesis): HDL로 작성된 코드를 실제 논리 게이트 수준(AND, OR, NOT 등)의 회로로 변환하는 과정. 타겟 하드웨어(FPGA나 ASIC)에 적합한 라이브러리 셀로 매핑하여, 최종적으로 게이트 수준으로 기술된 회로[9]가 생성됨.
  • 배치 및 배선(place and route): 합성된 회로를 물리적 레이아웃에 매핑하고 연결하는 과정.

6.2. SystemVerilog

디지털 시스템 설계에서 검증의 중요성이 증가함에 따라 이를 지원하기 위해 Verilog의 확장 형태인 SystemVerilog가 IEEE 1800-2005 표준으로 제정되었다.

2020년대 이전까지는 SystemVerilog는 검증용 테스트벤치 작성시 외엔 실제 업계에서는 잘 쓰이지 않았다. C에서 쓰이는 enum, class, struct, union등의 data type은 실제 하드웨어 합성시 결과물로 나오게 될 netlist[10]를 직관적으로 예측하기 어렵게 하고 툴 간에 문법 해석 차이가 있는 경우 netlist 기반의 시뮬레이션을 토대로 RTL[11] 디버깅을 하기 상당히 까다로워졌기 때문. 물론 베릴로그의 wire, reg data type도 소스코드에 선언된 그대로 wire와 register로 합성되는 것은 아니지만 SystemVerilog의 추상화에 비하면 사실상 하드웨어에 매우 가까운 편이라 소스코드에 존재하는 버그를 찾더라도 netlist를 직접 고치는 것이 가능할 정도였다. 또한 SystemVerilog는 툴체인에서 지원을 하지 않는 경우가 있기 때문에 더더욱 사용도가 떨어진 점도 있었다.[12]

2020년대에 돌입하면서 공정이 고도화됨에 따라 front-end 디자인을 real 값과 유사하게 구현하기 위한 방법으로 SystemVerilog format이 채택되었고, 이를 보다 고도화 한 UVM 등의 방법론과 프레임워크도 지속적으로 개발되고 있다. UVM Wikipedia OVM Wikipedia IEEE UVM Reference Manual

7. 관련 책과 자료

8. 관련 문서


[1] 여담으로 Verilog는 verfication과 logic의 합성어다. [2] 베릴로그를 기준으로 하는 학교도 있다. 서강대학교, 서울시립대학교, 광운대학교, 서울과학기술대학교, 한국교통대학교 등 [3] 다만, case문의 형태와 블록의 시작과 끝을 중괄호 대신 begin과 end를 사용하는 것은 파스칼(Pascal)언어와 유사하다. [4] 예를 들어서, initial 블록이라던지 지연(#; delay) 같은 문법은 원래 시뮬레이션에서 쓰라고 만든 기능이기 때문에 합성이 되지 않는다. 시뮬레이션에서만 사용되고 합성이 되지 않는 코드를 주의해서 사용해야 한다. [5] OrCad와 Allegro 등 각종 전자 설계 EDA를 개발하는 회사다. [6] 엄밀히 말하자면 sensitivity list에 나열된 신호가 변할 때. [7] 보수적인 엔지니어의 경우 반복문의 사용 자체를 금기시하기도 할 정도이다. [8] blocking 할당문인 =을 사용해도 합성되지만, 시뮬레이션 시 레이스 컨디션(race condition) 문제를 방지하기 위해 <= 사용을 강력하게 권장한다. [9] 주로 netlist라고 부름 [10] 쉽게 말해서 gate-level [11] 물론 이 경우 SystemVerilog 소스코드에 해당 [12] 칩을 제대로 하나 내보내려면, 단순 설계만 아니라 각종 합성부터 해서 backend작업까지 Pass를 시켜야 되는데, 이 backend 작업들 중에서 SystemVerilog를 지원하지 않거나 추가 라이센스비용을 내게하는 경우가 많다.