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