null
vuild_
Nodes
Flows
Hubs
Login
MENU
GO
Notifications
Login
⌂
Embedded C 학습 로드맵
Structure
what-is-embedded-system
•
임베디드 SW vs 일반 SW
•
CPU 아키텍처 기초
•
메모리 레이아웃
development-environment
•
툴체인 개요
•
프로젝트 & 라이브러리 구조
•
플래싱 & 디버깅
c-language-for-embedded
•
데이터 타입과 이식성
•
비트 연산
•
volatile과 최적화
gpio
•
GPIO 개념
•
출력 제어
•
입력 읽기 & 디바운스
basic-uart
•
UART 프레이밍 & 보레이트
•
송수신 구현
•
UART 디버그 출력
Flow Structure
비트 연산
9 / 15
GPIO 개념
☆ Star
↗ Full
volatile과 최적화
#volatile
#compiler-optimization
#dsync
#isync
#memory-barrier
@devpc
|
2026-04-02 05:47:09
|
GET /api/v1/flows/10/nodes/202?fv=2&nv=1
Context:
Flow v2
→
Node v1
0
Views
3
Calls
# volatile과 최적화 ## 컴파일러 최적화란? 컴파일러는 코드를 더 빠르게 실행되도록 **최적화**합니다. 그 중 하나가 **불필요해 보이는 메모리 접근을 제거**하는 것입니다. ```c /* 소스 코드 */ uint32_t status; status = *pReg; /* 레지스터 읽기 */ status = *pReg; /* 또 읽기 — 컴파일러 눈에는 중복 */ /* 최적화 후 (컴파일러 관점) */ status = *pReg; /* 두 번째 읽기는 "불필요"하므로 제거됨 */ ``` 컴파일러는 `*pReg`의 값이 프로그램 내에서 변하지 않았다고 판단하여 두 번째 읽기를 제거할 수 있습니다. 하지만 `*pReg`가 **하드웨어 레지스터**라면, 외부에서 값이 바뀔 수 있기 때문에 이 최적화는 잘못된 것입니다. --- ## volatile 키워드 `volatile`은 컴파일러에게 **"이 변수는 언제든지 외부에서 변할 수 있으니 최적화하지 말라"** 고 지시하는 키워드입니다. ```c /* ❌ volatile 없음 — 최적화로 두 번째 읽기가 제거될 수 있음 */ uint32_t *pReg = (uint32_t*)0xF003A000; uint32_t val1 = *pReg; uint32_t val2 = *pReg; /* 최적화로 사라질 수 있음 */ /* ✅ volatile 있음 — 두 번 모두 실제 메모리 접근 보장 */ volatile uint32_t *pReg = (volatile uint32_t*)0xF003A000; uint32_t val1 = *pReg; uint32_t val2 = *pReg; /* 반드시 두 번 읽음 */ ``` ### volatile이 필요한 상황 ``` 1. 하드웨어 레지스터 (주변장치) → 프로그램 외부(HW)에서 값이 바뀜 2. 인터럽트 핸들러와 공유하는 변수 → 인터럽트 발생 시 외부에서 값이 바뀜 3. 멀티코어 / 멀티스레드 공유 변수 → 다른 코어/스레드에서 값이 바뀜 ``` --- ## volatile의 한계 `volatile`은 컴파일러 최적화를 막을 뿐, **CPU 수준의 메모리 재배열**은 막지 못합니다. ``` [ CPU 파이프라인의 메모리 재배열 ] 소스 코드 순서: CPU 실행 순서 (재배열 가능): [1] 레지스터 설정 [1] 레지스터 설정 [2] Enable 비트 세팅 [3] 다음 코드 실행 ← 재배열! [3] 다음 코드 실행 [2] Enable 비트 세팅 ``` 특히 메모리 배리어(Memory Barrier)가 필요한 경우: - 레지스터 설정 순서가 중요한 초기화 코드 - 캐시/파이프라인이 있는 고성능 코어 --- ## TC37x: __dsync / __isync TriCore에는 파이프라인 재배열 및 캐시 일관성을 보장하는 특수 명령어가 있습니다. ```c /* __dsync — Data Synchronization Barrier */ __dsync(); /* * 이 명령어 이전의 모든 메모리(데이터) 접근이 * 완료될 때까지 파이프라인을 대기시킵니다. * → 레지스터 설정 후 다음 코드로 넘어가기 전에 사용 */ /* __isync — Instruction Synchronization Barrier */ __isync(); /* * 이 명령어 이전의 파이프라인을 모두 비웁니다. * → 코드/설정 변경 후 새 명령어를 가져오기 전에 사용 * → 예: MPU 설정 변경 후, 캐시 flush 후 */ ``` ### 사용 예시 ```c /* 클럭 분주 레지스터 설정 */ MODULE_SCU.CCUCON0.U = newClockConfig; __dsync(); /* 설정이 HW에 반영될 때까지 대기 */ /* 이후 코드는 새 클럭 설정이 적용된 상태에서 실행됨 */ ``` ``` [ __dsync 없을 때의 위험 ] CPU 파이프라인: |─────|──────────────|──────────────| | WR | (파이프라인) | 다음 코드 실행 | ← 아직 레지스터 설정 반영 안 됨! |─────|──────────────|──────────────| __dsync 있을 때: |─────| 대기 |─────────────────| | WR | ~~~~~~~~~~| 다음 코드 실행 | ← 레지스터 설정 완료 후 실행 |─────| |─────────────────| ``` ### iLLD에서의 사용 iLLD 드라이버 내부에는 필요한 곳에 `__dsync()`/`__isync()`가 이미 포함되어 있습니다. iLLD API를 사용하는 경우 직접 호출할 필요는 거의 없지만, 레지스터를 직접 조작할 때는 명시적으로 삽입해야 합니다. --- ## 다른 MCU와의 차이점 > ⚠️ 다른 환경에서 넘어온다면 이런 점이 다를 수 있습니다. - **volatile 자체**: 모든 C 컴파일러에서 동일하게 동작. 이식성 100%. - **메모리 배리어**: ARM Cortex-M은 `__DMB()`, `__DSB()`, `__ISB()` (CMSIS 매크로) 사용. 역할은 동일하지만 함수 이름이 다름. - **단순 MCU (Cortex-M0/M0+)**: 파이프라인이 짧아 재배열 문제가 적음. TC37x처럼 복잡한 파이프라인에서는 배리어가 더 중요해짐.
비트 연산
GPIO 개념
// COMMENTS
Newest First
ON THIS PAGE
No content selected.