2025년 7월 7일 월요일

기술노트4: C++ 템플릿 크래스 이해의 최소한

기술노트4: C++ 템플릿 크래스 이해의 최소한

개요

풍부한 컴퓨팅 환경을 접할 수 있게된 요즘 굳이 전공을 따지지 않더라도 컴퓨팅 언어에 친숙하다. 반도체 설계 역시 컴퓨팅 언어를 사용하여 설계 생산성을 한층 높여왔다. 이에 약간의 프로그래밍 언어에 대한 이해를 가졌다면 반도체 설계를 시작할 수 있다. 넓은 추상성을 포용하는 C++는 가장 널리 사용되는 컴퓨팅 언어로서 알고리즘의 기술은 물론 하드웨어 시스템 모델링까지 활용 된다. SystemC는 하드웨어를 기술하고 병렬 시뮬레이션 커널을 갖춘 C++의 크래스 라이브러리다. ETRI 0.5um CMOS 공정 표준셀 디자인 킷(이하 '디자인 킷')에서 제공하는 각종 예제와 학습자료들의 테스트벤치는 SystemC 기반으로 작성 되었다.


[출처] UML for ESL Design - Basic Principles, Tools, and Applications

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/


베릴로그도 모른다면 최소한 디지털 회로는 안다고 하자.

#include <systemc.h>

SC_MODULE(my_module) {
  // Module content (ports, signals, processes, etc.)
  SC_CTOR(my_module) {
    // Constructor code
  }
};



struct my_module : ::sc_core::sc_module {
  // Module content
  my_module(sc_core::sc_module_name name) : sc_module(name) {
    // Constructor code
  }
};


------------------
ㅙㅈ ㅅㄱ무냔색 겨ㅜㄴ 챙ㄷ? 냐드둔
Youtube: How Transistor runs CODE? 
https://youtu.be/HjneAhCy2N4?si=PDoHQ4b3CiD_72eW

댓글 없음:

댓글 쓰기