모듈의 인터페이스
모듈의 port list는 그 인터페이스 입니다 - 바깥 세상이 볼 수 있고 만질 수 있는 모든 것. 모듈 내부의 본문은 입력으로부터 출력을 계산합니다. 포트는 그 경계를 넘는 wire입니다.
현대의 ANSI 스타일 선언은 방향, 타입, 너비를 모두 인라인으로 둡니다.
module my_module(
input wire clk,
input wire reset,
input wire [7:0] data_in,
output reg [7:0] data_out,
output wire valid
);
// 본문
endmodule
이게 전체 모양입니다. 각 포트는 다음을 가집니다.
- 방향:
input,output,inout. - 타입:
wire또는reg(SystemVerilog에서는logic). - 너비:
[7:0]같은 범위. 생략하면 단일 비트. - 이름: 본문에서 사용할 식별자.
세 가지 방향
input - 바깥에서 구동됨
input은 항상 wire입니다. 구동 측이 모듈 바깥에 있습니다 - 상위 모듈의 신호이거나 testbench의 reg이거나. 이 모듈 안에서는 입력을 식 안에서 읽을 수만 있고 대입할 수는 없습니다.
module reader(input wire [7:0] data);
initial $display("data = %h", data);
endmodule
모듈 안에서 data = ...라고 쓰면 오류입니다.
output - 안에서 구동됨
output은 이 모듈 안의 무언가가 구동합니다. wire(assign이나 서브모듈 출력으로 구동)일 수도 있고 reg(always/initial로 구동)일 수도 있습니다.
module driver(
input wire a,
input wire b,
input wire clk,
output wire y, // wire - assign으로 구동
output reg q // reg - always로 구동
);
assign y = a & b; // y가 wire이므로 OK
always @(posedge clk)
q <= a; // q가 reg이므로 OK
endmodule
always 안에서 y에 대입하거나, assign으로 q를 구동하려고 하면 컴파일 오류입니다. 키워드 선택이 구동 방식과 일치해야 합니다.
inout - 양방향
inout 포트는 모듈 또는 외부의 무언가가 번갈아 가며 구동할 수 있는 wire입니다. 칩 경계 - I²C SDA 라인, 양방향 GPIO 핀, 공유 데이터 버스 등 - 에서 등장합니다. 모듈 내부에서는 tri-state 패턴으로 방향을 제어합니다.
module bidir_pin(
input wire data_out,
input wire output_enable,
output wire data_in,
inout wire pin
);
assign pin = output_enable ? data_out : 1'bz;
assign data_in = pin;
endmodule
pin이 공유 wire입니다. output_enable이 high이면 모듈이 pin을 data_out으로 구동합니다. low이면 핀을 high-impedance(z)로 풀어 줘서 외부 드라이버가 사용할 수 있게 합니다. data_in은 핀 위에 현재 무엇이 있든 그것을 항상 관찰합니다.
순수 내부 로직에서는 inout이 드뭅니다. 메모리 컨트롤러, CPU 코어, 이미지 처리 파이프라인을 만든다면 한 번도 필요하지 않을 수 있습니다. 칩 경계의 I/O ring을 위한 기능입니다.
단일 비트 vs 멀티 비트 포트
너비 범위는 선택입니다 - 단일 비트 포트라면 생략합니다.
input wire valid, // 1비트
input wire [7:0] data, // 8비트
input wire [31:0] addr, // 32비트
parameter를 너비로 사용할 수도 있습니다. 이게 매개변수화 모듈이 동작하는 방식입니다.
module bus #(
parameter WIDTH = 32
)(
input wire [WIDTH-1:0] in,
output wire [WIDTH-1:0] out
);
assign out = in;
endmodule
ANSI vs Verilog-1995 스타일
가끔 port list와 선언을 나눠 둔 옛 코드가 보일 수 있습니다.
// 옛 Verilog-1995 스타일 - 새 코드에서는 쓰지 마세요
module foo(clk, data_in, data_out);
input clk;
input [7:0] data_in;
output reg [7:0] data_out;
// ...
endmodule
port list에는 이름만 있고, 각 포트는 본문 안에서 따로 선언됩니다. 장황하고, "port list에는 있는데 선언되지 않음" 오류가 잘 나며, 타이핑이 두 배입니다. ANSI-2001 스타일(이 문서 전반에 나오는 스타일)이 이를 대체합니다.
module foo(
input wire clk,
input wire [7:0] data_in,
output reg [7:0] data_out
);
// ...
endmodule
ANSI 스타일을 사용하세요. 도구들은 2001년부터 이 스타일을 지원해 왔습니다.
완전한 예시
이 한 모듈에는 다음이 있습니다.
- parameter(
WIDTH). - clock, reset, load enable, data, shift enable 입력.
always블록 안에서 구동되는reg출력(data_out).- continuous assignment로 구동되는
wire출력(msb_out). - 각 포트의 방향, 타입, 너비를 인라인으로 적은 완전한 ANSI 스타일 선언.
여러분이 작성할 거의 모든 synthesis 가능 모듈이 이런 형태입니다.
다음에 볼 내용
다음 문서 - Module Instantiation - 은 이 모듈을 가져다가 더 큰 설계 안에서 사용하는 방법을 보여 줍니다. 방금 작성한 shifter는 그 자체로는 쓸모가 없습니다. 무언가가 그것을 인스턴스화해 시스템에 연결할 때 비로소 값어치를 합니다.
자주 묻는 질문
Verilog에는 어떤 포트 방향이 있나요?
input, output, inout 세 가지입니다. input은 모듈 바깥에서 구동됩니다. output은 모듈 내부에서 구동됩니다. inout은 양방향입니다 - 모듈이 같은 핀을 구동하기도 하고 읽기도 하는 tri-state bus에 유용합니다. 내부 포트의 대부분은 input이나 output이고, inout은 칩 핀에서만 등장합니다.
output wire와 output reg의 차이는 무엇인가요?
output wire는 출력이 continuous assignment나 서브모듈 출력 같은 - always 블록 외부의 무언가 - 가 구동한다는 뜻입니다. output reg는 procedural block(initial이나 always)에서 구동한다는 뜻입니다. 키워드는 always 안에서 그 신호를 대상으로 삼을 수 있는지를 결정하는 것이지, synthesize된 하드웨어에 플립플롭이 들어가는지를 결정하는 게 아닙니다.
Verilog 포트의 ANSI 스타일이란 무엇인가요?
ANSI 스타일은 각 포트의 방향과 타입을 port list 안에서 인라인으로 선언합니다: module foo(input wire [7:0] data, output reg [7:0] result);. 이전의 Verilog-1995 스타일은 port list에 이름만 적고 모듈 안에서 다시 선언했습니다. 새 코드에서는 항상 ANSI 스타일을 사용하세요 - 짧고, 오류가 적고, 현대적인 환경에서는 어디서나 표준입니다.
Verilog 모듈에 input과 output을 여러 개 둘 수 있나요?
네 - 모듈은 필요한 만큼의 포트를, 어떤 방향 조합으로든 가질 수 있습니다. port list에서 쉼표로 구분합니다. port list의 순서는 인스턴스화 시 positional connection 순서를 결정하지만, 순서에 의존하지 않기 위해 거의 항상 named connection(.port(signal))을 사용합니다.