2024년 1월 17일 수요일

ETRI 0.5um CMOS Std-Cell DK 예제: CPU 6502 [4]/시스템 수준 테스트벤치 (3/3)

ETRI 0.5um CMOS Std-Cell DK 예제: CPU 6502 [4]

II. 시스템 수준 테스트 벤치 [3부]

    II-6. Apple-1의 표준 입력 장치: 키보드

        simulation/a1_keyboard.cpp
        Apple-1의 입력용 PIA: 메모리 매핑된 키보드 포트
    II-7. Apple-1의 표준 출력장치: 디스플레이
        simulation/a1_display.cpp
        Apple-1의 출력용 PIA: 메모리 매핑된 디스플레이 포트

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


II. 시스템 수준 테스트 벤치 [3부]

II-6. Apple-1의 표준 입력 장치: 키보드

소프트웨어의 멀티 쓰레딩(multi-threding[바로가기])과 운영체제의 프로세스간 통신(IPC, Inter-Process Communication[바로가기])을 동원 함으로써 임의 순간에 이뤄지는 사건을 처리하여 하드웨어의 검증에 활용 할 수 있다. 이 기법들은 다중처리용으로도 이미 확립되어 있으므로 쉽게 소프트웨어를 제작할 수 있다. 게다가 이들 소프트웨어 장치들은 가장 원론적인 C/C++로 작성되어 있기에 하드웨어 행위 기술용 SystemC와 함께 활용하면 대화형 하드웨어 테스트벤치를 어렵지 않게 구축할 수 있다.

simulation/a1_keyboard.cpp

Apple-1의 기본 입력장치는 키보드(keyboard)다. 키보드는 외부장치로 별도의 프로세스(프로그램)로 작동 하며 명명된 파이프(named pipe 또는 FIFO)를 통해 Apple-1 컴퓨터 시뮬레이터와  통신한다. 키보드 프로세스의 C++ 코드는 아래와 같다. 프롬프트 '>' 를 표시한 후 리눅스 시스템의 표준입력장치 stdin (키보드)에서 ASCII 문자열을 받아 FIFO에 전송을 반복하는 매우 단순한 프로그램이다.

    // Filename: a1_keyboard.cpp
        ......
    int main()
    {
        int writefd, n;
        char buf[MAX_BUF]={0,};

        mkfifo(A1_KEYBOARD, 0666);
        writefd = open(
"keyboard.fifo"O_WRONLY);

        while(1)
        {
            putchar('>');
            fgets(buf, MAX_BUF, stdin);

            n = strlen(buf) + 2;
            write(writefd, buf, n);
        }

        close(writefd);
        unlink("keyboard.fifo");
    }

키보드 프로세스는 키보드 데이터 전송하기 전에 리눅스 운영체제의 시스템 함수 mkfifo()로 Apple-1 프로세스와 통신할 FIFO(named pipe)를 생성한다. 운영체제는 FIFO를 파일로 간주하므로 시스템 함수 open()으로 "keyboard.fifo"로 명명된 파일을 쓰기 모드 O_WRONLY로 연 후 입력된 키보드 문자열을 write()를 이용해 써넣다.

Apple-1의 입력용 PIA: 메모리 매핑된 키보드 포트

