Cortex-M3의 중요 섹션


10

타이밍 제약이나 동시성 문제로 인해 예외가 허용되지 않는 Cortex-M3에서 중요한 코드 섹션을 구현하는 것에 대해 조금 궁금합니다.

제 경우에는 LPC1758을 실행 중이며 TI CC2500 송수신기가 탑재되어 있습니다. CC2500에는 RX 버퍼의 데이터 및 TX 버퍼의 여유 공간에 대한 인터럽트 라인으로 사용할 수있는 핀이 있습니다.

예를 들어, MCU의 SRAM에 TX 버퍼가 있고 송수신기의 TX 버퍼에 여유 공간이 있으면이 데이터를 거기에 쓰려고합니다. 그러나 SRAM 버퍼에 데이터를 넣는 루틴은 TX의 여유 공간 인터럽트에 의해 중단 될 수 없습니다. 그래서 내가하고 싶은 것은이 버퍼를 채우는이 절차를 수행하는 동안 인터럽트를 일시적으로 비활성화하는 것이지만이 절차가 진행되는 동안 발생하는 모든 인터럽트가 완료된 후에 실행되도록하는 것입니다.

이것이 Cortex-M3에서 어떻게 가장 잘 이루어 집니까?

답변:


11

Cortex M3은 "Load-Exclusive"(LDREX) 및 "Store-Exclusive"(STREX)라고하는 유용한 작업 조작 (다른 많은 머신에서도 공통)을 지원합니다. 개념적으로 LDREX 작업은로드를 수행하고로드 된 위치가 다른 것에 의해 쓰여질 수 있는지 여부를 관찰하기 위해 특정 하드웨어를 설정합니다. 마지막 LDREX에 사용 된 주소에 STREX를 수행하면 다른 주소를 먼저 쓴 경우에만 해당 주소가 기록 됩니다 . STREX 명령어는 저장이 발생한 경우 0으로, 또는 중단 된 경우 1로 레지스터를로드합니다.

STREX는 종종 비관적입니다. 실제로 해당 위치를 건드리지 않았더라도 매장을 운영하지 않기로 결정하는 다양한 상황이 있습니다. 예를 들어, LDREX와 STREX 사이의 인터럽트는 STREX가 감시중인 위치에 도달했다고 가정합니다. 이러한 이유로 LDREX와 STREX 사이의 코드 양을 최소화하는 것이 좋습니다. 예를 들어 다음과 같은 것을 고려하십시오.

인라인 void safe_increment (uint32_t * addr)
{
  uint32_t new_value;
  하다
  {
    new_value = __ldrex (addr) + 1;
  } while (__ strex (new_value, addr));
}

다음과 같이 컴파일됩니다.

; R0에 해당 주소가 있다고 가정하십시오. r1 휴지통
lp :
  ldrex r1, [r0]
  r1, r1, # 1 추가
  strex r1, r1, [r0]
  cmp r1, # 0; 0이 아닌지 테스트
  bne lp
  .. 코드는 계속

코드가 실행되는 대부분의 시간 동안 LDREX와 STREX 사이에 "방해"가 발생하지 않으므로 STREX는 더 이상 어려움없이 성공할 수 있습니다. 그러나 LDREX 또는 ADD 명령 바로 다음에 인터럽트가 발생하면 STREX는 저장을 수행하지 않지만 대신 코드는 [r0]의 (업데이트 된) 값을 읽고 새로운 증분 값을 계산하기 위해 되돌아갑니다. 그것을 기반으로합니다.

safe_increment와 같은 연산을 형성하기 위해 LDREX / STREX를 사용하면 중요한 섹션을 관리 할 수있을뿐만 아니라 여러 섹션에서이를 피할 수 있습니다.


따라서 차단을 해제하면 다시 차단할 수 있도록 "차단"인터럽트 방법이 없습니까? 가능한 경우에도 이것이 우아한 솔루션이라는 것을 알고 있지만 ARM 인터럽트 처리에 대해 더 자세히 알고 싶습니다.
Emil Eriksson

