jtag_debugging - withrobot/myCortex-STM32F4 GitHub Wiki

개요

JTAG을 사용했을 때 얻을 수 있는 최대의 이점은 바로 소스레벨 디버깅입니다. PC에서 Visual Studio를 이용해 프로그램을 개발할 때 디버깅을 해 보셨다면 소스레벨 디버깅의 강력함을 쉽게 이해하실 수 있을 것입니다. 적절한 디버깅 수단도 없이 printf() 함수나 GPIO 한두개로 디버깅하면서 고생한 경험, 임베디드 개발자라면 누구나 가지고 있을 것입니다. JTAG이 있다면 그 고생을 안해도 됩니다!

myCortex-STM32F4 예제를 대상으로 JTAG 소스레벨 디버깅 방법을 설명드리겠습니다.

디버깅 기초

당연한 이야기입니다만, 디버깅을 하려면 해당 프로젝트를 EWARM에서 open 해야 합니다. File 메뉴의 [Open]->[Workspace]를 차례대로 선택하여 ex02.1_LED 폴더의 EWARM.eww 파일을 open 합니다.

디버깅을 위해 프로젝트를 빌드합니다. 단축키 F7을 누르거나 메뉴에서 Project->Make를 선택합니다.

임베디드 환경에서 디버깅을 위해서는 보드에 우선 펌웨어를 다운로드 해야 합니다(시뮬레이터를 사용하는 방법도 있지만 실제 보드를 조금 흉내내는 수준에 그쳐 거의 사용되지 않습니다)

EWARM에서는 단축키 Ctrl+D를 누르거나 메뉴에서 Project->Download and Debug를 선택함으로써 다운로딩과 디버깅 시작을 동시에 할 수 있습니다.

debug 1

몇번의 진행 윈도가 잠깐씩 나타났다 사라진 다음 위 화면과 같이 main() 함수가 녹색으로 바뀐 상태로 멈춰있습니다. CPU 코어의 클럭이 정지되면서 실행 역시 일시 정지 상태입니다. 코드에서 녹색으로 표시된 부분은 "이번에 실행할 대상 코드"를 의미합니다. 이 상태에서 단축키 F10 혹은 메뉴의 Debug->Step over를 선택하면 소스 코드 한줄이 실행되며 화면은 아래와 같이 바뀝니다.

debug 2

LED_Init() 함수가 녹색으로 바뀌었습니다.

Step over는 코드 한줄을 실행하라는 의미입니다. 만일 이 한줄이 함수 호출인 경우라면 그 함수 전체를 한줄로 취급합니다. 다음번 실행 대상인 LED_Init()은 함수입니다. 이 상태에서 step over를 하게 되면 LED_Init() 함수를 모두 다 실행하고 다음 줄인 LED_R_ON()이 녹색으로 될 것입니다.

반면 Step into는 함수 호출을 만났을 때 그 함수 내부로 따라 들어갑니다. LED_Init()을 실행할 순서에서 Step into를 해 보겠습니다. 단축키는 F11 입니다.

debug 3

main.c 파일만 열려있었지만 자동으로 led.c 파일이 열리면서 LED_Init() 함수의 첫 번째 라인이 녹색으로 표시됩니다. 이처럼 Step into는 호출하는 함수 내부까지 따라들어가는 기능입니다. 반면 Step out(단축키 Shift + F11)은 현재 함수의 끝까지 실행하고 함수를 호출한 곳으로 되돌아가는 기능입니다.

Shift+F11을 눌러 LED_Init() 함수의 실행을 끝까지 하고 나면 아래와 같이 next cursor가 표시됩니다.

debug 4

LED_R_ON()이 실행될 차례입니다. 이 시점에 myCortex-STM32F4 보드의 LED는 두개 모두 꺼져있습니다. 이제 F10을 눌러 14 라인을 실행하도록 하겠습니다.

debug 5

이때 보드를 확인해 보면 적색 LED가 켜져 있는 것을 확인할 수 있습니다. 마찬가지로 15 라인에서도 F10을 누르면 그순간 보드의 녹색 LED가 켜집니다.

