[Vitis]
📌 Export Hardware
우리는 어제 UART 기능이 있는 시스템 하나를 만들었다.
Generate Bitstream하고 Export Hardware시켜주도록 하자.


Include bitstream은 꼭 체크해줘야 한다. 그래야 비트스트림까지 같이 Export됨
📌 Vitis

AVR할 때는 마이크로칩스
CoretexM
Vitis는 IDE에 해당되는 것
다른 건 보드를 고를 수 있는데, 이 건 우리가 방금 만든 거라


우리가 만들어준 보드 파일의 타입이 바로 XSA 파일



지금 뭐한거냐면, 하드웨어 플랫폼을 하나 만든 것이다.
이렇게 해주면 우리가 만든 보드에 대한 BSP들이 따라온다.
- BSP(보드 서포트 팩)(드라이버 등) -
Vitis에서도 ARM에서 사용했 던 HAL 함수처럼, 여기만의 함수를 따로 학습하고 사용해야 한다.





#include <stdio.h> #include "platform.h" #include "xil_printf.h" int main() { init_platform(); print("Start!!"); while(1) { print("Hello World\n\r"); MB_Sleep(1000); // 1ms짜리 딜레이 함수 } cleanup_platform(); return 0; }
helloworld.c 파일에서 코드를 좀 수정해주자.


printf 함수는 표준 출력 함수로, 연결된 출력장치로 보내주는 기능을 한다.
우리는 UART만 구현해줬으니, UART를 통해 출력될 것이다.
ComportMaster(터미널 프로그램)를 통해 결과를 확인해보자.

위 IDE의 장점은
우리가 만든 모듈을 직접 시스템으로 만들 수 있다.
예시로, AVR이나 ARM IDE에서는 FND를 컨트롤하는 모듈은 존재하지 않는다.
하지만 Vitis에서는 우리 입맛대로 모듈을 만들어서 GPIO와 연결해주면
얼마든지 사용이 가능하다.
즉, 나만의 시스템을 만들 수 있다는 것이고 이 것이 바로 SoC의 매력이다.
int 사이즈는 컴퓨터에 따라서 간다.
8bit 컴퓨터에서는 8bit, 16bit 컴퓨터에서는 16bit
AVR은 8bit 컴퓨터인데 왜 int 사이즈가 16bit인가?
AVR은 약간 중간에 걸쳐져 있다. 약간 애매한 포지션이야.
그러다보니 중구난방이다.
요즘 MCU 시장은 CoretexM이 대세이고
그 다음으로는 SoC
ex) 삼성 엑시노스 : Core만 ARM꺼 쓰고, 나머지는 블록들 붙여서 SoC 만듦
[mblaze_led]
📌 Block Design
Basys3 보드의 16개 LED를 제어하는 시스템을 만들어보자.

초기 순서는 동일하다.
System clock 불러오고, Reset 불러오고 MicroBlaze 불러오면 됨.

참고로 이 건, 보드에 이미 있는 것들이다.
보드에 MCU는 없기 때문에 MicroBlaze는 IP에서 받아와야 한다.

코드 컴파일할 때 8KB 넘어가면, 안 되기 때문에 메모리를 128KB(최대)로 늘려주자.

Regenerate Layout해주면, 자동으로 깔끔하게 정리해준다.
UART는 직접 제어가 안 되기 때문에
중간에 제어하는 버스컨트롤러가 붙는다. 이 것이 바로 AXI Interconnect
필요없을 때는 연결을 끊어뒀다가 사용할 때 연결해주는 역할(Read_Enable 역할)
만약 이 기능이 없다면,
버스에 여러 데이터가 입력되고, 그 중 가장 쎈 놈따라서 가게 된다.
그 것이 바로 레이싱, 충돌과는 다름.
충돌은 둘 다 쎌 때 발생
SoC에 내장되어 있지만, Core를 제외하고는 다 페리페럴이라 부른다.
이 것들을 총칭하는 단어가 MCU
사우스브릿지? 노스브릿지?
뭐 이 것들에 해당되는 것들도 다 내장된 것이 SoC
옛날에는 다 나눠져 있었음

여기서 블록 이름도 바꿔줄 수 있다.

다 만들었다면, Validate Design해주고


