레이블이 FIR8인 게시물을 표시합니다. 모든 게시물 표시
레이블이 FIR8인 게시물을 표시합니다. 모든 게시물 표시

2025년 2월 4일 화요일

ETRI 0.5um CMOS DK 예제: FIR8 / [4] PC 사운드 카드 오실로스코프

ETRI 0.5um CMOS DK 예제: FIR8 / [4] PC 사운드 카드 오실로스코프

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

[주의] PC 사운드 카드를 ADC/DAC로 사용하는 것은 위험!!!

아무리 조심 한다고 해도 PC 내장 사운드 칩을 한순간에 태워먹을 수 있습니다. 이를 방지하려고 별도 외장 USB 사운드 장치를 찾아봤으니 스테레오 라인 입력을 지원하는 장치는 흔치도 않고 가격이 만만치 않습니다. (실험하다가 USB 장치 이미 두개 태워 먹음!)

라즈베리 파이 피코를 활용하여 샘플링 하고 스마트 폰에서 디스플레이 할 수 있는 앱 scoppy 를 시험 해보니 쓸만 합니다. 라즈베리 파이 피코 와이파이 버젼으로 연결 할 수도 있어서 매우 안전 합니다. 적어도 스마트 폰 태워 먹을 일은 없을 듯! 게다가 PhoneLink 로 PC에서 스마트 폰 화면을 끌어올 수 있으니 편리 합니다.

알리에서 라즈베리 파이 피코 2천원, 와이파이 버젼 7천원에 팔고 있길래 주문해 뒀으니 작동 시킬 수 있는지 확인해 보고 알려 드리겠습니다.

PC 사운드 카드를 ADC/DAC로 사용하는 것은 위험하니 행여 따라하려고 했다면 주의하세요.

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

오실로스코프는 멀티미터와 함께 실험실 필수품이다. 멀티미터는 3천원이면 구비할 수 있지만 오실로스코프는 제아무리 알리상회라 해도 십여만원은 줘야 한다. 주머니 사정이 여의치 않다면 아쉬운 대로 만들어 보기로 한다.

샘플링 레이트가 고작 48Khz에 불과 하지만 PC의 사운드 카드는 훌륭한 ADC/DAC 장치다.  수십만원 짜리 오실로스코프의 ADC 해상도가 8/16비트인 점에 비하면 PC 사운드 카드는 16/24비트다. 다만 라인 입력 입력 신호의 레벨이 낮아서(1Vpp) 잘못하면 컴퓨터를 태워 먹을 수도 있으니 버퍼를 달아 주도록 하자.

AC 신호를 처리하기 위해 +/- 양 전원이 필요하다.

사운드카드 입력 버퍼를 만들면서 Op Amp의 반전 단일 증폭기(Gain=1)도 알아보자. 다이오드는 전압강하 용이다. PC 사운드 카드 입력 레벨을 감안하여 약 +/-0.65볼트로 떨어 트린다. 직렬 저항들은 전류 제한 용이다.

+/- 양 전원장치가 없다면 간단히 만들어 볼 수 있다. 저항 두개로 분압기(voltage divider)를 만들 수 있다. 단일 이득(gain=1)을 갖는 Op Amp를 사용하 전원공급(9Vin)의 접지와 회로에 들어갈 +/- 4.5V의 GND를 분리해 주었다.

위 회로의 출처는 아래와 같다. 세상은 넓고 아이디어가 넘친다.

[출처] PC Soundcard Oscilloscope buffer/attenuator. Especially for Visual Analyser.
https://www.youtube.com/watch?v=uTDFYQrQMLc

PC 사운드 카드 오실로스코프를 가지고 내 칩 MPW 를 통해 만든 FIR_PE 칩을 테스트 해봤다. 입력 버퍼를 빵판에 꾸며서 그런지 샘플 신호가 기울고 있지만 디지털 칩의 동작을 확인 하는데 충분하다. 아래 움직이는 그림은 Vld(칩의 출력)와 Rdy(칩의 입력)를 관찰한 것이다.  입력 Rdy가 5 클럭 후 칩의 출력 Vld로 나오는 것을 확인 하였다.


PC Scope 소프트웨어는 Room Acoustics Software-REW다. 사운드 카드를 이용한 음향 시설 분석용인데 무료다.

https://www.roomeqwizard.com/

"내 칩 MPW"로 만든 칩이 동작한 탓에 해볼 것 들이 한두가지가 아니다.

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

[이전] [3] 칩 테스트 장치









2025년 1월 27일 월요일

ETRI 0.5um CMOS DK 예제: FIR8 / [3] 칩 테스트 장치

ETRI 0.5um CMOS DK 예제: FIR8 / [3] 칩 테스트 장치

