🌱 ARM Cortex-M Exception Model

🌱 ARM Cortex-M Exception Model

    Exception / Interrupt là các khái niệm rất quan trọng trong lõi ARM nói chung và ARM Cortex-M nói riêng.

    > Cách thiết kế chương trình theo Interrupt đã được giới thiệu tại bài viết này!

    Về cơ bản, một Exception / Interrupt sẽ mong muốn Core dừng chương trình hiện tại lại, chuyển sang thực hiện một chương trình khác (gọi là chương trình phục vụ ngắt), sau đó lại tiếp tục trở lại với chương trình đang chạy.

    ➤ Exception States

    Với lý thuyết trên, mỗi Exception được ARM quy định một số trạng thái như sau:

  • Inactive : Exception đã được enable, chưa xảy ra đang không active cũng không pending.
  • Pending : Exception đã xảy ra, đang đợi để được Core xử lý.
  • Active : Exception đang được Core xử lý, chưa hoàn tất.
  • Active and pending : Exception đang được Core xử lý, chưa hoàn tất, nhưng chính một exception tương tự cũng đang ở trạng thái pending.
Exception States
Sơ đồ chuyển trạng thái Exception

    Các trạng thái này sẽ được quản lý bởi bộ NVIC - Nested Vector Interrupt Controller.

    ➤ Exception Types

    Có thể chia exception của ARM Cortex-M thành 2 loại:

    ① System Exception

    Các Exception đến từ core, và tùy thuộc vào version sẽ có số lượng system exception khác nhau.

    🔻 Reset

    Xảy ra khi bật nguồn hoặc warm reset. Đây là tín hiệu Exception đặc biệt nhất, sẽ đưa hệ thống trở về trạng thái reset (giá trị thanh ghi, bộ nhớ), và đưa Processor trở về Thread Mode (Các Exception khác xảy ra đưa Processor vào Handler Mode). Đây cũng là Exception có mức ưu tiên cao nhất (-3).

    🔻 NMI (NonMaskable Interrupt)

    Là tín hiệu exception được thiết kế đến từ các peripheral hoặc Software, có mức ưu tiên cao thứ hai (-2) chỉ sau Reset. Vì vậy, một số dòng vi điều khiển sẽ thiết kế để liên kết các ngoại vi gửi tín hiệu NMI đến Core, thường là một số ngoại vi liên quan đến Safety (Ví dụ Watchdog trong chip của TI, Toshiba, Low-Voltage Detection trong dòng FM3 của Cypress, FCCU của một số chip NXP).

    🔻 HardFault

    Exception được tạo ra khi có một số lỗi trong quá trình xử lý exception khác, hoặc các lỗi mà không có exception nào đảm nhiệm đều gây ra Hardfault. Ví dụ: Access thanh ghi ngoại vi mà chưa được cấp Clock, Stack Overflow, ... HardFault có độ ưu tiên cố định cao thứ ba (-1).

    🔻 MemManage

    Exception xảy ra bởi một truy cập trái phép vào bộ nhớ. Ví dụ như vùng nhớ được MPU - Memory Protection Unit bảo vệ hoặc cấu hình là Execute Never (XN).

    🔻 BusFault

    Exception xảy ra khi có một lỗi xảy ra với transaction trên instruction bus hoặc data bus.

    🔻 UsageFault

    Exception xảy ra khi thực thi lệnh (undefined instruction, illegal unaligned access, invalid state on instruction execution, an error on exception return), hoặc các lỗi như phép chia cho số 0 (division by zero).

    🔻 SVCall (Supervisor Call)

    Exception được tạo ra bởi lệnh SVC. Trong môi trường OS, lệnh SVC cho phép application truy cập vào OS kernel và device tree.

    🔻 PendSV

    PendSV là một interrupt-driven request, sử dụng cho việc Context Switch trong môi trường OS.

    🔻 Systick

    Exception gây ra bởi bộ System Timer, bộ timer nội trong Processor khi nó đếm tràn về 0.

    ② Interrupt (IRQ)

    Tín hiệu Exception đến từ các ngoại vi hoặc phần mềm. Các ngoại vi ở đây thì tùy vào nhà sản xuất vi điều khiển tích hợp (Port, ADC, UART, Timer, ...).

