본문 바로가기

시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리

[Linux][ARM] Coprocessor(코프로세서) Assembly

ARM 프로세스 내 Coprocessor라는 하드웨어가 있습니다. Co-Processor라는 것은 Co-worker랍니다. CPU 혼자 모든 일을 다 할 수 없으니 Co-Processor가 필요한 것입니다.
 
Co-Processor는 C언어 같이 어떤 코드 흐름을 제어하는 일보다는 ARM Core나 Cache을 콘트롤하는 역할을 수행합니다. 그래서 ARM 프로세스로 칩을 디자인하는 개발자는 Co-Processor를 제어하는 명령어를 달달 외우고 있죠.
 
Co-Processor가 있으니 당연히 Co-Processor하고 대화를 해야 겠죠. Co-Processor에게 말을 거는 방법을 역시 레지스터를 이용해서 주어진 명령어를 실행시키면 됩니다.
Co-Processor에게 말을 걸면 Co-Processor가 알아듯는 용도의 레지스터가 따로 있거든요. 
 
그럼 Co-Processor와 통신하는 어셈블리 명령어를 조금 더 알아볼까요? 이 패턴도 어셈블리랑 비슷하니 너무 겁 먹지 마세요.
 
우선 Co-Processor에게 Data를 전달하는 명령어는 3가지로 분류됩니다.
1. Co-Processor 내부 레지스터를 직접 업데이트
2. Co-Processor 내부 레지스터를 ARM 레지스터로 읽거나 반대로 업데이트
3. Co-Processor 내부 레지스터를 직접 메모리에 읽거나 반대로 메모리에 있는 값을 Co-Processor 내부 레지스터에 업데이트
 
Co-Processor Data 명령어를 조금 더 자세히 들여다 볼까요?
 
1) Co-Processor 내부 레지스터를 직접 업데이트
Co-Processor 내부 Register ↔ Co-Processor 내부 Register
 
여기에 붙은 모든 규칙은 Coprocessor 내부에 정의되어 있고 형식은 다음과 같습니다.
CDP Coprocessor번호, Coprocessor 명령어, CRd, CRn
 
Co-Processor 명령어는 Co-Processor마다 준비된 명령어가 다르니 개별적으로 만든 것입니다. 그리고 CRd는 Co-Processor 내부에 있는 레지스터로 Destination 레지스터로 사용되는 레지스터이고, CRn은 Co-Processor 내부에 있는 레지스터로 인자 레지스터입니다.
 
2) Co-Processor 내부 레지스터를 ARM 레지스터로 읽거나 반대로 업데이트
Coprocessor 내부 레지스터 ↔ ARM 레지스터
 
Co-Processor 명령은 ARM core와 Co-Processor와 통신에 쓰이며 PSR명령과 유사한 형식입니다. 
Co-Processor의 레지스터들과 직접 레지스터 값을 교환하는 명령어는 MRC, MCR 인데 M X ← Y 형식입니다.
아래 형식을 기억하면 대략 레지스터를 전달하는 흐름을 알 수 있으니 잊지 마세요.
M X ← Y 형식
 
여기서 R은 ARM Core의 일반 레지스터이고요, C는 Co-Processor의 레지스터를 의미합니다. 
그러니까, 각각 Co-Processor를 제어하는 명령어는 두개 인데요. 각각 레지스터를 저장하는 흐름은 다음과 같습니다.
MRC는 R←C로서 R:=Coprocessor 
MCR은 C←R로서, C:=Register
 
2.1 [MRC Co-Processor 번호, 무조건 0, 레지스터 번호, Co-Processor 레지스터번호, c0, 무조건 0] 포멧 
 
그럼 다음 명령을 실행하면 Co-Processor 15번 1번 레지스터를 r3에 전송하라는 의미입니다.
MRC p15,0x0,r3,c1,c0,0x0   ; p15,0,r3,c1,c0,0 (system control)
 
2.2 [MCR Co-Processor 번호, 무조건 0, 레지스터 번호, Co-Processor 레지스터번호, c0, 무조건 0] 포멧 
 
다음 명령을 실행하면 Co-Processor 15번 2번 레지스터에 r4 값을 전송하라는 의미입니다.
MCR p15, 0, r4, c2, c0, 0
 
이번에는 Co-Processor 15번 1번 레지스터에 r5 값을 전송하라는 명령어입니다.
MCR p15,0x0,r5,c1,c0,0x0 
 
3)  Co-Processor 내부 레지스터를 직접 메모리에 읽거나 반대로 메모리에 있는 값을 Co-Processor 내부 레지스터에 업데이트
Coprocessor 내부 Register ↔ Memory
 
마지막으로, Co-Processor 내부 레지스터와 메모리 사이에 Load, Store할 수 있는 명령이 있는데 바로 LDC, STC입니다. LDC, STC에도 preindex, postindex 방식이 엄연히 존재합니다. 하나 예제를 들면 LDC p15, c2, [r0, 0x100] 이면, Co-Processor 15번의 레지스터 2번에 r0+0x100이 가리키는 곳의 값을 load하라는 의미입니다. 일반 load와 틀림없이 같지만 앞에 Co-Processor 번호인 p15가 붙는다는 것만 다릅니다. LDR은 Register의 R, LDC는 Co-Processor의 C인 것이죠. 
 
Co-Processor 15는 MMU, Cache에 관련된 Co-Processor로서, 이 레지스터를 잘 콘트롤 해야 일이 아주 많습니다.
 
 
 
 
# Reference: For more information on 'Linux Kernel';
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2