ARM Cortex A9에서 중요 섹션을 구현하는 방법


15

일부 레거시 코드를 ARM926 코어에서 CortexA9로 이식하고 있습니다. 이 코드는 베어 메탈이며 OS 또는 표준 라이브러리를 모두 포함하지는 않습니다. 코드의 중요한 섹션으로 인해 방지되어야하는 경쟁 조건과 관련이있는 것으로 보입니다.

이 CPU에 대해 중요한 섹션이 올바르게 구현되지 않았는지 확인하기 위해 접근 방식에 대한 피드백을 원합니다. GCC를 사용하고 있습니다. 미묘한 오류가 있다고 생각합니다.

또한 ARM에 대한 이러한 유형의 프리미티브 (또는 우수한 경량 스핀 록 / 세 미포 라이브러리)가있는 오픈 소스 라이브러리가 있습니까?

#define ARM_INT_KEY_TYPE            unsigned int
#define ARM_INT_LOCK(key_)   \
asm volatile(\
    "mrs %[key], cpsr\n\t"\
    "orr r1, %[key], #0xC0\n\t"\
    "msr cpsr_c, r1\n\t" : [key]"=r"(key_) :: "r1", "cc" );

#define ARM_INT_UNLOCK(key_) asm volatile ("MSR cpsr_c,%0" : : "r" (key_))

코드는 다음과 같이 사용됩니다.

/* lock interrupts */
ARM_INT_KEY_TYPE key;
ARM_INT_LOCK(key);

<access registers, shared globals, etc...>

ARM_INT_UNLOCK(key);

"키"의 개념은 중첩 된 중요 섹션을 허용하는 것으로, 기능의 시작과 끝에서 재진입 기능을 작성하는 데 사용됩니다.

감사!


1
infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/… 를 참조하십시오 . 포함 된 asm btw에서는 수행하지 마십시오. 기사처럼 기능합니다.
Jason Hu

ARM에 대해서는 아무것도 모르지만 mutex (또는 모든 스레드 간 또는 프로세스 간 동기화 기능)의 경우 a) 레지스터에 현재 캐시 된 모든 메모리 값이 플러시되도록하기 위해 "메모리"클로버를 사용해야합니다. asm을 실행 하기 전에 메모리로 돌아가고 b) asm이 다시로드 된 후 액세스되는 메모리의 모든 값. HuStmpHrrr가 권장하는대로 호출을 수행하면 암시 적으로이 클로버를 수행해야합니다.

또한 여전히 ARM을 말하지 않지만 'key_'에 대한 제약 조건이 올바르지 않습니다. 이것이 재 입력에 사용된다고 말했기 때문에 잠금에서 "= r"로 선언하면 의심스러운 것 같습니다. '='는 덮어 쓰려고하고 기존 값이 중요하지 않음을 의미합니다. 기존 값을 업데이트하려는 의도를 나타 내기 위해 '+'를 사용하려고 한 것 같습니다. 그리고 다시 잠금 해제를 위해 입력으로 나열하면 gcc는 변경하려는 의도는 없지만 실수하지 않은 경우 변경합니다. 나는 이것이 또한 '+'출력으로 나열되어야한다고 생각합니다.

1
이러한 고사양 코어를위한 어셈블리 코딩에 +1. 어쨌든, 이것은 권한 모드와 관련이있을 수 있습니까?
Dzarda

나는 당신 이 그것을 사용 ldrex하고 strex올바르게 해야한다고 확신합니다 . 여기 웹 페이지 사용하는 방법을 보여 ldrexstrex스핀 락을 구현합니다.

답변:


14

OS없이 중요한 섹션을 처리하는 데있어 가장 어려운 부분은 실제로 뮤텍스를 만드는 것이 아니라 현재 사용할 수없는 리소스를 코드에서 사용하려는 경우 어떻게해야하는지 파악하는 것입니다. load-exclusive 및 conditional-store-exclusive 명령어는 정수에 대한 포인터가 주어지면 원자 적으로 새로운 값을 저장하지만 지정된 정수에 포함 된 것을 반환하는 "스왑"함수를 생성하는 것이 매우 쉽습니다.