Exception Model - Vector Table

     Mỗi Exception sẽ được định danh bằng Exception Number, bắt đầu từ 1 (Reset), và được gọi là Vector Ngắt hay Line Ngắt. ARM Cortex-M hỗ trợ tối đa 15 System Exception, các Interrupt sẽ bắt đầu từ Exception Number = 16. Vì vậy để đơn giản hóa việc lập trình các Interrupt, ARM hỗ trợ thêm định danh các Exception bằng IRQ Number = Exception Number - 16. Tức là các IRQ sẽ bắt đầu từ 0.

    Về Priority, chỉ có 3 Exception đầu tiền là Reset / NMI / HardFault là có Priority cao nhất và cố định, các Exception khác đều có mức ưu tiên cấu hình được (>= 0).

    ➤ Vector Table

    Như đề cập ở trên, khi exception xảy ra, CPU sẽ nhảy đến một chương trình phục vụ ngắt để thực hiện chương trình. Chương trình phục vụ ngắt này có 3 loại:

  • ISR - Interrupt Service Routine : chương trình phục vụ ngắt của các Interrupt (IRQ)
  • Fault Handler : chương trình phục vụ ngắt của HardFault, MemManage fault, UsageFault, BusFault
  • System Handlerchương trình phục vụ ngắt của NMI, PendSV, SVCall, SysTick

    Một Exception đặc biệt đó là Reset đã được đề cập trong bài viết về Reset Sequence. Khi Reset xảy ra, Core sẽ nhảy đến địa chỉ 0x04 (chứ địa chỉ hàm Reset_ISR) để thực hiện PC = Reset_ISR.

    Tương tự như cơ chế này, các Exception khác khi xảy ra cũng cần có một ô nhớ cố định nào đó (giống 0x04 của Reset), để Core tìm đến, ô nhớ đó sẽ chứa địa chỉ của chương trình phục vụ ngắt tương ứng.

    ➥ Và ARM đã tập hợp chúng lại một chỗ, gọi là bảng Vector Table. Theo tài liệu của ARM thì Vector Table chứa giá trị reset của Stack Pointer, và địa chỉ của các Exception Handler.

Exception Model
ARM Cortex M3 Vector Table

    ➤ Exception Entry and Return

    ① Exception Entry

    Mình xét đến trường hợp đơn giản nhất là chương trình đang ở Thread Mode, thì có một Exception xảy ra, Core sẽ dừng chương trình hiện tại, chuyển qua Handler Mode, và gây ra quá trình Exception Entry.

    Giống như Function Call, việc Core thực hiện ngắt bản chất là PC nhảy từ địa chỉ lệnh này sang địa chỉ một hàm khác, vì vậy nó cần lưu lại thông tin (Trạng thái chương trình, Next Instruction Address và các thanh ghi tạm).

    Đối với vi điều khiển lõi ARM, Core ARM sẽ lưu trữ 8 thanh ghi của Core lên trên bộ nhớ stack, 8 thanh ghi này bao gồm: (xPSR, LR - Trạng thái chương trình / PC - liên quan đến next instruction address / R0-R3 và R12, các thanh ghi tạm).

    Các thanh ghi này được đề cập trong bài viết về Core Registers

Stack Frame

     8 thanh ghi trên được đẩy lên stack được gọi là Stack Frame, và quá trình trên gọi là quá trình Stacking.

    Sau khi stacking hoàn tất, Processor sẽ fetch bảngVvector Table để lấy địa chỉ hàm phục vụ ngắt để thực thi. Lúc này, Processor sẽ làm một công việc nữa là ghi giá trị EXC_RETURN vào thanh ghi LR. Giá trị này mang ý nghĩa Stack FrameOperation Mode mà processor đang thực có trước khi vào hàm ngắt hiện tại.

    Khi thực hiện xong chương trình ngắt, Core sẽ thực hiện UnStacking để trả lại giá trị cho các thanh ghi có trong Stack Frame.

    ② Exception Return

    EXC_RETURN đã được lưu vào LR trong Exception Entry như trên. Đây là một giá trị 32-bits chứa các thông tin về Stack Frame và Operation Mode của Processor trước khi vào hàm ngắt, thể hiện ở các bit [3:0] của EXC_RETURN. Đối với ARM Cortex M3/4, bit [31:4] của EXC_RETURN có giá trị là 0xFFFFFF. Sau khi thực hiện xong chương trình ngắt, giá trị này được gán vào PC và từ đó Core xác định được Stack Pointer và Operator Mode.

