Exception -4- (ARM32 VFP & FPE)

<kernel v5.4>

VFP(Vector Floating Point)

  • VFP는 반정도(half precision), 단정도(single precision) 및 배정도(double precision) 고정 소수점 연산을 지원하는 보조 연산 장치이다.
  • SIMD(Single Instruction Multiple Data)라고도 불린다.

 

버전별 특징

  • VPFv1
    • ARM10에서 사용
    • VFP 예외를 트래핑해야 한다. (VFP 지원 코드가 필요 하다)
    • gcc 컴파일 시: -mfpu=vfp
  • VFPv2
    • ARM11, ARMv5, ARMv6에서 사용
      • ARM10200E가 제공하는 VFP10 수정 버전 1
      • ARM926E/946E/966E에 대해 별도로 허가된 옵션으로 사용되는 VFP9-S
      • ARM1136JF-S, ARM1176JZF-S 및 ARM11 MPCore에서 제공되는 VFP11
    • VFP 예외를 트래핑해야 한다. (VFP 지원 코드가 필요 하다)
    • 16개의 64-bit FPU 레지스터
    • gcc 컴파일 시: -mfpu=vfp
  • VFPv3
    • ARMv7인 Cortex-A8부터 사용하고 NEON으로 불린다.
      • VFPv3-D16
        • 오직 16개의 64-bit FPU 레지스터
      • VFPv3-F16 variant
        • 일반적이지 않지만 단정도 고정 소수점 연산을 지원한다.
    • VFP에서 벡터 기능이 퇴색하고(deprecated) 파이프라인화되어 advanced SIMD로 진보되어 VFPv2의 2배 성능을 확보하였다.
      • 실제 배정도 고정 소수점 연산을 지원하는 scalar 명령셋과 지원하지 않는 vector 명령셋으로 구분된다.
        • 단 ARMv8에서는 vector 명령셋에서도 배정도 고정 소수점 연산을 지원한다.
        • 기존의 벡터 명령도 advanced SIMD와 같이 병렬 처리된다.
    • VFP 예외를 트래핑할 필요가 없다는 점을 제외하고 VFPv2와 동일하다. (VFP 지원 코드가 필요 없다)
    • 32개의 64-bit FPU 레지스터
    • 단정도 고정 소수점 연산(optional)
    • gcc 컴파일 시: -mfpu=neon
    • 참고: NEON | arm

 

GCC로 FP 코드 연동

  • -mfloat-abi=
    • soft
      • Floating Point 라이브러리를 호출하도록 생성하고 호출 방법으로 Floating Poing ABI 규격의 calling convention을 사용한다.
    • hard
      • -mfpu에서 지정한 VFP 하드웨어 코드를 생성하고 호출 방법 역시 각자의 방법을 사용한다.
    • softfp
      • -mfpu에서 지정한 VFP 하드웨어 코드를 생성하되 호출 방법은 soft 방식을 따른다.
  • -mfpu=
    • vfpv2’, ‘vfpv3’, ‘vfpv3-fp16’, ‘vfpv3-d16’, ‘vfpv3-d16-fp16’, ‘vfpv3xd’, ‘vfpv3xd-fp16’, ‘neon-vfpv3’, ‘neon-fp16’, ‘vfpv4’, ‘vfpv4-d16’, ‘fpv4-sp-d16’, ‘neon-vfpv4’, ‘fpv5-d16’, ‘fpv5-sp-d16’, ‘fp-armv8’, ‘neon-fp-armv8’ 및 ‘crypto-neon-fp-armv8’
    • neon
      • VFP 코드를 생성할 경우에 NEON(‘vfpv3’ alias)용으로 생성한다.
    • vfp
      • VFP 코드를 생성할 경우에 VFPv2(‘vfpv2’ alias)용으로 생성한다.
  • 참고: ABI(Application Binary Interface) | 문c

 

VFP 지원 코드

하드웨어 VFP 만으로는 처리할 수 없어서 아래와 같은 상황에서 VFP 지원을 받아 소프트웨어적으로 처리한다.

  • NaN 관련된 부동 소수점 연산
  • 비정규 값과 관련된 부동 소수점 연산
  • 부동 소수점 오버플로
  • 부동 소수점 언더플로
  • 정확하지 않은 결과
  • 0으로 나누기 오류
  • 잘못된 연산

 


FPE(Floating Point Emulation)

  • VFP가 없는 arm 아키텍처에서 커널이 제공하는 라이브러리를 통해 소프트 에뮬레이션 방법으로 동작한다.
  • arm에서는 VFP가 여러 가지 아키텍처에 따라 다르므로 특별히 고성능을 요구하지 않는 경우 호환 목적의 코드를 만들기 위해 FPE를 사용하기도 한다.

 

Undefined instruction으로부터 진입

call_fpe

arch/arm/kernel/entry-armv.S

