Cortex (ARM) 마이크로 컨트롤러에서 WFI (인터럽트 대기)에 대한 최상의 패턴


18

EFM Gekko 컨트롤러 (http://energymicro.com/)를 사용하여 배터리 구동 소프트웨어를 개발하려고하고 있는데, 아무 쓸모가 없을 때 컨트롤러가 잠들기를 원합니다. WFI (Wait For Interrupt) 명령어는이 목적으로 사용됩니다. 인터럽트가 발생할 때까지 프로세서를 휴면 상태로 만듭니다.

어딘가에 무언가를 저장하여 수면을 취한 경우,로드 익스 클루 시브 / 스토어 익스 클루 시브 작업을 사용하여 다음과 같은 작업을 수행 할 수 있습니다.

  // dont_sleep은 무언가가 발생할 때마다 2로로드됩니다.
  // 메인 루프가 적어도 한 번 순환하도록해야합니다. 인터럽트가 발생하면
  // 다음 문장에서 2로 리셋되게하는 발생
  // 그 후에 인터럽트가 발생한 것처럼 동작합니다.

  store_exclusive (load_exclusive (dont_sleep) >> 1);

  while (! dont_sleep)
  {
    // 다음 명령문과 store_exclusive 사이에 인터럽트가 발생하면 잠자 지 마십시오.
    load_exclusive (SLEEP_TRIGGER);
    if (! dont_sleep)             
      store_exclusive (SLEEP_TRIGGER);
  }

load_exclusive와 store_exclusive 조작 사이에 인터럽트가 발생하면 store_exclusive를 건너 뛰어 루프를 한 번 더 시스템이 실행하게됩니다 (인터럽트가 dont_sleep을 설정했는지 확인). 불행히도 Gekko는 쓰기 주소 대신 WFI 명령어를 사용하여 절전 모드를 트리거합니다. 같은 코드 작성

  if (! dont_sleep)
    WFI ();

'if'와 'wfi'사이에 인터럽트가 발생하여 dont_sleep을 설정할 위험이 있지만 wfi는 계속 진행됩니다. 이를 방지하는 가장 좋은 패턴은 무엇입니까? PRIMASK를 1로 설정하여 WFI를 실행하기 직전에 인터럽트가 프로세서를 중단하지 못하게하고 바로 다음에 지우겠습니까? 아니면 더 나은 트릭이 있습니까?

편집하다

이벤트 비트가 궁금합니다. 일반적인 설명에 따르면 다중 프로세서 지원을위한 것이지만 다음과 같은 것이 작동하는지 궁금합니다.

  if (dont_sleep)
    SEV (); / * WFE 지우기 이벤트 플래그를 따르지만 잠자기 상태가 아님 * /
  WFE ();

don't_sleep을 설정하는 모든 인터럽트는 SEV 명령도 실행해야하므로 "if"테스트 후에 인터럽트가 발생하면 WFE는 이벤트 플래그를 지우지 만 슬립 상태가되지는 않습니다. 좋은 패러다임처럼 들립니까?


1
WFI 명령어는 명령어가 실행될 때 깨우기 조건이 참이면 코어를 휴면 상태로 만들지 않습니다. 예를 들어 WFI가 실행될 때 IRQ가 지워지지 않으면 NOP 역할을합니다.
Mark

@Mark : 문제는 "if (! dont_sleep)"와 "WFI"사이에 인터럽트가 발생하면 WFI가 실행될 때 인터럽트 조건이 더 이상 보류되지 않지만 인터럽트가 dont_sleep을 설정했을 수 있다는 것입니다. 다른 반복을 실행하는 메인 루프를 정당화하는 무언가를했습니다. Cypress PSOC의 내 응용 프로그램에서 기본 코드가 잠자기 시작하면 확장 웨이크 업을 유발하는 모든 인터럽트가 스택을 징크스시킬 수 있지만 상당히 불안정 해 ARM이 스택 조작을 권장하지 않는다는 것을 이해합니다.
supercat

@supercat WFI가 실행될 때 인터럽트가 지워지거나 지워지지 않을 수 있습니다. 인터럽트를 해제하기로 선택한 시점과 장소에 따라 다릅니다. dont_sleep 변수를 제거하고 마스크 된 인터럽트를 사용하여 깨어 있거나 잠을 자고 싶을 때 표시하십시오. if 문을 모두 제거하고 메인 루프의 끝에 WFI를 남겨 둘 수 있습니다. 모든 요청을 처리 한 경우 IRQ를 지우면 잠을 잘 수 있습니다. 깨어 있어야하는 경우 IRQ를 트리거하면 마스크가 발생하지 않지만 WFI를 실행하려고하면 NOP가됩니다.
Mark

2
@supercat 좀 더 근본적인 수준에서, 인터럽트 중심 디자인과 '빅 메인 루프'디자인을 혼합하려고 시도하는 것 같습니다. 일반적으로 시간이 중요하지 않고 종종 폴링 기반이며 최소한의 인터럽트를 갖는 '빅 메인 루프'디자인입니다. 이것들을 혼합하면 추악한 것이 오히려 빠를 수 있습니다. 가능하다면 하나의 디자인 패러다임을 선택하십시오. 최신 인터럽트 컨트롤러를 사용하면 기본적으로 인터럽트와 태스크 큐 양 (서비스 한 인터럽트, 다음으로 높은 우선 순위 등) 사이에 선점 멀티 태스킹이 제공됩니다. 그것을 활용하십시오.
Mark

@Mark : 배터리로 작동하는 응용 프로그램에서 PIC 18x를 사용하는 시스템을 개발했습니다. 스택 제한으로 인해 인터럽트 내에서 너무 많은 것을 처리 할 수 ​​없었기 때문에 대부분의 항목이 주 루프에서 편리하게 처리됩니다. 오래 실행되는 작업으로 인해 1-2 초 동안 물건이 차단되는 지점이 몇 개 있지만 대부분 잘 작동합니다. ARM으로 마이그레이션하는 경우 간단한 RTOS를 사용하여 장기 실행 작업을보다 쉽게 ​​분할 할 수 있지만 선점 형 또는 공동 멀티 태스킹을 사용할지 확실하지 않습니다.
supercat

답변:


3

나는 그 dont_sleep일을 완전히 이해하지 못했지만 시도 할 수있는 한 가지는 PendSV 처리기에서 "주요 작업"을 수행하는 것입니다. 그런 다음 무언가가 필요할 때마다 다른 처리기에서 PendSV를 예약하십시오. 그것을하는 방법을 여기에서 보십시오 (M1 용이지만 M3은 너무 다르지 않습니다).

사용할 수있는 다른 방법 (이전 방법과 함께 사용 가능)은 종료시 절전 기능입니다. 활성화하면 WFI를 호출하지 않고 마지막 ISR 핸들러를 종료 한 후 프로세서가 절전 모드로 전환됩니다. 여기에 몇 가지 예가 있습니다 .


5
WFI 명령어는 프로세서를 깨우기 위해 인터럽트를 활성화 할 필요가 없으며 CPSR의 F 및 I 비트는 무시됩니다.
Mark

1
@ Mark 나는 문서에서 그 비트를 놓쳤을 것입니다. 그것에 대해 링크 / 포인터가 있습니까? 코어를 깨우는 인터럽트 신호는 어떻게됩니까? 인터럽트가 다시 활성화 될 때까지 계속 유지됩니까?
Igor Skochinsky

ASM 참조 매뉴얼은 여기에 있습니다 : infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/… cortex-m3에 대한보다 구체적인 정보는 다음과 같습니다 : infocenter.arm.com/help/ index.jsp? topic = / com.arm.doc.dui0552a /… 마스킹 된 인터럽트가 보류중인 경우 코어가 깨어나고 WFI 명령 후 작업이 계속됩니다. 보류중인 인터럽트를 지우지 않고 다른 WFI를 발행하려고하면 WFI가 NOP 역할을합니다 (WFI의 깨우기 조건이 참이므로 코어가 절전 모드가 아님).
Mark

@ Mark : dont_sleep을 설정하는 인터럽트 처리기가 SEV ( "Set Event") 명령을 실행 한 다음 WFI 대신 WFE ( "Wait For Event")를 사용하는 것이 좋습니다. Gekko 예제는 WFI를 사용하는 것 같지만 WFE도 작동 할 것으로 생각합니다. 이견있는 사람?
supercat

10

중요한 부분에 넣으십시오. ISR은 실행되지 않으므로 WFI 이전에 dont_sleep이 변경 될 위험은 없지만 여전히 프로세서를 깨우고 중요한 섹션이 종료되는 즉시 ISR이 실행됩니다.

uint8 interruptStatus;
interruptStatus = EnterCriticalSection();
if (!dont_sleep)
  WFI();
ExitCriticalSection(interruptStatus);

개발 환경에는 아마도 중요한 섹션 기능이 있지만 대략 다음과 같습니다.

EnterCriticalSection은 다음과 같습니다.

MRS r0, PRIMASK /* Save interrupt state. */
CPSID i /* Turn off interrupts. */
BX lr /* Return. */

ExitCriticalSection은 다음과 같습니다.

MSR PRIMASK, r0 /* Restore interrupt states. */
BX lr /* Return. */

2
흥미롭게도 많은 ARM 라이브러리는 로컬 상태를 유지하는 대신 전역 카운터를 사용하는 임계 섹션 구현을 사용합니다. 카운터 접근법이 더 복잡하고 시스템 전체의 모든 코드가 동일한 카운터를 사용하는 경우에만 작동하기 때문에 마음이 흔들리는 것을 발견했습니다.
supercat

1
중요 섹션을 종료 할 때까지 인터럽트를 비활성화하지 않습니까? 그렇다면 WFI로 인해 CPU가 무기한 대기하지 않습니까?
Corneliu Zuzu

1
@ Kenzi Shrimp의 대답은 이전 질문에 대답하는 Linux 토론 스레드를 가리 킵니다. 나는 그의 대답과 당신의 것을 편집하기 위해 그것을 편집했습니다.
Corneliu Zuzu

@CorneliuZuzu 다른 사람의 답변을 편집하여 자신의 토론을 중재하는 것은 좋은 생각이 아닙니다. '링크 만'답변을 개선하기 위해 따옴표를 추가하는 것은 다른 문제입니다. 당신이 당신의 원에 대한 실제 질문이 있다면, 질문으로 물어보고 이것에 연결하십시오.
Sean Houlihane

1
@SeanHoulihane 나는 무효화하거나 그녀의 답변에서 아무것도 제거하지 않았습니다. 왜 이것이 작동하는지에 대한 간단한 설명은 별도의 토론이 아닙니다. 솔직히이 답변이 WFI 설명 없이는 투표권이 있다고 생각하지는 않지만 가장 큰 가치는 있습니다.
Corneliu Zuzu

7

당신의 아이디어는 괜찮습니다. 이것이 바로 Linux가 구현하는 것입니다. 보다여기를 하십시오 .

인터럽트가 비활성화 된 경우에도 WFI가 작동하는 이유를 설명하기 위해 위에서 언급 한 토론 스레드에서 유용한 인용문을 작성하십시오.

다음 인터럽트까지 유휴 상태를 유지하려면 몇 가지 준비를해야합니다. 준비하는 동안 인터럽트가 활성화 될 수 있습니다. 이러한 인터럽트는 찾고있는 깨우기 이벤트 일 수 있습니다.

코드가 아무리 우수하더라도 인터럽트를 비활성화하지 않으면 항상 휴면 준비와 실제로 휴면 상태 사이에서 경쟁하게되어 깨우기 이벤트가 손실됩니다.

이것이 내가 알고있는 모든 ARM CPU가 코어 CPU (CPSR I 비트)에 마스킹되어 있어도 깨우는 이유입니다.

다른 것들은 유휴 모드 사용을 잊어 버려야합니다.


1
WFI 또는 WFE 명령 시점에 인터럽트 비활성화를 언급하고 있습니까? 목적에 WFI 또는 WFE를 사용하는 것 사이에 의미있는 차이점이 있습니까?
supercat

1
@ supercat : 분명히 WFI를 사용합니다. WFE IMO는 주로 멀티 코어 시스템의 코어 간 동기화 힌트에 사용됩니다 (예 : 스핀 록에서 실패한 후 스핀 록에서 나간 후 SEV 발행시 WFE 수행). 또한 WFE는 인터럽트 마스킹 플래그를 고려하므로 WFI만큼 유용하지 않습니다. 이 패턴은 실제로 Linux에서 잘 작동합니다.
새우

2

가정 :

  1. 메인 스레드는 백그라운드 작업을 실행합니다
  2. 인터럽트는 우선 순위가 높은 작업 만 실행하고 백그라운드 작업은 실행하지 않습니다.
  3. 메인 스레드는 언제든지 중단 될 수 있습니다 (일반적으로 인터럽트를 마스킹하지는 않습니다)

그런 다음 해결책은 PRIMASK를 사용하여 플래그 유효성 검사와 WFI 간의 인터럽트를 차단하는 것입니다.

mask_interrupts();
if (!dont_sleep)
    wfi();
unmask_interrupts();

0

종료시 절전 모드는 어떻습니까? IRQ 핸들러가 종료 될 때마다 자동으로 휴면 상태가되므로 구성된 후에는 "정상 모드"가 실행되지 않습니다. IRQ가 발생하면 처리기가 깨어나고 실행되며 다시 절전 모드로 돌아갑니다. WFI가 필요하지 않습니다.


2
프로세서가 중단되는 휴면 유형이 인터럽트 중에 발생하는 작업에 따라 다를 수 있다는 사실을 어떻게 가장 잘 처리해야합니까? 예를 들어, 핀 변경 이벤트는 직렬 데이터가 예정되어있을 수 있으므로 프로세서가 데이터를 기다리는 동안 기본 클록 발진기를 계속 실행해야 함을 나타낼 수 있습니다. 메인 루프가 이벤트 플래그를 지우고 진행 상황을 검사 한 후 WFI를 사용하여 프로세서를 적절한 절전 모드로 전환하면 어떤 모드에 영향을 줄 수있는 인터럽트가 이벤트 플래그를 설정하게됩니다.
supercat

... 수면을 중단합니다. 하나의 메인 루프 핸들러가 휴면 모드를 제어하면 모든 인터럽트에서 걱정하는 것보다 깨끗합니다. 모든 인터럽트에서 메인 루프의 해당 부분을 "스핀"하는 것이 최적으로 효율적이지 않을 수 있지만, 특히 수면 동작에 영향을 줄 수있는 모든 인터럽트가 플래그에 부딪히면 나쁘지 않아야합니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.