مقدمة: لماذا مسائل الكلمات الرئيسية في النص المدمج جيم

وفي مجال تطوير النظم المدمجة، يكون التفاعل بين الأجهزة هو الجسر بين البرامجيات والعالم المادي، إذ أن أجهزة التحكم الدقيقة والمجهزات تتصل بالمجسات والمحاضرات والمواقف المدمجة للذاكرة والأجهزة الخارجية من خلال السجلات وعناوين الذاكرة التي يمكن أن تتغير بشكل غير متزامن، كما أن لغة البرمجة C توفر النوع من النوع المؤهل لمعالجة هذه التغيرات الفاسدة غير المتوقعة.

وتتوسع هذه المادة بغرض ، وتحفر في ميكانيكيات التجميع الأمثل، وتطرح أنماطاً للتفاعل مع الأجهزة في العالم الحقيقي، وتوضح الثغرات المشتركة، وستتعلم بالضبط أين تضع [(FLT:4]) في رمزك C، ولماذا لا يزال لا غنى عنه على الرغم من التقدم اللغوي الحديث مثل أو C+

ماذا يفعل كلمة رئيسية في الواقع يفعل؟

وعلى مستوى اللغات، ]]] يقول للمجمع إن قيمة المتغيرات يمكن تعديلها بوسائل خارج تدفق البرنامج العادي - مثل المعدات، أو روتين خدمات التوقف، أو خيط متزامن يجري على أساس آخر.

  • Emit a load instruction] from the variable’s memory address every time the variable is read in source code (no caching in registers across reads).
  • ]Emit a store instruction] to the memory address every time the variable is written (no omission or reordering of writings).
  • Preserve the exact sequence] of accesses to that variable as written in the source, relative to other accesses (though not necessarily relative to non-]] accesses, which is a common misconception).

وهذه الضمانات هي ما يلزم بالضبط عندما يجب أن يتفاعل برنامج من نوع جيم مع سجلات الأجهزة المصممة للذاكرة التي تغير الدولة استنادا إلى أحداث خارجية، مثلا، يمكن أن يشير سجل مركز UART إلى أن نقطة التخرج جاهزة للقراءة، ولكن المجمّع قد يحسن إلى الحد الأمثل من حلقة الاقتراع التي تسجل، على افتراض أن القيمة لا تتغير أبدا.

How Compiler Optimization Creates Problems

(ج) تطبيق المجمّعين الحديثين (مجلس القيادة، كلانغ، معهد البحوث الزراعية، مجمع إدارة المخاطر المؤسسية) على التفسيرات الصارخة مثل التكاثر المستمر، والقضاء على الشفرة الميتة، والحركة الشفرة غير المشبوهة، والتخصيص، والنظر في حلقة الاقتراع البرئية هذه:

int *flag = (int *)0x20000000;
while (*flag == 0) {
 // wait for hardware
}

وبدون ، قد يقوم المجمّع بتحليل الجسم المُحدّث، ويلاحظ أن ] لم يكتب أبداً داخل الحلقة، ويمكنه بعد ذلك أن يُرفع حمولة قبل الحلقة، ويقارنها برقم الصفر مرة واحدة، ويُولّد حلقة لا نهائية لا تدقّد عنوان الأجهزة الفعلية مرة أخرى، وأن السلوك سليم وفقاً للجهاز المُجريدُ

ويدفع الإعلان باعتباره المجمّع إلى إصدار حمولة جديدة على كل تمزق، بما يكفل أن البرنامج يرى حالة المعدات الفعلية.

When and where to use the Keyword

ينبغي تطبيق كلمة الرئيسية في أي حالة يمكن فيها تعديل المتغير بواسطة طرف فاعل مستقل خارج نطاق الخيط الحالي (أو طريق التنفيذ الرئيسي) وتشمل حالات الاستخدام الكلاسيكية ما يلي:

  • سجلاّت /أو مدمجة حسب الذاكرة (سجلات مرجعية)
  • الفروق بين ممثلي الموظفين الدوليين والدائرة الرئيسية
  • Variables accessed by multiple threads in bare-metal or RTOS environment (with caution — alone does not provideذرity)
  • المتغيرات العالمية التي عدلتها عمليات نقل الألغام المضادة للأفراد
  • متعهدو الإشارات في بيئات شبيهة بـ (بوستاس)

