2024년 8월 6일 화요일

"Verilog-Verilator-SystemC 방법론 기초" [4] 실습: 쉬프트 레지스터

"Verilog-Verilator-SystemC 방법론 기초"
[4] 실습: 쉬프트 레지스터

[알림] 아래 내용중 질문, 지적, 보강 등 어떤 사항도 환영 합니다.

"Verilog-Verilator-SystemC 방법론 기초"는 "내 칩(My Chip) MPW 서비스": 오픈-소스 도구 활용 반도체 설계 특별과정 중 두번째 강좌로서 베릴로그(Verilog)와 오픈-소스 시뮬레이션 도구 Verilator 그리고 시스템 수준 검증 방법론 SystemC의 입문 과정이다. 컴퓨팅 언어를 활용한 디지털 회로의 설계와 검증을 다룬다(Quantative approach to digital circuit design using computing language and Open-Source EDA tools).

강의 내용은 아래와 같다.

[1] 도구 설치 [링크]
[2] 설계 언어 Verilog 와 검증 언어 SystemC/C++[링크]
[3] 하드웨어 기술 언어의 코딩 스타일[링크]
[4] 실습: 쉬프트 레지스터 [링크]
[5] 실습: FIR 필터 [링크]
[6] "내 칩 MPW" 요건에 맞춘 FIR 필터의 PE 설계[링크]

[부록] FIR 필터 PE의 "내 칩MPW" 제출용 GDS 생성[링크]


----------------------------------------------------------

목차:

I. 개요

II. 구조적 쉬프트 레지스터
    II-1. 반복생성(repeative generation block)
    II-2. 배열 생성(instantiate sub-module in array)
    II-3. Verilog와 SystemC/C++의 객체 선언과 사례화 과정 비교
    II-4. Verilator의 VCD: Verilog 내부신호 추적

III. 쉬프트 레지스터의 행위 기술(behavioral description)
    III-1. C++의 쉬프트 레지스터
    III-2. Verilog의 쉬프트 레지스터

IV. 맺음말

-----------------------------------------------------------


I. 개요

쉬프트 레지스터를  Verilog로 설계하고 SystemC로 검증 하는 두 가지 설계 방법론을 연습해 본다. 첫번째 방법은 앞서 설계하고 검증해 놓은 다수의 D-플립플롭을 하위 모듈로 사례화 하고 직렬로 연결한 구조적 설계(structural design)이며, 다른 방법은 하드웨어의 행위를 순차구문으로 표현하는 방법(behavioral design)이다. Verilog의 순차구문은 C 언어의 구문과 매우 닮아 있지만 할당(assignment)은 매우 다르다. 예제를 통해 하드웨어의 묘사를 가상으로 처리하는 시뮬레이터의 동작을 이해 해보자. 추상화 수준이 높을 수록 설계 방법론이 유연해 지지만 최종 목표인 전자회로와 멀어지므로 주의가 필요하다. 구문에 대한 이해와 작동원리를 파악하고 올바른 사용법을 익히도록 한다.

아래의 간단 참조서(Quick Reference)를 지참하고 시작해보자.

[1] SystemC Quick Reference card, http://www.eis.cs.tu-bs.de/klingauf/systemc/systemc_quickreference.pdf
[2] Verilog Quick Reference, https://web.stanford.edu/class/ee183/handouts_win2003/VerilogQuickRef.pdf
[3] C++ Quick Reference, https://www.hoomanb.com/cs/quickref/CppQuickRef.pdf
[4] 예제 소스 [바로가기]

II. 구조적 쉬프트 레지스터

