2024년 4월 3일 수요일

ETRI 0.5um CMOS DK 예제: FIR8 / [2] 아듀이노 보드 에뮬레이션

ETRI 0.5um CMOS DK 예제: FIR8

[2부] 아듀이노 보드 에뮬레이션

목차:

1. 개요

2. WSL에서 USB 장치 사용법

3. 아듀이노 개발환경 설치

4. 아듀이노 보드에 FIR8 알고리즘 구현

5. 에뮬레이션 프로토콜 정의

6. 테스트벤치 재사용: 시뮬레이터와 에뮬레이션의 병행

7. Co-Simulation /  Co-Emulation & Testbench Re-Use

8. 시뮬레이션 가속기/참고문헌

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

1. 개요

SystemC는 하드웨어의 행위(hardware behavior)를 묘사하기 위한 크래스(C++ class)와 동시실행 시뮬레이터(concurrency simulator)가 포함된 C++의 라이브러리다. 레지스터 전송수준(RTL)은 물론 트랜잭션 수준(TLM, Transaction Level Modeling)을 비롯하여 시스템 수준까지 매우 넓은 추상화 수준(abstraction level)을 지원한다. 합성가능(synthesizable)한 RTL의 CPU에서부터 운영체제 그리고 응용 프로그램까지 설계 할 수 있다는 의미다. 별도의 언어체계가 아닌 C++ 그 자체이기 때문에 컴퓨터로 할 수 있는 모든 것이 가능하다[SystemC는 최신방법론 인가?]. 물론 설계자의 상상력에 달려 있긴 하다. SystemC의 활용의 예로 시뮬레이션과 에뮬레이션을 함께 수행하는 기법(Co-Simulation/Co-Emulation)을 소개한다. 에뮬레이션은 컴퓨터 시스템의 주변장치로 연결된 하드웨어를 시뮬레이터로 끌어들인다. 이더(Ethernet)으로 연결된 다수의 컴퓨터를 동원하여 대규모 병렬 컴퓨팅을 구현할 수 있다. 주변장치와 컴퓨터 시스템 사이의 인터페이스 병목을 해결하면 FPGA는 물론 GPU 활용 시뮬레이션 가속기(HDL simulation accelerator)로 활용할 수 있다.

앞서 다룬 FIR8 알고리즘[바로가기]을 아듀이노 보드(Arnuino)에 구현하고 이를 에뮬레이션 하드웨어로 사용하는 예를 살펴본다. 컴퓨터와 아듀이노 보드 사이의 USB 인터페이스가 고속이라고 할 수 없는 직렬통신(serial communication)이기에 가속기로서 의미는 없지만 SystemC의 가능성을 엿볼 수 있을 것이다. 향후 MPW로 제공받은 칩 테스트의 용도로 쓰일 수 있다. 시뮬레이션과 에뮬레이션을 엮어 동시에 수행하게 되므로 테스트벤치를 재사용 할 수 있다.

2. WSL에서 USB 장치 사용법

리눅스 운영체제에서 직렬 포트는 터미널의 용도로 널리 사용되어 왔기에 주변장치로 매우 안정된 작동을 보여준다. WSL 서비스의 가상 머신(virtual machine)으로 리눅스가 설치되었다면 윈도우즈 운영체제의 장치 드라이버를 연결해 주어야 한다.

참고] Windows WSL: Connect USB device, https://learn.microsoft.com/en-us/windows/wsl/connect-usb

원래 윈도우즈의 USB 장치는 WSL에 연결되어 있지 않기 때문에 USBIPD-WIN 도구를 설치 하자. 아래 링크에서 .msi 설치 프로그램을 내려받아 실행 한다.

    https://github.com/dorssel/usbipd-win/releases

윈도우즈 파워-쉘(Power-Shell)을 관리자 권한으로 열어 가상 머신 리눅스와 공유가능한 USB 장치의 목록을 확인한다.

    PS C:\> usbipd list

    Connected:
    BUSID  VID:PID    DEVICE                         STATE
    2-8    045e:09c0  USB Input Device               Not shared
    2-10   8087:0026  Intel(R) Wireless Bluetooth(R) Not shared
    4-2    343c:0000  USB Type-C Digital AV Adapter  Not shared
    4-3    2a03:0043  USB Serial Device (COM3)       Not shared

