Câu hỏi Biên dịch một ứng dụng để sử dụng trong môi trường phóng xạ cao


Chúng tôi đang biên dịch một ứng dụng C / C ++ được nhúng được triển khai trong một thiết bị được bảo vệ trong một môi trường bị bắn phá với bức xạ ion hóa. Chúng tôi đang sử dụng GCC và biên dịch chéo cho ARM. Khi được triển khai, ứng dụng của chúng tôi tạo ra một số dữ liệu sai và bị treo thường xuyên hơn chúng tôi muốn. Phần cứng được thiết kế cho môi trường này và ứng dụng của chúng tôi đã chạy trên nền tảng này trong vài năm.

Có những thay đổi chúng tôi có thể thực hiện đối với mã của chúng tôi hay các cải tiến thời gian biên dịch có thể được thực hiện để xác định / sửa lỗi mềm và bộ nhớ tham nhũng gây ra bởi sự cố đơn lẻ? Có bất kỳ nhà phát triển nào khác đã thành công trong việc giảm thiểu tác hại của các lỗi mềm trên một ứng dụng dài hạn không?


1281
2018-04-24 19:09


gốc


Các giá trị trong bộ nhớ thay đổi hay là các giá trị trong bộ xử lý thay đổi? Nếu phần cứng là được thiết kế đối với môi trường, phần mềm sẽ chạy như thể đang chạy trên một môi trường không phóng xạ. - Thomas Matthews
Nếu có thể, bạn nên thiết lập một hệ thống ghi lưu trữ các sự kiện trong bộ nhớ không bay hơi có khả năng chống bức xạ. Lưu trữ đủ thông tin để bạn có thể theo dõi sự kiện và dễ dàng tìm ra nguyên nhân gốc rễ. - Thomas Matthews
@Thomas Matthews Mọi bộ nhớ đều có tỷ lệ lỗi FIT và các nhà sản xuất phần cứng thực hiện rất nhiều lời hứa. Hầu hết các vấn đề có thể do SEU sửa đổi ram khi chạy. - rook
thảo luận bổ sung có liên quan đây (tin tức của hacker) - Noam Hacker
Đây là giải pháp phần cứng / phần mềm kết hợp, nhưng tôi biết Texas Instruments (và có lẽ là những người khác) làm cho các chip nhúng cho các ứng dụng quan trọng an toàn bao gồm hai lõi trùng lặp, chạy ở chế độ khóa, nửa chu kỳ đồng hồ. Có các ngắt đặc biệt và đặt lại các hành động được thực hiện khi phần cứng phát hiện một điều gì đó khác biệt giữa các lõi, do đó bạn có thể khôi phục từ các lỗi. Tôi tin rằng TI thương hiệu chúng là bộ xử lý an toàn "Hercules". - mbrig


Các câu trả lời:


Làm việc trong khoảng 4-5 năm với phát triển phần mềm / phần mềm và kiểm tra môi trường vệ tinh thu nhỏ*, Tôi muốn chia sẻ kinh nghiệm của tôi ở đây.

* (các vệ tinh thu nhỏ dễ bị rối loạn sự kiện đơn lẻ hơn các vệ tinh lớn hơn do kích thước tương đối nhỏ, giới hạn cho các thành phần điện tử của nó)

Nói ngắn gọn và trực tiếp: không có cơ chế để phục hồi từ có thể phát hiện, sai sót   tình hình bởi chính phần mềm / phần mềm không có, ít nhất một    sao chép của phiên bản làm việc tối thiểu phần mềm / phần mềm một vài nơi cho phục hồi mục đích - và với phần cứng hỗ trợ phục hồi (chức năng).