블록 디자인을 HDL 언어로 바꿔주자.

SoC에서는 자동으로 보드의 LED랑 연결되기 때문에 xdc 파일에서는 주석처리해줘야 충돌이 방지된다.

비트스트림 생성해주자.
📌 Vitis(Hardware Export)
새로운 플랫폼 프로젝트와 어플리케이션 프로젝트를 생성해주자.
플랫폼은 방금 만든 mblaze_led_hw로 설정할 것

📌 GPIO Setting
IDE의 GPIO는 구조체로 작성되어 있다.
우리는 LED 컨트롤을 위해 직접 GPIO 셋팅을 해줄 것이다.
대부분의 시스템은 출력이 0, 입력이 1로 셋팅된다
- Output : 0, Input : 1
AVR만 출력이 1, 입력이 0 -> 이러니까 망한 거
객체 지향이나 절차 지향은 그냥 코딩하는 방식일 뿐이다.
C언어에서도 객체지향 방식을 사용할 수 있다.
어떻게? 구조체를 사용해서(C++의 Class 대신 사용)
근데 구조체에서는 함수를 못 써
대신 함수의 포인트변수를 사용해서 쓸 수 있다.
typedef struct { UINTPTR BaseAddress; /* Device base address */ u32 IsReady; /* Device is initialized and ready */ int InterruptPresent; /* Are interrupts supported in h/w */ int IsDual; /* Are 2 channels supported in h/w */ } XGpio;
이게 바로 구조체로 Class를 구현해 객체 지향 방식을 사용한 것

xparameters.h 에 들어가보면, 우리가 쓸 GPIO 관련 변수들이 어떻게 define돼 있는지를 볼 수있다.
📌 Main Code 작성
#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "xparameters.h" #include "xgpio.h" #define LED_ID XPAR_AXI_GPIO_0_DEVICE_ID // 원래는 bitstream할 때, LED로 이름 바꿔줬으면 좀 더 십게 알아볼 수 있다. #define LED_CHANNEL 1 int main() { init_platform(); print("Start!\n\r"); XGpio_Config *cfg_ptr; // XGpio_Config 구조체의 주소 XGpio led_device; // gpio 객체 u32 data = 0; // Initialize Led Device cfg_ptr = XGpio_LookupConfig(LED_ID); XGpio_CfgInitialize(&led_device, cfg_ptr, cfg_ptr->BaseAddress); // XGpio_CfgInitialize(InstancePtr, Config, EffectiveAddr) XGpio_SetDataDirection(&led_device, 1, 0); // XGpio_SetDataDirection(InstancePtr, Channel, DirectionMask) while(1) { print("Hello World\n\r"); data++; XGpio_DiscreteWrite(&led_device, 1, data); // XGpio_DiscreteWrite(InstancePtr, Channel, Mask) MB_Sleep(1000); } cleanup_platform(); return 0; }
[Switch를 활용한 LED Control]
📌 Block Design

만들어진 gpio에 switch 모듈을 붙여주자


📌 Hardware Export


Wrap 해줘서 HDL 코드로 변환

만들어진 XSA 파일을 Export
📌 Vitis에서 Main code 작성
1. Platform Project 생성
2. Application Project 생성
3. Code 작성
#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "xparameters.h" #include "xgpio.h" #define LED_ID XPAR_AXI_GPIO_LED_DEVICE_ID #define SWITCH_ID XPAR_AXI_GPIO_SWITCH_DEVICE_ID #define LED_CHANNEL 1 #define SWITCH_CHANNEL 1 int main() { init_platform(); print("Start!\n\r"); XGpio_Config *cfg_ptr_led; // XGpio_Config 구조체의 주소 XGpio_Config *cfg_ptr_switch; // XGpio_Config 구조체의 주소 XGpio led_device; // gpio 객체 XGpio switch_device; // gpio 객체 u32 led_data = 0; // u32 switch_data = 0; // Initialize Led Device cfg_ptr_led = XGpio_LookupConfig(LED_ID); cfg_ptr_switch = XGpio_LookupConfig(SWITCH_ID); XGpio_CfgInitialize(&led_device, cfg_ptr_led, cfg_ptr_led->BaseAddress); XGpio_CfgInitialize(&switch_device, cfg_ptr_switch, cfg_ptr_switch->BaseAddress); // XGpio_CfgInitialize(InstancePtr, Config, EffectiveAddr) XGpio_SetDataDirection(&led_device, LED_CHANNEL, 0); // 0을 줘서 출력 설정 XGpio_SetDataDirection(&switch_device, SWITCH_CHANNEL, 1); // 1을 줘서 입력 설정 // XGpio_SetDataDirection(InstancePtr, Channel, DirectionMask) while(1) { print("Hello World\n\r"); led_data = XGpio_DiscreteRead(&switch_device, SWITCH_CHANNEL); XGpio_DiscreteWrite(&led_device, LED_CHANNEL, led_data); // XGpio_DiscreteWrite(InstancePtr, Channel, Mask) MB_Sleep(1000); } cleanup_platform(); return 0; }
[FND Control]
📌 Block Design & Hardware Export


