태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.


◆ 목적

PLL와 와치독을 조작해보고, 이를 통해 시스템 초기화에 대한 감을 잡아 본다.

 

◆ 참고

싱크웍스( www.tms320.co.kr ), 28x CPU 핸드북(백종철 저), TMS320F28xx 메뉴얼 2

 

◆ 준비

PC, EVM 보드, 280x 계열의 DSP, CCS 2.1이상, JTAG 에뮬레이터( TDS510USB 권장 ), 점퍼선 8, 정전기 방지 손목띠

 

◆ 예제 파일

02_Initialize_280x.zip (싱크웍스 홈페이지 활용예제 게시판 참조)




이번에는 PLL과 와치독에 대해 알아볼 예정입니다. 이 두 주변회로는 DSP 시스템의 초기화와 관련된 아주 중요하고 기본적인 회로입니다. 각 회로의 구조나 레지스터 분석 등을 하나씩 해 나가면서 각 부분에 대해 하나씩 자세하게 알아보도록 합니다. 지금부터 설명의 용이함을 위하여 어투를 조금 편하게 하도록 하겠습니다. 이해해 주시리라 믿습니다.



PLL이란?

PLL이란 ‘Phase Lock Loop’의 약자이다. 이를 우리말로 번역하자면 위상 고정 회로정도로 해석할 수 있겠다. 현재 우리가 사용하고 있는 모든 프로세서들은 클럭을 기준으로 동작하게 되어 있다. 따라서 프로세서 동작에 있어서 클럭의 품질은 상당히 중요한 부분이 된다. PLL은 이러한 클럭의 위상을 말 그대로 고정해서 흔들리지 않게 하는 역할을 한다. 이것은 클럭의 위상정보를 명확히 하여 클럭의 품질을 높여주는 기능을 하게 되는 것이다.

 
그리고 여기에 플러스 기능이 하나 더 있다. 그것은 클럭의 입출력 비율을 조절하는 기능을 말한다. 프로세서의 성능이 향상됨에 따라, 프로세서에 인가되는 클럭 스피드는 점점 높아지게 되는데, 클럭을 발생시키는 오실레이터( 이하 OSC )나 크리스털 같은 부품들은 높은 클럭을 발생시키는 것 일수록 값이 비싸지고, EMI문제를 발생시킨다. 또한 이런 부품들은 구조적으로 고품질의 높은 클럭을 발생시키는데 그 한계가 존재한다. 이런 상황에서 PLL은 입출력 조절 기능을 통해 낮은 주파수의 클럭을 몇 배의 높은 클럭 주파수로 바꿔주는 역할까지 한다. 클럭의 입출력 비도 조절하고 클럭 품질까지 높여주니 이런 금상첨화가 따로 없다.

 
TMS320F28x DSP에 있는 PLL 회로는 최저 0.5배에서 최대 5배까지 그 입출력비가 조절 된다. 이런 상황에서 281x DSP에는 30MHz짜리 OSC, 280x DSP에는 20MHz짜리 OSC가 장착되어 있다. 따라서 각 칩의 프로세서에 공급되는 클럭은 281x 계열이 최저 15MHz에서 최대 150MHz, 280x 계열은 최저 10MHz에서 최대 100MHz까지이다. PLL에 관한 좀 더 자세한 사항은 예제를 통해 본격적으로 알아 볼 예정이다.



WatchDog( 와치독 )이란?
TMS320C2000 계열은 모터 제어와 같은 높은 안정성과 신뢰성을 요구하는 분야에 많이 적용된다. 엘리베이터 오작동 기사, 자동차 급 발진 등등, 이러한 안전사고를 최대한 막기 위해서는 칩 자체에도 많은 안정성과 신뢰성이 요구된다. 따라서 DSP에는 칩 자체의 안정성을 요구하는 요소들이 많이 내재되어 있는데, 그 중 대표적인 안전장치가 바로 와치독이 되겠다.

 
WatchDog Timer를 풀어보자면, WatchDog = 경비견, Timer = 시간 관련 카운터 레지스터라 볼 수 있겠다. 경비견 시간 관련 카운터 레지스터??? 무슨 말인지 감이 안 온다. 조금 더 살펴보자. 우선 경비견 이라 하면 무엇인가를 감시하는 것이라 생각할 수 있다. 그렇다면 경비견이 감시 하는 것은 무엇일까? 빙빙 돌려 말하는 것 같아 죄송스럽다. 이 경비견이 감시하는 부분은 바로 TMS320C2000 CPU. 이 경비견은 CPU를 감시하다가CPU가 적절치 못한 동작(오동작)을 하게 되면, 경비견은 냉정하게 임무를 수행한다. 바로 CPU를 물어버리는 것이다. 물려버린 CPU는 리셋 되게 된다. 아마 개한테 물리면 정신이 번쩍 드는 것과 비슷한 이치가 아닐까?


CPU
가 경비견에 물리지 않기 위해서는 적절한 동작이 필요하다. 무엇일까? 간단하게 생각해보면 알 수 있는데, 우리가 경비견에게 물리지 않기 위해 경비견에게 무언가 먹을 것을 던져 주는 것과 마찬가지로 DSP 역시 와치독에 의해 리셋 되지 않기 위해서는 적절한 시간 안에 와치독에게 밥(?)을 던져 주면 되는 것이다. 여기에서 적절한 시간을 계산하기 위해 Timer라는 카운터 레지스터가 필요하다. 정해진 시간 안에, CPU가 유효한 동작(밥 주기)을 하지 않는다면, WatchDog Timer는 넘치게 되고, 와치독 회로는 CPU를 리셋 시켜 버린다.