Apple-1 컴퓨터의 키보드 입력 포트(input port)는 주소 0xD010(PIA_KBD_REG)에 메모리 매핑 되어 있다. CPU가 읽기동작으로 접근해 오면 동기식 메모리는 주소가 키보드 입력 포트 PIA_KBD_REG와 일치할 경우 키보드 버퍼 KBD_Buff[] 에서 1바이트를 내보낸다.

    // Filename: sc_mem.cpp
    #define PIA_KBD_REG 0xD010
    #define PIA_KBD_CTL 0xD011
        ......
    void sc_mem::mem_Thread(){
        ......
        while (true){   // Thread: wait forever
            ......
            wait(clk.posedge_event()); // wait for event on 'clk'
            Address = AB.read(); // Read Address-Bus
            ......
            if (WE.read()){    // Memory Write Enable?
                ......
            }
            else { // Memory Read
                ......
                if (Address == PIA_KBD_REG){// PIA.A keyboard input
                    ......
                    if (KBD_Buff[nIdx]=='\0') {
                        ......
                        mem[Address] = 0x00;
                        mem[PIA_KBD_CTL]=(mem[PIA_KBD_CTL]&0x3F);
                        nIdx = 0;
                    } else {
                        ......
                        mem[Address] = (KBD_Buff[nIdx] | 0x80);
                        mem[PIA_KBD_CTL] = (mem[PIA_KBD_CTL]&0x3F);
                        KBD_Buff[nIdx++] = '\0';
                    }
                }
                else if (Address == PIA_KBD_CTL) { // PIA.A Control
                    if (KBD_Buff[nIdx])
                        mem[Address] = (mem[Address]|0x80);// Ready
                }
                    ......
                DO.write(mem[Address]); // READ MEMORY
            }
        }
    }

하드웨어의 주변 인터페이스 장치 PIA(Peripheral Interface Adaptor)는 포트를 감시하고 유효 데이터 전송을 확인하는 일련의 동작을 구현하기 위해 핸드-쉐이크(hand-shake)를 장치한다. PIA는 포트마다 데이터를 저장하는 레지스터와 핸드쉐이크용 컨트롤 레지스터를 둔다. 입력 포트에 유효한 데이터가 들어오면 준비되었음을 표시하고 CPU가 읽어가면 이 비트는 지워진다. Apple-1에 장치된 PIA는 컨트롤 레지스트 PIA_KBD_CTRL 상위 7번째 비트를 핸드-쉐이크 용으로 사용하고 있다. 키보드로부터 유효한 데이터가 있음을 알리기 위해 핸드-쉐이크 비트를 세운다.

    if (KBD_Buff[nIdx])
        mem[PIA_KBD_CTL] = (mem[PIA_KBD_CTL]|0x80); // Input Ready

CPU가 입력 포트의 데이터를 읽어가고 핸드-쉐이크 비트를 지운다.

    mem[Address] = (KBD_Buff[nIdx] | 0x80);      // Read KBD Reg.
    mem[PIA_KBD_CTL] = (mem[PIA_KBD_CTL]|0x3F);  // Clear B7

키보드 프로그램 a1_keyboard 에서 Apple-1 시뮬레이터로 키보드 값이 언재 전송될지 특정 되지 않았다. 대화형(interactive) 시뮬레이터라는 점을 기억하자. CPU는 시뮬레이터의 클럭 발생기에 반응하여 주기적으로 동작한다. 주변 장치는 임의 시점에 입력될 키보드 값을 취하기 위해 버퍼를 둔다. 하드웨어에서 입력을 감시하는 동작을 구현하기 위해 외부 프로세스로부터 전송되는 통로 FIFO를 감시하는 별도의 쓰레드를 띄우고 유효한 데이터가 들어올 경우 읽어서 메모리 데이터 버스로 출력한다. 동기실 메모리 모델 중 PIA의 입력 포트를 기술한 부분은 아래와 같다.

    // Filename: sc_mem.cpp
        ......
    unsigned char KBD_Buff[MAX_BUF] = "";

    // Keyboard Thread
    void* thread_keyboard(void *arg){
        char fifoBuf[MAX_BUF];
        int n;
        int readfd = open("keyboard.fifo", O_RDONLY);
            ......
        while(true) {
            n = read(readfd,(char*)fifoBuf,MAX_BUF-1)); // Read FIFO
            for (int i=0; i<n; i++) {
                KBD_Buff[i] = fifoBuf[i];
                KBD_Buff[i+1] = '\0';
            }
        }
    }

    // -----------------------------------------------
    // Synchronous Memory Model & Memory mapped I/O
    // -----------------------------------------------
    void sc_mem::mem_Thread() {
        uint32_t    Address;
        int         nIdx = 0;

        // Keyboard input from another terminal
        pthread_t thread_kbd;
        pthread_create(&thread_kbd, NULL, thread_keyboard, NULL);
            ......
        while (true){   // Thread: wait forever
            wait(clk.posedge_event()); // wait for event on 'clk'
            ......
            if (WE.read()) { // Memory Write Enable?
                ......
            }
            else { // Memory Read
                ......
                if (Address == PIA_KBD_REG) {// PIA.A keyboard input
                        ......
                    if (KBD_Buff[nIdx]=='\0'){
                        ......
                    }
                }
            }
        }
    }

