'컴파일러'에 해당되는 글 3건

  1. 2017.03.18 인터럽트와 volatile 지시자 1
  2. 2014.06.14 Assembler의 장점
  3. 2014.04.20 변수 vs. 메모리
Embedded Lecture/Arduino2017. 3. 18. 16:33


AVR 칩을 사용하는 Arduino Uno 보드 혹은 다른 임베디드(Embedded) 시스템에서 전역 변수(globlal variable)는 주메모리인 SRAM에 저장되고, 지역 변수(local variable)는 레지스터(register) 영역에 저장됩니다. 그러나 인터럽트 서비스 루틴(ISR)에서의 지역 변수는 SRAM의 Stack 영역에 저장되므로 빠른 인터럽트 처리를 위해서 루틴 내에 잦은 지역 변수의 사용은 바람직하지 않다는 것입니다.


사용자의 코드를 컴파일러(Compiler)는 나름데로 최적화시키는데, 예를 들어 사용자가 전역 변수 지정을 하였지만 그 값이 시종일관 변하지 않는 변수는 컴파일러가 임의로 상수처럼 여겨 레지스터 영역에 저장하게 한다는 것입니다. 물론 한정된 레지스터 영역의 리소스를 낭비하는 것 아니냐고 생각할 수 있지만, 컴파일러 입장에서는 빠른 실행 속도를 위해서 SRAM 보다는 레지스터 영역에 저장한다는 것입니다.


그러나 임베디드 시스템의 경우에 인터럽트 서비스 루틴에서 전역 변수의 갱신이 있었다 하더라도, 컴파일러는 실제 런타임 시 처럼 외부에서 인터럽트가 걸리는 상황이 아니므로 그 값은 변하지 않는다고 생각하여 결국 레지스터에 저장하고 주메모리 상에 변경된 값의 반영이 없다는 것입니다.


이런 상황을 위해서 지시자(directive)인 'volatile'의 선언은 변수형 앞에 두어 컴파일러가 임의로 판단하여 그 변수를 레지스터에 할당하지 못하도록 한다는 것입니다. 즉, 어떤 변수를 volatile로 지정하면 그 변수는 레지스터의 임시 저장소가 아니라 SRAM에서 직접 읽어오도록 컴파일한다는 것입니다.


결론적으로 Arduino 코딩의 경우 보통은 volatile로 정의할 필요는 없으나 인터럽트 서비스 루틴 내부에서 그 값이 변경되는 변수는 반드시 volatile로 선언해야 실시간으로 변경되는 데이터 값을 ISR 루틴 외부에서 읽어올 수 있게 된다는 것입니다.


int pin = 13;

volatile int state = LOW;


void setup()

{

  pinMode(pin, OUTPUT);

  attachInterrupt(0, blink, CHANGE);

}


void loop()

{

  digitalWrite(pin, state);

}


void blink()

{

  state = !state;

}


위의 예제에서 blink() 함수는 인터럽트 서비스 루틴으로 0번 인터럽트 핀의 상태가 변할 때마다 13번에 연결된 LED를 교대로 점멸하게 됩니다. 만일 state 변수에 volatile 선언이 없었다면, 비록 blink() 함수 내에 state 값이 변하더라도 이 함수를 벗어나서는 state 값이 갱신되지 않기 때문에 0번 인터럽트의 상태 변화에 LED의 토글(toggle) 현상이 보이지 않는다는 것입니다.



'Embedded Lecture > Arduino' 카테고리의 다른 글

아날로그 입력 및 온도계 예제  (0) 2017.03.19
아날로그 입력  (0) 2017.03.18
아두이노의 TWI(I2C) 통신  (0) 2017.03.18
아두이노의 시리얼 통신  (0) 2017.03.18
인터럽트의 처리(1)  (0) 2017.03.12
Posted by Nature & Life



