Đã khóa (Nguyên tử) Đăng ký đọc / ghi


8

Tôi đang mã hóa thứ gì đó bằng cách sử dụng kiểm soát trực tiếp GPIO, có một số tài nguyên tốt xung quanh việc này, chẳng hạn như http://elinux.org/RPi_Low-level_perodesals#GPIO_hardware_hacking ; quá trình này bao gồm mở ("/ dev / mem") và sau đó thao tác mmap ánh xạ hiệu quả địa chỉ vật lý mong muốn vào không gian địa chỉ ảo của bạn. Sau đó, bạn đọc phần 6 của http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Perodesals.pdf để tìm hiểu về cách I / O được kiểm soát.

Để thay đổi chức năng của pin (đầu vào hoặc đầu ra hoặc các chức năng đặc biệt khác nhau), bạn sửa đổi các trường 3 bit này trong các thanh ghi I / O GPFSELx (000 = đầu vào, 001 = ví dụ kẻ thù đầu ra). Các hoạt động sửa đổi này được biên dịch thành các hoạt động với tải và lưu trữ thông thường (ví dụ: thay đổi GPIO0 thành đầu vào: * (regptr) & = ~ 7; sẽ biên dịch thành một cái gì đó như

    ldr     r2, [r3, #0]     ; r = *ptr (load r2 from I/O register)
    bic     r2, r2, #7       ; r2 &= ~7
    str     r2, [r3, #0]     ; *ptr = r2 (store r2 to I/O register)

Vấn đề là ở đây: nếu xảy ra gián đoạn giữa tải và lưu trữ, và một quá trình khác hoặc ISR sửa đổi cùng một thanh ghi I / O, thì thao tác lưu trữ (dựa trên đọc cũ vào r2) sẽ hoàn nguyên các tác động của hoạt động khác đó. Vì vậy, việc thay đổi các thanh ghi I / O này thực sự cần phải thực hiện với thao tác đọc / sửa đổi / ghi nguyên tử (bị khóa). Các ví dụ tôi đã thấy không sử dụng thao tác bị khóa.

Vì các thanh ghi I / O này thường chỉ thay đổi khi thiết lập một cái gì đó, nên không có khả năng xảy ra sự cố, nhưng 'không bao giờ' luôn tốt hơn 'không thể xảy ra'. Ngoài ra, nếu bạn có một ứng dụng mà bạn đang bash bit để mô phỏng đầu ra của trình thu thập mở, thì (theo như tôi có thể nói) điều này liên quan đến việc lập trình đầu ra thành 0 và sau đó chuyển đổi giữa đầu ra (ở mức thấp) hoặc đầu vào ( cho tắt / cao). Vì vậy, trong trường hợp đó sẽ có các mod thường xuyên cho các thanh ghi I / O này và các sửa đổi không an toàn sẽ có nhiều khả năng gây ra sự cố.

Vì vậy, có thể có một hoạt động 'so sánh và thiết lập' ARM hoặc hoạt động tương tự có thể được sử dụng ở đây để làm điều này, bất cứ ai cũng có thể chỉ cho tôi điều đó và làm thế nào để điều đó xảy ra từ mã C?

[Lưu ý, không có gì đặc biệt là cần thiết khi bạn đã lập trình I / O làm đầu ra và chỉ thay đổi từ 0 thành 1 hoặc ngược lại; vì có một thanh ghi I / O mà bạn ghi vào, để đặt các bit được chọn thành 1 và một bit khác để xóa các bit đã chọn thành 0. Không cần đọc / ghi cho thao tác này, do đó không có nguy cơ bị gián đoạn].


Có thể tôi đã không hiểu chính xác điều này nhưng vì bạn mở /dev/memnên dường như mã của bạn là mã không gian người dùng. Tôi không nghĩ rằng trong bất kỳ hệ điều hành hiện đại nào, người ta phải cẩn thận về việc thay đổi giá trị đăng ký trong mã không gian người dùng. Tôi tin rằng đây sẽ không phải là vấn đề ngay cả trong mã không gian kernel vì Linux khôi phục tất cả các thanh ghi khi trình xử lý ngắt hoàn thành công việc của nó.
Krzysztof Adamski

1
Tôi hiểu rằng tải / lưu trữ đi đến một thanh ghi vật lý thông qua ánh xạ VM được thiết lập bởi mmap (thanh ghi I / O, không phải là thanh ghi CPU). Trong trường hợp này, không có lý do gì mà một quá trình khác, hoặc trình điều khiển thiết bị không thể thực hiện cùng một việc đồng thời và sửa đổi cùng một thanh ghi. (Tôi cho rằng nó đang sửa đổi một tập hợp các bit khác nhau trong reg, hoặc rõ ràng chúng ta có vấn đề lớn hơn). Không có lưu / khôi phục các thanh ghi IO vì có các thanh ghi bộ xử lý.
greggo

Tôi đã chỉnh sửa một chút để làm rõ 'Đăng ký I / O' trái ngược với r2, v.v.
greggo

Tôi có thể thấy quan điểm của bạn bây giờ. Tuy nhiên, đó là vấn đề ưu tiên hơn là xử lý ngắt. Sử dụng các hoạt động nguyên tử sẽ giúp ít nhất là khi hai quá trình đang cố gắng thiết lập các bit khác nhau cùng một lúc.
Krzysztof Adamski

ldrex / strex không hoạt động trên bộ nhớ không được lưu trữ. Các màn hình độc quyền dựa trên bộ nhớ cache. Trên thực tế, chẳng hạn, có thể khóa cứng CPU nếu bạn đã thử điều đó trên hệ thống SMP của Cortex-A9 chẳng hạn.
thinkfat

Câu trả lời:


3

Tôi đã xem xét điều này, ARM có các hướng dẫn 'ldrex và' strex ', strex sẽ trả về kết quả không thành công nếu tính độc quyền bị mất (hoặc có thể bị mất) kể từ ldrex, bao gồm một bộ chuyển đổi ngữ cảnh (hoặc bộ xử lý khác sửa đổi tương tự đăng ký trong môi trường đa bộ xử lý). Vì vậy, nó có thể được thực hiện bằng cách sử dụng đó; nếu strex thất bại, bạn lặp lại và thực hiện lại thao tác (với một ldrex mới).

tham khảo: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s02s01.html

Các thói quen dưới đây dường như hoạt động trên Raspberry Pi (trong đó chúng tạo ra trình biên dịch mà tôi mong đợi; và hiệu ứng trên các bit khi tôi sử dụng chúng như mong đợi. Tôi đã xác minh rằng chúng bảo vệ chống lại sự cố chuyển đổi ngữ cảnh) . Lưu ý rằng đây là những dòng nội tuyến thay vì các hàm, vì vậy chúng nên được đặt trong một tệp tiêu đề.

[ EDIT : Điều này không hoạt động cho mục đích được thảo luận, có vẻ như nó không được phép. Nếu tôi sử dụng các thường trình này trong đó * addr là một biến thông thường, nó hoạt động tốt. Khi tôi sử dụng nó trong đó * addr được trỏ đến một thanh ghi GPIO được ánh xạ, quá trình sẽ xảy ra lỗi bus. (Khi tôi thay đổi ldrex / strex thành ldr / str và vô hiệu hóa vòng lặp do, nó sẽ hoạt động). Vì vậy, có vẻ như màn hình độc quyền ARM không thể hoặc không được thiết lập để hoạt động trên các chế độ I / O được ánh xạ bộ nhớ và câu hỏi vẫn mở.]

//
// Routines to atomically modify 32-bit registers using ldrex and strex.
// 
//
//
//  locked_bic_to_reg( volatile unsigned * addr, unsigned val )
//                 *addr &= ~val
//  locked_or_to_reg( volatile unsigned * addr, unsigned val )
//                 *addr |= val
//   locked_insert_to_reg( volatile unsigned * addr, unsigned val, int width, int pos )
//           insert 'width' lsbs of 'val into *addr, with the lsb at bit 'pos'.
//           Caller must ensure 1 <= width <= 32 and 0 <= pos < 32-width
//
//
static inline void
locked_bic_to_reg( volatile unsigned * addr, unsigned val )
{
    int fail;
    do{
        asm volatile ("ldrex r0,[%1]\n"
           "   bic r0,r0,%2\n"
           "   strex %0,r0,[%1]": "=r"(fail) : "r"(addr), "r"(val): "r0" );
    }while(fail!=0);
}
static inline void
locked_or_to_reg( volatile unsigned * addr, unsigned val)
{
    int fail;
    do{
        asm volatile ("ldrex r0,[%1]\n"
           "   orr r0,r0,%2\n"
           "   strex %0,r0,[%1]": "=r"(fail) : "r"(addr), "r"(val): "r0" );
    }while(fail!=0);
}

static inline void
locked_insert_to_reg( volatile unsigned * addr, unsigned val, int width, int pos )
{
    int fail;
    if(width >=32 ) {
        *addr = val;    // assume wid = 32, pos = 0;
    }else{
        unsigned m=(1<<width)-1;
        val = (val&m) << pos;   // mask and position
        m <<= pos;

        do{
            asm volatile ("ldrex r0,[%1]\n"
               "   bic r0,r0,%2\n"   /// bic with mask
               "   orr r0,r0,%3\n"    // or result
               "   strex %0,r0,[%1]": "=r"(fail) : "r"(addr), "r"(m), "r"(val): "r0" );
        }while(fail!=0);
    }
}

Dường như với tôi đây là loại điều nên có trong các tệp .h dành riêng cho bộ xử lý, nhưng không có tệp .h nào dưới / usr / bao gồm hoặc / usr / lib / gcc / arm-linux-gnuispihf / chứa chuỗi 'ldrex '. Có thể là một nội dung , hoặc một trong các tiêu đề hạt nhân?
greggo

1
ldrex / strex được dùng để chia sẻ tài nguyên đa lõi (ram chia sẻ). swp thường được sử dụng để khóa lõi đơn của tài nguyên lõi đơn. ldrex / strex, tình cờ hoạt động như một giải pháp cốt lõi duy nhất (GỬI TRÊN VENDOR CHIP) nên nó bị sử dụng sai. Nó dường như hoạt động trên bộ xử lý raspberry pi mặc dù.
old_timer
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.