본문 바로가기
Programming/Verilog HDL

[Verilog HDL] 베릴로그 기본문법

by 끝까지 생각하고 알아내자 2023. 2. 27.

베릴로그라는 언어는 하드웨어를 묘사하기 위한 언어임을 먼저 알고 있어야 한다. 다시 말해 어떤 회로를 만들기 위해서 존재하는 언어이다. 회로설계라는 것은 어떤 입/출력을 구현하기 위해서 어떤 소자를 적절하게 사용할 것인가에 대해서 고민하는 과정이라고 생각한다. 이러한 설계과정에서 사용되는 툴(tool) 중에 한 개가 바로 베릴로그라는 언어이다. 베릴로그를 사용하면 회로에 존재하는 소자들을 간단히 언어를 이용해서 표현할 수 있고 배치와 연결 또한 언어의 구조를 이용해서 표현할 수 있다. 항상 어떤 컴퓨터언어를 배울 때는 그 언어의 문법을 알아야 한다. 따라서 베릴로그의 언어문법을 한번 알아보자. 

 

#verilog의 4가지 logic value

베릴로그에는 논리밸류가 총 4가지가 존재한다. 일반적인 논리회로에서는 0과1 두 가지 밸류만 존재하는데 베릴로그에서는 2가지가 추가되어서 총 4가지가 존재하게 된다. 

 

1. 논리 0

논리0은 말 그대로 전압이 낮은 상태 0을 말한다. 회로에서 전류가 흐르지 않는 상태를 툴에서 인지하고 있는 상태이다. 

2. 논리 1

논리1역시 전압이 높은 상태 1을 의미하고 회로에서 전류가 흐르는 상태를 말한다. 논리구조에서는 참을 의미한다. 

3. 논리 X

X는 정의되지않는 상태를 의미한다. 이 상태는 베릴로그에서 특별히 생긴 논리밸류인데 실제로 툴이 0인지 1인지 해석하지 못하는 상황을 구현하기 위해서 X라는 밸류를 사용한다. 

4. 논리 Z

Z는 임피던스가 높은상태를 의미한다. 임피던스가 높다는 말은 저항성이 매우 크다는 의미이고 저항이 매우 크기 때문에 전류는 거의 흐르지 못하는 상태가 된다. 따라서 Z상태는 전선이 거의 끊어져 있거나 전류가 외부적인 요인에 의해서 신호를 받을 수 없는 상황에서 Z밸류를 사용한다. 

 

-X와 Z의 논리연산

OR AND
X or 1=1 X and 0=0
X or X=X X and Z=X
X or Z=Z Z and 0=0
Z or 1=1 X and X=X

  X와Z는 우리가 알던 일반적인 밸류가 아니기 때문에 기본적인 논리연산이 어떻게 되고 어떤 결괏값이 되는지 알 필요성이 있다. 하지만 굳이 외우지 않아도 논리연산은 툴에서 기본적으로 수행하기 때문에 상관없다고 생각한다. 하지만 연산이 왜 이런 결과가 나오는지 과정자체는 알아두는 것이 좋다. 

 

#Verilog Data type 2가지

1. reg 타입

플립플랍의 array, 저장메모리, 디폴트값이 X이고, 1개비트를 저장할 수 있다는 특성을 가지고 있다. 조심해야 될 점은 reg타입은 회로소자인 레지스터를 지정하지는 않는다는 점이다. 다시 말해 모든 reg변수타입은 레지스터가 되지 않을 수도 있다는 점이다. 이 내용은 가장 많이 혼동하는 내용이며 후에 설계상의 오류가 될 수 있으므로 꼭 기억해 두 도로 하자. 

2. wire 타입

입력과 출력을 전달하는 wire로 취급되며 전송선으로 취급된다. 말그대로 전송선으로 구현되기 때문에 데이터를 저장할 수 없다는 특징을 가지고 있으며 데이터를 할당할 경우 설계상의 오류가 생길 수 있다.

 

베릴로그는 코드 블락이 존재하는데 이 블락은 각각의 뭉쳐진 코드덩어리라고 생각하면 이해하기 수월할 것이다. 이 블락들은 실행순서와 무관하며, 어떤 블락이 처음에 위치하든 나중에 위치하든 실행은 병렬적으로 실행되기 때문에 위치에 상관없이 실행순서는 동일하다는 특성을 가지고 있다. 따라서 이번에는 이 블락들이 어떤 종류가 있는지 알아보자. 

 

#Countinous assignments

블록 중에서 연속적으로 값을 계속 할당해 주는 어사인 블록이 존재한다. 

위와 같이 and게이트를 구성하는 어사인 블록을 표현했다. 이 블록은 b와 c를 and 연산해서 a에 출력하라는 명령어이다. 따라서 다시 말해 b, c를 입력으로 받아 a를 출력하는 조합회로를 구성했다고 볼 수 있다. 이 블록이 툴상에서는 b와 c의 값이 변화하면 연속적으로 그 값이 a에 반영되면서 연산이 이루어지는 구조이다. 그래서 assign블락은 조합회로만 구현할 수 있다는 특징이 있다. 한 줄 만에 코드를 다 써야 한다는 특징이 있고 구조가 복잡해질경우 코드가 길어진다는 단점이 있다. 

 

