A technical blog about Embedded, Coding, IoT, Autosar and AI.

Home/Embedded/ARM-CORTEX-M4/[CORTEX M4 - 004_c]: Other register of Arm cortex M4
EmbeddedARM-CORTEX-M4Author: DukeChanMarkdown

[CORTEX M4 - 004_c]: Other register of Arm cortex M4

Overview

On This Page

4.2 Programmer’s model

PRIMASK, FAULTMASK và BASEPRI

Các thanh ghi PRIMASK, FAULTMASKBASEPRI đều được sử dụng để mask exception hoặc interrupt. Mỗi exception (bao gồm cả interrupt) đều có một mức ưu tiên, trong đó:

  • số càng nhỏ thì mức ưu tiên càng cao
  • số càng lớn thì mức ưu tiên càng thấp

Ba thanh ghi đặc biệt này được dùng để mask exception dựa trên mức ưu tiên. Chúng chỉ có thể được truy cập ở privileged access level. Nếu đang ở unprivileged, thao tác ghi vào các thanh ghi này sẽ bị bỏ qua và thao tác đọc sẽ trả về 0.

Theo mặc định, cả ba thanh ghi này đều có giá trị 0, nghĩa là cơ chế mask, tức vô hiệu hóa exception hoặc interrupt, chưa được kích hoạt.

PRIMASK

Thanh ghi PRIMASK là một thanh ghi mask interrupt có độ rộng 1 bit. Khi bit này được set, nó sẽ chặn tất cả các exception (bao gồm cả interrupt), ngoại trừ:

  • NMI (Non-Maskable Interrupt)
  • HardFault

Về bản chất, việc set PRIMASK làm cho mức ưu tiên exception hiện tại được nâng lên mức 0, tức là mức cao nhất trong nhóm exception hoặc interrupt có thể lập trình được.

Cách sử dụng phổ biến nhất của PRIMASKtạm thời vô hiệu hóa toàn bộ interrupt trong một đoạn xử lý cần tính thời gian chính xác hoặc cần tính nguyên tử. Sau khi đoạn xử lý đó hoàn tất, PRIMASK cần được xóa để cho phép interrupt hoạt động trở lại.

FAULTMASK

Thanh ghi FAULTMASK khá giống với PRIMASK, nhưng mạnh hơn ở chỗ nó còn chặn cả HardFault. Điều này tương đương với việc nâng mức ưu tiên exception hiện tại lên -1.

FAULTMASK thường được sử dụng trong các đoạn mã xử lý lỗi (fault handler) để ngăn các lỗi khác tiếp tục phát sinh trong khi đang xử lý lỗi hiện tại. Ví dụ, FAULTMASK có thể được dùng để:

  • bỏ qua kiểm tra của MPU
  • chặn bus fault

nhằm giúp đoạn mã xử lý lỗi có thể thực hiện các hành động khắc phục dễ dàng hơn.

Khác với PRIMASK, FAULTMASK sẽ tự động được xóa khi exception return.

BASEPRI

Để cung cấp khả năng mask interrupt linh hoạt hơn, kiến trúc ARMv7-M còn cung cấp thanh ghi BASEPRI. Thanh ghi này cho phép mask exception hoặc interrupt dựa trên ngưỡng ưu tiên.

Độ rộng của BASEPRI phụ thuộc vào số lượng mức ưu tiên mà vi điều khiển hiện thực, và điều này do nhà sản xuất vi điều khiển quyết định. Phần lớn các vi điều khiển Cortex-M3/Cortex-M4 hỗ trợ:

  • 8 mức ưu tiên có thể lập trình → BASEPRI rộng 3 bit
  • hoặc 16 mức ưu tiên → BASEPRI rộng 4 bit

Cách hoạt động của BASEPRI:

  • Khi BASEPRI = 0 → cơ chế mask bị vô hiệu
  • Khi BASEPRI ≠ 0 → các exception hoặc interrupt có mức ưu tiên bằng hoặc thấp hơn giá trị đó sẽ bị chặn
  • Những exception có mức ưu tiên cao hơn vẫn có thể được CPU chấp nhận

Điều này rất hữu ích khi cần chặn một nhóm interrupt “ít quan trọng hơn”, nhưng vẫn cho phép các interrupt quan trọng hơn được xử lý.

Truy cập bằng CMSIS-Core

CMSIS-Core cung cấp một số hàm để truy cập các thanh ghi PRIMASK, FAULTMASK và BASEPRI trong môi trường C:

C
x = __get_BASEPRI();    // Đọc thanh ghi BASEPRI
x = __get_PRIMASK();    // Đọc thanh ghi PRIMASK
x = __get_FAULTMASK();  // Đọc thanh ghi FAULTMASK