과거에는 PC DOS 응용프로그램 개발시 어떻게 메모리를 효율적으로 관리하느냐가 관건이었던 적이 있습니다. 당시에는 메모리 집적기술의 한계로 PC에 1Mbyte를 설치하였다면 큰 자랑꺼리였으며 가격도 비쌌던 시절였습니다. 그러므로 응용프로그램은 DOS가 사용하는 영역의 나머지인 상위메모리를 사용하곤 하였습니다.


하지만 근래에는 메모리가 부족하여 응용프로그램을 실행하지 못하는 경우는 거의 없습니다. CPU의 속도와 메모리 용량 그리고 대용량 하드디스크의 발전은 더이상 가독성이 떨어지는 낮은 수준(low level)의 언어 사용은 물론 엄격한 메모리 관리의 부담을 덜어주게 되었습니다.


그러므로 언제부턴가 어셈블러(ASM)나 C-언어가 아닌 사용자에 즉, 사람에게 친숙한 높은 수준(high level)의 언어인 비쥬얼베이직(Visual Basic), C++ 언어 등이 대세가 되었습니다. 그럼에도 불구하고 AVR이나 PIC계열의 마이크로콘트롤러(MCU)에서는 여전히 낮은 수준의 프로그램 언어를 사용하고 있습니다.


MCU의 속도나 메모리 용량 등과 같은 성능의 발전과 avr-gcc와 같은 컴파일러의 지속적인 향상에 힘입어 요즈음은 어셈블러보다는 C-언어를 선호하게 된 것도 사실입니다. C-언어로 작성해 굳이 오버헤드(overhead)가 있을지라도 속도와 메모리 같은 개선된 MCU 성능으로 적용하는데 크게 문제가 되지 않은다는 것입니다.


하지만 아직도 상업적인 용도의 AVR 펌웨어(firmware)는 여전히 어셈블러를 사용하고 있습니다. 고성능의 MCU를 사용하여 단가를 높이기 보다는 합리적인 성능의 자원에 펌웨어를 어셈블러로 정교하게 작성하고 최적화하여 시장 경쟁력을 얻는다는 것입니다.





그러므로 어셈블러는 적어도 한번은 다루어봐야 하는 언어로 C-언어와 비교하여 다음과 같은 장점을 갖습니다.


1) 새로운 언어를 배운다는 스트레스는 문법을 익혀야 한다는 부담일 것입니다. 하지만 어셈블러는 문법이 C-언어에 비해서 매우 간단하다는 것입니다. 어셈블러는 원하는 코드 구현 자체가 까다로운 것이지 문법을 처음 익히기는 매우 쉽다는 것입니다. 


구현이 까다롭다는 것은 하드웨어에 대해 일일이 알아야 한다는 것과 아마도 120여개에 이르는 다양한 AVR 명령어가 존재하기 때문일 것입니다. 그러나 다양한 명령어는 기능이 유사한 소위 파생된 명령어로 인하여 용도별로 분류하면 사실상 몇 종류가 되지 않으며, 필요시 그때 그때 가져다 사용하면 됩니다.


2) 어셈블러를 다루면 특히 메모리 영역과 같은 하드웨어를 자세하게 익힐 수 있으며 이러한 지식은 향후 C-언어로 구현시 최적화된 코드를 생성할 수 있는 기회를 제공한다는 것입니다.


기존의 C-언어에서는 하드웨어를 자세히 다룰 필요는 없었습니다. 왜냐하면 사용코자 하는 AVR를 지정하면 컴파일러가 알아서 해주기 때문입니다. 메모리가 부족하면 컴파일러는 이를 사용자에게 일러주고 사용자는 불필요한 변수를 삭제해주면 되었기 때문입니다.


3) 정밀한 타이밍을 요구하는 펌웨어를 작성할 수 있습니다. 


어셈블러 명령어는 1~2 사이클의 클럭을 필요로 하며 사용자는 이를 직접 보면서 다루기 때문입니다. 하지만 C-언어를 사용할 때는 해당코드가 컴파일 후에 얼마의 클럭을 요구하는지 알 수가 없고, 알아낸다 하더라도 1us 정도의 정밀한 타이밍은 사실상 불가능하다는 것입니다. 예를 들어, 브러쉬리스(brushless) 모터를 제어시에 C-언어로 작성된 펌웨어는 고속 회전 영역에서 제어 불능상태가 될 수 있다는 것입니다.







