ST-Link로 구현하는 ARM 마이크로컨트롤러 심층 디버깅 기법
프로젝트 개발 중 다음과 같은 상황에 부딪힌 적이 있나요? 코드를 작성하고 보드에 다운로드했는데 아무런 반응이 없습니다. 시리얼 출력도 없고 LED도 깜빡이지 않아서 어디서 문제가 발생했는지 알 수 없는 경우 말입니다.
이때 printf 디버깅은 너무 느리고 LED 테스트 방식은 너무 원시적입니다. 당신이 필요한 것은 칩 내부로 직접 들어가 그 칩이 무엇을 하는지 관찰하는 것입니다.
바로 이것이 온라인 디버깅의 가치이며, 오늘 깊이 분석할 도구인 ST-Link가 그 열쇠입니다.
현대 임베디드 개발에서 ST-Link가 필수적인 이유
ARM Cortex-M 시리즈 MCU는 현재 32비트 임베디드 시장을 거의 지배하고 있으며, 스마트워치부터 산업용 PLC까지 그 뒤에는 STM32가 존재합니다. 하지만 시스템 복잡도가 증가함에 따라 개발자들은 더 이상 "한 줄의 코드를 수정하고 프로그램을 다시 다운로드"하는 방식으로 프로젝트를 진행할 수 없게 되었습니다.
ST사가 출시한 ST-Link는 본질적으로 아키텍처 전용 "사고 판독기"입니다. 표준 인터페이스를 통해 컴퓨터와 타겟 칩을 연결하고, 주변장치의 정상 작동을 방해하지 않으면서 다음과 같은 작업을 수행할 수 있습니다:
- 임의 위치에서 CPU 실행 중지
- 모든 레지스터와 변수의 현재 값 확인
- 명령어 단계 실행 및 각 단계 변화 관찰
- HardFault 예외 발생 시 전체 상태 캡처
- UART를 사용하지 않고 실시간 printf 로깅
모든 것의 뒤에는 고도로 표준화된 하드웨어 디버깅 아키텍처인 ARM CoreSight가 존재합니다.
ST-Link를 사용하는 것은 단순히 "Debug" 버튼을 누르는 법을 배우는 것을 넘어, 현대 임베디드 디버깅 시스템 전체를 이해하는 핵심 입구입니다.
ST-Link란 무엇인가? 단순한 다운로더가 아닙니다
많은 사람들이 ST-Link를 단순한 프로그래밍 도구로 생각하지만, 실제로는 훨씬 더 많은 기능을 제공합니다.
프로토콜 변환기 역할
ST-Link의 핵심 역할은 JTAG/SWD 프로토콜 변환기입니다. IDE(예: STM32CubeIDE 또는 Keil)에서 "중단점 설정"을 클릭하면 이 작업이 일련의 저수준 전기 신호로 변환되어 USB를 통해 ST-Link로 전송되고, 다시 SWD 타이밍으로 변환된 후 타겟 칩의 Debug Access Port (DAP)로 전달됩니다.
다시 말해:
PC의 디버깅 명령 → ST-Link → SWD 신호 → 칩 내부 응답
현재 주요 모델은 다음과 같습니다:
- ST-Link/V2: 클래식 모델로 초기 보드에 널리 사용됨
- ST-Link/V2-1: Nucleo 보드에 통합되어 가상 시리얼 기능 지원
- ST-Link/V3: 성능이 더 뛰어나며 SWD 주파수는 최대 180MHz, trace 및 전원 모니터링 지원
모든 모델이 동일한 통신 사양을 따르며, 유일한 차이점은 속도, 기능 확장성 및 안정성입니다.
지원되는 연결 방식
| 인터페이스 | 핀 수 | 사용 시나리오 |
|---|---|---|
| SWD | 2 데이터선 + 클럭 + GND + VREF (+ NRST) | 추천! 4~5개의 선만으로 PCB 공간 절약 |
| JTAG | 4 데이터선 + 클럭 + TDI/TDO 분리 | 다중 장치 연결 또는 기존 프로젝트 호환성 |
SWD 모드 사용을 강력히 권장합니다. 핀 수가 적고 라우팅이 간편할 뿐만 아니라 대부분의 STM32 칩에서 기본적으로 활성화되어 추가 구성이 필요하지 않습니다.
디버깅은 어떻게 발생하는가? 물리 계층부터 콘솔까지
IDE에서 "Debug" 버튼을 누를 때 무슨 일이 일어나는지 알고 계신가요? 이것은 일 버튼이 아닌 정교한 협업 프로세스입니다.
첫 번째 단계: 물리적 연결 설정
다음 선로가 올바르게 연결되어 있는지 확인하세요:
| ST-Link 핀 | 타겟 보드 핀 | 설명 |
|---|---|---|
| SWDIO | PA13 / JTMS | 양방향 데이터선 |
| SWCLK | PA14 / JTCK | 클럭선 |
| GND | GND | 공통 접지 필수 |
| VREF | 3.3V | 레벨 참조, 연결 권장 |
| NRST | 원격 리셋 구현 |
주의: VREF를 연결하지 않으면 일부 버전의 ST-Link가 전압을 판별하지 못해 인식에 실패할 수 있습니다.
두 번째 단계: 핸드셰이크 및 식별
ST-Link는 타겟 칩에 DP_IDR(Debug Port Identity Register) 읽기 요청을 전송합니다. 예상 반환값(보통 0x0BC11477)을 수신하면 통신 경로가 원활한 것입니다.
다음으로 AP를 통해 칩 IDCODE를 쿼리하여 데이터베이스와 일치하는지 확인하고, 유효한 STM32 장치인지 확인합니다.
⚠️ 이 단계가 실패하면 일반적인 원인은 다음과 같습니다:
- 칩에 전원이 공급되지 않음
- BOOT0가 높은 레벨로 설정되어 ISP 모드로 진입
- RDP Level 2(읽기 보호)가 활성화되어 디버깅 인터페이스가 영구적으로 비활성화됨
- SWD 핀이 일반 GPIO로 초기화됨(HAL 초기화가 너무 이른 경우)
세 번째 단계: 내부 일시 중지 및 디버깅 준비
식별이 성공하면 ST-Link는 시스템 리셋을 트리거하고 즉시 CPU를 halted state(일시 중지 상태)로 강제합니다. 이때 메인 프로그램은 아직 실행되지 않았지만 내부는 이미 제어 상태입니다.
IDE는 ELF 파일의 기호 테이블을 로드하고 함수 이름, 변수 이름을 메모리 주소와 일대일로 매핑합니다. 당신이 보는 main() 진입 주소가 바로 이 과정을 통해 얻어집니다.
네 번째 단계: 중단점은 어떻게 작동하는가?
여기에는 두 가지 메커니즘이 있습니다:
✅ 하드웨어 중단점(권장)
칩 내장 FPB(Flash Patch and Breakpoint Unit) 모듈을 사용하여 지정된 주소에 일치 규칙을 설정합니다. PC 레지스터가 해당 주소를 가리킬 때 자동으로 디버깅 이벤트를 트리거합니다.
장점: 성능에 영향을 주지 않으며 Flash 영역에 적용 가능합니다.
// FPB 존재 여부 확인 예제
if (*(uint32_t*)0xE0002000 & 0x01) {
// FPB가 존재하며 최대 6~8개의 하드웨어 중단점 지원
}
⚠️ 소프트웨어 중단점
대상 주소의 기계어 코드를 0xBE00(BKPT 명령어)로 교체합니다. CPU가 이 지점을 실행할 때 BKPT 예외가 발생하여 실행이 중지됩니다.
제한: RAM 영역에만 사용 가능하며 Flash 내용을 수정하려면 잠금 해제 작업이 필요해 효율이 낮습니다.
📌 팁: 하나의 중단점만 설정할 수 있다면, Flash 중단점 수량 제한을 초과한 것일 가능성이 높습니다!
printf가 UART에 의존하지 않게 하는 방법: ITM + SWO
전통적인 방법은 printf를 UART로 리디렉션하는 것이지만, 이는 하나의 외부 자원을 차지하며 실시성에 영향을 줄 수 있습니다.
더 나은 방법은 ITM(Instrumentation Trace Macrocell)과 SWO 핀을 조합하여 제로 오버헤드 로그 출력을 구현하는 것입니다.
원리 간략 설명
ITM은 가벼운 trace 모듈로 디버거에 문자 스트림을 전송할 수 있습니다. 데이터는 SWO(Single Wire Output) 핀을 비동기 직렬 방식으로 출력되어 ST-Link가 수신한 후 PC로 전달됩니다.
이는 다음과 같은 의미를 가집니다: 어떤 UART를 사용하지 않아도 printf 출력을 볼 수 있습니다!
실제 단계
- 하드웨어 연결
- 타겟 칩의 SWO 핀(보통 PB3)을 ST-Link의 SWO 핀에 연결
- 핀 헤더를 사용하는 경우 Cortex Debug Connector의 10번 핀에 해당
- ITM 및 DWT 활성화
void enable_trace(void) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // trace 기능 활성화
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // 주기 카운터 시작
ITM->TCR = ITM_TCR_DWTENA_Msk | ITM_TCR_ITMENA_Msk;
ITM->TER = 0x01; // 채널 0 활성화
}
- fputc 리디렉션
struct __FILE { int handle; };
FILE __stdout;
int fputc(int ch, FILE *f) {
while (ITM->PORT[0].u32 == 0); // 포트가 준비될 때까지 대기
ITM->PORT[0].u8 = ch; // 문자 전송
return ch;
}
- IDE 설정
- STM32CubeIDE에서 "ITM Viewer" 열기
- SWO 보레이트 설정(보통 CPU 주파수 / 4)
- 컴파일 및 실행하여 출력 확인
💡 효과는 printf("Hello from core!\r\n");가 IDE 콘솔에 직접 나타나는 것과 같으며, 어떤 물리적 시리얼 포트도 필요하지 않습니다!
디버깅 서브시스템 전경도: Cortex-M의 "블랙박스"
정말 강력한 것은 ST-Link가 아니라 Cortex-M 내장의 온칩 디버깅 엔진입니다. 이것이 전속도 제어 디버깅을 구현하는 근본적인 보장입니다.
다음은 주요 모듈 개요입니다:
| 모듈 | 기능 |
|---|---|
| DAP | 물리 인터페이스 컨트롤러, SWD/JTAG 통신 처리 |
| FPB | 하드웨어 중단점 및 Flash 패치 기능 제공 |
| DWT | 데이터 관찰점, 주기 카운팅, 주소 일치 지원 |
| ITM | 다중 채널 trace 출력, 타임스탬프 지원 |
| TPIU/ETB(일부 칩) | 고속 trace 데이터 패킹 출력 |
이러한 모듈들은 함께 ARM의 CoreSight 디버깅 아키텍처를 구성하여 외부 디버거가 CPU 동작에 깊이 개입할 수 있게 합니다.
실전 기법: DWT를 사용한 불법 메모리 액세스 포착
특정 전역 변수가 의도치 않게 수정되는 것을 의심한다면 DWT를 사용하여 데이터 관찰점을 설정할 수 있습니다:
volatile uint32_t bug_flag = 0;
// 쓰기 관찰점 설정
DWT->COMP0 = (uint32_t)&bug_flag; // 주소 모니터링
DWT->MASK0 = 0x0; // 모든 32비트 일치
DWT->FUNCTION0 = DWT_FUNCTION0_DATAVSIZE_BYTE |
DWT_FUNCTION0_LNK1ENA_Msk |
DWT_FUNCTION0_DATAVMATCH_Msk; // 쓰기 작업 시 halt 트리거
누군가 이 변수에 쓰기 작업을 수행하면 CPU가 즉시 중지되고 디버거가 문제가 발생한 코드 라인으로 자동 이동합니다.
수많은 if(bug_flag != 0) 판단보다 훨씬 효율적입니다.
공학 실전: 디버깅이 용이한 제품을 구축하는 방법
많은 엔지니어는 개발 단계에서만 디버거를 사용하고 양산 시에는 인터페이스를 잠급니다. 하지만 좋은 제품 설계는 후유 관리까지 고려해야 합니다.
✅ 모범 사례 목록
- 표준 디버깅 인터페이스 예비
- PCB에 10핀 Cortex Debug 소켓 배치
- 최소한 다음 핀 노출: SWDIO, SWCLK, GND, VREF, NRST
- 실인 방향 표시, 역삽입 방지
- 핵심 핀 재사용 금지
- PA13(SWDIO), PA14(SWCLK), PA15(JTDI), PB3(SWO), PB4(NJTRST)를 다른 용도로 사용하지 않음
- 특히 저전력 모드에서 이러한 핀 상태는 깨우기 및 디버깅 연결에 영향을 줌
- 디버깅 포트 유지
- 릴리스 버전에서도 다음 구성 유지:
// 수면/정지/대기 모드에서도 디버깅 허용
DBGMCU->CR |= DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STOP | DBGMCU_CR_DBG_STANDBY;
- 읽기 보호 합리적 사용
- RDP Level 1 사용: 디버깅 접근 금지, 지우기 및 재프로그래밍 허용
- RDP Level 2 사용 금지: 칩 영구 잠금, 복구 불가
- 신호 무결성 최적화
- SWD 라인 최대 길이 유지(<10cm)
- 고주파 신호선(예: CLK, PWM) 근처 배치 피하기
- 고속 환경에서 SWDIO/SWCLK에 22–100Ω 저항 직렬 연결하여 반사 억제
일반 문제 해결 가이드
❌ 문제 1: 연결 불가, "No target connected"
해결 경로:
- 🔌 타겟 보드에 전원이 공급되는지 확인? VDD 및 VSS 측정
- 🧩 BOOT0가 접지되어 있는지? 높은 레벨로 설정되면 시스템 메모리 모드로 진입
- 🔒 RDP Level 2가 활성화되어 있는지? BOOT0에 1.2V 연결 시도(위험 높음)
- 🔄 외부 리셋 회로가 NRST를 낮은 레벨로 유지하고 있는지?
- 📏 SWD 라인이 너무 길거나 간섭을 받고 있는지? SWD 주파수를 1MHz로 낮춰 시도
❌ 문제 2: 연결은 되지만 중단점이 작동하지 않음
가능한 원인:
- 💾 Flash에 중단점 설정 및 하드웨어 단위 수 초과(일반적으로 6~8개)
- 🧱 SWO 핀이 연결되지 않아 trace buffer가 가득 참
- 🛑 초기화 코드에서 디버깅 모듈이 비활성화됨(예:
__HAL_RCC_DBGMCU_CLK_DISABLE()호출)
해결 방안:
- 일부 중단점을 RAM 영역으로 이동(지역 변수 범위 내)
- 조건 중단점 사용으로 적중 횟수 줄이기
- HAL 초기화 순서 확인, 디버깅 기능이 미리 비활성화되지 않았는지 확인
❌ 문제 3: HardFault 발생 시 대처법
당황하지 마세요! 이것이 바로 ST-Link가 빛을 발하는 순간입니다.
HardFault_Handler에 중단점 설정- 실행이 중단되면 레지스터 뷰에서 다음 확인:
HFSR - 전체 오류 상태
CFSR - 세분화된 오류 유형(MemManage, BusFault, UsageFault)
BFAR - 버스 오류 주소(불법 주소 접근 시)
AFSR - 보조 정보
예를 들어, CFSR의 MMARVALID 비트가 설정되어 있다면 BFAR에는 경계 침범 주소가 저장됩니다.
역어셈블리 창과 함께 정확히 어떤 C 코드가 야포인터에 접근하려고 했는지 파악할 수 있습니다.
마지막으로: "사용법"에서 "원리 이해"로의 도약
ST-Link는 작은 디버깅 프로브처럼 보이지만, 그 뒤에는 세 가지 핵심 기술 계층이 연결되어 있습니다:
- 물리 계층: ST-Link 하드웨어가 USB ↔ SWD 프로토콜 변환 수행
- 칩 계층: Cortex-M 내부가 FPB/DWT/ITM 등 디버깅 모듈 제공
- 도구 체인 계층: IDE가 그래픽 인터페이스 및 자동화 제어 구현
HardFault가 발생하는 순간 이상 주소를 포착하고, ITM을 사용하여 작업 전환 트랙을 실시간으로 추적할 수 있을 때, 당신은 더 이상 "운으로 코드를 수정"하는 초보자가 아니라 임베디드 시스템의 "생명 징후 모니터링 능력"을 진정으로 마스터한 전문가가 됩니다.
향후 더 깊이 탐색하고 싶다면:
- ETB(Embedded Trace Buffer) 학습으로 명령어 수준 추적 구현
- TPIU를 사용해 로직 분석기에 고속 trace 데이터 출력
- FreeRTOS+Tracealyzer와 결합하여 작동 동작 시각화
이러한 고급 기술의 출발점은 바로 당신이 현재 손에 쥔 이 ST-Link입니다.
그러므로 다음에 이 도구를 들 때 기억하세요:
이것은 단순한 다운로더도, 프로브도 아니라 당신과 칩 사이의 첫 번째 언어 통역사입니다.