수성컴전자방입니다. 오늘은 Verilog의 기초적인 내용을 공부하고, AND, OR, NOT 등의 Gate를 만들어 보겠습니다.
- IDE: Intel Quartus 18.1
- Device: Cyclone V 5CSXFC6D6F31C6(밑에서 6번째)
- Simulation: ModelSim-Altera, Verilog HDL
목차
1. Intel Quartus 새 프로젝트 생성
2. Module
3. input, output, inout, wire
4. Instance
5. 정수 표현과 multiple bit
6. Opeator
7. built-in module
8. 예제
8.1. Top.v
8.2. gates.v
9. 글 마무리
10. 참고 문헌
1. Intel Quartus 새 프로젝트 생성
1.1. 왼쪽 위 파란색으로 동그라미 친 아이콘을 누르거나 Ctrl+N을 누릅니다.
1.2. New Quartus Prime Project를 클릭합니다.
1.3. Next를 클릭합니다.
1.4. working directory를 반드시 변경합니다.
1.5. Project 이름과 top-level design 이름을 입력합니다.(Project 이름을 입력하면 top-level design 이름도 기본적으로 동일하게 설정됨.)
1.6. Next를 클릭합니다.
1.7. 추가할 파일이 있으면 추가합니다. 저는 개인적으로 Project 경로에 추가할 파일들을 다 넣어둔 뒤 Add All을 클릭하는 것을 좋아합니다. 오늘은 첫 글이라서 추가할 파일이 없으므로 그냥 Next를 누르겠습니다.
1.8. Family를 설정합니다.(이 글은 Cyclone V 기준)
1.9. device를 선택합니다.(이 글은 5CSXFC6D6F31C6 기준. 이는 밑에서 6번째입니다.)
1.10. Next를 클릭합니다.
1.11. EDA(Electronic Design Automation) Tool을 설정합니다.(이 글은 ModelSim-Altera, Verilog HDL 기준)
1.12. 이제 Project 생성은 끝났습니다. Finish를 클릭합니다.
1.13. 다시 왼쪽 위 아이콘 또는 Ctrl+N을 누릅니다.
1.14. Verilog HDL File을 선택합니다.
1.15. 그러면 확장명이 .v인 파일이 생길 텐데 다른 이름으로 저장(Save As)합니다.
1.16. 저장합니다. 저는 편의상 파일명을 Top.v로 하겠습니다.(1.5번에서 Project 이름을 Top으로 했기 때문.)
(위의 스크린샷은 Top.v 외에 and2.v를 추가로 생성한 모습입니다.)
1.*. 왼쪽 위 Project Navigator가 처음에는 ‘Hierarchy’로 되어 있을 것입니다. 이것을 ‘Files‘로 변경하면 프로젝트에 추가된 파일 목록을 보실 수 있습니다.
2. Module
“Divide and Conquer”라는 말이 있습니다. 문제가 있을 때 분할하여 정복하라는 것입니다. 하드웨어 설계도 마찬가지입니다. 모듈화하여 설계하는 것이 편리합니다. 이때 기본적인 block이 되는 것이 바로 module입니다. Syntax는 아래와 같습니다.
module module_name ( input_output_list );
module_body;
endmodule;
예)
module _and2(y, a, b);
input a, b;
output y;
assign y=a&b;
endmodule
위의 예시는 2-input AND Gate입니다.
1행을 보십시오. module_name은 _and2이고, input_output_list는 y, a, b입니다. 그리고 1행이 semicolon(;)으로 끝난다는 것을 잊으시면 안 됩니다.
(2~5행은 다음 문단 이후에 설명 드리겠습니다.)
6행을 보시면 endmodule로 끝납니다. 여기가 바로 _and2 module이 끝나는 지점입니다. 여기는 semicolon(;)을 붙이지 않습니다.
이렇게 만든 module을 사용하려면 instance를 만들어야 합니다. instance를 공부하기 전에 먼저 port를 알아보겠습니다.
3. input, output, inout, wire
module의 input과 output 신호들을 port라고 합니다. port는 input, output, inout이 있습니다. 참고로 inout은 bi-directional port(input과 output이 동시에 되는 것)입니다. 앞으로 inout은 따로 언급하지 않겠습니다.
module과 module을 연결하기 위해서는 wire를 사용합니다. 1-bit wire는 선언 없이 사용할 수 있지만 2-bit 이상 wire를 사용하려면 꼭 선언해야 합니다.
2번 문단의 예시를 이어서 보겠습니다. port는 y, a, b입니다.
2행에 따라 a, b는 input입니다.
3행에 따라 y는 output입니다.
5행을 보면 assign y=a&b;
라고 나와 있는데 그냥 y=a&b;
라고 쓰면 안 되고 assign을 붙여야 합니다. assign문에서 ‘=’의 왼쪽에는 output이나 wire가 들어갈 수 있습니다.
합성결과)
참고) assign 없이 y=a&b;
라고 쓰려면 y를 reg로 선언해야 합니다.
output y;
reg y;
또는 output reg y;
4. Instance
module을 거푸집이라고 한다면, instance는 거푸집으로 찍어낸 객체와 같습니다. instance를 C++의 object에 비유할 수도 있겠습니다. Syntax는 아래와 같습니다.
module_name instance_name ( port_list );
예) 2번 문단의 예시에 나온 module이 있는 상태에서 진행.
module _and3(y, a, b, c);
input a, b, c;
output y;
wire w0;
_and2 U0_and2(.y(w0), .a(b), .b(a));
_and2 U1_and2(y, w0, c);
endmodule
6~7행 모두 and2 module의 instance입니다.
6행에서는 ‘U0_and2’라는 instance를 만듭니다. port_list를 작성할 때 각 port마다 .y(w0)처럼 작성된 것을 보실 수 있습니다.
.y(w0)을 예시로 설명하자면 여기서 ‘.’을 ‘dot operator’라고 하며 port와 port를 연결하는 역할을 합니다. y는 module을 작성할 때 괄호 안에 작성한 port의 이름입니다.
괄호 안의 w0는 C언어의 함수로 치면 parameter라고 생각하시면 됩니다. 정확하게는 y를 w0로 연결한다는 뜻입니다.
참고로 and2 U0_and2(.a(b), .b(a), .y(w0));로 순서를 바꾸어 써도 됩니다.
7행에서는 ‘U1_and2’라는 instance를 만듭니다. 여기서는 port_list를 작성할 때 module의 dot operator와 port 이름을 쓰지 않고 C언어의 함수와 유사한 상태로 사용하는 것을 보실 수 있습니다.
1번 문단의 예시에서 작성한 순서대로 y에 y를, a에 w0를, b에 c를 연결합니다.
이렇게 쓰는 경우(dot operator를 쓰지 않는 경우)에는 6행과 달리 순서를 바꿔 쓸 수 없습니다.(학교 시험 볼 때 꿀팁일 수도..?^^)
합성 결과)
5. 정수 표현과 multiple bit
Syntax: [[<size>]'<radix>]value
대괄호 안에 있는 것은 선택사항입니다.
radix는
- b: 2진수(binary)
- h: 16진수(hexadecimal) 입니다.
예1) 8’b0101_0001은 8-bit binary value로서 81입니다.(중간의 _는 편의상 쓰는 구분자이며, 없어도 무방합니다.)
예2) 16’b1011_0010_0101_0001==16’hb251==45649
port, wire 등을 multiple bit으로 선언하려면 이름 앞에 [n-1:0]을 붙입니다.(little-endian order)
예) 4-bit input을 사용하려면: input [3:0] d; d=4’b0111;
6. Operator
- Unary Operator
- NOT: ! 또는 ~
- Bit-wise Operators
- AND: &
- OR:
|
- XOR: ^
- XNOR: ^~
- Logical Operators
- Logical AND: &&
-
Logical OR:
참고로 Logical Operator의 연산 결과는 1-bit 정수(0 또는 1)입니다.
위의 operator들 외에도 ==, !=, ? 등의 연산자가 있습니다. C언어와 거의 유사하므로 오늘은 설명을 생략하겠습니다. 나중에 기회가 되면 깊이 다루어 보도록 하겠습니다.
예1) assign y=a|b;
예2) 2’b01 & 2’b11 == 2’b01 (Bit-wise Operator)
예3) 2’b01 && 2’b11 == 1’b0 (Logical Operator)
7. built-in module
기본적인 논리 Gate는 직접 module을 만들지 않고 built-in module을 사용할 수도 있습니다.
and(y, a, b);
or(y, a, b);
not(y, a);
xor(y, a, b);
nand(y, a, b);
nor(y, a, b);
예)
module Top(y, a, b);
input a, b;
output y;
nand(y, a, b);
endmodule
NAND Gate를 사용해 보았습니다. CMOS Gate에서 AND Gate는 NAND Gate에 inverter를 붙인 것이기 때문에 NAND를 구현할 때 AND와 NOT을 쓰는 것보다 NAND Gate를 쓰는 것이 더 저렴합니다. 그러나…
합성 결과)
FPGA 때문인지 오히려 nand built-in module을 써도 AND Gate와 inverter로 구현해 주더라고요. 신기했습니다.
8. 예제
Project Name & Top-level design: Top
2-input AND Gate를 만드는 예제를 알아보겠습니다.
8.1. Top.v
module Top(y, a, b);
input a, b;
output y;
//6~8행 중 하나만 사용
_and2 _and20(y, a, b); //module
assign y=a&b; //operator
and(y, a, b); //built-in
endmodule
위의 예제에서 6~8행 중 하나만 남기고 다른 것은 지우셔야 컴파일됩니다.
8.2. gates.v
module _and2(y, a, b);
input a, b;
output y;
assign y=a&b;
endmodule
module _or2(y, a, b);
input a, b;
output y;
assign y=a|b;
endmodule
module inv(y, a);
input a;
output y;
assign y=~a; //assign y=!a; is also available
endmodule
module _xor2(y, a, b);
input a, b;
output y;
assign y=a^b;
endmodule
module _nand2(y, a, b);
input a, b;
output y;
assign y=~(a&b);
endmodule
module _nor2(y, a, b);
input a, b;
output y;
assign y=~(a|b);
endmodule
이 module들을 사용하시려면 별도 파일로 저장하신 후 프로젝트에 그 파일을 추가하시면 됩니다.(프로젝트에서 새 파일을 만들면 따로 추가 과정 불필요)
9. 글 마무리
제 글을 읽어 주셔서 감사합니다. 글에 오류가 있다면 댓글 남겨 주시면 감사하겠습니다. 다음에 만나요!
10. 참고 문헌
1) David Money Harris, Sarah L. Harris. 2013. Digial Design and Computer Architecture. 2nd Edition. Elsevier Korea L.L.C.