본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 94일차 - SoC Design(Block Design과 Vitis를 활용한 기초 기능의 구현)

by Graffitio 2023. 11. 17.
[Harman 세미콘 아카데미] 94일차 - SoC Design(Block Design과 Vitis를 활용한 기초 기능의 구현)
728x90
반응형
[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 파일에서 코드를 좀 수정해주자.

 

Build

 

 

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

 

 


 

728x90
반응형