본문 바로가기

실전! Verilog HDL RTL Design

[Verilog HDL] 15. regfile.v를 최적화 하자.(parameter와 배열 인덱싱 기법)

반응형

이번 장에서는 ch13에서 만들었던 regfile.v code를 좀 더 유연하고 단순하게 최적화 해 보겠다. 이러한 작업을 refactoring이라고도 한다. 처음에는 기능 구현에만 중점을 두어 빨리 코딩했더라도 추후에 refactoring을 통해 코드를 단순화 시키고 최적화 시키는 작업이 필요할 수 있다. 비단 합성 했을 때 하드웨어 성능이나 면적 측면에서 좋아 지지 않더라도 코드의 가독성을 높이고 재사용 가능성을 높이기 위한 최적화도 필요한 경우가 많다.

 

이를 위해 새로 배워햐 할 기능이 'parameter'이다. parameter는 verilog에서 주로 상수를 표현하는데 사용하기도 하지만 한 module의 instance마다 다르게 사용해야 할 변수들이 있을 때 사용하기도 한다. 어떤 면에서 C++의 template기능과 유사하기도 하다.

 

chapter 13에서 register file을 설계할 때 address width와 data width는 고정이었다. 하지만 실졔 설계에 사용할 때 address width와 data width는 사용하고자 하는 경우마다 달라야 하는 경우가 많다. 그 때마다 bit width만 다른 코딩을 해야하는 것은 너무 비 효율적이다. 따라서 module를 코딩할 때 instance별로 다르게 설정해야 하는 값을 parameter로 정의해 놓으면 module의 instance를 생성 할 때 마다 다른 값을 쓸 수 있도록 하는 것이다.

 

아래의 코드에서 이 module에서 필요한 paramter 2개를 정의하는 것을 볼 수 있다. 하나는 ADDR_WIDTH이고 하나는 DATA_WIDTH이다. parameter는 module name 바로 뒤에 #() 의 괄호 안에 정의한다. 정의할 때 대입하는 값은 default 값으로 instance할 때 parameter 값을 재정의 해 주지 않을 경우 default 값이 적용된다.

 

이렇게 해서 'ADDR' port와 'DIN'. 'DOUT' port의 bit width를 instance 생성시 바꿀 수 있게 된다.

 

그리고 module 내부에서 상수로 사용해야 하는 값을 parameter로 정의할 수 있는데 아래에 MEM_SIZE와 같이 ADDR_WIDTH에 따라 결정되는 상수 값을 선언할 수 있다. 이렇게 하면 ADDR_WIDTH에 따라 MEM_SIZE값이 적절하게 계산 되어 사용되므로 편리하다.

 

chapter 13에서는 register의 배열로 메모리 mem을 선언하여 사용했었다. 그리고 mem의 각 요소별로 초기화와 write하는 부분을 각각 always 문으로 기술하였는데 실전에서도 이렇게 사용하는 것은 너무 비효율적이다.

따라서 이부분을 하나의 always 문에 기술하는 법을 살펴보자.

 

우선 for loop 사용법에 대해서 알아야 한다. verilog 를 처음 배우는 초보자는 RTL code에서 for loop 나 while loop는 무조건 쓰면 안된다고 알고 있는 사람이 있는 데 이는 잘못된 것이다. 언듯 C언어에서 for loop는 순차적으로 실행되니 hardware로 어떻게 구현될 지 와닿지 않을 수 있다. hardware 측면에서 for loop는 loop unrolling 이라고 하는 기법을 사용하여 여러개의 hardware logic이 concurrent하게 수행 되는 구조로 합성이 된다.

 

여기서는 mem의 모든 요소를 reset 구간에서 '0'으로 초기화 하는 부분에 for loop를 사용했다. for loop의 인덱스 변수 i는 integer keyword를 사용했는데 C에서 int type과 같은 것으로 보면 된다.

이렇게 기술하면 RSTn이 '0'일 때 모든 mem 요소들이 '0'으로 초기화 된다.

 

그리고 RSTn이 '1'인 경우 CLK의 positive edge에서 mem의 index로 ADDR값을 사용하여 DIN을 저장할 수 있다.

즉 입/출력 port 또는 wire, reg 변수 등을 verilog array의 index로 사용할 수 있는 것이다.

 

DOUT을 출력하는 것은 ADDR에 해당하는 mem요소를 바로 DOUT에 출력하는 combinational logic으로 코딩할 수 있다.

 

위의 방법을 사용하여 아래와 같이 간단한 구조로 다시 refactoring하여 코딩할 수 있다.

 


module regfile #(
	parameter ADDR_WIDTH=8,
	parameter DATA_WIDTH=16
	)
	(
	input wire CLK,
	input wire RSTn,
	input wire [ADDR_WIDTH-1:0] ADDR,
	input wire WE,
	input wire [DATA_WIDTH-1:0] DIN,
	output reg [DATA_WIDTH-1:0] DOUT
	);

parameter MEM_SIZE=2**ADDR_WIDTH;

integer  i;
reg [DATA_WIDTH-1:0]  mem [0:MEM_SIZE-1];

always @(posedge CLK or negedge RSTn)
begin
	if (!RSTn)
	begin
      	for (i = 0; i<MEM_SIZE; i=i+1)
            mem[i] <= 0;
   	end
   	else if (WE)
	begin
        mem[ADDR] <= DIN;
	end
end

always @(*)
	DOUT = mem[ADDR];

endmodule

parameter, for loop, memory indexing 기법으로 이렇게 단순하게 코딩 할 수 있다는 것을 꼭 기억하기 바란다.

 

아래의 테스트 벤치에서 regfile을 instance하면서 parameter 값을 재정의 하는 것을 볼 수 있다. module name 과 instance name 사이의  #()에서 괄호 안에 설정하고자 하는 parameter 이름을 '.'뒤에 써주고 괄호안에 설정하는 값을 써 주면 된다.

나머지는 chapter 13과 동일하다.

 

`timescale 1ns/1ns
 
module test;

// delare variables
reg clock;
reg resetn;
reg [1:0] addr;
reg [3:0] din;
reg we;
 
wire [3:0] dout;
 
// clock generation
always #10 clock = ~clock;
 
regfile  #(
    .ADDR_WIDTH(2), 
    .DATA_WIDTH(4)
    ) u_regfile (
    .CLK      (clock),
    .RSTn     (resetn),
    .ADDR     (addr),
    .WE       (we),
    .DIN      (din),
    .DOUT     (dout)
);
 
 
// create wave dump file
initial
begin
    $dumpfile("regfile.vcd");
    $dumpvars(0, test);
end
 
// input stimulus
initial
begin
    clock <= 0;
    resetn <= 0;
    we <= 0;
    #100;
    resetn <= 1;
    @(posedge clock);
    addr <= 2'd0;
    din <= 4'd1;
    we  <= 1;
    @(posedge clock);
    addr <= 2'd1;
    din <= 4'd2;
    @(posedge clock);
    addr <= 2'd2;
    din <= 4'd3;
    @(posedge clock);
    we <= 0;
    addr <= 0;
    @(posedge clock);
    addr <= 1;
    @(posedge clock);
    addr <= 2;
    #100;
    $finish();
end
 
endmodule​

아래와 같은 명령으로 a.out을 만들고 a.out을 실행하면 chapter 13과 동일한 결과를 얻을 수 있다.

$ iverilog regfile.v test.v
$ ./a.out
반응형