Bây giờ, tình trạng này thường được xử lý cả ở cấp phần cứng và phần mềm. Ở đây, như bạn yêu cầu, tôi sẽ chia sẻ những gì chúng tôi có thể làm ở cấp phần mềm.

  1. ... phục hồi mục đích .... Cung cấp khả năng cập nhật / biên dịch / reflash phần mềm / phần mềm của bạn trong môi trường thực. Đây là gần như phải có tính năng cho bất kỳ phần mềm / phần mềm nào trong môi trường có độ ion hóa cao. Không có điều này, bạn có thể có phần mềm dự phòng / phần cứng nhiều như bạn muốn nhưng tại một thời điểm, tất cả chúng đều sẽ nổ tung. Vì vậy, chuẩn bị tính năng này!

  2. ... phiên bản tối thiểu ... Có phản hồi, nhiều bản sao, phiên bản tối thiểu của phần mềm / chương trình cơ sở trong mã của bạn. Điều này giống như chế độ An toàn trong Windows. Thay vì chỉ có một phiên bản đầy đủ chức năng của phần mềm của bạn, có nhiều bản sao của phiên bản phần mềm / phần mềm tối thiểu của bạn. Bản sao tối thiểu thường có kích thước ít hơn nhiều so với bản sao đầy đủ và hầu như luôn luôn có chỉ có hai hoặc ba tính năng sau:

    1. có khả năng nghe lệnh từ hệ thống bên ngoài,
    2. có khả năng cập nhật phần mềm / phần mềm hiện tại,
    3. có khả năng giám sát dữ liệu vệ sinh của hoạt động cơ bản.
  3. ... sao chép ... đâu đó ... Có phần mềm / phần mềm dự phòng ở đâu đó.

    1. Bạn có thể, với hoặc là mà không cần phần cứng dự phòng, hãy thử có phần mềm / phần mềm dự phòng trong uC ARM của bạn. Điều này thường được thực hiện bằng cách có hai hoặc nhiều phần mềm / phần mềm giống hệt nhau trong các địa chỉ riêng biệt mà gửi nhịp tim cho nhau - nhưng chỉ có một người sẽ hoạt động cùng một lúc. Nếu một hoặc nhiều phần mềm / phần mềm được biết là không phản hồi, hãy chuyển sang phần mềm / chương trình cơ sở khác. Lợi ích của việc sử dụng phương pháp này là chúng tôi có thể thay thế chức năng ngay sau khi xảy ra lỗi - không có bất kỳ liên hệ nào với bất kỳ hệ thống / bên nào chịu trách nhiệm phát hiện và sửa lỗi (trong trường hợp vệ tinh, thường là Trung tâm điều khiển nhiệm vụ ( MCC)).

      Nói đúng ra, không có phần cứng dư thừa, bất lợi khi thực hiện điều này là bạn thực sự không thể loại trừ tất cả các điểm duy nhất của thất bại. Ít nhất, bạn vẫn sẽ có một điểm duy nhất của thất bại, đó là chính công tắc (hoặc thường là sự khởi đầu của mã). Tuy nhiên, đối với một thiết bị bị giới hạn bởi kích thước trong một môi trường có độ ion hóa cao (chẳng hạn như vệ tinh pico / femto), việc giảm điểm lỗi duy nhất xuống một điểm không có phần cứng bổ sung sẽ vẫn đáng xem xét. Tuy nhiên, đoạn mã cho việc chuyển đổi chắc chắn sẽ ít hơn nhiều so với mã cho toàn bộ chương trình - làm giảm đáng kể nguy cơ nhận được một sự kiện trong đó.

    2. Nhưng nếu bạn không làm điều này, bạn nên có ít nhất một bản sao trong hệ thống bên ngoài có thể tiếp xúc với thiết bị và cập nhật phần mềm / phần mềm (trong trường hợp vệ tinh, nó lại là trung tâm điều khiển nhiệm vụ).

    3. Bạn cũng có thể có bản sao trong bộ nhớ vĩnh viễn của mình trong thiết bị có thể được kích hoạt để khôi phục phần mềm / phần mềm hệ thống đang chạy
  4. ... tình huống sai lầm có thể phát hiện được .. Lỗi phải là có thể phát hiện được, thường là bởi phần cứng mạch hiệu chỉnh / dò lỗi hoặc bằng một đoạn mã nhỏ để sửa lỗi / phát hiện lỗi. Tốt nhất là đặt mã nhỏ, nhiều và độc lập từ phần mềm / phần mềm chính. Nhiệm vụ chính của nó là chỉ có để kiểm tra / sửa chữa. Nếu mạch phần cứng / phần mềm là đáng tin cậy (chẳng hạn như nó là bức xạ cứng hơn so với phần còn lại - hoặc có nhiều mạch / logic), sau đó bạn có thể xem xét việc sửa lỗi với nó. Nhưng nếu nó không phải là, nó là tốt hơn để làm cho nó như phát hiện lỗi. Việc hiệu chỉnh có thể do hệ thống / thiết bị bên ngoài. Để sửa lỗi, bạn có thể xem xét sử dụng thuật toán sửa lỗi cơ bản như Hamming / Golay23, vì chúng có thể được triển khai dễ dàng hơn cả trong mạch / phần mềm. Nhưng nó cuối cùng phụ thuộc vào khả năng của đội bạn. Để phát hiện lỗi, CRC thường được sử dụng.

  5. ... phần cứng hỗ trợ phục hồi Bây giờ, nói đến khía cạnh khó khăn nhất về vấn đề này. Cuối cùng, sự phục hồi đòi hỏi phần cứng chịu trách nhiệm cho sự phục hồi được ít nhất chức năng. Nếu phần cứng bị hỏng vĩnh viễn (thường xảy ra sau khi phần cứng của nó bị hỏng) Tổng liều ion hóa đạt đến mức nhất định), sau đó có (thật đáng buồn) không có cách nào để phần mềm giúp phục hồi. Do đó, phần cứng là mối quan tâm tối quan trọng nhất đối với một thiết bị tiếp xúc với mức bức xạ cao (như vệ tinh).

