MCU
STM32F746G DISCOVERY(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
HAL driver를 사용하여 UART각 Polling, Interrupt mode 입출력 Test(Receive Overrun 방지)

 

- UART Firmware 작성시 아래와 같이 크게 세가지 모드로 구분 할 수 있음

- 각 mode별로 입출력 Test

1. Polling

2. Interrupt

3. DMA

이번 페이지에서는 Polling, Interrupt mode 사용


1. STM32CubeMX : UART-Polling 설정

- Configuration은 STM32F746 DISCOVERY - printf()를 이용하여 UART로 문자열 출력 참고

 

- ATOLLIC : main.c

- 터미널에서 Keyboard 입력을 받아 다시 Terminal로 출력 코드 작성

- 필요한 변수 선언

#define RX_MAX    1

uint8_t rcv_data[RX_MAX];
 

- while() 작성

  while (1)
  {
	HAL_UART_Receive(&huart1, rcv_data, RX_MAX, 1000);

    if(rcv_data[0])   {
        printf("Rx OK = %c\r\n", rcv_data[0]);
        rcv_data[0] = 0x00;
    }

    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))   {
        printf("UART1 Overrun Error occurred.\r\n");
        __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
 

- 위와 같이 작성하였지만 Keyboard 입력이 빠르지 않기 때문에 Overrun Error가 발생하지 않는다.

때문에 HAL_Delay만 추가 해서 Test

  while (1)
  {
	HAL_Delay(50);
	HAL_UART_Receive(&huart1, rcv_data, RX_MAX, 1000);

    if(rcv_data[0])   {
        printf("Rx OK = %c\r\n", rcv_data[0]);
        rcv_data[0] = 0x00;
    }

    if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))   {
        printf("UART1 Overrun Error occurred.\r\n");
        __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
    }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
 

- Delay 하나로, Keyboard 입력이 쌓임 - 바로 처리 하지 못하고 Delay - Overrun Error 발생함을 확인


2-1. STM32CubeMX : UART-Interrupt(1) 설정

- Configuration은 STM32F746 DISCOVERY - printf()를 이용하여 UART로 문자열 출력 참고

- USART1 NVIC enable 할 것

 

- ATOLLIC : main.c

- 터미널에서 Keyboard 입력을 받아 다시 Terminal로 출력 코드 작성

- 필요한 변수 및 callback() 작성

#define RX_MAX    1

uint8_t rcv_data[RX_MAX];

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)  {
	if(huart->Instance == USART1)
	{
		HAL_UART_Receive_IT(&huart1, rcv_data, RX_MAX);
	}
}
 

- while() 작성

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_UART_Receive_IT(&huart1, rcv_data, RX_MAX);
  
  while (1)
  {
	  if(rcv_data[0])
	  {
		  printf("Rx OK = %c\r\n", rcv_data[0]);
		  rcv_data[0] = 0x00;
	  }
	  
	  if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))
	  {
		  printf("UART1 Overrun Error occurred.\r\n");
		  __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
	  }
	/* USER CODE END WHILE */
	
	/* USER CODE BEGIN 3 */
	}
 

- 위와 같이 작성하였지만 Keyboard 입력이 빠르지 않기 때문에 Overrun Error가 발생하지 않는다.

때문에 HAL_Delay만 추가 해서 Test

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_UART_Receive_IT(&huart1, rcv_data, RX_MAX);
  
  while (1)
  {
	  HAL_Delay(50);

	  if(rcv_data[0])
	  {
		  printf("Rx OK = %c\r\n", rcv_data[0]);
		  rcv_data[0] = 0x00;
	  }
	  
	  if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))
	  {
		  printf("UART1 Overrun Error occurred.\r\n");
		  __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
	  }
	/* USER CODE END WHILE */
	
	/* USER CODE BEGIN 3 */
	}
 

- HAL_UART_Receive_IT()를 사용하였음에도 Overrun Error 발생

 

Q. printf()가 시간이 오래 걸리는데 printf()를 사용해서 문제가 발생하지 않았나? Delay가 너무 길다.

A. printf()를 사용하지 않아 문제가 발생하지 않았다 하더라도, Polling 방식과 마찬가지로, 내가 컨트롤 할 수 없는 Delay가 발생할때 Overrun Error 발생될 수 있음(데이터가 들어오면 처리 해야 하는데, delay 때문에 처리는 못하고 데이터가 또 들어오면 Overrun)

 

Q. HAL_UART_Receive_IT 버퍼 사이즈를 키우면 되지 않나?

A. 수신 사이즈 만큼의 데이터가 들어와야 인터럽트 발생되며 사이즈를 무한정 키울수도 없음.

수신 사이즈를 1000으로 키웠는데 999개가 들어온다면? 1001개가 들어온다면?

 

아래에 Overrun Error를 방지하기 위한 예제를 설명합니다.


2-2. STM32CubeMX : UART-Interrupt(2) 설정

- Configuration은 STM32F746 DISCOVERY - printf()를 이용하여 UART로 문자열 출력 참고

- USART1 NVIC enable 할 것

 

- ATOLLIC : main.c

- 터미널에서 Keyboard 입력을 받아 다시 Terminal로 출력 코드 작성

- main.c : main() 수정

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  HAL_UART_Receive_IT(&huart1, rcv_data, RX_MAX);
  
  while (1)
  {
	  HAL_Delay(50);
	  
	  if(rcv_data[0])
	  {
  		  printf("Rx OK = %c\r\n", rcv_data[0]);
  		  rcv_data[0] = 0x00;
  	  }

  	  if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_ORE))
  	  {
  		  printf("UART1 Overrun Error occurred.\r\n");
  		  __HAL_UART_CLEAR_IT(&huart1,UART_CLEAR_OREF);
  	  }
  	/* USER CODE END WHILE */

  	/* USER CODE BEGIN 3 */
  }
 