📌 Main Code
1. Counting
#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "xparameters.h" #include "xgpio.h" #define LED_ID XPAR_AXI_GPIO_LED_DEVICE_ID #define SWITCH_ID XPAR_AXI_GPIO_SWITCH_DEVICE_ID #define FND_ID XPAR_AXI_GPIO_FND_DEVICE_ID #define LED_CHANNEL 1 #define SWITCH_CHANNEL 1 #define FND_AN_CHANNEL 1 #define FND_DISP_CHANNEL 2 int main() { init_platform(); print("Start!\n\r"); XGpio_Config *cfg_ptr_led; // XGpio_Config 구조체의 주소 XGpio_Config *cfg_ptr_switch; // XGpio_Config 구조체의 주소 XGpio_Config *cfg_ptr_fnd; // XGpio_Config 구조체의 주소 XGpio led_device; // gpio 객체 XGpio switch_device; // gpio 객체 XGpio fnd_device; // gpio 객체 u32 led_data = 0; u32 fnd_data = 0; u8 com_data = 0; u8 com = 0; u32 prev_time = 0; u32 tick_time = 0; u32 count = 0; u8 fnd_value[] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67 }; ////////////////////////////////////// Initialize Led Device ////////////////////////////////////// cfg_ptr_led = XGpio_LookupConfig(LED_ID); cfg_ptr_switch = XGpio_LookupConfig(SWITCH_ID); cfg_ptr_fnd = XGpio_LookupConfig(FND_ID); //-----------------------------------------------------------------------------------------------// XGpio_CfgInitialize(&led_device, cfg_ptr_led, cfg_ptr_led->BaseAddress); XGpio_CfgInitialize(&switch_device, cfg_ptr_switch, cfg_ptr_switch->BaseAddress); XGpio_CfgInitialize(&fnd_device, cfg_ptr_fnd, cfg_ptr_fnd->BaseAddress); // XGpio_CfgInitialize(InstancePtr, Config, EffectiveAddr) //-----------------------------------------------------------------------------------------------// XGpio_SetDataDirection(&led_device, LED_CHANNEL, 0); // 0을 줘서 출력 설정 XGpio_SetDataDirection(&switch_device, SWITCH_CHANNEL, 1); // 1을 줘서 입력 설정 XGpio_SetDataDirection(&fnd_device, FND_AN_CHANNEL, 1); // 0을 줘서 출력 설정 XGpio_SetDataDirection(&fnd_device, FND_DISP_CHANNEL, 0); // 0을 줘서 출력 설정 // XGpio_SetDataDirection(InstancePtr, Channel, DirectionMask) /////////////////////////////////////////////////////////////////////////////////////////////////// while(1) { // print("Hello World\n\r"); // UART Test // led_data = XGpio_DiscreteRead(&switch_device, SWITCH_CHANNEL); // XGpio_DiscreteWrite(&led_device, LED_CHANNEL, led_data); // XGpio_DiscreteWrite(&fnd_device, FND_DISP_CHANNEL, fnd_data); switch(com) { case 0 : XGpio_DiscreteWrite(&fnd_device, FND_AN_CHANNEL, 0b1110); fnd_data = ~fnd_value[count/1000]; break; case 1 : XGpio_DiscreteWrite(&fnd_device, FND_AN_CHANNEL, 0b0111); fnd_data = ~fnd_value[count/100%10]; break; case 2 : XGpio_DiscreteWrite(&fnd_device, FND_AN_CHANNEL, 0b1011); fnd_data = ~fnd_value[count/10%10]; break; case 3 : XGpio_DiscreteWrite(&fnd_device, FND_AN_CHANNEL, 0b1101); fnd_data = ~fnd_value[count%10]; break; } com++; com = com % 4; if(tick_time - prev_time > 100) { prev_time = tick_time; count++; } // XGpio_DiscreteWrite(InstancePtr, Channel, Mask) MB_Sleep(1); tick_time++; XGpio_DiscreteWrite(&fnd_device, FND_DISP_CHANNEL, fnd_data); } cleanup_platform(); return 0; }

