비틀린 상수
parameter는 상수입니다 - 시뮬레이션이 시작되기 전에 컴파일러가 값을 선택해 설계에 굳혀 넣습니다. 비틀린 부분은 모듈을 인스턴스화하는 누구든 값을 바꿀 수 있다는 점입니다. 같은 모듈 소스 파일이 호출자가 매개변수화한 방식에 따라 크기가 다른 회로를 만들 수 있습니다.
이게 재사용 가능한 IP를 만드는 방법입니다. WIDTH와 DEPTH parameter를 가진 FIFO 모듈 하나가 회사의 모든 팀에 봉사할 수 있습니다. WIDTH parameter를 가진 카운터는 16까지 세든 40억까지 세든 같은 소스입니다.
기본 parameter 선언
새 문법 세 가지.
- Parameter 블록: 모듈 이름과 port list 사이에
#( parameter WIDTH = 8 ). 기본값8은 누구도 오버라이드하지 않을 때 모듈이 사용하는 값입니다. - 사용처:
output reg [WIDTH-1:0] count. parameter는 그냥 상수입니다 - 비트 범위에 끼워 넣습니다. 같은 모듈 소스가WIDTH에 따라 8비트나 16비트 출력을 만들어 냅니다. - 오버라이드 문법: 인스턴스화 시
counter #(.WIDTH(16)) dut16(...).#(...)블록은 모듈 이름 다음, 인스턴스 이름 앞에 위치합니다. 언급하지 않은 parameter는 기본값을 유지합니다.
localparam: 내부 상수
호출자가 오버라이드하지 않았으면 하는 상수가 있습니다. 상태 인코딩이 전형적입니다.
localparam은 IDLE, RUNNING, DONE을 모듈 내부에서 쓸 수 있게 하면서 인스턴스화에서 오버라이드는 불가능하게 합니다. 옳은 선택입니다 - 외부에서 IDLE이 어떤 상태 값을 가리키는지 바꾸는 건 끔찍한 일입니다.
호출자가 구성해야 할 것(너비, 깊이, 동작 옵션)에는 parameter를, 나머지에는 localparam을 사용하세요. 흔한 패턴은 parameter로부터 localparam을 파생시키는 것입니다.
parameter WIDTH = 32;
localparam WIDTH_M1 = WIDTH - 1; // 한 번 계산; 오버라이드 불가
실전: 매개변수화된 너비
parameter의 가장 흔한 용도는 버스 너비를 유연하게 만드는 것입니다. 어떤 너비에서도 동작하는 가산기.
소스 파일 하나, 인스턴스 둘, 너비 둘, 복붙 없음. 이게 parameter의 효용 전부입니다.
여러 parameter, named 오버라이드
큰 모듈에는 종종 여러 parameter가 있습니다. 이름으로 어떤 순서로든 오버라이드하세요.
module fifo #(
parameter WIDTH = 8,
parameter DEPTH = 16,
parameter AFULL = DEPTH - 2
)(
input wire clk,
input wire reset,
// ... 포트들 ...
);
// ...
endmodule
// 호출 지점에서:
fifo #(.WIDTH(32), .DEPTH(1024)) cmd_queue (.clk(clk), .reset(reset), ...);
모든 parameter를 오버라이드해야 하는 건 아닙니다. 언급되지 않은 parameter는 기본값을 유지합니다. 예제의 AFULL 기본값은 DEPTH로부터 계산되므로, DEPTH를 오버라이드하면 AFULL도 자동으로 따라갑니다 - AFULL을 독립적으로 오버라이드하지 못하게 하고 싶다면 localparam이 처리할 만한 의존성이기도 합니다.
흔한 실수
오버라이드에서 #을 잊는 것. counter (.WIDTH(16)) dut(...)는 오버라이드처럼 보이지만 Verilog은 (.WIDTH(16))을 포트 연결로 읽습니다. counter #(.WIDTH(16)) dut(...)로 적어야 합니다.
상수가 아닌 곳에 parameter 사용. parameter는 elaboration 시점에 결정됩니다 - 신호가 존재하기 전입니다. 런타임 신호 위에서 매개변수화할 수 없습니다 - 값이 시뮬레이션 시간에 입력이 무엇을 하는지에 의존한다면 그건 parameter가 아니라 로직입니다.
port list에서 parameter와 localparam 혼동. 맨 위의 #(...) 블록에는 parameter만 들어갑니다. localparam은 본문 안에 살죠. 둘을 바꿔 쓰면 컴파일러가 알려 줍니다.
다음에 볼 내용
이제 컴파일 시점에 크기가 결정되는 모듈을 만들 수 있습니다. 다음 두 문서는 리터럴 상수(8'h1F, 4'b1010, 32'd100) 작성 규칙과, 신호가 구동되지 않거나 충돌할 때 등장하는 x와 z 값을 다룹니다. 그 다음에는 연산자로 이동합니다 - 이제까지 본 vector와 parameter로 할 수 있는 모든 것.
자주 묻는 질문
Verilog의 parameter란 무엇인가요?
parameter는 모듈 내부에 선언되는 컴파일 시점 상수입니다. 상수가 허용되는 곳이면 어디든 - 버스 너비, 메모리 깊이, 상태 인코딩, 기본값 - 사용할 수 있습니다. 중요한 점은 모듈의 각 인스턴스 가 parameter를 오버라이드할 수 있다는 것입니다. 같은 소스 파일이 한 곳에서는 8비트 카운터를, 다른 곳에서는 32비트 카운터를 만들어 낼 수 있습니다.
parameter와 localparam의 차이는?
parameter 값은 모듈을 인스턴스화할 때 외부에서 오버라이드할 수 있습니다. localparam 값은 그럴 수 없습니다 - 내부 상수일 뿐입니다. 모듈 작성자가 호출자에게 손대게 하고 싶지 않은 상태 인코딩과 파생 상수에는 localparam을 사용하세요.
Verilog에서 parameter는 어떻게 오버라이드하나요?
모듈을 인스턴스화할 때 인스턴스 이름 앞에 parameter 오버라이드 블록을 추가합니다: counter #(.WIDTH(16)) my_inst (.clk(clk), .count(count));. .WIDTH(16) 문법은 그 특정 parameter를 설정합니다. 나열하지 않은 parameter는 기본값을 유지합니다. 여러 오버라이드는 #(...) 안에서 쉼표로 구분합니다.
매개변수화된 모듈이란?
호출자가 오버라이드할 수 있는 parameter 선언을 하나 이상 노출하는 모듈입니다. WIDTH와 DEPTH parameter를 가진 FIFO 모듈 하나가 사내 모든 팀에 봉사할 수 있습니다 - 같은 소스가 한 곳에서는 32비트x16 깊이의 FIFO를, 다른 곳에서는 8비트x1024 깊이의 FIFO를 만들어 냅니다. 재사용 가능한 IP 블록 라이브러리는 이렇게 작성됩니다.