- stm32f7xx_it.c : USART1_IRQHandler 생성됨

void USART1_IRQHandler(void)
{
	/* USER CODE BEGIN USART1_IRQn 0 */

	/* USER CODE END USART1_IRQn 0 */
	HAL_UART_IRQHandler(&huart1);
	/* USER CODE BEGIN USART1_IRQn 1 */

	/* USER CODE END USART1_IRQn 1 */
}
 

- stm32f7xx_it.c : USART1_IRQHandler 수정

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
	if((huart1.Instance->ISR & UART_FLAG_RXNE) != RESET)
	{
		rcv_data[0] =  huart1.Instance->RDR;
	}

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}
 

위와 같이 rx 데이터가 들어올 때 바로 읽어야 Overrun Error 발생 하지 않음

 

※ UART Interrupt mode 추가 설명(꼭 읽고 가면 좋은)

- HAL_UART_Receive_IT()를 호출하면 UART 수신 인터럽트에 관한 설정을 자동으로 해주며, UART 수신 인터럽트 대기 상태가 됨(while()이나 다른 Timer 인터럽트에서 호출 하면 데이터를 잃어 버릴수 있음)

 

- HAL_UART_Receive_IT()에서 데이터 size가 고정되어 있는것이 아니라면 한 바이트씩 수신 추천

 

- UART 수신 인터럽트가 발생(데이터가 들어오면)하면 USARTx_IRQHandler() 실행됨

 

2-1. STM32CubeMX : UART-Interrupt(1) 추가 설명

- USARTx_IRQHandler()HAL_UART_IQRHandler() 호출하여 인터럽트 관련 처리를 수행함

(STM32CubeMX에서 USART1 NVIC enable 하면 자동 생성됨)

 

- HAL_UART_IQRHandler()의 수신 인터럽트에서 HAL_UART_RxCpltCallback() 호출

 

- HAL_UART_RxCpltCallback()는 weak로 선언 되어 있으며 사용자가 원하는대로 작성, 마지막에 HAL_UART_Receive_IT()를 호출하여 다시 UART 수신 인터럽트 대기 상태로 만들 것

 

2-2. STM32CubeMX : UART-Interrupt(2) 추가 설명

- 위 USARTx_IRQHandler()의 huart1.Instance->RDR 넣는 어레이는 링버퍼로 작성 권장(데이터 잃어 버리고 싶지 않으면)

 

MCU
STM32F746G DISCOVERY(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
printf()를 이용하여 UART로 문자열 출력

 

- STM32F746G DISCOVERY에서는 Uart1과 Uart6을 이용 가능

 

- Uart1은 디버거인 STM103과 연결되어 디버거의 VCP(Virtual Com Port)를 이용하여 별도 장비 없이 Uart를 이용 가능

- Uart6은 Arduino D0/D1 핀과 연결

 

1. STM32F746 DISCOVERTY Uart1 관련 회로도

 

2. STM32CubeMX : UART-Polling 설정

- 'Pinout & Configuration' Tab으로 이동

1) Connectivity 선택

2) USART1 항목 선택

- Mode : Asynchronous, Synchronous 등

- Hardware Flow Control :Disable

- PB6(USART1_TX), PB7(USART1_RX)가 자동 할당(녹색 표시)됨

PB6(USART1_TX) → PA9(USART1_TX)로 변경

 

- Parameter Settings 확인

1) Baud Rate : 115200 Bits/s

2) Word Length : 8 Bits

3) Parity : None

4) Stop Bits : 1

 

- NVIC Settings 확인

1) NVIC 선택

2) USART1 global interrupt : Enabled에 체크

- GENERATE CODE 클릭

 

- STM32CubeMX Version 5.1.0에서 아래와 같은 에러가 뜨는데 그냥 무시하고 써도 된다는 의견이 있다.

Is there any way to disable GFXSimulator in CubeMx 5.1? For STM32F429 CubeMx often displays warnings that some simulation settings are incorrect even for projects which have nothing to do with graphics.

Cookie Notice Cookies and similar technologies enable us to provide you with an optimized user experience and functionality of our website. They also help us to monitor its performance and to make our advertising and marketing relevant to you. You have full control over which cookies are set by clic...

community.st.com

- 무시하고 진행


3. ATOLLIC 실행

- USART1_IRQHandler 생성됨

void USART1_IRQHandler(void)
{
	/* USER CODE BEGIN USART1_IRQn 0 */

	/* USER CODE END USART1_IRQn 0 */
	HAL_UART_IRQHandler(&huart1);
	/* USER CODE BEGIN USART1_IRQn 1 */

	/* USER CODE END USART1_IRQn 1 */
}
 

- 표준 출력 변경 : 표준 출력이 Terminal I/O에 연결 되어 있기 때문에 아래를 추가하여 표준 출력을 변경 해야 함

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
int _write(int FD, char *buffer, int len)
{
	HAL_UART_Transmit (&huart1, (uint8_t*)buffer, len, 500);
	return len;
}
/* USER CODE END 0 */
 

- printf() : 사용 하여 메시지 출력

  /* USER CODE BEGIN 2 */
  printf("STM32F746 DISCOVERTY printf() TEST.\r\n");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
 

 

- USART1은 디버거인 STM103과 연결되어 디버거의 VCP(Virtual Com Port)를 이용하여 별도 장비 없이 Uart를 이용할수 있다. 내컴퓨터 - 장치 관리자로 들어가 몇번 Com Port에 할당 되었는지 확인