밥 안준다고 CPU를 리셋 시켜버리는 것이 안정성과 어떤 연관이 있는지 쉽게 감이 오지 않는 분들을 위해, 예를 하나 들어 보도록 하겠다. 28x DSP로 엘리베이터의 모터를 제어하고 있는 상황을 가정해보자. 평소에 엘리베이터는 안전하게 동작할 것이다. 그러나 어떤 이유로 인해( 날씨가 너무 더워 칩 주변 온도가 높아졌다 던지, 번개가 쳐서 전기가 흘러 들어온다던 지.. ) DSP에 원인 모를 오류가 발생했다손 치자. DSP는 엘리베이터가 멈춰야 할 곳도 모르고 계속해서 모터를 움직이게 할 수 있다. 이런 상황은 생각만 해도 끔찍한 결과를 연출하게 된다. 그러나 이 엘리베이터를 제어하는 DSP에 와치독이 활성화 되어 있다고 다시 생각해보자. DSP에 아무런 이상이 없을 때에는 위와 마찬가지로 엘리베이터 제어도 잘하고 와치독에 적절한 밥도 계속 제공할 것이다. 그러나 아까같이 DSP에 원인 모를 오류가 생겼다면 이번에는 어떨까? 물론 DSP에 이상이 생겼다고 반드시 와치독에게 밥을 못 주는 것은 아니지만, 그래도 이러할 가능성은 크다. 이상이 있는 DSP는 와치독에게 적절한 식사를 제공하지 못하고 화가 난(?) 와치독은 DSP를 확 물어버린다. 물려버린 DSP는 바들바들 떨면서 모든 움직임을 멈출 것이다. 곧 엘리베이터는 그 자리에 멈추게 된다.


현재 독자 분들이 멈춰버린 엘리베이터 안에 있다고 생각해보자. 와치독이 얼마나 유용하고 고마운 존재인지 느껴지시는가? 앞에서는 와치독 회로를 경비견으로 비유했다. 하는 일도 이름도 비슷비슷하기 때문인데, 실제 회로 구조는 어떤 모양일까? 회로 구조까지 얼추 개랑 비슷했다면, 정말 대단한 회로였겠지만 애석하게도(?) 그렇지는 않다. 회로 구조가 어떠한지, 또 매번 밥을 준다던데 그 밥은 무엇인지 다음에 올 예제를 통해 하나씩 알아보도록 하자.




1. EVM
의 회로를 다음과 같이 결선하십시오.

 


[LED]

PWM1 ~ J5100 1

PWM2 ~ J5100 2

PWM3 ~ J5100 3

PWM4 ~ J5100 4

PWM5 ~ J5100 5

PWM6 ~ J5100 6

PWM7 ~ J5100 7

PWM8 ~ J5100 8




2. "02_Initialize.pjt"
를 빌드하고, 예제 동작을 확인하십시오.

동작을 확인하면, 다음은 소스의 수정을 통해 PLL과 와치독에 대해서 몸소 체험 할 수 있어야 한다. 그러나 현재 우리는 예제 소스를 수정할 정도의 여력이 안 된다. 아직 배우지 않아서 그런 것이므로, 하나씩 차근히 따라가 보자.

 

 

레지스터 분석


PLL

PLL회로는 위상을 고정시켜, 클럭을 품질을 높여주고, 동시에 클럭의 입출력 비를 조절한다고 했었다. 여기서 개발자가 조절할 수 있는 내용은 클럭의 입출력 비율 조절이다. 해당 레지스터를 살펴보자.

 


PLL
컨트롤 레지스터의 구조이다. 대개 레지스터 구조가 이와 비슷하게 생겼는데, 이미 익숙하신 분들께서도 계시고 처음 접해본 분들께서도 계실 것이다. 처음 보는 분들을 배려해 레지스터 구조에 대해 전반적인 설명을 약간 하려 한다. 위 레지스터를 표본으로 설명하도록 하겠다. 먼저 총 16bit로 되어 있으며, 앞의 12bit ‘Reserved‘ 되어 있다. Reserved 영역은예약됐음정도로 해석하면 되는데, 이는 Ti가 예약해 놓은 영역으로 개발자가 이 영역에 접근해봐야 아무런 의미가 없다는 것을 뜻한다. Reserved 영역은 개발자가 신경 쓰지 않아도 된다. 레지스터의 각 영역 밑에는 'R', 'W', ’-x' 이런 표시들이 있는데 이는 해당 레지스터 영역의 특징을 간단히 설명한 것이다.

 
‘R’이라 쓰여 있으면 이는 이 영역에 있는 값을 개발자가 읽어오는 것이 가능하다는 뜻이다. ‘R'과는 반대로 ’W'는 개발자가 해당 영역에 값을 써 넣을 수 있다는 것을 뜻한다. 마지막으로 ‘-x' 표시는 DSP를 리셋 시킨 다음 각 레지스터에 들어가 있는 초기 값을 뜻한다. 이 내용들을 종합해서 예를 들어 보면 위의 레지스터에서 Reserved 영역은 읽기 가능하고 리셋 후 초기 값은’0‘이다. 그리고 DIV 영역은 읽고 쓰기가 가능하고 리셋 후 초기 값은 역시’0‘이다. 위의 내용들은 아주 간단하며, 모든 레지스터 구조에 공통적으로 해당하는 부분만을 설명한 것이다. 추가로 Protected 영역이라는 단어가 보이는데, 이는 간단히 설명하자면 몇몇 조작에 있어서 조심성을 기하는 레지스터들은보호처리가 되어 있는데 이를 나타내는 것이다. 이 부분에 대해서는 나중에 가서 좀 더 다루도록 하겠다.

 
이제 본론으로 돌아와 PLLCR 레지스터에서 하는 일을 알아보도록 하자.

 


위 그림은 PLLCR 레지스터의 각 필드를 명확하게 설명해 놓은 그림이다. PLLCR의 앞의 12bit( 15~4 ) Reserved 되어 있다. 앞에서 언급했듯이 신경 쓰지 않아도 된다. 다음 4bit( 3~0 ) DIV라는 이름의 영역으로 클럭의 입출력 비율을 조정하는 일을 담당한다. 그 내용을 보자. 위의 표를 유심히 보면 CLKIN이란 값은 OSCCLK의 값에 DIV 영역에 쓰여 져 있는 값을 곱한 다음 2분주하여 얻어지게 되어 있다. 여기서 OSCCLK PLL회로의 입력 클럭으로 DSP에 장착되어 있는 오실레이터에서 발생하는 클럭이다. 그리고 CLKIN PLL회로의 출력 클럭으로 이 클럭은 DSP CPU에 전달된다. 여기서 클럭 시스템에 관한 내용이 나오는데 이는 추후에 다루기로 하고, 다시 PLL의 클럭 비율 조절을 보자.

