ARM이 만든 2선 디버그 인터페이스(Serial Wire Debug). JTAG의 디버그 기능을 최소 핀으로 구현한 것이다.
신호선
SWDIO — 데이터 (양방향) SWCLK — 클럭 (+ GND 필수, VREF 권장, nRESET 선택)
2선만으로 JTAG의 디버깅 기능을 모두 제공한다. 핀이 부족한 소형 MCU에서 결정적 장점이다.
JTAG 핀과 공유
- Cortex-M의 SWJ-DP(Serial Wire / JTAG Debug Port)
- 같은 핀을 두 모드로 사용
- 특정 시퀀스를 보내면 JTAG ↔ SWD 전환
SWDIO = JTAG의 TMS SWCLK = JTAG의 TCK
프로토콜
- ① 요청 (8비트) — : 디버거 → 타깃. 읽기/쓰기, AP/DP, 주소
- ② 턴어라운드 — : 방향 전환 (SWDIO가 양방향이므로)
- ③ 응답 (3비트) — : OK / WAIT / FAULT
- ④ 데이터 (33비트): 32비트 + 패리티
응답 코드
- OK — (001) — 정상
- WAIT — (010) — 아직 준비 안 됨. 재시도
- FAULT (100) — 오류. 상태 레지스터 확인
WAIT가 반복되면 타깃이 저전력 모드이거나 코어 클럭이 멈춘 것이다.
SWO — 추가 출력 핀
SWDIO + SWCLK + SWO (3선)
SWO(Serial Wire Output)는 타깃 → 디버거 단방향 고속 출력
무엇에 쓰나
- ① printf 대체 (ITM) — UART보다 빠르고 핀 하나만 추가
- ② 실행 추적 (ETM/ITM) — 어떤 코드가 실행됐는지
- ③ 성능 프로파일링 — PC 샘플링
- ④ 이벤트 타임스탬프
// ITM으로 문자 출력 — UART보다 훨씬 빠르다
ITM_SendChar('A');
// printf 를 ITM으로 리다이렉트
int _write(int fd, char *ptr, int len) {
for (int i = 0; i < len; i++) ITM_SendChar(ptr[i]);
return len;
}
UART printf는 115200bps에서 문자당 약 87μs가 걸려 ISR에서 쓰면 타이밍이 완전히 깨진다. ITM은 수 사이클이라 훨씬 덜 방해한다.
디버그 구성 요소 (CoreSight)
| 유닛 | 기능 |
|---|---|
| DWT | 데이터 워치포인트, 사이클 카운터, PC 샘플링 |
| FPB | 플래시 브레이크포인트 (플래시 코드에 중단점) |
| ITM | 소프트웨어 계측 출력 |
| ETM | 명령어 추적 (전체 실행 흐름 기록) |
| TPIU | 추적 데이터를 외부로 출력 |
DWT 사이클 카운터가 실무에서 유용하다.
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
uint32_t t0 = DWT->CYCCNT;
function_to_measure();
uint32_t cycles = DWT->CYCCNT - t0;
추가 하드웨어 없이 정확한 실행 시간 측정이 가능하다.
하드웨어 브레이크포인트 개수 제한
Cortex-M3/M4 : FPB 비교기 6개 (명령 6 또는 명령 2 + 리터럴 4 구성) Cortex-M0/M0+: 4개
- Cortex-M7 — : 8개
플래시에 있는 코드는 명령을 바꿔 넣을 수 없으므로 하드웨어 비교기를 써야 한다. 개수를 넘으면 디버거가 "브레이크포인트를 설정할 수 없다"고 거부한다.
RAM에서 실행하는 코드는 소프트웨어 브레이크포인트(BKPT 명령 삽입)가 가능해 개수 제한이 없다.
흔한 문제
연결 실패
→ 펌웨어가 SWD 핀을 GPIO로 재설정했다 → connect under reset
→ 저전력 모드 진입 → 디버그 유지 옵션(DBGMCU) 활성화
→ 클럭 속도가 너무 높다 → SWD 클럭을 낮춘다
→ 배선 길이·노이즈 → 짧게, GND를 함께
SWO 출력이 안 나온다
→ SWO는 코어 클럭에 동기화된다. 디버거에 정확한 클럭을 알려야 한다
→ 클럭을 바꾸면 SWO 보율도 바뀐다
디버그 유지 설정
__HAL_DBGMCU_FREEZE_IWDG(); // 정지 시 워치독도 멈춤
DBGMCU->CR |= DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STOP | DBGMCU_CR_DBG_STANDBY;
저전력 모드에서도 디버거 연결을 유지한다. 개발 중에만 켠다 (전력 소모가 늘어난다).