__set_BASEPRI(x);       // Ghi giá trị mới vào BASEPRI
__set_PRIMASK(x);       // Ghi giá trị mới vào PRIMASK
__set_FAULTMASK(x);     // Ghi giá trị mới vào FAULTMASK

__disable_irq();        // Set PRIMASK, vô hiệu hóa IRQ
__enable_irq();         // Clear PRIMASK, cho phép IRQ

Truy cập bằng Assembly

Ngoài ra, có thể truy cập các thanh ghi mask exception này bằng Assembly:

asm
MRS r0, BASEPRI      ; Đọc BASEPRI vào R0
MRS r0, PRIMASK      ; Đọc PRIMASK vào R0
MRS r0, FAULTMASK    ; Đọc FAULTMASK vào R0

MSR BASEPRI, r0      ; Ghi R0 vào BASEPRI
MSR PRIMASK, r0      ; Ghi R0 vào PRIMASK
MSR FAULTMASK, r0    ; Ghi R0 vào FAULTMASK

Ngoài ra, lệnh CPS (Change Processor State) cũng cho phép set hoặc clear PRIMASK và FAULTMASK bằng cách ngắn gọn hơn:

asm
CPSIE i    ; Enable interrupt  (clear PRIMASK)
CPSID i    ; Disable interrupt (set PRIMASK)
CPSIE f    ; Enable fault      (clear FAULTMASK)
CPSID f    ; Disable fault     (set FAULTMASK)

Lưu ý: Thanh ghi FAULTMASKBASEPRI không có trên kiến trúc ARMv6-M (ví dụ: Cortex-M0).

CONTROL register

Thanh ghi CONTROL được sử dụng để xác định:

  • lựa chọn Stack Pointer đang dùng (MSP hoặc PSP)
  • mức đặc quyền khi chạy ở Thread mode (Privileged hoặc Unprivileged)

Ngoài ra, với Cortex-M4 có FPU, một bit trong CONTROL còn cho biết ngữ cảnh hiện tại có đang sử dụng floating point unit hay không.

Lưu ý: Trên ARMv6-M (ví dụ Cortex-M0), hỗ trợ cho bit nPRIV và chế độ unprivilegedphụ thuộc hiện thực. Nó không có trên thế hệ Cortex-M0 đầu tiên và Cortex-M1, còn trên Cortex-M0+ thì là tùy chọn.

Thanh ghi CONTROL:

  • chỉ có thể được ghi khi đang ở privileged access level
  • nhưng có thể được đọc ở cả privileged và unprivileged

Các bit trong CONTROL

  • nPRIV (bit 0): xác định mức đặc quyền trong Thread mode

    • 0 (mặc định): Thread mode chạy ở privileged
    • 1: Thread mode chạy ở unprivileged
      Trong Handler mode, CPU luôn ở privileged
  • SPSEL (bit 1): chọn Stack Pointer dùng trong Thread mode

    • 0 (mặc định): Thread mode dùng MSP
    • 1: Thread mode dùng PSP
      Trong Handler mode, bit này luôn được xem là 0 và việc ghi vào bit này sẽ bị bỏ qua
  • FPCA (bit 2): chỉ có trên Cortex-M4 có FPU
    Bit này cho biết ngữ cảnh hiện tại có sử dụng các thanh ghi floating point hay không.

    • 0: ngữ cảnh hiện tại chưa dùng FPU, không cần lưu các thanh ghi FPU khi có exception
    • 1: ngữ cảnh hiện tại đã dùng FPU, cần lưu thêm các thanh ghi FPU khi có exception

Bit FPCA sẽ được set tự động khi có lệnh floating point được thực thi, và sẽ được phần cứng clear khi vào exception.

Giá trị mặc định sau reset

Sau reset, thanh ghi CONTROL = 0. Điều này có nghĩa là:

  • Thread mode dùng MSP
  • Thread mode chạy ở privileged

Một chương trình đang chạy ở privileged Thread mode có thể đổi:

  • từ MSP sang PSP
  • hoặc từ privileged sang unprivileged

bằng cách ghi vào CONTROL.

Tuy nhiên, một khi bit nPRIV đã được set, đoạn mã đang chạy trong Thread mode sẽ không còn quyền truy cập CONTROL để tự nâng quyền lại.

Đây là điểm rất quan trọng để xây dựng một mô hình bảo mật cơ bản. Ví dụ, một hệ thống nhúng có thể chạy các ứng dụng không đáng tin cậy ở unprivileged, từ đó hạn chế khả năng chúng làm hỏng toàn bộ hệ thống hoặc gây ra lỗ hổng bảo mật.

