Translate

2021년 9월 8일 수요일

3장. C 설계의 바름 검증 (C Validation)

 3장. C 설계의 바름 검증 (C Validation)

개요
실습 폴터구조
실습 1. GNUPLOT 를 이용한 도식화
실습 2: GNUPLOT, Windows GDI+, SDL Lib 그리고 Python을 내장한 SystemC 테스트 벤치
    2-1. sc_fifo<T> 채널을 이용한 시스템 수준(System Level) 테스트 벤치
    2-2. sc_signal<T*>
    2-3. sc_signal<T>, 클럭 상세(clock-level data transfer) 데이터 전송
실습 3. Arbitrary Precision Type

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

개요

합성이나 검증 도구들이 제아무리 우수 하더라도 최초 C 설계가 바르게 되었어야 한다는 점은 두말할 나위가 없다. 앞선 fir() 예에서 해봤듯이 검증 벡터 파일을 읽어 설계의 출력과 비교하는 방식이 비교적 수월하다. 하지만 미리 만들어 놓은 벡터 파일에 오류(error)와 손상(defect)으로부터 무결함과 모든 경우를 충분히 고려되었다고(completeness) 보장 되어야 한다.

검증용 참조 벡터 파일이 어짜피 알고리즘을 통해 만들어 졌다면 굳이 중간에 벡터 파일을 만들고 이를 다시 테스트 벤치에서 읽어 들이는 위험(또는 버거로움)을 감수할 필요는 없다. 차라리 알고리즘을 테스트 벤치에 포함 시킴으로서 검증 벡터 훼손의 위험을 차단할 수 있다. 멀티미디어 신호처리, 기계학습 응용처럼 대규모 시험용 입력 벡터와 검증 벡터를 요구하는 경우 파일 입출력 또한 시뮬레이션에 상당한 부담이 된다.

HDL 도 파일 입출력 같은 테스트 벤치 작성용 APl들을 갖추고 있으나 고수준 컴퓨팅 언어에 비할 바가 못된다. 특히 알고리즘 개발과 검증용의 우수한 시스템 소프트웨어 패키지들이 제공하는 API를 들여다 쓰려면 C/C++ 테스트 벤치의 필연성은 부정하기 어렵다. 다만 소프트웨어 개발용의 C 언어에는 하드웨어 묘사를 위한 시간(time management)과 사건처리(event process) 그리고 하드웨어 채널(HW channel)의 개념이 없다. 이를 극복하기 위해 HDL에 C 루틴을 연결해 주는 여러 기준(PLI/VPI, VHPI, FLI, DPI 등등)이 존재하지만 복잡하기도 하거니와 해당 HDL 이나 특정 툴 벤더에 종속되어서 알고리즘 코드를 변경해 주어야 하는 일까지 생긴다. 따라서 C 알고리즘과 합성으로 얻은 RTL/HDL을 변경없이 적용할 수 있고 충분히 검증된 시스템 툴 API 들과 라이브러리 들을 사용할 수 있는 SystemC를 사용해야 하는 이유라 할 것이다.

실습 폴더 구조

실습1. GNUPLOT 를 이용한 도식화 [다운로드]

신호처리에서 많이 쓰이는 해밍 윈도우(Hamming Window) 함수를 HLS를 목표로 작성한 C 설계와 순수 C 모델과 비교 검증하는 테스트벤치를 작성해 본다. 윈도우 함수는 단순 하지만 곱셈의 연산량이 매우 많아 실시간 처리를 위해 파이프라인 구조의 하드웨어를 구성하면 효과적이다. [참고: Window Function을 쓰는 이유 / Window Function ]

해밍 윈도우 함수의 효과를 도식적으로 확인 할 수 있도록 파워 스펙트럼으로 보여준다. 이를 위해 C 로 작성된 simple_fft2() 를 테스트 벤치에 포함 시켰고 입출력 데이터를 그림으로 확인하기 위해 gnuplot 툴을 테스트 벤치에 연동 시켰다. 결과를 그림으로 보면서 DSP에서 윈도우 함수의 효용성을 직관적으로 확인해 볼 수 있다. [참고: Simple DFT in C , gnuplot homepage ]


테스트 벡터와 연산의 결과를 파일로 생성하고 외부의 도구를 이용해 읽고 확인하는 번거로운 과정 없이 테스트벤치에 내장하므로서 검증의 생산성을 높인다. 수많은 신호처리 라이브러리와 소프트웨어를 검증에 사용 할 수 있다는 점은 설계 검증에 C 언어를 사용하는 가장 큰 장점 중에 하나다. Numerical Recipe, Python NumPy, MatLab 등 이미 이공계 뿐만 아니라 모든 산업의 개발업무에 적용되고 있다.