"내 칩 서비스" MPW 2024-2차 칩이 28핀 SSOP 패키지로 배포 되었다. 디지털 회로의 경우 비록 28핀 밖에 않되는 칩이지만 테스트 하려면 테스트 벡터 제네레이터, 로직 아날라이져 같은 장비를 필요로 한다. 여러 클럭에 걸쳐 일련의 테스트 벡터를 주어 기능을 확인해야 하므로 입출력 신호 검사 장비는 프로그래머블 해야함은 물론이다. 비록 이런 장비를 갖췄더라도 장비 사용법은 쉽지 않다. 고가의 테스트 장비에 복잡한 사용법으로 인해 테스트 할 엄두가 나지 않는다면 직접 만들어 보자.

칩 테스트 장치는 기본적으로 SCE-MI 에 준하여 구성되었다.


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

SystemC 로 작성한 테스트 벤치를 통해 테스트 벡터 생성하고 출력을 검출한다. 동작 모드는 클럭 상세(Cycle Accurate)다. 아듀이노 메가보드를 활용하여 DUT(하드웨어)와 SystemC 테스트 벤치(소프트웨어) 사이의 모델 인터페이스 한다.

테스트 대상 칩은 아래와 같다.

설계명: FIR_PE/DB-19

설계자: 지하은/경희대학교

설계내용: 어레이 FIR 필터 용 프로세싱 엘리먼트(PE)

입출력 핀 수: 입력 17개, 출력: 8개

패키지 핀맵:

 

테스트 장치 제작

준비물

Arduino 2560 MEGA R.3

 

Proto Shield

 

Arcryl Housing/Universal Board

 

Wires & Pin Header

 

Test Clips

 

SOP-28 (300mil) Test Socket

Oscilloscope & Multi-Tester

 




Arduino MEGA/Modeling Interface

회로도: https://content.arduino.cc/assets/MEGA2560_Rev3e_sch.pdf

디지털 핀: https://docs.arduino.cc/resources/pinouts/A000067-full-pinout.pdf


