Menu

첫 Verilog 모듈: 단계별 따라하기

처음부터 완전한 Verilog 모듈을 작성해 봅니다 - 선언, 포트, 작은 조합 논리, 그리고 그것을 구동하는 testbench까지. 브라우저에서 바로 실행 가능합니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

모듈은 Verilog의 기본 단위

Verilog의 모든 것은 module 안에 살고 있습니다. 모듈은 회로의 한 조각을 감쌉니다. 어떤 신호가 들어오고, 어떤 신호가 나가며, 그 사이에 어떤 wire/register/논리가 있는지를 선언합니다. 여러분이 본 모든 칩은 다른 모듈을 인스턴스화하는 모듈들의 트리이며, 그 끝은 벤더가 제공한 게이트 primitive까지 내려갑니다.

형태는 항상 같습니다.

module name(port_list);
    // 선언: wire, reg, parameter
    // 본문: assign, 인스턴스화, always 블록
endmodule

단계적으로 완성된 모듈에 다가가 봅시다.

가장 작은 유용한 모듈: 2입력 AND 게이트

뭔가 단순하고 자기 완결적인 게 필요합니다. 2입력 AND 게이트가 딱입니다 - 입력 둘, 출력 하나, 로직 한 줄.

Run을 눌러 보세요. AND 진리표의 네 행을 볼 수 있을 겁니다. 각 부분을 자세히 살펴봅시다.

모듈 선언 읽기

module and_gate(
    input  wire a,
    input  wire b,
    output wire y
);
  • module and_gate는 이름이 and_gate인 모듈을 선언합니다. 이 이름이 다른 모듈에서 인스턴스화할 때 쓰는 이름입니다.
  • 괄호 안의 목록은 port list - 외부에서 보이는 신호들입니다.
  • input wire a - a는 입력 포트이며 wire(외부에서 구동됨)입니다.
  • output wire y - y는 모듈 내부의 무언가가 구동하는 출력 포트입니다.

짧게 쓰고 싶다면 input wire a 대신 input a라고 써도 됩니다 - 방향만 적으면 기본 타입이 wire입니다. 하지만 명시적으로 쓰는 습관을 들이는 게 좋습니다. Module Ports에서 포트 형태를 더 다룹니다.

본문

assign y = a & b;

이게 continuous assignment입니다. "언제든 ab가 바뀌면 y를 두 값의 비트와이즈 AND로 다시 계산하라"는 뜻입니다. clock도 timing도 없습니다 - 이 관계는 항상 참입니다. 그게 순수한 조합 논리입니다.

endmodule은 블록을 닫습니다. 모듈이 끝났습니다.

testbench

and_gate만으로는 실행할 수 없습니다. 그 입력을 구동하고 출력을 관찰하는 두 번째 모듈이 필요합니다. 그게 testbench이고, 관례상 test, tb, 또는 <design>_tb라고 부릅니다.

module test;
    reg  a, b;
    wire y;

    and_gate dut(.a(a), .b(b), .y(y));
    ...
endmodule

세 가지 주목할 점.

  • port list가 없음. testbench는 시뮬레이션의 최상위입니다 - 그 바깥에는 아무것도 없습니다.
  • reg a, bwire y. design under test(DUT)의 입력은 testbench에서 reg인데, procedural block에서 구동 하기 때문입니다. 출력은 DUT가 구동하므로 wire입니다.
  • and_gate dut(.a(a), .b(b), .y(y)). 이게 인스턴스화입니다. and_gate의 사본을 찍어 내고 그것을 dut(흔한 이름 - "design under test")라고 부릅니다. .a(a) 문법은 "이 인스턴스의 a라는 포트를 로컬 a 신호에 연결하라"는 뜻입니다. Module Instantiation에서 더 깊이 다룹니다.

자극(stimulus)

initial begin
    a = 0; b = 0; #1 $display("a=%b b=%b y=%b", a, b, y);
    a = 0; b = 1; #1 $display("a=%b b=%b y=%b", a, b, y);
    a = 1; b = 0; #1 $display("a=%b b=%b y=%b", a, b, y);
    a = 1; b = 1; #1 $display("a=%b b=%b y=%b", a, b, y);
    $finish;
end

