QSPI FLASH External loader và đọc kiểm tra bộ nhớ ngoài

Bài viết này cung cấp các thông tin về External Loader và quá trình thực hiện – kiểm tra giao tiếp với bộ nhớ Flash ngoài qua thao tác đọc bộ nhớ. Để hiểu nội dung này hiệu quả hơn, các bạn nên đọc qua các nội dung đã được chia sẻ trước tại website hoặc fanpage TAPIT: IC nhớ FLASH,SDRAM và mô hình bộ nhớ của vi điều khiển khi sử dụng bộ nhớ ngoài; Sử dụng Linker Script trong khai báo, cấu hình sử dụng bộ nhớ ngoài -VĐK STM32; Mở rộng bộ nhớ STM32 với QSPI Flash MT25QL128: Ghép nối phần cứng, cấu hình ngoại vi; Các chế độ hoạt động của QSPI Flash và thư viện quadspi.

File external loader cho QSPI flash “<name>.stldr”
Dữ liệu được đưa vào chip QSPI Flash như thế nào? Việc sử dụng hàm ghi được hỗ trợ bởi HAL API để sao chép dữ liệu từ Flash nội sang QSPI Flash không được sử dụng nhiều trong các ứng dụng thực tế. Thông thường, mục đích sử dụng QSPI Flash là để lưu một lượng dữ liệu lớn mà Flash nội không chứa được. Vì vậy, nếu sử dụng HAL API để ghi ra bộ nhớ Flash ngoài nghĩa là bộ nhớ nội cũng phải đủ không gian chứa chúng. Một trường hợp nữa là sử dụng  bộ nhớ QSPI Flash để lưu trữ các dữ liệu từ RAM trong quá trình hoạt động, trường hợp này cũng ít được sử dụng vì RAM thường chứa các dữ liệu được sử dụng để tính toán với tốc độ cao hơn. Trường hợp phổ biến hơn là đưa trước các dữ liệu hằng vào bộ nhớ Flash ngay từ ban đầu. Có 2 cách để thực hiện việc này:

  • Sử dụng các bộ lập trình Flash chuyên dụng ví dụ như FlashPro-M (X2S) [1]
  • Dùng giải pháp của ST cho các kit phát triển của ST: external QSPI loader!

Nội dung bài viết này sẽ giới thiệu và hướng dẫn sử dụng giải pháp của ST “External QSPI loader” và các thử nghiệm trên kit phát triển của STMicroelectronics!  Cùng tìm hiểu External QSPI loader là gì và nó hoạt động như thế nào?

  • External QSPI loader là một file binary được compile từ một chương trình có các hàm API cơ bản cho QSPI với hàm main không thực hiện tác vụ nào! Nó là một chương trình với thư viện được thêm vào chứa các hàm thao tác với QSPI Flash cơ bản như đọc, ghi, xóa, xóa sector,…; Hàm main được để trống vì chương trình sẽ không thực hiện tác vụ nào. Sau khi compile, các thư viện được thêm vào vào sẽ được tách ra một file object riêng. File object chứa các hàm cơ bản cùng với cấu hình chân cho QSPI này được biến đổi thành một dạng file có đuôi “.stldr” (ST loader). Tool STM32CubeProg [2] được sử dụng để load các file này vào vi điều khiển. Bây giờ, file dữ liệu của người dùng ở dạng binary (file này chứa các giá trị biến, mảng, hằng,…) có thể được nạp vào bộ nhớ ngoài thông qua công cụ STM32CubeProg.
  • Khi thao tác nạp file dữ liệu người dùng vào bộ nhớ thì phần mềm STM32CubeProg sẽ thông qua ST-Link và khối Debug bên trong vi điều khiển để điều khiển, cấu hình ngoại vi QSPI dựa trên các hàm cơ bản chứa trong file “.stldr” để nạp file binary chứa dữ liệu vào QSPI Flash.
  • Như vậy, có thể thấy rằng khối debug lúc này làm việc như một master bên trong vi điều khiển. Sơ đồ tổng quát quá trình hoạt động với external loader như sau:

Hình 1: Quá trình khối Debug điều khiển ngoại vi QSPI để nạp file binary ra QSPI Flash

