Vòng bỏ phiếu nhanh nhất - làm cách nào tôi có thể cắt 1 chu kỳ CPU?


8

Trong một ứng dụng thời gian thực - trên ARM Cortex M3 (tương tự STM32F101), tôi cần thăm dò một chút về thanh ghi ngoại vi bên trong cho đến khi nó bằng 0, càng chặt càng tốt. Tôi sử dụng dải bit để truy cập bit thích hợp. Mã C (đang hoạt động) là

while (*(volatile uint32_t*)kMyBit != 0);

Mã đó được sao chép trong RAM thực thi trên chip. Sau khi tối ưu hóa thủ công², vòng bỏ phiếu giảm xuống như sau, tôi đã hẹn giờ đến 6 chu kỳ:

0x00600200 681A      LDR      r2,[r3,#0x00]
0x00600202 2A00      CMP      r2,#0x00
0x00600204 D1FC      BNE      0x00600200

Làm thế nào sự không chắc chắn bỏ phiếu có thể được hạ xuống? Một vòng lặp 5 chu kỳ sẽ phù hợp với mục tiêu của tôi: lấy mẫu đó càng gần càng tốt với 15,5 chu kỳ sau khi nó về không.

Thông số kỹ thuật của tôi gọi đáng tin cậy để phát hiện xung thấp ít nhất 6,5 chu kỳ xung nhịp CPU; đáng tin cậy phân loại nó là ngắn nếu nó kéo dài dưới 12,5 chu kỳ; và đáng tin cậy phân loại nó miễn là nếu nó kéo dài hơn 18,5 chu kỳ. Các xung không có mối quan hệ pha xác định với xung nhịp CPU, đây là tham chiếu thời gian chính xác duy nhất của tôi. Điều đó đòi hỏi một vòng bỏ phiếu tối đa 5 đồng hồ. Trên thực tế, tôi đang mô phỏng mã chạy trên CPU 8 bit hàng chục năm tuổi có thể thăm dò với chu kỳ 5 xung nhịp và những gì đã trở thành thông số kỹ thuật.


Tôi đã cố gắng bù căn chỉnh mã bằng cách chèn NOP trước vòng lặp, trong nhiều biến thể tôi đã thử, nhưng không bao giờ quan sát thấy sự thay đổi.

Tôi đã cố gắng đảo ngược CMP và LDR, nhưng vẫn nhận được 6 chu kỳ:

0x00600200 681A      LDR      r2,[r3,#0x00]
; we loop here
0x00600202 2A00      CMP      r2,#0x00
0x00600204 681A      LDR      r2,[r3,#0x00]
0x00600206 D1FC      BNE      0x00600202

Đây là 8 chu kỳ

0x00600200 681A      LDR      r2,[r3,#0x00]
0x00600202 681A      LDR      r2,[r3,#0x00]
0x00600204 2A00      CMP      r2,#0x00
0x00600206 D1FB      BNE      0x00600200

Nhưng đây là 9 chu kỳ:

0x00600200 681A      LDR      r2,[r3,#0x00]
0x00600202 2A00      CMP      r2,#0x00
0x00600204 681A      LDR      r2,[r3,#0x00]
0x00600206 D1FB      BNE      0x00600200

Đo thời gian bit thấp bao nhiêu, trong bối cảnh không xảy ra gián đoạn.

² Mã do trình biên dịch ban đầu tạo ra đã sử dụng r12 làm thanh ghi đích và đã thêm 4 byte mã vào vòng lặp, tốn 1 chu kỳ.

Các số đã cho có được với trình giả lập STIce thời gian thực được cho là chính xác theo chu kỳ và tính năng kích hoạt trình giả lập của nó khi đọc tại địa chỉ của người đăng ký. Trước đây tôi đã thử bộ đếm "Trạng thái" với điểm dừng trong vòng lặp, nhưng kết quả phụ thuộc vào vị trí của điểm dừng. Một bước thậm chí còn tệ hơn: nó luôn đưa ra 4 chu kỳ cho LDR, khi đó ít nhất là xuống 3.


căn chỉnh có thể quan trọng, miền đồng hồ gpio có thể chi phối hiệu suất cũng như trạng thái chờ flash, nó sẽ là hơn 3 đồng hồ nhưng có thể là 6+ hoặc thậm chí nhiều hơn tùy thuộc. Tôi hy vọng dải bit không phải là một điểm nhấn hiệu suất để đọc, nhưng bạn có thể kiểm tra bit thay vì so sánh bằng 0 và xem. dòng dưới cùng bạn phải thử nó ...
old_timer

1
Chế độ Thumb không có hướng dẫn cbnz để so sánh và chi nhánh trên một thanh ghi khác bằng không? Bạn đã biên dịch với gcc -Os -mcpu=cortex-m3?
Peter Cordes

1
@Peter Cordes: Tôi không sử dụng gcc, nhưng ArmCC 5 (trình biên dịch thế hệ trước của ARM trước khi đi LLVM). Tối ưu hóa là thời gian và tối đa, và các tùy chọn cho CPU được cho là do IDE tự động thiết lập, nhưng tôi sẽ kiểm tra xem. Vâng, có CBZ / CBNZ, nhưng khi tôi đọc tài liệu, nó không thể phân nhánh ngược.
fgrieu

1
Ok, vì vậy bạn (hoặc trình biên dịch) có thể hủy đăng ký với ldr/ cbz reg, end_of_loopcho những cái bên trong, và vẫn là cmp/ bnzở phía dưới. Nhưng điều đó sẽ cung cấp cho bạn một khoảng thời gian bỏ phiếu không đồng nhất, ví dụ 1 trong mỗi 8 cuộc thăm dò, trong trường hợp có vấn đề.
Peter Cordes

2
Bạn có chắc là bạn đã không hiểu sai thông số kỹ thuật? Có thể thông số kỹ thuật nếu đề cập đến "chu kỳ cụ thể của thiết bị không phải là chu kỳ CPU" (ví dụ: bộ hẹn giờ hoặc UART với nguồn đồng hồ riêng có chu kỳ chậm hơn nhiều) và có thể "ngắn như 13 chu kỳ cụ thể của thiết bị" có thể "ngắn" 13000 chu kỳ cụ thể của CPU ".
Brendan

Câu trả lời:


8

Nếu tôi hiểu chính xác câu hỏi, đó không nhất thiết phải giảm các chu kỳ vòng lặp, mà là số chu kỳ giữa các mẫu hệ quả (ví dụ: hướng dẫn LDR). Nhưng có thể có nhiều hơn một LDR mỗi lần lặp. Bạn có thể thử một cái gì đó như thế này:

    ldrb    r1, [r0]

loop:
    cbz     r1, out
    ldrb    r2, [r0]
    cbz     r2, out
    ldrb    r1, [r0]
    b       loop

out:

Khoảng cách giữa hai hướng dẫn LDRB khác nhau để các mẫu không cách đều nhau.

Điều này có thể trì hoãn thoát khỏi vòng lặp một chút, nhưng từ mô tả vấn đề tôi không thể nói nếu nó quan trọng hay không.

Tôi tình cờ có quyền truy cập vào mô hình M7 chính xác theo chu kỳ và khi quá trình ổn định vòng lặp ban đầu của bạn chạy trên M7 theo 3 chu kỳ mỗi lần lặp (nghĩa là LDR cứ sau 3 chu kỳ), trong khi vòng lặp được đề xuất ở trên chạy theo 4 chu kỳ, nhưng hiện tại đã có có hai LDR trong đó (vì vậy LDR cứ sau 2 chu kỳ). Tỷ lệ lấy mẫu chắc chắn được cải thiện.

Để cung cấp tín dụng, việc hủy đăng ký với CBZ như một sự phá vỡ đã được đề xuất bởi @Peter Cordes trong một bình luận .

Phải thừa nhận rằng M3 sẽ chậm hơn nhưng vẫn đáng để thử, nếu đó là tốc độ lấy mẫu mà bạn theo đuổi.

Ngoài ra, bạn có thể kiểm tra xem LDRB thay vì LDR (như trong đoạn mã trên) có thay đổi gì không, mặc dù tôi không mong đợi điều đó.

CẬP NHẬT: Tôi có một phiên bản vòng lặp 2 LDR khác mà trên M7 hoàn thành trong 3 chu kỳ mà bạn có thể thử quan tâm (cũng có thể nghỉ CBZ cho phép dễ dàng cân bằng các đường dẫn sau vòng lặp):

    ldr     r1, [r0]

loop:
    ldr     r2, [r0]
    cbz     r1, out_slow
    cbz     r2, out_fast
    ldr     r1, [r0]
    b       loop

out_fast:
    /* NOPs as required */

out_slow:

Tin tốt: chạy ở 10 chu kỳ / vòng với 2 lần lấy mẫu, do đó tốc độ mẫu trung bình là OK. Vấn đề nghiêm trọng: độ trễ giữa các mẫu là 4 chu kỳ từ lần này đến r2lần khác r1, nhưng 6 chu kỳ từ lần này r1đến lần khác r2(do b loopở giữa), khi tôi muốn (nhiều nhất) chu kỳ giữa các lần lấy mẫu. Một vấn đề dễ dàng được khắc phục là chúng ta đạt được outsau một độ trễ lớn hơn sau mẫu nếu lối ra là vì r1bằng 0 so với khi nó r2bằng 0. Trong các tin tức khác, ldrbtừ một địa chỉ dải bit gây ra vấn đề, đã đổi thành ldr.
fgrieu

2
Rất vui vì nó đã hoạt động :) Không muốn cân bằng các mẫu trước khi xác nhận rằng ý tưởng cơ bản có hiệu quả với bạn. Ngoài ra, thật khó để dự đoán M7 của tôi chạy dịch sang M3 như thế nào. Tôi đã đưa cho bạn phiên bản vòng lặp đó vì nó tạo ra tốc độ mẫu thống nhất trên M7 (LDR cứ sau 2 chu kỳ). Nhưng tôi cũng đã có một phiên bản khác thực sự nhanh hơn trên M7 (3 chu kỳ trên mỗi vòng lặp và vẫn là 2 LDR), bạn có thể dùng thử nó vì thích thú. Tôi sẽ cập nhật câu trả lời của tôi.
alex_mv

3
Tôi xác nhận rằng vòng bỏ phiếu thứ hai của bạn chạy trong 10 chu kỳ với hai mẫu có khoảng cách đều nhau trên emu Cortex-M3 của tôi. Nó phản hồi câu trả lời của tôi (hiện đã được gỡ bỏ) về tính đơn giản và cho phép kiểm tra các xung ngắn hơn. 2 noptrước loop:và 4 nopsau out_fast:làm cho nó ldrsau một out_slow:mẫu 10 chu kỳ sau mẫu được nhìn thấy lần đầu tiên ở mức 0, bất kể trong ba mẫu đó là gì. Thông số kỹ thuật của tôi (như được diễn đạt trong câu hỏi) yêu cầu 13 và điều đó không quan trọng để điều chỉnh. Vấn đề được giải quyết 100%! Rất cám ơn, cũng như cảm ơn Peter Cordes vì ​​bình luận của anh ấy và B Degan cho tiền thưởng đầu tiên.
fgrieu

@fgrieu: oh yeah, đó là một mẹo thông minh trong bản cập nhật mới nhất. out_fastcó thể nhỏ gọn hơn 5 nopgiây, có thể chỉ cần thực hiện một thao tác khác ldrhoặc có thể thực hiện btheo hướng dẫn tiếp theo, nếu điều đó diễn ra nhiều chu kỳ hơn NOP trên CPU của bạn mà không gây ô nhiễm dự đoán nhánh (nếu có).
Peter Cordes

1

Bạn có thể thử điều này nhưng tôi nghi ngờ rằng nó sẽ cho cùng 6 chu kỳ

0x00600200 581a      LDR      r2,[r3,r0]; initialize r0 to 0x0
0x00600202 4282      CMP      r2,r0
0x00600204 D1FC      BNE      0x00600200

4
Tôi đã thử: 6 chu kỳ :-(
fgrieu
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.