- ATOLLIC - Debug로 들어가서 새 Terminal 시작

- 위에서 확인한 COM Port와 Setting값 설정

- Terminal에서 'STM32F746 DISCOVERTY printf() TEST.' 문구 확인 가능

 
MCU
STM32F746G DISCOVERY(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
HAL driver를 사용하여 GPIO에 Switch를 연결해 외부 인터럽트 모드로 입력을 받아 LED를 ON-OFF 한다.

 

1. STM32CubeMX : GPIO OUTPUT 설정

- 'Pinout & Configuration' Tab으로 이동

1) Pinout View 로 마우스 이동

2) PI1을 Mouse 왼쪽 버튼 클릭하여 GPIO_Output 선택

3) Mouse 오른쪽 버튼 클릭하여 Enter User Label 선택 : GPIO_Output → LED 으로 변경

 

- GPIO Configuration 확인

1) GPIO 선택

2) PI1 선택

3) GPIO output level : Low(LED ON), HIGH(LED OFF) ; 초기 상태

4) GPIO mode : Output Push Pull, Output Open Drain

5) Maximum output speed : Low, Medium, High

6) User Label : LED

2. STM32CubeMX : GPIO EXTI 설정

- 'Pinout & Configuration' Tab으로 이동

1) Pinout View 로 마우스 이동

2) PI11을 Mouse 왼쪽 버튼 클릭하여 GPIO_EXTI1 선택

3) Mouse 오른쪽 버튼 클릭하여 Enter User Label 선택 : GPIO_EXTI1 → SW1 으로 변경

- GPIO Configuration 확인

1) GPIO 선택

2) PI11 선택

3) GPIO mode : External Interrupt Mode with Rising edge trigger detection

※ Rising edge로 선택 하였으나 회로 구성에 따라 스위치를 누를때 or 뗄때 Interrupt 발생

4) GPIO Pull-up/Pull-down : No pull-up and No pull-down, Pull-up, Pull-down

5) User Label : SW1

 

- NVIC 설정

1) NVIC 선택

2) EXTI line1 interrupt : Enabled에 체크

- GENERATE CODE 클릭


3. ATOLLIC : main.c 수정

- SW1 입력이 들어올 때마다 LED toggle 되는 source code

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == SW1_Pin)
	{
		HAL_GPIO_TogglePin(LED_GREEN_GPIO_Port, LED_GREEN_Pin);
	}
}
/* USER CODE END 0 */
 

- 총 16개의 외부 GPIO source는 EXTI line 0부터 15까지로 제공

※ PXn 형태의 GPIO pin들 중 n이 중복되는 핀의 EXTI 동시 사용 불가

ex) PA0, PB0, PC0, … 등의 GPIO들은 모두 EXTI0 line에 연결되므로 중복 사용 불가

 

 

MCU
STM32F746G DISCOVERY(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
HAL driver를 사용하여 GPIO에 LED를 연결해 LED를 ON-OFF 한다.

 

1. STM32CubeMX : GPIO OUTPUT 설정

- 'Pinout & Configuration' Tab으로 이동

1) Pinout View 로 마우스 이동

2) PI1을 Mouse 왼쪽 버튼 클릭하여 GPIO_Output 선택

 

3) Mouse 오른쪽 버튼 클릭하여 Enter User Label 선택 : GPIO_Output → LED 으로 변경

 

- GPIO Configuration 확인

1) GPIO 선택

2) PI1 선택

3) GPIO output level : Low(LED ON), HIGH(LED OFF) ; 초기 상태

4) GPIO mode : Output Push Pull, Output Open Drain

5) Maximum output speed : Low, Medium, High

6) User Label : LED

 

2. STM32CubeMX : GPIO INPUT 설정

- 'Pinout & Configuration' Tab으로 이동

1) Pinout View 로 마우스 이동

2) PI11을 Mouse 왼쪽 버튼 클릭하여 GPIO_Input 선택

 

3) Mouse 오른쪽 버튼 클릭하여 Enter User Label 선택 : GPIO_Input → SW1 으로 변경

 

- GPIO Configuration 확인

1) GPIO 선택

2) PB1 선택

3) GPIO mode : Input mode

4) GPIO Pull-up/Pull-down : No pull-up and No pull-down, Pull-up, Pull-down

5) User Label : SW1

 

- GENERATE CODE 클릭


3. ATOLLIC 실행

 

- File - Import...

 

- STM32CubeMX에서 생성한 프로젝트 폴더를 선택 후 Finish

 

- ATOLLIC : main.c 수정

 

- main() : SW1 입력에 따라 LED toggle 되는 source code

while (1)
{
/* USER CODE END WHILE */
	if(HAL_GPIO_ReadPin(SW1_GPIO_Port, SW1_Pin) == 0)
	{
		HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
	}
	else
	{
		HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
	}
/* USER CODE BEGIN 3 */
}
 

 

 

MCU
STM32F746G DISCOVERY(ST)
IDE
Atollic(TrueSTUDIO), STM32CubeMX
목표
위 MCU와 IDE 환경에서 개발을 위한 MCU 기본 설정

 

이 STM32F746-DISCO 카테고리는 아래의 32F746GDISCOVERY Board로 테스트 하였다.

• STM32F746NGH6 Arm® Cortex® core-based microcontroller with 1 Mbyte of Flash memory and 340 Kbytes of RAM, in BGA216 package

• 4.3” RGB 480×272 color LCD-TFT with capacitive touch screen

• Ethernet compliant with IEEE-802.3-2002

• USB OTG HS

• USB OTG FS