Ngoài các gợi ý cho trên dự đoán lỗi của phần vững do sự kiện duy nhất khó chịu, tôi cũng muốn đề nghị bạn có:

  1. Phát hiện lỗi và / hoặc thuật toán sửa lỗi trong giao thức truyền thông giữa các hệ thống con. Đây là điều khác gần như phải có để tránh các tín hiệu không đầy đủ / sai nhận được từ hệ thống khác

  2. Lọc trong đọc ADC của bạn. Làm không phải sử dụng đọc ADC trực tiếp. Lọc bộ lọc theo bộ lọc trung bình, bộ lọc trung bình hoặc bất kỳ bộ lọc nào khác - không bao giờ tin tưởng giá trị đọc duy nhất. Lấy mẫu nhiều hơn, không ít hơn - hợp lý.


731
2018-04-25 02:58





NASA có một bài báo về bức xạ cứng phần mềm. Nó mô tả ba nhiệm vụ chính:

  1. Theo dõi thường xuyên bộ nhớ cho các lỗi sau đó lọc ra các lỗi đó,
  2. cơ chế phục hồi lỗi mạnh mẽ và
  3. khả năng cấu hình lại nếu một cái gì đó không còn hoạt động nữa.

Lưu ý rằng tốc độ quét bộ nhớ phải đủ thường xuyên mà các lỗi nhiều bit hiếm khi xảy ra, như ECC bộ nhớ có thể phục hồi từ lỗi một bit, không phải lỗi nhiều bit.

Phục hồi lỗi mạnh mẽ bao gồm chuyển luồng điều khiển (thường khởi động lại quy trình tại một điểm trước lỗi), phát hành tài nguyên và khôi phục dữ liệu.

Đề xuất chính của họ để khôi phục dữ liệu là để tránh sự cần thiết cho nó, thông qua việc có dữ liệu trung gian được coi là tạm thời, do đó khởi động lại trước khi lỗi cũng cuộn ngược dữ liệu về trạng thái đáng tin cậy. Điều này nghe có vẻ tương tự như khái niệm "giao dịch" trong cơ sở dữ liệu.

Họ thảo luận về các kỹ thuật đặc biệt thích hợp cho các ngôn ngữ hướng đối tượng như C ++. Ví dụ

  1. ECC dựa trên phần mềm cho các đối tượng bộ nhớ tiếp giáp
  2. Lập trình theo hợp đồng: xác minh điều kiện tiên quyết và postconditions, sau đó kiểm tra đối tượng để xác minh nó vẫn đang ở trạng thái hợp lệ.

370
2018-04-24 19:32



Điều này thực sự giống như một cái gì đó mà nguyên chất ngôn ngữ sẽ tốt. Vì các giá trị không bao giờ thay đổi, nếu chúng bị hỏng, bạn có thể quay lại định nghĩa ban đầu (nghĩa là nó được cho là), và bạn sẽ không vô tình làm điều tương tự hai lần (vì thiếu tác dụng phụ). - PyRulez
RAII là một ý tưởng tồi, bởi vì bạn không thể phụ thuộc vào nó thực hiện một cách chính xác hoặc thậm chí cả. Nó có thể làm hỏng dữ liệu của bạn một cách ngẫu nhiên, vv Bạn thực sự muốn nhiều bất biến như bạn có thể nhận được, và sửa lỗi cơ chế trên đó. Sẽ dễ dàng hơn nhiều khi vứt bỏ những thứ bị hỏng hơn là cố gắng sửa chữa chúng bằng cách nào đó (chính xác bạn biết đủ để quay về trạng thái cũ đúng không?). Bạn có thể muốn sử dụng một ngôn ngữ khá ngu ngốc cho điều này, mặc dù - tối ưu hóa có thể làm tổn thương nhiều hơn họ giúp đỡ. - Luaan
@ PyRulez: ngôn ngữ thuần túy là một trừu tượng, phần cứng không phải là tinh khiết. Trình biên dịch khá tốt trong việc ẩn sự khác biệt. Nếu chương trình của bạn có giá trị, nó sẽ không được sử dụng nữa sau bước X, trình biên dịch có thể ghi đè lên nó bằng một giá trị được tính trong bước X + 1. Nhưng điều này có nghĩa là bạn không thể quay trở lại. Chính thức hơn, các trạng thái có thể của một chương trình trong một ngôn ngữ thuần túy tạo thành một biểu đồ tuần hoàn, có nghĩa là hai trạng thái tương đương và có thể được hợp nhất khi các trạng thái có thể truy cập từ cả hai đều tương đương nhau. Việc sáp nhập này phá hủy sự khác biệt trong đường dẫn dẫn đến các trạng thái đó. - MSalters
bằng cách nào đó tôi cảm thấy như thế này tất cả dẫn trở lại để xem mã bit ban đầu. Một khi bạn có một tập hợp các số, tất cả những gì bạn phải làm là đảm bảo chúng không thay đổi trong một phân vùng cụ thể. Sau đó, tất cả các bạn làm là tái mô hình các con số nếu họ di chuyển hoặc biến mất. Các phân vùng khác được sử dụng để lưu trữ dữ liệu, v.v. - DeerSpotter


Dưới đây là một số suy nghĩ và ý tưởng:

Sử dụng ROM sáng tạo hơn.