initial 블록은 시뮬레이션 시작 시 한 번 실행됩니다. 내부에서는

  • a = 0; b = 0;이 입력을 구동합니다. 이건 blocking assignment입니다 - 순서가 의미가 있고, 각 문장은 다음 문장 전에 일어납니다.
  • #1은 시뮬레이션 시간을 1 단위 진행시킵니다. 입력이 바뀐 뒤 y가 안정될 시간이 필요합니다. #1이 없으면 $displayy의 옛 값을 출력하게 됩니다.
  • $display(...)는 시뮬레이션 콘솔에 출력합니다. format string은 C의 printf처럼 동작합니다: %b는 binary, %d는 decimal, %h는 hex, %t는 시뮬레이션 시간.
  • $finish는 시뮬레이션을 종료합니다. 없으면 시뮬레이터가 영원히 시간을 진행하며 결코 오지 않을 이벤트를 기다립니다.

직접 망가뜨려 보기

모듈을 OR 게이트로 바꿔 보세요(&|로). 진리표가 뒤집힙니다. 이제 XOR도 해 보세요.

뼈대는 같고 연산자만 다릅니다. 조합 논리의 게임은 이게 전부입니다 - 포트를 선언하고, assign을 쓰고, 입력을 sweep하고, 출력을 관찰합니다.

출력이 두 개인 모듈

모듈은 출력이 여러 개일 수도 있습니다. half-adder입니다 - 입력 둘, sum과 carry-out.

assign 문은 나란히 있지만 병렬로 일어납니다 - "먼저 sum을 계산하고 그다음에 carry"가 아닙니다. 둘 다 항상 참입니다. 그게 하드웨어 vs 소프트웨어에서 이야기한 concurrency를 구체화한 모습입니다.

지금까지 알게 된 것

여러분은 Verilog 파일의 전체 골격을 봤습니다 - 포트가 선언된 설계 모듈과 본문, 그리고 그것을 인스턴스화하고 initial 블록에서 입력을 구동하며 결과를 보고하는 testbench 모듈. 앞으로 보게 될 거의 모든 Verilog 소스 파일이 이 템플릿에 들어맞습니다. 나머지는 더 풍부한 로직, 멀티비트 신호, clocked 동작, 더 큰 testbench로 채워 가는 일입니다.

다음에 볼 것: comment와 코드 스타일. 모듈이 커져도 가독성을 유지하는 방법입니다.

자주 묻는 질문

가장 단순한 Verilog 모듈은 무엇인가요?

가장 작은 합법적인 Verilog 모듈은 module name; endmodule - 포트도 없고 본문도 없는 것입니다. 가장 작은 유용한 모듈은 단일 출력 모듈입니다: module and_gate(input wire a, input wire b, output wire y); assign y = a & b; endmodule. 이건 어떤 큰 설계에도 그대로 넣을 수 있는 실제 조합 논리입니다.

Verilog 모듈은 어떻게 실행하나요?

모듈을 단독으로 실행할 수는 없습니다 - 그것은 회로 기술이지 프로그램이 아닙니다. 설계를 인스턴스화하고 입력을 구동하는 testbench 모듈을 작성한 다음, iverilog -o sim design.v test.v로 둘 다 컴파일하고 vvp sim으로 실행합니다. 이 페이지의 브라우저 에디터는 Run을 누르면 두 단계를 모두 자동으로 처리합니다.

Verilog의 testbench는 무엇인가요?

testbench는 두 번째 모듈입니다 - 보통 포트가 없고 - 여러분의 설계를 동작시키는 것이 임무입니다. 설계를 인스턴스화하고, initial 블록을 통해 입력을 흔들고, $display$monitor로 출력을 관찰하고, 끝나면 $finish를 호출합니다. testbench는 synthesis 대상이 아닙니다 - 동작 검증을 위한 것입니다.

Verilog 코드에 왜 $finish가 필요한가요?

하드웨어는 멈추지 않기 때문입니다. 시뮬레이터는 시간이 흐르는 척하므로, 명시적인 $finish가 없으면 새로운 이벤트를 기다리며 영원히 시간을 진행합니다. $finish는 시뮬레이터에게 '끝났으니 깔끔하게 종료하라'고 알립니다. testbench에서는 initial 블록의 마지막 줄입니다 - 테스트를 실행한 다음 finish.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기