int32_t atomic_swap(int32_t *dest, int32_t new_value)
{
  int32_t old_value;
  do
  {
    old_value = __LDREXW(&dest);
  } while(__STREXW(new_value,&dest);
  return old_value;
}

위와 같은 함수가 주어지면 다음과 같은 것을 통해 뮤텍스에 쉽게 들어갈 수 있습니다.

if (atomic_swap(&mutex, 1)==0)
{
   ... do stuff in mutex ... ;
   mutex = 0; // Leave mutex
}
else
{ 
  ... couldn't get mutex...
}

OS가없는 경우 가장 큰 어려움은 종종 "뮤텍스를 얻을 수 없음"코드에 있습니다. mutex로 보호되는 리소스가 사용 중일 때 인터럽트가 발생하면 인터럽트 처리 코드에 플래그를 설정하고 원하는 정보를 저장하기 위해 정보를 저장 한 다음 주요 코드를 가져와야합니다. 뮤텍스가 뮤텍스를 해제 할 때마다 뮤텍스가 유지되는 동안 인터럽트가 무언가를 원했는지 여부를 확인하고, 그렇다면 인터럽트 대신 작업을 수행합니다.

단순히 인터럽트를 비활성화하여 인터럽트를 비활성화하면 (다른 종류의 뮤텍스가 필요하지 않을 수 있음) 뮤텍스 보호 리소스를 사용하려는 인터럽트 문제를 피할 수 있지만, 일반적으로 인터럽트를 비활성화하면 다른 종류의 뮤텍스가 필요하지 않습니다.

유용한 타협은 위에서 설명한대로 플래그를 사용하는 것이지만 뮤텍스 비활성화 인터럽트를 해제하고 앞서 언급 한 플래그를 확인하는 메인 라인 코드를 가지고 있습니다 (뮤텍스를 해제 한 후 인터럽트를 다시 활성화). 이러한 접근 방식은 인터럽트를 매우 길게 비활성화하지 않아도되지만 뮤텍스를 해제 한 후 메인 라인 코드가 인터럽트 플래그를 테스트하면 플래그를 보는 시간과 시간 사이에 위험이 발생할 가능성을 방지합니다 뮤텍스를 획득 및 해제하고 인터럽트 플래그에 작용하는 다른 코드에 의해 선점 될 수 있습니다. 메인 라인 코드가 뮤텍스를 해제 한 후 인터럽트 플래그를 테스트하지 않으면

어쨌든, 가장 중요한 것은 뮤텍스 보호 리소스를 사용할 수 없을 때 사용하려고 시도하는 코드가 리소스가 해제되면 시도를 반복하는 수단을 갖는 것입니다.


7

이것은 중요한 부분을 수행하는 데 도움이됩니다. 인터럽트를 비활성화합니다. 시스템에 데이터 결함이 있거나 처리하는 경우 작동하지 않을 수 있습니다. 또한 인터럽트 대기 시간이 증가합니다. 리눅스 irqflags.h가 이를 처리 할 몇 가지 매크로를 가지고있다. cpsiecpsid지침 어쩌면 유용한; 그러나 상태를 저장하지 않으며 중첩을 허용하지 않습니다. cps레지스터를 사용하지 않습니다.

를 들어 코어 텍스-A의 시리즈는이 ldrex/strex더 효율적이며 형성하기 위해 작업 할 수 뮤텍스 중요 섹션 또는 그들은 함께 사용할 수있는 잠금 장치가없는 중요한 부분을 제거하는 알고리즘.

어떤 의미에서는 ldrex/strexARMv5처럼 보입니다 swp. 그러나 실제로는 구현하기가 훨씬 더 복잡합니다. 작업 캐시가 필요하며 대상 메모리가 ldrex/strex캐시에 있어야합니다. 에 대한 ARM 설명서 ldrex/strex는 메커니즘이 비 Cortex-A CPU에서 작동하기를 원하기 때문에 다소 필요합니다. 그러나 Cortex-A의 경우 로컬 CPU 캐시를 다른 CPU와 동기화하는 메커니즘은 ldrex/strex명령어 를 구현하는 데 사용되는 것과 동일 합니다. Cortex-A 시리즈의 경우 예약 그래뉼 ( ldrex/strex예약 메모리 크기 )은 캐시 라인과 동일합니다. 이중 연결 목록과 같이 여러 값을 수정하려는 경우 메모리를 캐시 라인에 정렬해야합니다.

미묘한 오류가 있다고 생각합니다.

mrs %[key], cpsr
orr r1, %[key], #0xC0  ; context switch here?
msr cpsr_c, r1

시퀀스를 선점 할 수 없도록해야합니다 . 그렇지 않으면 인터럽트가 활성화 된 두 가지 주요 변수가 표시 될 수 있으며 잠금 해제가 올바르지 않습니다. 메모리 swp와 함께 명령어를 사용하여 ARMv5에서 일관성을 유지할 수 있지만이 명령어는 다중 CPU 시스템에서 더 잘 작동하기 때문에 Cortex-A에서 더 이상 사용되지 않습니다 .ldrex/strex

이 모든 것은 시스템이 어떤 종류의 스케줄링을했는지에 달려 있습니다. 메인 라인과 인터럽트 만있는 것 같습니다. 중요한 섹션을 작업 할 레벨 (시스템 / 사용자 공간 등)에 따라 스케줄러에 연결하기 위해 중요한 섹션 프리미티브 가 필요한 경우가 종종 있습니다 .

또한 ARM에 대한 이러한 유형의 프리미티브 (또는 우수한 경량 스핀 록 / 세 미포 라이브러리)가있는 오픈 소스 라이브러리가 있습니까?

휴대용으로 작성하기가 어렵습니다. 즉, 이러한 라이브러리는 특정 버전의 ARM CPU 및 특정 OS에 대해 존재할 수 있습니다.


2

그 중요한 부분에 몇 가지 잠재적 인 문제가 있습니다. 이 모든 것에 대한주의 사항과 해결책이 있지만 요약하면 다음과 같습니다.

  • 최적화 또는 임의의 다른 이유로 컴파일러가 이러한 매크로를 통해 코드를 이동하는 것을 막는 것은 없습니다.
  • 컴파일러는 인라인 어셈블리가 따로 언급되지 않는 한 프로세서 상태의 일부를 저장하고 복원합니다.
  • 시퀀스 중간에 인터럽트가 발생하는 것을 막고 읽을 때와 쓰는 시점 사이의 상태를 변경하는 것은 없습니다.

먼저 컴파일러 메모리 장벽이 필요합니다 . GCC는이를 클로버 로 구현합니다 . 기본적으로 이것은 컴파일러에게 "아니요. 메모리 액세스 결과에 영향을 줄 수 있기 때문에이 인라인 어셈블리에서 메모리 액세스를 이동할 수 없습니다"라고 말하는 방법입니다. 특히, 둘 필요 "memory"하고 "cc"(가) 시작과 끝 매크로 모두에서 내리 쳤을를. 이것들은 컴파일러가 메모리 액세스를 가질 수 있다는 것을 알고 있기 때문에 함수 호출과 같은 다른 것들이 인라인 어셈블리와 관련하여 재정렬되는 것을 방지합니다. "memory"클로버가있는 인라인 어셈블리의 조건 코드 레지스터에서 ARM 유지 상태에 대한 GCC를 보았 으므로 클로버가 반드시 필요합니다 "cc".

둘째, 이러한 중요한 섹션은 인터럽트 활성화 여부보다 훨씬 더 많은 것을 저장하고 복원합니다. 특히, 그들은 대부분의 CPSR (현재 프로그램 상태 레지스터)을 저장하고 복원 하고 있습니다 (링크는 A9의 멋진 다이어그램을 찾을 수 없기 때문에 Cortex-R4에 대한 링크이지만 동일해야합니다). 실제로 상태를 수정할 수있는 부분 에는 미묘한 제한 이 있지만 여기서 필요한 것 이상입니다.

무엇보다도 여기에는 조건 코드가 포함됩니다 ( cmp다음 과 같은 조건부 명령이 결과에 작용할 수 있도록 명령 결과 가 저장 되는 위치 ). 컴파일러는 이것에 의해 혼동 될 것입니다. "cc"위에서 언급 한 클로버를 사용하면 쉽게 해결할 수 있습니다 . 그러나 이렇게하면 코드가 실패 할 때마다 문제가 발생하는 것처럼 들리지 않습니다. 그러나 임의의 다른 코드를 수정하면 컴파일러가 약간 다른 작업을 수행하여 이로 인해 깨질 수있는 시한 폭탄이 다소 있습니다.

또한 Thumb 조건부 실행구현 하는 데 사용되는 IT 비트를 저장 / 복원하려고 시도합니다 . Thumb 코드를 실행하지 않으면 중요하지 않습니다. 필자는 GCC의 인라인 어셈블리가 IT 비트를 처리하는 것 외에 IT 비트를 처리하는 방법을 찾지 못했습니다. 컴파일러는 IT 블록에 인라인 어셈블리를 배치해서는 안되며 어셈블리가 항상 IT 블록 외부에서 종료 될 것으로 예상합니다. 나는 GCC가 이러한 가정을 위반하는 코드를 생성하는 것을 본 적이 없으며, 최적화가 엄격한 상당히 복잡한 인라인 어셈블리를 수행 했으므로 합리적으로 확신합니다. 이것은 실제로 IT 비트를 변경하려고 시도하지 않을 것이며,이 경우 모든 것이 정상입니다. 이 비트를 수정하려고하면 "건축 적으로 예측할 수없는" 것으로 분류됩니다따라서 모든 종류의 나쁜 일을 할 수는 있지만 아무것도하지 않을 것입니다.

저장 / 복원 될 비트의 마지막 범주 (실제로 인터럽트를 비활성화하는 것 외에)는 비트입니다. 이것들은 변경되지 않을 것이므로 아마도 중요하지 않을 것입니다. 그러나 의도적으로 모드를 변경하는 코드가 있으면 이러한 인터럽트 섹션이 문제를 일으킬 수 있습니다. 권한 모드와 사용자 모드 사이를 변경하는 것이 내가 기대하는 유일한 경우입니다.

셋째, 사이 CPSR의 다른 부분 변경하는 인터럽트를 방지 아무것도 없다 MRSMSR의를 ARM_INT_LOCK. 이러한 변경 사항을 덮어 쓸 수 있습니다. 가장 합리적인 시스템에서 비동기 인터럽트는 CPSR을 포함하여 인터럽트하는 코드의 상태를 변경하지 않습니다. 그렇게하면 코드가 무엇을할지 추론하기가 매우 어려워집니다. 그러나 (FIQ 비활성화 비트 변경이 가장 가능성이 높은 것 같습니다) 시스템에서이 작업을 수행하는지 고려해야합니다.

다음은 내가 지적한 모든 잠재적 문제를 해결하는 방식으로 구현하는 방법입니다.

#define ARM_INT_KEY_TYPE            unsigned int
#define ARM_INT_LOCK(key_)   \
asm volatile(\
    "mrs %[key], cpsr\n\t"\
    "ands %[key], %[key], #0xC0\n\t"\
    "cpsid if\n\t" : [key]"=r"(key_) :: "memory", "cc" );
#define ARM_INT_UNLOCK(key_) asm volatile (\
    "tst %[key], #0x40\n\t"\
    "beq 0f\n\t"\
    "cpsie f\n\t"\
    "0: tst %[key], #0x80\n\t"\
    "beq 1f\n\t"\
    "cpsie i\n\t"
    "1:\n\t" :: [key]"r" (key_) : "memory", "cc")

