본문 바로가기
취미/독서

임베디드 프로그래밍 C코드 최적화

by 빵케잌 2020. 2. 10.

* 개론

임베디드 시스템은 범용의 목적이 아닌 특수 목적을 가지고 제작되기 때문에 시스템을 구성하는 장치나 소프트웨어가 제한적이다.

임베디드 시스템의 구현은 HW로만 할 수도 있고, HW와 SW를 병행하여 구현할 수도 있다. HW만 사용해서 구현한다면 빠른 속도를 지원하지만, 기능을 수정하거나 확장하기가 어렵다. 즉, 다시 추가 설계를 하여 하드웨어를 다시 생산해야 하기 때문에 시간과 비용이 많이 소비된다.

임베디드 프로세서를 선택할 때 가격, 전력소모, 성능, 크기 등을 고려해야 한다.

임베디드 소프트웨어 개발환경은 실행환경이 다르다. 이를 크로스 개발환경이라고 한다.

개발환경에 툴체인(크로스 컴파일러, 크로스 어셈블러, 링커/로케이터 등으로 구성)을 구성하여 SW를 개발하게 된다. 그것을 케이블과 소프트웨어를 통해서 타겟 시스템에 옮긴다.

※ 로케이터 : 오브젝트 파일을 타겟 보드의 메모리 주소를 할당하는 것

※ JTAG 케이블은 타겟 시스템에 다운로드뿐만아니라 플래시 메모리에 프로그램을 탑재하거나 디버깅 기능도 가능하다.

스타트업 코드는 하드웨어를 초기화, 인터럽트 벡터 테이블을 생성하고 메모리 검사, 스택과 힙을 생성하고 초기화하는 동작을 한다. C를 인식하기 전의 코드이기 때문에 어셈블리 코드로 작성해야 한다.

* 하드웨어 제어

임베디드 환경에서는 시스템에서 사용되는 주변기기가 다르므로 공통적인 기능을 제공하는 함수가 없다. 

주변장치는 마이크로프로세서에서 메모리로 인식되며, memory mapped I/O와 I/O mapped I/O 방식이 있다. 전자는 개발자가 지정하는 것이고, 후자는 지정된 메모리를 사용하는 방법이며, ARM은 전자, 인텔은 후자의 방식을 사용한다.

입출력 포트의 모드를 레지스터를 통해 설정하고, 데이터 입출력 레지스터에 값을 써서 포트를 거쳐 장치에 데이터가 전달된다(읽기는 반대). 레지스터를 사용하려면 레지스터가 사용할 메모리를 선언해야 한다.

-> 여기서부터 비트연산이 시작된다.

* 기본 동작 코드
1. 특정 비트 on, off
a |= (0x1 << 5) + (0x3 << 2) ;  // 2, 3, 5번 bit set
a &= ~((0x1 << 5) + (0x3 << 2)); // 2, 3, 5번 bit clear
2. 특정 비트 반전
a ^= (0x1 << 5) + (0x3 << 2); // 2, 3, 5번 bit clear
3. 비트 검사
        a & (0x1 << 5)
4. 비트 추출
a & 0x7 // [6:4] 비트 추출 

* 비트 연산의 활용
x를 더했을 때 무조건 4의 배수로 만드는 코드
start += x;
start += 0x3;
start &=  ~(0x3);

* 매크로
매크로는 각 인자를 괄호로 묶어주고 전체 수식 또한 괄호로 묶어줘야 한다.

* 매크로를 활용한 주소접근 최적화
#define PA              (*(volatile unsigned char *) 0x30000000)
PA |= (0x7 << 5);

-> 포인터를 두면 메모리에 값을 읽거나 쓸 때 한 단계를 더 거쳐야한다. 매크로를 이용하면 속도 효율성을 높일 수 있다.

Ch4. 컴파일러 최적화, 너무 믿지 마세요

volatile의 기능적 의미는 no-cache이다.  하드웨어가 사용하는 메모리는 항상 volatile로 선언하기

Ch6. 포인터에 대한 오해와 진실

 1. 메모리는 포인터 없이 직접 접근할 수 있다.
 2. 포인터끼리의 연산은 -만 가능하다.
 3. 배열의 이름은 상수이다.
 4. 배열 이름은 배열 첫 요소에 대한 주소이고, &배열 이름은 배열의 시작 주소다.
 5. 배열 첨자( [] )에 음수 값을 사용할 수 있다.

* 포인터를 빠르게 하는 방법 : 포인터 체인을 제거하라.

* 구조체 포인터에서 구조체 멤버의 멤버에 접근하는 것이 빈번히 발생하면 이를 제거하는 것이 좋다.
ex) a->p1->x =0;
     a->p1->y = 0;
    a->p1->z = 0;

-> struct Point *k = a->p1;
   k->x = 0;
   k->y = 0;
   k->z = 0;

* 속도에서는 배열이, 메모리에서는 포인터가 효율적이다. 포인터는 큰 메모리의 이동을 효과적으로 수행시킬 수 있는 방법이다.

* if문 vs switch문
 다중 분기문은 if문보다 switch문이 속도, 메모리 모두 효율적이다. ( 연산을 한 번만 수행하고 이를 변수에 저장하여 사용하기 때문)
 if문을 쓸 경우 선택될 확률이 높은 것을 앞쪽에 둔다.

'취미 > 독서' 카테고리의 다른 글

부의 추월차선:직장인 편  (0) 2020.05.02
인생의 격차는 30대에 만들어진다.  (0) 2020.03.21
미비포유  (1) 2016.06.19
귀환(여행의 기술3)  (0) 2016.06.18
풍경, 예술(여행의 기술2)  (0) 2016.06.18