만약 DIV 10( = 16진수 0xA, 2진수 1010 )을 써 넣으면 PLL회로의 출력은 입력 클럭의 10배 후, 다시 2분주하여 결국 입력 OSCCLK 5배 한 클럭이 출력으로 나가게 된다. 만약 DIV 5를 써 넣으면 PLL 출력 클럭은 입력 클럭의 2.5배가 된다. 이를 식으로 정리하면 다음과 같다.


PLL 출력 = (PLL 입력(오실레이터 발생 클럭)*DIV )/2


마지막으로 DIV 영역에 ‘0’을 써 넣으면 ‘PLL 바이패스라는 말이 있다. PLL 바이패스가 뭘까? PLL 바이패스는 PLL 회로 블록으로 입력 클럭이 들어가지만 PLL을 거치지 않고 다른 경로로 클럭이 지나가도록 하는 것이다. 그런데 이 경우에 DIV 영역에 ‘1’을 써 넣을 때와 CLKIN이 같다. 도통 무슨 말인지 잘 모르겠다면 PLL 회로 블록을 통해 좀 더 명확하게 확인해 보도록 하자.

 

 281x OSC & PLL Block


 280x OSC & PLL Block


위 그림은 OSC PLL 블럭을 나타낸 것이다. 여기서 네모점선 쳐진 영역이 PLL 회로의 블록인데, 우리의 주 관심사가 되겠다. 오실레이터(OSC)에서 발생하는 클럭은 PLL 회로 블록을 사용하거나 혹은 사용하지 않거나 해서 CPU에 공급된다. 위에서 언급한 것과 같이 281x XF_XPLLDIS 핀을 통해 PLL의 사용 유무를 결정할 수 있고, 280x는 위의 기능이 없는 대신에 PLL Locked 상태를 알려주는 비트가 추가되어 있다. 따라서 281x에서는 PLL의 정상화까지 사용자가 소프트웨어적으로 지연을 제공해야 했고, 불안정한 상태가 발생할 수 있었지만 280x는 이를 알려주는 비트가 있으므로 이 비트가 Locked를 알려줄 때까지 공회전을 하면 된다.

또한 280x OSC PLL의 전원공급을 조절할 수 있는 비트를 가지고 있어서 클럭분실회로의 테스트 등이 손쉽게 가능하다.

 

아래의 표는 PLLSTS 레지스터의 각 비트별 기능을 보여준다. (280x에만 존재)

 


그럼 PLL 회로를 살펴보고 간단하게 정리해보자. OSC에서 발생한 클럭은 PLL회로를 거치면서 안정화 되고 배율이 조정되어 마지막에 위치한 /2 분주기를 거쳐 CLKIN이란 이름으로 CPU에 전달된다.

 

여기까지 설명하면 PLL에 관해서는 간단하게나마 어떻게 동작하고 어떤 일을 하는 것인지에 대해 이해하고 계시리라 판단된다. 실상 PLL에 대해서는 더 설명할 부분이 없는 것 같다. 여기서 좀 더 깊이 들어가려면 28x DSP의 클럭킹 시스템에 대한 이해와 PLL 설정을 CCS 상에서 직접 실습해 보는 정도의 내용이 필요한데, 이 역시 차차 설명하고 실습해 보도록 하겠다.


WatchDog(
와치독) 블럭 구조


와치독은 시스템 안전장치의 일종으로, 일정 시간 안에 CPU가 와치독에게 적절한 밥(?)을 주지 않으면 와치독은 CPU를 리셋 시켜 버리는 회로라고 했다.( 무서운 회로군.. ) 와치독은 레지스터 구조를 보기에 앞서, 먼저 그 회로 구조를 살펴보도록 하겠다. 이것이 독자 분들이 와치독을 이해하는데 한결 수월하지 않나 싶기 때문이다.

 

 와치독 블럭 구조

 
제법 복잡해 보이지만 별거 아니다. 그림의 왼쪽 상단 위에서부터 차근차근 살펴보자. 먼저 입력으로 OSCCLK가 들어온다. OSCCLK가 들어온다는 것은 PLL 회로를 거치지 않은 클럭이 들어온다는 뜻이다. 즉 와치독은 PLL과 별도의 클럭으로 동작되기 때문에 PLL 설정에 무관하다는 것을 알 수 있다. 입력된 클럭은 512 분주기를 거쳐 다시 자체의 Prescaler를 거친다. 다음은 스위치를 거쳐 8-bit WatchDog Counter라는 회로로 들어가게 된다. 여기서 8-bit짜리 WatchDog Counter는 입력되는 클럭에 따라 0~256까지 업카운팅(Up Counting)을 하게 되고, 카운팅 결과가 overflow (256을 넘쳤을 때)났을 때 출력 펄스를 발생 시킨다. 그리고 이 때 WatchDog Counter에서는 카운팅 값이 다시 0으로 돌아와 다시 업카운팅을 하는 반복 작업을 수행한다. 된다. 그리고 이 과정에서 발생하는 출력 펄스는 CPU를 리셋 시키거나 와치독 인터럽트를 발생시키는 일을 하는데 이는 위 그림의 오른쪽을 보면 알 수 있다. (WDRST : 리셋 , WDINT : 와치독 인터럽트)

 
여기까지 WatchDog Counter에 의해 일어나는 CPU 리셋과 와치독 인터럽트 발생 과정을 알아봤다. 그런데 여기서 CPU가 와치독에게 어떤 적절한 행동을 해 주면 와치독에 의한 리셋을 막을 수 있다고 했다. 이는 무엇일까? 다시 위 그림을 보자. 그림 왼편을 보면 WDKEY라 해서 55+AA가 있다. 이 회로의 출력을 보자. Bad Key는 리셋으로 직결되지만, Good Key WatchDog Counter Clear한다. WDKEY라는 회로에 55 AA를 순차적으로 써 넣어주면 이 때의 출력 신호가 WatchDog Counter의 값을 Clear시켜 overflow에 의한 CPU 리셋을 막는다는 것을 알 수 있다. 여기서 주의할 점은 WatchDog Counter는 계속 카운팅하기 때문에 WDLEY 55+AA값을 주기적으로 넣어줘야 한다는 점과 55+AA가 아닌 다른 값이나 순서를 어겨 값을 집어넣을 경우, 바로 리셋이 걸린다는 점이다.

 
여기까지가 와치독에 관련된 주요한 내용의 전부이다. 이외에 와치독에 의한 리셋과 더불어 발생하는 와치독 인터럽트가 있지만, 우리는 아직 인터럽트에 대해 배우지 않았기 때문에 그냥 여기서는 넘어가도록 하겠다. 추가로 28x DSP에서 CPU가 리셋되는 경우는 와치독에 의한 리셋과 외부 리셋 신호(XRS)에 의한 경우 밖에 없다. 이번엔 와치독 회로 구조를 바탕으로 레지스터를 살펴보도록 하겠다.

 

 

 와치독 컨트롤 레지스터 구조