Lưu trữ bất cứ điều gì bạn có thể trong ROM. Thay vì tính toán mọi thứ, hãy lưu trữ các bảng tra cứu trong ROM. (Hãy chắc chắn rằng trình biên dịch của bạn xuất các bảng tra cứu của bạn vào phần chỉ đọc! In ra các địa chỉ bộ nhớ trong thời gian chạy để kiểm tra!) Lưu trữ bảng vectơ ngắt của bạn trong ROM. Tất nhiên, chạy một số bài kiểm tra để xem ROM của bạn đáng tin cậy như thế nào so với RAM của bạn.

Sử dụng RAM tốt nhất của bạn cho ngăn xếp.

SEU trong ngăn xếp có lẽ là nguồn có khả năng xảy ra sự cố nhất, vì đó là nơi mà các biến chỉ mục, biến trạng thái, địa chỉ trả về và con trỏ của các loại khác nhau thường sống.

Thực hiện thói quen hẹn giờ và đánh dấu thời gian theo dõi.

Bạn có thể chạy một thói quen "kiểm tra sanity" mỗi lần đánh dấu thời gian, cũng như một thói quen cơ quan giám sát để xử lý hệ thống khóa lên. Mã chính của bạn cũng có thể định kỳ tăng bộ đếm để biểu thị tiến độ và quy trình kiểm tra độ chính xác có thể đảm bảo điều này đã xảy ra.

Triển khai thực hiện mã sửa lỗi trong phần mềm.

Bạn có thể thêm dự phòng vào dữ liệu của mình để có thể phát hiện và / hoặc sửa lỗi. Điều này sẽ thêm thời gian xử lý, có khả năng để lại bộ vi xử lý tiếp xúc với bức xạ trong một thời gian dài hơn, do đó làm tăng cơ hội lỗi, vì vậy bạn phải xem xét sự cân bằng.

Nhớ cache.

Kiểm tra kích thước bộ đệm CPU của bạn. Dữ liệu mà bạn đã truy cập hoặc sửa đổi gần đây có thể sẽ nằm trong bộ nhớ cache. Tôi tin rằng bạn có thể vô hiệu hóa ít nhất một số bộ đệm (với chi phí hiệu suất lớn); bạn nên thử điều này để xem mức độ nhạy cảm của bộ nhớ cache đến SEU như thế nào. Nếu cache cứng hơn RAM thì bạn có thể thường xuyên đọc và ghi lại dữ liệu quan trọng để đảm bảo nó nằm trong bộ nhớ đệm và đưa RAM trở lại vào dòng.

Sử dụng trình xử lý lỗi trang một cách khéo léo.

Nếu bạn đánh dấu một trang bộ nhớ là không có mặt, CPU sẽ phát hành một lỗi trang khi bạn cố truy cập nó. Bạn có thể tạo trình xử lý lỗi trang thực hiện kiểm tra trước khi thực hiện yêu cầu đọc. (Các hệ điều hành PC sử dụng hệ thống này để tải các trang trong suốt đã được hoán đổi vào đĩa.)

Sử dụng ngôn ngữ lắp ráp cho những điều quan trọng (có thể là tất cả mọi thứ).

Với ngôn ngữ lắp ráp, bạn biết những gì trong sổ đăng ký và những gì có trong RAM; bạn biết những gì RAM bảng đặc biệt CPU đang sử dụng, và bạn có thể thiết kế những thứ trong một cách vòng xoay để giữ cho rủi ro của bạn xuống.

Sử dụng objdump để thực sự nhìn vào ngôn ngữ lắp ráp được tạo ra, và tìm ra số lượng mã mà mỗi thói quen của bạn chiếm được.

Nếu bạn đang sử dụng một hệ điều hành lớn như Linux thì bạn đang yêu cầu gặp rắc rối; có rất nhiều phức tạp và rất nhiều thứ để đi sai.

Hãy nhớ rằng nó là một trò chơi xác suất.

Một bình luận viên nói

Mọi thói quen bạn viết để bắt lỗi sẽ phải chịu sự thất bại của chính nó từ cùng một nguyên nhân.

Trong khi điều này là đúng, cơ hội của các lỗi trong (nói) 100 byte mã và dữ liệu cần thiết cho một thói quen kiểm tra để hoạt động chính xác là nhỏ hơn nhiều so với cơ hội của các lỗi ở nơi khác. Nếu ROM của bạn là khá đáng tin cậy và hầu như tất cả các mã / dữ liệu thực sự trong ROM thì tỷ lệ cược của bạn thậm chí còn tốt hơn.

Sử dụng phần cứng dự phòng.

Sử dụng 2 hoặc nhiều thiết lập phần cứng giống hệt nhau với mã giống hệt nhau. Nếu kết quả khác nhau, một thiết lập lại nên được kích hoạt. Với 3 thiết bị trở lên, bạn có thể sử dụng hệ thống "bỏ phiếu" để xác định hệ thống nào đã bị xâm phạm.


107
2018-04-24 23:11