실습 2: GNUPLOT, Windows GDI+, SDL Lib 그리고 Python을 내장한 SystemC 테스트 벤치

2-1. sc_fifo<T> 채널을 이용한 시스템 수준(System Level) 테스트 벤치 [다운로드]

아직 하드웨어 구조가 구체적으로 정해지지 않았으므로 비트 상세(bit-wise detail) 및 클럭 상세(clock detail) 의 테스트 벤치 구성이 불가하다. 하지만 SystemC는 시스템 수준의 기술을 위해 모듈간 다양한 통신체널을 제공한다. 시스템 수준의 모델 기술을 위해 제공되는 FIFO 채널을 이용해 모듈간 통신 연결을 구성한다.

sc_fifo<> 채널로 연결된 모듈간 통신은 핸드쉐이크가 필요없다. 이 통신 채널 내에서 전송을 제어 한다. 테스트 벡터 생성 모듈 sc_stimulus에서 각 모듈로 데이더 전송의 동기가 별도의 핸드쉐이크 없이 채널 내에서 맞춰 질 수 있음을 보기 위해 sc_fifo 채널의 깊이가 달리 하였다.

2-2. sc_signal<T*> [다운로드]

모듈간 데이터 전송은 채널을 통해 이뤄진다. SystemC에서 제공하는 기본 채널로 sc_signal<T>가 있다. 채널에 실릴 수 있는 자료형(data type) T는 C/C++ 의 모든 자료형이 가능 하다. 값 뿐만 아니라 주소(포인터) 사용도 가능하다. sc_signal 채널은 sc_fifo 와는 달리 채널내에 전송 동기를 맞추는 기작이 포함되어 있지 않다. 따라서 별도의 핸드 쉐이크 신호가 필요하다. 시뮬레이션 개시를 위해 reset 이 사용 되었고 시뮬레이션 진행은 클럭 clock 신호를 따른다. 하지만 모듈간 데이터 전송은 여전히 클럭 상세는 아니다.

2-3. sc_signal<T>, 클럭 상세(clock-level data transfer) 데이터 전송 [다운로드]

아직 HLS 가 이루어 지기 전이지만 모듈 간 클럭 상세수준(clock-level)의 데이터 전송을 모델링 해보자. 매 클럭 마다 데이터가 채널에 실린다. 클럭 상세 모델의 경우 채널에서 데이터 전송 동기를 맞춰주지 않으므로 핸드 쉐이크(request-ready handshake) 과정이 필요하다.

앞의 예제에서 SystemC 테스트 벤치에 내장 시킨 파이썬(Python)은 Numpy와 matplotlib를 이용해 그래프를 그리는 용도로 사용 했었다. 앞으로 사용할 RTL 시뮬레이터인 ModelSim에 SystemC 테스트 벤치를 적용할 것이다. 유감 스럽게도 matplotlib 가 그래픽 드라이버 호환성이 떨어져 ModelSim 에서 사용 할 수 없었다. 다행히 gnuplot은 사용 가능 하다. 따라서 Python의 용도를 파워 스펙트럼 구하는 용도로 변경 하였다. C 로 작성된 fft 소프트웨어를 그대로 Python 함수로 만들었다.

아직 C 설계가 합성되기 전이지만 SystemC 테스트 벤치에서 모듈 간 데이터 전송은 클럭 상세 수준이다. Python을 내장한 SystemC 테스트 벤치가 HDL 시뮬레이터에서 실행 가능 하다. 이 SystemC 테스트 벤치는 향후 HLS 된 RTL Co-Simulation 에 사용될 것이다.


실습 3. Arbitrary Precision Type [다운로드]

고수준 추상화 언어에서 자료형의 기본 단위는 바이트(byte, 8-비트) 다. 컴퓨터 구조의 태생적으로 한개 문자를 표현하기 위한 비트폭을 기본 단위로 정한 데서 연유한다. 디지털 하드웨어에서 자료의 기본 단위는 2진수 한자리로서 비트(bit)다. 알고리즘이 적용될 분야에 필요한 만큼의 정밀도와 숫자의 가변 범위에 따라 자료의 폭을 비트 단위로 정해야 하드웨어의 낭비가 없다. C 언어에서 하드웨어를 설계 할 때 성능좋은 합성기라면 테스트 값으로 알고리즘을 적용하여 가변 비트 폭을 정해 줄 수도 있으나 아직 그 수준에 이르진 못했다. 사실 설계의 검증에 사용된 테스트 벡터가 실사용에 충분할 만큼 마련되었다고 장담하기도 어렵다. 따라서 C 설계 단계에서 부터 설계자가 오버플로우 같은 연산 오류를 방지하고 정밀도 측정을 바탕으로 구체적인 비트폭을 결정할 수 있어야 한다.