와치독을 컨트롤 하는 레지스터 구조이다. 각 레지스터의 필드 영역을 위의 그림과 비교하면서 살펴보시길 바란다. 먼저 WDFLAG 영역은 와치독 리셋 상태를 알려주는 플래그 비트로 와치독에 의한 리셋 신호가 발생하면 이 비트가 1 set 된다. 이 비트는 개발자가 0으로 Clear 하기 전까지 계속 그 상태를 유지하고 있으며, 0으로 Clear하는 방법은 이 비트 필드에 ‘1’을 써 넣어주면 된다.( = W1C ) 그러나 최근 Ti 데이터시트를 보면 이 영역이 Reserved 되어 있는데, 오작동이 잦아서 Ti에서 Reserved 시켜놨다고 한다.

 
다음은 WDDIS 영역이다. 위 그림에서 WatchDog Counter로 들어가기 직전 스위치가 있는데, 이 스위치를 제어하는 필드 영역으로 보면 되겠다. 즉 이 비트는 와치독의 사용 여부를 결정하는데 이 비트가 1이면 와치독을 사용하지 않겠다는 것이고(스위치가 열려 있다.) 이 비트가 0이면 와치독을 사용하겠다는 뜻이다. 여기서 알아둬야 할 점은 이 비트 영역에 값을 써 넣기 위해서는 한 가지 필수조건이 필요한데, 이는 뒤에 가서 다시 언급하도록 하겠다.

 
다음은 WDCHK 영역으로 와치독의 로직 체크 비트이다. 개발자는 이 영역에 반드시 ‘101’을 써 넣어 줘야 한다. 그렇지 않을 경우 그 즉시 리셋이 걸리게 되어 있다.

 

마지막으로 WDPS라는 영역인데, 이 영역은 와치독에 입력되는 OSCCLK을 분주하는 역할을 담당한다. 와치독에서 입력 클럭을 분주하지 않고 사용하면 WatchDog Counter에서 카운팅이 너무 빨리 일어나기 때문에 리셋이 불필요하게 자주 걸린다. 따라서 와치독 회로의 입력단에 512분주기와 더불어 WDPS 라는 자체 분주 영역을 만들어 놓은 것이다. 이 영역에 쓰여지는 값에 따른 클럭 분주 결과는 다음과 같다.

 


000 WDCLK = OSCCLK / 512 / 1( 리셋시 초기값 )

001 WDCLK = OSCCLK / 512 / 1

010 WDCLK = OSCCLK / 512 / 2

011 WDCLK = OSCCLK / 512 / 4

100 WDCLK = OSCCLK / 512 / 8

101 WDCLK = OSCCLK / 512 / 16

110 WDCLK = OSCCLK / 512 / 32

111 WDCLK = OSCCLK / 512 / 64


여기까지 WDCR 레지스터에 대해 알아봤다. 여기에 추가로 와치독과 관련된 레지스터가 몇몇 더 있다. 다음을 보자.

 

 


WDCNTR
레지스터는 8bit WatchDog Counter의 카운팅 상태를 나타내 주는 레지스터로 이 레지스터의 WDCNTR
영역을 읽으면 현재까지 카운팅 된 값을 알 수 있다.

 

 


이 레지스터는 WatchDog Counter Clear하는 역할을 담당한다. WDKEY 영역에 16진수로 55 AA를 순차적으로 써 넣으면 WatchDog Counter의 상태를 0으로 Clear시켜 와치독에 의한 CPU 리셋을 막을 수 있다.

 

 


먼저 WDINTS 영역은 와치독 인터럽트 상태(WDINT)를 나타내는 영역으로 이 비트가 0이면 현재 와치독 인터럽트가 활성화 되어 있다는 것을 나타내고 1이면 비활성화 된 상태를 나타낸다.


다음 WDENINT 영역은 WatchDog Counter의 출력 신호 결과를 CPU 리셋에 이용할 것인지, 와치독 인터럽트에 이용할 것인지를 결정짓는 역할을 한다. 즉 이 비트에 0을 써 넣으면 WatchDog Counter의 출력 신호가 CPU 리셋 하는데 이용되고, ( WDRST는 활성화, WDINT는 비활성화 ) 1을 써 넣으면 WatchDog Counter의 출력 신호가 와치독 인터럽트를 발생 시키는데 이용된다.


마지막으로 WDOVERRIDE 영역은 앞에서 잠깐 언급한 WDCR 레지스터의 WDDIS의 값을 쓰기 위한 필수조건을 나타낸다. 즉 이 비트가 1일 경우 WDDIS의 비트 상태를 변경 할 수 있고 이 비트가 0이면 WDDIS 상태를 바꾸는 것은 불가능해진다. 여기서 만약 WDOVERRIDE 1을 써 넣음으로써, 이 비트 영역이 0으로 Clear 되면, 이를 다시 소프트웨어적으로 1 set 시키는 것이 불가능해진다. 만약 이 비트를 다시 1 set 시키기 원한다면 DSP를 리셋 시키는 수밖에 없다.

 
지금까지 우리는 시스템 초기화의 아주 중요한 부분으로 PLL과 와치독에 관해 그 구조와 레지스터에 대해 분석했다. 이제는 이 내용들을 토대로 실제 본 예제의 소스코드에서 어떻게 PLL과 와치독이 설정되고 쓰였는지 소스분석을 통해 알아보도록 하겠다.

 


//
소스 분석

// 초기화 시작

// Step 1. 시스템 컨트롤 초기화:

DINT;

 

InitSysCtrl();

 

// Step 2. GPIO 초기화

EALLOW;

GpioMuxRegs.GPAMUX.all = 0x0000;

