Watchdog Timer 는 그 단어에서 유추할 수 있는 그대로 CPU를 감시하면서 시간을 계수하는 회로로, 프로그램이 의도치 않게 비정상적으로 중단되거나 무한루프(Infinite Loop)에 빠지는 등 시스템 통제가 불가능한 상황에서 자동으로 시스템을 리셋하는 하드웨어 기능이다.
내부에 있는 8비트 Watchdog 업 카운터가 최대 값인 0xFF을 넘어 overflow 되면 리셋(또는 인터럽트)이 일어나는데 Watchdog Timer에 의한 CPU리셋을 원하지 않는다면 타이머의 카운터가 최대 값에 도달하기 전에 카운터 값을 0으로 초기화(Clear)하는 코드가 실행되어야 한다.
즉, 칩이 실행하는 프로그램이 안정적이고 연속적으로 동작하려면, 프로그램 상에서 Watchdog Timer에 설정된 시간주기보다 짧은 주기로 Watchdog Timer 카운터 값을 초기화하는 코드가 실행되어야 함을 의미한다.
Watchdog 이란 이름에서 유추할 수 있는 것처럼 이 모듈은 경비견 역할을 하고 경비견에게 주기적으로 밥을 주는 행동(Watchdog Timer 초기화)을 하지 않으면 경비견이 짖어서 경고를 하는 것과 같다.
워치독 타이머의 회로를 보면 INTOSC1에서 클락소스를 공급받는데, 이는 첫번째 Internal oscillator 로 10MHz로 동작한다. 즉, PLL 회로를 거치지 않은 별도의 클럭을 사용한다는 뜻이다.
그리고 INTOSC1 에서 WDCLK으로 스케일 다운하는데는 PREDIVCLK와 prescaler라는 두 파라미터가 있다.
이렇게 WDCLK 값을 설정하고 나면 WDCNTR 레지스터에선 WDCLK에 따라 8 bit up counter로 작동하고 이 값이 맥스인 0xFF(255)를 넘어 overflow 되면 설정에 따라 리셋 또는 인터럽트에 대한 트리거가 발생하고, Watchdog Conter 값은 다시 0으로 돌아가 업카운팅을 반복한다.
이 리셋/인터럽트를 막으려면 WDKEY 레지스터에 Good Key 라고 부르는 0x55 와 0xAA 가 순차적으로 입력해야 한다. WDCNTR 가 overflow 되기 전에 주기적으로 Good Key가 입력되면 WDCNTR 값을 0으로 Clear 시켜 리셋/인터럽트가 발생하지 않는다.
소스코드
다음 syscfg 툴에서 WATCHDOG 설정을 해보자.
WDCLK 계산공식은 아래와 같은데 syscfg 툴에서 아주 친절하게 계산까지 해준다.
PREDIVCLK = INTOSC1 / Pre-divider
WDCLK = PREDIVCLK / Prescaler
Predivider 값을 4096으로, Prescaler 값을 64로 설정했을 때
10,000,000 Hz / 4096 / 64 = 38.14697265625 Hz가 된다. 역수를 취하면 0.0262144 s
그리고 워치독 카운터 WDCNTR이 8bit 니까 0.0262144 s * 256 = 6.7108864 s
즉, 이렇게 설정하고 워치독 start를 한 다음에 약 6.7초가 지날 동안 카운터 값을 초기화하는 Good Key 입력이 없으면 리셋이 일어나는 것이다.
보니까 워치독 카운트 타임은 Predivider 와 Prescaler 값에 의해 최소 50ms 에서 최대 6710ms 사이로 설정할 수 있다.
빌드를 하고 board.c 에 가서 생성된 코드는 아래와 같다. 한줄씩 살펴보자
void myWATCHDOG0_init(){
SysCtl_disableWatchdog();
SysCtl_setWatchdogMode(SYSCTL_WD_MODE_RESET);
SysCtl_setWatchdogPredivider(SYSCTL_WD_PREDIV_4096);
SysCtl_setWatchdogPrescaler(SYSCTL_WD_PRESCALE_64);
SysCtl_setWatchdogWindowValue(0);
SysCtl_enableWatchdog();
}
1. SysCtl_disableWatchdog() : 워치독은 기본적으로 Device_init()을 할때 disable 된 상태이다. 그렇긴 한데 여기서 한번더 disable 해주고 있다.
WDCR Register, WDDIS에 1을 쓴다. 0을 쓰면 enable하는거고 이 비트는 SCSR 레지스터의 WDOVERRIDE 비트로 잠글 수 있다.
2. SysCtl_setWatchdogMode(SYSCTL_WD_MODE_RESET) : 워치독은 리셋 또는 인터럽트 시그널을 내보낼 수 있는데 그 중 RESET을 트리거하는 모드로 설정한다.
3. SysCtl_setWatchdogPredivider(SYSCTL_WD_PREDIV_4096) : INTOSC1에서 WDCLK를 만들기 위한 분주 파라미터 중 첫번 째 값이다. INTOSC1을 4096으로 나눈다.
4 SysCtl_setWatchdogPrescaler(SYSCTL_WD_PRESCALE_64) : INTOSC1에서 WDCLK를 만들기 위한 분주 파라미터 중 두번 째 값이다. PREDIVCLK를 64로 나눈다.
5. SysCtl_setWatchdogWindowValue(0) : 워치독 미니멈 값을 0으로 설정하여 사용하지 않는 것으로한다. 만약 이 값을 0보다 큰 값으로 설정하면 워치독 카운터가 최대값에 도달했을 때 뿐만아니라 미니멈 값에 미치미 못했는데 Good Key가 눌린 경우에도 리셋/인터럽트를 트리거할 수 있다.
즉, 워치독 카운터 윈도우를 설정하는 것이다. STM32에선 WWDG로 부르는 기능이다.
6. SysCtl_enableWatchdog() : 워치독 카운터를 enable한다.
7. main.c loop에 SysCtl_serviceWatchdog() 추가 : 이렇게 까지 했으면 반드시 메인루프 어딘가에서 계속적으로 SysCtl_serviceWatchdog() 함수를 호출하여 WDCNTR 값이 맥스에 도달하기 전에 0으로 초기화해야 한다. 안그러면 CPU가 계속 리셋이 될 것이다.
for(;;)
{
SysCtl_serviceWatchdog();
}
SysCtl_serviceWatchdog() 함수 내부를 살펴보면 Good Key인 0x55, 0xAA를 순차적으로 쓰고있다.
static inline void
SysCtl_serviceWatchdog(void)
{
EALLOW;
//
// Enable the counter to be reset and then reset it.
//
HWREGH(WD_BASE + SYSCTL_O_WDKEY) = SYSCTL_WD_ENRSTKEY;
HWREGH(WD_BASE + SYSCTL_O_WDKEY) = SYSCTL_WD_RSTKEY;
EDIS;
}
//
// Keys for WDKEY field. The first enables resets and the second resets.
//
#define SYSCTL_WD_ENRSTKEY 0x0055U
#define SYSCTL_WD_RSTKEY 0x00AAU
인터럽트 모드
만약 워치독을 리셋모드가 아니라 인터럽트 모드로 사용한다면 달라지는 부분은 아래와 같다.
// Function Prototypes
__interrupt void wakeupISR(void);
// Main
void main(void)
{
Device_init();
Interrupt_initModule();
Interrupt_initVectorTable();
// Re-map watchdog wake interrupt signal to call the ISR function. 콜백함수 등록
Interrupt_register(INT_WAKE, &wakeupISR);
// Set the watchdog to generate an interrupt signal. 워치독을 인터럽트 시그널 모드로 선택
SysCtl_setWatchdogMode(SYSCTL_WD_MODE_INTERRUPT);
// Enable the watchdog wake interrupt signal. 인터럽트 enable
Interrupt_enable(INT_WAKE);
EINT;
ERTM;
SysCtl_enableWatchdog();
for(;;)
{
// 워치독 카운터값 리셋
SysCtl_serviceWatchdog();
}
}
// Wakeup ISR - The interrupt service routine called when the watchdog triggers the wake interrupt signal
__interrupt void wakeupISR(void)
{
// do something.
// Acknowledge this interrupt located in group 1
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP1);
}
콜백함수 내부에는 시스템이 종료되기전에 해야할 일을하고 리셋하게끔 구성할 수 있다. 강제 리셋할 때는 WDCR 레지스터의 WDCHK 를 사용하면 될 것같다. 이 bits는 WDCR 레지스터를 조작하는 동안에도 항상 101값을 유지하고 있어야하고 만약 다른 값이 쓰여지면 그 즉시 리셋/인터럽트 트리거가 발생한다.
그리고 Watchdog을 본래용도로 사용하지 않는다면 SysCtl_getResetCause() 함수를 통해서 reset cause를 알 수 있으니 특수 목적으로 사용할 수도 있겠다. 예를들면 Application에서 Bootloader로 갈 때의 flag라던가 말이다.
참고.
주의할 것은 디버깅(에뮬레이션) 모드에서는 인위적으로 리셋 트리거 했을 때 정상적으로 작동하지 않는다. 플래시 빌드로 다운로드하고 전원을 껐다 켜서 테스트 해보아야한다.
참고.
TRM에 리셋소스에 대한 내용이 나와있는데 그 중에 특이했던 것은 RESC라는 레지스터는 리셋 원인에 따라 값이 업데이트되고 리셋 이후에도 그 값을 유지하고 있어 어떤 이유로 리셋이 일어났는지 알 수 있는 레지스터다.
참고.
별개로 NMI는 Non Maskable Interrupt 의 약자로 코드상에서 disable 될 수 없는 Interrupt 로서 Hardware failure를 detect하는 목적으로 존재하고 있다. 매뉴얼 159페이지에 NMI Sources 에 대해 나와있다.
'임베디드 개발 > TMS320F2838x (C28x)' 카테고리의 다른 글
TMS320F28388D ] CPU2에서 SCI, SPI 사용하기 위한 설정방법 (2) | 2022.12.06 |
---|---|
TMS320F28388D ] 타이머, Timer Interrupt 사용하기 (0) | 2022.12.05 |
TMS320F28388D ] FLASH Build 와 RAM Build 같이 사용하는 .cmd 파일 만들기 (3) | 2022.12.01 |
CCS ] 링커 커맨드(.cmd) 파일에 ALIGN(x) directive를 넣는 이유 (0) | 2022.12.01 |
TMS320F28388D ] printf 사용하기 위한 설정 (0) | 2022.11.30 |