Dung lượng bộ nhớ Stack cho dự án nhúng sử dụng Arm Cortex-M? (P1)

“Dung lượng bộ nhớ Stack bao nhiêu là đủ?” – Bạn đã từng đặt câu hỏi này cho dự án của mình chưa?

Chúng ta cùng tìm hiểu trả lời câu hỏi trên trong chuỗi bài viết này nhé!

1. Tổng quan

Trong quá trình thiết bị hoạt động, nếu dung lượng Stack không đủ để sử dụng, có thể sẽ dẫn đến việc tràn dữ liệu vào không gian bộ nhớ khác. Dẫn đến việc chương trình báo lỗi hay hoạt động bất thường. Ngoài ra, đối với các hệ thống có yêu cầu bảo mật, tình trạng tràn bộ nhớ Stack cũng có thể dẫn đến các lỗ hổng bảo mật.

Hầu hết đối với các công cụ Môi trường phát triển phần mềm tích hợp (IDE) được sử dụng để triển khai chương trình dự án vi điều khiển, các công cụ này đều yêu cầu người lập trình khai báo trước thông số kích thước của phân vùng Stack. Thông thường, các thông số này thường được cấu hình sẵn kèm theo gói phần mềm dành cho mã chương trình vi điều khiển được sử dụng (điều này thường dẫn đến việc người lập trình không biết đến hay quan tâm tìm hiểu sâu đến bộ nhớ Stack, cho đến khi chương trình xảy ra lỗi). Vì vậy, việc nắm chắc các kiến thức để sử dụng, cấu hình bộ nhớ Stack cho một dự án nhúng là quan trọng. Từ đó để người lập trình có thể thiết lập phạm vi / kích thước Stack cho các dự án một cách phù hợp. Thông thường, các công cụ (IDE) cũng thường cho phép nhà phát triển xác định kích thước của bộ nhớ Heap (bộ nhớ Heap được sử dụng cho các hàm như “malloc()”, chủ đề này mình sẽ trình bày trong một bài viết khác).

Có nhiều yếu tố có thể ảnh hưởng đến nhu cầu về dung lượng Stack, chẳng hạn như các công cụ biên dịch (compilation tools) với các tùy chọn biên dịch (compilation options), sử dụng Hệ điều hành thời gian thực (RTOS),.. Vì vậy không có quy tắc chung đơn giản nào để xác định kích thước Stack cho tất cả các dự án. Bài viết này trình bày các nội dung tổng quan, nền tảng nhu cầu về kích thước Stack trong các ứng dụng nhúng cho các vi điều khiển được thiết kế dựa trên vi xử lý Arm Cortex-M. Hy vọng qua nội dung chia sẻ này sẽ giúp các bạn có thể phân tích và quyết định được việc xác định kích thước Stack cho dự án của các bạn.

2. Cấu trúc bộ nhớ Stack

Chúng ta sẽ tìm hiểu tổng quan về hoạt động Stack trong Vi xử lý Arm Cortex-M. Trong Vi xử lý này, Stack hoạt động theo mô hình “Full Descending”, với mô hình này, có một con trỏ được gọi là con trỏ Stack (SP – Stack Pointer) trỏ đến vị trí cuối cùng chứa dữ liệu trong phân vùng Stack, nếu dữ liệu được push (thêm vào) vào Stack, SP sẽ giảm giá trị (trỏ đến địa chỉ nhỏ hơn / phía dưới) và dữ liệu sẽ được lưu ngay tại địa chỉ mà SP trỏ tới, chính là giá trị của SP.

Hình 1. Hoạt động của mô hình “Full Descensing” (nguồn: community.arm.com)

Ví dụ:

  • push R0: thêm giá trị của R0 ngay sau địa chỉ thấp nhất hiện tại của Stack
  • pop R0: di chuyển/loại bỏ phần tử R0 ngay địa chỉ thấp nhất hiện tại của Stack 

 

Tùy theo cấu trúc của vi xử lý Arm Cortex M mà có những loại SP khác nhau.

  • Armv6-M (Cortex-M0, Cortex-M0+, Cortex-M1): có 2 loại SP
  • Armv7-M (Cortex-M3, Cortex-M4, Cortex-M7): có 2 loại SP
  • Armv8-M: tối đa 4 loại SP