/*
  Filenale: fir8_test.ino
  FIR8 with SystemC Co-Emulation
  Chip Test at Cycle Accurate Level
  MyChip MPW 2024-2
*/
//----------------------// SOIC-28 Package
#define PIN_Xin_3   22  // 1
#define PIN_Xin_2   23  // 2
#define PIN_Xin_1   24  // 3
#define PIN_Xin_0   25  // 4
//            VDD       // 5
#define PIN_Yin_3   26  // 6
#define PIN_Yin_2   27  // 7
#define PIN_Yin_1   28  // 8
#define PIN_Yin_0   29  // 9
#define PIN_RDY_i   30  // 10
#define PIN_CLK_i   31  // 11
#define PIN_Cin_0   32  // 12
#define PIN_Cin_1   33  // 13
#define PIN_Cin_2   34  // 14
#define PIN_Cin_3   35  // 15
#define PIN_Cin_4   36  // 16
#define PIN_Cin_5   37  // 17
#define PIN_Cin_6   38  // 18
#define PIN_VLD_o   39  // 19
#define PIN_Yout_0  40  // 20
#define PIN_Yout_1  41  // 21
#define PIN_Yout_2  42  // 22
#define PIN_Yout_3  43  // 23
//            VSS       // 24
#define PIN_Xout_0  44  // 25
#define PIN_Xout_1  45  // 26
#define PIN_Xout_2  46  // 27
#define PIN_Xout_3  47  // 28
//-------------------------------------------------------------------
class Port_Xin {
  uint8_t _val;
public:
  Port_Xin(uint8_t val)  // Constructor
  {
    // Set digital pins to output connecting DUT's INPUT
    pinMode(PIN_Xin_3, OUTPUT);
    pinMode(PIN_Xin_2, OUTPUT);
    pinMode(PIN_Xin_1, OUTPUT);
    pinMode(PIN_Xin_0, OUTPUT);
    write(val);
  }
  uint8_t read()
  {
    return _val;
  }
  void write(uint8_t val)
  {
    digitalWrite(PIN_Xin_3, val & 0x08);
    digitalWrite(PIN_Xin_2, val & 0x04);
    digitalWrite(PIN_Xin_1, val & 0x02);
    digitalWrite(PIN_Xin_0, val & 0x01);
    _val = val;
  }
};
//-------------------------------------------------------------------
class Port_Yin {
  uint8_t _val;
public:
  Port_Yin(uint8_t val)
  {
    // Set digital pins to output connecting DUT's INPUT
    pinMode(PIN_Yin_3, OUTPUT);
    pinMode(PIN_Yin_2, OUTPUT);
    pinMode(PIN_Yin_1, OUTPUT);
    pinMode(PIN_Yin_0, OUTPUT);
    write(val);
  }
  uint8_t read()
  {
    return _val;
  }
  void write(uint8_t val)
  {
    digitalWrite(PIN_Yin_3, val & 0x08);
    digitalWrite(PIN_Yin_2, val & 0x04);
    digitalWrite(PIN_Yin_1, val & 0x02);
    digitalWrite(PIN_Yin_0, val & 0x01);
    _val = val;
  }
};
//-------------------------------------------------------------------
class Port_Cin {
  uint8_t _val;
public:
  Port_Cin(uint8_t val)
  {
    // Set digital pins to output connecting DUT's INPUT
    pinMode(PIN_Cin_6, OUTPUT);
    pinMode(PIN_Cin_5, OUTPUT);
    pinMode(PIN_Cin_4, OUTPUT);
    pinMode(PIN_Cin_3, OUTPUT);
    pinMode(PIN_Cin_2, OUTPUT);
    pinMode(PIN_Cin_1, OUTPUT);
    pinMode(PIN_Cin_0, OUTPUT);
    write(val);
  }
  uint8_t read()
  {
    return _val;
  }
  void write(uint8_t val)
  {
    digitalWrite(PIN_Cin_6, val & 0x40);
    digitalWrite(PIN_Cin_5, val & 0x20);
    digitalWrite(PIN_Cin_4, val & 0x10);
    digitalWrite(PIN_Cin_3, val & 0x08);
    digitalWrite(PIN_Cin_2, val & 0x04);
    digitalWrite(PIN_Cin_1, val & 0x02);
    digitalWrite(PIN_Cin_0, val & 0x01);
    _val = val;
  }
};
//-------------------------------------------------------------------
class Port_RDY_i {
  bool _val;
public:
  Port_RDY_i(bool val)
  {
    // Set digital pins to output connecting DUT's INPUT
    pinMode(PIN_RDY_i, OUTPUT);
    write(val);
  }
  bool read()
  {
    return _val;
  }
  void write(bool val)
  {
    digitalWrite(PIN_RDY_i, val);
    _val = val;
  }
};
//-------------------------------------------------------------------
class Port_Xout {
  uint8_t _val;
public:
  Port_Xout(uint8_t val)  // Constructor
  {
    // Set digital pins to input connecting DUT's OUTPUT
    pinMode(PIN_Xout_3, INPUT);
    pinMode(PIN_Xout_2, INPUT);
    pinMode(PIN_Xout_1, INPUT);
    pinMode(PIN_Xout_0, INPUT);
    write(val);
  }
  uint8_t read()
  {
    _val = ((digitalRead(PIN_Xout_3)? 0x08:0x00) |
            (digitalRead(PIN_Xout_2)? 0x04:0x00) |
            (digitalRead(PIN_Xout_1)? 0x02:0x00) |
            (digitalRead(PIN_Xout_0)? 0x01:0x00));
    return _val;
  }
  void write(uint8_t val)
  {
    _val = val;
  }
};
//-------------------------------------------------------------------
class Port_Yout {
  uint8_t _val;
public:
  Port_Yout(uint8_t val)  // Constructor
  {
    // Set digital pins to input connecting DUT's OUTPUT
    pinMode(PIN_Yout_3, INPUT);
    pinMode(PIN_Yout_2, INPUT);
    pinMode(PIN_Yout_1, INPUT);
    pinMode(PIN_Yout_0, INPUT);
    write(val);
  }
  uint8_t read()
  {
    _val = ((digitalRead(PIN_Yout_3)? 0x08:0x00) |
            (digitalRead(PIN_Yout_2)? 0x04:0x00) |
            (digitalRead(PIN_Yout_1)? 0x02:0x00) |
            (digitalRead(PIN_Yout_0)? 0x01:0x00));
    return _val;
  }
  void write(uint8_t val)
  {
    _val = val;
  }
};
//-------------------------------------------------------------------
class Port_VLD_o {
  bool _val;
public:
  Port_VLD_o(bool val)  // Constructor
  {
    // Set digital pins to input connecting DUT's OUTPUT
    pinMode(PIN_VLD_o, INPUT);
    write(val);
  }
  bool read()
  {
    _val = digitalRead(PIN_VLD_o);
    return _val;
  }
  void write(uint8_t val)
  {
    _val = val;
  }
};
//-------------------------------------------------------------------
class Port_CLK {
  bool _val;
public:
  Port_CLK(bool val)
  {
    pinMode(PIN_CLK_i, OUTPUT);
    write(val);
  }
  void write(bool val)
  {
    digitalWrite(PIN_CLK_i, val);
    _val = val;
  }
  bool read()
  {
    return _val;
  }
  void posedge()
  {
    digitalWrite(PIN_CLK_i, false);
    digitalWrite(PIN_CLK_i, true);
    _val = true;
  }
  void negedge()
  {
    digitalWrite(PIN_CLK_i, true);
    digitalWrite(PIN_CLK_i, false);
    _val = false;
  }
  bool cycle()
  {
    if (_val)
    {
      negedge();
      posedge();
    }
    else
    {
      posedge();
      negedge();
    }
    return _val;
  }
};
//-------------------------------------------------------------------
void establishContact()
{
  while (Serial.available() <= 0)
  {
    Serial.print('A');  // send a capital A
    delay(300);
    if (Serial.read()==(int)'A')
      break;
  }
}
//-------------------------------------------------------------------
void setup()
{
  // start serial port at 9600 bps:
  Serial.begin(9600);
  while (!Serial)
  {
    ;  // wait for serial port to connect.
  }
  establishContact();  // send a byte to establish contact until receiver responds
  // Monitoring LED
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
}
#define N_RX  3 // [0]={Xin[7:4]|Yin[3:0]}
                // [1]={-|Cin[6:0]};
                // [2]={-[7:2]|Clk|RDY};
