기술노트4: C++ 템플릿 크래스 이해의 최소한
개요
풍부한 컴퓨팅 환경을 접할 수 있게된 요즘 굳이 전공을 따지지 않더라도 컴퓨팅 언어에 친숙하다. 반도체 설계 역시 컴퓨팅 언어를 사용하여 설계 생산성을 한층 높여왔다. 이에 약간의 프로그래밍 언어에 대한 이해를 가졌다면 반도체 설계를 시작할 수 있다. 넓은 추상성을 포용하는 C++는 가장 널리 사용되는 컴퓨팅 언어로서 알고리즘의 기술은 물론 하드웨어 시스템 모델링까지 활용 된다. SystemC는 하드웨어를 기술하고 병렬 시뮬레이션 커널을 갖춘 C++의 크래스 라이브러리다. ETRI 0.5um CMOS 공정 표준셀 디자인 킷(이하 '디자인 킷')에서 제공하는 각종 예제와 학습자료들의 테스트벤치는 SystemC 기반으로 작성 되었다.
C++의 템플릿 크래스의 최소한 이해와 베릴로그와 비교 그리고 병렬 시뮬레이션 커널의 사건 구동 병렬 시뮬레이션이 작동 하는 원리를 정성적으로 설명한다.
SystemC를 처음 접하면 마치 새로운 언어처럼 보이나 실은 C++ 그 자체일 뿐이다. 다양한 템플릿 크래스를 매우 현명하게 사용 되었을 뿐이다. 본 문서는 SystemC를 활용하여 시스템 수준 모델링을 시작하기전에 C++ 템플릿 크래스의 최소한 이해를 돕기 위한 것이다.
크래스는 단적으로 복합 자료(변수)를 묶는 C 언어의 구조체(structure)의 확장이다. 자료에 더하여 이 자료를 취급하는 행동(함수)을 함께 묶는다.
복합자료를 묶는 가장 널리 활용되는 예가 아마도 복소수(complex number) 체계일 것이다. 복소수체계에서 한 객체는 두개의 값, 허수와 실수,으로 표현된다.
Complex_Number = (Real) + (Image)i
컴퓨팅을 위해 구조체로 표현하면,
struct Complex_t
{
int Re;
int Im;
};
복소수 체계를 다루는 방법은 일반적인 수체계와 상이하다. 예를 들어 복소값의 크기는 켤레 복소수를 취한 제곱 구하기다.
struct Complex_t X;
mult = (X.Re*X.Re) - (X.Im*X.Im);
power = sqrt(mult*mult);
이 계산법은 X가 복소수 일때 유효하다. 따라서 복소수를 취급하는 방법을 아예 자료 구조체 내에 넣어두는 것이 현명하다.
class Conplex_t {
int Re;
int Im;
int conjugate(){
return (Re*Re - Im*Im);
}
int power(){
return (sqrt(conjugate()*conjugate());
}
};
선언할 수 있는 숫자가 정수 뿐만 아니라 부동 소숫점 실수도 있으므로 정수를 취급하는 위의 크래스에 더하여 또 만들어야 한다면 매우 불합리하다. 이럴때 아래와 같이 템플릿을 사용한다.
template<typename T>
class Complex_t {
T Re;
T Im;
public:
Complex_t(T re, T im) { // Constructor
Re = re; Im = im;
}
void put_Re(T x) { Re = x;}
void put_Im(T x) { Im = x;}
T get_Re() { return Re;}
T get_Im() { return Im;}
T conjugate(){ return((real*real)-(image*image));}
T Power(){ return (sqrt(abs(conjugate())));}
};
객체를 선언하여 사례화 할 때 템플릿에 자료형을 지정한다.
Complex_t<int> IntX;
Complex_t<float> FloatY;
복소수 표현 크래스의 내부 자료 Re 와 Im은 외부에 노출되지 않는다. 내부로 접근 하려면 외부 공개된 소속 함수들을 통한다.
IntX.put_Im(5);
Float.Y.put_Re(3.1);
크래스와 동일한 이름을 갖는 특별한 함수가 있다. 크래스 객체를 선언 할 때 내부를 구축하는 역활을 한다. 이를 구성자(constructor)라 한다. 구성자 함수는 선언될 때 한번 호출될 뿐이다.
Complex_t<int> intX(2,3);
Complex_t<float> floatY(3.14, 4.5);
---------------------------------------------------
템플릿의 활용도는 매우 넓다. C++의 기본 자료형의 경우 비트 폭과 연산 방법이 정해져 있다. 하지만 하드웨어의 경우 임의의 비트폭을 갖는 객체가 가능 하며 연산자 또한 이에 맞춰져야 한다.
template <u_int N>
class bit_vector_t
{
bool m_next_val[N];
bool m_curr_val[N];
int nLen;
char m_sz_val[N+1];
public:
bit_vector_t():nLen((int)N)
{
for (int i=0; i<N; i++) m_next_val[i] = false;
for (int i=0; i<N; i++) m_curr_val[i] = false;
}
bit_vector_t(const char* szVal):nLen((int)N)
{
write(szVal);
}
char* to_string()
{
for (int i=0; i<(int)N; i++)
if (m_curr_val[i]) m_sz_val[i] = '1';
else m_sz_val[i] = '0';
m_sz_val[N] = '\0';
return m_sz_val;
}
void write(const char* szVal)
{
if ((int)strlen(szVal)!=nLen)
{
fprintf(stderr, "Bit Vector NOT match!\n");
return;
}
for (int i=0; i<strlen(szVal); i++)
if (szVal[i]=='1') m_curr_val[i] = true;
else m_curr_val[i] = false;
}
int length()
{
return nLen;
}
// overload the | operator
friend bit_vector_t operator | (const bit_vector_t& obj1, const bit_vector_t& obj2)
{
bit_vector_t<N> Temp;
for (int i=0; i<N; i++)
Temp.m_curr_val[i] = obj1.m_curr_val[i] | obj2.m_curr_val[i];
return Temp;
}
// overload the & operator
friend bit_vector_t operator & (const bit_vector_t& obj1, const bit_vector_t& obj2)
{
bit_vector_t<N> Temp;
for (int i=0; i<N; i++)
Temp.m_curr_val[i] = obj1.m_curr_val[i] & obj2.m_curr_val[i];
return Temp;
}
};
이정도면 혹시 C++는 하드웨어 모델링을 위해 만들어진 것은 아닐까라는 생각이 든다.
스트림 출력 연산자 활용 사건 감응 지정
SystemC로 하드웨어 모델 테스트 벤치를 작성하기 위해 필요한 최소한의 이해는 C++ 템플릿 크래스의 이것이 전부다. 믿거나 말거나! 예제를 통해 익혀보자.
SystemC가 C++의 크래스 라이브러리라고 했지만 여전히 생소하다. 모듈 크래스만 풀어보자.
SystemC는 하드웨어 객체들을 묘사하기 위한 크래스들의 집합체이며 아울러 병렬실행 커널을 가지고 있다.
<그림>
SystemC 가 C++ 이므로 문장의 병렬성은 없다. 모든 병렬실행의 단위는 함수다. 이 함수는 사건에 의해 시뮬레이션 커널에 의해 호출되는데 이를 사건구동 이라 한다.
https://www.doulos.com/knowhow/systemc/systemc-tutorial/modules-and-processes/
구동된다.
https://techne-atelier.com/digital-design/a-tour-of-systemc/
댓글 없음:
댓글 쓰기