Ngày nay, ECC có sẵn thông qua phần cứng, giúp tiết kiệm thời gian xử lý. Bước một là chọn một vi điều khiển với ECC tích hợp. - Lundin
Một nơi nào đó trong tâm trí của tôi là một tham chiếu đến phần mềm bay không gian (có lẽ là tàu con thoi?), Nơi kiến ​​trúc dự phòng được thiết kế rõ ràng không giống nhau (và bởi các đội khác nhau). Làm như vậy giảm thiểu khả năng xảy ra lỗi hệ thống trong thiết kế phần cứng / phần mềm, làm giảm khả năng tất cả các hệ thống bỏ phiếu bị rơi cùng lúc khi đối đầu với cùng một yếu tố đầu vào. - Peter M
@PeterM: AFAIK cũng được tuyên bố là phần mềm máy bay cho Boeing 777: Ba phiên bản của ba nhóm bằng ba ngôn ngữ lập trình. - Martin Schröder
@DanEsparza RAM thường có hoặc là một tụ điện (DRAM) hoặc một vài bóng bán dẫn trong phản hồi (SRAM) lưu trữ dữ liệu. Một sự kiện bức xạ có thể giả mạo sạc / xả tụ điện, hoặc thay đổi tín hiệu trong vòng phản hồi. ROM thường không cần khả năng viết (ít nhất là không có trường hợp đặc biệt và / hoặc điện áp cao hơn) và do đó có thể vốn ổn định hơn ở mức vật lý. - Andrey Akhmetov
@ DanEsparza: Có nhiều loại ký ức ROM. Nếu "ROM" được mô phỏng bằng eeprom hoặc flash readonly-at-5v nhưng có thể lập trình-at-10v, thì thực sự "ROM" đó vẫn dễ bị ion hóa. Có lẽ chỉ ít hơn những người khác. Tuy nhiên, có những thứ tốt đẹp như ol ROM mặt nạ hoặc là PROM dựa trên cầu chì mà tôi nghĩ sẽ cần một lượng bức xạ thực sự nghiêm trọng để bắt đầu thất bại. Tôi không biết tuy nhiên nếu vẫn còn sản xuất. - quetzalcoatl


Bạn cũng có thể quan tâm đến các tài liệu phong phú về chủ đề của khả năng chịu lỗi thuật toán. Điều này bao gồm nhiệm vụ cũ: Viết một loại sắp xếp chính xác đầu vào của nó khi số lượng so sánh không đổi sẽ không thành công (hoặc, phiên bản hơi ác hơn, khi số so sánh thất bại quy mô càng tiệm cận càng nhiều càng tốt log(n) cho n so sánh).

Một nơi để bắt đầu đọc là bài báo năm 1984 của Huang và Abraham "Dung sai lỗi dựa trên thuật toán cho hoạt động ma trậnÝ tưởng của họ là mơ hồ tương tự như tính toán mã hóa đồng cấu (nhưng nó không thực sự giống nhau, vì chúng đang cố gắng phát hiện lỗi / sửa lỗi ở cấp độ hoạt động).

Một hậu duệ gần đây của bài báo đó là Bosilca, Delmas, Dongarra, và của Langou "Dung sai lỗi dựa trên thuật toán được áp dụng cho tính toán hiệu suất cao".


93
2018-04-24 21:13



Tôi thực sự thích phản ứng của bạn. Đây là một cách tiếp cận phần mềm chung chung hơn để toàn vẹn dữ liệu, và một giải pháp dung sai lỗi dựa trên thuật toán sẽ được sử dụng trong sản phẩm cuối cùng của chúng tôi. Cảm ơn! - rook


Viết mã cho môi trường phóng xạ không thực sự khác với viết mã cho bất kỳ ứng dụng quan trọng nào.