• SAI audio codec

• Two ST-MEMS digital microphones

• 128-Mbit Quad-SPI Flash memory

• 128-Mbit SDRAM (64 Mbits accessible)

• Two user and reset push-buttons

• Board connectors:

– Camera

– microSD™ card

– RF-EEPROM daughterboard connector

– 2×USB with Micro-AB

– Ethernet RJ45

– SPDIF RCA input connector

– Audio line in and line out jack

– Stereo speaker outputs

– ARDUINO® Uno V3 expansion connectors

위와 같이 실장되어 있어서 여러가지 테스트 하기에 좋다.


1. STM32CubeMX 실행

 

- New Project

 

- Part Number Search : STM32F746NG

※ 스터디 목적이므로 DISCOVERY를 선택하지 말고, MCU를 선택하여 빈(peripheral이 초기화 되지 않은) 프로젝트로 시작

 

- Start Project

 

-Pinout 설정

- Peripheral Tree에서 설정

- Pin을 직접 설정

 

2. SYS(ST-LINK 사용을 위해서는 설정해야 함, ISP mode 사용시 필요 없으며 ISP mode 사용 방법은 다른 곳에서 설명)

- 'Pinout & Configuration' Tab으로 이동

1) System Core 선택

2) SYS 선택

- Debug : Serial Wire(SWD) 선택

- PA13(SWDIO), PA14(SWCKL)에 자동 할당(녹색으로 표시)

 

3. Clock Configuration(HSE) 설정

- 'Pinout & Configuration' Tab으로 이동

1) System Core 선택

2) RCC(Reset and Clock Control) 항목 선택

- High Speed Clock(HSE) : Crystal/Ceramic Resonator 선택

- PH0-OSC_IN(RCC_OSC_IN), PH1-OSC_OUT(RCC_OSC_OUT)자동 할당(녹색 표시 )

- 'Clock Configuration' Tab으로 이동

1) PLL Source Mux : HSE 선택

2) HCLK : 200(MHz) 입력(아래 참고)

 

4. Clock Configuration(LSE) 설정

- 'Pinout & Configuration' Tab으로 이동

1) System Core 선택

2) RCC(Reset and Clock Control) 항목 선택

- Low Speed Clock(LSE) : Crystal/Ceramic Resonator 선택

- PC14-OSC32_IN(RCC_OSC32_IN), PC15-OSC32_OUT(RCC_OSC32_IN)자동 할당(녹색 표시)

- 'Clock Configuration' Tab으로 이동

1) RTC Clock Mux : LSE 활성화 안됨

 

5. RTC 설정

- 'Pinout & Configuration' Tab으로 이동

1) Timers 선택

2) RTC(Real Time Clock) 항목 선택

- Activate Clock Source 체크

- 'Clock Configuration' Tab으로 이동

1) RTC Clock Mux : LSE 선택

- RTC 기능 설정 후 LSE 선택 가능

 

6. 'Project Manager' Tab

1) Project Name 입력

2) Toolchain / IDE 선택 : 여기서는 TrueSTUDIO선택

※ Toolchain / IDE를 TrueSTUDIO가 아닌 다른것으로 선택하고 GENERATE CODE를 한번이라도 하면

Toolchain을 TrueSTUDIO로 다시 선택해도 ATOLLIC에서 Build가 안되는 경우가 있으니,

위 같은 실수를 했을 경우 생성된 source를 지우고 다시 GENERATE CODE 할것을 권장함

3) GENARATE CODE 클릭

 

 

1. 정의

- QEMU는 다양한 CPU 구조와 시스템을 소프트웨어로 흉내 내는 오픈소스 가상화 도구

- 원래는 다른 종류의 CPU에서 만든 실행 파일을 실행하기 위해 개발

- 지금은 운영체제 부팅, 드라이버 개발, 보안 분석, 자동화 테스트 등에 널리 사용

- CLI 기반으로 동작하며, 스크립트 자동화에 적합

- 리눅스 환경에서 가장 강력하게 작동하며, 리눅스 개발자들이 많이 사용

- KVM, VirtIO, 가상 네트워크 등 고급 기능도 리눅스에서 잘 지원됨

- Windows나 macOS에서도 실행 가능하지만 일부 기능은 제한될 수 있음

- 커널, 부트로더, 루트 파일시스템을 가상으로 테스트하기에 유용

 

2. 동작 방식과 기술 기반

2-1. 운영 모드

모드
설명
예시
Full System Emulation
전체 가상 보드/시스템 에뮬레이션
ARM 리눅스 실행
User-mode Emulation
특정 유저모드 바이너리만 실행
ARM ELF 바이너리 실행
KVM 가상화 지원
x86 등의 경우 KVM 통해 하드웨어 가상화 사용 가능
빠른 리눅스 VM 실행

 

2-2. 핵심 기술

  • TCG (Tiny Code Generator) : CPU 명령어 실시간 변환 (JIT 기반)
  • Device Model : 가상 디스크, 네트워크, USB 등 구현
  • KVM 연동 : 하드웨어 지원 시 성능 ↑
  • GDB stub : 원격 디버깅 가능

 

3. 구성 구조 (개념도)

+--------------------------------+

 |    Host Linux/Windows    |

 |                                          |

 |  +--------------------------+  |

 |   |       QEMU Core        |   |

 |   |---------------------------|   |

 |   | CPU Emulator (TCG)|   |

 |   | Memory Emulator    |   |

 |   | Device Model           |   |

 |  +--------------------------+  |

 |                      |                    |

 |            (옵션) KVM            |

+--------------------------------+

 

4. 실제 사용 목적

