레이블이 ETRI 0.5um CMOS Std-Cell DK인 게시물을 표시합니다. 모든 게시물 표시
레이블이 ETRI 0.5um CMOS Std-Cell DK인 게시물을 표시합니다. 모든 게시물 표시

2025년 10월 19일 일요일

라즈베리 파이 피코 활용, "내 칩" 테스트 장치(오실로스코프, 로직 아날라이져) 자작

라즈베리 파이 피코 활용,
"내 칩" 설계와 검증 그리고 테스트 장비 자작


1. Raspberry Pi PICO 보드 2개 (USB-C 타입 커넥터)

2. 어댑터 보드

왼쪽의 피코보드는 24채널 로직 아날라져 또는 "내 칩" 에뮬레이터-테스터 용이다.
오른쪽 피코보드는 2채널 아날로그-8채널 로직 오실로스코프다.

2-1. 회로도:

    https://github.com/GoodKook/ETRI-0.5um-CMOS-MPW-Std-Cell-DK/blob/main/PSCE_API/Board/Raspberry_PICO/PI_PICO_SCH.pdf

2-2. PCB 거버:

    https://github.com/GoodKook/ETRI-0.5um-CMOS-MPW-Std-Cell-DK/blob/main/PSCE_API/Board/Raspberry_PICO/Gender_Raspberry_PICO_Y3.zip

2-3. PCB 제작비:

    2천원(댓글 문의시 무상?)

2-4. 오실로스코프 프론트-엔드용 부품

    참조: https://oscilloscope.fhdm.xyz/wiki/Analog-Front-End

    Op Amp: MCP6002(8핀 DIP) x2
    다이오드: 1N4148 x4
    저항:  100옴(1/8w) x10, 680K옴(1/8w) x 2
    가변저항(트리머): 1M옴 x2
    커페시터(탄탈): 100n x3
    택트스위치: 4mm 소형 x2
    핀헤더: 수직(1열), 앵글(2열)
    핀헤더소켓: 수직(1열)

3. 조립

 

 

헤더 핀 긴 것으로 기판 양쪽으로 돌출 시키려면 가공과 조립이 다소 어렵지만 테스트 클립을 물리거나 확장할 때 매우 유용하다.


4. 소프트웨어: 오실로스코프와 로직 아날라이져

4-1. 피코 오실로스코프 펌웨어

    a. 참조:
        https://oscilloscope.fhdm.xyz/wiki/firmware-versions

    b. 펌웨어 다운로드:
        https://github.com/fhdm-dev/scpdl1/raw/master/a/v18/scoppy-pico-v18.uf2

    c. 오실로스코프 디스플레이 안드로이드 앱:
        https://play.google.com/store/apps/details?id=xyz.fhdm.scoppy
        * 1채널은 무료. 2채널을 사용하려면 앱 구입(약 5천원)

4-2. 로직 아날라이져:

    a. 깃허브: https://github.com/gusmanb/logicanalyzer
    * 펌 웨어와 응용 프로그램 소스가 제공되지만 미리 컴파일된 바이너리 사용

    b. 펌웨어:
    https://github.com/gusmanb/logicanalyzer/releases/download/v6.0.0.0/logic-analyzer-firmware_v6.0.0_BOARD_PICO.zip

    c. 소프트웨어:
    https://github.com/gusmanb/logicanalyzer/releases/download/v6.0.0.1/all-in-one_6.0.0.1-win-x64.zip

5. "내 칩" 검증 에뮬레이터-테스터 사용법

    a. 위의 두 앱은 자작파들 사이에 매우 잘 알려져 있어서 굳이 별도의 설명이 필요 없을만큼 차고 넘친다.

    b. 펌-웨어를 교체할 수 있으므로 FPGA 보드를 연결하여 칩 검증과 테스트 용도로 사용할 수 있다. 사용법은 디자인 킷 예제를 참조할 것.

    "내 칩 제작 서비스" 표준 셀 디자인 킷:
    https://github.com/GoodKook/ETRI-0.5um-CMOS-MPW-Std-Cell-DK


2024년 7월 9일 화요일

ETRI 0.5um CMOS DK 예제: 비디오 게임기 "Pong"

ETRI 0.5um CMOS DK 예제: 비디오 게임기 "Pong"

"알기만 하는 사람은 좋아하는 사람만 못하고, 좋아하는 사람은 즐기는 사람보다 못하다"

<논어>의 어느 구절을 현대에 맞게 표현 한 글이라고 한다. 베릴로그와 반도체 설계를 교과목으로 배우다 보면 마땅한 예제가 없기도 하고 수많은 문법들을 모두 다뤄야 한다는 압박에 수박 겉핧기 식이 되어 버린다. 그러다 조별 과제가 떨어지면 덩달아 반도체에 정나미도 떨어질까봐 염려된다. 이제 학기도 끝났고 어렴풋이 베릴로그와 반도체 설계의 맛을 봤다면 이제 즐겨볼 차례다. 거의 모든 베릴로그 교과서에 '교통 신호 제어기(Verilog Traffic Light)'나 '자판기(Verilog Vending Machine)' 등이 예제로 등장한다. 이들 예제들은 나름대로 유한 상태 머신(FSM)의 기술방법을 설명하기에 훌륭하긴 하지만 심심하다. 지루한 학기가 끝난 지금 여전히 반도체 설계에 애정이 남아 있길 바란다.

베릴로그로 비디오 게임을 구현해 보자. 아주 예전에 초등학교 문방구 앞에 있었다는 '탁구' 게임이다. 마이크로 컨트롤러(MCU)로 고전 게임들을 재현하는 프로젝트(Arduino Game Console)가 흔한 요즘이다. 이번 예제는 그보다 단순 하니 너무 기대는 말자. 비디오 게임을 아주 단순화 하여 베릴로그 만으로 구현 하였다. 이번 예제의 특징은 베릴로그의 조합회로 기술법이 아주 다양하게 사용되었다는 점이다. 또한 여러 기법 중 모듈 인수화(parameterized module)를 적절히 구사하였다. 아울러 비디오 신호및 디스플레이 컨트롤러를 이해할 수 있을 것이다. 입출력 신호의 갯수가 10여개에 불과 하며 베릴로그를 최적화 하면 "내칩 MPW"에 지월 할 수 있을 것이다. 예제는 아래 링크에서 받을 수 있다.