GpioMuxRegs.GPADIR.all = 0x00FF;

EDIS;

 

GpioDataRegs.GPADAT.all = 0x00FF;

 

EINT;

ERTM;

 

// 초기화 끝

                                   [코드 1] 02_Initialize.pjt의 시스템 초기화 소스


코드 1은 본 절의 예제 소스 중에서 시스템 초기화 부분만을 나타낸 것이다. 주석까지 포함해도 몇 줄 안 된다. 그 중에 지금껏 우리가 배웠던 PLL이나 와치독과 관련된 코드는 눈을 씻고 찾아봐도 없다. 실컷 배워 놓고 이게 어떻게 된 일이냐 하고 의심하기 이전에 InitSysCtrl()이란 함수를 눈여겨 봐 주시기 바란다. 함수의 이름에서 뭔가 냄새가 느껴진다. 냄새가 느껴지는 쪽을 찾아보도록 하자.

 

InitSysCtrl()이란 함수는 DSP280x_SysCtrl.c 라는 파일에 정의되어 있는데, 이 함수는 다시 DisableDog(), InitPll(0xA), InitPeripheralClocks()로 구성되어 있다. 각각의 함수가 어떤 일을 하는지 코드를 통해 알아보자.

 


void DisableDog(void)

{

EALLOW;

SysCtrlRegs.WDCR= 0x0068;

EDIS;

}

                                                    [코드 2] DisableDog()


먼저 DisableDog()이다. 아주 간단하게 내용인데, 그 내용인즉슨 WDCR 0x0068을 집어넣는 코드이다. 이를 WDCR 레지스터를 보면서 분석해보면, 와치독 로직 체크 비트에 ‘101’을 써 넣고, WDDIS 1을 써 넣어 와치독을 사용하지 않겠다는 것을 의미한다. 즉 함수는 이름 그대로 와치독을 비활성화 시키는 함수이다.

 

이 함수는 시스템 초기화 부분 중에 가장 먼저 호출되었다. 게다가 한참 중요성을 강조했던 와치독을 비활성화 시키는 코드이므로 선뜻 이해가 가지 않으시겠지만, 일반적으로 와치독의 사용은 코드 개발 단계에서 불편함을 초래할 수 있기 때문에, 사용하지 않다가 개발 과정이 마무리 되면 사용하게 된다. 그리고 가장 먼저 호출 된 이유는 리셋 후 초기 상태가 와치독 사용 모드이기 때문에 이 함수를 재빨리 호출하지 않으면 와치독에 의한 리셋으로 나머지 코드가 수행되지 않기 때문이다.

 


void InitPll(Uint16 val)

{

  volatile Uint16 iVol;  

 

  // Make sure the PLL is not running in limp mode

  if (SysCtrlRegs.PLLSTS.bit.MCLKSTS != 1)

  {

      if (SysCtrlRegs.PLLCR.bit.DIV != val)

      {

 

        

         EALLOW;

         // Before setting PLLCR turn off missing clock detect

         SysCtrlRegs.PLLSTS.bit.MCLKOFF = 1;

         SysCtrlRegs.PLLCR.bit.DIV = val;

         EDIS;

 

         // Optional: Wait for PLL to lock.

         // During this time the CPU will switch to OSCCLK/2 until

         // the PLL is stable.  Once the PLL is stable the CPU will

         // switch to the new PLL value.

         //

         // This time-to-lock is monitored by a PLL lock counter.  

         //  

         // Code is not required to sit and wait for the PLL to lock.  

         // However, if the code does anything that is timing critical,

         // and requires the correct clock be locked, then it is best to

         // wait until this switching has completed. 

 

         // The watchdog should be disabled before this loop, or fed within

         // the loop.  

 

         DisableDog();

 

         // Wait for the PLL lock bit to be set. 

         // Note this bit is not available on 281x devices.  For those devices

         // use a software loop to perform the required count.

 

         while(SysCtrlRegs.PLLSTS.bit.PLLLOCKS != 1) { }

        

         EALLOW;

         SysCtrlRegs.PLLSTS.bit.MCLKOFF = 0;

         EDIS;

      }

  }

 

  // If the PLL is in limp mode, shut the system down

  else

  {

     // Replace this line with a call to an appropriate

     // SystemShutdown(); function.

     asm("        ESTOP0");

  }

}

  [코드 3] InitPll()


이번엔 InitPll(0xA)이다. 이 함수를 보면 전달 인자를 받아 그 값을 PLLPCR 레지스터의 DIV에 넣는다. 즉 개발자가 함수로 인자를 전달하면 그 값에 맞춰 PLL 설정을 하는 함수인 것이다. 여기서 한 가지 알아둬야 할 점은 PLL 설정을 바꾸면 시스템 클럭이 바뀌는데, 이 때 변경된 내용이 안정화되기까지는 어느 정도 시간이 필요하다. 281x에서는 이 시간 동안 와치독에 의한 리셋을 막고 안정화하기 위해  for문으로 지연 시켰다. 280x에서는 PLLSTS PLLLOCKS 비트가 PLL의 정상화를 알려주기 전까지 공회전을 하고 있다. C에 대한 기초적인 지식과 각 해당 레지스터의 구조만 알고 있다면 특별한 설명 없이도 이런 코드들은 분석이 가능하리라 믿는다.

 


void InitPeripheralClocks(void)

