TRACE32 스크립트 활용한 디버깅 자동화

TRACE32
이동: 둘러보기, 검색

OVERVIEW

1. 소프트웨어 문제 재현

Q : 소프트웨어의 문제가 랜덤하게 발생합니다. 최초 실행 후, 3~4초 이내에 문제가 발생하지 않으면, 소프트웨어를 계속 사용이 가능합니다. 하지만, 문제가 발생하면, 그 이후에는 오동작을 합니다. 디버깅의 어려운 점은 문제 재현이 100~200회 수행할 때 한번 발생합니다. 이 경우 문제 재현을 자동화 할 수 있습니까?

 

A : 소프트웨어 디버깅을 하는 과정에서 가장 많은 시간을 소비하는 것이 바로 문제 재현입니다. 문제 재현이 되어야 실제 디버깅 작업을 수행할 수 있는데, 경우에 따라서 문제 재현이 잘 되지 않는 경우가 있습니다. 위의 사례처럼 문제를 재현하기 위해서 100~20회 반복적으로 소프트웨어를 수행/문제 재현 여부 확인 작업을 해야 하는 경우, 개발 효율을 매우 떨어지게 됩니다.

 

TRACE32는 위와 같은 상황을 위해 스크립트로 자동화가 가능합니다. 자동화를 통해 문제 재현 여부까지 확인할 수 있습니다.

 

먼저, 전원을 외부에서 제어하여 리셋 관리도 동시에 되는 경우와, TRACE32를 이용하여 리셋을 이용하는 경우의 스크립트입니다.

TRACE32를 이용하여 리셋하는 경우

외부에 전원 제어 장치가 있는 경우

system.resetout

Wait state.power()

 

System.resetout 스크립트는 반드시 TRACE32의 system이 ready 상태에이어야 합니다 TRACE32의 system 상태를 ready로 설정하는 방법은 다양한 방법이 존재합니다. 그 중 가장 쉬운 방법은 system.up이라는 스크립트를 이용하는 것입니다.

 

Wait state.power()는 타겟에 전원이 공급될 때까지 대기하는 스크립트입니다. 만일 전원이 꺼지는 것을 감지하길 원한다면, wait !state.power()를 이용하면 됩니다. 만일 전원이 정상적으로 공급되고 있는 상태라면, wait state.power()는 대기 없이 수행이 진행됩니다.

 

그런데, 외부의 전원 제어 장치의 동작을 감지하는 목적이라면, 먼저 wait !state.power()를 수행하고, wait state.power()를 이용해야 합니다. 이 순서의 스크립트를 이용하면 전원이 on/off되는 것을 감지할 수 있습니다.

 

위의 예제에 더불어, 실졔 사례에 해당하는 스크립트를 완성해보도록 하겠습니다. 예를 들어, 문제가 발생하면 특정 함수까지 수행하는 것이 불가능한 경우를 찾아내기 위한 스크립트를 작성해보도록 하겠습니다.

 

TRACE32를 이용하여 리셋하는 경우

외부에 전원 제어 장치가 있는 경우

System.cpu xxx-architecture

System.jc 10.Mhz

System.up

 

L1:

system.resetout

 

go my_function

wait !run()

 

goto L1

 

Enddo

System.cpu xxx-architecture

System.jc 10.Mhz

 

L1:

Wait !state.power()

Wait state.power()

 

System.up

 

go my_function

wait !run()

 

goto L1

 

Enddo

 

TRACE32를 이용하여 타겟을 reset하는 경우는 system.up을 1회만 수행합니다. 하지만, 외부 전원 제어 장치를 이용하는 경우는 전원이 공급될 때마다 system.up을 수행합니다. System.up을 수행한 후 전원이 지속적으로 공급되면 시스템은 ready상태가 유지됩니다. 이 경우 system.resetout을 수행하더라도 계속 유지가 되므로, 다시 system.up을 할 필요가 없습니다.

 

