C 코드 최적화의 필요성
- 임베디드 CPU 한계 : 저전력, 저가로 성능 제한
-> 제한된 자원으로 좋은 성능을 내기위해 필요
+ CPU 구조, 어셈블러, 컴파일러에 대한 이해 필요
일반적인 최적화 방법
- 나눗셈, 나머지 연산을 피함
- 반복을 최적화
- 메모리 보다 빠른 레지스터 사용
- 함수 호출 방식을 최적화
AVR core 구조
- 폰노이만 : 데이터, 프로그램 메모리를 하나의 버스로 사용
- 하버드 : 데이터, 프로그램 메모리를 분리 -> 동시에 명령어와 데이터를 읽고쓰기 가능
-> AVR core는 하버드 아키텍처를 따름
CPU 연산 과정
- fetch : 메모리서 명령어 가져옴
- decode : 명령어에 있는 레지스터 해석
- execute : 실행
AVR core
- single level pipelining 사용
- 한 명령어가 실행되는 동안 다음 명령어가 pre fetch됨
-> 매 클럭 사이클마다 명령어가 실행
- 32개의 8비트 범용 레지스터를 가지고 있음. 이를 잘 활용해서 최적화
- R0 ~ R25 : 데이터 레지스터로 사용
- R26 ~ R31 : 주소 레지스터로 사용
AVR 컴파일러
- AVR studio : gcc 사용. avr-gcc
- code vision : 상용 컴파일러 사용
AVR GCC 최적화 옵션
- 옵션에 따라 코드 크기, 성능 최적화 레벨 지정 가능
ex) -O0, -O1, -O2, -O3, -Os
=> avr-GCC -O3 main.c
*-O0 는 최적화 x, -O3은 가장 높은 성능, -Os는 코드 사이즈 가장 줄임
데이터에 따른 최적화
- 타입에 따른 최적화 : 가능한 크기가 작은 타입 사용
- 범위에 따른 최적화 : 같은 크기더라도 전역보다 지역변수가 유리.
* atmega128에선 전역변수는 SRAM(16비트 어드레스모드)에 할당.
-> 변수 크기가 8비트 크기여도 16비트 크기 어드레스로 할당하여 공간 낭비
static을 이용한 최적화
- 전역 변수에 static 사용시 선언된 파일 내에서만으로 범위 제한.
- 지역 변수의 경우 static 사용 안하는것이 좋다.
어셈블리어를 사용한 최적화
- 적당히 사용하면 좋으나 코드 이식성을 떨어지게함.
- 인라인 어셈블리 : C코드 일부를 어셈블리어로 사용
ex : #define sei() __asm__ __volatile__("sei"::)
- __asm__ : 인라인 어셈블리를 의미하는 지시어
- __volatile__ : 최적화에 의해 없어지는걸 방지
- sei : SREG 레지스터의 I flag(전역 인터럽트 플레그)를 셋
일반 | 인라인 어셈블리 | |
C코드 | #include <avr/io.h> void enable_usart_rx(void) { UCSR0B |= 0x80; }; int main() { enable_usart_rx(); while(1){ } } |
#include <avr/io.h> #define enable_usart_rx() \ __asm__ __volatile__(\ "lds r24, 0x00C1" "\n\t" \ "ori r24, 0x80" "\n\t" \ "sts 0x00C1, r24" \ ::) int main() { enable_usart_rx(); while(1){ } } |
AVR 코드 메모리 사용량 | 296bytes | 226bytes |
최적화 옵션 | -Os | -Os |
Cycle Counter
- 어셈블리 명령어가 실행된 회수
- 실행이 끝난 후 값 = 총 실행된 명령어 회수
'컴퓨터과학 > 임베디드' 카테고리의 다른 글
조금씩 임베디드 - 9. 링커 스크립트 (0) | 2022.08.17 |
---|---|
조금씩 임베디드 - 8. ARM 어셈블리어 (0) | 2022.08.17 |
조금씩 임베디드 - 6. 임베디드 시스템 개발과 부트로더 (0) | 2022.06.22 |
조금씩 임베디드 - 5. 주변장치 2 (0) | 2022.06.22 |
조금씩 임베디드 - 4. 주변장치 1 (0) | 2022.06.19 |