#define N_TX  2 // [0]={Xout[7:4]|Yout[3:0]}
                // [1]={-[7:1]|VLD[0]}
uint8_t rxBuf[N_RX], txBuf[N_TX];
// Instantiate DUT Ports
Port_Xin    Xin(0x00);
Port_Yin    Yin(0x00);
Port_Cin    Cin(0x00);
Port_Xout   Xout(0);
Port_Yout   Yout(0);
Port_RDY_i  RDY_i(false);
Port_VLD_o  VLD_o(false);
Port_CLK    CLK(false);
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]={Xin[7:4]|Yin[3:0]}
      Xin.write((rxBuf[0] & 0xF0) >> 4);
      Yin.write((rxBuf[0] & 0x0F));
      //rxBuf[1]={-|Cin[6:0]};
      Cin.write((rxBuf[1] & 0x7F));
      //rxBuf[2]={-[7:2]|Clk|RDY};
      RDY_i.write((rxBuf[2] & 0x01));
      CLK.write((rxBuf[2] & 0x02));
      return;
    }
  }
}
void TxPacket()
{
  int txByte;
  while(1)
  {
    if (Serial.availableForWrite() >= N_TX)
    {
      // [0]={Xout[7:4]|Yout[3:0]}
      txBuf[0] = (((uint8_t)Xout.read() << 4) | ((uint8_t)Yout.read() & 0x0F));
      // [1]={-[7:1]|VLD[0]}
      txBuf[1] = ((uint8_t)VLD_o.read() & 0x01);
      for(int i=0; i<N_TX; i++)
      {
        txByte = (int)txBuf[i];
        Serial.write(txByte);
      }
      return;
    }
  }
}
uint8_t counter;
void loop()
{
  counter += 1;
  digitalWrite(LED_BUILTIN, (counter & 0x10)? HIGH:LOW);
  RxPacket();
  TxPacket();
}

SystemC Testbench

1. DUT Wrapper


/**********************************************************************
Filename: sc_fir_pe.h
Purpose : Test wrapper
          Chip Test of FIR PE (MyChip 2024-2)
Author  : goodkook@gmail.com
History : Jan. 2025, First release
***********************************************************************/

#ifndef _SC_FIR_PE_H_
#define _SC_FIR_PE_H_

#include <systemc.h>

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

SC_MODULE(sc_fir_pe)
{
    sc_in<bool>             clk;
    sc_in<bool>             Rdy;
    sc_out<bool>            Vld;
    sc_in<sc_uint<8> >      Cin;
    sc_in<sc_uint<4> >      Xin;
    sc_out<sc_uint<4> >     Xout;
    sc_in<sc_uint<4> >      Yin;
    sc_out<sc_uint<4> >     Yout;

#define N_TX    3
#define N_RX    2

    void transact(void)
    {
        uint8_t     x, y, txPacket[N_TX], rxPacket[N_RX];

        txPacket[0] = ((uint8_t)Xin.read()<<4) | ((uint8_t)Yin.read() & 0x0F);
        txPacket[1] = ((uint8_t)(Cin.read()) & 0x7F);
        txPacket[2] = (((uint8_t)Rdy.read())? 0x01:0x00) | (((uint8_t)clk.read())? 0x02:0x00);

        // 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;
        }

        Xout.write((sc_uint<4>)(rxPacket[0]>>4));
        Yout.write((sc_uint<4>)(rxPacket[0] & 0x0F));
        Vld.write(rxPacket[1]? true:false);

#ifdef LA_FIFO
        x  = txPacket[2]  & 0x02;       // clk
        x |= txPacket[2]  & 0x01;       // Rdy
        x |= rxPacket[1]?   0x04:0x00;  // Vld
        x |= (rxPacket[0]  & 0x01)<<4;  // Yout0
        x |= (rxPacket[0]  & 0x02)<<4;  // Yout1
        x |= (rxPacket[0]  & 0x04)<<4;  // Yout2
        x |= (rxPacket[0]  & 0x08)<<4;  // Yout3
        if((nWrite = write(la_fifo, &x, 1)) < 1)
            fprintf(stderr,"la_fifo: write error\n");
        else
            fflush(0);
#endif
    }
    
    void pe_thread(void)
    {

        while(true)
        {
            wait(clk.posedge_event());
            transact();
            wait(clk.negedge_event());
            transact();
        }
    }

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