HLS 도구들은 이러 점을 감안 하여 자료의 비트폭을 구체적으로 표현한 기법을 제공한다. 다행히 C++의 템플릿은 임의의 자료형을 선언하는 방법을 제공한다. 예를 들어 16비트와 32비트 자료형을 C/C++에서 정의하면 다음과 같다.

typedef int16_t in_data_t;
typedef int32_t out_data_t;

이를 구체적으로 32비트 폭의 자료 객체임을 명시하는 방법으로 SystemC 에서는 다음과 같이 선언한다.

#include "systemc.h"
typedef sc_int<16> in_data_t;
typedef sc_int<32> out_data_t;

자료형의 비트단위 구체화는 비트 단위 상세의 하드웨어를 겨냥한 것이다. 

---------------------------------------------------------------------------------
고위 합성 튜토리얼(High-Level Synthesis Tutorial)

2021년 9월 3일 금요일

2장. 실습 3: HLS 설계 최적화(Design Optimization)

2장. 실습 3: HLS 설계 최적화(Design Optimization)

HLS로 합성된 설계물의 성능을 높여본다. C 설계 소스에 #define 매크로를 이용해 합성 지시자(directives)를 넣거나 Tcl 스크립트로 작성할 수 있다. HLS 에서 C 에서 RTL로 변환시 합성 지시자로 크게 두가지를 고려해 볼 수 있다.

  • 인터페이스(interface): 외부 모듈과 데이터를 주고 받기 위한 절차
  • 처리량(throughput): 입력이 주어지고 출력을 얻기까지 소요되는 클럭 수

1. 인터페이스

인터페이스는 FSM(Finite State Machine)으로 구현되어 설계의 일부로 포함된다. C 의 함수에 명시 되지 않은 인터페이스를 적용될 하드웨어마다 RTL/HDL 로 따로 준비해 두고 관리하기 어렵다. 따라서 인터페이스(INTERFACE)를 지정만 하면 자동생성 해주는 HLS의 인터페이스 합성은 매우 유용한 도구다.

C 소스 파일을 열면 특성 창에 함수들의 목록과 인수들을 나열해 준다. 값으로 전달되는(call-by-value) 인수는 RTL의 입력 포트(input ports)로, 주소가 전달 되는(call-by-address) 포인터 인수는 출력 포트(output ports)가 된다. 각 포트에서 사용할 인터페이스를 선택하면 합성기는 그에 맞는 제어 신호들과 절차를 FSM으로 자동생성한다. 인터페이스가 변경 되면 그에 맞는 HDL 테스트벤치도 재생성 되는데 이는 테스트 벤치의 자동화의 큰 장점이다. (아쉽게도 자동 생성된 인터페이스 테스트 벤치는 그리 신통치 않음!)

HLS 가 제공하는 인터페이스 옵션에는 일방형(Block I/O), 준비-확인형(RDY-ACK), 마스터- 슬레이브(Master-Slave)형을 비롯해 표준화된 온-칩 메모리의 접근이나 시스템 버스 AXI 를 선택할 수 있다.

설계 모듈이 외부와 데이터를 주고 받기 위한 방법으로 C 의 경우 파라메터 전달 그리고 리턴 값을 받는 함수 호출 규정이 전부다. RTL의 경우 핸드쉐이크, 시스템 버스, 마스터-슬레이브 라는 다양한 방법을 취할 수 있으며 그에 맞는 절차와 제어 신호를 동반한다. 이 절차를 프로토콜(protocol)이라 한다. 선택한 인터페이스의 절차가 다르므로 소요되는 클럭의 수도 다를 수 있다.

1-1. ap_vld 방식 인터페이스

특별히 인터페이스를 지정하지 않은 경우의 프로토콜은 가장 단순한 방식을 취한다. 대기신호(idle)를 내면 외부에서 유효 입력값과 함께 시작 신호(start)를 준다. DUT는 시작 신호를 받아 내부 처리를 개시한다. 외부에 입력을 받았다는 확인을 해주지 않으므로 start와 입력 x 를 전송 종료시 까지 유지하고 있어야 한다.