하지만, system.up을 수행한 후 전원이 끊어지면, system은 down 상태가 됩니다. 이 경우 전원이 공급된 후, 다시 system.up을 수행해야만 ready 상태가 됩니다.

 

위의 예제는 둘 모두, 타겟이 정상적으로 my_function까지 수행되면 타겟을 리셋시켜 처음부터 재실행합니다. my_function까지 수행되지 않으면, 무한히 대기상태로 동작합니다.

 

만일 my_function이 3초 이내에 수행될 수 있도록 코드가 작성되어 있다면, 다음과 같이 수정할 수 있습니다.

 

TRACE32를 이용하여 리셋하는 경우

외부에 전원 제어 장치가 있는 경우

System.cpu xxx-architecture

System.jc 10.Mhz

System.up

 

L1:

system.resetout

 

go my_function

wait 3s

 

if run()

(

   Beep

   Enddo

)

 

goto L1

 

Enddo

System.cpu xxx-architecture

System.jc 10.Mhz

 

L1:

Wait !state.power()

Wait state.power()

 

System.up

 

go my_function

wait 3s

 

if run()

(

   Beep

   Enddo

)

 

goto L1

 

Enddo

 

위 예제는 3초 대기후에도 타겟이 실행 중인 경우를 이상 현상이 발생한 것으로 판단하는 스크립입니다. 정상적인 수행을 한 경우, 3초 후에 타겟은 my_function에 정지한 상태가 되고, 다시 테스트를 수행합니다.

 

그러나 3초 대기후에 타겟이 계속 실행 중이라면, 소리를 발생시키고, 스크립트를 종료합니다.

 

 

 

2. 디버깅을 위한 데이터 수집

Q : 디버깅을 위해 데이터를 수집하고자 합니다. 제가 지금까지 데이터를 수집하는 방법은 디버깅 장비를 연결하여 타겟을 일정시간마다 정지시켜서 변수 값을 읽고 기록하였습니다. 문제는 이런 방식으로 데이터를 수집하려면 타겟을 정지시키는 것이 다른 문제를 일으키더군요. 예를 들면, 외부에서 입력된 신호를 처리하지 못해 소프트웨어가 오동작하는 사례가 발생합니다. 그래서 타겟을 정지하지 않고 데이터를 수집할 수 있는 방법이 필요합니다.

 

A : TRACE32를 이용하여 타겟을 정지시키지 않고 데이터를 수집하는 방법은 여러 가지 방법이 있습니다.

 

먼저, TRACE32-PowerTrace(이하 PowerTrace)를 사용하는 경우와 TRACE32-PowerDebug(이하 PowerDebug)를 사용하는 경우로 나뉩니다. PowerTrace를 이용하면 실시간 데이터가 변경되는 정보를 모두 추적이 가능합니다. PowerTrace는 마이크로 프로세서의 코어 동작을 실시간 관찰이 가능하므로, 이 기능을 활용하면 데이터의 변경과 참조를 완벽하게 확인이 가능합니다. 그러나, PowerTrace는 마이크로 프로세서가 TRACE 기능을 지원해야 활용이 가능합니다. 이 문서에서는 범용적으로 활용 가능한 PowerDebug를 이용하는 방법으로 설명하겠습니다.

 

PowerDebug는 타겟이 실행 중인 상태에서도 변수를 관찰할 수 있습니다. TRACE32-PowerView(이하 PowerView)의 명령 창에서 system.option.dualport on 명령을 이용하시면, 실행 중 변수 관찰이 가능한 환경을 구성할 수 있습니다. 단, PowerDebug를 이용하면, PowerTrace처럼 데이터가 변경되는 모든 순간을 확인할 수 없으며, JTAG 속도에 따라서 관찰되는 데이터가 샘플링되어 출력됩니다.

<그림 2-1> 타겟 실행 중, 데이터 관찰 기능 활성화 옵션

 

   

<그림 2-2> 같은 내용이지만, dualport 옵션이 활성화 된 경우(우)와 활성화 되지 않은 경우(좌)

 