سجلا قياسيا مصنّفا حسب الأصول

وهذه هي أكثر الحالات شيوعاً في مجال الاستخدام في نظام C. Most microcontrollers map peripheral control and status registers into the processor’s memory address space. For example, on an ARM Cortex-M MCU, the GPIO output data register might live at address . Accessing it through a pointer cast to ensure updates that every level read state

#define GPIOA_ODR ( (volatile uint32_t *) 0x40020014 )
#define GPIOA_IDR ( (volatile uint32_t *) 0x40020010 )

void toggle_led(void) {
 *GPIOA_ODR ^= (1 << 5); // toggle bit 5 – compiler will generate a load-modify-store
}

وبدون ، يمكن للمجمع أن يجمع بين عدة كتب أو إعادة ترتيبها، مما يسبب جروحا أو إخفاقات صامتة.

متغيرات مصحوبة بمخططات الخدمة المشتركة بين الفئتين

وعندما يستكمل معهد الحقوقيين الدولي متغيرا عالميا قرأه الفريق الرئيسي، يجب أن يكون الوصلان معا مؤهلين، مثال نموذجي: زيادة منضدة للدغات، أو وضع علم للحدث، أو ملء حاجز من مفاعل UART ISR.

volatile uint32_t system_tick = 0;

void SysTick_Handler(void) {
 system_tick++; // ISR modifies this
}

void main_loop(void) {
 while (1) {
 uint32_t current_tick = system_tick; // main loop reads
 // ...
 }
}

If were not , the compilationr might cache its value in a register inside , never seeing the increments done by the ISR. Using forces the main cycle to fetch the latest value from memory each time.

DMA and Shared Memory

يمكن لمراقبي الوصول إلى الذاكرة المباشرة أن ينسخوا البيانات بين الظواهر والذاكرة دون تدخل من وحدة منع الحمل، ومن النمط النموذجي ما يلي:

  1. The CPU sets up a DMA transfer to fill a buffer from an ADC.
  2. جهاز التحكم في وزارة الدفاع يكتب البيانات في عازلة الذاكرة.
  3. ويقرأ الاتحاد أن الحاجز بعد اكتمال النقل (يُقرع العلم أو يُستخدم فيه انقطاع).

وإذا أُعلن أن العازل هو صفيفة بسيطة، يجوز للمجمع أن يقرأ على النحو الأمثل، معتقداً أن البيانات لم تُكتب قط من قبل وحدة منع الجريمة والعدالة الجنائية، ويجب إعلان العازل [أو استخدام مؤشر ] لضمان أن يقرأ وحدة مكافحة الإرهاب القيم الفعلية التي كتبتها إدارة الشؤون الإنسانية.

مثال: بولينج سجل مركز هدرواير

فلنوسع المثال الأصلي إلى سيناريو أكثر واقعية - في انتظار إتمام معاملة خاصة بتسهيلات الأوراق المالية عن طريق قراءة سجل للوضع.

// Memory-mapped SPI peripheral registers
typedef struct {
 volatile uint32_t CR; // control register
 volatile uint32_t SR; // status register
 volatile uint32_t DR; // data register
} SPI_TypeDef;

#define SPI1_BASE 0x40013000
#define SPI1 ((SPI_TypeDef *) SPI1_BASE)

void spi_send_byte(uint8_t data) {
 // Wait until transmit buffer empty (bit 1 in SR set)
 while ( !(SPI1->SR & (1 << 1)) ) {
 // busy wait
 }
 // Write data to data register
 SPI1->DR = data;
 // Wait for transmission to complete (bit 7 in SR set)
 while ( !(SPI1->SR & (1 << 7)) ) {
 // busy wait
 }
}

لأن يُعلن ] داخل الهيكل، كل قراءة من ] تمس بالفعل عنوان المعدات، وبدون ، يمكن أن تكون الحلقة الأولى على النحو الأمثل للحلقة النهائية أو الثانية قد تُستغل بالكامل - كارثة للاتصال.