목적
설명
OS 포팅 및 디버깅
라즈베리파이 없이도 커널 부팅 테스트
CI 자동화 테스트
QEMU로 리눅스 부팅/실행 확인
악성코드 분석
가상 환경에서 의심 바이너리 실행
보드 시뮬레이션
실제 보드가 없는 초기 개발 단계
고전 시스템 복원
레거시 OS (예: Win98, DOS 등) 실행

 

5. 사용하는 방식 (예시 포함)

# 예시: ARM 보드 시뮬레이션
qemu-system-arm -M virt -cpu cortex-a15 \
 -kernel zImage \
 -append "console=ttyAMA0 root=/dev/vda" \
 -drive file=rootfs.ext4,format=raw,if=virtio \
 -nographic
 
  • -M: 머신 타입 (virt, raspi2, vexpress-a9 등 다양)
  • -cpu: CPU 모델
  • -kernel: 커널 이미지 직접 지정
  • -drive: 디스크 이미지 연결
  • -nographic: 그래픽 창 없이 콘솔만 사용

 

6. 사용되는 프로젝트 규모

규모
예시
개인 개발
커널 포팅, 임베디드 테스트
중소기업
IoT 디바이스 펌웨어 테스트
대기업/클라우드
OpenStack, Android Emulator, CI/CD 환경
리눅스 배포판
Debian, Ubuntu, Fedora의 테스트 환경에서 사용

 

7. 사용 시나리오

7-1. 커널 및 OS 부팅 테스트

  • 목적: 임베디드 리눅스, Android, RTOS 등 운영체제 부팅 확인
  • 예시:

- ARM64 커널 + rootfs 부팅 (-M virt)

- U-Boot → 커널 → 루트FS 전체 부팅 순서 확인

  • 활용:

- 보드 없이도 OS 포팅 가능

- Init 과정/부팅 속도/로그 분석

 

7-2. 디바이스 드라이버 및 시스템콜 테스트

  • 목적: 커널 드라이버 또는 HAL 계층 개발 전 테스트
  • 예시:

- VirtIO 기반 블럭/네트워크 디바이스 확인

- 시리얼 포트, RTC, USB 에뮬레이션 테스트

  • 한계:

- GPIO, SPI, I2C 등 실제 하드웨어 연결은 불가

 

7-3. 펌웨어 / 부트로더 개발 & 검증

  • 목적: U-Boot, UEFI 등 부트코드 실험
  • 예시:

- qemu-system-aarch64 -bios QEMU_EFI.fd → EFI 테스트

- U-Boot 명령어, 환경 변수, 로딩 동작 확인

 

7-4. 커널 디버깅 / 크래시 분석

  • 목적: GDB 연동으로 실시간 문제 파악
  • 예시:
qemu-system-aarch64 ... -S -gdb tcp::1234
 

→ GDB 접속 후 커널 panic, early boot crash 원인 추적

  • 활용:

- 초기에 죽는 커널 디버깅

- Task 전환, 인터럽트 흐름 확인

 

7-5. 사용자 공간 프로그램 실행 (User-mode Emulation)

  • 목적: ARM, MIPS 등 외부 아키텍처용 바이너리 실행
  • 예시:
qemu-aarch64 ./my_program
 
  • 활용:

- 교차 컴파일된 앱 테스트

- 간단한 유틸리티 디버깅

 

7-6. 루트파일시스템/디스크 이미지 테스트

  • 목적: ext4, squashfs 등 이미지 부트 검증
  • 예시:
-drive file=rootfs.ext4,if=virtio
 
  • 활용:

- Buildroot, Yocto 루트FS 구조 점검

- 자동 마운트, init 실행 체크

 

7-7. 보안 분석 / 악성코드 실행

  • 목적: 격리된 환경에서 코드 실행 확인
  • 예시:

- 의심 ELF 바이너리 실행

- GDB, strace와 연동한 syscall 추적

  • 활용:

- 샌드박스 역할

- 리버스 엔지니어링 기초 환경

 

7-8. CI/CD 테스트 자동화

  • 목적: 빌드 후 자동화된 부팅 테스트
  • 예시:

- GitHub Actions에서 QEMU로 커널 부팅 성공 여부 체크

  • 활용:

- 커널/루트FS 변경 후 안정성 검증

- 로그 비교로 리그레션 테스트 수행

 

7-9. 가상 네트워크 및 다중 VM 시뮬레이션

  • 목적: 내부 통신, NAT, DHCP 등 테스트
  • 예시:

QEMU VM 여러 개 실행 + 가상 네트워크 브리지 연결

  • 활용:

IoT 게이트웨이 시뮬레이션

통신 모듈 테스트

 

8. 실무자라면 꼭 알아야 할 포인트

  • QEMU는 정확한 하드웨어 시뮬레이션은 아님

- register-level 정확도 필요하면 QEMU 보드 구현을 수정하거나 RenodeSystemC 사용 고려

 

  • 디바이스 모델은 보드에 따라 제한됨

- 예: raspi3는 USB 모델이 미완성일 수 있음

 

  • QEMU + GDB 디버깅 가능
qemu-system-arm ... -S -gdb tcp::1234
# 다른 터미널에서
arm-none-eabi-gdb vmlinux
(gdb) target remote :1234
 
  • 에뮬레이션 속도는 느릴 수 있음

- 단순 테스트/디버깅용으로 적합, 퍼포먼스 테스트는 실제 보드 필요

 

'리눅스(Linux) > 기본 지식' 카테고리의 다른 글

MachineWare  (0) 2025.04.18
리눅스(Linux) 기본 명령어(2) - cd  (0) 2025.03.18
리눅스(Linux) 기본 명령어(1)  (0) 2025.03.18
리눅스(Linux) 시스템 기본 구조  (0) 2025.03.18

 

