🌱 MPU và DMA trong STM32 Hoạt Động cùng nhau Như Thế Nào?
Ở những bài viết trước mình đã từng nói về MPU - Memory Protection Unit. MPU được biết đến như một ngoại vi sử dụng để bảo vệ việc truy cập bộ nhớ. Tuy nhiên, việc hạn chế truy cập đó là đối với Core, còn đối với những master khác (Ví dụ như DMA) thì MPU có thể hạn chế truy cập của chúng đến memory hay không?
Câu trả lời là không! Và bên dưới mình sẽ giải thích và Demo vấn đề này.
Xem thêm:
👉 Về mặt lý thuyết
Rất đơn giản khi xét về mặt lý thuyết, MPU là một Core Peripheral, vì vậy nó sẽ chỉ tác dụng đối với Core. Chúng ta có thể nhìn vào sơ đồ hoạt động của MPU như hình sau:
Có thể thấy MPU nằm trong processor, và là trung gian giữa Core và Memory. Trong khi đó, DMA không nằm trong processor. Vì vậy, dễ thấy DMA và MPU không có liên quan gì đến nhau.
👉 Về mặt thực hành
Bài toán của mình đặt ra để kiểm tra hoạt động này của DMA và MPU. Bài toán sử dụng MPU với 2 Region config như sau:
Region | Start Address | End Address | Access Right |
---|---|---|---|
0 | 0x0000.0000 | 0xFFFF.FFFF | Full Access |
1 | 0x2000.7000 | 0x2000.FFFF | Read Only |
Vùng RAM (Region 1) sẽ dùng để test hoạt động của DMA.
-
Sử dụng DMA transfer từ một vùng RAM khác (địa chỉ 0x2001.6000, data = 0x5A5A.5A5A) sang địa chỉ thuộc Region 1 (địa chỉ 0x2000.7F00).
👉 Kết quả mong muốn: Transfer thành công do DMA không bị ảnh hưởng bởi MPU. -
Sử dụng Core ghi trực tiếp data vào địa chỉ thuộc Region 1 (địa chỉ 0x2000.7F00).
👉 Kết quả mong muốn: Chương trình nhảy vào Fault (MemManage) do Core bị MPU hạn chế quyền write vào Region 1.
💬 Source Code
#include "stm32f4xx.h"
#define SRCADDR 0x20016000UL
#define DESTADDR 0x20007F00UL
// MPU Configuration
void MPU_Init(void) {
// Enable MPU
MPU->CTRL = MPU_CTRL_ENABLE_Msk | MPU_CTRL_PRIVDEFENA_Msk;
// Region 0: Full access to entire memory
MPU->RNR = 0; // Select Region 0
MPU->RBAR = 0x00000000; // Base address
MPU->RASR = (0x1F << MPU_RASR_SIZE_Pos) | // Size: 4GB (2^(31+1))
(0x3 << MPU_RASR_AP_Pos) | // Full access (RW)
MPU_RASR_ENABLE_Msk; // Enable region
// Region 1: Read-only SRAM (0x20007000 - 0x2000FFFF)
MPU->RNR = 1; // Select Region 1
MPU->RBAR = 0x20007000; // Base address
MPU->RASR = (0x0F << MPU_RASR_SIZE_Pos) | // Size: 32KB (2^(15+1))
(0x1 << MPU_RASR_AP_Pos) | // Read-only (Privileged RO)
MPU_RASR_ENABLE_Msk; // Enable region
}
// DMA Configuration (Memory-to-Memory)
void DMA_Init(void) {
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // Enable DMA2 clock
DMA2_Stream0->CR = 0; // Disable stream before config
DMA2_Stream0->CR = (0 << DMA_SxCR_CHSEL_Pos) | // Channel 0
(0b10 << DMA_SxCR_DIR_Pos) | // Memory-to-Memory
DMA_SxCR_MINC | // Memory increment
DMA_SxCR_PINC | // Peripheral increment
(0b10 << DMA_SxCR_MSIZE_Pos) | // Memory size: 32-bit
(0b10 << DMA_SxCR_PSIZE_Pos) | // Peripheral size: 32-bit
(0b01 << DMA_SxCR_PL_Pos); // Priority: Medium
}
void DMA_Config(uint32_t src, uint32_t dest, uint32_t size) {
DMA2_Stream0->NDTR = size; // Number of data items
DMA2_Stream0->PAR = src; // Source address
DMA2_Stream0->M0AR = dest; // Destination address
DMA2_Stream0->CR |= DMA_SxCR_EN; // Enable DMA
}
int main(void) {
// Initialize MPU and DMA
MPU_Init();
DMA_Init();
// Write to Source Data - SRAM
*(volatile uint32_t *)SRCADDR = 0x5A5A5A5A;
// DMA Transfer: Should succeed
DMA_Config(SRCADDR, DESTADDR, 1);
// Wait for DMA transfer to complete
while (!(DMA2->LISR & DMA_LISR_TCIF0));
DMA2->LIFCR |= DMA_LIFCR_CTCIF0; // Clear flag
// Core Write: Should trigger MemManage Fault
*(volatile uint32_t *)DESTADDR = 0x12345678;
while (1); // Check memory in debugger
}
// MemManage Fault Handler
void MemManage_Handler(void) {
while (1); // Trap here for debugging
}
Giải thích:
- MPU_Init: Cấu hình 2 region: Region 0 (Full access toàn bộ 4GB), Region 1 (Read-only SRAM 32KB từ 0x20007000).
- DMA_Init: Cấu hình DMA2 Stream 0 cho Memory-to-Memory.
- DMA_Config: Truyền 1 word (4 byte) từ 0x20016000 sang 0x20007F00.
- Core Write: Thử ghi trực tiếp vào 0x20007F00, sẽ gây MemManage Fault.
- Handler: Bẫy lỗi MemManage để debug.
💬 Kết quả
Khi transfer bằng DMA - Data transfer thành công.
Khi ghi trực tiếp bằng Core - Giá trị ô nhớ không thay đổi, và chương trình nhảy vào fault.
👉 Video Demo
Video Demo về hoạt động của DMA và MPU trên vi điều khiển STM32
///...
👉 Kết luận
MPU là một công cụ tốt để protect memory khỏi các truy cập của Core, tuy nhiên với các Non-Core Master thì MPU không chắc có tác dụng hạn chế quyền truy cập (Cần kiểm chứng thêm với các Non-Core Master khác ngoài DMA - Ví dụ Debugger). Đối với DMA đã được kiểm chứng hoạt động, nó không bị hạn chế bởi các Attribute (Access Right) của MPU. Vì vậy, để bảo vệ memory khỏi các Non-Core Master như DMA, chúng ta cần các cơ chế khác.
>>>>>> 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 😊