ما بعد : الروايات والحدود المشتركة

The keyword is powerful but it is often misunderstood. Several important limitations must be recognized:

عدم وجود ضمانات للطاقة الذرية

] does not] make reads or writes nuclear. On a 32-bit ARM processor, reading a 32-bit ] is typicallytom, but reading a 64-bit value might not be. For multi-byifi reads on an 8-bit MCU, the compilationr41 value guarantee multiple load

لا ضمانات أوامر الذاكرة

does not prevent the compilationr or CPU from reordering non-] accesses around ] accesses. The C standard only specifies that accesses to the same object are not reordered with respect to each other. For multi-core or weakly-ordered structures (e.

ليس بديلاً عن التواؤم السليم

وفي البيئات المتعددة الطبقات )المكتب الإقليمي أو برنامج العمل البيئي( ](FLT:48[ غير كاف للمتغيرات المشتركة، ويمكن للخيوط المتعددة أن تقرأ وتكتب نفس المتغير، ودون أن تتزامن على الوجه الصحيح )الآداب، أو السايمافي، أو العمليات الذرية(، لا تزال تستطيع الحصول على ظروف عرقية والآراء غير المتسقة للذاكرة. ]الكتابة: ٤٩[ فقط ضمان عدم قيام المجم ِّع بتأليف بقراءة.

When not] to Use

ويغري هذا الرش ][ لكل متغير عالمي " في حالة حدوث ذلك " ، ولكنه يؤدي إلى نتائج عكسية، ويمنع الإفراط في استخدام المجمّع من أن يُحدِد قواعد قانونية، ويُغمِر دورات وصول الذاكرة، ويمكن أن يخفي مشاكل التصميم الحقيقية.

  • المتغيرات التي لا تقرأ أو تكتب إلا في خبأ واحد دون تعديل خارجي.
  • (ب) حلقات حاسمة للأداء حيث لا يتأثر المتغير بمعدات أو بمراجعي حسابات دولية.
  • كبديل للعمليات الذرية السليمة عندما تكون هناك وحدات متعددة من وحدات احتواء المكلورة أو سياقات قابلة للمقاطعة.
  • وفيما يتعلق بالمتغيرات المستخدمة مع - يعني الجسم أن البرمجيات لا يمكن أن تعدلها، ولكن يمكن للمعدات (مثل سجل الأحوال فقط) أن تكون نمطا صالحا ولكن يجب فهمه.

القانون العالمي الحقيقي: UART RX مع المقاطعتين و Ping-Pong Buffers

(أ) النظر في جهاز استقبال يستعمل الضم المزدوج، ويكتب المعهد البوليسترات في شاحنات واحدة بينما يُعمل في الدائرة الرئيسية الأخرى، ويجب أن يكون العلم الذي يُبدل العوازل :

#define BUF_SIZE 64
volatile char buffer_a[BUF_SIZE];
volatile char buffer_b[BUF_SIZE];
volatile int active_buffer = 0; // 0 = buffer A, 1 = buffer B
volatile int bytes_received = 0;

void UART_IRQHandler(void) {
 char data = UART->DR; // hardware register
 if (active_buffer == 0) {
 if (bytes_received < BUF_SIZE) {
 buffer_a[bytes_received++] = data;
 }
 } else {
 if (bytes_received < BUF_SIZE) {
 buffer_b[bytes_received++] = data;
 }
 }
}

int main(void) {
 while (1) {
 if (bytes_received > 0) {
 // Process data from active_buffer
 // Swap buffers after processing
 int current_buf = active_buffer;
 char *data_ptr = (current_buf == 0) ? buffer_a : buffer_b;
 int count = bytes_received;
 // ... process data_ptr[0..count-1] ...
 // Reset and switch
 bytes_received = 0;
 active_buffer = current_buf ^ 1;
 }
 }
}

All buffers and the control variables are so that the main cycle sees the latest data written by the ISR. Note: Even here, there is a risk of the main cycle reading while the ISR is updating it — but on a single-core MCU with a single-threaded main cycle and interrupts that can fire at any time, [FLT combined59]

الاعتبارات المتعلقة بالجمعيات والمؤهلات

ويمكن لمجمعين مختلفين أن يعاملوا ] معاملة مختلفة اختلافاً طفيفاً في الحالات الحادة، ويحدد المعيار جيم (المادة 6-7-3 من المادة 11) الحد الأدنى من المتطلبات، ولكن يمكن للمجمعين أن يقدموا ضمانات أقوى أو أضعف:

  • GCC/Clang:] Treat as per the standard; they do not reorder ]] accesses with each other but may reorder non- around them. Use and
  • IAR Embedded Workbench:] Provides additional semantics: by default, all accesses to ] objects are treated astom for the size of the object (up to 32 bits) and ordering is preserved. This can be dangerous if you rely on weak ordering.
  • ARM Compiler (armcc): ] Similar to GCC.
  • MSVC:] Historically, MSVC gave ] acquire/release semantics for reads and writes, but starting with VS 2015, standard conformance mode ()) remove those ordering guarantees. Use to retain legacy behavior.