'Embedded Programming > Assembler' 카테고리의 다른 글

Assembly어의 문장 형식  (6) 2014.06.26
식별자와 상수  (0) 2014.06.16
Posted by Nature & Life
Embedded Programming/AVR 2014. 4. 20. 21:51



1) 전역변수(Global variable)


SRAM에 저장되어 있다가 이를 사용할 때마다 register로 읽혀지며 사용이 끝나면 다시 SRAM에 저장되므로 처리속도가 늦은 변수로, 특별히 속성을 정하지 않고 변수를 정의하면 SRAM 영역에 저장됩니다. 게다가 Compiler는 이를 변수로 처리하지 않고 그 상황에서 이 변수가 갖는 값에 해당하는 상수로 처리하는 경우가 있는데 이런 상황에서 여러 함수가 이 변수를 공유하여 사용하는데 문제를 야기하고 컴파일 시 최적화 옵션에 따라서도 영향을 받게 됩니다.


특히 인터럽트 서비스 루틴(Interrupt Serve Routine, ISR) 함수와 그 밖의 함수 사이에 공유하는 전역변수의 경우에 흔하게 발생하며 이를 방지하기 위해서는 전역변수에 'volatile'을 선언해 주어야 합니다.

ex) volatile unsigned char zc_count;


2) 지역변수(Local variable)


ATmega8의 경우, 다른 MCU에서처럼 지역변수를 Stack에 저장하는 것이 아닌 32개의 General Purpose Registers(GPFs)로 처리하므로 항상 처리속도가 빠릅니다. 하지만 ISR에 사용되는 변수는 특성상 Stack에 저장합니다.


3) 정적변수(static variable) 


함수가 시작될 때 register로 옮겨지고 함수가 종료될 때 SRAM으로 다시 옮겨져 저장되므로 만일 함수 내에서 여러 번 사용된다면 전역변수보다 정적변수가 처리속도가 증가하게 됩니다.





4) Flash Memory에 데이터의 저장


물론 메모리 용량이 크고 고성능의 MCU를 사용하면 문제가 되지는 않지만 가격대 성능비 등을 고려하여 그렇지 못한 경우, 변수로 사용해야 할 SRAM 용량을 절약할 필요가 있습니다. 만일 SRAM 용량이 부족하게 되면 Stack overflow가 발생하거나 프로그램이 오동작을 할 수 있습니다.


avr-gcc에서 상수 데이터를 프로그램이 적재되는 Flash Memory에 저장하기 위해서는 먼저 상수 데이터를 전역변수처럼 함수의 밖에서 정의하고 이때 상수가 byte 데이터라면 prog_char 또는 prog_uchar로 선언하며 읽을 때문 pgm_read_byte() 또는 pgm_read_word() 함수를 사용합니다.


참고로 비록 상수 데이터를 prog_char 또는 prog_uchar로 정의하더라도 이들이 함수 내에서 정의되면 컴파일 시 SRAM 변수로 처리되므로 주의가 요구되며, 일반적으로 Flash Memory는 SRAM에 비하여 용량이 훨씬 크므로 유용하게 사용할 수 있습니다.


5) EEPROM에 데이터의 저장


AVR에서 EEPROM을 I/O register를 사용하여야 접근할 수 있으므로 avr-gcc 에서는 EEPROM을 eeprom_read_byte() 혹은 eeprom_write_byte() 함수로 각각 읽기, 쓰기가 가능합니다.



'Embedded Programming > AVR ' 카테고리의 다른 글

다양한 AVR Package 비교  (0) 2014.06.14
AVR의 메모리 구조  (3) 2014.04.20
AVR이란?  (0) 2014.03.11
부트로더란?  (0) 2012.12.10
Posted by Nature & Life