#ifdef LA_FIFO
int la_fifo, nWrite;
#endif

    SC_CTOR(sc_fir_pe):
        clk("clk"),
        Cin("Cin"), Xin("Xin"), Xout("Xout"),
        Yin("Yin"), Yout("Yout")
    {
        SC_THREAD(pe_thread);
        sensitive << clk;

        // Arduino DUT
        //fd = open("/dev/ttyACM0", O_RDWR | O_NDELAY | O_NOCTTY);
        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");

#ifdef LA_FIFO
        la_fifo = open("la_fifo", O_WRONLY);
        if(la_fifo<0)
            fprintf(stderr,"la_fifo: open error\n");
#endif
    }
    
    ~sc_fir_pe(void)
    {
    }
};

#endif



2. Testbench


/**********************************************************************
Filename: sc_fir_pe_tb.h
Purpose : Testbench
          Chip Test of FIR PE (MyChip 2024-2)
Author  : goodkook@gmail.com
History : Jan. 2025, First release
***********************************************************************/

#ifndef _SC_FIR_PE_TB_H_
#define  _SC_FIR_PE_TB_H_

#include <systemc.h>
#include "sc_fir_pe.h"

SC_MODULE(sc_fir_pe_tb)
{
    sc_clock                clk;
    sc_signal<sc_uint<8> >  Cin;
    sc_signal<sc_uint<4> >  Xin;
    sc_signal<sc_uint<4> >  Xout;
    sc_signal<sc_uint<4> >  Yin;
    sc_signal<sc_uint<4> >  Yout;
    sc_signal<bool>         Vld;
    sc_signal<bool>         Rdy;

    sc_fir_pe*                u_sc_fir_pe;

    uint16_t    Test_Cin[5] = {0, 0, 0, 0, 0};
    uint16_t    Test_Xin[5] = {0, 0, 0, 0, 0};
    uint16_t    Test_Yin[5] = {0, 0, 0, 0, 0};
    uint8_t     Test_Xout = 0;
    uint16_t    Test_Yout = 0;

    // Test utilities
    void Test_Gen()
    {
        Cin.write(0x00);
        Xin.write(0x00);
        Yin.write(0x00);
        Rdy.write(false);

        // INIT
        for (int i=0; i<10; i++)
            wait(clk.posedge_event());
        wait(clk.posedge_event());
        Rdy.write(true);
        wait(clk.posedge_event());
        Rdy.write(false);
        for (int i=0; i<10; i++)
            wait(clk.posedge_event());

        Cin.write(Test_Cin[0]);
        Xin.write(0x00);
        Yin.write(0x00);
        Rdy.write(false);

        while(true)
        {
            Cin.write(Test_Cin[0] & 0x007F);    // Cin: 7-Bits
            wait(clk.posedge_event());
            Rdy.write(true);
            wait(clk.posedge_event());
            Rdy.write(false);
            Xin.write(Test_Xin[0] & 0x000F);    // Xin: 4-Bits
            Yin.write(Test_Yin[0] & 0x000F);    // Yin: 4-Bits
            wait(clk.posedge_event());
            Xin.write(Test_Xin[0] >> 4);
            Yin.write((Test_Yin[0] >> 4) & 0x000F);     // Yin: 4-Bits
            wait(clk.posedge_event());
            Yin.write((Test_Yin[0] >> 8) & 0x000F);     // Yin: 4-Bits
            wait(clk.posedge_event());
            Yin.write((Test_Yin[0] >> 12) & 0x000F);    // Yin: 4-Bits
            wait(clk.posedge_event());
            //printf("Cin = %04d Xin = %04d Yin = %06d ---> ", Test_Cin[0], Test_Xin[0], Test_Yin[0]);

            Test_Xin[4] = Test_Xin[3];  Test_Xin[3] = Test_Xin[2];  Test_Xin[2] = Test_Xin[1];  Test_Xin[1] = Test_Xin[0];
            Test_Yin[4] = Test_Yin[3];  Test_Yin[3] = Test_Yin[2];  Test_Yin[2] = Test_Yin[1];  Test_Yin[1] = Test_Yin[0];
            Test_Cin[4] = Test_Cin[3];  Test_Cin[3] = Test_Cin[2];  Test_Cin[2] = Test_Cin[1];  Test_Cin[1] = Test_Cin[0];

#if defined(TEST_MULTIPLIER_C)
            Test_Cin[0]++;
            if (Test_Cin[0]>127)
            {
                Test_Cin[0] = 0;
                Test_Xin[0]++;
                if (Test_Xin[0]>255)
                    sc_stop();
            }
#elif defined(TEST_MULTIPLIER_X)
            Test_Xin[0]++;
            if (Test_Xin[0]>255)
            {
                Test_Xin[0] = 0;
                Test_Cin[0]++;
                if (Test_Cin[0]>127)
                    sc_stop();
            }
#elif defined(TEST_ADDER_X)
            Test_Cin[0] = 1;
            Test_Xin[0]++;
            if (Test_Xin[0]>255)
            {
                Test_Xin[0] = 0;
                Test_Yin[0]++;
                if (Test_Yin[0]>1000)
                    sc_stop();
            }
#elif defined(TEST_ADDER_Y)
            Test_Cin[0] = 1;
            Test_Yin[0]++;
            if (Test_Yin[0]>255)
            {
                Test_Yin[0] = 0;
                Test_Xin[0]++;
                if (Test_Xin[0]>255)
                    sc_stop();
            }
#elif defined(TEST_ADDER_R)
            Test_Cin[0] = 1;
            Test_Xin[0] = (uint16_t)rand() & 0x00FF;    // Xin: unsigned 8-bit
            Test_Yin[0] = (uint16_t)rand() & 0xFFFF;    // Yin: unsigned 16-bit
#else   // RAND (default)
            Test_Cin[0] = (uint16_t)rand() & 0x007F;    // Cin: unsigned 7-bit
            Test_Xin[0] = (uint16_t)rand() & 0x00FF;    // Xin: unsigned 8-bit
            Test_Yin[0] = (uint16_t)rand() & 0xFFFF;    // Yin: unsigned 16-bit
#endif            
        }
    }

    void Test_Mon()
    {
        uint16_t    Yout_Expected = 0;

        while(true)
        {
            wait(clk.posedge_event());
            if (Vld.read())
            {
                wait(clk.posedge_event());
                wait(clk.posedge_event());
                Test_Xout = (uint8_t)Xout.read();
                Test_Yout = (uint16_t)Yout.read();
                wait(clk.posedge_event());
                Test_Xout |= (uint8_t)Xout.read() << 4;
                Test_Yout |= (uint16_t)Yout.read() << 4;
                wait(clk.posedge_event());
                Test_Yout |= (uint16_t)Yout.read() << 8;
                wait(clk.posedge_event());
                Test_Yout |= (uint16_t)Yout.read() << 12;
                printf("[Cin=%03d] * [Xin=%03d] + [Yin=%05d] ---> ",
#if defined(TEST_ADDER_X) || defined(TEST_ADDER_Y) || defined(TEST_ADDER_R) || defined(RAND)
                        Test_Cin[3], Test_Xin[3], Test_Yin[3]);
                Yout_Expected = (Test_Cin[3]*Test_Xin[3]+Test_Yin[3]);
#else
                        Test_Cin[2], Test_Xin[2], Test_Yin[2]);
                Yout_Expected = (Test_Cin[2]*Test_Xin[2]+Test_Yin[2]);
#endif
                printf("Xout = %03d Yout = %05d", Test_Xout, Test_Yout);
                if (Test_Yout!=Yout_Expected)
                    printf(":ERROR, Expected Yout = %05d\n", Yout_Expected);
                else
                    printf("\n");
            }
        }
    }