디버깅의 종료는 단축키 Ctrl+Shift+D 혹은 Debug->Stp[ Debugging 메뉴입니다. 디버깅을 하고 나면 해당 펌웨어 이미지가 플래쉬 메모리에 다운로드 되면서 플래쉬 메모리를 덮어쓴다는 점도 염두에 둬야 합니다.

Breakpoint와 변수값 확인

앞장에서와 같이 Step 기능을 이용하면 소스를 한줄씩 실행해서 살펴볼 수 있습니다. 한편 펌웨어 소스 전체 중 자신이 관심있는 부분만 보기 위해서는 step 기능으로 한줄씩 실행해 나가다가는 시간낭비가 심할 것입니다. 이런 경우를 위해 Breakpoint 기능이 있습니다.

소스의 원하는 곳에 breakpoint를 설정하고 MCU를 run 시키면 breakpoint가 설정된 곳이 실행되기 직전에 실행을 멈춥니다. 그때부터는 다시 Step over, step into, step out 등으로 한줄식 실행해 볼 수 있습니다.

Breakpoint를 설정하고 싶은 소스코드 라인에 커서를 위치시킨 후 단축키 F9를 누르면 붉은 원이 표시되면서 breakpoint가 설정됩니다.

ex07.1_ADC_1ch 예제를 통해 살펴보도록 하겠습니다. 디버깅 과정을 시연하기 위해 우선 컴파일러 최적화 옵션을 끄고 빌드하도록 하겠습니다. 최적화 옵션이 켜져 있으면 최적화를 통해 일부 코드는 실행되지 않거나 실행 순서가 바뀌는 경우도 있어 처음 접하는 사람에게는 어렵게 느껴질 수 있습니다.

단축키 Alt+F7 혹은 메뉴에서 Project->Options를 선택한 다음 아래 화면과 같이 최적화 옵션을 None으로 설정하고 Ok를 선택합니다.

optimization

이제 Ctrl+D 단축키를 눌러 디버깅을 시작합니다.

debug 6

main()에서 실행을 대기중입니다. 이때 main.c 파일의 88라인으로 커서를 이동시킨 다음 단축키 F9를 눌러 breakpoint를 설정합니다.

debug 7

위와 같이 코드가 붉은색으로 표시됩니다. 이 상태에서 단축키 F5를 눌러 run 시킵니다. 일시정지 상태에서 run 상태로 되면서 main() 함수의 소스코드들이 실행됩니다. 그러다가 breakpoint가 실행될 차례가 되면 자동으로 다시 일시정지 상태가 되어 아래와 같이 화면에 표시됩니다.

debug 8

ADC_GetConversionValue() 함수가 실행될 차례입니다. 이 상태에서 우측에 있는 Disassembly 창을 닫고 메뉴에서 View->Locals를 선택합니다. 그러면 아래와 같이 지역 변수의 상태를 볼 수 있는 창이 나타납니다.

debug 9

이 화면을 보면 변수 adc_result의 값이 0이라는 것을 볼 수 있습니다. 이때 F10을 눌러 한줄 step over 하면 아래와 같이 화면이 바뀝니다.

debug 10

한줄 진행되면서 adc_result 변수의 값이 2815로 바뀌었습니다. 이 숫자는 ADC 채널 1에 인가한 전압을 ADC해서 얻은 결과입니다. 실험하는 환경에 따라 다른 값이 출력될 수 있습니다. 이처럼 방금 step에서 변화한 변수 값은 붉은색으로 표시됩니다.

한번 더 step over를 해서 volt를 계산해 보겠습니다.

debug 11

소스코드는 한줄 더 진행되었지만 volt 변수 값은 변화없이 계속 상태입니다. 이는 volt 변수는 float 형태이고, 지금 FPU가 동작하는 중이어서 일반적인 디버깅으로는 이 값을 확인할 수 없기 때문입니다.

한번 더 step over하면 resistor 변수의 값이 바뀝니다.

debug 12

이처럼 step 기능과 breakpoint 기능을 사용해 자유자제로 자신이 보고싶은 코드를 실행시킬 수 있으며, 동시에 변수 값을 확인할 수 있습니다. 변수 뿐만 아니라 특정 memory 영역을 보거나 레지스터의 값을 확인할 수도 있습니다.

앞에서 최적화 옵션을 None으로 바꾸고 디버깅을 했었는데, 다시 원래 상태로 되돌린 다음 디버깅을 한번 해 보시면 조금 다른 실행 모습을 볼 수 있습니다.