Ngoài những gì đã được đề cập, dưới đây là một số mẹo linh tinh:

  • Sử dụng các biện pháp an toàn "bánh mì & bơ" hàng ngày cần có mặt trên bất kỳ hệ thống nhúng bán chuyên nghiệp nào: cơ quan giám sát nội bộ, thiết bị phát hiện điện áp thấp bên trong, màn hình đồng hồ bên trong. Những điều này thậm chí không cần phải được đề cập trong năm 2016 và chúng là tiêu chuẩn trên khá nhiều vi điều khiển hiện đại.
  • Nếu bạn có một MCU an toàn và / hoặc ô tô theo định hướng, nó sẽ có một số tính năng giám sát nhất định, chẳng hạn như một cửa sổ thời gian nhất định, bên trong đó bạn cần phải làm mới cơ quan giám sát. Điều này được ưu tiên nếu bạn có một hệ thống thời gian thực quan trọng trong nhiệm vụ.
  • Nói chung, sử dụng một MCU phù hợp cho các loại hệ thống, và không phải một số fluff chính thống chung bạn nhận được trong một gói ngô mảnh. Hầu hết các nhà sản xuất MCU ngày nay đều có MCU chuyên dụng được thiết kế cho các ứng dụng an toàn (TI, Freescale, Renesas, ST, Infineon, v.v ...). Các tính năng này có rất nhiều tính năng an toàn tích hợp, bao gồm cả lõi khóa: có nghĩa là có 2 lõi CPU thực hiện cùng một mã và chúng phải đồng ý với nhau.
  • QUAN TRỌNG: Bạn phải đảm bảo tính toàn vẹn của thanh ghi MCU nội bộ. Tất cả các thanh ghi điều khiển và trạng thái của các thiết bị ngoại vi phần cứng có thể ghi được có thể nằm trong bộ nhớ RAM và do đó dễ bị tổn thương.

    Để bảo vệ bạn khỏi bị đăng ký tham nhũng, tốt nhất là chọn một vi điều khiển với các tính năng "ghi một lần" được tích hợp sẵn của thanh ghi. Ngoài ra, bạn cần phải lưu trữ các giá trị mặc định của tất cả các thanh ghi phần cứng trong NVM và sao chép xuống các giá trị đó vào các thanh ghi của bạn theo các khoảng thời gian đều đặn. Bạn có thể đảm bảo tính toàn vẹn của các biến quan trọng theo cách tương tự.

    Lưu ý: luôn sử dụng chương trình phòng thủ. Có nghĩa là bạn phải thiết lập tất cả các đăng ký trong MCU và không chỉ các ứng dụng được ứng dụng sử dụng. Bạn không muốn một số thiết bị ngoại vi phần cứng ngẫu nhiên đột nhiên thức dậy.

  • Có tất cả các loại phương pháp để kiểm tra lỗi trong RAM hoặc NVM: checksums, "walking patterns", phần mềm ECC vv vv. Giải pháp tốt nhất hiện nay là không sử dụng bất kỳ trong số này, nhưng để sử dụng một MCU với built-in ECC và kiểm tra tương tự. Bởi vì làm điều này trong phần mềm là phức tạp, và kiểm tra lỗi trong chính nó do đó có thể giới thiệu lỗi và các vấn đề bất ngờ.

  • Sử dụng dự phòng. Bạn có thể lưu trữ cả hai bộ nhớ dễ bay hơi và không bay hơi trong hai phân đoạn "gương" giống hệt nhau, mà phải luôn luôn tương đương. Mỗi phân đoạn có thể có một tổng kiểm tra CRC đính kèm.
  • Tránh sử dụng những ký ức bên ngoài MCU.
  • Thực hiện một trình điều khiển ngoại lệ mặc định của dịch vụ gián đoạn / trình xử lý ngoại lệ mặc định cho tất cả các ngắt / ngoại lệ có thể xảy ra. Ngay cả những cái bạn không sử dụng. Các thói quen mặc định không nên làm gì ngoại trừ tắt nguồn ngắt riêng của nó.
  • Hiểu và nắm lấy khái niệm lập trình phòng thủ. Điều này có nghĩa là chương trình của bạn cần xử lý tất cả các trường hợp có thể, ngay cả những trường hợp không thể xảy ra trong lý thuyết. Ví dụ.

    Phần mềm quan trọng của sứ mệnh chất lượng cao phát hiện càng nhiều lỗi càng tốt, và sau đó bỏ qua chúng một cách an toàn.

  • Không bao giờ viết các chương trình dựa vào hành vi được chỉ định kém. Có khả năng hành vi như vậy có thể thay đổi đáng kể với những thay đổi phần cứng bất ngờ do bức xạ hoặc EMI gây ra. Cách tốt nhất để đảm bảo rằng chương trình của bạn không bị crap như vậy là sử dụng một chuẩn mã hóa như MISRA, cùng với một công cụ phân tích tĩnh. Điều này cũng sẽ giúp lập trình phòng thủ và làm sạch các lỗi (tại sao bạn không muốn phát hiện lỗi trong bất kỳ loại ứng dụng nào?).
  • QUAN TRỌNG: Không thực hiện bất kỳ sự phụ thuộc nào của các giá trị mặc định của các biến thời gian lưu trữ tĩnh. Tức là, đừng tin vào nội dung mặc định của .data hoặc là .bss. Có thể có bất kỳ khoảng thời gian nào giữa thời điểm khởi tạo đến điểm mà biến được sử dụng thực sự, có thể đã có rất nhiều thời gian cho RAM bị hỏng. Thay vào đó, hãy viết chương trình sao cho tất cả các biến như vậy được thiết lập từ NVM trong thời gian chạy, ngay trước thời điểm biến được sử dụng lần đầu tiên.

    Trong thực tế, điều này có nghĩa là nếu một biến được khai báo ở phạm vi tệp hoặc là static, bạn không bao giờ nên sử dụng = để khởi tạo nó (hoặc bạn có thể, nhưng nó là vô nghĩa, bởi vì bạn không thể dựa vào giá trị dù sao đi nữa). Luôn đặt nó trong thời gian chạy, ngay trước khi sử dụng. Nếu có thể liên tục cập nhật các biến như vậy từ NVM, thì hãy làm như vậy.

    Tương tự như vậy trong C ++, không dựa vào các hàm tạo cho các biến thời gian lưu trữ tĩnh. Yêu cầu (các) nhà xây dựng gọi một thói quen "thiết lập" công khai, mà bạn cũng có thể gọi sau này trong thời gian chạy, ngay từ ứng dụng người gọi.

    Nếu có thể, hãy xóa mã khởi động "sao chép" khởi tạo .data và .bss (và gọi các hàm dựng C ++) hoàn toàn, để bạn nhận được các lỗi liên kết nếu bạn viết mã dựa vào đó. Nhiều trình biên dịch có tùy chọn để bỏ qua điều này, thường được gọi là "tối thiểu / khởi động nhanh" hoặc tương tự.

    Điều này có nghĩa là mọi thư viện bên ngoài phải được kiểm tra để chúng không chứa bất kỳ sự phụ thuộc nào như vậy.

  • Thực hiện và xác định trạng thái an toàn cho chương trình, đến nơi bạn sẽ hoàn nguyên trong trường hợp có lỗi nghiêm trọng.

  • Việc triển khai báo cáo lỗi / hệ thống nhật ký lỗi luôn hữu ích.

