반도체 소프트웨어

요약 4. ARM 어셈블리 명령어 본문

CPU Architecture/ARM Architecture

요약 4. ARM 어셈블리 명령어

반도체 소프트웨어 엔지니어 2023. 12. 30. 16:47
  1. 개요
    • 함수 시작과 끝에 주로 실행하는 어셈블리어 루틴
      • stp: 레지스터 -> 스택 푸시 명령어
      • ldp: 레지스터 -> 스택 팝 명령어
    • ARM프로세서와 대화할 수 있는 언어 ex) ARM 프로세서에게 연산 시키기
    • 어셈블리 명령어 실행한 결과는 레지스터에 저장
    • 어셈블리 명령어 기본 형태 ex) "MOV <Xt>, #imm" == "<Xt> = #imm"
    • 어셈블리 명령어 종류
    • 어셈블리어는 유닛 단위, 루틴 단위로 공부하는 게 좋음
    • 어셈블리어를 기계적으로 외우기보다는 해당 명령어가 ARM 프로세서 내부적으로 어떻게 동작하는지 원리를 파악하는 것이 중요
  2. 어셈블리 명령어의 실전 프로젝트 활용 사례
    • 부트로더 개발
      • 부트로더: 시스템 전원 인가 후 최초로 실행되는 소프트웨어
      • 스타트업 코드: 부트로더에서 가장 먼저 실행되는 코드
        1. 스택 초기화
        2. MMU 설정
        3. 시스템 레지스터 설정
        4. 익셉션 벡터 베이스 주소 설정
    • ARM 아키텍처의 기능을 활용한 드라이버 구현
      • ARM 아키텍처의 기능을 활용해 모듈을 구현하려면 어셈블리 명령어 입력 필요
      • 브링업 = 하드웨어 초기화
      • 하이퍼바이저: 두 개 이상 OS를 사용하는 시스템 아키텍처
    • 성능 최적화
    • 디버깅
  3. 어셈블리 명령어 정리
    • Move
      • mov: 레지스터에 상수 설정 및 레지스터 데이터 옮길때, 대입할때 사용
      • mvn: not 연산한 결과를 대입 <Rd> = ~#imm (2의보수, 음수 처리시에 주로 사용)
    • 산술 명령어
      • add: 단순 덧셈 연산뿐만아니라 구조체 멤버 접근시 주소 계산에도 사용함
      • sub: 단순 뺄셈 연산뿐만 아니라 sp, 스택 공간 확보시에도 많이 사용 ex) sub r13, r13, #3c
      • adc: add와 거의 유사하며 마지막에 캐리를 더함(add이후에 1을 더하는것)
      • sbc: <Rd> = <Rn> - <Rm> - ~(Carry)
      • rsb: 오퍼랜드에 대해 반대 순서로 뺄셈 연산을 수행하는 명령어
      • rsc: <Rd> = <Rm> - <Rn> - ~(carry)
    • 비트 시프트 명령어: 곱셈, 나눗셈으로 주로 사용
      • LSL: << 
        1. 지정된 크기만큼 왼쪽으로 비트 시프트(엄청 많이 사용)
        2. 스타트업 코드에서 많이 사용, 성능이 좋은 명령어
        3. add 명령어와 함께 사용되기도 함 ex) ADD R1, R1, R0, LSL #4 : R0를 비트시프트해서 저장한뒤, 그 값을 R1과 더한 값을 R1에 저장
      • LSR: >>
      • ASR: >>>
        1. LSR 한 뒤에 MSB가 1이면, 오른쪽 비트 시프트시에 0으로 바뀌는 비트를 1로 변경(보통 맨 왼쪽이 1로 변경됨)
      • ROR: X를 Y만큼 오른쪽으로 Rotate
        1. 오른쪽 비트 시프트되는 만큼을 MSB에 업데이트
        2. ROR RO, RO, #4: 4비트 만큼 로테이트
    • 논리비트 명령어
      • AND: 비트 AND
      • ORR: 비트 OR
      • ORN: 비트 OR NOT(X OR ~Y)
      • EOR: 비트 Exclusive OR
      • BIC: 비트 클리어 연산(X AND ~Y), 특정 비트(커널 비트 설정 등)를 클리어 할 때 주로 사용
    • 메모리 연산 명령어

      • H, SH, B 등 접미사는 다양하고 작은 비트단위로 데이터를 다룰때 사용
      • 보통 메인메모리보다는 캐시메모리에 접근할 확률이 높음
      • 메모리에 접근하는 명령어여서 주의깊게 다양한 관점에서 분석이 필요
      • LDR: 메모리 -> 레지스터로 데이터 저장
        1. 해당 메모리 주소에 접근해서 데이터를 레지스터에 저장
        2. 포인터 타입으로 선언된 구조체 필드에 접근할 때 자주 사용
        3. 포인터 데이터형 접근시 많이 사용
        4. 사용하다가 데이터 어보트 발생하는 경우가 꽤 있음
        5. LDR 명령어 형태
          • LDR <Rd>, <addr> ==> <Rd> = *<addr>
          • LDR <Rd>, [Rn, #imm] ==> <Rd> = *(Rn + #imm)
      • STR: 레지스터 -> 메모리로 데이터 저장
        1. 메모리 주소에 레지스터 데이터를 저장
        2. 구조체 필드에 저장할때도 많이 사용
        3. STR 명령어 형태(다른 명령어랑 조금다름): 왼쪽에서 오른쪽
          • STR <Rd>, <addr> ==> *<addr> = <Rd>
          • STR <Rd>, [Rn, #imm] ==> *(Rn + imm) = <Rd>
      • 접근하는 메모리 주소가 올바르지 않은 경우
        1. ARMv7: data abort 익셉션 발생
        2. ARVv8: synchronous 익셉션 발생
    • ARMv7(A32) 플래그 설정 명령어
      • 일반적인 어셈블리어와 다르게 플래그 설정 명령어들은 cpsr 플래그 비트를 업데이트하는 동작을 실행!!
      • 변수와 비트 연산을 수행한 결과에 따라 조건부로 분기하는 루틴에서 조건부 플래그를 많이 사용
      • 즉 아래 명령어로 연산한 결과가 CPSR의 컨디션 플래그를 업데이트
      • 종류
        1. CMP(COMPARE): Rn - op2 결과를 컨디션 플래그 해석에 맞게 비트를 업데이트 ex) CMP Rn, op2 에서 Rn - op2가 음수이면 CPSR.N 플래그를 1로 설정
        2. CMN(COMPARE NEGATIVE): Rn + op2 결과를 컨디션 플래그 해석에 맞게 업데이트
        3. TST(TEST): Rn & op2 (어떤 비트가 설정되었는지 확인할때 주로 사용)
        4. TEQ(test equivalence): Rn ^ op2
      • 컨디션 플래그 종류(해석)
        1. N: 연산 결과 음수일때 1로 설정
        2. Z: 연산 결과가 0일때 1로 설정
        3. C: 캐리인경우 1로 설정
        4. V: 오버플로우인경우 1로 설정
      • 보통 플래그 설정 다음에 조건부 분기 명령어가 쓰임
        1. ARM 아키텍처는 컨디션 플래그에 따라 수행 여부가 결정되는 조건부 실행 플래그를 지원
          1. 컨디션 비트값에 따라서 조건부 코드에서 다르게 분기
          2. 즉 조건문을 어셈블리어로 변환할 때 ARM 아키텍처의 플래그 설정값을 사용하여 표현
        2. 사례: BEQ 0x2000000 - cpsr 컨디션 플래그 비트 중 Z값이 1이면 0x2000000으로 분기
      • 조건부 플래그와 접미사 (https://www.dongs.kr/post_arm.html -> 좋은 CV 블로그), (B.COND 타입의 명령어에서 COND으로 지정될 수 있다)
    • 조건부 분기 명령어 (분기 명령어: 프로그램 흐름 바꾸는 명령어) (비트연산은 ARM 코어 내 ALU에서 처리되기에 실행 속도가 빠름!)
      • B (offset): 지정된 주소로 분기 = 지정된 주소를 PC 레지스터에 저장하는 것(스타트업 코드에서 많이 보임)
      • BL (offset): 지정된 주소로 분기하면서 링크 레지스터를 업데이트 (복귀할 주소), (c언어 함수 호출시에 주로 보임)
      • BR <Rt>: 레지스터의 값으로 분기
      • BLR <Rt>: 레지스터의 값으로 분기하면서 링크 레지스터를 업데이트(함수 포인터 사용시에 주로 보임)
      • CBZ (ARMv8에서만 사용)
        1. CBZ Rt, <label> 
        2. Rt 레지스터값이 0이면 지정된 주소나 레이블로 브랜치, 예시: cbz w26, 0xFFFFFFC010019E4C
        3. 수도코드: if (Rt == 0) b <label> else 다음 주소 명령어 실행
      • CBNZ
        1. cbnz w26, 0xFFFFFFC010019E4C
        2. Rt 레지스터값이 0이 아니면 지정된 주소나 레이블로 브랜치
        3. 수도코드: if (Rt != 0) b <label> else 다음 주소 명령어 실행
      • TBZ(test bit and branch if zero)
        1. test bit: 특정 비트가 0인지 1인지 여부를 확인하는 동작
        2. TBZ Rt, #imm, <label>
        3. Rt 레지스터 값의 #imm번째 비트가 클리어됐으면(0이면) 지정된 레이블로 분기
        4. if !(Rt & 0b#imm) b <label> else 다음 주소 명령어 실행
        5. tbz w26, #1, 0xFFFFFFC010019E4C == if !(w26 & 0b0010) else 다음 주소 명령어 실행(맨 오른쪽 비트가 0번째 비트)
        6. if (thread_flags & _TIF_NEED_RESCHED) {} 같은 코드가 tbz 사용 예시
      • TBNZ
        1. Rt 레지스터값의 #imm번째 비트가 1로 셋팅됐으면 지정된 레이블로 분기
        2. if (Rt & 0b#imm) b <label> else 다음 주소 명령어 실행
    • 트랩 명령어
      • 단독으로 실행되는 명령어
      • 다른 어셈블리어와 다르게 범용 레지스터를 입력으로 지정하지 않아도 실행됨
      • 시스템 컨트롤 명령어
      • 트랩 유발
      • 익셉션 유발
      • 명령어 실행시 소프트웨어적으로 익셉션 유발
      • SVC
        1. 슈퍼바이저 콜(supervisor call)
        2. 유저 애플리케이션 -> 커널 공간으로 이동
        3. (ARMv7) user 모드 -> supervisor 모드로 스위칭

          • Svc호출시 동작(ARMv7)
            • 익셉션 발생
            • pc에 익셉션 엔트리 주소 저장
            • 시스템 콜 핸들러 호출
          • Svc호출시 동작(ARMv8): el0 -> el1로 스위칭
      • HVC
        1. 하이퍼바이저 콜
        2. 게스트 os에서 하이퍼바이저로 스위칭
        3. 커널에서 하이퍼바이저로 진입할때 실행
        4. 단일 os만 실행되는 시스템에서는 디버깅 정보를 저장하는 용도로 EL2를 활용
        5. 기타
          • 하이퍼바이저 아키텍처: 여러 os를 동시에 운영하는 아키텍처(가상화 환경)
          • 하이퍼바이저: 가상머신의 시스템 리소스 관리하는 역할
          • 게스트 os = 가상머신
          • vcpu를 통해 각 게스트 os가 사용할 cpu 설정 가능
      • SMC
        1. 시큐어 모니터 콜
        2. 논 시큐어 <-> 시큐어 상태로 실행 흐름을 스위칭
        3. 트러스트존 아키텍처에서 주로 사용
    • 프로세스 상태 제어 명령어
      • ARMv7: xPSR(CPSR, SPSR)레지스터
        1. 직접 수정 가능
        2. MRS <Rt> xPSR: 값을 레지스터에 로드 (읽기)
        3. MSR xPSR_<field> <Rt>: 레지스터값을 xpsr필드에 쓰기 ex) MSR SPSR_s R0 / MSR SPSR_sf R0
        4. 접미사(특정비트 필드)(R0에서 접미사 범위에 해당하는 부분만 업데이트)
          • c: [7:0] 비트필드
          • x: [15:8] 비트필드
          • s: [23:16] 비트필드
          • f: [31:24] 비트필드
      • ARMv8: PSTATE (인터페이스, 레지스터)
        1. 간접 수정 가능
        2. MSR DAIFSet, <Xt> : PSTATE.{D,A,I,F} 필드에 써주는 명령어
          • 의사코드: PSTATE.DAIF |= <Xt>
          • 보통 부팅이나 soc 검증시에 수동으로 설정할때 많이 사용
          • 완벽하게 잘 사용해야함
        3. MSR DAIFClr, <Xt> : <Xt> 레지스터의 값을 PSTATE.{D,A,I,F}필드에 비트 클리어
          • 의사코드: PSTATE.DAIF & = ~<Xt>
          • 레지스터에 해당하는 값과 동일한 위치의 PSTATE필드만 비트 클리어

 

출처: 인프런, "시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리", https://inf.run/FiFG

'CPU Architecture > ARM Architecture' 카테고리의 다른 글

요약 6. ARMv8 익셉션 레벨  (0) 2023.12.31
요약 5. ARMv7 동작모드  (0) 2023.12.30
요약 3. ARM 레지스터  (0) 2023.12.30
요약 2. ARM 공부 이유  (0) 2023.12.30
요약 1. ARM 프로세서 역사  (0) 2023.12.28