2026년 3월 15일 일요일

[베릴로그 RTL 예제] 탁구 게임기 -5편: GLCD의 버스 기능 모델(BFM)-

[베릴로그 RTL 예제] 탁구 게임기 -5편: GLCD의 버스 기능 모델 -

목차:

1. RTL 베릴로그로 "탁구대" 그리기
2. 그래픽 LCD 구동 칩의 시뮬레이션 모델
3. "탁구대"의 그래픽 테스트 벤치
4. 움직이는 탁구공

5. GLCD의 버스 기능 모델(BFM)
    5-1. 버스 기능 모델
    5-2. BFM 수준으로 작성된 GLCD 모델
    5-3. 실습 및 과제

----------------------------------------------------------------------------------------------------------------
오픈-소스 반도체 설계 도구가 설치된 "내 칩 디자인 킷"의 WSL 가상 디스크 이미지  [링크]
----------------------------------------------------------------------------------------------------------------

5. GLCD의 버스 기능 모델

디지털 파형을 보며 그래픽 게임기를 설계하기는 불가능에 가깝다. 앞의 편에서 RTL 베릴로그로 작성한 설계를 검증하기 위해 그래픽 LCD의 시뮬레이션 모델을 제작하고 이를 테스트벤치에 활용하였다. 하지만 명색이 비디오 게임기 인데 제아무리 시뮬레이션이라지만 실행 속도가 너무 느렸다.

5-1. 버스 기능 모델

처리율과 클럭율이 각각 다른 두 모듈 사이에 정보전달에 핸드쉐이크 기법이 사용되었다. 이(인터페이스)를 검증하기 위해 양 모듈 모두 동등한 추상화 수준에서 기술하고 시뮬레이션 했지만 속도가 느리다. 사건 구동 시뮬레이션에서 속도를 올리려면 사건 발생을 줄여야 한다. 설계 대상은 RTL 이지만 시뮬레이션 모델의 내부 동작 역시 이에 준하는 추상화 수준일 필요는 없다. 두 모듈 사이의 인터페이스 동작(핸드 쉐이크)가 확인된 이상 그래픽 LCD 모델의 추상화 수준을 높임으로써 시뮬레이션 속도를 올려보기로 한다. "버스 기능 모델(BFM, Bus Functional Model)"을 구글 인공지능으로 찾아보면 다음과 같이 답해주었다[링크].

정의: 트랜잭션 검증 모델(TVM)이라고도 하며, 컴퓨터나 집적 회로(IC) 설계에서 외부 버스와 통신하는 구성 요소를 검증하기 위한 합성 불가능한 소프트웨어 모델 입니다. 하드웨어 시뮬레이션에서 버스 프로토콜(예: AMBA, PCIe)의 트랜잭션 수준 동작을 모방하는 검증 모델입니다.

목적: RTL(Register Transfer Level) 설계가 완료되기 전에, 상위 수준에서 기능 검증을 수행하기 위해 사용합니다. 즉, 실제 하드웨어의 동작을 소프트웨어적으로 시뮬레이션하여 데이터 전송, 인터럽트 등 버스 기반 동작을 미리 테스트합니다.

구조: 일반적인 버스 프로토콜의 신호 수준 동작을 모델링하여 복잡한 로직을 대신 수행합니다.

활용: 트랜잭션 검증 모델(TVM)이라고도 불리며, 복잡한 SoC(System on Chip) 설계에서 버스 인터페이스의 기능적인 정확성을 확인하는 데 필수적입니다.

5-2. BFM 수준으로 작성된 GLCD 모델

GLCD 모델이 바로 "합성 불가능한 소프트웨어 모델"이다. BFM 수준으로 GLCD 모델을 작성해보자. "탁구 게임기"의 RTL과 GLCD 사이에 전송이 이뤄지는 버스 인터페이스 신호를 살펴보자. GLCD 상에 그림을 그릴 정보는 좌표 x_pos, y_pos와 픽셀 값 pixel이며 p_tick 과 busy로 두 모듈 사이의 핸드쉐이크 동작이 이뤄진다. 두 모듈 사이에 연결된 신호들과 전송규약을 묶어 "버스"라 한다.

GLCD 모델에서 좌표를 받아 그림을 그리는 절차를 RTL 보다 높은 추상화 수준을 변경해 주도록 한다. 먼저 "탁구 게임기" RTL 과 연결되는 GLCD의 외형에는 큰 변화 없다. GLCD 내부에 적용된 클럭이 빠져 있는 점에 주목한다. 디지털 회로의 RTL에서 벗어났음을 의미한다.

//
// Filename: sc_glcd128x64_TLM.h
//

#ifndef _SC_GLCD128x64_TLM_H_
#define _SC_GLCD128x64_TLM_H_

#include <systemc.h>
#include <SDL2/SDL.h>

