verilog로 재귀

verilog에서 재귀?



이 기사는 task문이나 function문의 재귀 호출에 관련하는 기사가 아니고, 재귀적 구조의 회로의 실장 방법을 소개하는 기사입니다.
덧붙여서, task 문이나 function 문은 atomatic을 부가하는 것으로 재귀 호출이 가능합니다..

verilog에서 재귀 모듈 인스턴스화할 수 없음은 generate 블록 내에서만 재귀 적으로 모듈을 인스턴스화 할 수 있습니다.
generate 문은 verilog2001에서 도입 된 구문으로 하위 모듈과 회로의 정의를 매개 변수화하고 추상화 할 수 있습니다.
verilog의 재귀 모듈의 구현 방법에 대해서는 Recursive and Iterative designs in Verilog이나 Recursive Modules 등의 기사에서도 소개되고 있습니다.

무엇이 대단한가?


  • 트리 구조의 연산 회로를 간결하게 기술 할 수있다
    트리 등의 재귀적인 구조의 회로를 간결하게 표현할 수 있습니다.
    멀티플렉서나 합계 계산 회로 등 많은 회로는 트리 구조로 표현할 수 있기 때문에, 폭넓은 회로로 활용할 수 있습니다.
  • 매개 변수화하기 쉬운
    모듈을 호출 할 때마다 모듈의 파라미터를 재구성 할 수 있으므로 모듈의 파라미터를 쉽게 조정할 수 있습니다.

  • 재귀 모듈 구현



    여기에서는 트리 구조의 최대치 계산 회로를 예로 재귀 모듈의 실장 방법을 소개하려고 합니다.
    이제 즉시 구현 코드를 살펴 보겠습니다.
    module Max #
    (
        parameter WIDTH = 8, // データのビット幅
        parameter SIZE  = 8  // データの個数(1, 2, 4, 8, ...)
    )
    (
        input  wire [SIZE*WIDTH-1:0] idata, // 入力データ
        output wire      [WIDTH-1:0] odata  // 出力データ
    );
    
        wire [WIDTH-1:0] data0;
        wire [WIDTH-1:0] data1;
    
        generate
            // 基底部
            if (SIZE == 1)
    
                // 入力データをそのまま出力する
                assign odata = idata;
    
            // 帰納部
            else begin
    
                // 最大値の計算
                assign odata = ($signed(data0) > $signed(data1)) ? data0 : data1;
    
                // 再帰呼び出し
                Max # 
                (
                    .WIDTH(WIDTH),   // データのビット幅はそのまま
                    .SIZE(SIZE >> 1) // データの個数を半分にする
                )
                max0
                (
                    .idata(idata[(SIZE>>1)*WIDTH-1:0]), // 入力データの前半分を下位モジュールに入力
                    .odata(data0)
                );
    
                // 再帰呼び出し
                Max # 
                (
                    .WIDTH(WIDTH),   // データのビット幅はそのまま
                    .SIZE(SIZE >> 1) // データの個数を半分にする
                )
                max1
                (
                    .idata(idata[SIZE*WIDTH-1:(SIZE>>1)*WIDTH]), // 入力データの後半分を下位モジュールに入力
                    .odata(data1)
                );
            end
        endgenerate
    
    endmodule
    

    위의 코드는 아래 그림과 같은 회로입니다.



    이 회로에서는 입력 데이터를 2 개로 분할하고 절반 크기의 하위 모듈에 각각의 데이터를 입력합니다.
    그런 다음 하위 모듈의 출력 데이터를 비교하고 결과를 출력합니다.

    모듈 본체는 크고, 기저부, 귀납부로 나누어져 있어 SIZE 파라미터에 의해 어느 하나의 회로가 선택됩니다.

  • 기저부(SIZE = 1)
    입력 데이터는 하나이므로 수신 한 데이터를 그대로 출력합니다.

  • 귀납부(SIZE = 2, 4, 8, ...)
    입력 데이터를 2 개로 분할하고 2 개의 하위 모듈에 각각 입력합니다.

  • 하위 모듈을 인스턴스화 할 때 하위 모듈의 매개 변수를 재구성 할 수 있습니다.
    예를 들어, 가산 회로의 트리를 작성할 때는 가산으로 발생하는 자릿수 상승을 고려하여 하위 모듈의 입력 비트 폭을 조정하는 것으로, 효율적으로 가산 회로의 트리를 구축할 수도 있습니다.

    또, 이번 작성한 최대치의 계산 회로는 데이터의 개수가 2의 거듭제곱일 때에만 올바르게 동작합니다만, 귀납부에서 올바르게 처리하는 것으로 임의의 사이즈의 데이터에 대응시킬 수가 있습니다.

    간단하네요!
    assign odata = ($signed(data0) > $signed(data1)) ? data0 : data1;
    

    의 부분을 변경함으로써 다양한 계산 회로를 재귀시킬 수 있습니다.

    예를 들어,
    assign odata = $signed(data0) + $signed(data1)
    

    그러면 총합의 계산 회로가 되고,
    assign odata = $signed(data0) * $signed(data1)
    

    그러면 총승의 계산 회로가 됩니다.

    주의점



    불행히도 모든 합성 도구와 시뮬레이터가 재귀 모듈을 구현하는 것을 지원하는 것은 아니며 일부 환경에서는 제대로 작동하지 않을 수 있습니다.
    ※Xilinx사의 합성 툴인 Vivado나 PlanAhead에서는 시뮬레이션이나 합성이 가능합니다.

    위의 회로에서 SIZE를 $ n $로하면 $\log (n) $에 비례하여 신호 지연이 증가하고 $ n $에 비례하여 사용 자원 수가 증가합니다.
    대규모 회로를 실장하면 회로의 퍼포먼스가 악화될 가능성이 있으므로 주의해 주세요.

    좋은 웹페이지 즐겨찾기