모듈의 설정값을 외부에서 변경 가능하게 만드는 상수
하나의 모듈 설계로 다양한 비트폭, 크기, 동작을 가진 회로를 생성할 수 있다.
Parameter 는 모듈 외부에서 변경이 가능한 반면, localparam 은 외부에서 변경이 불가하다.
module counter (
input clk,
input rst,
output [WIDTH-1:0] count
);
// 외부에서 변경 가능
parameter WIDTH = 8;
parameter MAX_COUNT = 255;
// 내부 전용 (외부 변경 불가)
localparam HALF = MAX_COUNT / 2;
reg [WIDTH-1:0] cnt;
always @(posedge clk or posedge rst) begin
if (rst)
cnt <= 0;
else if (cnt == MAX_COUNT)
cnt <= 0;
else
cnt <= cnt + 1;
end
assign count = cnt;
endmodule
module fifo #(
parameter DEPTH = 16,
parameter WIDTH = 8
)(
input clk,
input rst,
input [WIDTH-1:0] din,
output [WIDTH-1:0] dout
);
// 파라미터를 포트 선언에도 사용 가능!
endmodule
위와 같이 포트 선언부쪽에 Parameter를 선언하는것이 권장된다.
위와 같이 코드를 짜야 파라미터가 in/out Port의 폭에 영향을 줄 때 명확하게 코드의 구조를 파악할 수 있다.
module fifo (input clk, ...);
parameter DEPTH = 16;
parameter WIDTH = 8;
// ...
endmodule
이는 구형 스타일의 paramter 선언 위치로, 권장되지 않는다고한다.
인스턴스 생성시 #으로 Parameter 값을 보내주면된다.
module top;
wire [15:0] data_in, data_out;
wire clk, rst;
// 기본값 사용 (DEPTH=16, WIDTH=8)
fifo u_fifo_default (
.clk(clk),
.rst(rst),
.din(data_in[7:0]),
.dout(data_out[7:0])
);
// 파라미터 변경 (DEPTH=32, WIDTH=16)
fifo #(
.DEPTH(32),
.WIDTH(16)
) u_fifo_large (
.clk(clk),
.rst(rst),
.din(data_in),
.dout(data_out)
);
// 순서 기반 전달 (비권장 ⚠️)
fifo #(64, 32) u_fifo_huge (...);
endmodule
위와같이 인스턴스 생성시 모듈명 파라미터 인스턴스명 포트연결 순으로 적어주면 하위 모듈의
Parameter를 변경할 수 있다.
// 옛날 방식 - 사용하지 마세요!
module top;
fifo u_fifo (...);
defparam u_fifo.DEPTH = 32; // 인스턴스 후에 변경
defparam u_fifo.WIDTH = 16;
endmodule
위와 같이 인스턴스 생성 후에 defparam을 이용해 하위 모듈의 parameter를 변경할 수 있다.
그러나 이는 비권장되는 방법이다.
module reg_file #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 5,
parameter NUM_REGS = 32 // 2^ADDR_WIDTH
)(
input clk,
input we,
input [ADDR_WIDTH-1:0] addr,
input [DATA_WIDTH-1:0] wdata,
output [DATA_WIDTH-1:0] rdata
);
// localparam으로 내부 상수 계산
localparam DEPTH = 1 << ADDR_WIDTH; // 2^ADDR_WIDTH
reg [DATA_WIDTH-1:0] regs [0:NUM_REGS-1];
always @(posedge clk) begin
if (we)
regs[addr] <= wdata;
end
assign rdata = regs[addr];
endmodule
참고로 여기서
reg [DATA_WIDTH-1:0] regs [0:NUM_REGS-1];
는 reg [DATA_WIDTH-1.0] 타입의 배열 regs를 의미한다.
(배열의 크기는 NUM_REGS 이다)
위의 모듈의 Parameter를 수정해보자.
// 사용 예시
module cpu;
// 32비트 CPU용 (32개 레지스터)
reg_file #(
.DATA_WIDTH(32),
.ADDR_WIDTH(5)
) u_gpr (...);
// 8비트 MCU용 (16개 레지스터)
reg_file #(
.DATA_WIDTH(8),
.ADDR_WIDTH(4),
.NUM_REGS(16)
) u_mcu_reg (...);
endmodule
위와 같이 객체 생성시 파라미터값을 넘겨줘서 , 하위 모듈의 파라미터값을 수정할 수 있다.
위의 모듈에서는 Parameter를 활용해 32비트 cpu용 레지스터 32개, 8비트 cpu용 레지스터 8개
를 만들고있다.
module buffer #(
parameter WIDTH = 8,
parameter DEPTH = 4,
// 다른 파라미터로부터 계산
parameter ADDR_BITS = $clog2(DEPTH) // log2 함수
)(
input [ADDR_BITS-1:0] addr,
input [WIDTH-1:0] din,
output [WIDTH-1:0] dout
);
reg [WIDTH-1:0] mem [0:DEPTH-1];
// ...
endmodule
위와 같이 파라미터 A를 파라미터 B로부터 자동으로 계산되도록 (의존성)할 수 있다.
위의 코드의 경우 메모리의 깊이 (DEPTH) 에 따라서 메모리의 비트를 나타내는 ADDR_BITS가
변화해야한다(즉, 의존성이 존재한다).
이렇게 A와 B가 의존성이 있는 경우 둘 중 하나가 바뀌면 사람이 일일이 수정해줘야하는데, 이는 너무 귀찮다.
이때, 우리는 파라미터를 사용해서 이런 의존성을 자동화 한다.
예를들어 위의 코드에서는 시스템 태스크인 $clog2() (log2(n)을 올림한 정수)를 활용해
ADDR_BITS이 DEPTH에 따라 자동으로 변하도록 한다.