DUT가 처리를 마치면 유효 출력을 알리고(ready) 핸드 쉐이크 절차를 끝낸다(done). 외부에서 유효 출력을 받고자 대기 중 인지 확인하지 않고 일방적으로 종료한다. 설계 모듈 DUT가 전송의 개시와 종료에 대한 일방적 권한을 가진다.

1-2. ap_ack 방식 인터페이스

핸드쉐이크에 준비와 확인절차를 거치는 양방향 절차를 사용하는 편이 안전하고 다중처리에 용이하다. 상대로부터 유효한 데이터를 받고 이를 확인(acknowledge)해주면 인터벌 기간동안 상대가 다른 일을 할 수 있게 해줄 수 있다. DUT는 인터벌을 마치고 상대로부터 받을 준비가 되었음을 알려올 때를 기다려 유효한 출력을 내보낸다. 외부 모들은 DUT 가 몇 클럭만에 처리를 마치는지 모르므로 받을 준비가 되었음을 알려 주어야 한다.

인터페이스 절차를 수행하기 위해 유한 상태 머신(FSM, Finite-State Machine)을 구성하는데 대기와 학인(Rdy-Ack)을 위해 한개 이상의 상태를 차지해야 하므로 핸드쉐이크에 여분의 클럭을 필요하다. 온-칩 버스에 붙일 모듈이라면 더욱 복잡한 절차가 요구된다.

2. 처리량(throughput) 최적화

앞서 언급 되었던 인터벌 클럭수의 최소화다. 

C 언어의 변수(variables)와 연산자(arithmetic-logical operators) 그리고 조건문(if-condition)을 HDL로 변환하기는 어려울 것이 없다. 두 언어 모두 의미가 동일하기 때문이다. 다만 변수(variable)의 경우 연결선(wire) 또는 저장소(flip-flop)가 될 수 있다. 코딩 스타일에 의한 것일 수 있고 최적화의 결과로 나뉠 수도 있다. 하드웨어의 구조가 극적으로 바뀌는 경우는 반복문(loop)이다.

신호처리(DSP), 인공지능(AI) 등의 고급 알고리즘은 행렬계산에 전적으로 의존한다. 행렬 계산은 컴퓨터 언어의 반복문(for-loop)으로 구현된다. 수많은 반복문을 얼마나 빠르게 수행할 것인가가 오늘날 컴퓨팅 성능의 최대 관건이 되었다. C 언어로 기술된 알고리즘을 전용 RTL 하드웨어로 구현해 빠르게 수행하려면 결국 클럭 수를 최소화 하는 것이다.

2-1. 말려있는 반복문(Rolled Loop)

HLS는 C 설계에서 계산이 집중되는 반복문을 찾아내서 병렬성을 검출하고 이를 최적화 한다. 반복문에 최적화 지시를 하지 않고 합성 해보자.

다음은 성능 보고서의 일부다. 인터벌이 무려 27이다. 하드웨어 사용량은 의외로 작다. 특히 C 의 for 반복문 내에 32비트 레지스터가 10개나 필요한데 쉬프트 레지스터를 플립플롭으로 구현 했다면 320개의 플립플롭이 필요하다. 그런데 전체 플립플롭의 수가 476개로 나온 것은 너무나 적은 수다.

합성 보고서의 저장소 항목을 보면 shift_reg가 ram_1p 로 구현 된 것으로 보아 쉬프트 레지스터는 플립플롭을 사용하지 않았다.

또한 곱셈(mult) 연산이 반복문 내에 포함 되었지만 DSP 자원의 사용량도 매우 적다. 연산자 항목을 보니 1개의 곱셈기와 누산기가 사용됐을 뿐이다.


위의 내용으로 볼때 for 반복문은 말린채(rolled) 동작하고 내부 구조는 다음과 같을 것으로 추정된다.

2-2. 반복 풀기(UNROLL Loop)

HLS 합성기에게 말려있던 for 반복문을 풀도록(UNROLL) 지시해보자.

합성 보고서를 보니 레이턴시와 인터벌이 절반 이하로 줄었으나 하드웨어의 사용량이 급격히 증가했다.

하드웨어 자원 사용내역을 보니 11개의 곱셈기(mul)와 8개의 가산기(add) 그리고 1개의 누산기(acc) 가 사용 되었다.