이 목록은 컴퓨터에 연결된 장치에 따라 다르다. 나열된 장치들이 모두 공유되어 있지 않다. 아듀이노 보드의 USB 장치를 찾아 공유 시킨다. 아듀이노 보드는 USB의 직렬 포트로 연결된다. 위의 목록에서 "USB Serial Device (COM3)" 다. 윈도우즈에서 아듀이노 IDE를 설치했다면 "Arduino Uno (COM3)"로 목록에 표시될 것이다. 아듀이노 보드의 BUSID를 WSL에 공유시키는 명령은 아래와 같다.

    PS C:\> usbipd bind --busid 4-3

공유가 이뤄 졌는지 확인해보자.

    PS C:\> usbipd list

    Connected:
    BUSID  VID:PID    DEVICE                         STATE
    2-8    045e:09c0  USB Input Device               Not shared
    2-10   8087:0026  Intel(R) Wireless Bluetooth(R) Not shared
    4-2    343c:0000  USB Type-C Digital AV Adapter  Not shared
    4-3    2a03:0043  USB Serial Device (COM3)       Shared

공유된 USB 장치를 WSL에 연결(attach)시킨다. 장치 식별번호 busid 에 유의한다. 파워-쉘이 관리자 권한이어야 한다.

    PS C:\> usbipd attach --wsl --busid 4-3

    usbipd: info: Using WSL distribution 'Ubuntu-20.04' to attach; the device will be available in all WSL 2 distributions.
    usbipd: info: Using IP address 172.17.128.1 to reach the host.

리눅스 터미널을 열어 lsusb 명령으로 USB 장치들의 목록을 확인해 보자.

    ~$ lsusb

    Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
    Bus 001 Device 002: ID 2a03:0043 dog hunter AG Arduino Uno Rev3
    Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

나열된 목록에 윈도우즈에서 연결시킨 아듀이노 보드가 보인다.

3. 아듀이노 개발환경 설치

아듀이노 개발환경(IDE, integrated development environment)은 윈도우즈와 리눅스용이 있다. 아래 링크에서 64비트 리눅스용 압축 파일을 내려받는다.

    https://www.arduino.cc/en/software

압출 파일을 적당한 위치에 푼다. 옵션 폴더 /opt 에 풀기를 권장한다. 시스템이 관리하는 폴더이므로 관리자 권한으로 실행한다.

    $ cd /opt
    $ sudo unzip arduino-ide_2.3.2_Linux_64bit.zip
    $ sudo chown -R <user> arduino-ide_2.3.2_Linux_64bit

아듀이노 개발환경 실행 파일을 /usr/bin 에 심볼 링크(바로가기) 한다.

    $ sudo ln -s /opt/arduino-ide_2.3.2_Linux_64bi/arduino-ide arduino-ide

리눅스의 실행 파일 경로에 바로가기가 생성되었으므로 어느 위치에서도 아듀이노 IDE를 실행할 수 있다.

    $ cd
    $ cd ETRI-0.5u-CMOS-MPW-DK-Example--FIR8/0_algorithm/sc_emulated/
    $ arduino-ide fir8_ino

아두이노 보드가 리눅스의 장치 /dev/ttyACM0 에 연결된 것을 확인한다.

4. 아듀이노 보드에 FIR8 알고리즘 구현

아듀이노 보드는 8비트 마이크로 프로세서 ATMEGA(AVR)을 내장하고 있다. 프로그램 개발도구로 전용 C/C++ 컴파일러를 사용한다. 이 도구는 오픈-소스로서 표준 C/C++다. 따라서 앞 장에서 다뤘던 언-타임드 FIR8 알고리즘 fir8.cpp 을 변경 없이 구현 할 수 있다. 아듀이노 에뮬레이션 코드는 아래와 같다.

    fir8_ino.ino

