반도체

[MCU강의⑦] MCU의 언어

한주엽

<편집자 주> 마이크로컨트롤러유닛(MCU)의 면면을 파헤치는 해설 기사를 게재합니다. MCU는 다양한 전자제품에 탑재되는 핵심 반도체 가운데 하나입니다. 글 싣는 순서는 다음과 같습니다. ①MCU란 무엇인가? ②MCU의 내부 구성 ③CPU란 무엇인가? ④MCU 소프트웨어 개발 ⑤CPU 실행방법 ⑥ALU의 구성 ⑦MCU용 언어 ⑧호스트 PC를 타겟 보드에 연결하는 툴 선택하기 ⑨범용 IO ⑩MCU 개발에 사용되는 소프트웨어 개요 ⑪MCU의 팹 공정.

마이크로컨트롤러유닛(MCU)은 메모리(예 ROM)에 이진(0/1) 패턴으로 저장돼 있는 명령어들을 실행한다. 그러나 이진 명령어들은 사용자 친화적이지 않다. 무슨 뜻인지 사람이 이해하기 어렵다. 따라서 특정 이진 패턴을 대신할 수 있는 언어 시스템으로 프로그램을 작성해야만 한다. 이런 언어 시스템을 프로그래밍 언어라고 부른다. 프로그래머가 MCU의 프로그램 작성을 위해 프로그래밍 언어를 완벽하게 익히는 것이 반드시 필요하다. 프로그래밍 언어에는 여러 가지 종류가 있는데, 이러한 언어들 중 몇 가지를 살펴보자.

‘③CPU란 무엇인가’에서 설명했듯 CPU에 작동을 지시하는 명령어는 ROM에서 0과 1로 이루어진 이진 패턴의 형태로 읽혀진다. 궁극적으로는 어떻게든 이러한 이진(0/1) 패턴들을 생성해내야만 하지만 어떤 패턴이 어떤 명령어인지 한 눈에 파악하기란 매우 어렵다. 따라서 개별 이진 패턴에 이름을 정해주고 이러한 이름을 사용하여 프로그램을 작성하게 된다. 이진 패턴으로 구성된 명령어들을 기계어라고 부르며, 기계어에 사용된 이진 패턴들에 할당된 이름들을 어셈블리 언어 또는 니모닉(mnemonic) 언어라고 한다. 예를 들어, 덧셈은 ‘3부 CPU란 무엇인가’에서 0000 0001로 정의됐다. 이 패턴을 여기서는 ADD라고 부른다면, 0000 0001은 기계어로 작성된 덧셈 명령어가 되며, ADD는 이를 어셈블리 언어로 작성된 명령어가 된다.

MCU 개발자는 각자의 MCU를 위한 기계어와 어셈블리 언어를 결정한다. 따라서 MCU마다 사용되는 언어가 다를 수 있고, 사용하려는 MCU의 어셈블리 언어를 반드시 배워야 한다. MCU가 다른 MCU로 바뀔 때마다 프로그래머는 새로운 MCU의 어셈블리 언어를 배우지 않으면 안 된다. 이는 프로그래머 입장에선 시간 소모가 너무 크다. 고급 컴퓨터 프로그래밍 언어(포트란, 베이직 같은)를 조사한 결과 모든 MCU에 사용할 수 있는 공통 언어로 C가 선택됐다. C는 처음에는 그 서술형 방식이 MCU용 언어로서 적합하기 때문에 채택됐지만 지금은 PC, MCU 및 메인프레임 컴퓨터에서도 널리 사용되고 있다.

기계어

 그림 1. 기계어의 예
그림 1. 기계어의 예

ST마이크로의 MCU인 STM32의 코어텍스-M3 CPU를 이용해 ‘레지스터 8에 있는 값에 1을 더한 뒤 그 합산 결과를 레지스터 8에 저장하라’는 명령어를 기계어로는 어떻게 나타내는 지 살펴보자. 이 같은 명령어들을 나타내는 데 사용되는 패턴은 [1111 0001]로서, 이는 ‘범용 레지스터의 값을 12비트의 수치 값에 더한 뒤 그 합계를 범용 레지스터에 저장하라’는 뜻이다. 여기에 이진수 [0000 1000]이 뒤따르는데, 이는 합산 결과를 저장하게 될 범용 레지스터의 번호이다. 이 경우의 번호는 8(레지스터 8)이다. 연산 대상인 수치는 레지스터 8에 있기 때문에 또 다른 [0000 1000]이 뒤따르게 된다. 마지막으로 추가될 패턴은 수치 ‘1’([0000 0001])이다. 이 모든 패턴들을 더하면 [1111 0001 0000 1000 0000 1000 0000 0001]이 된다. 이 이진 패턴이 ‘레지스터 8에 있는 값에 1을 더한 뒤 그 합산 결과를 레지스터 8에 저장하라’는 기계어 명령어인 것이다(그림 1). 메모리(예컨대, ROM)에 저장된 기계어 패턴을 객체라고 부른다. 프로그래밍 언어로 작성된 프로그램들은 기계어로 번역되어야만 MCU가 이를 실행할 수 있다.

어셈블리 언어(니모닉 언어)

그림 2. 어셈블리 언어의 예
그림 2. 어셈블리 언어의 예
기계어는 이진 패턴들로 구성되어 있으므로 사람이 이러한 포맷으로 조작하기는 매우 어려울 수 밖에 없다. 따라서 기계어 코드에 대략 일대일로 대응되는 니모닉 언어를 사용해 프로그램을 작성하게 된다. 니모닉 언어를 어셈블리 언어라고 한다.