Cách sử dụng file “.stldr” sẽ được trình bày cụ thể hơn trong phần triển khai các trọng số của mô hình máy học lên bộ nhớ QSPI Flash còn ở một bài viết khác, còn bài viết này sẽ tập trung vào thực hiện các chức năng đọc ghi đối với QSPI Flash.

Kiểm tra việc “đọc” dữ liệu tại flash ngoài

1. Dùng HAL API (Indirect mode)
Trong các bài viết liên quan nên đọc, đã có các hướng dẫn cấu hình ngoại vi, hiệu chỉnh thư viện. Phần này sẽ thực hiện kiểm tra các giá trị đọc được bằng cách viết một chương trình đơn giản như sau:

Phần define và biến:

  • WriteBuffer: Vùng nhớ chứa đoạn dữ liệu mà người dùng cần ghi vào QSPI Flash. Cụ thể ở đây là chuỗi có nội dung “Hello World! My name is Phuong!”
  • ReadBuffer: Mảng để chứa chuỗi các ký tự đọc được từ QSPI Flash.
  • Chương trình đơn giản này sẽ thực hiện sẽ ghi chuỗi trong WriteBuffer vào QSPI Flash rồi sau đó lại đọc ra ReadBuffer để kiểm chứng xem những nội dung ghi vào có đúng không.

Thêm vào  “USER CODE BEGIN/END 2” đoạn code sau:

Kết quả sau khi thực hiện chương trình:

Hình 2: Nội dung của mảng ReadBuffer

Như hình trên, mảng ReadBuffer chứa đúng nội dung được ghi trong WriteBuffer! Các phần tử thứ 31 trở đi có giá trị 255 là vì công nghệ NAND Flash, khi thao tác xóa được thực hiện thì đầu ra của các tế bào NAND là 1. Chế độ sử dụng HAL API được kiểm tra thành công. 

2. Memory mapped (không dùng external loader)
Phần này chỉ kiểm tra việc có thể đọc được dữ liệu từ flash lên mà không cần gọi hàm “uint8_t CSP_QSPI_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)”. Để làm được điều này, chúng ta sẽ ghi một hằng ký tự tương tự như ví dụ trên, nhưng khi đọc, chúng ta sẽ dùng hàm memcpy. Một lưu ý, chúng ta có thể đọc dữ liệu, sử dụng dữ liệu từ flash bằng memcpy, strncpy, strcpy, strcmp, strstr… nhưng không thể ghi vào vị trí đó bằng các hàm như memset, memcpy, memmove…Điều này được ghi rõ trong application note AN476[3] như trong hình 3. Chính vì thế, dữ liệu phải được ghi vào trước khi memory mapped mode được kích hoạt.

Hình 3: Lưu ý về các hoạt động ghi, xóa

Đầu tiên, thực hiện chỉnh sửa file linker, thêm vào các đoạn mã như sau:

Viết thêm đoạn code sau vào phần “USER CODE BEGIN/END PD” trong file main.

Trong đoạn code hàm main, ta gõ vào khu vực “USER CODE BEGIN/END 2” như sau:

Sau khi chạy và debug, dữ liệu trong Live Expression và Memory view có được như ở hình 4 và hình 5:

Hình 4: Kết quả mảng ReadBuffer từ hàm memcpy trong Live Expression

Hình 5: Kết quả từ memory view từ địa chỉ 0x90000000 của QSPI Flash