1. MachineWare란?

- MachineWare는 SoC(System-on-Chip) 수준의 하드웨어 시뮬레이터

- 보드 없이도 SoC 내부 동작을 가상으로 구현하고, 그 위에서 실제 펌웨어, OS, 드라이버 코드를 실행·테스트 가능

  • CPU, 레지스터, 인터럽트, MMIO 장치 등을 가상화
  • QEMU보다 낮은 수준에서 더 정밀한 시뮬레이션 가능
  • 하드웨어가 없을 때도 개발이 가능함

 

2. 동작 방식과 기술 기반

항목
설명
기반 기술
SystemC + VCML (Virtual Component Modeling Library)
프로그램 언어
C++로 작성된 시뮬레이터
실행 환경
Linux 유저 공간 애플리케이션
지원 ISA
Arm, RISC-V, PowerPC 등
입력 바이너리
ELF 실행파일, 커널 이미지, U-Boot 등
디버깅 연동
GDB 서버 탑재 → 일반 GDB로 원격 디버깅 가능
디바이스 모델링
UART, SPI, GPIO, Interrupt Controller 등 포함 가능
실행 형태
커맨드라인 (GUI 없음), CI/CD 통합 용이

 

3. 구조도

┌───────────────────────────────┐

│ Linux Host (x86_64)                                                                            │

│ ┌─────────────────────────┐             │

│ │ MachineWare 시뮬레이터                                                │            │

│ │ ┌───────┐ ┌──────────┐             │            │

│ │ │ CPU                 │ │ UART 등                     │             │            │ ← 가상화된 SoC 구성

│ │ └───────┘ └──────────┘             │            │

│ └─────────────────────────┘             │

└───────────────────────────────┘

  • GDB로 연결 가능 (target remote :1234)
  • 로그, 시리얼 출력을 텍스트로 저장 가능

 

4. 실제 사용 목적

목적
설명
HW 없이 소프트웨어 개발 선행
RTL 또는 실보드 없이 U-Boot, Kernel, BSP 포팅 가능
자동화 테스트 환경 구성
병렬 시뮬레이터 실행으로 회귀 테스트 및 커버리지 확보
시연/데모용 플랫폼 제공
고객에게 물리 보드 없이 데모 가능
시스템 아키텍처 검증
SoC 설계 초기에 아키텍처 레벨 성능/기능 검토 가능
가상 ECU 구성 (자동차)
AUTOSAR 기반 ECU의 기능 단위 통합 검증 가능

 

5. 실제 사용하는 방식

시나리오 1 : 리눅스 BSP 포팅

./simulator \
  -kernel uImage \
  -dtb myboard.dtb \
  -initrd rootfs.cpio.gz \
  -serial stdio \
  -gdb-port 1234
 
  • 부트로더 → 커널 → 루트 파일시스템까지 실행됨
  • 커널 메시지는 가상 UART로 출력

 

시나리오 2 : 자동화 테스트

  • CI/CD 시스템에서 시뮬레이터 수십 개를 병렬로 실행
for i in {1..50}; do
  ./simulator -test test$i.json &
done
 
  • 테스트 조건, 디바이스 레지스터 초기값, 인터럽트 발생 타이밍 등을 json 파일로 제어

 

시나리오 3 : 초기 펌웨어 검증

#define UART0 (*(volatile uint32_t*)0x40011000)
UART0 = 'H';
UART0 = 'i';
 
  • 위 코드를 실행하면, 가상 UART 로그 파일에 "Hi" 출력됨
  • 실기기 UART 동작과 동일하게 동작 확인 가능

 

6. 사용되는 프로젝트 규모

규모
적합성
설명
소형 MCU 프로젝트
부적합
보드 저렴하고 개발 구조 단순함
중형 리눅스 SoC 프로젝트
적합
BSP 포팅, 드라이버 테스트 등
대규모 플랫폼/OS/드라이버 개발
매우 적합
하드웨어 미확정 시점, 자동화가 핵심
수십 명 이상 병렬 개발팀
매우 적합
병렬 테스트, 연속 통합 필수 환경

 

7. 다른 도구와 비교

솔루션
강점
단점
QEMU
빠른 부팅 속도, 무료, 폭넓은 OS 지원, 자동화 친화적
디바이스 모델링 정밀도 낮음, 커스터마이징 한계, 실보드와의 차이 큼
Simics (Intel)
Intel 기반 대형 시뮬레이터.
전체 시스템 수준 시뮬레이션, 복잡한 HW 모델링 가능, 고정밀
고가, 학습 곡선 큼, 개인·중소팀에 비효율
Renode (Antmicro)
IoT용 시뮬레이터.
IoT/MCU에 최적화, 경량, 오픈소스, CI 연계 쉬움
범용 SoC 모델링 한계, 대형 SoC 개발엔 부적합
OVPsim / Imperas
Cycle-accurate, 성능 분석에 최적, 상용 CPU 모델 보유
유연성 낮음, 학습 진입장벽 있음, 일부 ISA만 지원
Trace32 ISS
JTAG 기반 디버거. 실보드 없는 디버깅 가능, Trace32 사용자에 자연스러운 연계
시뮬레이션보단 디버깅 용도, 대규모 시뮬레이션엔 부적합
Synopsys Virtualizer / Cadence VDK
ASIC 수준의 정밀 시뮬레이션, RTL 연계 가능
매우 고가, 설정 복잡, 범용 SW 개발엔 과도
MachineWare
VCML 기반 정밀 SoC 시뮬레이터.
고성능 시뮬레이터, VCML 기반 유연한 HW 구성, CI 친화적
생태계 작음, 사용자 문서 부족, 진입 장벽 있음

 