Exception Return

    ➤ Tổng kết

    Từ những kiến thức kể trên, chúng ta có thể tổng hợp lại flow đơn giản của một Exception/Interrupt như sau (giả định chỉ có duy nhất ngắt đang xét xảy ra):

  1. Ban đầu khi được enable, Interrupt ở trạng thái Inactive (chưa xảy ra).
  2. Khi có một tín hiệu ngắt xảy ra, interrupt đó sẽ vào trạng thái Pending.
  3. Nếu không có ngắt nào đang thực thi, NVIC sẽ chuyển trạng thái interrupt thành Active, processor mode là Handler Mode, thực hiện exception entry - stacking 8 thanh ghi stack frame lên bộ nhớ stack.
  4. Stacking hoàn tất, Core sẽ fetch bảng Vector Table, tìm đến địa chỉ của Vector Ngắt tương ứng, gán PC bằng địa chỉ đó để bắt đầu thực thi chương trình phục vụ ngắt (ISR).
    1. Cùng lúc này Core sẽ gán giá trị EXC_RETURN vào thanh ghi LR.
  5. Sau khi thực thi xong chương trình ngắt, giá trị EXC_RETURN được gán vào PC để xác định stack pointer và operation mode, sau đó là quá trình UnStacking diễn ra để trả về giá trị các thanh ghi.
  6. Interrupt trở lại trạng thái Inactive.

    Trên đây là các kiến thức cơ bản về Exception/Interrupt được mình tổng hợp và dịch từ tài liệu core của ARM, nếu có điều gì chưa chính xác mong các bạn đọc có thể bổ sung và giúp mình follow kênh Youtube bên dưới nhé!

    ➥ Tài liệu tham khảo: Cortex M3 Device Generic User Guide

>>>= Follow ngay =<<<

Để nhận được những bài học miễn phí mới nhất nhé 😊

Chúc các bạn học tập tốt 😊

        

Nguyễn Văn Nghĩa

Mình là một người thích học hỏi và chia sẻ các kiến thức về Nhúng IOT.

5 Nhận xét

  1. dạ anh cho em hỏi ở phần Exception Entry thì 8 thanh ghi (xPSR, LR - Trạng thái chương trình / PC - liên quan đến next instruction address / R0-R3 và R12, các thanh ghi tạm) được lưu vào stack, em thắc mắc là nó sẽ lưu vào vùng nào của stack ạ, và trong quá trình unstacking thì thứ tự return của nó ra sao ạ, em cảm ơn anh

    Trả lờiXóa
  2. Stack ở đây đề cập đến vùng stack đang dùng (main stack), nó không chia làm các vùng nhỏ hơn. Còn hoạt động của stack là LIFO, nên thứ tự unstack ngược lại thôi

    Trả lờiXóa
    Trả lời
    1. ví dụ trong projects RTOS của mình có 4 task, khi context switch xảy ra thì cần lưu giá trị của các thanh ghi core của task hiện tại lại trước khi chuyển qua task khác, nếu thứ tự thực hiện lần lượt là task1,2,3,4 mà hoạt động theo kiểu LIFO thì làm sao các task lấy ra đúng các giá trị của thanh ghi core được nhỉ

      Xóa
  3. Trong RTOS thì lại khác nhé, thông thường thì mỗi task sẽ sử dụng một vùng nhớ làm stack của riêng nó

    Trả lờiXóa
    Trả lời
    1. dạ vậy anh giải thích dùm em là 8 thanh ghi đó trong rtos thì nó sẽ được lưu và lấy ra như thế nào vậy ạ

      Xóa
Mới hơn Cũ hơn