#ifdef VCD_TRACE_YES
    sc_trace_file* fp;  // VCD file
#endif

    SC_CTOR(sc_fir_pe_tb):
        clk("clk", 100, SC_NS, 0.5, 0.0, SC_NS, false),
        Vld("Vld"),
        Rdy("Rdy"),
        Xin("Xin"),
        Xout("Xout"),
        Yin("Yin"),
        Yout("Yout")
    {
        SC_THREAD(Test_Gen);
        sensitive << clk;

        SC_THREAD(Test_Mon);
        sensitive << clk;
        
        // Instaltiate FIR8
        u_sc_fir_pe = new sc_fir_pe("u_sc_fir_pe");
        u_sc_fir_pe->clk(clk);
        u_sc_fir_pe->Cin(Cin);
        u_sc_fir_pe->Xin(Xin);
        u_sc_fir_pe->Xout(Xout);
        u_sc_fir_pe->Yin(Yin);
        u_sc_fir_pe->Yout(Yout);
        u_sc_fir_pe->Rdy(Rdy);
        u_sc_fir_pe->Vld(Vld);

#if VCD_TRACE_YES
        // WAVE
        fp = sc_create_vcd_trace_file("sc_fir_pe_tb");
        sc_trace(fp, clk, "clk");
        sc_trace(fp, Cin,  "Cin");
        sc_trace(fp, Xin,  "Xin");
        sc_trace(fp, Xout, "Xout");
        sc_trace(fp, Yin,  "Yin");
        sc_trace(fp, Yout, "Yout");
        sc_trace(fp, Rdy,  "Rdy");
        sc_trace(fp, Vld,  "Vld");
#endif
    }
    
    ~sc_fir_pe_tb(void)
    {
    }
};