그러면 어셈블리 언어 몇 가지를 살펴보자. 위에 나온 ‘레지스터 8에 있는 값에 1을 더한 뒤 그 합산 결과를 레지스터 8에 저장하라’는 명령어에서 처음의 [1111 0001]은 덧셈을 나타내는데, 이를 ADD.W로 명명할 수 있다. 이런 식의 표현이 니모닉이다. 범용 레지스터인 레지스터 8은 R8으로 표시된다. 가수(더해지는 수)는 #1으로 표시된다(#는 일반적으로 수를 십진법으로 표시하는 데 사용된다). 이 모든 니모닉을 더하면 ADD.W R8 R8 #1 이 되는데, 이것이 우리가 처음에 살펴보았던 기계어의 어셈블리 언어 버전이다(그림 2).

코어텍스-M3가 사용하는 네 가지 산술 연산을 위한 어셈블리 언어는 ADD(덧셈), SUB(뺄셈), MUL(곱셈) 및 SDIV(나눗셈)이다. 논리 연산을 위한 어셈블리 언어는 AND(논리곱), ORR(논리합) 및 EOR(배타적 논리합)이다. 프로그램 내에서 ‘점프’ 하라는 명령어에는 B.W(무조건 분기)와 BL(조건부 분기)이 있다. 또한 아무런 연산도 수행하지 말라는 명령어는 NOP(“no operation”의 축약어)이다.

수행될 연산을 명시하는 기계어 명령어의 니모닉 부분을 ‘연산 코드(operation code: 줄여서 “opcode”)’라고 한다. 연산 코드에 이어지는 기계어 명령어의 주소 값 및 데이터 값 부분을 ‘피연산자(operand)’라고 한다. 끝으로, 어셈블리 언어를 기계어로 번역하는 과정을 ‘어셈블링(assembling)’이라고 한다.

C

니모닉을 이용하면 프로그래밍이 어느 정도까지는 간단해진다. 그러나 니모닉과 기계어 명령어 간에는 일대일 대응 관계가 있기 때문에 여전히 사용하기가 쉽지는 않다. 이런 점을 고려해 컴퓨터에 사용되는 고급 언어인 C가 MCU 프로그래밍에 채택됐다.

어셈블리 언어와 기계어는 특정 CPU용이므로 이처럼 특정 CPU용의 언어로 작성된 프로그램들은 다른 CPU에 사용할 수가 없다. 그러나 C는 CPU와 관계 없이 작성할 수 있으므로 매우 편리하다. 이런 이유 때문에 C는 현재 CPU언어에 흔히 사용되고 있다. C로 작성된 명령어에 대한 보다 상세한 설명은 시판되고 있는 수많은 교재들을 참조하기 바란다.

그림 3. C와 기계어 및 어셈블리 언어의 비교
그림 3. C와 기계어 및 어셈블리 언어의 비교

C와 어셈블리 언어의 관계를 살펴보자. 하지만 그 전에 먼저 C의 while 문장을 이용하여 NOP를 500번 반복하기 위한 기술 방법을 살펴보자. 그림 3을 보기 바란다. 여기에서 볼 수 있는 프로그램은 먼저 변수 m을 정의한 뒤 m을 0으로 치환하고, NOP가 실행될 때마다(__asm("nop");) m에 1을 더한다(++m). 그러다가 마침내 m이 500에 이르면 while 루프에서 벗어난다(while (m<500)). 그림 3은 좌측에서 우측 방향 순서대로 다음 사항들을 보여준다. 이 프로그램을 C로 표현하는 방법, 기계어 명령어들이 저장되는 ROM 주소, 기계어 명령어(16진수), 어셈블리 언어 명령어, 각 명령어들에 대한 설명. 여기서 ??main_5는 주소 0x80001d4에 주어진 이름이다. 이와 유사하게, ??main_4는 주소 0x80001e0의 이름이다. 어셈블리 언어는 주소를 직접 다루지 않고 주소에 주어진 이름을 이용해 주소를 쓴다.

먼저 int에 의해 정의되는 m이 R8에 할당된다. 그러면 m=0가 R8에 0을 넣는다. CMP.W는 R8을 500과 비교한다. R8이 500 이상이면 BGE.N은 프로그램이 ??main_4로 분기하도록 만든다. 그러나 여기서는 R8이 0이므로 분기 없이 ??main_5로 간다. ??main_5는 R8에 1을 더한 뒤 NOP를 실행한다. 그 다음에 CMP.W는 R8을 다시 500과 비교한다. 이번에는 R8이 500 미만이면 BLT.N이 프로그램으로 하여금 ??main_5로 분기하도록 만든다. 따라서 R8은 0에서 시작하여 1, 2, 3 식으로 1씩 증가해나간다. m이 500에 이르면 프로그램은 ??main_5로 분기하는 대신에 ??main_4로 간다. 이 프로그램은 NOP 명령을 500번 반복한다.

이 그림은 C를 읽기가 얼마나 쉬운지를 보여준다. 어셈블리 언어는 기계어보다 훨씬 읽기가 쉽긴 하지만 그래도 이를 이해하기 위해서는 니모닉을 알아야 한다. 그러나 PC 컴파일러는 C를 어셈블리 언어로 자동 번역한 뒤 다시 기계어로 번역해 주므로 프로그래머는 프로그램을 C로 작성하기만 하면 된다.

C만 알면 MCU용 프로그램을 작성할 수 있다고까지 감히 말할 수 있을 것이다. 그렇다고 해도 엔지니어라면 MCU가 주어진 프로그램들을 실제로 어떻게 실행하는지 알아야 한다.

글 : 마사루 스가이(ST마이크로 MMS그룹)

한주엽
webmaster@ddaily.co.kr
기자의 전체기사 보기 기자의 전체기사 보기
디지털데일리가 직접 편집한 뉴스 채널