3
인터럽트를 비활성화 할 수 있으며 Cortex-M0에는이를 대체 할 실질적인 대안이없는 경우가 많습니다. LDREX / STREX 접근 방식은 인터럽트를 비활성화하는 것보다 더 깨끗하다고 ​​생각합니다. 많은 경우 실제로 중요하지 않습니다. . 코드를 멀티 코어 CPU로 마이그레이션하면 ldrex / strex 접근 방식이 작동하지만 인터럽트를 사용하지 않는 접근 방식은 작동하지 않습니다. 또한 일부 RTOS의 실행 코드는 권한을 축소하여 인터럽트를 비활성화 할 수 없습니다.
supercat

어쨌든 FreeRTOS와 함께 끝날 것이므로 직접하지는 않지만 어쨌든 배우고 싶습니다. 절차 중에 발생하는 인터럽트를 버리는 것과는 달리 인터럽트를 차단하기 위해 어떤 인터럽트 비활성화 방법을 사용해야합니까? 폐기하려면 어떻게해야합니까?
Emil Eriksson

관련 코드에 괄호가 없기 때문에 위의 답변 중 하나를 신뢰할 수 없습니다 while(STREXW(new_value, addr); .

@Tim : 죄송합니다. 입력이 완벽하지 않습니다. 비교를 위해 작성한 실제 코드가 없기 때문에 사용하는 시스템이 STREXW 또는 __STREXW를 사용했는지 여부는 기억 나지 않지만 컴파일러 참조는 __strex를 본질적으로 표시합니다 (STREXW와 달리 32 비트 STREX, __strex 내장 용도는 제공된 포인터 크기에 따라 STREXB, STREXH 또는 STREX를 생성합니다)
supercat

4

MCU 소프트웨어에 원형 버퍼 또는 FIFO가 필요한 것처럼 들립니다. 읽기 및 쓰기를 위해 두 개의 인덱스 또는 포인터를 어레이로 추적하면 전경 및 배경이 간섭없이 동일한 버퍼에 액세스 할 수 있습니다.

포 그라운드 코드는 언제든지 순환 버퍼에 자유롭게 쓸 수 있습니다. 쓰기 포인터에 데이터를 삽입 한 다음 쓰기 포인터를 증가시킵니다.

백그라운드 (인터럽트 처리) 코드는 읽기 포인터에서 데이터를 소비하고 읽기 포인터를 증가시킵니다.

읽기 및 쓰기 포인터가 같으면 버퍼가 비어 있고 백그라운드 프로세스가 데이터를 보내지 않습니다. 버퍼가 가득 차면 포 그라운드 프로세스는 더 이상 쓰기를 거부합니다 (또는 필요에 따라 오래된 데이터를 덮어 쓸 수 있음).

리더와 라이터를 분리하기 위해 순환 버퍼를 사용하면 인터럽트를 비활성화 할 필요가 없습니다.


예, 분명히 순환 버퍼를 사용하지만 증가 및 감소는 원자 연산이 아닙니다.
Emil Eriksson

3
@Emil : 반드시 그럴 필요는 없습니다. 두 개의 포인터와 하나의 "사용할 수없는"슬롯이있는 클래식 원형 버퍼의 경우 메모리 쓰기가 원자 적이며 순서대로 시행 되기만하면됩니다. 독자는 하나의 포인터를 소유하고, 작가는 다른 포인터를 소유하며, 둘 다 포인터를 읽을 수 있지만 포인터 소유자 만 포인터를 씁니다. 이때 원자 순서대로 쓰기 만하면됩니다.
John R. Strohm

2

정확한 위치를 기억할 수는 없지만 ARM에서 온 라이브러리 (TI, ARM이 아니라 CMSIS 또는 이와 유사한 것이어야합니다 .ST를 사용해야하지만이 파일이 ARM에서 나온 곳을 읽은 것을 기억합니다. ) 전역 인터럽트 비활성화 옵션이 있습니다. 함수 호출입니다. (나는 일하지 않지만 내일 정확한 기능을 찾을 것이다). 나는 당신의 시스템에 좋은 이름으로 그것을 감싸고 인터럽트를 비활성화하고, 당신의 일을하고 다시 활성화 할 것입니다. 그러나 더 나은 옵션은 전역 인터럽트 비활성화 대신 세마포어 또는 큐 구조를 구현하는 것입니다.


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