دائماً ما تتشاور مع وثائق مجمّعيكم وتختبروا التجمع المولد عندما يكون السلوك الصحيح حاسماً.

البدائل والنُهج الحديثة

While ] remains essential for equipment registers and ISR communication, some use cases are better served by newer language features:

Use Case Recommended Tool
Reading/writing memory-mapped I/O volatile qualified pointer
Variable shared between ISR and main loop (single core) volatile + disabling interrupts when accessing multi-word variables
Variable shared between multiple threads (SMP, RTOS) _Atomic (C11) or compiler intrinsics + memory barriers
Flag or status bit touched by both threads and ISRs stdatomic.h with atomic_flag or atomic_int
DMA buffers written by peripheral, read by CPU volatile qualified pointer (or ensure compiler doesn’t optimize via proper barriers)

وفي الفئة " جيم+ " ، يوفر نموذج كل من الذرة وطلب الذاكرة، غير أنه بالنسبة لسجلات المعدات، لا يزال النمط الموحد - C++20 لا يحل محله بالنسبة للذاكرة المدمجة في I/O.

الأخطاء المشتركة وكيفية تجنبها

ننسى استخدام على النقاط إلى البرمجيات الصلبة

A common error is to declare a pointer to a equipment register without qualifying the pointed to type as :

int *reg = (int *)0x40000000; // WRONG – not volatile
while (*reg == 0) ; // may be optimized

صحيح:

volatile int *reg = (volatile int *)0x40000000; // read volatile-qualified

Declaring the Pointer It themselves as

وإذا أردتم تعديل العنوان المرشد بواسطة أجهزة (معالجة)، فيمكنكم استخدام [()] - ولكن هذا يعني أن المتغير المرجعي يمكن أن يتغير، وليس البيانات التي يشير إليها.

استخدام قسم حرج دون انحراف

يفترض أن لديك جهازاً من طراز () من طراز 64-bit على وحدة من وحدات التصنيع العسكري ذات المدارات الثماني، وتقرأه الحلقة الرئيسية على ارتفاعها وانخفاضها، ويمكن أن يستكمل معهد الإنقاذ القيمة بين قراءتي البقايا، مما يعطي قيمة فاسدة. ] لا يساعد هنا - يجب أن تقطع عن طريق قراءة أو استخدام آلية دخول ذرية.

موجز

إن كلمة " FLT:91 " الرئيسية في C هي أداة أساسية لضمان التفاعل الصحيح في الأجهزة المدمجة، وهي تمنع المجمّع من الإبعاد الأمثل للقراءة والكتابة إلى مواقع الذاكرة التي يمكن تغييرها عن طريق أحداث خارجية، سواء كانت سجلات الأجهزة، أو روتينات الخدمة المقطعية، أو أجهزة التحكم في الـ دي إم آي، غير أن ليست رصاصة فضية:

وبالنسبة لأي مطور مدمج، فإن التأقلم ]FLT:93[ هو طقوس المرور، إذ أنه يتكون من فهم قوي لسلوك المجم ِّع وخريطة الذاكرة الحاسوبية ونموذج الذاكرة في الهيكل، وسوف تتجنبون صنفا كاملا من الإخفاقات الخفية والصعبة التلف.

المزيد من القراءة