https://github.com/GoodKook/ETRI-0.5u-CMOS-MPW-DK-Example--pong_pt1/

시현 장치에 그림을 표현하려면, 그것도 움직이게 보이도록 하려면 엄청난 신호가 필요하다. 예를 들어 640x480 크기의 VGA 화면에 초당 10프레임을 보여준다고 해보자. 픽셀당 1비트의 흑백 화면이라고 해도 초당 무려 3백만개(640x480x10=3,072,000)의 값이 생산되어야 한다. 이런 신호를 생산하는 하드웨어를 설계한다고 해보자. 하드웨어를 설계 한다면 디지털 파형을 떠올리기 마련이다. 이 파형을 들여다 보며 작동을 파악하기는 불가능 하다. 비디오를 다루는 하드웨어의 설계의 테스트 벤치에는 파형이 만들어내는 모습을 시각적으로 볼 수 있는 방안이 마련되어야 한다.

멀티미디어 정보를 보여주고 들려주는 소프트웨어 도구들이 다양하게 준비되어 있으므로 이를 활용한다. SDL(Simple Directmedia Layer, https://libsdl.org/ )은 게임 소프트웨어의 제작에 사용되는 C++ 라이브러리다. 화면과 음성 그리고 입출력 장치를 쉽게 제어할 수 있다. 응용 프로그램 제작에 필요한 소스는 물론 라이브로리와 헤더가 모두 공개되어 있으므로 하드웨어 설계물의 검증을 위한 테스트 벤치에 어렵지 않게 접목시킬 수 있다.

SystemC 테스트 벤치에 SDL을 연결하여 베릴로그 시뮬레이션하는 모습은 아래와 같다.

이 시뮬레이션을 파형으로 보면 이렇다. 탁구대 크기를 128x64로 줄였음에도 파형을 통해 게임이 진행되는 상황을 살펴보기는 곤란하다.

중간 틈틈이 키보드로 패들을 상하로 조작하는 모습을 볼 수 있다. SDL을 접목 시켜 하드웨어 시뮬레이션에 입출력을 대화형(interactive/real-world stimulus, 실시간 상호작용)으로 구성함으로써 실제와 근접한 검증 환경이 되었다.

베릴로그 RTL의 검증을 위해 에뮬레이션 기법으로 게임기를 FPGA에 구현하였다. 비디오 신호를 다루는 만큼 컴퓨터 화면은 물론 실제 비디오 장치(VGA와 G-LCD)에 연결 하였다. 탁구 게임기 베릴로그는 비디오 신호를 생성 할 뿐 실제 디스플레이 컨트롤러는 포함되어 있지 않다. 비디오 컨트롤러까지 포함하면 "내칩 MPW"에서 허용하는 크기를 훌쩍 넘어서기 때문이다. 별도의 아두이노 보드를 비디오 컨트롤러로 사용한 에뮬레이션 구성은 아래와 같다. 시뮬레이션을 위해 구성한 테스트 벤치가 그대로 재활용 되고 있다는 점에 유의하자. 설계물의 추상화 수준이 베릴로그 RTL에서 FPGA 게이트 회로로 변경 되었다고 검증 환경이 바뀌었다면 온전한 검증이라고 할 수 없다.

아두이노 보드를 가지고 그래픽 LCD 구동, VGA 구동 시키는 공개 라이브러리가 많이 있으므로 이를 활용 하였다. 반도체를 설계하면서 베릴로그를 익히는데 그치지 않고 내장형 시스템 (embedded system) 프로그래밍 까지 두루 섭렵해보자. CPU 라는 반도체에 의미를 부여하는  펌웨어 코딩 또한 반도체 설계의 한 방법론이다.


이 예제를 만드는 도중 '반도체 설계 취미가'의 책상 샷! 아주 저렴하게 반도체 설계 실험실을 꾸몄다. FPGA+Arduino DUE+MEGA+Oscilloscope+MultiTester 합쳐서 50만원도 않됨. 이게다~ 알리 상회 덕분.

반도체 설계와 MPW제작에 심오한 알고리즘의 구현도 좋지만 재미를 빼놓을 수 없다. 좀더 최적화 하고 아이디어를 넣어 재미있는 게임기를 만들어 보기 바란다. "내칩 MPW", 씹고 뜯고 맛보고 즐겨보자.


2024년 6월 2일 일요일

ETRI 0.5um CMOS DK 예제: counter8/16, 디지털 회로 칩 테스트 방법

ETRI 0.5um CMOS DK 예제: counter8/16, 디지털 회로 칩 테스트 방법

목차

    1. 개요
    2. Verilog: 적재가능한 8비트 2진 카운터
    3. SystemC: 테스트 벤치
    4. Arduino: 칩과 테스트벤치 인터페이스
    5. 칩 테스트
    6. 결론

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

1. 개요

한 객체(신호)를 다수의 비트로 표현하는 디지털 회로를 테스트 하기는 쉽지 않다. 로직 아날라이져(logic analyzer)라는 측정 장비를 사용하면 트리거를 설정한 후 출력을 병렬로 획득하고 이를 분석할 수 있으나 입력은 사정이 다르다. 병렬로 주어야 할 입력 신호의 각 비트선 마다 펑션 제네레이터(function generator)를 쓸 수도 있겠으나 동원해야할 신호 발생기의 댓수도 많아질 뿐만 아니라 정작 동시에 값을 주어야 하기 때문에 동기를 맞춰줘야 하는 어려움이 있다. 로직 아날라이져 측정기는 가격이 만만치 않긴 하지만 시장에서 구입 할 수 있다. 하지만 임의 병렬 신호 발생기는 시장에 나와있지 않다. 이는 설계마다 주어야할 입력신호 열이 다르기 때문에 범용 장비로 발매되기 곤란하다. 임의 신호열을 적절하게 발생 시키려면 장비를 프로그램 해주어야 한다. 통상 테스트 엔지니어가 이 부분을 담당한다. 디지털 테스트 신호 발생 장비는 주문형으로써 매우 고가다.

"내칩 MPW"에서 제작해 주는 칩의 핀 수는 28개다. 이중 전원과 접지를 빼면 26개의 핀이 신호선으로 사용할 수 있다. 입력 또는 출력으로 사용 할 핀의 갯수는 설계마다 다르다. 만일 디지털 설계로 칩을 제작했을 경우 이를 테스트 하려면 장비를 동원해야 하는 어려움에 봉착하게된다.

디지털 설계의 검증은 레지스터 전송 수준(RTL, Register Transfer Level) 시뮬레이션을 수행 한다. 이를 위해 검증 대상(DUT, Design Under Test)에 입력을 주고 출력을 검사하는 테스트 벤치(testbench)를 작성한다. 테스트 벤치는 DUT를 기술했던 HDL로 작성하거나 그보다 높은 추상화 수준의 C++ 언어를 사용한다. 어떤 경우든, Verilog, VHDL, C, C++ 등은 모두 컴퓨팅 언어다.

상당한 성능의 마이크로 컨트롤러를 채택한 응용 보드들이 매우 저렴하게 보급되고 있다. 교육 뿐만 아니라 취미가, 심지어 연구 및 산업 현장에서도 활용되고 있다. 그중 가장 널리 보급된 아두이노 보드를 활용하여 칩 테스트 방법을 설명한다. 사용할 아두이노 보드는 Arduino 2560 MEGA 다. 입출력 전압이 5V 이며 입출력 설정이 가능한 디지털 핀의 갯수가 32개 이상으로 "내칩 MPW"에서 제작되는 칩을 테스트 할 수 있는 요건에 부합한다. 가격 또한 2~3만원 대로 매우 저렵하다. 게다가 수많은 사용자들이 만들어 놓은 라이브러리들이 풍부하게 제공된다는 점 또한 큰 매력이라고 하겠다.

아듀이노 보드는 컴퓨터에 기본적으로 장착된 USB 주변장치와 직렬 통신 방식으로 연결 된다. 운영체제(윈도우즈, 리눅스 등)는 컴퓨터에 장착된 모든 주변장치를 컴퓨팅 언어로 제어 할 수 있도록 장치 구동기 소프트웨어를 제공한다. USB의 직렬 통신 포트도 예외는 아니다.  앞서 DUT의 시뮬레이션 검증을 위해 테스트 벤치는 컴퓨팅 언어로 제작해 두었다. 따라서 테스트 벤치에서 DUT와 주고받던 모든 입출력 신호는 모두 USB를 통해 아듀이노보드와 통신 할 수 있다는 뜻이다.

Verilog, VHDL 같은 하드웨어 기술 언어(HDL)도 C/C++ 언어의 함수를 호출 할 수 있는 방법(PLI/VPI, DPI, VHPI 등)이 마련되어 있으며 이를 통해 운영체제의 장치 구동기에 접근 할 수 있다. 하지만 매우 난해하다. 합성을 고려한 칩의 RTL 설계는 HDL을 이용하고 테스트 벤치는 SystemC를 활용하길 권한다. SystemC는 소프트웨어 및 하드웨어를 망라한 시스템 모델링을 위해 제공되는 C++ 라이브러리(libraries)다. GNU C++ 컴파일러로 컴퓨터로 할 수 있는 모든 수단을 칩 설계와 검증에 동원 할 수 있다는 뜻이다[참조]. 게다가 SystemC 는 오픈-소스다.

                        +---------------+
                        | SystemC TB    |
                        +-----+  in     |
                   +-------+  |  C++    |
                   |  DUT  |  |         |
                   |   in  +--->        |
                   |Verilog|  |         |
                   |       <----        |
                   |       |  |         |
                   +-------+  |         |
                         +----+         |
             Simulation  |              |
                         +--------------+

                         Re-Use Testbench

                         +--------------+
                         | SystemC TB   |
                         +----+   in    |
+--------+  +---------+       |   C++   |
|  DUT   |  | Arduino |       |         |
|   in   +---> MEGA   |       |         |
| MyChip |  |         <=[USB]=>         |
|  MPW   <----        |       |         |
|        |  |         |       |         |
+--------+  +---------+       |         |
                         +----+         |
        Chip Test        |              |
       by Emulation      +--------------+

Verilog 로 작성된 설계물의 검증을 위해 SystemC 로 작성된 테스트 벤치를 칩-테스트에 재활용(testbench re-use)하는 방법을 제시한다. 예제를 위한 환경구성 요건은 아래와 같다.

소프트웨어
    - 운영체제: WSL/Ubuntu 20 [설치법]
    -  개발도구: GNU C++/clang 17, SystemC, Verilator [설치법]

하드웨어
    - Arduino 2560 MEGA [설치법]

예제 깃-허브:
    https://github.com/GoodKook/ETRI-0.5u-CMOS-MPW-DK-Example--Counter8


2. Verilog: 적재가능한 8비트 2진 카운터

표준 LSI인 4비트 2진 카운터74163을 2개 엮어 8비트 2진 카운터를 설계 했다. 74163의 데이터 쉬트에 따르면 2진 카운터의 내부 논리 회로도는 아래와 같다.

논리회로로 그려진 이 회로도는 쉽게 눈에 들어오지 않는다. 인간의 언어와 가까운 Verilog로 작성하면 아래와 같다. 베릴로그 언어를 잘 알지 못한다 해도 한눈에 1씩 증가하는 카운터 임을 알 수 있다. 반도체 설계에 언어를 하려는 근본적인 이유다.

// Filename: LS163.v
// Loadable binary 4-bit counter

module LS163(nCLR, nLOAD, Din, CLK, ENP, ENT , Dout, RCO);
    input           CLK, nCLR, nLOAD;
    input           ENP, ENT;
    input  [3:0]    Din;
    output [3:0]    Dout;
    output          RCO;

    reg [3:0] cnt;
    always @(posedge CLK or negedge nCLR)
    begin
        if (!nCLR)
            cnt <= 0;
        else
        begin
            if (!nLOAD)
                cnt <= Din;
            else if (ENP && ENT)
                cnt <= cnt + 4'b0001;
        end
    end

    assign RCO = cnt[3] & cnt[2] & cnt[1] & cnt[0];
    assign Dout = cnt;
endmodule

다음은 2개의 4비트 카운터를 이어붙인 8비트 카운터다. 컴퓨팅 언어에 익숙한 현대인은 금방 뜻을 알아차릴 수 있다. 역시 언어로 하드웨어를 묘사하기 쉽다.

module counter8(CLK, nCLR_L, nCLR_H, nLOAD_L, nLOAD_H, ENP, ENT, Din, Dout, RCO);
    input           CLK;
    input           nCLR_L, nCLR_H;
    input           nLOAD_L, nLOAD_H;
    input           ENP, ENT;
    input  [7:0]    Din;
    output [7:0]    Dout;
    output          RCO;

    wire Full;    

    LS163 u_LS163xL(
            .nCLR(nCLR_L),
            .nLOAD(nLOAD_L),
            .Din(Din[3:0]),
            .CLK(CLK),
            .ENP(ENP),
            .ENT(ENT),
            .Dout(Dout[3:0]),
            .RCO(Full));

    LS163 u_LS163xH(
            .nCLR(nCLR_H),
            .nLOAD(nLOAD_H),
            .Din(Din[7:4]),
            .CLK(CLK),
            .ENP(ENP),
            .ENT(Full),
            .Dout(Dout[7:4]),
            .RCO(RCO));
endmodule

3. SystemC: 테스트 벤치

8비트 2진 카운터의 테스트 벤치는 SystemC로 작성 하였다. SystemC를 잘 모른다 하더라도 앞서 베릴로그를 읽을 수 있다면 그 뜻을 바로 알아차릴 수 있을 것이다. 컴퓨팅 언어 공부는 일단 예제 읽기로 시작하자.

/*****************************************************
Vendor: GoodKook, goodkook@gmail.com
Associated Filename: sc_counter8_TB.h
Purpose: Testbench for counter8
Revision History: Jun. 1, 2024
*******************************************************/

#ifndef _SC_COUNTER8_TB_H_
#define _SC_COUNTER8_TB_H_

#include <systemc.h>
#include "Vcounter8.h"
#ifdef CO_EMULATION
#include "counter8.h"
#endif

SC_MODULE(sc_counter8_TB)
{

    sc_clock            CLK;
    sc_signal<bool>     nCLR_L;
    sc_signal<bool>     nCLR_H;
    sc_signal<bool>     nLOAD_L;
    sc_signal<bool>     nLOAD_H;
    sc_signal<bool>     ENP;
    sc_signal<bool>     ENT;
    sc_signal<bool>     RCO;

    // Verilator treats all Verilog's vector as <uint32_t>
    sc_signal<uint32_t> Din_n32;
    sc_signal<uint32_t> Dout_n32;
    // Exact DUT ports' vector width
    sc_signal<sc_uint<8> > Din_n8;
    sc_signal<sc_uint<8> > Dout_n8;

    // Verilated DUT or Foreign Verilog
    Vcounter8*   u_Vcounter8;
#ifdef CO_EMULATION
    counter8*    u_counter8;
    sc_signal<sc_uint<8> >  Dout_emu;
    sc_signal<bool>         RCO_emu;
#endif

    // Convert Verilator's ports to DUT's ports
    void conv_method()
    {
        Din_n8.write((sc_uint<8>)Din_n32);
        Dout_n8.write((sc_uint<8>)Dout_n32);
    }

    void test_generator();

    sc_trace_file* fp;  // VCD file

    SC_CTOR(sc_counter8_TB) :   // Constructor
        CLK("CLK", 100, SC_NS, 0.5, 0.0, SC_NS, false)
    {
        // DUT Instantiation
        u_Vcounter8 = new Vcounter8("u_Vcounter8");
        // Binding
        u_Vcounter8->CLK(CLK);
        u_Vcounter8->nCLR_L(nCLR_L);
        u_Vcounter8->nCLR_H(nCLR_H);
        u_Vcounter8->nLOAD_L(nLOAD_L);
        u_Vcounter8->nLOAD_H(nLOAD_H);
        u_Vcounter8->ENP(ENP);
        u_Vcounter8->ENT(ENT);
        u_Vcounter8->Din(Din_n32);
        u_Vcounter8->Dout(Dout_n32);
        u_Vcounter8->RCO(RCO);

#ifdef CO_EMULATION

        u_counter8 = new counter8("u_counter8");
        u_counter8->CLK(CLK);
        u_counter8->nCLR_L(nCLR_L);
        u_counter8->nCLR_H(nCLR_H);
        u_counter8->nLOAD_L(nLOAD_L);
        u_counter8->nLOAD_H(nLOAD_H);
        u_counter8->ENP(ENP);
        u_counter8->ENT(ENT);
        u_counter8->Din(Din_n8);
        u_counter8->Dout(Dout_emu);
        u_counter8->RCO(RCO_emu);

#endif

        SC_THREAD(test_generator);
        sensitive << CLK;

        SC_METHOD(conv_method);
        sensitive << Din_n32 << Dout_n32;

        // VCD Trace
        fp = sc_create_vcd_trace_file("sc_counter8_TB");
        fp->set_time_unit(100, SC_PS);
        sc_trace(fp, CLK, "CLK");
        sc_trace(fp, nCLR_L, "nCLR_L");
        sc_trace(fp, nCLR_H, "nCLR_H");
        sc_trace(fp, nLOAD_L, "nLOAD_L");
        sc_trace(fp, nLOAD_H, "nLOAD_H");
        sc_trace(fp, ENP, "ENP");
        sc_trace(fp, ENT, "ENT");
        sc_trace(fp, Din_n8, "Din");
        sc_trace(fp, Dout_n8, "Dout");
        sc_trace(fp, RCO, "RCO");

#ifdef CO_EMULATION
        sc_trace(fp, Dout_emu, "Dout_emu");
        sc_trace(fp, RCO_emu, "RCO_emu");
#endif
    }

    // Destructor
    ~sc_counter8_TB() {}
};

#endif // _SC_CPU_6502_TOP_H_

RTL 시뮬레이션을 위해 Verilog 설계를 SystemC로 변환해주는 Verilator 도구를 사용했다. 베릴로그 상위 모듈 counter8 을 SystemC로 변환된 모듈명이 Vcounter8 이다. 이어 USB-UART 를 통해 아두이노 보드와 통신하는 에뮬레이션 모듈은 counter8 이다.

//
// Emulation Interface to "Counter8"/Cycle Accurate
// goodkook@gmail.com
//

#ifndef _COUNTER8_H_
#define _COUNTER8_H_

#include "systemc"

// Includes for accessing Arduino via serial port
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>

SC_MODULE(counter8)
{
    // PORTS
    sc_in<bool>         CLK;
    sc_in<bool>         nCLR_L;
    sc_in<bool>         nCLR_H;
    sc_in<bool>         nLOAD_L;
    sc_in<bool>         nLOAD_H;
    sc_in<bool>         ENP;
    sc_in<bool>         ENT;
    sc_in<sc_uint<8> >  Din;
    sc_out<sc_uint<8> > Dout;
    sc_out<bool>        RCO;

    // Arduino Serial IF
    int fd;                 // Serial port file descriptor
    struct termios options; // Serial port setting

#define N_TX    2
#define N_RX    2

    void counter8_thread(void)
    {
        uint8_t     x, y, txPacket[N_TX], rxPacket[N_RX];
        while(true)
        {
            wait(CLK.posedge_event());
            txPacket[0] = (uint8_t)(CLK.read()?     0x40:0x00) |
                          (uint8_t)(nCLR_L.read()?  0x20:0x00) |
                          (uint8_t)(nCLR_H.read()?  0x10:0x00) |
                          (uint8_t)(nLOAD_L.read()? 0x08:0x00) |
                          (uint8_t)(nLOAD_H.read()? 0x04:0x00) |
                          (uint8_t)(ENP.read()?     0x02:0x00) |
                          (uint8_t)(ENT.read()?     0x01:0x00);
            txPacket[1] = (uint8_t)((sc_uint<8>)(Din.read()));

            // Send to Emulator
            for (int i=0; i<N_TX; i++)
            {
                x = txPacket[i];
                while(write(fd, &x, 1)<=0)  usleep(1);
            }

            // Receive from Emulator
            for (int i=0; i<N_RX; i++)
            {
                while(read(fd, &y, 1)<=0)   usleep(1);
                rxPacket[i] = y;
            }

            Dout.write(rxPacket[0]);
            RCO.write((rxPacket[1]&0x01)? true:false);
        }
    }

    SC_CTOR(counter8) :   // Constructor
        CLK("CLK")
    {
        SC_THREAD(counter8_thread);
        sensitive << CLK;

        // Arduino DUT
        fd = open("/dev/ttyACM0", O_RDWR | O_NOCTTY);
        if (fd < 0)
        {
            perror("Error opening serial port");
            return;
        }

        // Set up serial port
        options.c_cflag = B9600 | CS8 | CLOCAL | CREAD;
        options.c_iflag = IGNPAR;
        options.c_oflag = 0;
        options.c_lflag = 0;
        // Apply the settings
        tcflush(fd, TCIFLUSH);
        tcsetattr(fd, TCSANOW, &options);
        // Establish Contact
        int len = 0;
        char rx;
        while(!len)
            len = read(fd, &rx, 1);
        if (rx=='A')
            write(fd, &rx, 1);
        printf("Connection established...\n");
    }
};
#endif

줄 수는 많은듯 해도 내용은 별것 없다. 실제로 2진 카운터의 동작은 없으며 아두이노 보드와 직렬 통신을 열고 입출력 신호를 주고 받는 역활만 한다.

4. 아듀이노: 칩과 테스트벤치 인터페이스

아두이노 보드는 칩과 테스트 벤치 사이의 가교역활을 한다. 단순한 통신 중계를 수행한다. 먼저, 칩의 핀에 연결할 아두이노 보드의 디지털 핀 을 정의하고,

/*  counter8 with SystemC Co-Emulation */

#define PIN_CLK     22
#define PIN_nCLR_L  23
#define PIN_nCLR_H  24
#define PIN_nLOAD_L 25
#define PIN_nLOAD_H 26
#define PIN_ENP     27
#define PIN_ENT     28
#define PIN_Din_0   29
#define PIN_Din_1   30
#define PIN_Din_2   31
#define PIN_Din_3   32
#define PIN_Din_4   33
#define PIN_Din_5   34
#define PIN_Din_6   35
#define PIN_Din_7   36
#define PIN_Dout_0  37
#define PIN_Dout_1  38
#define PIN_Dout_2  39
#define PIN_Dout_3  40
#define PIN_Dout_4  41
#define PIN_Dout_5  42
#define PIN_Dout_6  43
#define PIN_Dout_7  44
#define PIN_RCO     45

아듀이노 보드와 PC 사이의 보오 레이트 지정 뿐인 UART 설정한다. 수많은 사용자들에 의해 직렬 통신에 관한 모든 준비가 이미 되어 있어 사용자로써 감사할 일이다. 이어 칩에 연결할 핀들의 입출력 방향 지정,

void setup()
{
  // start serial port at 9600 bps:
  Serial.begin(9600);
  while (!Serial)  {}

  establishContact();

  // Set digital pins to output connecting DUT's INPUT
  pinMode(PIN_CLK     , OUTPUT);  digitalWrite(PIN_CLK     , LOW);
  pinMode(PIN_nCLR_L  , OUTPUT);  digitalWrite(PIN_nCLR_L  , LOW);
  pinMode(PIN_nCLR_H  , OUTPUT);  digitalWrite(PIN_nCLR_H  , LOW);
  pinMode(PIN_nLOAD_L , OUTPUT);  digitalWrite(PIN_nLOAD_L , LOW);
  pinMode(PIN_nLOAD_H , OUTPUT);  digitalWrite(PIN_nLOAD_H , LOW);
  pinMode(PIN_ENP     , OUTPUT);  digitalWrite(PIN_ENP     , LOW);
  pinMode(PIN_ENT     , OUTPUT);  digitalWrite(PIN_ENT     , LOW);
  pinMode(PIN_Din_0   , OUTPUT);  digitalWrite(PIN_Din_0   , LOW);
    ......
  pinMode(PIN_Din_6   , OUTPUT);  digitalWrite(PIN_Din_6   , LOW);
  pinMode(PIN_Din_7   , OUTPUT);  digitalWrite(PIN_Din_7   , LOW);

  // Set digital pins to input connecting DUT's OUTPUT
  pinMode(PIN_Dout_0  , INPUT);
    ........
  pinMode(PIN_Dout_6  , INPUT);
  pinMode(PIN_Dout_7  , INPUT);
  pinMode(PIN_RCO     , INPUT);

  // Monitoring LED
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
}

테스트 벤치와 주고 받을 데이터들을 비트 단위로 분할(시신된 입력 벡터) 및 통합(송신할 출력 벡터)한다. 칩과는 비트 단위로 연결되어있지만 UART를 통해 한번에 전송되는 데이터 단위는 8비트다.

#define N_RX  2 // [0]={-|CLK|nCLR_L|nCLR_H|nLOAD_L|nLOAD_H|ENP|ENT}
                // [1]=Din;
#define N_TX  2 // [0]=Dout
                // [1]={-------|RCO}

uint8_t rxBuf[N_RX], txBuf[N_TX];

void RxPacket()
{
  int rxByte;
  while(true)
  {
    if (Serial.available() >= N_RX)
    {
      for(int i=0; i<N_RX; i++)
      {
        rxByte = Serial.read();
        rxBuf[i] = (uint8_t)rxByte;
      }
      //rxBuf[0]={-|CLK|nCLR_L|nCLR_H|nLOAD_L|nLOAD_H|ENP|ENT}
      digitalWrite(PIN_nCLR_L,  rxBuf[0] & 0x20);
      digitalWrite(PIN_nCLR_H,  rxBuf[0] & 0x10);
      digitalWrite(PIN_nLOAD_L, rxBuf[0] & 0x08);
      digitalWrite(PIN_nLOAD_H, rxBuf[0] & 0x04);
      digitalWrite(PIN_ENP,     rxBuf[0] & 0x02);
      digitalWrite(PIN_ENT,     rxBuf[0] & 0x01);
      // rxBuf[1] = Din
      digitalWrite(PIN_Din_0,   rxBuf[1] & 0x01);
        ........
      digitalWrite(PIN_Din_7,   rxBuf[1] & 0x80);

      // Clocking to DUT
      digitalWrite(PIN_CLK, HIGH);
      digitalWrite(PIN_CLK, LOW);

      return;
    }
  }
}

void TxPacket()
{
  int txByte;
  while(1)
  {
    if (Serial.availableForWrite() >= N_TX)
    {
      txBuf[0] = ((digitalRead(PIN_Dout_0)? 0x01:0x00) |
                  (digitalRead(PIN_Dout_1)? 0x02:0x00) |

                    .........

                  (digitalRead(PIN_Dout_7)? 0x80:0x00));  // Dout
      txBuf[1] = ((digitalRead(PIN_RCO)? 0x01:0x00)); // RCO

      for(int i=0; i<N_TX; i++)
      {
        txByte = (int)txBuf[i];
        Serial.write(txByte);
      }

      return;
    }
  }
}

PC쪽의 테스트벤치에서 테스트 벡터를 줄때마다 무한 반복한다. 무슨일이 벌어지는지 궁금 하니 가끔씩 LED를 깜박여 줘도 좋다.

uint8_t counter;

void loop()
{
  counter += 1;
  digitalWrite(LED_BUILTIN, (counter & 0x10)? HIGH:LOW);

  RxPacket();
  TxPacket();
}

5. 칩 테스트 실행

"내칩 MPW"를 통해 제작된 칩은 아래 사진의 오른쪽 처럼 28핀 SOP 패키지로 제공된다. 사진의 칩은 예제와 다른 칩이다. 예제에서 보인 8비트 카운터는 LS163 표준 LSI 2개와 동일하게 구현한 것이다. 실제 칩은 없지만 표준 LSI 칩을 이용하여 시험할 대상의 칩으로 간주하자. 브레드 보드에 회로를 꾸민 후 아두이노 보드에 연결 했다.

테스트 구성은 아래와 같다.

                                    +------------------------+
                                    |           SystemC TB   |
                                    |             in C++     |
                                    |           +---------+  |
                                    +-----+     |  Test   |  |
                                          |  +---Generator|  |
                              +-------+   |  |  |         |  |
                              |  DUT  |   |  |  +---------+  |
                              |   in  <------+               |
                              |Verilog|   |  |  +---------+  |
                              |       -------|-->[DUT_Out]|  |
                              |       |   |  |  |         |  |
                              +-------+   |  |  |         |  |
                                          |  |  |         |  |
+--------+  +---------+       +.......+   |  |  | Compare |  |
|  DUT   |  | Arduino |       :       :   |  |  |         |  |
|   in   +---> MEGA   |       : UART  <------+  |         |  |
| MyChip |  |         <=[USB]=> Driver:   |     |         |  |
|  MPW   <----        |       : S/W ............>[Emu_Out]|  |
|        |  |         |       :       :   |     +---------+  |
+--------+  +---------+       +.......+   |                  |
                                    +-----+                  |
                                    |                        |
                                    +------ -----------------+
        Chip Test by Co-Emulation

테스트 칩은 전원을 USB 포트로부터 공급받는다. 전원 소모를 확인하기 위해 전류 측정장치를 사용했다. 아울러 오실로 스코프로 클럭 신호를 모니터링 한다.

 

깃 허브에서 내려받은 예제를 실행한다. 메이크 스크립트가 준비되어 있으니 이를 실행 한다.

    % cd emulation

    % make -fcounter8.mak run

칩에 공급되는 클럭은 오실로 스코프 상으로 확인할 수 있다. 약 50Khz 의 속도로 측정됐다. 이는 모든 테스트 입력과 클럭 신호를 PC의 SystemC에서 주기 때문이다. 이를 싸이클 상세(Cycle Accurate) 데스트라 한다. 동작 속도를 포함한 물리적 특성을 시험하기 보다 동작을 검사하기 위한 목적이다. 테스트 벤치는 베릴로그 모델과 에뮬레이션 칩의 입출력을 모두 VCD로 저장한다.

베릴로그 모델의 출력 Dout[7:0]과 칩의 에뮬레이션 출력 Dout_emu[7:0]이 일치하는 것을 알 수 있다. 하지만 전체 256회의 클럭을 카운트 한후 RCO가 칩과 베릴로그 모델이 상이하다.

베릴로그 모델에 이상이 있음이 관찰 되었다. 베릴로그 모델의 LS163 모듈에서 RCO가 잘못 됐다.

    assign RCO = cnt[3] & cnt[2] & cnt[1] & cnt[0];

    assign RCO = cnt[3] & cnt[2] & cnt[1] & cnt[0] & ENT;

로 수정되어야 한다. 2개의 74163비트 카운터를 구성 했을 경우 문제 없으나 4개를 연결한 16비트 카운터에서는 문제를 일으킨다.

대규모 입출력 검사를 파형으로 관찰하는 것은 불가능하다. 위의 단순한 예만 보더라도 무려 65526개의 클럭이 주어진 후에야 비로서 RCO의 이상 유무를 관할 할 수 있다. 게다가 계수하는 중간에 일어날 오류를 파형으로 관찰하는 것은 무모한 짓이다. 테스트 벤치에서 표준 값과 테스트 출력을 자동 비교하는 루틴을 만들어 놓도록 하자. 

6. 결론

디지털 칩의 테스트는 다수의 입출력 신호선과 응용 별로 상이한 테스트 벡터를 사용하기 때문에 전문 장비를 요구한다. 이를 극복하는 방법으로 RTL 검증에 사용했던 SystemC 테스트 벤치와 아두이노 보드를 활용하여 저렴하게 테스트 하는 방법을 소개했다. 싸이클 상세로 시험 하기 때문에 동작 속도는 느리지만 칩의 기본동작을 검증하는데 충분하다.

[참고]

[1] SCE-MI(Standard Co-Emulation Modeling Interface), https://www.accellera.org/downloads/standards/sce-mi

[2] CPU 6502 emulation, https://github.com/GoodKook/ETRI-0.5um-CMOS-MPW-DK-Example--6502-CPU





2024년 5월 24일 금요일

오픈-소스 도구를 활용한 ETRI 0.5u CMOS 표준 셀 디자인 킷의 제작과 활용

오픈-소스 도구를 활용한 ETRI 0.5u CMOS 표준 셀 디자인 킷의 제작과 활용

2024년 전자공학회 하계 종합 학술대회에서 위의 제목으로 강연 합니다. 이번 제주도에서 열리는 학술대회의  "MPW/마이칩 사업단"의 특별 세션에 오셔서 많은 의견을 나눌 수 있길 고대합니다. - 국일호, 경희대학교 전자공학과

강연 요약

"내칩 MPW 제작 서비스"는 반도체 설계실무자 인재양성 지원을 위해 반도체를 제작 해주는 사업이다. 특히 학부생을 대상으로 세계 유일의 무료 서비스라 할 수 있다. 학부과정에서 고가의 설계 도구를 활용하기 어렵고, 제작을 마친 칩의 테스트도 만만치 않은 난제라 할 것이다. 다행히 최근 소프트웨어 분야에서 성공적인 성과를 이뤄낸 오픈-소스 운동에 반도체 설계도구 역시 활발하여 높은 완성도를 보여주고 있다. 이 도구들은 해외 유수의 교육용 MPW 사업에서 반도체 설계 방법론으로 자리잡았다[1][2]. 무료로 사용 할 수 있는 오픈-소스 반도체 설계 도구들은 시간과 장소에 구애받지 않으며 고가의 설계도구에 접근하기 어려운 한계를 극복할 수 있어 가히 ‘반도체 설계의 민주화’라고 일컬어진다[3].

본 강연에서는 표준 셀 기반의 디지털 반도체 설계를 중심으로 오픈-소스 반도체 설계도구의 활용 방법을 소개한다.

- 오픈-소스 반도체 설계 도구의 최근 경향
- 오픈 소스 반도체 설계 도구들, 베릴로그 RTL 합성기, 배치배선기, 레이아웃 도구의 소개
- 경희대학교 반도체 전공트랙에서 제작한 표준 셀 디자인 킷 및 디자인 플로우 소개[4]
- 표준 셀 제작과정과 오픈 소스 도구(합성기, 배치배선기, 레이아웃 도구)들과 연계 방법
- FPGA 보드를 활용한 에뮬레이션 기법으로 RTL 검증
- 오픈-소스 아두이노 보드 및 FPGA 활용 에뮬레이션 기법을 이용한 칩 테스트 전략
- 디자인 킷을 활용한 예[5]로 FIR 필터의 어레이 프로세서 구조 구현 및 “내칩 MPW”의 기준(설계 면적, 핀 수)에 맞추는 방안

풀-커스텀 방식의 “내칩 MPW”를 수행하려면 학부 과정에서 쌓아온 아날로그 및 디지털 전자회로, 컴퓨터 구조 등의 하드웨어 뿐만 아니라 베릴로그를 위시한 하드웨어 언어, C/C++, 파이썬, TCL, 쉘 스크립트까지 아우르는 컴퓨팅 언어, 신호처리 및 인공지능 알고리즘, 운영체제 등 모든 학습 역량들을 총동원해야 한다. MPW의 참여가 단지 레이아웃 도면의 생성과 칩의 제작에 국한되지 않고 알고리즘 개발, 설계 자동화 도구, 설계물의 검증 그리고 칩 테스트등 폭넓은 반도체 산업의 생태계 전반을 체험 학습하는 계기가 될 것이다.

ETRI-경희 대학교 디자인 킷[4]과 설계 플로우가 완성 단계에 있다. 이 설계 플로우에는 FPGA를 활용한 설계 검증과 칩이 출고되면 테스트까지 이어지도록 하였다. Verilog HDL-RTL과 SystemC/C++ 테스트벤치를 엮은 Co-Simulation, 이에 덧붙여 FPGA를 활용한 Co-Emulation 그리고 Testbench Re-Use 까지 교과서에서 읽어봄직한 설계및 검증 기법을 모두 포함한다. 지난 2023년 1차 MPW를 통해 제작한 칩이 작동 실패라는 고배를 마셨으나 원인이 파악된 만큼 조만간 실리콘 검증까지 완성 할 수 있을 것으로 예상한다.

RTL 설계, 시뮬레이션, 합성, FPGA 구현 검증, ASIC 레이아웃 생성, 칩 테스트까지 반도체 설계 전과정을 오픈 소스 툴 사용하므로 비용은 0원. 게다가 ETRI 0.5um CMOS 공정 칩 제작과 패키지까지 무료다. 다만 에뮬레이션 검증을 위해 하드웨어를 구입해야 해서 다소 비용이든다. 현재 Arduino DUE와 Digilent Arty A7(Xilinx FPGA)의 구입에 소요될 비용은 약 50만원선이다. 주문해 놓은 중국의 GoWin FPGA가 아직 도착 전이라 테스트 해보지 못했지만 잘되면 총비용 10만원대로 가능 할듯하다. 학생 개인이 충당할수 있는 수준이다.

에뮬레이션 검증의 예제로 6502 CPU/Apple-1를 아래와 같이 재현해 보았다.

1. Verilog RTL 단계: Verilator, Questa*Sim 그리고 SystemC TB의 Co-Simulation

2. FPGA 검증 단계: Yosys 합성기로 Xilinx FPGA용 네트리스트 생성, Vivado이용 P&R, SystemC TB 재활용 Co-Emulation. 동작속도 약 ~1khz

3. FPGA 에뮬레이션: Yosys 합성기, Vivado P&R, Arduino DUE Stand-Alone. 동작속도 약 20Khz

4. Digital Std-Cell DK 사용하여 생성한 CPU 6502의 레이아웃은 ETRI 0.5um CMOS 공정 진행중

베릴로그 CPU 6502의 실행. 응용 소프트웨어는 AppleLite BASIC이다. 흰 바탕의 화면은 Verilator를 사용 SystemC로 변환하여 실행 시킨 화면. 검은 바탕의 화면은 FPGA에 구현된 6502 CPU를 Arduino DUE로 에뮬레이션 시킨 장면이다. 6502 CPU의 입출력 주변장치와 메모리 그리고 클럭은 아듀이노 보드에서 제어한다. 클럭의 속도는 약 20Khz에 불과한데 이는 아두이노의 마이크로 컨트롤러를 에뮬레이션 호스트로 사용하기 때문이다. 에뮬레이터 실행은 RTL 시뮬레이터보다 빠르지만 Verilator에 이르지 못한다. 에뮬레이션이 비록 느리긴 하지만 하드웨어로 구현된 설계의 검증 수단으로써 의미가 있다.


디자인 킷과 예제는 모두 깃허브를 통해 공개되어 있다[6].

참고

[1] eFabless,  https://efabless.com/
[2] The Open Road Project, https://theopenroadproject.org/
[3] OpenROAD: Toward a Self-Driving, Open-Source Digital Layout Implementation Tool Chain, https://vlsicad.ucsd.edu/Publications/Conferences/371/c371.pdf
[4] ETRI 0.5um CMOS MPW Std-Cell DK, https://github.com/GoodKook/ETRI-0.5um-CMOS-MPW-Std-Cell-DK
[5] ETRI 0.5um CMOS MPW DK Example: FIR8, https://github.com/GoodKook/ETRI-0.5u-CMOS-MPW-DK-Example--FIR8
[6] ETRI 0.5um CMOS MPW DK Example: CPU_6502, https://github.com/GoodKook/ETRI-0.5um-CMOS-MPW-DK-Example--6502-CPU


 






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 알고리즘 및 병렬처리 구조 탐색
[다음] [3] 칩 테스트 장치