외부의 c[N]이 두개의 메모리 인터페이스를 갖게 된 점이 눈길을 끈다. 왜 그랬을까?

엄청나게 늘어난 FF과 DSP 블럭 그리고 가산기 들을 고려해 보면 for 반복문을 풀면서 병렬처리 구조의 하드웨어를 구성한 것으로 보인다. 합성된 RTL/HDL을 읽어보고 문석할 수도 있겠으나 정신 건강상 해롭다. 위의 하드웨어 사용량과 인터벌을 참고하여 데이터 페스를 추정해 보면 다음과 같을 것이다.

동일한 두개의 메모리 인터페이스를 가지게 된 연유를 대략 짐작할 수 있다. 역시 외부의 c[N]이 병렬처리의 병목(bottle-neck)의 원인으로 보인다.

2-3. 배열 분리하기(ARRAY_PARTITION)

대규모 저장소를 확보할 수 있으나 주소-읽기/쓰기-데이터의 순차적인 절차를 준수해야 하는 메모리는 병렬 처리에 매우 불리하다. 메모리 인터페이스를 해결하려면 c[N]인 배열을 메모리가 아닌 개별 입력으로 분리해 내도록(ARRAY_PARTITION) 합성기에게 지시해보자.

합성 보고서에 따르면 하드웨어 사용량은 비슷하나 인터벌이 절반으로 줄었다.

메모리 인터페이스가 사라졌다. 하지만 입력 포트의 비트수가 엄청나게 증가했다.

상수 계수들이 일거에 준비 되었으므로 이제 열한번의 곱셈이 동시에 이뤄질 수 있게된 하드웨어를 가지게 되었다. 병렬 가산기를 몇단 더 추가하여 누산(accumulation)을 수행한다.

외부에서 입력되는 11개의 32비트 상수 때문에 입력 포트 와이어가 엄청나게 늘었다. ASIC 이라면 금속 층에 배선하므로 이정도 와이어 증가는 참을만 하다. 하지만 배선 용량을 고정되어 있는 FPGA의 경우 심각히 고려해야 한다. 배선용량이 모자라면 로직 블럭(LUT)을 통해 배선을 할 것이고 이는 최대지연 경로(max delay-path)를 늘리게 되어 동작속도를 심각하게 낮춘다.

2-4. 상수 곱셈

위의 fir()은 두 종류의 입력 포트를 가진다. 포트 c[]가 상수라는 점에 주목하자. fir(x, c[], *y) 로 주어진 경우 HLS 합성기는 c[]가 x와 마찬가지로 변수로 인식한다. 따라서 곱셈기는 변수와 변수의 곱셈기가 된다. 만일 c[]를 합성기에게 상수임을 노출 시키면 강력한 최적화를 수행한다. 상수의 유효 비트(dynamic range)에 따라 작은 크기의 곱셈기를 사용하거나 고정된 상수라면 단지 비트맵 변경을 통해 실제 곱셈기 없이 연산을 수행 할 수도 있다.

C의 함수 fir()에 적용될 필터 계수는 모두 상수배열 이었다. 특히 상수 중에 0이 포함되었을 뿐만 아니라 좌우 대칭이라는 점이 눈에 띈다. 0을 곱하는 곱셈기는 필요 없다. 따라서 두개의 거대한 곱셈기를 제거 할 수 있다.

상주중 가장 큰 수는 c[5]=63이다. 따라서 c 의 변화 범위(dynamic range)는 63이 2진수 (111111)이므로 7비트 이나, c[4]=c[6]=56 보다 7 차이가 난다. 7을 왼쪽으로 3비트 옮기면 56이다. 이런 단순한 대수(arithmetic)를 적용해 계수의 종류를 줄여 나가면 곱셈을 아예 제거 할 수도 있을 것이다.

계수의 종류를 줄여 나가다 보면 곱셈을 최소화 한 덧셈과 와이어 쉬프트로 계산을 끝낼 수도 있다. 결국 논리식 간략화의 문제로 귀결될 것이다. 상수 배열 c[]을 HLS에 노출 시키기 위해 fir() 설계를 변경했다.

UNROLL과 ARRAY_PARTITION 조건을 주고 HLS 합성 해보자.

지연은 목표치 내로 들어 왔으며 하드웨어 사용량도 매우 적어졌고 인터벌은 5다. 상수 계수 c[]가 노출되지 않았을 때와 비교해보면 매우 효과적인 최적화를 수행한 것을 알 수 있다.