{

  EALLOW;

  // HISPCP/LOSPCP prescale register settings, normally it will be set to default values

  SysCtrlRegs.HISPCP.all = 0x0001;

  SysCtrlRegs.LOSPCP.all = 0x0002;

  SysCtrlRegs.XCLK.bit.XCLKOUTDIV=2;

     

// Peripheral clock enables set for the selected peripherals.  

// If you are not using a peripheral you may want to leave

// the clock off to save on power.

 

SysCtrlRegs.PCLKCR0.bit.ECANBENCLK =1;    // eCAN-B

SysCtrlRegs.PCLKCR0.bit.ECANAENCLK =1;    // eCAN-A

SysCtrlRegs.PCLKCR0.bit.rsvd3   =0;

SysCtrlRegs.PCLKCR0.bit.SCIBENCLK =1;     // SCI-B

SysCtrlRegs.PCLKCR0.bit.SCIAENCLK =1;     // SCI-A

SysCtrlRegs.PCLKCR0.bit.SPICENCLK =1;     // SPI-C

SysCtrlRegs.PCLKCR0.bit.SPIBENCLK =1;     // SPI-B

SysCtrlRegs.PCLKCR0.bit.SPIAENCLK =1;     // SPI-A

SysCtrlRegs.PCLKCR0.bit.SPIDENCLK =1;     // SPI-D

SysCtrlRegs.PCLKCR0.bit.rsvd2   =0;

SysCtrlRegs.PCLKCR0.bit.I2CAENCLK  =1;    // I2C

SysCtrlRegs.PCLKCR0.bit.ADCENCLK  =1;    // ADC

SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC  =0;   // TBCLKSYNC default

SysCtrlRegs.PCLKCR0.bit.rsvd1   =0;

 

SysCtrlRegs.PCLKCR0.all = 0x3214;

 

// Note: not all peripherals are available on all 280x derivates.

// Refer to the datasheet for your particular device.

{

union PCLKCR0_REG regPCLKCR0;

regPCLKCR0.bit.ECANBENCLK =1;    // eCAN-B

regPCLKCR0.bit.ECANAENCLK =1;    // eCAN-A

regPCLKCR0.bit.rsvd3   =0;

regPCLKCR0.bit.SCIBENCLK =1;     // SCI-B

regPCLKCR0.bit.SCIAENCLK =1;     // SCI-A

regPCLKCR0.bit.SPICENCLK =1;     // SPI-C

regPCLKCR0.bit.SPIBENCLK =1;     // SPI-B

regPCLKCR0.bit.SPIAENCLK =1;     // SPI-A

regPCLKCR0.bit.SPIDENCLK =1;     // SPI-D

regPCLKCR0.bit.rsvd2   =0;

regPCLKCR0.bit.I2CAENCLK  =1;    // I2C

regPCLKCR0.bit.ADCENCLK  =1;    // ADC

regPCLKCR0.bit.TBCLKSYNC  =0;   // TBCLKSYNC default

regPCLKCR0.bit.rsvd1   =0;

SysCtrlRegs.PCLKCR0.all = regPCLKCR0.all;

}

 

{

union PCLKCR0_REG regPCLKCR0;

regPCLKCR0.bit.ECANBENCLK =1;    // eCAN-B

regPCLKCR0.bit.ECANAENCLK =1;    // eCAN-A

regPCLKCR0.bit.rsvd3   =0;

regPCLKCR0.bit.SCIBENCLK =1;     // SCI-B

regPCLKCR0.bit.SPIAENCLK =1;     // SPI-A

regPCLKCR0.bit.SPIDENCLK =1;     // SPI-D

regPCLKCR0.bit.rsvd2   =0;

regPCLKCR0.bit.I2CAENCLK  =1;   // I2C

regPCLKCR0.bit.ADCENCLK  =1;    // ADC

regPCLKCR0.bit.TBCLKSYNC  =0;  // TBCLKSYNC default

//regPCLKCR0.bit.rsvd1   =0;

 

regPCLKCR0.bit.SCIAENCLK =1;     // SCI-A

regPCLKCR0.bit.SPICENCLK =1;     // SPI-C

regPCLKCR0.bit.SPIBENCLK =1;     // SPI-B

SysCtrlRegs.PCLKCR0.all = regPCLKCR0.all;

}

  SysCtrlRegs.PCLKCR1.bit.ECAP1ENCLK = 1;  // eCAP1

  SysCtrlRegs.PCLKCR1.bit.ECAP2ENCLK = 1;  // eCAP2

  SysCtrlRegs.PCLKCR1.bit.ECAP3ENCLK = 1;  // eCAP3

  SysCtrlRegs.PCLKCR1.bit.ECAP4ENCLK = 1;  // eCAP4

 

  SysCtrlRegs.PCLKCR1.bit.EPWM1ENCLK = 1;  // ePWM1

  SysCtrlRegs.PCLKCR1.bit.EPWM2ENCLK = 1;  // ePWM2

  SysCtrlRegs.PCLKCR1.bit.EPWM3ENCLK = 1;  // ePWM3

  SysCtrlRegs.PCLKCR1.bit.EPWM4ENCLK = 1;  // ePWM4

  SysCtrlRegs.PCLKCR1.bit.EPWM5ENCLK = 1;  // ePWM5

  SysCtrlRegs.PCLKCR1.bit.EPWM6ENCLK = 1;  // ePWM6

  SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;     // Enable TBCLK within the ePWM

 

  SysCtrlRegs.PCLKCR1.bit.EQEP1ENCLK = 1;  // eQEP1

  SysCtrlRegs.PCLKCR1.bit.EQEP2ENCLK = 1;  // eQEP2

                      

  EDIS;

}

[코드 4] InitPeripheralClocks()


이 함수는 280x DSP가 가지고 있는 주변회로에 클럭 공급에 관한 설정을 담당하는 함수이다. 이 함수의 구체적인 내용들은 이후의 시스템 클럭킹 부분에서 다루도록 할 예정이고, 여기서는 다만 DSP가 주변회로에 클럭을 공급할 것인지, 안할 것인지에 대해 설정하는 부분이라고 보시면 되겠다. 각 해당 레지스터 비트 필드에 1을 써 넣으면 클럭을 공급하고 0을 써 넣으면 클럭 공급을 중단하겠다는 것인데, 개발 단계에서는 모든 주변회로에 클럭을 공급하고 있다가 개발 마무리에 와서 자신이 사용하지 않는 주변회로에는 클럭 공급을 중단하는 것이 바람직하다 하겠다.

 

위의 3가지 함수에 대한 분석이 얼추 끝났다. 사실 각 레지스터의 구조를 배운 우리로써는 분석이란 단어가 무색할 만한 과정이었다. 3가지 함수는 InitSysCtrl()이란 함수에서 호출 되면서 시스템 초기화의 중요한 일부를 차지하고 있다. 여기서의 각 함수 내용은 그다지 어렵지 않았다. 그러나 우리가 한 가지 기억해야 할 것은 함수들의 호출 순서임을 잊지 말아야 한다.

 