36
2018-04-27 14:11



Một cách để xử lý các boolean bị hỏng (như trong liên kết ví dụ của bạn) có thể là làm cho TRUE tương đương với 0xffffffff sau đó sử dụng POPCNT với một ngưỡng. - wizzwizz4
@ wizzwizz4 Cho rằng giá trị 0xff là giá trị mặc định của ô flash không được lập trình, có vẻ như là một ý tưởng tồi. - Lundin
%01010101010101010101010101010101, XOR sau đó là POPCNT? - wizzwizz4
@ wizzwizz4 Hoặc chỉ giá trị 0x1, theo yêu cầu của tiêu chuẩn C. - Lundin
@ wizzwizz4 Tại sao bạn sử dụng một số hoặc tất cả các phương pháp được đề cập ở trên (ECC, CRC, v.v.). Nếu không, tia vũ trụ cũng có thể lật một chút trong .text , thay đổi mã op hoặc tương tự. - Lundin


Có thể sử dụng C để viết các chương trình hoạt động mạnh mẽ trong các môi trường như vậy, nhưng chỉ khi hầu hết các hình thức tối ưu hóa trình biên dịch bị vô hiệu hóa. Tối ưu hóa các trình biên dịch được thiết kế để thay thế nhiều kiểu mã hóa dường như dư thừa bằng các mẫu mã "hiệu quả hơn", và có thể không có đầu mối rằng lý do mà lập trình viên đang thử nghiệm x==42 khi trình biên dịch biết không có cách nào x có thể có thể giữ bất cứ điều gì khác là bởi vì các lập trình viên muốn ngăn chặn việc thực thi mã nhất định với xgiữ một số giá trị khác - ngay cả trong trường hợp cách duy nhất nó có thể giữ giá trị đó sẽ là nếu hệ thống nhận được một số loại trục trặc điện.

Khai báo các biến dưới dạng volatile thường hữu ích, nhưng có thể không phải là thuốc chữa bách bệnh. Đặc biệt quan trọng, lưu ý rằng mã hóa an toàn thường đòi hỏi nguy hiểm các hoạt động có phần cứng interlocks yêu cầu nhiều bước để kích hoạt, và mã đó được viết bằng cách sử dụng mẫu:

... code that checks system state
if (system_state_favors_activation)
{
  prepare_for_activation();
  ... code that checks system state again
  if (system_state_is_valid)
  {
    if (system_state_favors_activation)
      trigger_activation();
  }
  else
    perform_safety_shutdown_and_restart();
}
cancel_preparations();

