[Verilog 문법] 3.2 파라미터

YUN·2026년 1월 22일

디지털 회로 설계

목록 보기
11/20
post-thumbnail

1. 파라미터(Parameter)

모듈의 설정값을 외부에서 변경 가능하게 만드는 상수

하나의 모듈 설계로 다양한 비트폭, 크기, 동작을 가진 회로를 생성할 수 있다.

2. Parameter 과 Localparam

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

3. Parameter 선언 위치

(1) 포트 선언부(권장)

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의 폭에 영향을 줄 때 명확하게 코드의 구조를 파악할 수 있다.

(2) 모듈 본문 내부 (구형 스타일, 비권장)

module fifo (input clk, ...);
    parameter DEPTH = 16;
    parameter WIDTH = 8;
    // ...
endmodule

이는 구형 스타일의 paramter 선언 위치로, 권장되지 않는다고한다.

4. 모듈 외부에서 Paramter 값 변경하기

(1) 인스턴스 생성시 # 이용

인스턴스 생성시 #으로 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를 변경할 수 있다.

(2) 인스턴스 생성 후에 defparam 이용

// 옛날 방식 - 사용하지 마세요!
module top;
    fifo u_fifo (...);
    
    defparam u_fifo.DEPTH = 32;  // 인스턴스 후에 변경
    defparam u_fifo.WIDTH = 16;
endmodule

위와 같이 인스턴스 생성 후에 defparam을 이용해 하위 모듈의 parameter를 변경할 수 있다.

그러나 이는 비권장되는 방법이다.

5. 예제 : 가변 폭 레지스터

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개

를 만들고있다.

6. 파라미터 의존성

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에 따라 자동으로 변하도록 한다.

profile
안녕하세요. 전자공학부 학부생의 공부 기록입니다.

0개의 댓글