#Procedual Blocks

- always block

실행할 때 매 event마다 블록을 실행하는 블록이다. begin~end로 묶이는 블록이며, 조합회로 뿐만 아니라 순차회로도 구현할 수 있다는 특성을 가지고 있다. 

always block 구조

begin과 end는 언어사용에서 괄호라고 생각하면 이해하기 쉬울 것이다. 특히 always 블록에는 sensetive list라는 개념이 존재하는데 이 리스트는 always문이 실행되는 list를 의미한다. 즉 이 리스트에 있는 변수들이 변화하면 always문이 동작하게 된다. 따라서 어떤 모듈을 작성할 때 이 블록이 작동되도록 하는 조건변수를 여기 적어주면 의도에 맞게 설계할 수 있다. 

 

-inital block

이니셜 블록은 always블럭문과는 다르게 실행할때 최초 1회만 실행되는 블럭이다. 따라서 초기변수에 초기값을 할당할때 주로 많이 사용되고 시뮬레이션 테스트벤치코드 작성에 사용되는 블럭이다. 

이니셜 블럭은 always문과는 다르게 센서티브 리스트가 존재하지 않는다. 따라서 실행 조건 없이 초기에 한 번만 실행되고 그 이후로는 실행되지 않는다. 

 

#Blocking/Non-blocking assign

베릴로그에서는 값을 할당하는 방법이 2가지 존재한다. 일반적인 언어 같은 경우는 한 가지 방법만이 존재하는데 베릴로그에서 2가지 방법이 존재하는 이유는 실행순서에 의존하는 순차회로라는 개념이 존재하기 때문이다. 따라서 조합회로에 사용하는 할당과 순차회로에 사용하는 할당방법이 서로 다르다.

 

1. Blocking assign

블로킹 할당은 조합회로를 구현할 때 거의 사용되는 할당방법이다. 이 할당방법은 업데이트된 변수가 그대로 사용된다는 특징이 있다. 

위와 같이 블로킹 할당을표현한다. 따라서 위와같은 명령문에서는 c가 증가된채로 그다음 a로 연산된다. 

 

2. Non-blocking assign

논 블락킹 할당은 순차회로를 구현할 때 많은 사용되는 할당방법이다. 이 할당방법은 이후명령에 대해서는 업데이트가 되지 않는다는 점을 가지고 있다. 기호는 <=를 사용한다. 

위와 같은 논블로킹 할당은 사용한다. 논 블락킹 할당은 업데이트가 되지 않기 때문에 좌변에 있는 d는 1이 증가하게 되지만 b에 연산될 때는 증가된 d로 사용되지 않고 증가된 지전의 값으로 연산된다는 특징을 가지고 있다. 

 

#수의 표현

베릴로그에서는 숫자도 표현하는 방법이 따로 존재한다. 기본적으로 툴에서 해석하는 수는 2진법으로 해석하지만 사용자가 사용할 때는 다른 수의 표현도 표현할 수 있다. 

위와 같이 숫자를 표현해서 변수에 할당할 수 있다. 기본적인 형태는 <size>'<data type><number>의 형태로 표현한다. 위와 같은 경우는 5비트 사이즈크기에 10진수 데시마 1과 2를 int변수에 각각 할당하라는 명령문이다.

 

-기호별 data type

d(decimal) 10진수
b(binary) 2진수
h(hexadecimal) 16진수
o(octal) 8진수

 따라서 마지막에 사용되는 number칸에는 앞에서 설정해 준 데이터 타입에 맞게 설정해야 오류가 안 난다. 예를 들어 2진수 데이터 타입으로 설정했으면 0과 1로만 number칸에 작성해야 툴이 정확하게 해석한다. 

 

#Concatenate(데이터의 그룹화)

다음은 그룹화에 대해서 알아보자. 이 개념은 베릴로그에서 가장 독보적이고 중요한 개념 중 하나이고, 이 개념을 통해서 복잡한 코드를 간단하게 나타낼 수 있게 된다. 특히 다양한 신호를 하나의 신호로 그룹화해서 할당하고 싶을 때 매우 유용하게 사용된다. 그룹화에는 문법상으로 중괄호 {}를 사용한다. 

그룹화의 가장 기본적인 개념은 '이어 붙인다'는 개념이다. 즉 서로 다른 데이터들을 하나의 변수로 할당하고 싶을 때 그룹화를 통해서 붙인 다음 할당이 가능하게 할수있다. 첫번째 명령문은 5비트 데이터와 2비트 데이터를 각각 붙인다는 의미이고, 두번째 명령어는 a데이터 3개를 붙인다음 b의 0번째 신호를 붙이라는 의미이다. 세 번째는 b의 모든 비트를 and연산하고 a의 모든 비트를 or연산하고 a의 0번째 신호를 5개 붙여서 모두 그룹화해서 int에 할당하라는 의미이다. 위와 같이 단순한 조합회로를 간단하게 그룹화를 통해서 구현할 수도 있고, 연산과정에서 비트수가 맞지 않을 경우 overflow를 방지하기 위해서 하위비트에 0을 추가할 때에도 그룹화를 사용하기도 한다.