[FPGA와 Verilog 초보자 가이드 2] 모듈

Verilog 자체를 배우는 것은 어려운 일은 아닙니다. 하지만 좋은 디자인을 만들어 내는 것은 어려운 일일수 있죠. 본 강좌에서는 간단한 디자인에 포커스를 맞추어 설명하도록 하겠습니다.

Verilog를 처음 시작하면 대부분은 MCU에서 동작하는 코드를 C언어로 작성하는 것과 같이 인스트럭션을 순차적으로 작성하곤 합니다. 하지만 FPGA, CPLD, ASIC과 같은 디지털회로는 한번에 여러 개의 일을 수행할 수 있습니다. 이러한 디지털회로의 기능을 이용하려면 먼저 동시에 발행하는 여러 가지 일들을 비주얼화 하는 방법을 배워야 합니다.


Verilog 모듈

Verilog는 디지털 회로를 다루고 Verilog에서 모듈은 디지털 회로의 하나의 컴포넌트를 의미합니다. 이러한 컴포넌트는 아주 간단히는 게이트가 될수 있고 ALU나 메모리와 같은 복잡한 회로가 될수 도 있습니다. 모듈은 모듈내에 필요한 요소들을 가지고 있다는 점에서 그리고 여러개의 메소드로 외부와 인터페이싱된다는 점에서 C++의 클래스 개념과 유사합니다. C++의 클래스처럼 모듈 역시 인스턴스화 시킬 수 있지만, 모듈은 100% 클래스와 같지는 않습니다. 모듈을 그래픽하게 표현한다면 여러개의 포트를 가지고 있는 하나의 박스로 표현할 수 있습니다. 포트는 입력, 출력 혹은 입출력 둘다가 될 수 있으며, 포트는 한개 비트 너비를 가질수 있고 또 여러개의 비트 너비를 가질수도 있습니다. 아래의 그림은 여러개의 입출력을 가진 모듈을 그린 그림입니다. 입력, 출력의 숫자와 그것들의 너비와 방향은 모듈 기능에 의해 전적으로 종속됩니다.


포트는 입력이나 출력 혹은 양방향이 될수 있습니다. Verilog의 기능을 간단히 말한다면 모듈들을 생성하고, 모듈을 서로 연결시키고, 상호동작의 타이밍을 관리하는 것이라고 볼 수 있습니다.

그러면 Verilog식 "Hello World" 프로그램을 작성하여 볼까요? 먼저 Verilog에서 NOT 게이트(인버터)를 디자인하고, 시뮬레이션 한뒤 실제 하드웨어에서 테스트하여 보겠습니다. NOT 게이트의 출력은 항상 입력값의 반대값입니다. 아래는 NOT게이트의 진리표입니다.

INPUT

A

OUTPUT

B = NOT A

0

1

1

0

NOT 게이트는 하나의 입력과 하나의 출력을 가지고 B =! A라는 내부 동작을 가진 하나의 모듈로 볼 수 있습니다. 인버터 모듈의 그린 그림은 아래와 같습니다.


그럼 이 모듈을 Verilog에서 어떻게 표현 할 수 있는지 살펴 보면 아래와 같습니다.

module myModule(A, B);
input wire A;
output wire B;
assign B = !A;
endmodule

작성된 모듈의 이름은 myModule 인데, 첫번째 라인의 "module"이라는 키워드를 이용하여 선언이 되었습다. 이 "module" 키워드를 이용하여 myMoudle을 정의하고 두개의 포트를 할당하였습니다. myModule 안에 있는 모든 것들은 키워드 "module"과 "endmodule"사이에 위치하게 됩니다. myMoudle은 한 비트 너비의 두개의 포트를 가지고 있으며, 포트의 크기나 방향은 첫번째 줄에서는 알수 없습니다.

두세번째 줄에서는 포트 A와 포트 B가 각각 입력과 출력으로 선언되었습니다. Verilog에서는 두개의 기본적인 데이터 타입이 있는데 "wire"와 " reg"입니다. int, real 등과 같은 다른 많은 데이터 타입도 있습니다만 wire와 reg는 verilog에서 매우 중요한 자리를 차지하고 있는 데이터 타입입니다.

"wire"는 두개의 다른 물체를 전기적으로 연결하는 전선과 같은 역활을 합니다. 전위차를 구리선의 한쪽끝에 주게 되면 전위차를 없애기 전까진 계속 유지되고, 이것은 Verilog에서도 그대로 적용됩니다. wire는 인가된 로직상태를 유지하게 되며, 아무런 로직도 인가되지 않았다면 wire의 상태는 unknow 상태가 됩니다. Verilog에서는 "wire"는 모듈내의 것들을 연결하거나 혹은 모듈끼리 연결하는데 사용됩니다.

반면에 "reg" 키워드는 로직 상태를 저장할 수 있고, 누군가 그것을 바꾸기 전까진 로직상태를 유지할 수 있습니다. 마이크로컨트롤러의 레지스터와 같은 개념으로 보면 되겠습니다. 이것은 플립플롭과 비슷합니다. 플립플롭을 한가지 상태에 두게 만든다면 누군가 그 상태를 변경할 때까지 플립플롭은 그 상태를 유지합니다.

정리하면, "wire"를 이용해 모듈의 입력이나 출력을 설정할 수 있고, "reg"는 모듈의 출력으로 이용이 가능합니다. "wire"가 출력으로 사용이 되었을때에는 모듈내에서 "wire" 상태를 변화시킬수 있는 "reg"가 있어야하는데, 그래야 wire가 의미있는 데이터를 가질 수 있기 때문입니다. 만약 "reg"가 출력으로 사용되지 않는다면, reg는 고유의 데이터를 보관합니다.

그러면 왜 myModule는 입출력 둘다를 wire로 선언하였을까요? 대답은 모듈이 NOT 게이트를 표현하였기 때문입니다. 게이트는 데이터값을 저장하지 않고 게이트의 출력은 오직 입력 값에 의해 결정됩니다. 입력에 어떤 로직상태가 들어온다면 출력은 그 로직의 반대가 될것이고 아무런 로직도 입력에 들어오지 않는다면 출력값은 unknown상태가 됩니다. 그래서 이 모듈의 출력이 의미 있는 값을 가지게 할려면 항상 어디선가는 입력값을 주어야 합니다.

위의 코드에서 중요한 또다른 키워드는 "assign"입니다. assign 키워드는 combinational circuit을 생성하는데 사용이 됩니다. '=' 기호 우측의 코드들은 평가되고 결과값은 '='의 좌측에 할당 되며 이러한 것은 비동식으로 처리됩니다. 오른쪽에 변화가 생기자마자 그 결과값은 왼쪽에 반영이 됩니다.

이제, 위의 코드를 가지고 코드를 시뮬레이션하여 예상대로 동작하는지 살펴보겠습니다. 넓은 의미로 시뮬레이션은 알려진 입력값에 대해 알려진 출력값을 확인하는 작업입니다. 출력값이 올바르게 확인되었다면 이것을 검증이라고 부릅니다. 시뮬레이션과 검증 툴이 많이 있는데, 여기서는 iSim(Xilinx ISE Webpack의 일부분) 을 시뮬레이션과 웨이브폼 검사에 사용하겠습니다.

Comments