앞서 만들어 놓은 D-플립플롭 dffrs 을 재사용 하여 쉬프트 레지스터를 기술 해본다. `define 매크로로 정의하여 필요에 따라 임의로 길이 쉬프트 레지스터를 표현 할 수 있다. 다수의 하위 모듈을 사례화 하고 연결관계를 구성하는 두가지 방법을 비교해 보자. 쉬프트 레지스터 모듈 이름은 n_dffrs_shifter 다.

II-1. 반복생성(repeative generation block)

아래의 예는 for 반복문(for-loop)을 사용하여 반복적으로 하위모듈을 한개씩 생성한다.  

- generate ~ endgenerate 내의 for 반복문은 실행문장이 아니다. 시뮬레이션이 시작되기전 내부구성 때(elaboration phase) 하위모듈의 반복적인 사례화(instantiate sub-module)용이다. 

- 반복변수 i 는 genvar 로 선언하여 네트가 아님을 나타낸다. 시뮬레이션이 시작되면 for 반복문과 함께 무효화된다.

- 반복적으로 사례화된 하위 모듈 마다 이름(instance name)을 붙이지 않고 블록 이름(block name)을 달아주어 배열이 되도록 한다.

II-2. 배열 생성(instantiate sub-module in array)

하위 모듈을 배열로 사례화한 쉬프트 레지스터다.

- 하위 모듈(sub-module)의 배열(array) 사례화도 가능 하다.

- 배열 D-플립플롭 dffrs와 같은 크기의 배열로 선언한 지역신호를 포트에 연결하였다.

- 쉬프트 레지스터로 구성하기 위해 for 반복문을 활용하여 배열 색인(index)를 조정했다. 여기에 사용된 for 반복문은 병렬구문으로 하위 모듈의 연결을 구성하기 위한 용도로 사용된 것이다.

쉬프트 레지스터를 구성하는 두가지 방법을 비교해 보자. 반복적으로 생성하는 경우 반복문 내에 하위 모듈을 한개씩 사례화한다. 배열 사례화의 경우 한꺼번에 하위모듈을 생성하고 반복문으로 연결 관계만 조정하였다. 두 방법 모두 구성단계(elaboration phase)에서 쉬프트 레지스터를 구성하므로 시뮬레이션 실행 중 부담은 같다.

II-3. Verilog와 SystemC/C++의 객체 선언과 사례화 과정 비교 

C++ 언어로 하드웨어를 모델링하는 SystemC는 Verilog와 매우 닮았다. 두 언어에서 모듈 객체 선언하고 사례화 하는 과정을 비교해 보자. 고전적인 컴퓨팅 언어에서 변수(variables)의 선언은 자료형(data type)에 부합하는 저장장소의 확보를 의미한다.

    char myChar;

    yourChar = myChar;

초기화 되지 않은채 사용되면 컴파일러는 경고를낸다.

    Run-time check: the variable 'myChar' is being used without being initialized

단일 저장소 변수가 복합 저장소 구조체(structure)로 고도화 하면서 객체(object)로 발전하기에 이르렀다. C++ 언어는 크래스(class)로 객체를 표현한다. 크래스 내에 저장소의 확보뿐만 아니라 객체를 다루는 방법(method, member function)을 담는다.

크래스와 동일한 이름을 가진 소속 함수를 구성자(constructor)라 한다. 구성자는 객체가 선언될 때 자동으로 호출된다. 구성자의 역활은 크래스 내부의 객체 초기화를 물론 크래스를 사용하기 전에 내부구조를 완성한다.

앞의 예제에서 SystemC로 작성한 테스트벤치 모듈 크래스의 예에서 구성자의 역활을 봤었다. DUT의 사례화는 물론 입출력 포트에 지역 신호(local signal)들에 연결(binding)을 수행한다. 시뮬레이션이 시작되기 전 시뮬레이터의 구조를 완성(elaboration)한다.

베릴로그를 컴퓨팅 언어의 시각에서 보면 객체 지향적이다. 모듈 객체를 선언하면 모듈을 사례화하고 포트에 지역신호를 연결한다. C++의 크래스와 차이라면 하위 모듈을 선언하면서 사례화와 포트 연결을 노출시켰다는 점이다. C++ 의 경우 언어의 규정상 이 절차를 크래스 구성자에 두었을 뿐이다.


II-4. Verilator의 VCD: Verilog 내부신호 추적

SystemC의 VCD 기록은 모듈 크래스의 경계(boundary)에 노출된 포트 만 기록할 수 있다. Verilator에 의해 변환된 모듈 크래스의 내부를 추적하기 어렵다. 이는 Verilog의 내부 신호들을 추적할 수 없기 때문에 디버깅을 곤란하게 한다. 다행히 Verilator는 $dumpvar 같은 PLI 함수들을 지원하므로 Verilog 모듈 내에서 파형을 덤프(dump waveform) 할 수 있다.

Verilator는 변환된 모듈 크래스의 내부 신호를 기록하는 다른 방법도 제공한다. 아래의 예는 상위 모듈 크래스에서 추적할 모듈의 계층깊이(level)를 지정하여 해당 계층의 모듈에서 모든 reg 신호를 VCD로 기록 하도록 지정한 것이다.

[주] 연속 할당(continuous assign) wire는 VCD 추적되지 않는다. 그 이유를 토의해 보라.

D-플립플롭 dffrs를 반복 생성한 경우 VCD 추적은 아래와 같다. 생성 블록(generation block)마다 dffrs가 한개씩 사례화 되어 있다.

D-플립플롭 dffrs를 배열로 선언하여 사례화한 경우 VCD 추적은 아래와 같다. 하위 모듈이 배열로 생성되어 있다. for-반복문은 플립플롭 사이의 연결을 재구성 했을 뿐이다.

배열로 모듈을 한번에 사례화한 경우  VCD 추적은 아래와 같다. D-플립플롭의 사례화 명에 배열 색인이 붙어 있다.

만일 반복 생성과 배열 선언이 함께 주어질 경우를 가정해보자. 마치 구성단계에서 동적 사례화가 이뤄지는 것처럼 보인다. 아래의 VCD 기록 파형을 보면서 어떤 문제가 있는지 토론해 보라.

언어의 관점에서 보면 문제될 것이 없지만 하드웨어를 감안한다면 불합리 하다. 이런 경우 합성기(synthesizer)는 오류를 낸다.


III. 쉬프트 레지스터의 행위 기술(behavioral description)

미리 제작해 놓은 D-플립플롭을 직렬로 배치하여 만들어 본 1비트 쉬프트 레지스터는 게이트 수준(네트리스트)에 가까웠다. 추상화 수준(abstraction level)을 높여 쉬프트 레지스터를 행위 수준(behavioral level)에서 기술해본다.

III-1. C++의 쉬프트 레지스터

쉬프트 레지스터를 C++로 기술 하면 아래와 같다. 구문의 실행순서로 쉬프트 행위가 기술된다.

    // Shift register model in C++

    #define NUM_REG 4

    uint8_t shifter(uint8_t din)
    {
        static uint8_t x[NUM_REG];

        x[0] = din;
        for (int i=NUM_REG-1; i<0; i--)
            x[i] = x[i-1];

        uint8_t qout = x[NUM_REG-1];
        return(qout);
    }

[주] 쉬프트 레지스터는 신호처리를 비롯하여 각종 알고리즘을 프로그래밍 언어로 기술할 때 두루 활용된다. 알고리즘은 시간 미분(time derivate) 방정식에서 시작되어 컴퓨팅을 위해 이산(discrete-time) 신호로 표현한 것이다.

즉시할당과 순차구문이 원칙인 C++ 에서 반복문 i 의 올림(i++)과 내림(i--)에 주의해야 한다. 아래와 같은 연속적인 할당은 의미없다.

    for (int i=1; i<NUM_REG; i++)
        x[i] = x[i-1];

for  반복문이 끝나면 결국 qout = din 가 되어버리기 때문이다.


III-2. Verilog의 쉬프트 레지스터

하드웨어 언어는 RTL 에서 행위를 기술할 수 있다. Verilog 는 사건반응 always 구역내에서  순차구문으로 행위를 기술한다. C++ 와 달리 지연할당(deferred assignment)으로 할당문이 놓인 순서에 무관 하다. 임의 길이 8비트 쉬프트 레지스터의 행위를 Verilog로 기술하면 아래와 같다.

순차 구문 이지만 지연할당이므로 for 반복문의 순서와 무관하다.

[주] 하드웨어 언어에서 병렬구문에 해당하는 연속할당(continuos assignment)은 시뮬레이션 진행중 실행을 위한 문장이라기 보다 네트리스트의 표현이라고 볼 수 있다.

순차구문 구역(always block) 내에서 조건문(if~else~), 선택문(case~endcase), 반복문(for, while~)등을 사용할 수 있다. 이 구문들의 문법은 C와 닮아있고 기능은 동일하다.  하지만 지연 할당된다는 점이 매우 다르다. 또한 하드웨어를 묘사한다는 점을 감안하여 사용에 주의가 필요하다.

IV. 맺음말

쉬프트 레지스터를 예로 삼아 Verilog의 하드웨어를 기술하는 두 가지 방법에 대하여 살펴봤다. 미리 만들어 놓은 하위 모듈(sub-module)의 사례화(instantiate)한 구조적(structural) 방법과 행위(behavioral)를 순차 구문으로 묘사하는 방법이다. 또한 하드웨어를 기술하는 추상화 수준을 높이면서 얻는 설계 생산성의 장점을 실감할수 있다. 하지만 추상화 수준이 높아 질수록 전자회로 하드웨어와 멀어지고 자동화 도구에 의존하게 된다는 점을 항상 염두에 두어야 할 것이다.

시뮬레이터를 통하여 언어로 묘사한 하드웨어를 가상으로 작동시켜 볼 수 있지만 그 결과가 실제 전자회로와 일치할 것이라는 보장은 없다. 시뮬레이터는 최소한의 안전장치가 될 뿐임을 상기해두자. 소프트웨어로 하드웨어의 동작을 가상으로 실행하는 원리를 이해한다면 검증을 위한 수준 높은 테스트 벤치의 작성에 큰 보탬이 된다.


댓글 없음:

댓글 쓰기