8. 주요 사용 시나리오

조건
사용 권장 여부
보드가 아직 없음
권장
커널 포팅, 부트로더 개발 중
권장
CI 테스트 자동화가 필요함
적극 권장
임베디드 RTOS 단순 어플리케이션
권장하지 않음
JTAG으로 실시간 디버깅 필요
권장하지 않음 (Trace32이 적합)

 

9. 도입 시 고려사항

  • 시뮬레이터 설정/구축에는 VCML, SystemC 지식 필요
  • 보드가 없을 때만 쓰는 게 아님 — "자동화 기반 테스트/CI 통합"이 핵심
  • 하드웨어 정확한 사양 없이 시뮬레이터 구성하면 실기기와 차이날 수 있음

요약 정리

핵심 문장
설명
보드 없이도 실제 코드 실행
시뮬레이터 위에서 U-Boot, 커널, 펌웨어 실행 가능
자동화 테스트에 최적화
병렬 실행, 로그 분석, CI/CD 통합
소형 MCU 용도는 아님
SoC 기반 BSP/OS 레벨 개발용
JTAG 디버거와는 전혀 다름
실기기 디버깅은 Trace32가 적합
리눅스 기반에서 동작
호스트 OS는 Linux, 시뮬레이션 대상 OS는 자유

 

 

1. 개발 방향

- Multi-tasking 되는 MPU의 필요성

- MPU(Memory Protection unit)이 있는 MPU 필요함 : Cortex-A 가능

- Windows는 라이센스 비용 때문에 진입 장벽이 큼

- Linux는 개발 환경 구축이 어려움(그래도 어쩌겠어..)

- Raspberry Pi 수급 어려움

- NXP는 i.MX에서 Yocto Project 로 리눅스 cross compile 지원

- ST는 STM32MP1에서 Yocto Project 로 리눅스 cross compile 지원

 

→ STM32MP1 보드가 있어서 테스트 진행

아무것도 모르는 상태에서 보드 개발과 Linux 돌리는 것까지 얼마나 걸릴까? ㅜㅜ

 
  • MIPI 인터페이스가 있어 MIPI 카메라 연결
  • M4도 같이 있어 부가 회로 구성이 용이
  • 800MHz 속도는 아쉬움
  • LCD 해상도 높음

 

2. STM32MP157F-EV1

MPU with Arm Dual Cortex-A7 800 MHz, Arm Cortex-M4 real-time coprocessor, 3D GPU,

TFT/MIPI DSI displays, FD-CAN, Secure boot and Cryptography

STM32MP157F-EV1 - STMicroelectronics

STM32MP157F-EV1 - Evaluation board with STM32MP157F MPU, STM32MP157F-EV1, STMicroelectronics

www.st.com

STM32MP157F-EV1 특징

 
  • STM32MP157 Arm®-based dual Cortex®-A7 800 MHz 32 bits + Cortex®-M4 32 bits MPU
  • ST PMIC STPMIC1
  • 2 × 4-Gbit DDR3L, 16 bits, 533 MHz
  • 2 × 512-Mbit Quad-SPI Flash memory
  • 32-Gbit eMMC v5.0
  • 8-Gbit SLC NAND, 8 bits, 8-bit ECC, 4-KB PS
  • 1-Gbit/s Ethernet (RGMII) compliant with IEEE-802.3ab
  • USB Host 4-port hub
  • USB OTG HS
  • CAN FD
  • 5.5" TFT 720×1280 pixels with LED backlight, MIPI DSISM interface, and capacitive touch panel
  • SAI audio codec
  • 5-megapixel, 8-bit camera
  • 4 × ST-MEMS digital microphones
  • Smartcard
  • microSD™ card

 

3. Block Diagram

 

4. MCU & MPU

4-1. MCU(마이크로컨트롤러 유닛)

Arm®Cortex-M®와 같은 MMU-less 코어를 중심으로 구축되어 베어 메탈 또는 실시간 운영 체제(RTOS) 맥락에서 결정론적 작업에 매우 효율적. STMicroelectronics STM32 MCU는 많은 애플리케이션에 필요한 충분한 SRAM(정적 RAM)과 플래시 메모리를 내장하고 있으며, 이는 외부 메모리로 완료할 수 있음

 

4-2. MPU(마이크로프로세서 유닛)

Arm® Cortex-A®와 같은 코어에 의존하며, 메모리 관리 유닛(MMU)을 통해 가상 메모리 공간을 관리하므로 Linux®와 같은 풍부한 운영 체제(OS)를 효율적으로 지원 가능. 빠른 상호 연결을 통해 프로세싱 유닛, 고대역폭 주변 장치, 외부 메모리(RAM 및 NVM) 및 일반적으로 GPU(그래픽 프로세싱 유닛) 간의 가교 역할을 함

 

5. Evaluation Board

Evaluation Board는 아래 링크에서 참조

 

 

 
Target
Board
STM32MP157F-EV1(ST)
Host
PC - Ubuntu 22.04
목표
STM32MP1 개발 환경 구축 - hello-world application

아래 링크를 참조하여 진행 한다.

1. 준비

1-1. cross-compilation via SDK

- SDK 환경 설정 스크립트 실행

PC $ source /home/본인PC이름/STM/Developer-package/SDK/environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabi
 

1-2. Prepare kernel source code

- ST 패치

PC $ for p in `ls -1 ../*.patch`; do patch -p1 < $p; done
 

1-3. Configure kernel source code