여전히 1개의 거대한 곱셈기가 남았다. 이 곱셈기의 용도는 뭘까 궁금하다. 그 이유를 알기 위해 툴이 생성한 HDL 코드를 읽어볼 생각은 말자. 어디까지나 툴이 읽을 것을 전제로 생성되었다.

HDL 시뮬레이션으로 곱셈기의 입출력을 추적해 보니 시뮬레이션을 종료할 때까지 한 입력이 23으로 고정된 것으로 보아 최적화에 소수(prime number)를 간략화 하지 못한 것으로 보인다. 제아무리 소수라지만 곱셈기를 제거할 수도 있었을 것이다. 하지만 23을 제거하기 위해 여러단의 덧셈기로 바꾸기엔 유효 비트들이 많아서 인터벌이 길어질 수 있으므로 최적화의 타협 끝에 결정된 것으로 보인다.

만일 계수 c[5]를 24로 바꿨더라면 어떤 결과를 냈을까?

하드웨어에서 곱셈기는 사라졌고 인터벌도 줄었다.

3. 결론

HLS 툴의 합성기는 매우 훌륭했다. 최고의 효과를 얻으려면 C 알고리즘 작성시 구조와 최적화를 고려하자.

[소스파일 다운로드]

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

고위 합성 튜토리얼(High-Level Synthesis Tutorial)
[목차][이전][다음]


2장. 실습 2: Tcl 스크립트

2장. 실습 2: Tcl 스크립트

- GUI 말고 커멘드 라인 에서 Tcl 스크립트로 Vitis HLS 를 실행 시킬 수 있다.

- directives.tcl 은 Solution 마다 따로 준비 해야 한다.


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

고위 합성 튜토리얼(High-Level Synthesis Tutorial)
[목차][이전][다음]


2021년 9월 1일 수요일

2장. 추가: SystemC Co-Simulation Testbench

 2장. 추가: SystemC Co-Simulation Testbench

SystemC를 자세히 논하진 않겠다. 다만 IEEE1666-2011 표준으로 등재된 시스템 반도체 설계 언어다. 사실 새로운 언어는 아니고 C++ 를 하드웨어 설계및 검증용 라이브러리를 확장 한 것이다. 하드웨어 언어의 클럭(clock), 채널(channel)의 개념과 병렬 시뮬레이션 커널을 포함한다. 개발 툴은 표준 C++ 컴파일러(GCC, MS Visual Studio C++)면 충분하다. 기존의 HDL을 이해 한다면 접근하기 어렵지 않을 것이다. 혼합 언어를 지원하는 HDL 시뮬레이터라면 별도의 인터페이스(PLI/VPI, FLI, VHPI, DPI 등등) 절차 없이 HDL과 통합 실행 시킬 수있다. SystemC의 모듈이 Verilog, SystemVerilog의 module 이나 VHDL 의 entity-architecture 와 동등하게 취급된다. 일예로 ModelSim은 HDL 모듈을 SystemC로 불러올 수 있고 그 반대도 가능하다. (단, 서브모듈 추적에는 제약이 있음)

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

[참고서]

[1] System Design using SystemC [link]/번역서 'SystemC를 이용한 시스템 설계'[link]
2002년 SystemC 버젼 1.1이 나왔던 초창기에 쓰여진 책이다. 너무 오래되서 현재 버전과 다른 점이 많지만 SystemC의 기본개념을 잘 설명하고 있다.

[2] SystemC: From the Ground-Up, 2nd [link]/번역서 '원리부터 배우는 SystemC'[link]
SystemC 버젼 2.1 을 기준으로 쓰여졌다. TLM 을 포함한다. 2010년에 2판이 나왔고 번역서는 1판으로 쓰였다.

인터넷에 SystemC로 검색하면 많은 문서들이 나온다. 최신 문서를 보는 편이 좋다. 굳이 위의 책을 구입할 필요는 없다. 혹시 도서관에서 번역서가 보이면 읽어봐 주면 고맙겠다.

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

SystemC로 감싼 C 디자인과 HDL을 단일 테스트 벤치로 통합 실행하는 검증 환경을 구성하는 방법을 소개한다. 먼저 소스 코드와 컴퓨팅 환경은 다음과 같다.

  • DUT의 소스는 자일링스 HLS 튜토리얼 에서 가져온 fir.c 와 RTL로 합성한 Verilog 다.
  • SystemC 는 2.3 버젼을 사용한다. 소스코드는 https://accellera.org/ 에서 무료로 받을 수 있다.
  • C++ 컴파일러로 마이크로소프트의 비주얼스튜디오 2019 커뮤니티 버젼이다. 조건부 무료다.
  • HDL 시뮬레이터로 멘토그픽스 모델심(ModelSim) 10.1 버젼이다. 무료 아니다. 인텔 알테라 버젼을 무료로 쓸 수 있긴 하나 SystemC를 지원하지 않는다.
  • 전 과정은 PC의 Windows 10 에서 실시되었다.
  • 여기에 설명하는 소스 파일들은 다음 링크에서 다운 받을 수 있다. [다운로드]