아듀이노는 단순히 마이크로 프로세서 활용 보드를 넘어 개방형 시스템 개발 도구로 성공적인 모습을 보여준다. 수없이 많은 개발자(취미가들도 상당부분을 차지한다)들이 다양한 입출력 장치 프로그램의 라이브러리를 제공하여 아듀이노 개발환경을 풍요롭게 하고있다. USB 직렬 포트 구동 라이브러리도 그중 하나다. 사용법도 매우 단순하다. 설정 함수에서 보오 율(baud rate)을 설정하고 호스트 컴퓨터의 연결을 시도한다.

    void setup()
    {
      Serial.begin(9600);
      while (!Serial)
      {
        ;  // wait for serial port to connect(native USB port only)
      }

      establishContact();  // send a byte to establish contact

      pinMode(LED_BUILTIN, OUTPUT);
      digitalWrite(LED_BUILTIN, HIGH);
    }

아듀이노 보드는 별도의 운영체제를 갖지 않으므로 펌웨어 내부에서 무한반복하며 시리얼 포트의 입력을 기다린다.

    void loop()
    {
      if (Serial.available() > 0)
      {
        digitalWrite(LED_BUILTIN, LOW);
        incomingByte = Serial.read();
        x = (uint8_t)incomingByte;

        fir(&y, x);

        for (int i=0; i<FILTER_TAP_NUM; i++)
        {
          outingByte = (int)(shift_reg[i]);
          Serial.write(outingByte); // Send shift register X
        }

        outingByte = (int)(y & 0x00FF);
        Serial.write(outingByte); // LSB of y
        outingByte = (int)((y>>8) & 0x00FF);
        Serial.write(outingByte); // MSB of y

        digitalWrite(LED_BUILTIN, HIGH);
      }
    }

직렬 포트를 통해 8비트 입력를 받아 필터 함수 fir(*y, x)를 호출한다. 계산 결과 y는 16비트 이므로 두번에 걸쳐 전송한다. 디버깅을 위해 쉬프트 레지스터 격인 shift_reg[]를 결과 이전에 전송하고 있다.

5. 에뮬레이션 프로토콜 정의

호스트에서 실행중인 SystemC 테스트 벤치와 아듀이노 에뮬레이션 보드 사이의 프로토콜(protocol, 통신규칙)을 정의한다. 이 통신규칙은 설계물에 따라 입출력 신호의 비트 폭이 다르다. 직렬통신은 8비트 단위의 데이터 전송이므로 비트 단위로 조합과(또는) 분할로 에뮬레이션 될 설계물(함수)에 맞춰져야 한다. 통신이 발생하는 시점은 전적으로 호스트 쪽의 타임드 테스트벤치 모델에 의해 결정된다. 에뮬레이션을 위한 fir8 의 SystemC 모듈은 아래와 같다.

    sc_fir8.h

입출력 포트는 타임드 모형과 동일하다. 하위 모듈로 8개의 PE를 두고 연속적으로 연결한 배열 구조 대신 에뮬레이터 보드와 통신을 수행한다. 에뮬레이션 보드와 통신을 개시하는 시점은 타임드 모형과 클럭 동기를 맞춘 클럭 상승 엣지 사건이 발생되었을 때다. 

    SC_MODULE(sc_fir8)
    {
        sc_in<bool>             clk;
        sc_in<sc_uint<8> >      Xin;
        sc_out<sc_uint<8> >     Xout;
        sc_in<sc_uint<16> >     Yin;
        sc_out<sc_uint<16> >    Yout;

        sc_signal<sc_uint<8> >  X[FILTER_TAP_NUM];

        void fir8_thread(void)
        {
            while(true)

            {
                wait(clk.posedge_event());
                x = (uint8_t)Xin.read();
                while(write(fd, &x, 1)<=0)  // Send Byte
                    ......
                y = (uint16_t)(yH<<8) | (uint16_t)(yL);

                Yout.write(y);
            }
        }

        SC_CTOR(sc_fir8): clk("clk"),
            ......
        {
            SC_THREAD(fir8_thread);
            sensitive << clk;
                ......
        }
    };

