Mẫu tốt nhất cho WFI (chờ ngắt) trên các vi điều khiển Cortex (ARM)


18

Tôi đang tìm cách phát triển phần mềm chạy bằng pin bằng bộ điều khiển Gekko của EFM (http://energymicro.com/) và muốn bộ điều khiển ngủ bất cứ khi nào không có gì hữu ích cho nó. Lệnh WFI (Chờ cho ngắt) được sử dụng cho mục đích này; nó sẽ đặt bộ xử lý vào trạng thái ngủ cho đến khi xảy ra gián đoạn.

Nếu giấc ngủ được thực hiện bằng cách lưu trữ một cái gì đó ở một nơi nào đó, người ta có thể sử dụng các hoạt động độc quyền tải / cửa hàng để thực hiện một số việc như:

  // dont_s ngủ được tải với 2 bất cứ khi nào điều gì đó xảy ra
  // nên buộc vòng lặp chính quay vòng ít nhất một lần. Nếu gián đoạn
  // xảy ra khiến nó được đặt lại thành 2 trong câu lệnh sau,
  // hành vi sẽ như thể sự gián đoạn xảy ra sau nó.

  store_exinating (load_exinating (dont_s ngủ) >> 1);

  trong khi (! dont_s ngủ)
  {
    // Nếu ngắt xảy ra giữa câu lệnh tiếp theo và store_exinating, đừng ngủ
    load_exinating (SLEEP_TRIGGER);
    nếu (! dont_s ngủ)             
      store_exinating (SLEEP_TRIGGER);
  }

Nếu xảy ra gián đoạn giữa các hoạt động load_exinating và store_exinating, thì hiệu ứng sẽ là bỏ qua store_exinating, do đó làm cho hệ thống chạy qua vòng lặp thêm một lần nữa (để xem liệu ngắt có được đặt không ngủ không). Thật không may, Gekko sử dụng lệnh WFI thay vì địa chỉ ghi để kích hoạt chế độ ngủ; viết mã như

  nếu (! dont_s ngủ)
    WFI ();

sẽ có nguy cơ xảy ra gián đoạn giữa 'nếu' và 'wfi' và đặt dont_s ngủ, nhưng wfi sẽ tiếp tục và thực hiện bằng mọi cách. Mô hình tốt nhất để ngăn chặn điều đó là gì? Đặt PRIMASK thành 1 để ngăn chặn các ngắt làm gián đoạn bộ xử lý ngay trước khi thực hiện WFI và xóa nó ngay sau đó? Hoặc có một số mẹo tốt hơn?

BIÊN TẬP

Tôi đang tự hỏi về bit Sự kiện. Theo mô tả chung, nó muốn sử dụng cho hỗ trợ đa bộ xử lý, nhưng tự hỏi liệu một cái gì đó như sau có thể hoạt động không:

  nếu (dont_s ngủ)
    SEV (); / * Sẽ làm theo cờ sự kiện rõ ràng WFE nhưng không ngủ * /
  WFE ();

Mỗi ngắt đặt bộ không ngủ cũng nên thực hiện lệnh SEV, vì vậy nếu ngắt xảy ra sau kiểm tra "nếu", WFE sẽ xóa cờ sự kiện nhưng không đi ngủ. Nghe có vẻ như một mô hình tốt?


1
Lệnh WFI không đặt lõi ngủ nếu điều kiện đánh thức của nó là đúng khi lệnh được thực thi. Ví dụ: nếu có IRQ không rõ ràng khi WFI được thực thi thì nó hoạt động như một NOP.
Đánh dấu

@Mark: Vấn đề sẽ là nếu một ngắt được thực hiện giữa "if (! Dont_s ngủ)" và "WFI", thì điều kiện ngắt sẽ không còn chờ xử lý khi WFI thực thi, nhưng ngắt có thể đã đặt không ngủ vì nó đã làm một cái gì đó sẽ biện minh cho vòng lặp chính chạy lặp lại khác. Trên ứng dụng Cypress PSOC của tôi, bất kỳ ngắt nào gây ra sự thức tỉnh kéo dài sẽ làm hỏng ngăn xếp nếu mã dòng chính sắp ngủ, nhưng điều đó có vẻ khá khó chịu và tôi hiểu ARM không khuyến khích các thao tác ngăn xếp như vậy.
supercat

@supercat Ngắt có thể hoặc không thể bị xóa khi WFI thực thi. Tùy thuộc vào bạn và khi nào / nơi bạn chọn để xóa ngắt. Loại bỏ biến dont_s ngủ và chỉ sử dụng ngắt mặt nạ để biểu thị khi bạn muốn thức hoặc ngủ. Bạn chỉ có thể loại bỏ câu lệnh if cùng nhau và để WFI ở cuối vòng lặp chính. Nếu bạn đã phục vụ tất cả các yêu cầu, hãy xóa IRQ để bạn có thể ngủ. Nếu bạn cần tỉnh táo, hãy kích hoạt IRQ, mặt nạ của nó sẽ không có gì xảy ra, nhưng khi WFI cố gắng thực thi thì nó sẽ NOP.
Đánh dấu

2
@supercat Ở cấp độ cơ bản hơn, có vẻ như bạn đang cố gắng kết hợp một thiết kế điều khiển ngắt với thiết kế 'vòng lặp chính lớn', thường không quan trọng về thời gian, thường bỏ phiếu và có các ngắt tối thiểu. Trộn những thứ này có thể trở nên khá xấu xí khá nhanh. Nếu có thể, chọn một mô hình thiết kế hoặc mô hình khác để sử dụng. Hãy nhớ rằng với các bộ điều khiển ngắt hiện đại, về cơ bản bạn sẽ nhận được đa nhiệm ưu tiên giữa các ngắt và số tiền cho hàng đợi nhiệm vụ (dịch vụ một ngắt, sau đó ưu tiên cao hơn tiếp theo, v.v.). Sử dụng nó như là một lợi thế của bạn.
Đánh dấu

@Mark: Tôi đã phát triển một hệ thống sử dụng PIC 18x khá tốt trong một ứng dụng chạy bằng pin; do các giới hạn ngăn xếp, nó không thể xử lý quá nhiều trong một ngắt, vì vậy phần lớn các công cụ được xử lý trong vòng lặp chính trên cơ sở không thuận tiện. Nó chủ yếu hoạt động khá tốt, mặc dù có một vài điểm mà mọi thứ bị chặn trong một hoặc hai giây vì các hoạt động dài. Nếu tôi di chuyển sang ARM, tôi có thể sử dụng RTOS đơn giản để giúp phân tách các hoạt động dài hạn dễ dàng hơn, nhưng tôi không chắc nên sử dụng đa nhiệm ưu tiên hay hợp tác.
supercat

Câu trả lời:


3

Tôi không hoàn toàn hiểu dont_sleepđiều này, nhưng một điều bạn có thể thử là thực hiện "công việc chính" trong trình xử lý PendSV, được đặt ở mức ưu tiên thấp nhất. Sau đó, chỉ cần lên lịch PendSV từ các trình xử lý khác mỗi khi bạn cần một cái gì đó được thực hiện. Xem ở đây cách thực hiện (đối với M1 nhưng M3 không quá khác biệt).

Một thứ khác bạn có thể sử dụng (có thể cùng với cách tiếp cận trước đó) là tính năng Ngủ khi thoát. Nếu bạn kích hoạt nó, bộ xử lý sẽ chuyển sang chế độ ngủ sau khi thoát trình xử lý ISR ​​cuối cùng mà không cần bạn phải gọi WFI. Xem một số ví dụ ở đây .


5
lệnh WFI không yêu cầu ngắt được kích hoạt để đánh thức bộ xử lý, các bit F và I trong CPSR bị bỏ qua.
Đánh dấu

1
@Mark Tôi đã bỏ lỡ bit đó trong tài liệu, bạn có một số liên kết / con trỏ về nó không? Điều gì xảy ra với tín hiệu ngắt đánh thức lõi? Nó có được chờ đợi cho đến khi ngắt được kích hoạt lại không?
Igor Skochinsky

Hướng dẫn tham khảo ASM có tại đây: infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0361c/ Thông tin cụ thể hơn cho cortex-m3 có tại đây: infocenter.arm.com/help/ index.jsp? topic = / com.arm.doc.dui0552a / Lỗi khi một ngắt mặt nạ trở thành đang chờ xử lý, lõi thức dậy và tiếp tục hoạt động sau lệnh WFI. Nếu bạn đã cố gắng phát hành một WFI khác mà không xóa mà WFI đang chờ xử lý thì sẽ hoạt động như một NOP (lõi sẽ không ngủ vì điều kiện đánh thức cho WFI là đúng).
Đánh dấu

@Mark: Một điều tôi đang cân nhắc là sẽ có bất kỳ trình xử lý ngắt nào mà bộ dont_s ngủ cũng thực hiện lệnh SEV ("Đặt sự kiện"), sau đó sử dụng WFE ("Chờ sự kiện") thay vì WFI. Các ví dụ Gekko dường như sử dụng WFI, nhưng tôi nghĩ WFE cũng có thể hoạt động. Có suy nghĩ gì không?
supercat

10

Đặt nó trong một phần quan trọng. Các ISR sẽ không chạy, do đó bạn không có nguy cơ không thay đổi giấc ngủ trước WFI, nhưng chúng vẫn sẽ đánh thức bộ xử lý và ISR sẽ thực thi ngay khi phần quan trọng kết thúc.

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

Môi trường phát triển của bạn có thể có các chức năng phần quan trọng, nhưng nó đại khái như thế này:

EnterCriticalSection là:

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

ExitCriticalSection là:

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

2
Thật kỳ lạ, một số thư viện ARM sử dụng triển khai phần quan trọng sử dụng bộ đếm toàn cầu thay vì duy trì trạng thái cục bộ. Tôi thấy rằng tâm trí không ổn định vì cách tiếp cận bộ đếm phức tạp hơn và sẽ chỉ hoạt động nếu tất cả các mã trên toàn hệ thống sử dụng cùng một bộ đếm.
supercat

1
Sẽ không bị gián đoạn cho đến khi chúng ta thoát khỏi phần quan trọng? Nếu vậy, WFI sẽ không khiến CPU phải chờ đợi vô thời hạn?
Corneliu Zuzu

1
Câu trả lời của @Kenzi Tôm chỉ ra một chủ đề thảo luận Linux trả lời câu hỏi trước đây của tôi. Tôi chỉnh sửa câu trả lời của anh ấy và của bạn để làm rõ điều đó.
Corneliu Zuzu

@CorneliuZuzu Chỉnh sửa câu trả lời của người khác để xen vào cuộc thảo luận của riêng bạn không phải là một ý tưởng tuyệt vời. Thêm trích dẫn để cải thiện câu trả lời 'chỉ liên kết' là một vấn đề khác. Nếu bạn có một câu hỏi thực sự về chiến thắng của mình, có thể hỏi nó như một câu hỏi và liên kết với câu hỏi này.
Sean Houlihane

1
@SeanHoulihane Tôi không có hiệu lực cũng như không xóa bất cứ điều gì khỏi câu trả lời của cô ấy. Một sự làm rõ ngắn về lý do tại sao điều này hoạt động không phải là một cuộc thảo luận riêng biệt. Thành thật mà nói tôi không cảm thấy câu trả lời này xứng đáng được bình chọn mà không cần làm rõ WFI, nhưng nó xứng đáng với điều đó nhất.
Corneliu Zuzu

7

Ý tưởng của bạn là tốt, đây chính xác là những gì Linux thực hiện. Xem tại đây .

Trích dẫn hữu ích từ chuỗi thảo luận được đề cập ở trên để làm rõ lý do tại sao WFI hoạt động ngay cả khi ngắt bị vô hiệu hóa:

Nếu bạn có ý định nhàn rỗi cho đến lần gián đoạn tiếp theo, bạn phải thực hiện một số chuẩn bị. Trong quá trình chuẩn bị đó, một ngắt có thể trở nên hoạt động. Một ngắt như vậy có thể là một sự kiện đánh thức mà bạn đang tìm kiếm.

Cho dù mã của bạn tốt đến đâu, nếu bạn không vô hiệu hóa các ngắt, bạn sẽ luôn có một cuộc đua giữa việc chuẩn bị đi ngủ và thực sự đi ngủ, dẫn đến các sự kiện đánh thức bị mất.

Đây là lý do tại sao tất cả các CPU ARM mà tôi biết sẽ thức dậy ngay cả khi chúng bị che ở CPU lõi (bit CPSR I).

Bất cứ điều gì khác và bạn nên quên sử dụng chế độ không tải.


1
Bạn đang đề cập đến việc vô hiệu hóa các ngắt tại thời điểm hướng dẫn WFI hoặc WFE? Bạn có thấy sự khác biệt có ý nghĩa giữa việc sử dụng WFI hoặc WFE cho mục đích này không?
supercat

1
@supercat: Tôi chắc chắn sẽ sử dụng WFI. WFE IMO chủ yếu dành cho các gợi ý đồng bộ hóa giữa các lõi trong hệ thống đa lõi (ví dụ: thực hiện WFE khi spinlock bị lỗi và phát hành SEV sau khi đi ra khỏi spinlock). Ngoài ra, WFE đưa cờ che dấu gián đoạn vào tài khoản để nó không hữu ích ở đây như WFI. Mẫu này thực sự hoạt động tốt trong Linux.
Tôm

2

Giả sử rằng:

  1. Các chủ đề chính chạy các nhiệm vụ nền
  2. Các ngắt chỉ chạy các tác vụ ưu tiên cao và không có tác vụ nền
  3. Chủ đề chính có thể bị gián đoạn bất cứ lúc nào (nó thường không che dấu ngắt)

Sau đó, giải pháp là sử dụng PRIMASK để chặn các ngắt giữa xác thực cờ và WFI:

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

0

Còn chế độ Ngủ trên chế độ Thoát thì sao? Điều này sẽ tự động chuyển sang chế độ ngủ bất cứ khi nào trình xử lý IRQ thoát, do đó thực sự không có bất kỳ "chế độ bình thường" nào chạy sau khi được định cấu hình. Một IRQ xảy ra, nó thức dậy và chạy trình xử lý, và trở lại giấc ngủ. Không cần WFI.


2
Làm thế nào một người nên đối phó tốt nhất với thực tế là kiểu ngủ mà bộ xử lý sẽ rơi vào có thể khác nhau dựa trên điều gì đó xảy ra trong khi gián đoạn? Ví dụ, một sự kiện thay đổi pin có thể chỉ ra rằng dữ liệu nối tiếp có thể sắp diễn ra và do đó bộ xử lý sẽ giữ cho bộ dao động xung nhịp chính chạy trong khi chờ dữ liệu. Nếu vòng lặp chính xóa cờ sự kiện, kiểm tra những gì đang diễn ra và đặt bộ xử lý sang chế độ ngủ thích hợp với WFI, thì bất kỳ ngắt nào có thể ảnh hưởng đến chế độ nào sẽ phù hợp sẽ đặt cờ sự kiện ...
supercat

... và hủy bỏ giấc ngủ. Có một trình xử lý vòng lặp chính điều khiển chế độ ngủ có vẻ sạch hơn là phải lo lắng về nó trong mỗi lần ngắt. Phải "quay" một phần của vòng lặp chính trên mỗi ngắt có thể không hiệu quả tối ưu, nhưng nó không quá tệ, đặc biệt là nếu tất cả các ngắt có thể ảnh hưởng đến hành vi ngủ đều đánh cờ.
năm11
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.