1. 개요

개별적으로 실행하여 테스트 벡터 파일을 주고 받기 보다 제대로된 Co-Simulation을 해본다. 이렇게......

제대로 된 Co-Simulation 이라면 이래야 하지 않을까?

  • 설계물의 C 코드에 변경 없을 것
  • 한 테스트 벤치에 C, SystemC, HDL들이 실행형 바이너리로 결합될 것(Executable specification)
  • 시뮬레이션 실행 중 실시간으로 입출력 검증이 이뤄질 것

이미 HDL과 C++를 동일한 환경에서 실행이 가능한 도구(C-HDL Mixed simulation)가 준비되어 있다. HDL에서 C 언어 인터페이스를 위해 Verilog의 PLI와 VPI, VHDL의 FLI와 VHPI, SystemVerilog 의 DPI 등이 표준화 되어있다. 하지만 이런 인테페이스 기준은 지나치게 복잡해서 학습 접근을 막고(반도체 하드웨어 설계자에겐 C++는 여전히 높은 벽인가?) 때로는 설계물의 C 코드를 변경을 요구하기도 한다. 게다가 HDL 전용 시뮬레이터가 아니면 실행 시킬 수 없다. 다행히 SystemC가 IEEE 1066-2011으로 표준 제정되다. SystemC는 C++를 라이브러리 수준에서 확장한 것이므로 GCC 나 비주얼 C++ 같은 표준 C++ 컴파일러 라면 실행 파일 생성이 가능하다.

2. C 디자인 검증(C Validation)

C 디자인 fir()를 SystemC 환경에서 검증한다. 먼저 표준 C 로 작성된 검증용 프로그램과 비교해 보자.

main() 함수 내에 테스트 용 입력(test input vectors)을 생성하고 DUT 인 fir()을 반복적으로 호출한다. DUT의 출력을 파일에 취합해 두었다가 테스트를 마치고 참조 벡터 파일과 비교한다. 두 파일이 일치하면 검증이 성공한 것으로 한다. 테스트 입력의 생성, 함수 호출 그리고 결과 값의 수집이 차례로 수행된다. C 설계물의 검증이라면 비록 단순하긴 해도 충분하다.

HLS에서는 C 의 함수 fir()이 RTL로 합성될 것이다. 클럭의 개념이 없는 C의 함수와 RTL을 혼합 검증 해야 한다. 이때 다음과 같은 의문이 생긴다.

  • RTL로 합성된 하드웨어 모듈 fir 은 어느 순간에 작동될까?
  • 언재 유효한 입력이 있다고 판단할까?
  • 몇 개의 클럭 만에(letency 와 interval) 유효한 출력을 낼까?

C 프로그래밍 언어의 실행 절차는 순차적으로 고정되었다. 하지만 하드웨어를 기술한 언어 HDL을 실행시키려면 시간의 흐름을 관리하는 시뮬레이션 커널이 필요하다. 시간을 멈추고 다수의 모듈을 작동시켜서 병렬실행을 모사(simulate parallel execution)한다. 시간을 진행하며 어느 채널에 변화(사건, event)가 발생했는지 탐지하고 이에 반응하도록 연결된 모듈을 호출한다. 이렇게 실행방식이 전혀 다른 두 C와 HDL 모델을 혼합하여 검증할 수 있는 시험환경이 필요하다. SystemC 가 그것을 가능케 해준다.

SystemC로 구성한 C 함수 fir()의 시험 환경은 다음과 같다.

2-1. C 설계에 SystemC 싸개(wrapper) 씌우기 (sc_fir.h)

