Các phần quan trọng trên Cortex-M3


10

Tôi đang tự hỏi một chút về việc triển khai các phần mã quan trọng trên Cortex-M3, nơi các ngoại lệ không được phép do các hạn chế về thời gian hoặc các vấn đề tương tranh.

Trong trường hợp của tôi, tôi đang chạy LPC1758 và tôi có bộ thu phát TI CC2500 trên tàu. CC2500 có các chân có thể được sử dụng làm đường ngắt cho dữ liệu trong bộ đệm RX và không gian trống trong bộ đệm TX.

Ví dụ, tôi muốn có bộ đệm TX trong SRAM của MCU của tôi và khi có không gian trống trong bộ đệm TX của bộ thu phát, tôi muốn ghi dữ liệu này vào đó. Nhưng thói quen đặt dữ liệu vào bộ đệm SRAM rõ ràng không thể bị gián đoạn bởi ngắt giữa không gian trống trong TX. Vì vậy, những gì tôi muốn làm là tạm thời vô hiệu hóa các ngắt trong khi thực hiện thủ tục điền vào bộ đệm này nhưng có bất kỳ gián đoạn nào xảy ra trong quá trình này thực thi sau khi nó kết thúc.

Làm thế nào điều này được thực hiện tốt nhất trên Cortex-M3?

Câu trả lời:


11

Cortex M3 hỗ trợ một cặp hoạt động hữu ích (cũng phổ biến trong nhiều máy khác) được gọi là "Tải độc quyền" (LDREX) và "Độc quyền lưu trữ" (STREX). Về mặt khái niệm, hoạt động LDREX thực hiện tải, cũng đặt một số phần cứng đặc biệt để quan sát xem vị trí đã tải có thể được ghi bởi một thứ khác hay không. Việc thực hiện STREX đến địa chỉ được sử dụng bởi LDREX cuối cùng sẽ khiến địa chỉ đó chỉ được ghi nếu không có gì khác được viết trước . Lệnh STREX sẽ tải một thanh ghi bằng 0 nếu cửa hàng diễn ra hoặc 1 nếu nó bị hủy bỏ.

Lưu ý rằng STREX thường bi quan. Có nhiều tình huống trong đó nó có thể quyết định không thực hiện cửa hàng ngay cả khi vị trí được đề cập trên thực tế không bị chạm vào. Ví dụ: một ngắt giữa LDREX và STREX sẽ khiến STREX cho rằng vị trí đang theo dõi có thể đã bị tấn công. Vì lý do này, thông thường nên giảm thiểu số lượng mã giữa LDREX và STREX. Ví dụ, hãy xem xét một cái gì đó như sau:

nội tuyến void safe_increment (uint32_t * addr)
{
  uint32_t new_value;
  làm
  {
    new_value = __ldrex (addr) + 1;
  } while (__ strex (new_value, addr));
}

mà biên dịch thành một cái gì đó như:

; Giả sử R0 giữ địa chỉ trong câu hỏi; thùng rác r1
lp:
  ldrex r1, [r0]
  thêm r1, r1, # 1
  strex r1, r1, [r0]
  cmp r1, # 0; Kiểm tra nếu khác không
  bne lp
  .. mã tiếp tục

Phần lớn thời gian mã thực thi, sẽ không có gì xảy ra giữa LDREX và STREX để "làm phiền" chúng, vì vậy STREX sẽ thành công mà không cần phải quảng cáo thêm. Tuy nhiên, nếu xảy ra gián đoạn xảy ra ngay sau lệnh LDREX hoặc ADD, STREX sẽ không thực hiện lưu trữ mà thay vào đó, mã sẽ quay lại để đọc giá trị (có thể được cập nhật) của [r0] và tính giá trị gia tăng mới dựa trên điều đó

Sử dụng LDREX / STREX để hình thành các hoạt động như safe_increment giúp không chỉ quản lý các phần quan trọng mà còn trong nhiều trường hợp để tránh sự cần thiết của chúng.


Vì vậy, không có cách "chặn" ngắt để chúng có thể được phục vụ một lần nữa khi chúng được bỏ chặn? Tôi nhận ra rằng đây có lẽ là một giải pháp không phù hợp ngay cả khi có thể nhưng tôi chỉ muốn tìm hiểu thêm về xử lý ngắt ARM.
Emil Eriksson