다음은 데이터를 출력하는 방법으로 TRACE32의 기능을 이용하는 방법과 외부로 데이터를 출력하는 방법입니다. TRACE32의 내부 기능을 이용하는 방법은 제공되는 UI를 이용하여 출력하는 방법으로, 실행중인 타겟으로부터 데이터를 직접 출력합니다. var.profile 명령을 이용하면 데이터를 시간(x축) 대비 데이터 값의 크기(y축)으로 출력이 가능합니다. 데이터 수집 최대 속도는 10samples/s가 가능합니다.

 

<그림 2-3> var.profile 명령을 이용하여 데이터를 출력

 

이 방법은 TRACE32를 이용하여 간편하게 출력하여 관찰 가능합니다. 하지만, 다른 방법으로 분석하기 위해서는 외부 툴을 이용해야 하며, 이를 위해서는 데이터를 파일로 저장할 필요가 있습니다. 또, 위 방법은 sampling rate가 10Hz 이상의 더 빠른 속도로 sampling이 불가능합니다. 그래서 정밀한 분석이 필요한 환경에서는 활용하기 어려울 수 있습니다.

 

빠른 속도의 sampling과 외부에서 사용 가능한 데이터로 출력하는 방법은 Practice Script로 가능합니다. File 입출력은 open과 close를 이용해서 파일을 생성하고, write를 이용하여 텍스트를 출력 할 수 있습니다. 다음 예제는 TRACE32로 가장 빠르게 데이터를 수집하는 스크립트입니다.

 

local &count

// 지역 변수 선언

open #1 log.txt /create

// 파일 생성, 파일 명 log.txt

write #1 clock.time() "    " os.timer()

// clock.time() 현재 시간 출력

// os.timer() milliseconds 정보를 습득

 

on stop goto L_CLOSE_FILE

// 스크립트 종료 버튼을 누르면, L_CLOSE_FILE 레이블로 이동

 

while 1.==1.  // 무한 루프

(

           write #1 os.timer() "    " data.float("IEEE",die_temp))

        // 시간과 데이터 출력

)

L_CLOSE_FILE:

close #1

// 파일 닫기

Enddo

 

이 예제는 데이터를 지속적으로 저장하는 동작을 수행합니다. Sampling rate는 JTAG clock 속도, 스크립트를 실행하는 속도(Host PC 환경에 영향 받음)에 영향을 받습니다. 그렇기 때문에 Sampling Rate를 정확하게 설정은 어렵습니다. 테스트 환경(Host - win10 core-i7, Target - TC277TF 10Mhz JTAG clock)에서는 약 4000 Samples/s로 동작이 가능합니다. 앞에서 수행한 방식에 비해 매우 높은 속도로 데이터 수집이 가능합니다.

 

주의 1

Os.timer() 함수 : 해당 함수는 Host 운영체제의 영향을 받습니다. 그렇기 때문에 우리가 원하는 정확한 Milliseconds 단위의 시간이 출력되지 않습니다. 테스트 결과 윈도우에서는 15ms 단위로 시간이 출력됩니다. 예를 들어, os.timer()를 통해 현재 1초가 출력된 후에 계속 값을 지속적으로 읽을 경우 1.015초가 읽히기 전까지 1초로 출력됩니다.

 

만일 반드시 milliseconds 단위로 계산할 필요가 있다면, 15ms 단위로 읽힌 데이터 개수에 비례해서 시간을 계산하셔야 합니다. 예를 들어, 15ms 내에 데이터가 20개 읽혔다면, 각 데이터는 15/20=0.75(ms)만큼 time interval을 갖게 됩니다.

 

주의 2

Os.timer()는 Host 운영체제가 실행되는 시점에 타이머도 동작을 시작합니다. 그래서 현재 시간을 확인하기 위해서는 반드시 현재 시점의 시간과 milliseconds 단위 타이머의 tick값을 기록해둬야 합니다. 그래서, 예제 스크립트에서는 ‘write #1 clock.time() "    " os.timer()’ 코드를 이용하여 시간 정보를 기록하고 있습니다.