Quay trở lại privileged bằng exception

Một chương trình chạy ở unprivileged Thread mode không thể tự chuyển lại thành privileged Thread mode. Muốn làm điều đó, bắt buộc phải dùng exception mechanism.

Trong quá trình xử lý exception, exception handler có thể clear bit nPRIV. Sau khi exception return, bộ xử lý sẽ quay lại Thread mode ở mức privileged.

Khi có embedded OS, thanh ghi CONTROL có thể được thay đổi tại mỗi lần context switch, cho phép:

  • một số task chạy ở privileged
  • một số task khác chạy ở unprivileged

Các tổ hợp của nPRIV và SPSEL

Về mặt lý thuyết có 4 tổ hợp giữa nPRIVSPSEL, nhưng trong thực tế chỉ có 3 tổ hợp là phổ biến:

  • nPRIV = 0, SPSEL = 0
    Ứng dụng đơn giản, toàn bộ chương trình chạy ở privileged, chỉ dùng MSP

  • nPRIV = 0, SPSEL = 1
    Hệ thống có embedded OS, task hiện tại chạy ở privileged Thread mode, dùng PSP; còn MSP dùng cho kernel và exception handler

  • nPRIV = 1, SPSEL = 1
    Hệ thống có embedded OS, task hiện tại chạy ở unprivileged Thread mode, dùng PSP; còn MSP dùng cho kernel và exception handler

  • nPRIV = 1, SPSEL = 0
    Thread mode chạy ở unprivileged nhưng vẫn dùng MSP. Trường hợp này ít gặp trong thực tế vì đa số embedded OS tách riêng stack của user task và kernel

Trong các ứng dụng đơn giản không dùng embedded OS, thường không cần thay đổi giá trị của CONTROL. Toàn bộ chương trình có thể chạy ở privileged và chỉ dùng MSP.

Truy cập CONTROL bằng CMSIS

Trong các thư viện tuân theo CMSIS, có thể truy cập CONTROL bằng các hàm sau:

C
x = __get_CONTROL();   // Đọc giá trị hiện tại của CONTROL
__set_CONTROL(x);      // Ghi giá trị mới vào CONTROL

Hai lưu ý quan trọng khi sửa CONTROL

  • Với Cortex-M4 có FPU, bit FPCA có thể được set tự động nếu chương trình có dùng lệnh floating point. Nếu chương trình có sử dụng floating point mà vô tình clear FPCA, sau đó xảy ra interrupt thì các thanh ghi FPU có thể không được lưu lại trong quá trình exception entry và có nguy cơ bị interrupt handler ghi đè. Khi đó chương trình sẽ không thể tiếp tục xử lý chính xác sau khi quay lại task bị ngắt.

  • Về mặt kiến trúc, sau khi sửa CONTROL, nên dùng lệnh ISB (Instruction Synchronization Barrier) hoặc hàm __ISB() trong CMSIS để đảm bảo thay đổi có hiệu lực với các lệnh tiếp theo. Tuy nhiên, do pipeline của Cortex-M3/M4/M0/M0+/M1 khá đơn giản nên trên thực tế việc bỏ qua ISB thường không gây vấn đề.

Truy cập CONTROL bằng Assembly

asm
MRS r0, CONTROL    ; Đọc CONTROL vào R0
MSR CONTROL, r0    ; Ghi R0 vào CONTROL

Kiểm tra CPU đang ở privileged hay không

Có thể kiểm tra mức thực thi hiện tại bằng cách kết hợp IPSRCONTROL:

C
int in_privileged(void)
{
    if (__get_IPSR() != 0) return 1;                  // Đang ở Handler mode -> privileged
    else if ((__get_CONTROL() & 0x1) == 0) return 1; // Thread mode và nPRIV = 0
    else return 0;                                    // Unprivileged Thread mode
}

Ý tưởng của đoạn mã trên là:

  • nếu IPSR != 0 thì CPU đang ở Handler mode, mà Handler mode luôn là privileged
  • nếu đang ở Thread mode thì kiểm tra bit nPRIV của CONTROL
  • nếu nPRIV = 0 thì đang ở privileged
  • ngược lại là unprivileged

Author

DukeChan

Xin chào các con vợ, anh là Duc Khong Buon. Các bài biết của anh trên này với mục đích text note và lưu trữ tài liệu. Cảm ơn các con vợ đã view profile của anh.

Continue Reading

Previous and Next Articles

Related Articles

Community

Leave a Comment

Loading comments...
Loading comments...