Loại Stack SP trong Armv6/7-M SP trong Armv8-M
Main Stack: Các ứng dụng không có Hệ điều hành (OS), các ứng dụng xử lý ngoại lệ (system exception & Interrupt) Main Stack Pointer (MSP) – Secure Main Stack Pointer (MSP_S): Các chương trình phần mềm Bảo mật. Áp dụng khi phần mềm chứa công nghệ TrustZone.

– Non-secure Main Stack Pointer (MSP_NS): Các chương trình không áp dụng bảo mật.

Process Stack: Các ứng dụng tác vụ trong môi trường Hệ điều hành Process Stack Pointer (PSP) – Secure Process Stack Pointer (PSP_S): Các chương trình phần mềm Bảo mật. Áp dụng khi phần mềm chứa công nghệ TrustZone.

– Non-secure Processor Stack Pointer (PSP_NS): Các chương trình không áp dụng bảo mật.
(Nên xem lại kỹ hơn nội dung cột này)

* Tìm hiểu về TrustZone tại đây.

Trong hầu hết các ứng dụng không sử dụng Hệ điều hành Nhúng (ví dụ như RTOS), MSP sẽ được sử dụng. PSP có thể không cần sử dụng. Trong trường hợp này, chúng ta chỉ cần một không gian (một phân vùng) Stack trong tổ chức bộ nhớ cho chương trình ứng dụng.

Trong trường hợp chương trình sử dụng RTOS, thông thường sẽ có một vùng nhớ cho Main Stack, và nhiều vùng nhớ cho Process Stack, mỗi vùng tương ứng cho mỗi luồng (thread/task) của chương trình ứng dụng. PSP sẽ có thể di chuyển qua mỗi vùng của Process Stack khi OS chuyển đổi giữa các luồng.

Giá trị MSP được nạp trong quá trình khởi động của thiết bị từ word (32-bits) đầu tiên trong trong bộ nhớ chương trình (mặc định tại địa chỉ 0x0000 0000 trong Cortex-M0/M0+/M3/M4, hoặc có thể được cấu hình trong các bộ xử lý mới hơn như Cortex-M7). Thông thường, cấu hình địa chỉ bắt đầu và kích cỡ của Main Stack được định nghĩa trong đoạn mã khởi động (startup code), và các công cụ phát triển cho phép các bạn thay đổi các thông số cài đặt sẵn này. Đoạn mã khởi động thường được xây dựng bởi Nhà cung cấp vi điều khiển (microcontroller vendors) hoặc Bên cung cấp các gói phần mềm phát triển (development suites). Hầu hết các đoạn mã khởi động đều dựa trên khung phần mềm (software frame) CMSIS-CORE của ARM.

Cấu trúc bộ nhớ SRAM thường được bố trí theo một trong 2 cách như trong hình sau:

Hình 2. Hai cách phân bổ Stack (nguồn: community.arm.com)

Trong một số ứng dụng cần sử dụng bộ nhớ ngoài cho Stack thì sau khi SP được nạp trong quá trình khởi động, các mã code phục vụ cho quá trình khởi tạo các ngoại vi giao tiếp với bộ nhớ ngoài thực hiện xong, giá trị SP sẽ được ghi địa chỉ mới của vùng nhớ ngoài. Tiếp theo đó, trước khi hàm main được gọi thì SP có thể được người dùng thay đổi bằng cách ghi một giá trị mới. Kỹ thuật này thường được dùng khi dự án chúng ta cần sử dụng bộ nhớ ngoài cho Stack.

Trong phần 1 của chuỗi bài viết này, mình đã giới thiệu đến các bạn tầm quan trọng của việc hiểu về Stack và việc xác định được kích thước Stack để hỗ trợ cho dự án của các bạn; giúp các bạn hiểu được cấu trúc của bộ nhớ Stack, các loại Stack. Bài viết tiếp theo, mình sẽ tiếp tục giới thiệu đến các bạn các công cụ hỗ trợ, các kịch bản để xác định, tính toán kích thước Stack cho chương trình ứng dụng. Cùng đón chờ nhé!

Chúc các bạn thành công!

N.T.Nhien

Tìm hiểu thêm:
Fanpage Cộng đồng Kỹ thuật TAPIT: TAPIT – Learning, Research and Sharing Community