우리의 목표였던 InitSysCtrl()함수를 분석함으로써, 본 절의 예제에서 사용된 시스템 초기화 내용은 분석한 셈이 되는 것이다. 그러나 여기에서 좀 더 할일이 있다. InitSysCrtl.c 파일에 대한 분석이다. 사실 이 파일은 DSP의 기본적인 시스템 초기화 내용을 담고 있는 파일로, Ti에서 제공하는 예제 파일에 모두 포함되어 있다. 개발자는 이 파일을 이용하던지 이런 종류의 코드들을 직접 구현하든지 해서 시스템의 초기화를 맞춰줘야 한다. 우리는 이 파일의 관한 대부분의 내용은 알고 있다. 다만 몇 가지 함수가 남았다. 정확히는 두 가지이다.

 


void InitFlash(void)

{

  EALLOW;

  //Enable Flash Pipeline mode to improve performance

  //of code executed from Flash.

  FlashRegs.FOPT.bit.ENPIPE = 1;

 

  //                CAUTION

  //Minimum waitstates required for the flash operating

  //at a given CPU rate must be characterized by TI.

  //Refer to the datasheet for the latest information. 

  //Set the Random Waitstate for the Flash

  FlashRegs.FBANKWAIT.bit.RANDWAIT = 5;

 

  //Set the Paged Waitstate for the Flash

  FlashRegs.FBANKWAIT.bit.PAGEWAIT = 5;

  //Set the Waitstate for the OTP

  FlashRegs.FOTPWAIT.bit.OTPWAIT = 8;

 

  //                CAUTION

  //Minimum cycles required to move between power states

  //at a given CPU rate must be characterized by TI.

  //Refer to the datasheet for the latest information.

   

  //For now use the default count

  //Set number of cycles to transition from sleep to standby

  FlashRegs.FSTDBYWAIT.bit.STDBYWAIT = 0x01FF;      

 

  //Set number of cycles to transition from standby to active

  FlashRegs.FACTIVEWAIT.bit.ACTIVEWAIT = 0x01FF;  

  EDIS;

  //Force a pipeline flush to ensure that the write to

  //the last register configured occurs before returning. 

  asm(" RPT #7 || NOP");

}

                                                  [코드 5] InitFlash()


위의 코드 5. InitFlash() 함수는 플래시 메모리를 이용할 때, 고속 수행을 위한 설정 내용을 담고 있다. TI가 제공하는 SPRC097이나 SPRC191 예제나, 개발 과정의 코드들은 대부분 RAM에서 수행되기에, 위 코드는 사용되지 않는다.

이에 관한 내용은 추후에 플래시를 다루는 장에서 다루도록 하겠다.

 

 


void KickDog(void)

{

EALLOW;

SysCtrlRegs.WDKEY = 0x0055;

SysCtrlRegs.WDKEY = 0x00AA;

EDIS;

}

 [코드 6] KickDog()


마지막으로 남은 것은 KickDog()이란 함수이다. 이쯤 되면 독자 분들이 함수 이름에서 그 역할을 대강 눈치 챌 수 있지 않을까 싶다. 말 그대로 개를 차버리는, 조금은 비인도적인 이름을 가진 함수이다. 그 내용을 보면 WDKEY 55 AA를 순차적으로 집어넣고 있다. 와치독이 활성화 되어 있을 때, 와치독에 의한 리셋이나 인터럽트 신호가 발생하는 것을 막아주는 코드이다. 현재 본 예제에서는 쓰이지 않고 있지만, 조만간 실습 때 사용해볼 코드이다.

 

 

실습

PLL 바꿔 보기

 

1. 예제 동작을 확인하고 멈춘 상태에서 다음 그림과 같이 DSP281x_SysCtrl.c 파일을 열어보십시오.





2. InitSysCtrl()
함수에 있는 InitPll()의 전달인자를 0xA → 0x1로 바꿔보십시오.

 




3.
프로그램을 Rebuild하고, 실행시켜 보십시오.

예제를 실행시키고, EVM 보드에 있는 LED에 어떠한 변화가 있는지 살펴보자. 아마 처음과는 달리 LED가 점멸하는 속도가 매우 늦어진 것을 확인할 수 있을 것이다. 이는 우리가 수정한 코드를 통해 이해할 수 있는데, 맨 처음 InitPll()함수의 전달인자가 0xA, 이는 PLL 출력 비를 입력 클럭에 5배를 곱해서 출력을 한 결과가 된다. 여기서 예를 든 경우는 TMS320F2812 버전을 가지고 실습했기 때문에, InitPll() 함수의 전달인자가 0xA인 경우에, DSP 칩의 구동 클럭은 150MHz가 된다. 여기서 우리가 실습을 통해 InitPll() 전달 인자를 0x1로 바꿔 줬을 경우, DSP의 구동 클럭은 15MHz, 즉 예제의 default 상태보다 1/10 수준으로 그 속도가 떨어짐을 알 수 있다. 따라서 순차 점멸하던 LED의 움직임은 그 만큼 느려져서 동작하게 되는 것이다. PLL의 설정을 바꿔보는 실습은 매우 간단하다. 단지 InitPll()함수의 전달 인자만 바꿔주면 되는 것이다. 각자 전달인자를 바꿔가며 PLL 설정에 따른 칩의 구동 클럭 변경에 관한 이해를 다잡아 가시기 바란다.


WatchDog
확인하기

1. InitPll()의 전달 인자를 다시 0xA 상태로 맞춰 놓은 다음, Initialize.c의 소스에 그림과 같은 코드를 추가하십시오.

 




2.
코드에 수정이 있었으니 Rebuild과정을 거친 다음, DSP를 실행시켜 보십시오.




3. EVM
보드의 LED에 어떤 변화가 있는지 관찰하십시오.
이번엔 LED에 어떤 변화가 관찰되는가? 아마 1번부터 4 LED까지만 빠르게 점멸 하는 것이 보일 것이다. 왜 이렇게 바뀐 것일까? 지금까지 스스로 잘 정리하신 분들이라면 우리가 추가한 WDCR 레지스터 관련 코드를 통해 이 현상을 분석할 수 있을 것이다. 잘 모르겠더라도 실망하지 마시길 바란다. 내용이 어렵다기 보다는 아직 익숙하지 않아서일 확률이 크기 때문이다.

 