3
Có thể vô hiệu hóa các ngắt và trên Cortex-M0 thường không có giải pháp thay thế thực tế nào cho việc đó. Tôi coi cách tiếp cận LDREX / STREX là sạch hơn so với việc vô hiệu hóa các ngắt, mặc dù trong nhiều trường hợp, điều đó không thực sự quan trọng . Lưu ý rằng cách tiếp cận ldrex / strex sẽ hoạt động nếu mã được di chuyển sang CPU đa lõi, trong khi cách tiếp cận vô hiệu hóa sẽ không bị gián đoạn. Ngoài ra, một số mã chạy của RTOS với các quyền bị giảm mà không được phép vô hiệu hóa các ngắt.
supercat

Tôi có thể cuối cùng sẽ đi với FreeRTOS vì vậy tôi sẽ không tự mình làm điều này nhưng dù sao tôi cũng muốn học. Tôi nên sử dụng phương pháp vô hiệu hóa các ngắt nào để chặn các ngắt như được mô tả trái ngược với loại bỏ bất kỳ ngắt nào xảy ra trong quy trình? Làm thế nào tôi có thể làm điều đó nếu tôi muốn loại bỏ chúng?
Emil Eriksson

Câu trả lời ở trên không thể tin cậy được vì mã liên quan bị thiếu dấu ngoặc đơn: while(STREXW(new_value, addr); Làm thế nào chúng ta có thể tin những gì bạn nói là chính xác nếu mã của bạn thậm chí không được biên dịch?

@Tim: Xin lỗi, cách đánh máy của tôi không hoàn hảo; Tôi không có mã thực tế mà tôi đã viết để so sánh, vì vậy tôi không nhớ hệ thống tôi đang sử dụng STREXW hay __STREXW, nhưng tham chiếu trình biên dịch liệt kê __strex là nội tại (không giống như STREXW bị giới hạn STREX 32 bit, sử dụng nội tại __strex tạo ra STREXB, STREXH hoặc STREX tùy thuộc vào kích thước con trỏ được cung cấp)
supercat

4

Có vẻ như bạn cần một số bộ đệm tròn hoặc FIFO trong phần mềm MCU của bạn. Bằng cách theo dõi hai chỉ mục hoặc con trỏ vào mảng để đọc và ghi, bạn có thể có cả tiền cảnh và nền truy cập vào cùng một bộ đệm mà không bị nhiễu.

Mã nền trước được tự do ghi vào bộ đệm tròn bất cứ lúc nào. Nó chèn dữ liệu vào con trỏ ghi, sau đó tăng con trỏ ghi.

Mã nền (xử lý ngắt) tiêu thụ dữ liệu từ con trỏ đọc và tăng con trỏ đọc.

Khi các con trỏ đọc và ghi bằng nhau, bộ đệm trống và quá trình nền sẽ không gửi dữ liệu. Khi bộ đệm đầy, quá trình tiền cảnh từ chối ghi thêm (hoặc có thể ghi đè dữ liệu cũ, tùy thuộc vào nhu cầu của bạn).

Sử dụng bộ đệm tròn để tách độc giả và người viết nên loại bỏ sự cần thiết phải vô hiệu hóa các ngắt.


Vâng, rõ ràng là tôi sẽ sử dụng bộ đệm tròn nhưng tăng và giảm không phải là hoạt động nguyên tử.
Emil Eriksson

3
@Emil: Họ không cần phải thế. Đối với bộ đệm tròn cổ điển, với hai con trỏ và một khe "không thể sử dụng", tất cả những gì cần thiết là ghi bộ nhớ là nguyên tử và được thực thi theo thứ tự. Người đọc sở hữu một con trỏ, nhà văn sở hữu con trỏ khác, và mặc dù cả hai đều có thể đọc một trong hai con trỏ, chỉ chủ sở hữu của con trỏ mới viết con trỏ của mình. Tại thời điểm đó, tất cả những gì bạn cần là viết theo thứ tự nguyên tử.
John R. Strohm

2

Tôi không thể nhớ chính xác vị trí nhưng trong các thư viện đến từ ARM (Không phải TI, ARM, nó phải nằm trong CMSIS hoặc đại loại như vậy, tôi sử dụng ST nhưng tôi nhớ đọc ở đâu đó rằng tệp này đến từ ARM vì vậy bạn cũng nên có nó ) có một tùy chọn vô hiệu hóa ngắt toàn cầu. Đây là một cuộc gọi chức năng. (Tôi không làm việc nhưng tôi sẽ tìm kiếm chức năng chính xác vào ngày mai). Tôi sẽ kết thúc nó với một cái tên đẹp trong hệ thống của bạn và vô hiệu hóa các ngắt, làm việc của bạn và kích hoạt lại. Đã nói rằng, tùy chọn tốt hơn sẽ là thực hiện một semaphore hoặc một cấu trúc hàng đợi thay vì vô hiệu hóa ngắt toàn cầu.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.