call_fpe:
        get_thread_info r10                     @ get current thread
#ifdef CONFIG_NEON
        adr     r6, .LCneon_arm_opcodes
2:      ldr     r5, [r6], #4                    @ mask value
        ldr     r7, [r6], #4                    @ opcode bits matching in mask
        cmp     r5, #0                          @ end mask?
        beq     1f
        and     r8, r0, r5
        cmp     r8, r7                          @ NEON instruction?
        bne     2b
        mov     r7, #1
        strb    r7, [r10, #TI_USED_CP + 10]     @ mark CP#10 as used
        strb    r7, [r10, #TI_USED_CP + 11]     @ mark CP#11 as used
        b       do_vfp                          @ let VFP handler handle this
1:
#endif
        tst     r0, #0x08000000                 @ only CDP/CPRT/LDC/STC have bit 27
        tstne   r0, #0x04000000                 @ bit 26 set on both ARM and Thumb-2
        reteq   lr
        and     r8, r0, #0x00000f00             @ mask out CP number
 THUMB( lsr     r8, r8, #8              )
        mov     r7, #1
        add     r6, r10, #TI_USED_CP
 ARM(   strb    r7, [r6, r8, lsr #8]    )       @ set appropriate used_cp[]
 THUMB( strb    r7, [r6, r8]            )       @ set appropriate used_cp[]
#ifdef CONFIG_IWMMXT
        @ Test if we need to give access to iWMMXt coprocessors
        ldr     r5, [r10, #TI_FLAGS]
        rsbs    r7, r8, #(1 << 8)               @ CP 0 or 1 only
        movscs  r7, r5, lsr #(TIF_USING_IWMMXT + 1)
        bcs     iwmmxt_task_enable      
#endif
 ARM(   add     pc, pc, r8, lsr #6      )
 THUMB( lsl     r8, r8, #2              )
 THUMB( add     pc, r8                  )
        nop

instruction이 VFPv3(NEON) 또는 소프트 지원이 필요한 VFPv1/v2 아키텍처에서 동작되는 경우 각각 커널의 VFP  소프트 지원 또는  FPE(Floating Point Emulation) 라이브러리를 통해 해당 예외 처리를 수행한다.

  • 코드 라인 3~15에서 instruction이 VFP이고 지원되지 않는 아키텍처에서 undefined exception으로 진입한 경우이므로 이에 대응하는 VFP 소프트 지원 라이브러리를 통해 해당 명령을 수행한다.
    • 코드 라인 3~8에서 instruction이 VFP 명령어인지 비교하기 위해 LCneon_arm_opcodes 테이블의 값들과 비교하는데 테이블의 끝까지 비교해도 일치하지 않은 경우 이므로 레이블 1로 이동한다. 즉 VFP 명령이 아닐 경우 보조 프로세서 명령 여부를 찾을 계획이다.
    • 코드 라인 9~11에서 instruction(r0)을 mask(r5)하여 opcode(r7) 값과 비교하여 같지 않은 경우 다음 테이블 엔트리와 비교하기 위해 레이블 2로 이동하여 루프를 돈다.
    • 코드 라인 12~15에서 보조 프로세서 인수 전달용 구조체 curr->used_cp[] 배열의 10번째와 11번째에 1을 저장한 후 do_vfp 레이블로 이동한다.
  • 코드 라인 15~33에서 VFP가 없는 아키텍처에서 undefined exception으로 진입한 경우이므로 이에 대응하기 위해 FPE(Floating Point Emulation)를 호출하여 지원한다.
    • 코드 라인 15~19에서 보조프로세서관련 명령이 아닌 경우 fault 처리를 위해 lr(__und_usr_fault_32 )주소로 복귀한다.
      • 보조 프로세서 관련 명령
        • CDP(Coprocessor Data oPerations), CPRT(COprocessor Register Transfer)
        • LDC(Load Data from Coprocessor) 및 STC(Store To Coprocessor) 등
    • 코드 라인 20~25에서 CP 번호를 r8 레지스터에 대입하고 이 값을 인덱스로 curr->used_cp[]에 1을 저장한다.
    • 코드 라인 26~32에서 보조프로세서가 iWMMXt인지 판단되는 경우 iwmmxt_task_enable 레이블로 이동한다.
    • 코드 라인 33에서 보조 프로세서 번호에 해당하는 테이블 위치로 jump 한다.
      • CP 번호가 1~2번인 경우 FPE(Floating Point Emulation)를 처리하러 do_fpe 레이블로 이동한다.
      • CONFIG_CRUNCH 커널 옵션이 설정되고 CP 번호가 4~6번인 경우 MaverickCrunch를 처리하러 crunch_task_enable 레이블로 이동한다.
      • CONFIG_VFP가 설정되고 CP번호가 10~11번인 경우 VFP를 처리하러 do_vfp 레이블로 이동한다.
      • 이 외의 경우에는 fault 처리를 위해 lr(__und_usr_fault_32) 주소로 복귀한다.

 

CP 별(보조프로세서 인덱스) jump 테이블

        ret.w   lr                              @ CP#0
        W(b)    do_fpe                          @ CP#1 (FPE)
        W(b)    do_fpe                          @ CP#2 (FPE)
        ret.w   lr                              @ CP#3
#ifdef CONFIG_CRUNCH
        b       crunch_task_enable              @ CP#4 (MaverickCrunch)
        b       crunch_task_enable              @ CP#5 (MaverickCrunch)
        b       crunch_task_enable              @ CP#6 (MaverickCrunch)
#else
        ret.w   lr                              @ CP#4
        ret.w   lr                              @ CP#5
        ret.w   lr                              @ CP#6
#endif
        ret.w   lr                              @ CP#7
        ret.w   lr                              @ CP#8
        ret.w   lr                              @ CP#9
#ifdef CONFIG_VFP
        W(b)    do_vfp                          @ CP#10 (VFP)
        W(b)    do_vfp                          @ CP#11 (VFP)
#else
        ret.w   lr                              @ CP#10 (VFP)
        ret.w   lr                              @ CP#11 (VFP)
#endif
        ret.w   lr                              @ CP#12
        ret.w   lr                              @ CP#13
        ret.w   lr                              @ CP#14 (Debug)
        ret.w   lr                              @ CP#15 (Control)

 

.LCneon_arm_opcodes

arch/arm/kernel/entry-armv.S

#ifdef CONFIG_NEON
        .align  6

.LCneon_arm_opcodes:
        .word   0xfe000000                      @ mask
        .word   0xf2000000                      @ opcode

        .word   0xff100000                      @ mask
        .word   0xf4000000                      @ opcode

        .word   0x00000000                      @ mask
        .word   0x00000000                      @ opcode

NEON instruction을 구분하기 위한 mask와 opcode이다.

  • 마지막 word 두 개는 종료를 구분하기 위한 값이다.

 

 

ARM instruction set format

bit 27~26이 설정된 명령이 보조 프로세서 관련 명령이다.

  • STC, STC2, LDC, LDC2, MCRR, MCRR2, MRRC, MRRC2, CDP, CDP2, MCR, MCR2, MRC, MRC2

 

Advanced SIMD (NEON) instruction set format

ARMv7에서 동작하는 Floating Point 장치이다.

  • VLD1~4, VST1~4, VADD, VSUB, VDIV, …)
  • 참고: VFP Instruction Set Quick Reference | arm – 다운로드 pdf

 

VFP 소프트 지원 호출 함수

do_vfp

arch/arm/vfp/entry.S

@ VFP entry point.
@
@  r0  = instruction opcode (32-bit ARM or two 16-bit Thumb)
@  r2  = PC value to resume execution after successful emulation
@  r9  = normal "successful" return address
@  r10 = this threads thread_info structure
@  lr  = unrecognised instruction return address
@  IRQs enabled.
@
ENTRY(do_vfp)
        inc_preempt_count r10, r4
        ldr     r4, .LCvfp
        ldr     r11, [r10, #TI_CPU]     @ CPU number
        add     r10, r10, #TI_VFPSTATE  @ r10 = workspace
        ldr     pc, [r4]                @ call VFP entry point
ENDPROC(do_vfp)

VFP 소프트 지원용 핸들러 함수를 호출한다.

  • VFPv1 및 VFPv2 아키텍처에서는 고정 소수 연산의 예외처리를 소프트웨어의 도움을 받아 처리하게 되어 있다.
  • .LCvfp
    • vfp/vfpmodule.c – core_initcall(vfp_init)을 통해 초기화된다.
      • 초기화 전에는 .LCvfp 값은 vfp_null_entry() 함수 주소를 가리킨다.
      • 초기화가 정상적으로 완료되면 .LCvfp 값은 vfp_support_entry() 함수 주소를 가리킨다.

 

vfp_null_entry()

arch/arm/vfp/entry.S

ENTRY(vfp_null_entry)
        dec_preempt_count_ti r10, r4
        ret     lr
ENDPROC(vfp_null_entry)

preemption 카운터를 감소시키고 복귀한다.

 

FPE 호출 함수

do_fpe

arch/arm/kernel/entry-armv.S

do_fpe:
        ldr     r4, .LCfp
        add     r10, r10, #TI_FPSTATE           @ r10 = workspace
        ldr     pc, [r4]                        @ Call FP module USR entry point

FPE(Floating Point Emulation) 핸들러 함수를 호출한다.

  • .LCfp
    • nwfpe/fpmodule.c – module_init(fpe_init)을 통해 초기화된다.
      • 초기화되면 nwfpe_enter: 레이블 주소를 가리킨다.

 

참고

 

댓글 남기기