#endif


3. Main

/********************************************************************
Filename: sc_main.cpp
Purpose : Chip Test of FIR PE (MyChip 2024-2)
Author  : goodkook@gmail.com
History : Jan. 2025, First release
********************************************************************/
#include "sc_fir_pe_tb.h"
int sc_main(int argc, char** argv)
{
    sc_fir_pe_tb u_sc_fir_pe_tb("u_sc_fir_pe_tb");
    //sc_start(990, SC_US);
    sc_start();
    return 0;
}


4. Python Logic Analyzer



#
# Real-Time Logic Analyzer
#
#
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
HEIGHT = 3
def generate_wave(frame, tick, data,
                    Signal, Mask, PosY, line_Signal) -> None:

    for i in np.arange(0, 80, 2):
        if (not(data[int(i/2)] & Mask)) and (not(data[int(i/2)+1] & Mask)):# Zero
            Signal[i]   = PosY
            Signal[i+1] = PosY
        elif (not(data[int(i/2)] & Mask)) and (data[int(i/2)+1] & Mask):# Rising edge
            Signal[i]   = PosY
            Signal[i+1] = PosY+HEIGHT
        elif (data[int(i/2)] & Mask) and (not(data[int(i/2)+1] & Mask)):# Falling edge
            Signal[i]   = PosY+HEIGHT
            Signal[i+1] = PosY
        else:                                                           # One
            Signal[i]   = PosY+HEIGHT
            Signal[i+1] = PosY+HEIGHT

    Signal[80] = Signal[79]

    line_Signal.set_xdata(tick[:frame])
    line_Signal.set_ydata(Signal[:frame])
#--------------------------------------------------------------------
def update(frame, fp_fifo, data, tick,
            line_Clk, line_Rdy, line_Vld,
            line_Yout0, line_Yout1, line_Yout2, line_Yout3,
            Clk, Rdy, Vld, Yout0, Yout1, Yout2, Yout3) -> None:

    for i in range(0, 40):
        data[i] = data[i+1]

    data[40] = int.from_bytes(fp_fifo.read(1), "big")

    generate_wave(frame, tick, data, Yout0, 0x10,  0, line_Yout0)
    generate_wave(frame, tick, data, Yout1, 0x20,  5, line_Yout1)
    generate_wave(frame, tick, data, Yout2, 0x40, 10, line_Yout2)
    generate_wave(frame, tick, data, Yout3, 0x80, 15, line_Yout3)
    generate_wave(frame, tick, data,   Vld, 0x01, 20, line_Vld)
    generate_wave(frame, tick, data,   Rdy, 0x04, 25, line_Rdy)
    generate_wave(frame, tick, data,   Clk, 0x02, 30, line_Clk)
#--------------------------------------------------------------------
def run():
    data  = [0] * 41
    tick  = [0] * 81
    Clk   = [0] * 81
    Rdy   = [0] * 81
    Vld   = [0] * 81
    Yout0 = [0] * 81
    Yout1 = [0] * 81
    Yout2 = [0] * 81
    Yout3 = [0] * 81

    fp_fifo = open("./la_fifo", "rb")
    for i in range(0, 40):
        data[i] = int.from_bytes(fp_fifo.read(1), "big")

    fig, ax    = plt.subplots()

    line_Clk   = ax.plot(tick[0],   Clk[0], label="Clk")[0]
    line_Rdy   = ax.plot(tick[0],   Rdy[0], label="Rdy")[0]
    line_Vld   = ax.plot(tick[0],   Vld[0], label="Vld")[0]
    line_Yout0 = ax.plot(tick[0], Yout0[0], label="Yout[0]")[0]
    line_Yout1 = ax.plot(tick[0], Yout1[0], label="Yout[1]")[0]
    line_Yout2 = ax.plot(tick[0], Yout2[0], label="Yout[2]")[0]
    line_Yout3 = ax.plot(tick[0], Yout3[0], label="Yout[3]")[0]

    ax.set(xlim=[0, 800], ylim=[-1, 35])
    ax.legend(loc='upper left', fancybox=True, shadow=True)

    for i in np.arange(0, 80, 2):
        tick[i] = tick[i+1] = i * 10
    tick[80] = tick[79]

    ani = animation.FuncAnimation(
                        fig=fig,
                        func =update, 
                        fargs= (fp_fifo, data, tick,
                                line_Clk, line_Rdy, line_Vld,
                                line_Yout0, line_Yout1,
                                line_Yout2, line_Yout3,
                                Clk, Rdy, Vld,
                                Yout0, Yout1, Yout2, Yout3),
                        interval=10, cache_frame_data=False)
    plt.show()