모듈이 사례화(instantiate)되면 크래스의 구성자가 최초 실행된다. 이 구성자 내에서 운영체제의 직렬 포트 장치 구동기를 초기화 하고 에뮬레이션 보드와 연결을 시도한다. 리눅스 운영체제는 주변장치를 파일로 취급하므로 표준 입출력 장치 stdio.h 의 open(), read(), write()로 접근한다. 연결 시도 규칙은 에뮬레이션 보드에서 'A'를 보내오면 호스트는 이를 반송하여 연결을 확인토록 하였다. WSL 리눅스에서 아듀이노 보드가 연결된 장치명은 /dev/ttyACM0 였다. 이 장치를 입출력이 가능하도록 설정하고 보오율은 아듀이노 보드 측의 펌웨어 설정과 동일한 9600 이다.

        SC_CTOR(sc_fir8): clk("clk"),......
        {
            ......
            // Arduino DUT
            fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY);

            // Set up serial port
            options.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
                ......
            tcsetattr(fd, TCSANOW, &options);

            // Establish Contact
            while(!len)
                len = read(fd, &rx, 1);
            if (rx=='A')
                write(fd, &rx, 1);
        }

클럭 clk 에 의해 구동되는 쓰레드 함수 fir8_thread()는 아래와 같다.

        sc_signal<sc_uint<8> >  X[FILTER_TAP_NUM];

        void fir8_thread(void)
        {
            uint8_t     x;
            uint8_t     yL, yH;
            uint16_t    y;

            while(true)
            {
                wait(clk.posedge_event());
                x = (uint8_t)Xin.read();
                while(write(fd, &x, 1)<=0)  // Send Byte
                    usleep(100);

                for (int i=0; i<FILTER_TAP_NUM; i++)
                {
                    while(read(fd, &x, 1)<=0)  // Shift Register of X
                        usleep(100);
                    X[i].write(sc_uint<8>(x));
                }

                while(read(fd, &yL, 1)<=0)  // LSB of y
                    usleep(100);
                while(read(fd, &yH, 1)<=0)  // MSB of y
                    usleep(100);
                y = (uint16_t)(yH<<8) | (uint16_t)(yL);

                Yout.write(y);
            }
        }

클럭 clk에 상승 엣지 사건이 발생하면 에뮬레이션 보드로 8비트 Xin을 전송하면서 통신이 개시된다. 호스트 모듈(SystemC의 테스트벤치)은 에뮬레이터로부터 출력이 전송되기를 기다리며 시뮬레이터의 시간을 멈춘채 대기한다.

에뮬레이터는 직렬 포트로 전송된 x 를 받아 fir()을 호출하고 필터 계산을 수행한 뒤 그 결과를 호스트로 전송한다. 계산결과를 전송하기 전에 디버깅 목적으로 8개 쉬프트 레지스터 값을 먼저 내보낸다. 필터 함수 fir()의 계산 결과 y 는 16비트 이므로 2번에 걸쳐 전송된다. 이때 하위 바이트 먼저 전송하기로 하였다.

호스트의 SystemC 에뮬레이터 구동 모듈은 직렬 포트를 통해 전송된 8비트 데이터를 받아 이를 16비트로 재조립하여 Yout로 내보낸다. SystemC 시뮬레이터는 비로서 제어권을 받아 시간을 진행 시킨다.

디버깅 용으로 전송받은 쉬프트 레지스터의 값을 시간 순으로 VCD 기록한다.

#ifdef VCD_TRACE_FIR8
        // WAVE
        fp = sc_create_vcd_trace_file("sc_fir8");
        fp->set_time_unit(100, SC_PS);  // resolution (trace) ps
        sc_trace(fp, clk, "clk");
        sc_trace(fp, Xin,  "Xin");
        sc_trace(fp, Xout, "Xout");
        sc_trace(fp, Yin,  "Yin");
        sc_trace(fp, Yout, "Yout");
        char szTrace[8];
        for (int i=0; i<FILTER_TAP_NUM; i++)
        {
            sprintf(szTrace, "X_%d", i);
            sc_trace(fp, X[i], szTrace);
        }
