Site icon TAPIT

Arm Cortex M7 – Cache và Buffer trong truyền tải dữ liệu

Cùng tìm hiểu cache, buffer là gì? Vai trò của cache, buffer? Vấn đề không đồng nhất dữ liệu Cache Coherency và hướng giải quyết trong truyền tải dữ liệu với vi điều khiển STM32 được thiết kế sử dụng vi xử lý Arm Cortex M7? Các nội dung trong bài viết được xem xét dựa trên thực tế trong quá trình thực hiện đề tài của nhóm nghiên cứu. 

CACHE
1. Cache là gì?
Cache là một bộ nhớ có kích thước nhỏ được cấu thành từ các cell SRAM và nằm  giữa vi xử lý và bộ nhớ RAM như hình dưới:

Hình 1: Bộ nhớ Cache giữa CPU và bộ nhớ RAM

2. Vì sao lại có cache?

Việc truy cập RAM tốn rất nhiều thời gian do 2 nguyên nhân chính sau: Khoảng cách từ RAM đến CPU (đặc biệt là RAM ngoài); nếu sử dụng DRAM thì RAM phải tốn một khoảng thời gian nhất định để refresh các cell RAM.

Các chương trình nói chung đều mang tính “locality” nghĩa là gần. Chúng có thể là “spatial locality” nghĩa là gần nhau về mặt “không gian” như việc truy cập các phần tử liên tiếp nhau trên một mảng, (hay/ hoặc) việc thực thi các assembly instruction nối tiếp nhau trong bộ nhớ. Chúng cũng có thể là “temporal locality” nghĩa là gần nhau về mặt “thời gian” ví dụ như việc truy cập vào biến đếm i trong vòng lặp “for(int i;…)”.

Chính vì thế, để giảm thời gian truy cập vào các dữ liệu mang tính chất “locality”, các nhà thiết kế bộ nhớ thêm vào một bộ nhớ nhỏ có tốc độ rất cao (thường là tương đương tốc độ CPU) là “Cache” để lưu các dữ liệu đó.

BUFFER
1. Buffer là gì?
Buffer trong bài viết này có thể hiểu một cách “đơn giản” nhưng chưa đẩy đủ lắm là “Cache của Cache”. Còn vì sao cách hiểu này không đầy đủ thì Buffer được đề cập ở đây còn được gọi là “Victim Buffer”/“Write Buffer” và bộ nhớ Buffer này chỉ phục vụ hoạt động ghi ngược (write back) vào trong memory như được trình bày trong hình 2 dưới. Các bạn có thể tự tìm hiểu đầy đủ hơn về Buffer, một dịp nào đó TAPIT có thể sẽ chia sẻ thêm cùng các bạn nội dung này. 

Hình 2: Vị trí của Write Buffer

2. Vì sao cần Buffer?
Có hai quá trình mà mọi người nên hiểu rõ khi tìm hiểu về Buffer:

Hình 3: Mô tả quá trình Write through

Hình 4: Quá trình Write back cho một biến tồn tại trong Cache

Vậy, giả sử bộ nhớ Cache đầy, và CPU muốn đọc một biến Y và Y sẽ phải được load lên vào vị trí của X. Câu hỏi được đặt ra là: Hệ thống bộ nhớ của chúng ta cần làm gì để đảm bảo biến X trong Cache và RAM được đồng nhất? Nếu chúng ta cứ mặc kệ và xoá biến X trong Cache, thì khi CPU cần đọc biến X từ RAM lên lại, chúng ta sẽ có X = A thay vì A’!

Cách giải quyết: Khi thay đổi giá trị biến X = A ➡ A’ trong Cache, Cache sẽ đánh dấu là giá trị biến đó đã bị sửa đổi (bằng một bit gọi là bit Dirty – bẩn). Khi chúng ta cần thay thế Y vào vị trí đang chứa X = A’, Cache cần phải ghi ngược biến X = A’ (biến đã bị đánh dấu Dirty) vào Memory rồi mới được đọc biến Y vào vị trí đó! Toàn bộ quá trình được mô tả như hình dưới:

Hình 5: Quá trình thay thế trong thủ tục Write back

Dựa vào 2 định nghĩa trên, ta sẽ thấy rằng nếu read/write miss occupied (khi CPU đọc dữ liệu nhưng dữ liệu đó không có trong cache và cần phải thay thế một vị trí nhớ trong cache để load dữ liệu mới lên) xảy ra thì write back tốn nhiều thời gian nếu vị trí nhớ hiện tại bị đánh dấu là dirty. Tuy nhiên, trên thực tế write back cho thấy tốc độ trung bình cao hơn do ít phải truy cập ngược lại vào RAM trong phần lớn thời gian.

Vậy bây giờ làm sao để tiếp tục giảm số lần mà write back phải truy cập bộ nhớ? Hoặc chính xác hơn là làm sao để chúng ta có thể thay thế dữ liệu X = A’ trong trường hợp hình 5 nhưng không cần ngay lập tức ghi ngược X = A’ về RAM?

Lúc này, ta add thêm một bộ nhớ Buffer như hình 2! Nếu Cache cần Writeback về RAM thì nó sẽ ghi vào Buffer trước! Nếu Buffer đầy thì nó sẽ ghi một phần dữ liệu về RAM trong khi Cache vẫn có thể thay thế các vị trí nhớ với các biến dữ liệu khác. Nếu trong quá trình ghi/đọc, CPU truy cập vào một biến đang được ghi trong Buffer thì biến đó sẽ lại được load từ Buffer lên Cache và CPU một cách nhanh chóng.

Đến đây, ta đã hiểu được một cách đơn giản Cache và Buffer và tác dụng của chúng, giờ ta sẽ đi khám phá vấn đề của hệ thống nhớ có Cache trong hệ thống multi master (multi core, DMA)

Cache Coherency (Không đồng nhất dữ liệu)
Trong vi điều khiển STM32, thông thường sẽ có 1 đến 2 DMA master để hỗ trợ giảm tải cho CPU trong việc di chuyển dữ liệu từ ngoại vi/bộ nhớ đến bộ ngoại vi/bộ nhớ thông qua ma trận bus. Tuy nhiên, khi sử dụng DMA với Cache, ta có thể gặp phải trường hợp sau: 

Hình 6: Sự không đồng nhất dữ liệu giữa Cache và RAM do DMA gây ra

Ta thấy rằng, do có DMA cũng là một master có khả năng thay đổi dữ liệu trên RAM nên đã có sự không đồng nhất giữa RAM và Cache! Điều này có thể gây ra những lỗi rất nghiêm trọng và khó giải quyết vì nó không hiện hữu rõ ràng trong phần mềm mà do phần cứng bên dưới gây ra! Chính vì thế, ở phần tiếp theo ta sẽ thảo luận những cách giải quyết vấn đề này!

Hướng giải quyết Cache Coherency
1. Phần mềm:

2. Phần cứng:

Chi tiết về MPU sẽ được trình bày trong bài viết tiếp theo. 

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

Tìm hiểu thêm:
Tổng hợp hướng dẫn Internet of Things với NodeMCU ESP8266 và ESP32
Tổng hợp các bài hướng dẫn Lập trình vi điều khiển STM32
[HỌC ONLINE: LẬP TRÌNH VI ĐIỀU KHIỂN STM32, VI XỬ LÝ ARM CORTEX – M]
Fanpage Cộng đồng Kỹ thuật TAPIT: TAPIT – Learning, Research and Sharing Community