1번 실습에서 Initialize.c에 추가한 코드를 살펴보자. WDCR 레지스터에 0x002D라는 값을 넣어주는 코드이다. 이를 WDCR 레지스터 구조를 보면서 그 내용을 파악해보면, WDDIS 0을 써 넣음으로써, 와치독을 활성화 시키고, 로직 체크 비트는 101, 그리고 와치독에 적정한 분주를 주는 코드이다. 여기서 이 코드의 핵심은 와치독을 활성화하는데 있다. 함수로 만들었다면 EnableDog()쯤으로 만들어 표현할 수 있었겠는데, 시스템 초기화 마지막 단에 와치독을 활성화시킴으로써, 코드가 수행도중 리셋이 자꾸 걸리게 되는 것이다. 따라서 LED의 점멸 패턴은 다음 그림과 같이 변경되어 우리 눈에 보이는 것이다.

 


LED
를 통해 DSP에 주기적으로 리셋이 걸려있다는 것 이외에도 DSP에 리셋이 자꾸 걸리는지 확인해보도록 하자.

 


4. DSP 동작을 멈추고, [Dubug] → [Reset CPU]를 통해 DSP를 리셋 시키십시오.

 


5. 리셋 후 나타나는 Disassembly 창이 나타나면 이 때 보이는 녹색 화살표가 가리키는 부분에 브레이크 포인트를 걸어보십시오.

※참고 : 녹색 화살표가 가리키는 지점은 DSP를 리셋 시켰을 때 분기하는 주소번지이다. 여기에 관해서는리셋과 부트 로더에 관한 이해가 필요한데, 여기에서는 그냥 DSP를 리셋 시키면 이 번지로 점프하는 구나 정도로 생각하시면 되겠다.




6.
준비가 됐으면 DSP를 동작 시켜 보십시오.

RUN을 통해 DSP를 동작시키면, 웬일인지 자꾸 브레이크 포인트에 걸려 칩 동작이 멈추게 된다. 이는 DSP가 실행 명령을 받고, 프로그램을 수행하던 도중 와치독에 의한 리셋으로 인해 리셋 번지에 걸어놓은 브레이크 포인트에서 칩 동작이 계속 멈추게 되는 것이다. 정말인지 확인해보고 싶다면 1번 실습에서 추가했던 코드에 주석처리를 하고(와치독을 비활성화 시키고) 다시 DSP를 동작 시켜 보시기 바란다. 좀 전과 같이 브레이크 포인트에 걸리는 일없이 아주 잘 동작할 것이다.

 
이번엔 와치독을 활성화 시켜 놓은 상태에서, WDKEY를 통해 와치독에 의한 리셋을 막아보는 실습을 해 보자.

 


7. 리셋 번지에 있는 브레이크 포인트를 지우고, Initialize.c에 다음 그림과 같은 위치에 KickDog()함수를 추가 시켜보십시오.

 

 
※참고 : KickDog() 함수는 DSP280x_SysCtrl.c에 정의되어 있기 때문에 우리는 가져다가 쓰기만 하면 된다.




8. Rebuild
과정 후 칩을 동작 시켜보십시오.




9. LED
의 변화를 관찰 하십시오.

LED에 어떤 변화가 있는가? ‘KickDog’의 의미처럼 개를 걷어 차버리는 효과가 있던가? 아마 아무런 효과 없이 LED는 와치독에 의한 리셋으로 1~4번까지의 LED만 점멸하는 것을 볼 수 있을 것이다. 뭔가 이상하다. 앞에서 배운 KickDog() 함수의 내용은 WDKEY에 순차적으로 55 AA를 집어넣는 내용이다. 순서도 맞고 값도 제대로다. 따라서 와치독에 의한 리셋을 막아줘야 하는데도 불구하고, 뭔가 잘못된 구석이 있다. 필자는 개인적으로 다음 내용을 보기 전에 독자 분들께서 직접 왜 현상이 일어나는지에 대해 원인을 분석 해 보시기를 권고 드린다. 왜냐하면 이런 과정들은, 자신의 문제 해결 능력을 조금씩 키워나가는데 도움이 되기 때문이다.


권고는 어디까지나 권고에서 끝날 일이고, 여기서는 이에 대한 문제해석을 빠트릴 수 없다. 여기서 문제가 된 것은 KickDog() 함수의 위치였다. 분명 와치독을 활성화 시킨 후에 KickDog() 함수를 사용하긴 하였으나, 이는 단지 한 번 밖에 WatchDog Counter Clear 하지 못한다. 따라서 이후에 카운팅 되는 결과에 따라 발생한 리셋 신호들은 막아내지 못한 것이다. 앞에서 언급했듯이 와치독을 활성화 시킨 후, WDKEY를 사용하는 데에는 주의해야 할 점이 있다고 했다. 이는 WDKEY의 위치에 대해 강조하려 했던 말인데, 다음 실습을 보자.

 


10. KickDog()함수를 그림과 같이 delay_loop()의 부분에 추가 시켜 놓고, 칩을 실행시켜보십시오.

 

 


11. LED의 점멸 패턴을 확인하십시오.

어떤가? 이번엔 LED의 점멸 패턴이 우리의 예상과 맞아 떨어지는가? delay_loop()는 본 예제 알고리즘 부분에 계속적으로 수행되므로 이 함수 안에 KickDog()함수를 집어넣으면, 수시로 WatchDog Counter Clear 시켜 와치독에 의한 리셋을 막을 수가 있게 되는 것이다.

 
여기까지 하면 PLL과 와치독에 관련된 내용을 거의 소화한 셈이다. 이제부터는 독자 분들 나름대로 코드도 바꿔보고 레지스터 구조도 다시 보면서 지금 했던 내용들은 자기 것으로 만들어 가는 과정만이 남았다. WDKEY의 내용을 55+AA가 아닌 다른 값이나 순서를 변경해보기도 하고, PLL의 주기를 계속 변경해보기도 하면서 본 장을 마무리 하도록 바란다.

 

DSP에 대해 좀 더 알고 싶으세요? 싱크웍스 무료실습강좌를 신청하세요. ---------> 무료실습강좌 신청하기
본 내용이 유익하셨다면 싱크웍스 블로그 2.0을 구독하세요. -------------------------> 싱크웍스 RSS



 

 
Posted by syncworks

댓글을 달아 주세요