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