🌱 Memory Barrier Instructions
Ở bài viết trước mình đã cùng mọi người tìm hiểu về khái niệm Memory Types và Memory Attributes. Khái niệm này đề cập đến việc sắp xếp thứ tự thực hiện các câu lệnh trong bộ nhớ. Như ở bài viết đó chúng ta cũng bắt gặp các vùng nhớ với types là Normal Memory, với việc các lệnh có thể thực hiện gần như song song, không cần thực hiện tuần tự để tối ưu hiệu suất chương trình.
Với cơ chế này thì hoàn toàn có thể gây ra một số trường hợp processor thực hiện sai lệnh/dữ liệu. Ví dụ, việc write vào một vùng nhớ chưa thực hiện xong nhưng Access Right của vùng nhớ đó lại bị thay đổi, việc này có thể dẫn đến chương trình bị Hardfault/MemManage Fault. Vì vậy, khi sử dụng khái niệm Memory Types, đặc biệt là Normal Memory, chúng ta sẽ cần một số lệnh hỗ trợ việc ràng buộc thứ tự thực hiện các lệnh, và ARM hỗ trợ chúng ta một số lệnh như vậy, gọi là Memory Barrier.
👉 Memory Barrier là gì?
Memory Barrier là các lệnh yêu cầu processor áp dụng một số ràng buộc về mặt thứ tự thực hiện truy cập bộ nhớ xảy ra trước và sau của lệnh Memory Barrier này. Trong một vài kiến thức, khái niệm này được biết đến như Memory Fences.
Như bài viết trước đã giới thiệu, một số phương thức Optimize của vi xử lý như Cache, Buffer, re-oder memory có thể làm thứ tự thực hiện các câu lệnh thực tế khác với thự tự trong chương trình. Thông thường thì sự re-order này là không nhìn thấy được, và developer thường không cần quan tâm đến khái niệm Memory Barrier.
Tuy nhiên, trong một số trường hợp, chúng ta cần quan tâm đến vấn đề re-order này khi có một số vấn đề xảy ra. Ví dụ, device driver hoặc khi chương trình cần đồng bộ hóa dữ liệu giữa các luồng.
Kiến trúc ARM cung cấp một số lệnh memory barriers, cho phép chương trình buộc core phải đợi việc truy cập bộ nhớ phải hoàn thành. Các lệnh này cho phép ở cả ARM và Thumb Code, cho cả User Mode và Priviledge Mode.
👉 Memory Barrier là thuật ngữ chung áp dụng cho một lệnh hoặc chuỗi lệnh, buộc processor phải thực hiện các sự kiện đồng bộ hóa đối với các lệnh load/store (Các lệnh này thường xuất hiện khi thao tác với bộ nhớ - LDM, STM). Kiến trúc ARM định nghĩa một vài lệnh memory barrier với các chức năng:
- Thứ tự thực hiện các lệnh load/store
- Việc hoàn tất các lệnh load/store
- Đồng bộ context đối với các lệnh read/write memory
Với kiến trúc ARMv7, memory barrier đươc cung cấp dưới dạng các lệnh ARM hoặc Thumb, còn với kiến trúc ARMv6, memory barrier được thực hiện bằng cách ghi vào thanh ghi CP15.
👍 Một số trường hợp cần sử dụng Memory Barrier
- Giữa hai lần truy cập bộ nhớ mà có một thành phần khác cũng có thể truy cập vào vùng nhớ này (Ví dụ core hoặc thread khác), thường áp dụng với Shared Memory trong các hệ thống multicore/multithread.
- Giữa một truy cập bộ nhớ và một interrupt/event.
- Giữa một truy cập bộ nhớ và truy cập vào các thanh ghi System control.
👉 Ba lệnh Memory Barrier
💬 Data Synchronization Barrier (DSB)
Lệnh này buộc processor phải đợi toàn bộ các việc truy cập dữ liệu đang chờ phải được hoàn tất, trước khi bất kỳ lệnh nào khác có thể được thực hiện.
Đối với Vi xử lý ARM Cortex M, nó có thể được sử dụng để:
- Đảm bảo bộ nhớ được update trước các lệnh tiếp theo như SVC - SuperVisor Call, WFI - Wait For Interrupt, WFE - Wait For Event.
- Tránh hiện tượng Deadlock.
Đối với kiến trúc ARMv7, không nhất thiết việc ghi giá trị vào [Addr] phải được thực hiện xong (Có nhiều cơ chế giúp truy cập nhanh như Cache, Write Buffer), vì vậy khi một core khác cố gắng check cờ thì giá trị của cờ vẫn luôn không đổi, việc này sẽ dẫn đến hiện tượng deadlock (các core ngồi yên chờ nhau).
👉 Chính vì vậy, chúng ở sau lệnh Store, chúng ta cần sử dụng lệnh DSB (như hình trên).
💬 Data Memory Barrier (DMB)
Lệnh này sử dụng giữa hai lệnh truy cập bộ nhớ để đảm bảo lệnh đứng trước của DMB sẽ hoàn thành trước lệnh đứng sau của DMB, đảm bảo 2 lần truy cập bộ nhớ sẽ không bị ảnh hưởng đến nhau. Lệnh này chỉ ảnh hưởng đến access data, không ảnh hưởng đến thứ tự thực hiện các lệnh của processor.
Trường hợp sử dụng, các hệ thống multi-master:
- Khi có sự tham gia truy cập bộ nhớ của DMA, lệnh DMB cần sử dụng giữa các lệnh truy cập bộ nhớ của CPU và DMA.
- Semaphore và mailbox trong các hệ thống multicore/multithread.
Processor 1:STR R5, [R1] ; // set new dataLDR R6, [R2];Processor 2:STR R6, [R2] ;LDR R5, [R1] ; //read new data
Processor 1:STR R5, [R1] ; // set new dataSTR R0, [R2] ; // send flag indicating data readyProcessor 2:WAIT([R2]==1) ; // wait on flagLDR R5, [R1] ; // read new data
Trong trường hợp này, Data Memory Barrier có thể được sử dụng để đảm bảo quá trình set và check cờ hoạt động đúng.
Processor 1:
STR R5, [R1] ; // set new data
DMB [ST] ; // ensure all observers observe data before the flag
STR R0, [R2] ; //send flag indicating data ready
Processor 2:
WAIT([R2]==1) ; // wait on flag
DMB ; // ensure that the load of data is after setting flag
LDR R5, [R1]
Ví dụ 2: lệnh DMB đảm bảo việc truy cập (ghi) và bộ nhớ sẽ có hoàn thành trước khi lệnh tiếp theo hoạt động.
💬 Instruction Synchronization Barrier (ISB)
Lệnh này flush pipeline và prefetch buffer trong processor, các lệnh sẽ được fetch về từ cache hoặc memory, sau khi lệnh này hoàn tất. Điều này đảm bảo rằng sự ảnh hưởng của context altering operation sẽ được thực hiện trước lệnh ISB có thể được nhìn thấy bởi bất kỳ lệnh nào được fetch sau lệnh ISB. Lệnh này nên được thực hiện sau khi cập nhật thanh ghi CONTROL, hoặc trong các processor sử dụng cache.