2. Switch를 활용한 FND 컨트롤
#include <stdio.h> #include "platform.h" #include "xil_printf.h" #include "xparameters.h" #include "xgpio.h" #define LED_ID XPAR_AXI_GPIO_LED_DEVICE_ID #define SWITCH_ID XPAR_AXI_GPIO_SWITCH_DEVICE_ID #define FND_ID XPAR_AXI_GPIO_FND_DEVICE_ID #define LED_CHANNEL 1 #define SWITCH_CHANNEL 1 #define FND_COM_CHANNEL 1 #define FND_SEG7_CHANNEL 2 int main() { init_platform(); print("Start!\n\r"); XGpio_Config *cfg_ptr_led; // XGpio_Config 구조체의 주소 XGpio_Config *cfg_ptr_switch; // XGpio_Config 구조체의 주소 XGpio_Config *cfg_ptr_fnd; // XGpio_Config 구조체의 주소 XGpio led_device; // gpio 객체 XGpio switch_device; // gpio 객체 XGpio fnd_device; // gpio 객체 u32 data = 0; u8 fnd_value[] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 }; ////////////////////////////////////// Initialize Led Device ////////////////////////////////////// cfg_ptr_led = XGpio_LookupConfig(LED_ID); cfg_ptr_switch = XGpio_LookupConfig(SWITCH_ID); cfg_ptr_fnd = XGpio_LookupConfig(FND_ID); //-----------------------------------------------------------------------------------------------// XGpio_CfgInitialize(&led_device, cfg_ptr_led, cfg_ptr_led->BaseAddress); XGpio_CfgInitialize(&switch_device, cfg_ptr_switch, cfg_ptr_switch->BaseAddress); XGpio_CfgInitialize(&fnd_device, cfg_ptr_fnd, cfg_ptr_fnd->BaseAddress); // XGpio_CfgInitialize(InstancePtr, Config, EffectiveAddr) //-----------------------------------------------------------------------------------------------// XGpio_SetDataDirection(&led_device, LED_CHANNEL, 0); // 0을 줘서 출력 설정 XGpio_SetDataDirection(&switch_device, SWITCH_CHANNEL, 1); // 1을 줘서 입력 설정 XGpio_SetDataDirection(&fnd_device, FND_COM_CHANNEL, 0); // 0을 줘서 출력 설정 XGpio_SetDataDirection(&fnd_device, FND_SEG7_CHANNEL, 0); // 0을 줘서 출력 설정 // XGpio_SetDataDirection(InstancePtr, Channel, DirectionMask) /////////////////////////////////////////////////////////////////////////////////////////////////// while(1) { // print("Hello World\n\r"); // UART Test // led_data = XGpio_DiscreteRead(&switch_device, SWITCH_CHANNEL); // XGpio_DiscreteWrite(&led_device, LED_CHANNEL, led_data); // XGpio_DiscreteWrite(&fnd_device, FND_DISP_CHANNEL, fnd_data); data = XGpio_DiscreteRead(&switch_device, SWITCH_CHANNEL); XGpio_DiscreteWrite(&led_device, LED_CHANNEL, data); for(int i = 0;i<4;i++) { XGpio_DiscreteWrite(&fnd_device, FND_COM_CHANNEL, ~(1<<i)); XGpio_DiscreteWrite(&fnd_device, FND_SEG7_CHANNEL, ~fnd_value[(0xf & data>>(i*4))]); MB_Sleep(1); } } cleanup_platform(); return 0; }