무시간(un-timed, 클럭 개념이 없는) C 함수 fir()을 SystemC 환경으로 끌어들이기 위해 싸개(wrapper)를 씌웠다. 시그널 채널 x_rdy의 상승 엣지(posedge) 감응된 메쏘드(C++의 멤버 함수로 콜-백  call-back 함수와 같다.) sc_fir_method() 에서 fir()이 호출된다. 외부로 부터 유효한 x 가 준비 되었다는 표시로 x_rdy 가 주어진다. x_rdy가 상승 엣지가 될 때 까지 기다렸다가 입력 x 를 읽어 함수 fir()을 호출 한다. 무시간 함수 fir()은 출력값 y 와 함께 즉시 되돌려 진다. y를 출력 하면서 외부에 y_rdy 로 준비 되었다는 표시를 해준다.

지금은 fir()이 무시간 C의 함수지만 향후 RTL로 합성되었을 경우 x_rdy 와 y_rdy 사이에 인터벌 클럭 만큼 간격이 벌어질 것이다.


2-2. 테스트 입력 벡터 생성(sc_stim.h)

테스트 데이터 x 는 외부에서 생성 요청이 있을 때(x_req.posedge()) 마다 새 값을 생성한다. 새 값이 준비되었다는 표시를 x_rdy 에 실어 외부에 알린다.

2-3. 검증용 모니터링(sc_mon.h)

DUT에서 출력을 내면 이를 감지하여(y_rdy.posedge()) 표준 벡터 파일에서 값을 읽어 비교한다. 시뮬레이션이 진행 되는 동안 실시간 비교가 이루어 진다.

2-4. 통합

위의 세가지 모듈을 한데 통합한다. HDL 에서 시그널로 각 모듈의 입출력을 바인딩 하는 것과 같다. 모듈이 아니므로 컨스트럭터에 감응과 메쏘드는 없다.

2-4. C++ main()

SystemC는 C++다. main() 이 필요하다. 그리고 위의 모듈들은 모두 헤더에 선언된 C++ Class에 불과 하다. 따라서 탑 모듈인 sc_fir_test 를 전언하고 객체를 구성해 줘야 한다. 이어서 sc_start()로 시뮬레이션 커널을 호출하여 시뮬레이션을 진행한다.

2-5. 무시간 시뮬레이션 (un-timed simulation)

SystemC는 디버깅을 위해 VCD 파형을 만들수 있다. VCD 파형을 gtkwave 유틸리티로 볼 수 있다.

3. C-RTL-SystemC Co-Simulation

HLS로 RTL을 얻었다면 이제 C-RTL-SystemC를 함께 묶어 시뮬레이션 해보자. 구성은 다음과 같다.

Verilog RTL을 SystemC 로 불러들이기 위해 싸개(wrapper)를 만든다. 크래스 속성에 sc_foreign_module로 지정된 것은 ModelSim의 규격이다. C++의 크래스에 Verilog 모듈의 이름과 입출력 포트들을 선언해 준것 뿐이다.

그외 SystemC 파일들은 C 설계 검증에서 사용했던 것과 같다. C-RTL-SC 를 모두 통합한 Co-Simulation 결과는 다음과 같다. 입력 x 의 생성과 y의 출력 사이에 RTL에서 소요된 인터벌(RTL interval)이 있다.

4. 결론

HLS 도구들이 C 를 변환하여 HDL을 생성하는 만큼 테스드 벤치도 자동 생성하는데 큰 노력을 기울이고 있다. 설계 전과정에서 검증이 차지하는 비중이 워낙 큰 만큼 테스트 벤치 자동화의 중요성에 토를 달 생각은 없다. 다만 자동 변환 과정이 너무나 복잡한데다 그로 인해 생성된 HDL 도 읽어볼 수는 있을 지언정 알아보기 어렵다. 더구나 HDL이 표준 입출력이 가능 하다고 해도 C/C++ 만큼에 비할 바가 못된다. 그리고 C/C++는 수학 함수들을 비롯해 수많은 검증 라이브러리를 갖추고 있다. 어렵게 C 테스트벤치를 HDL 로 바꿀 것이 아니라 SystemC로 검증 환경을 구현하는 편이 훨씬 생산적이라는 데 이의는 없을 것이다.

C/C++ 기반의 테스트 벤치의 또다른 장점으로 FPGA를 활용 Co-Emulation  구성도 가능하다는 점이다. FPGA 대신 비트코인 채굴에 쓰인다는 GPU, 컴퓨팅 가속기(Accelerator HW), 네트워크에 연결된 다른 컴퓨터(심지어 클라우드 컴퓨팅, Cloud Computing)들을 연결 할 수도 있다. 대규모 프로젝트에서 이미 그렇게들 하고 있다고 한다.


[전체 소스 보기]

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

고위 합성 튜토리얼(High-Level Synthesis Tutorial)
[목차][이전][다음]