컴파일해야합니다 -mcpu=cortex-a9지원하지 않는 구형 ARM의 CPU로 인해 적어도 일부 GCC 버전 (광산 등) 기본 cpsiecpsid.

내가 사용하는 ands대신의 and에서 ARM_INT_LOCK가이 Thumb 코드에서 사용될 경우 16 비트 명령어 그래서. "cc"엄격히 성능 / 코드 크기 혜택 그래서 소지품은, 어쨌든 필요하다.

0하고 1있는 지역 레이블 참조가.

이들은 버전과 동일한 방식으로 사용할 수 있어야합니다. 은 ARM_INT_LOCK단지 빨리 / 작은 원래 하나입니다. 불행히도, 나는 ARM_INT_UNLOCK몇 가지 지시 사항 근처에서 안전하게 할 수 있는 방법을 생각해 낼 수 없었습니다 .

IRQ 및 FIQ 비활성화시 시스템에 제약이있는 경우 단순화 할 수 있습니다. 예를 들어, 항상 함께 사용 중지 된 경우 다음과 같이 하나의 cbz+ 로 결합 할 수 있습니다 cpsie if.

#define ARM_INT_UNLOCK(key_) asm volatile (\
    "cbz %[key], 0f\n\t"\
    "cpsie if\n\t"\
    "0:\n\t" :: [key]"r" (key_) : "memory", "cc")

또는 FIQ를 전혀 신경 쓰지 않으면 완전히 활성화 / 비활성화하는 것과 비슷합니다.

당신이 다른 아무 것도 알지 못하는 경우 그때 당신은 또한 사용은 모두 제외하고, 원래의 코드와 매우 비슷한 계속 수, 잠금 및 잠금 해제 사이 CPSR에서 다른 상태 비트의 변경 "memory""cc"내리 쳤을 때 모두 ARM_INT_LOCKARM_INT_UNLOCK


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.