티스토리 뷰

FPGA Interface API 개발 방법 살펴보기


이 글에서는 이 FPGA Interface API에 대한 함수 및 구현에 필요한 흐름에 대해서 살펴보도록 하겠습니다. 이전 글에서 헤더 파일을 만들기 위해서는 LabVIEW FPGA를 통해서 관련 코드를 구현하고 이를 컴파일 하여 BitFile을 생성해야 한다고 설명드렸습니다.


컴파일을 통해서 생성이 된 BitFile은 Host(Windows, RTOS)에서 FPGA의 Host 함수들을 이용하여 FPGA 기능을 처리할 수 있습니다. LabVIEW FPGA에서는 FPGA에서 구현된 기능들을 프러퍼티 노드와 인보크 노드를 통해서 사용할 수 있습니다. 랩뷰에서는 아래의 그림처럼 관련 vi들을 찾을 수가 있습니다. 


1. LabVIEW에서의 FPGA 처리 과정


빨간색 모양의 네모 상자 안의 함수들이 FPGA를 Host에서 사용할 때 가장 많이 사용되는 vi들입니다. 



그럼 실제로 LabVIEW에서 FPGA 코드를 어떻게 사용하는지 살펴보도록 하겠습니다. 아래의 그림처럼 사용되는 것이 일반적은 Host에서 FPGA 기능을 처리하는 방안입니다. 



FPGA에 대한 Host에서의 프로세스 처리 과정은 다음과 같습니다.

  1. FPGA Open : 컴파일을 통해서 생성된 BitFile을 Open 한다.
  2. FPGA Run : BitFile을 시작한다.
  3. FPGA Process : 시작된 BitFile내의 알고리즘에 특정한 값을 변경하거나 처리된 결과값을 가져온다. 값을 변경하는데는 컨트롤이, 값을 읽어오는데는 인디게이터가 많이 사용되며 대량의 데이터는 FIFO를 사용한다.
  4. FGPA Close : FPGA 관련 기능을 종료한다. 

2. C/C++ 언어에서의 FPGA 처리 과정


그럼 이제 LabVIEW에서 처리한 부분을 C 코드로 변형하기 위한 방법을 알아보도록 하겠습니다. 


2014/08/20 - [National Instruments/RIO] - FPGA Interface C API를 통해 C언어로 FPGA 구현하기 - 1 : 헤더 파일 생성

에서 확인한것 처럼 FPGA를 컴파일 한뒤 헤더파일을 생성하시면 FPGA 사용을 위한 헤더 파일들이 생성이 되게 됩니다. 


NiFpga.h에는 LabVIEW에서 사용했던 인보크노드와 프로퍼티 노드에 대한 내용이 정의되어 있고, NiFpga_(vi이름).h에는 사용자가 FPGA를 개발할 때 사용한 컨트롤, 인디게이터, FIFO에 대한 이름이 정의가 되어 있습니다. Host에서 FPGA를 사용하기 위해서는 NiFpga.h의 내용을 살펴보시면 됩니다. 좀더 명확한 함수의 이해를 바라신다면 FPGA Interface C API의 Help 파일을 참조하시면 됩니다. 


일반적으로 C언어에서 관련 FPGA 코드를 구현하기 위해서는 아래와 같은 과정을 거치게 됩니다. 



위의 함수의 내역을 간략히 살펴보면 다음과 같습니다.


이름

설명 

  NiFpga_Initialize()

FPGA 함수를 Host에서 사용하기 위해서 초기화 작업을 진행합니다.

  NiFpga_IsNotError()

FPGA의 모든 함수는 NiFpga_Status를 리턴하는데 0이 아니면 전부 에러가 발생한 것입니다. NiFpga_Status를 통해서 Initialize가 제대로 되었는지를 판단합니다. 이 함수는 다른 함수에서도 사용이 가능합니다. 함수 사용 이후 에러가 발생하면 C언어에서 프로그램을 종료하거나 재시도 할 수 있습니다.

  NiFpga_Open()

RIO 제품을 설정하고, 관련 BitFile을 설정합니다. BitFile에 대한 내역은  NiFpga_(vi이름).h에서 확인할 수 있습니다.

  NiFpga_Run()

FPGA를 실행합니다.

  NiFpga_Write(Read)I32()

FPGA에 특정값을 쓰거나 읽습니다. Write는 쓰기, Read는 읽기 기능을 수행합니다. NiFpga_WriteI32()는 i32형의 데이터를 쓰는데 이용이 되고, 데이터 타입에 맞게 다양한 함수가 존재합니다. 상세한 내용은 도움말을 참조하시면 될것 같습니다.

  NiFpga_ Close()

FPGA를 종료합니다. 

  NiFpga_Finalize()

FPGA 관련 함수 사용에 대한 리소스를 모두 해제합니다. Initialize와 Finalize 함수는 어떠한 코드에서는 반드시 사용되어야 한다고 NI에서 명시하고 있습니다.  