Như vậy, có thể thực hiện đọc dữ liệu của QSPI Flash trong Memory View và có thể sử dụng được hàm memcpy. Điều này cho thấy vi điều khiển và khối debug có thể đọc được từ địa chỉ của QSPI Flash một cách tự động mà không cần gọi hàm “CSP_QSPI_Read((uint8_t* pData, uint32_t ReadAddr, uint32_t Size)”, chế độ memory mapped được kiểm tra thành công. 

3. Memory mapped (cùng với external loader):

Có thể thấy, ở 2 trường hợp sử dụng trên thì việc ghi một đoạn dữ liệu vào QSPI Flash vẫn phải dựa vào hàm “uint8_t CSP_QSPI_WriteMemory(uint8_t* buffer, uint32_t address, uint32_t buffer_size)” và dữ liệu vẫn phải nằm đâu đó trong Flash nội trước khi kích hoạt memory mapped mode.

Như vậy, trong tình huống sử dụng cần dữ liệu phải được nạp sẵn vào trong QSPI Flash và vi xử lý chỉ việc đọc và sử dụng. Đây là tình huống sử dụng phổ biến hơn, việc lập trình sẽ đơn giản hơn và bộ nhớ Flash nội cũng sẽ được giải phóng bớt một phần không gian.

Để thực hiện việc này, người sử dụng vẫn cần phải hiệu chỉnh linker script như phần 2, tuy nhiên tại phần “USER CODE BEGIN/END PD” cần bổ sung thêm một macro như sau:

Tại vị trí “USER CODE BEGIN/END PV”, bổ sung thêm tiền tố QSPI cho con trỏ WriteBuffer như dưới.
Lưu ý, WriteBuffer đã được hiệu chỉnh so với ví dụ trên, từ pointer tới hằng chuỗi thành mảng ký tự. Lý do của việc thay đổi này là nếu để con trỏ tới hằng chuỗi thì external loader sẽ chỉ ghi địa chỉ của con trỏ lên bộ nhớ ngoài thay vì cả chuỗi ký tự. Để có thể ghi cả chuỗi ký tự từ con trỏ hằng yêu cầu can thiệp nhiều hơn vào linker script và việc thay đổi này giúp đơn giản hóa các thao tác trong quá trình kiểm tra.

Đoạn mã nằm trong “USER CODE BEGIN/END 2”, hiệu chỉnh như sau:

Đến đây, người dùng cần phải add external loader file cho chip MT25QL128 trên board STM32F746 DISCO theo các bước sau:

  • Vào phần Debug Configuration:

Hình 6: Vào mục “Debug Configuration”

  • Vào tab “Debugger”, tại phần “External loaders”, nhấn “Add”:

Hình 7: Nhấn “Add” để thêm external loaders

  • Tại cửa sổ “External Loader” hiện lên, chọn file external loader tương ứng. Trong hình 8 dưới, người đọc có thể thấy tác giả chọn bản “N25Q128A-STM32F746G-DISCO.stldr” khác với tên chip là “MT25QL128ABA”. Điều này là vì, 2 dòng chip này đều do micron sản xuất và có tập lệnh hầu như giống nhau nên chúng ta có thể dùng cho nhau trong các tác vụ đọc, ghi thông thường.

Hình 8: Chọn external loader tương ứng cho board STM32F746 DISCO bản C01 (schematic version)

  • Đến đây, file external loader đã được thêm vào quá trình debug thành công

Hình 9: Thêm file external loader thành công

  • Nhấn Apply, sau đó nhấn Debug.

Hình 10: Xác nhận để debug

  • Bây giờ ta debug và xem thử mảng ký tự đã được ghi vào QSPI Flash chưa. Kết quả được thể hiện ở hình 11 và hình 12 dưới:

Hình 11: Kết quả trong Live Expression

Hình 12: Kết quả từ memory view từ địa chỉ 0x90000000 của QSPI Flash

Như vậy, bài viết đã cung cấp các thông tin giúp hiểu được công dụng và cách dùng file “external loader” trong việc thao tác với QSPI Flash. Đi kèm với đó là công cụ STM32CubeProg của hãng STMicroelectronics cho phép các nhà phát triển có thể đọc, ghi, xóa,… với thanh ghi, flash nội, qspi flash trên các vi điều khiển STM32. Để tìm hiểu thêm về cách sử dụng phần mềm các bạn đọc các tài liệu [4][5][6][7].

Các tài liệu tham khảo:
[1]https://www.elprotronic.com/products/flashpro-m-x2s 
[2]https://www.st.com/en/development-tools/stm32cubeprog.html
[3]https://www.st.com/resource/en/application_note/dm00227538-quadspi-interface-on-stm32-microcontrollers-and-microprocessors-stmicroelectronics.pdf
[4]http://www.emcu.eu/stm32-cube-programmer/
[5]https://www.st.com/en/development-tools/stm32cubeprog.html#documentation
[6]https://www.st.com/resource/en/data_brief/stm32cubeprog.pdf
[7]https://www.youtube.com/watch?v=xIfh_uYy-OU&t=854s  12’30s

Nhóm tác giả
Ng.Q.Phương, Ng.H.Phúc, Ng.H.N.Thương