#endif

6. 테스트벤치 재사용: 시뮬레이터와 에뮬레이션의 병행

에뮬레이션에서는 하위 SystemC 모듈로 구성 하였던  타임드 모형 sc_fir8.h 을 장치 구동 쓰레드 함수로 대체되었을 뿐 외형은 변함이 없다. 따라서 타임드 알고리즘의 시험에 사용했던 테스트 벤치를 변경없이 적용 한다. 시뮬레이션과 에뮬레이션이 병행된 검증용 메이크 파일은 다음과 같다.

    Makefile

메이크 유틸리티로 컴파일,

    ~$ cd
    ~$ cd ETRI-0.5u-CMOS-MPW-DK-Example--FIR8/0_algorithm/sc_emulated/
    ~$ make

실행,

    ~$ make run

아래 그림은 재활용된 테스트 벤치 sc_fir8_tb.h와 에뮬레이션 구동 모듈로 재구성된 sc_fir8.h 그리고 아듀이노 보드의 관계를 보여준다.

타임드 모델에서 sc_fir8,h 는 PE를 사례화 하고 어레이 구조를 구성 했었다. 이에 반해 에뮬레이션 모델은 아듀이노 보드와 통신하는 역활을 수행한다. 언타임드 모델의 fir8() 함수가 그대로 아듀이노 보드에 구현되어있다.

    타임드 모델의 sc_fir8.h

    에뮬레이션 모델의 sc_fir8.h
    아듀이노 보드에서 fir(), fir8_ino.ino

에뮬레이션의 결과를 VCD로 확인 할 수 있다. SystemC 테스트 벤치의 클럭 동기에 맞춰 에뮬레이션이 진행되고 있다.

참고] FIR8 예제 깃-허브:
    https://github.com/GoodKook/ETRI-0.5u-CMOS-MPW-DK-Example--FIR8.git

7. Co-Simulation/Co-Emulation

https://www.youtube.com/watch?v=Wr1XdpmyJ-w&t=58s


8. 시뮬레이션 가속(Simulation Acceleration) 참고문헌

1. UVM Simulation Acceleration, https://www.aldec.com/en/solutions/hardware_emulation_solutions/acceleration

2. Simulation Acceleration—Maximizing Simulator Performance, https://community.cadence.com/cadence_blogs_8/b/fv/posts/simulation-acceleration-maximizing-simulator-performance

3. Accelerating video and image processing design for FPGA using HDL coder and simulink, https://ieeexplore.ieee.org/abstract/document/7446221

4. A Full-System VM-HDL Co-Simulation Framework for Servers with PCIe-Connected FPGAs, https://par.nsf.gov/servlets/purl/10061085

5. Speeding-Up Simulation-Based Fault Injection of Complex HDL Models, https://ieeexplore.ieee.org/abstract/document/7781836

6. Linear Acceleration of HDL Simulation Using Naive Parallel Processing, https://webinars.sw.siemens.com/en-US/linear-acceleration-of-hdl-simulati/

7. Connecting reality and simulation: Couple high speed FPGAs with your HDL simulation, https://www.design-reuse.com/articles/13384/connecting-reality-and-simulation-couple-high-speed-fpgas-with-your-hdl-simulation.html

8. VHDL simulation acceleration using specialized functions, https://ieeexplore.ieee.org/document/621458

9. Simulation Acceleration with HW Re-Compilation Avoidance, https://ieeexplore.ieee.org/document/4450547

10. HDL Simulation Acceleration Solution for Microchip FPGA Designs, https://www.aldec.com/en/support/resources/documentation/whitepapers?file=2103

11. Integrating SystemVerilog and SCE-MI for Faster Emulation Speed, Developing your own Emulation API, https://www.aldec.com/en/company/blog/55--integrating-systemverilog-and-sce-mi-for-faster-emulation-speed


-------------------------------------------------------------
[이전] [1] FIR 알고리즘 및 병렬처리 구조 탐색
[다음]









댓글 없음:

댓글 쓰기