그럼 실제로 테스트를 위해 코딩한 내용을 살펴보도록 하겠습니다. FPGA BitFile에 대한 코드는 아래와 같습니다. 




Method For DIO라는 컨트롤을 통해서 연결된 DO의 출력값을 변경하는 코드입니다.


이 FPGA 코드를 컴파일하여, 헤더파일을 생성하면 아래와 같은 NiFpga_(vi이름).h의 헤더가 생깁니다. 아래의 헤더에는 BitFile 그리고 사용한 컨트롤에 대한 이름이 Define 되어 있습니다. 


/*

 * Generated with the FPGA Interface C API Generator 13.0.0

 * for NI-RIO 13.0.0 or later.

 */


#ifndef __NiFpga_FPGAcode_h__

#define __NiFpga_FPGAcode_h__


#ifndef NiFpga_Version

   #define NiFpga_Version 1300

#endif


#include "NiFpga.h"


/**

 * The filename of the FPGA bitfile.

 *

 * This is a #define to allow for string literal concatenation. For example:

 *

 *    static const char* const Bitfile = "C:\\" NiFpga_FPGAcode_Bitfile;

 */

#define NiFpga_FPGAcode_Bitfile "NiFpga_FPGAcode.lvbitx"


/**

 * The signature of the FPGA bitfile.

 */

static const char* const NiFpga_FPGAcode_Signature = "B61AB3A790C4B84394CBD3496867411F";


typedef enum

{

   NiFpga_FPGAcode_ControlBool_stop = 0x810E,

} NiFpga_FPGAcode_ControlBool;


typedef enum

{

   NiFpga_FPGAcode_ControlI16_MethodForDIO = 0x8112,

} NiFpga_FPGAcode_ControlI16;


#endif



이에 대한 값을 실제 코드에서 어떻게 사용하는지 살펴보도록 하겠습니다. 아래의 코드는 생성된 헤더파일들을 이용하여 Host에서 FPGA를 사용하기 위해 구현한 코드입니다. 위의 헤더 코드와 아래의 실제코드에 색으로 칠해진 부분을 살펴보십시오. 실제 함수에서 어떻게 적용이 되는지를 파악할 수 있습니다. 


// fpga 변수

NiFpga_Session session;

NiFpga_IrqContext irqContext;


printf("Initializing...NI FPGA \n");


// FPGA 초기화

NiFpga_Status status = NiFpga_Initialize();

printf("Opening a session...\n");


// FPGA OPEN

NiFpga_MergeStatus(&status, NiFpga_Open(NiFpga_FPGAcode_Bitfile, NiFpga_FPGAcode_Signature, "RIO0",
    NiFpga_OpenAttribute_NoRun, &session));


// FPGA RUN

NiFpga_MergeStatus(&status, NiFpga_Run(session, 0));

// While & Command

while(NiFpga_IsNotError(status) && ! m_bStop)

{


printf("\n");

printf("|---------------------------------------------------------------|\n");

printf("| [0] : ALL LED OFF                                             |\n");

printf("| [1] : GREEN LED ON                                            |\n");

printf("| [2] : RED LED OFF                                             |\n");

printf("| [3] : YELLOW LED OFF                                          |\n");

printf("| [4] : ALL LED BLINKING                                        |\n");

printf("| [5] : YELLOW, GREEN BLINKING                                  |\n");

printf("| [9] : EXIT Program                                            |\n");

printf("|---------------------------------------------------------------|\n");


// Input Command From Users

printf("\nEnter The Command : ");

scanf("%d", &m_nUserInput); 

printf("\n\n");


// FPGA로 데이터 쓰기

// 여기에 입력이 되는 값을 통해서 위의 LabVIEW로 짜여진 FPGA 코드의 Case문이 실행이 됩니다.

if(m_nUserInput >= 0 && m_nUserInput <= 5) // 메소드

{

NiFpga_MergeStatus(&status, NiFpga_WriteI16(session, NiFpga_FPGAcode_ControlI16_MethodForDIO, m_nUserInput));

}

}


NiFpga_MergeStatus(&status, NiFpga_Close(session, 0));

status = NiFpga_Finalize();


return 0;


다른 함수들은 대부분이 session과 특정 정해진 값들만 받아서 이해하기 쉬우실 겁니다. 다만 컨트롤에 값을 넣는 부분이 조금 어려우실 것 같아 좀더 내용을 붙이도록 하겠습니다.


NiFpga_MergeStatus(&status, NiFpga_WriteI16(session, NiFpga_FPGAcode_ControlI16_MethodForDIO, m_nUserInput));


아래의 LabVIEW 코드를 보시면 Method For DIO라는 컨트롤이 있습니다. 위의 코드를 실행을 하면 m_nUserInput의 값이 아래 코드의 Method For DIO로 전달이 됩니다. 따라서 m_nUserInput의 값에 따라 아래의 FPGA 코드의 Case문이 변경이 되어 실행이 되게 되는 것 입니다. 




도움이 되셨나요,

도움이 되셨으면 아래의 그림을 클릭해주세요.



댓글