Nếu trình biên dịch dịch mã theo kiểu chữ tương đối, và nếu tất cả kiểm tra trạng thái hệ thống được lặp lại sau prepare_for_activation(), hệ thống có thể mạnh mẽ chống lại gần như bất kỳ sự kiện trục trặc đơn hợp lý nào, ngay cả những người sẽ tự ý làm hỏng bộ đếm chương trình và ngăn xếp. Nếu một trục trặc xảy ra ngay sau khi một cuộc gọi đến prepare_for_activation(), điều đó sẽ hàm ý kích hoạt đó sẽ phù hợp (vì không có lý do nào khác prepare_for_activation() sẽ được gọi trước khi trục trặc). Nếu trục trặc gây ra mã để tiếp cận prepare_for_activation() không thích hợp, nhưng ở đó không có sự kiện trục trặc tiếp theo, sẽ không có cách nào để mã tiếp theo chạm tới trigger_activation() mà không cần phải vượt qua kiểm tra xác nhận hoặc gọi cancel_preparations trước tiên [nếu stack ổn định, việc thực hiện có thể tiến tới một điểm ngay trước đó trigger_activation() sau bối cảnh được gọi là prepare_for_activation() trả về, nhưng cuộc gọi đến cancel_preparations() sẽ xảy ra giữa các cuộc gọi đến prepare_for_activation() và trigger_activation(), do đó làm cho cuộc gọi thứ hai vô hại.

Mã như vậy có thể được an toàn trong C truyền thống, nhưng không phải với các trình biên dịch C hiện đại. Các trình biên dịch như vậy có thể rất nguy hiểm trong môi trường đó bởi vì tích cực chúng cố gắng chỉ bao gồm mã sẽ có liên quan trong các tình huống có thể xảy ra thông qua một số cơ chế được xác định rõ ràng và hậu quả hậu quả cũng sẽ được xác định rõ. Mã có mục đích sẽ là phát hiện và làm sạch sau khi thất bại có thể, trong một số trường hợp, cuối cùng làm cho mọi việc tồi tệ hơn. Nếu trình biên dịch xác định rằng sự phục hồi đã cố gắng trong một số trường hợp gọi hành vi không xác định, nó có thể suy ra rằng các điều kiện đòi hỏi sự phục hồi như vậy không thể xảy ra, do đó loại bỏ mã có thể đã kiểm tra chúng.


30
2018-04-25 16:14



Thực tế, có bao nhiêu trình biên dịch hiện đại không có -O0 hoặc một công tắc tương đương? GCC sẽ làm rất nhiều điều kỳ lạ nếu bạn cho phép, nhưng nếu bạn yêu cầu nó không làm chúng, nó thường có thể khá là chữ. - Leushenko
Xin lỗi, nhưng ý tưởng này về cơ bản là nguy hiểm. Vô hiệu hóa tối ưu hóa sẽ tạo ra một chương trình chậm hơn. Hay nói cách khác, bạn cần một CPU nhanh hơn. Khi điều đó xảy ra, CPU nhanh hơn sẽ nhanh hơn bởi vì các khoản phí trên cổng transistor của chúng nhỏ hơn. Điều này làm cho chúng dễ bị ảnh hưởng hơn nhiều. Chiến lược tốt hơn là sử dụng chip chậm, lớn, trong đó một photon đơn lẻ ít có khả năng đập hơn một chút và đạt được tốc độ bằng -O2. - MSalters
Lý do phụ tại sao -O0 là một ý tưởng tồi bởi vì nó phát ra nhiều hướng dẫn vô dụng hơn. Ví dụ: cuộc gọi không có nội tuyến chứa các hướng dẫn để lưu sổ đăng ký, thực hiện cuộc gọi, khôi phục sổ đăng ký. Tất cả những điều này có thể thất bại. Một hướng dẫn không có ở đó không thể thất bại. - MSalters
Tuy nhiên, một lý do khác tại sao -O0 là một ý tưởng tồi: nó có xu hướng lưu trữ các biến trong bộ nhớ thay vì trong một thanh ghi. Bây giờ nó không chắc chắn rằng bộ nhớ là dễ bị ảnh hưởng của SEU, nhưng dữ liệu trong chuyến bay là dễ bị tổn thương hơn so với dữ liệu ở phần còn lại. Di chuyển dữ liệu vô ích nên tránh, và -O2 giúp ở đó. - MSalters
@MSalters: Điều quan trọng không phải là dữ liệu được miễn nhiễm với sự gián đoạn, mà là hệ thống có thể xử lý các gián đoạn theo cách đáp ứng các yêu cầu. Trên nhiều trình biên dịch vô hiệu hóa tất cả các tối ưu hóa tạo ra mã thực hiện một số lượng quá nhiều đăng ký để đăng ký di chuyển, đó là xấu, nhưng lưu trữ các biến trong bộ nhớ là an toàn hơn từ một quan điểm phục hồi hơn giữ chúng trong sổ đăng ký. Nếu có hai biến trong bộ nhớ có nghĩa vụ phải tuân theo một số điều kiện (ví dụ: v1=v2+0xCAFEBABE và tất cả các cập nhật cho hai biến được thực hiện ... - supercat


Đây là một chủ đề cực kỳ rộng. Về cơ bản, bạn không thể thực sự phục hồi từ bộ nhớ tham nhũng, nhưng bạn ít nhất có thể thử không kịp thời. Dưới đây là một số kỹ thuật bạn có thể sử dụng:

  • dữ liệu kiểm tra hằng số. Nếu bạn có bất kỳ dữ liệu cấu hình nào không đổi trong một thời gian dài (bao gồm các thanh ghi phần cứng mà bạn đã cấu hình), hãy tính toán tổng kiểm tra của nó khi khởi tạo và xác minh nó theo định kỳ. Khi bạn thấy sự không khớp, đã đến lúc khởi tạo lại hoặc đặt lại.

  • lưu trữ các biến với dự phòng. Nếu bạn có một biến quan trọng x, viết giá trị của nó trong x1, x2 và x3 và đọc nó như (x1 == x2) ? x2 : x3.

  • triển khai thực hiện chương trình giám sát dòng chảy. XOR một cờ toàn cầu với một giá trị duy nhất trong các hàm / nhánh quan trọng được gọi từ vòng lặp chính. Chạy chương trình trong môi trường không có bức xạ với phạm vi kiểm tra gần 100% sẽ cung cấp cho bạn danh sách giá trị chấp nhận được của lá cờ vào cuối chu kỳ. Đặt lại nếu bạn thấy độ lệch.

  • theo dõi con trỏ ngăn xếp. Ở đầu vòng lặp chính, so sánh con trỏ ngăn xếp với giá trị mong đợi của nó. Đặt lại trên độ lệch.


27
2018-04-25 17:05