- fragment 적용

PC $ make ARCH=arm multi_v7_defconfig "fragment*.config"
 
PC $ for f in `ls -1 ../fragment*.config`; do scripts/kconfig/merge_config.sh -m -r .config $f; done
 
PC $ yes '' | make ARCH=arm oldconfig
 

 

2. Compile kernel source code

2-1. Kernel images 빌드(시간 오래 걸림)

PC $ make ARCH=arm uImage vmlinux dtbs LOADADDR=0xC2000040
 

2-2. Kernel module 빌드(시간 오래 걸림)

PC $ make ARCH=arm modules
 

- Output build artifacts 생성

PC $ mkdir -p $PWD/install_artifact/
PC $ make ARCH=arm INSTALL_MOD_PATH="$PWD/../build/install_artifact" modules_install O="$PWD/../build"
 

 

3. Kernel 배포

3-1. Kernel을 보드에 올리기

PC $ scp arch/arm/boot/uImage root@<board ip address>:/boot
 

3-2. Devicetree를 보드에 올리기

PC $ rm install_artifact/lib/modules/5.15.67/build install_artifact/lib/modules/5.15.67/source
 

3-3. Kernel module 복사

PC $ scp -r install_artifact/lib/modules/* root@<ip of board>:/lib/modules
 

3-4. ttyACM에 붙이려면 ST-LINK/V2-1가 연결 되어 있어야 한다.

PC $ ls /dev/ttyACM*
 

3-5. minicom 을 /dev/ttyACM0에 연결

PC $ minicom -D /dev/ttyACM0
 

3-6. 리셋 버튼을 눌러 보드를 리셋하면 부팅 로그가 미니컴 창에 표시됨

3-7. ip 얻기

Board $ ip addr show eth0
 

3-8. modules.dep, modules.symbols 재생성

Board $ /sbin/depmod -a
 

3-8. 디스크 데이터를 메모리와 동기화

Board $ /sync
 

3-10. 보드 재시작

Board $ reboot
 

 

4. Kernel device driver 수정

4-1. Board에서 로그 정보 확인

Board $ dmesg | grep -i stm_drm_platform_probe
 

4-2. 아래 폴더의 drv.c 수정 하여 메시지 추가 하여 저장

/home/본인PC이름/STM/Developer-package/stm32mp1-openstlinux-5.15-yocto-kirkstone-mp1-v22.11.23/sources/arm-ostl-linux-gnueabi/linux-stm32mp-5.15.67-stm32mp-r2-r0/linux-5.15.67/drivers/gpu/stm/

 

static int stm_drm_platform_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct drm_device *ddev;
	int ret;
	[...]

	DRM_INFO("Simple example - %s\n", __func__);

	return 0;
	[...]
}
 

4-3. Kernel 다시 빌드

PC $ make uImage LOADADDR=0xC2000040
 

4-4. Kernel을 보드에 올리기

PC $ scp arch/arm/boot/uImage root@172.xx.xx.xx:/boot
 

4-5. 보드 재시작

Board $ reboot
 

4-6. Board에서 로그 정보 확인

Board $ dmesg | grep -i stm_drm_platform_probe
 
 

 

 

Target
Board
STM32MP157F-EV1(ST)
Host
PC - Ubuntu 22.04
목표
STM32MP1 개발 환경 구축 - hello-world application

 


아래 링크를 참조하여 진행 한다.

1. 폴더 만들기

PC $ mkdir /home/본인PC이름/STM/Developer-package/stm32mp1-openstlinux-5.15-yocto-kirkstone-mp1-v22.11.23/sources/gtk_hello_world_example
 

 

2. 폴더로 이동

PC $ cd /home/본인PC이름/STM/Developer-package/stm32mp1-openstlinux-5.15-yocto-kirkstone-mp1-v22.11.23/sources/gtk_hello_world_example
 

 

3. gtk_hello_world.c 생성

#include <gtk/gtk.h>

static void
print_hello (GtkWidget *widget,
             gpointer   data)
{
  g_print ("Hello World\n");
}

static void
activate (GtkApplication *app,
          gpointer        user_data)
{
  GtkWidget *window;
  GtkWidget *button;
  GtkWidget *button_box;

  window = gtk_application_window_new (app);
  gtk_window_set_title (GTK_WINDOW (window), "Window");
  gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

  button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
  gtk_container_add (GTK_CONTAINER (window), button_box);

  button = gtk_button_new_with_label ("Hello World");
  g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
  g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
  gtk_container_add (GTK_CONTAINER (button_box), button);

  gtk_widget_show_all (window);
}

int
main (int    argc,
      char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}
 

 

4. Makefile 생성

PROG = gtk_hello_world
SRCS = gtk_hello_world.c

CLEANFILES = $(PROG)

# Add / change option in CFLAGS and LDFLAGS
CFLAGS += -Wall $(shell pkg-config --cflags gtk+-3.0)
LDFLAGS += $(shell pkg-config --libs gtk+-3.0)

all: $(PROG)

$(PROG): $(SRCS)
	$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)

clean:
	rm -f $(CLEANFILES) $(patsubst %.c,%.o, $(SRCS))
 

 

5. cross-compile

PC $ make
 

에러나서 구글 검색해 보니 gtk3을 설치하라고 한다.

PC $ sudo apt-get install libgtk-3-dev
 

설치 후 다시 compile

 

6. 실행

보드로 실행 파일 업로드

PC $ scp gtk_hello_world root@172.10.6.98:/usr/local
 

weston으로 실행

Board $ cd /usr/local/
Board $ su -l weston -c "/usr/local/gtk_hello_world" 
 

 

+ Recent posts