#--------------------------------------------------------------------

run()


5. Makefile


# SystemC Environments -----------------------------------------
export SYSTEMC = /usr/local/systemc-3.0.0
export SYSTEMC_HOME = $(SYSTEMC)
export SYSTEMC_INCLUDE = $(SYSTEMC_HOME)/include
export SYSTEMC_LIBDIR = $(SYSTEMC_HOME)/lib-linux64
export LD_LIBRARY_PATH :=$(LD_LIBRARY_PATH):$(SYSTEMC_LIBDIR)
export CXX = clang++
export CXXFLAGS = -std=c++17

# SystemC testbench Reuse --------------------------------------
SC_SRCS      =  ./sc_main.cpp
SC_HDRS      =  ./sc_fir_pe.h \
./sc_fir_pe_tb.h
SC_TARGET    = sc_fir_pe_tb

# Conditional building option
ifeq ($(VCD_TRACE),YES)
  VCD_TRACE = VCD_TRACE_YES
else
  VCD_TRACE = VCD_TRACE_NO
endif
ifeq ($(TEST_MODE), MULTIPLIER_C)
  TEST_MODE = TEST_MULTIPLIER_C
else ifeq ($(TEST_MODE), MULTIPLIER_X)
  TEST_MODE = TEST_MULTIPLIER_X
else ifeq ($(TEST_MODE), ADDER_X)
  TEST_MODE = TEST_ADDER_X
else ifeq ($(TEST_MODE), ADDER_Y)
  TEST_MODE = TEST_ADDER_Y
else
  TEST_MODE = TEST_ADDER_R
endif

# Build Rules --------------------------------------------------
all :
@echo
@echo 'Makefile for Chip Test of "FIR_PE/MyChip MPW 2024-2"'
@echo 'Usage:'
@echo '    Build with Options,'
@echo '        TEST_MODE=<...> VCD_TRACE=<...> make build'
@echo '           TEST_MODE: MULTIPLIER_C, MULTIPLIER_X, ADDER_R, ADDER_X, ADDER_Y, RAND'
@echo '           VCD_TRACE: YES or NO'
@echo
@echo '    Run test,'
@echo '        make run'
@echo
@echo '    View VCD at run-time,'
@echo '        TEST_MODE=<...> make run_vcd'
@echo
@echo '        make clean'
@echo

build: $(SC_TARGET)

$(SC_TARGET): $(SC_SRCS) $(SC_HDRS)
$(CXX) $(CXXFLAGS) -I$(SYSTEMC_INCLUDE) -L$(SYSTEMC_LIBDIR) \
-D$(TEST_MODE) -D$(VCD_TRACE) -DLA_FIFO \
-lsystemc -o$(SC_TARGET) $(SC_SRCS)

run: $(SC_TARGET)
rm -f la_fifo
mkfifo la_fifo
python3 la_fifo.py &
./$(SC_TARGET)

run_vcd:
$(CXX) $(CXXFLAGS) -I$(SYSTEMC_INCLUDE) -L$(SYSTEMC_LIBDIR) \
-D$(TEST_MODE) -DVCD_TRACE_YES \
-lsystemc -o$(SC_TARGET) $(SC_SRCS)
rm -f sc_fir_pe_tb.vcd
mkfifo sc_fir_pe_tb.vcd
./$(SC_TARGET) &
shmidcat sc_fir_pe_tb.vcd | gtkwave -v -I sc_fir_pe_tb.sav

clean :
rm -f $(SC_TARGET)
rm -f sc_fir_pe_tb.vcd
rm -f la_fifo


테스트 실행

    % make

    Makefile for Chip Test of "FIR_PE/MyChip MPW 2024-2"
    Usage:
        Build with Options,
            TEST_MODE=<...> VCD_TRACE=<...> make build
               TEST_MODE: MULTIPLIER_C, MULTIPLIER_X, ADDER_R, ADDER_X, ADDER_Y, RAND
               VCD_TRACE: YES or NO

        Run test,
            make run

        View VCD at run-time,
            TEST_MODE=<...> make run_vcd

            make clean

    % TEST_MODE=ADDER_R VCD_TRACE=NO make build

    % make run





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

[이전] [2] 아듀이노 보드 에뮬레이터
[다음] [4] PC 사운드 카드 오실로 스코프

[참고]
1. "내 칩 제작 서비스" 2025년 1차 MPW 대비 오픈-소스 도구 설치 [바로가기]
2. ETRI 0.5um CMOS DK 예제: counter8/16, 디지털 회로 칩 테스트 방법 [바로가기]