시스템 수준 모델링에서 FIFO 장치의 역활이 매우 중요하다.

II-7. Apple-1의 표준 출력장치: 디스플레이

사건을 일으키는(또는 데이터를 생성하는) 주체에 따라 모델의 복잡도가 매우 차이난다.

simulation/a1_display.cpp

Apple-1 컴퓨터의 기본 출력 장치는 문자 디스플레이다. 디스플레이는 외부장치로 별도의 프로세스(프로그램)로 작동 하며 명명된 파이프(named pipe 또는 FIFO)를 통해 Apple-1 컴퓨터 시뮬레이터가 출력하는 문자를 표시한다. 디스플레이 프로세스의 C++ 코드는 아래와 같다. FIFO에 유효한 데이터가 차면 이를 읽어 출력하는 매우 단순한 프로그램이다. 키보드 프로세스보다 간단하다. 반복 while() 구간 내에 read()는 FIFO 읽기용 시스템 함수다. FIFO 파일이 비었을 경우 다른 프로세스에게 실행권을 넘기는 블로킹 시스템 함수를 씀으로써 무한 반복 구역내에서 폭주하지 않는다.

    // Filename: a1_display.cpp
    int main()
    {
        int readfd, n;
        char buf[MAX_BUF]={0,};

        mkfifo("display.fifo", 0666);
        readfd = open(
"display.fifo", O_RDONLY);

        while(1){
            n = read(readfd, buf, 1));   // MAX_BUF
            putchar(buf[0]);
        }
    }

Apple-1의 출력용 PIA: 메모리 매핑된 디스플레이 포트

Apple-1 컴퓨터의 디스플레이 출력 포트(output port)는 주소 0xD012(PIA_DSP_REG)에 메모리 매핑 되어 있다. CPU가 쓰기동작으로 동기식 메모리에 접근해 올때 주소가 디스플레이 출력 포트 PIA_DSP_REG와 일치할 경우 "display.fifo"로 명명된 파이프(named pipe 또는 FIFO)에 1바이트를 써넣는다. 동기식 메모리 모델 중 PIA의 출력 포트 부분은 아래와 같다.

    // Filename: sc_mem.cpp
    ......
    #define PIA_DSP_REG 0xD012
    #define PIA_DSP_CTL 0xD013
    ......
    // ------------------------------------------------
    // Synchronous Memory Model & Memory mapped I/O
    // ------------------------------------------------
    void sc_mem::mem_Thread()
    {
        uint32_t Address;
        .....
        // FIFO(named pipe) for display terminal
        int writefd = open("display.fifo", O_WRONLY);
        .....
        while (true) {
            wait(clk.posedge_event());
            Address = AB.read(); // Read Address-Bus

            if (WE.read()){ // Memory Write
                mem[Address] = DI.read();  // WRITE MEMORY
                ......
                // Memory Mapped I/O (Screen & Keyboard)
                if (Address == PIA_KBD_REG)    // PIA.A keyboard reg.
                    .....
                else if (Address == PIA_DSP_REG) { // PIA.B display
                    unsigned char c = (unsigned char)(mem[Address]);
                    write(writefd, (char*)&c, 1); // Write to FIFO
                    mem[Address] = (mem[Address]&0x07F); // Clear B7
                }
            }
            else { // Memory Read
            .....
            }
        }
    }

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

[목차][이전][다음]


댓글 없음:

댓글 쓰기