SC_MODULE(sc_glcd128x64_TLM)
{
    sc_in<bool>             reset;
    sc_in<sc_uint<7> >      x_pos;
    sc_in<sc_uint<6> >      y_pos;
    sc_in<bool>             pixel;
    sc_in<bool>             p_tick;
    sc_out<bool>            busy;

    void Display_Thread(void);

    // SDL2--------------------------
    SDL_Window* window;
    SDL_Renderer* renderer;
    SDL_Event event;

    SC_CTOR(sc_glcd128x64_TLM)
    {
        SC_THREAD(Display_Thread);
        sensitive << p_tick;

        // SDL2--------------------------
        window = NULL;
        renderer = NULL;
        if (SDL_Init(SDL_INIT_VIDEO) < 0)
            return;

        window = SDL_CreateWindow("SDL2 Window",
                              SDL_WINDOWPOS_UNDEFINED,
                              SDL_WINDOWPOS_UNDEFINED,
                              128, 64,
                              SDL_WINDOW_SHOWN);

        if (!window)
        {
            SDL_Quit();
            return;
        }

        SDL_SetWindowTitle(window, "GLCD 128x64");
        SDL_SetWindowResizable(window, SDL_FALSE);

        renderer = SDL_CreateRenderer(window, -1,
                                SDL_RENDERER_ACCELERATED);
    }
};

#endif

그림을 그리는 사건 구동함수 Display_Thread() 가 단지 p_tick에 감응 지정되었다. 사건구동함수는 다음과 같다. RTL 수준의 동작이 모두 배제 되었으므로 매우 단순하다. 핸드쉐이크를 위해 p_tick에 반응하여 busy 신호를 내고 있을 뿐이다. 정작 점을 찍는 동작은 사건 구동에 무관하다.

//
// Filename: sc_glcd128x64_TLM.cpp
//

#include <unistd.h>
#include "sc_glcd128x64_TLM.h"

void sc_glcd128x64_TLM::Display_Thread(void)
{
    int x, y;

    busy.write(false);

    while(true)
    {
        // SDL QUIT event
        if (SDL_PollEvent(&event) && (event.type == SDL_QUIT))
        {
            SDL_DestroyRenderer(renderer);
            SDL_DestroyWindow(window);
            SDL_Quit();
            sc_stop();
        }

        wait(p_tick.posedge_event());

        busy.write(true);

        wait(p_tick.negedge_event());

        x = x_pos.read();
        y = y_pos.read();
        if (pixel.read())
            SDL_SetRenderDrawColor(renderer, 255, 255, 255,
                            SDL_ALPHA_OPAQUE);
        else
            SDL_SetRenderDrawColor(renderer,0,0,0,SDL_ALPHA_OPAQUE);

        SDL_RenderDrawPoint(renderer, x, y);

        if (x==127 && y==63)
            SDL_RenderPresent(renderer);                

        busy.write(false);
    }
}

5-3. 실습 및 과제

버스 기능 모델 GLCD가 적용된 "빠른 탁구공"의 실습 예제 소스 구성은 다음과 같다.

    $cd ~/ETRI050_DesignKit/Projects/RTL/pong_SbS/05_Ball_GLCD_BFM

    $ tree
    .
    ├── _Docs_
    ├── pong_SbS
    │   └── pong_SbS.v
    └── simulation
        ├── Makefile
        ├── sc_glcd128x64_TLM.h
        ├── sc_glcd128x64_TLM.cpp
        ├── sc_pong_SbS_TB.h
        ├── sc_pong_SbS_TB.cpp
        └── sc_main.cpp

a. 실습

예제의 시뮬레이터 빌드와 실행은 모두 make 유틸리티로 수행한다. 소스 파일의 구성은 앞장에서와 동일하므로 Makefile의 변화는 없다.

    $ make build

시뮬레이터를 실행해보자.

    $ make run

빠르게 움직이는 "탁구공"을 보게될 것이다. 파형을 보면 핸드쉐이크가 일어난 후 점을 찍기까지 GLCD 모델 내에서 사건발생이 일어나지 않는다.

    $ make wave

"탁구공" RTL의 클럭이 상승 사건이 발생하면 델타 싸이클 내에 핸드쉐이크가 완료되는 모습을 볼 수 있다.

b. 과제

[과제1] RTL의 입력 busy와 출력 p_tick 가 clk의 델타 싸이클 내에 변화하는 모습이 시뮬레이션으로 얻은 디지털 파형에 나타나고 있지 않은점에 유의해야 한다. 사건 구동 시뮬레이션에서 "델타 싸이클"의 의미에 대하여 토론해 보자.

[과제2] 하드웨어 설계 자동화 도구(EDA Tool)는 반도체 관련 산업의 중요한 축이다. 소프트웨어로 하드웨어를 모사하는 사건 구동 시뮬레이터의 작동에 대하여 이해해보자. 아래의 관련문서(디자인 킷에 포함되어 있음)가 도움이 될 것이다.

    "C++ 템플릿 크래스와 SystemC의 최소한 이해"[링크]

[과제3] 불필요한 rom_bit의 계산을 최소화 할 수 있다.

    reg [2:0]  rom_bit;

    always @*
        if ((x_ball<=x_pos) && ((x_ball+7)>=x_pos) &&
            (y_ball<=y_pos) && ((y_ball+7)>=y_pos))
        begin
            rom_bit = x_pos - x_ball;
            pixel = rom_data[rom_bit];
        end
        else
        begin
            rom_bit = 0;
            pixel = 0;
        end

움직이는 "탁구공" 생성 RTL에서 이미지 출력부분을 위와 같이 변경 한후 시뮬레이션 출력 파형을 검토해보라. 아울러 계산 최소화로 얻는 잇점도 있으나 단점에 대해서도 논의해 보자.

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

<6편: 탁구대, 움직이는 공 그리고 탁구채>

댓글 없음:

댓글 쓰기