طرق تحسين الكود ل Redd. الجزء 2: ذاكرة غير قابلة للتخزين المؤقت وتشغيل الحافلة الموازية

في المقالة الأخيرة ، اكتشفنا أن ذاكرة التخزين المؤقت هي بالتأكيد شيء مفيد ، ولكن فيما يتعلق بمنطق وحدة التحكم ، فإنها تخلق أحيانًا صعوبات. على وجه الخصوص ، يقدم عدم إمكانية التنبؤ بفترات النبض أو التأخيرات الأخرى في التكوين البرنامجي للرسوم البيانية الزمنية. حسنًا ، وفي الخطة "العامة للبرنامج" ، يمكن أن يؤدي ضعف موقع الوظيفة إلى تقليل الكسب من ذاكرة التخزين المؤقت إلى لا شيء ، مما يؤدي باستمرار إلى إعادة تشغيله من الذاكرة البطيئة. ذكرت أنه قبل 15 عامًا ، كان علينا أن نجعل معالجًا خاصًا يعمل على إصلاح المشكلات التي نشأت لمعالج SPARC-8 ، ووعدت بإخبار كم سيكون من السهل حل هذه الصعوبات عند تطوير معالج Nios II تم توليفه للاستخدام في حزمة Redd. لقد حان الوقت للوفاء بالوعد.



المقالات السابقة في السلسلة:


  1. تطوير أبسط "البرامج الثابتة" FPGAs المثبتة في Redd ، وتصحيح الأخطاء باستخدام اختبار الذاكرة كمثال.
  2. تطوير أبسط "البرامج الثابتة" لأجهزة FPGA المثبتة في Redd. الجزء 2. رمز البرنامج.
  3. تطوير جوهرها الخاص لتضمينها في نظام المعالجات المستندة إلى FPGA.
  4. تطوير برامج للمعالج المركزي Redd على سبيل المثال من الوصول إلى FPGA.
  5. أول تجارب باستخدام بروتوكول الدفق على مثال اتصال وحدة المعالجة المركزية والمعالج في FPGA لمجمع Redd.
  6. ميلاد سعيد Quartusel ، أو كيف أن المعالج قد حان لهذه الحياة.
  7. طرق تحسين الكود ل Redd. الجزء 1: تأثير مخبأ.

اليوم ، سيكون كتابنا المرجعي هو كتيب التصميم المضمن ، أو بالأحرى قسمه 7.5. استخدام ذاكرة مزدوجة بإحكام مع البرنامج التعليمي لمعالج Nios II . القسم نفسه ملون. اليوم نقوم بتصميم أنظمة المعالجات Intel FPGAs في برنامج Platform Designer. في أيام Altera ، كان يطلق عليه QSys (وبالتالي ملحق .qsys لملف المشروع). ولكن قبل ظهور QSsys ، استخدم الجميع سلفه ، SOPC Builder (في ذاكرته ، تم ترك امتداد ملف .sopcinfo ). لذلك ، على الرغم من أن الوثيقة تحمل شعار Intel ، إلا أن الصور الموجودة بها هي لقطات شاشة من SOPC Builder. تمت كتابته بوضوح قبل أكثر من عشر سنوات ، ومنذ ذلك الحين تم تصحيح المصطلحات فقط فيه. صحيح أن النصوص حديثة تمامًا ، لذا فإن هذه الوثيقة مفيدة جدًا كدليل تدريبي.

تجهيز المعدات


هكذا. نريد إضافة ذاكرة إلى نظام معالج Spartan الخاص بنا ، والذي لا يتم تخزينه مؤقتًا في نفس الوقت ويعمل بأعلى سرعة ممكنة. بالطبع ، ستكون هذه هي ذاكرة FPGA الداخلية. سنضيف ذاكرة لكل من الكود والبيانات ، لكن هذه ستكون كتل مختلفة. لنبدأ بذاكرة البيانات كأبسطها. نضيف ذاكرة OnChip المعروفة بالفعل إلى النظام.



حسنًا ، دعنا نقول أن حجمه سيكون 2 كيلو بايت (المشكلة الرئيسية في الذاكرة الداخلية لـ FPGA هي أنه صغير ، لذا عليك حفظه). الباقي هو ذاكرة عادية ، والتي أضفناها بالفعل.



لكننا لن توصيله إلى ناقل البيانات ، ولكن إلى حافلة خاصة. لكي تظهر ، نذهب إلى خصائص المعالج ، ونذهب إلى علامة التبويب Caches and Memory Interfaces وفي قائمة التحديد ، حدد عدد منافذ البيانات الرئيسية المحسوبة بإحكام ، القيمة 1.



إليك منفذ جديد للمعالج:



نحن في الآونة الأخيرة توصيل كتلة الذاكرة المضافة حديثا لذلك!



حيلة أخرى تتمثل في تعيين عناوين لهذه الذاكرة الجديدة. يحتوي المستند على سلسلة طويلة من التفكير حول أمثلية فك ترميز العنوان. ينص على أنه يجب التمييز بين الذاكرة غير المخزنة مؤقتًا وجميع أنواع الذاكرة الأخرى بواسطة جزء واحد من العنوان يتم التعبير عنه بوضوح. لذلك ، في المستند ، ينتمي كافة الذاكرة غير القابلة للتخزين المؤقت إلى النطاق 0x2XXXXXXX. لذا ، أدخل العنوان 0x2000000 يدويًا وقفله حتى لا يتغير مع المهام التلقائية التالية.



حسنًا ، ولأغراض جمالية بحتة ، أعد تسمية الكتلة ... دعنا نسميها ، على سبيل المثال ، NonCachedData .



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


لاحظ أن المستند لا يفسر السبب ، ولكن تجدر الإشارة إلى أنه حتى مع ذاكرة المنفذ المزدوج ، يمكن توصيل منفذ واحد فقط بسيد رئيسي غير مرفق. يجب أن تكون متصلا الثاني إلى المعتاد.
لنقم بإضافة 8 كيلو بايت من الذاكرة ، وجعلها ثنائية المنفذ ، واترك الباقي افتراضيًا:



أضف منفذ تعليمة غير قابل للتخزين المؤقت إلى المعالج:



نحن نسمي الذاكرة NonCachedCode ، وصل الذاكرة إلى الحافلات ، عيّنها العنوان 0x20010000 وقفلها (لكلا المنفذين). المجموع ، لقد حصلنا على شيء مثل هذا:



هذا كل شيء. نحن حفظ وتوليد النظام ، وجمع المشروع. الجهاز جاهز. نمر إلى جزء البرنامج.

تحضير BSP في جزء البرنامج


عادة ، بعد تغيير نظام المعالج ، ما عليك سوى اختيار عنصر قائمة إنشاء BSP ، ولكن اليوم يتعين علينا فتح محرر BSP. نظرًا لأننا نادرًا ما نفعل هذا ، اسمحوا لي أن أذكرك بموقع عنصر القائمة المقابل:



هناك نذهب إلى علامة تبويب رابط . نرى أننا أضفنا المناطق التي ترث أسماء من كتل RAM:



سأوضح كيفية إضافة قسم سيتم وضع الكود فيه. في قسم القسم ، انقر فوق "إضافة":



في النافذة التي تظهر ، أعطِ اسم القسم (لتجنب حدوث تشويش في المقالة ، سأسميها تمامًا على عكس اسم المنطقة ، أي nccode) وأقرها بالمنطقة (قمت بتحديد NonCachedCode من القائمة):



هذا كل شيء ، توليد BSP وإغلاق المحرر.

وضع رمز في قسم ذاكرة جديد


واسمحوا لي أن أذكرك بأن لدينا وظيفتين في البرنامج موروثة من المقالة السابقة: MagicFunction1 () و MagicFunction2 () . في التمريرة الأولى ، حمل كلاهما أجسادهم في ذاكرة التخزين المؤقت ، والتي كانت مرئية على الذبذبات. علاوة على ذلك - اعتمادًا على الموقف في البيئة ، فقد عملوا إما بأقصى سرعة أو فرك بعضهم البعض باستمرار بأجسادهم ، مما أدى إلى تنزيلات مستمرة من SDRAM.

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

void MagicFunction1()__attribute__ ((section("nccode"))); void MagicFunction1() { IOWR (PIO_0_BASE,0,1); IOWR (PIO_0_BASE,0,0); ... 

نقوم بإجراء الجولة الأولى من تكرار واحد للحلقة (أضع نقطة توقف على خط الوقت):
  while (1) { MagicFunction1(); MagicFunction2(); } 

نرى النتيجة التالية:



كما ترى ، يتم تنفيذ الوظيفة الأولى بالفعل بأقصى سرعة ، ويتم تحميل الوظيفة الثانية من SDRAM. تشغيل المدى الثاني:



تعمل كلتا الوظيفتين بأقصى سرعة. الوظيفة الأولى لا تفريغ الثانية من ذاكرة التخزين المؤقت ، على الرغم من حقيقة أن بينهما هو الملحق الذي تركته بعد كتابة المقال الأخير:

 volatile void FuncBetween() { Nops256 Nops256 Nops256 Nops64 Nops64 Nops64 Nops16 Nops16 } 

لم يعد هذا الإدراج يؤثر على الموضع النسبي للوظيفتين ، حيث غادر أولهما في مساحة مختلفة تمامًا من الذاكرة.

بضع كلمات عن البيانات


وبالمثل ، يمكنك إنشاء قسم من البيانات غير المخزنة مؤقتًا ووضع متغيرات عمومية هناك ، مع تعيين نفس السمة لها ، ولكن لتوفير مساحة ، لن أقدم مثل هذه الأمثلة.

لقد أنشأنا منطقة لمثل هذه الذاكرة ، يمكن إجراء التعيين إلى القسم بنفس طريقة إنشاء قسم الكود. يبقى فقط لفهم كيفية تعيين السمة المقابلة لمتغير. فيما يلي المثال الأول لإعلان مثل هذه البيانات الموجودة في أحشاء الكود الذي تم إنشاؤه تلقائيًا:

 volatile alt_u32 alt_log_boot_on_flag \ __attribute__ ((section (".sdata"))) = ALT_LOG_BOOT_ON_FLAG_SETTING; 

ماذا يعطينا؟


حسنًا ، في الواقع ، من الأشياء الواضحة: الآن يمكننا وضع الجزء الرئيسي من الشفرة في SDRAM ، وفي القسم غير القابل للتخزين المؤقت يمكننا وضع تلك الوظائف التي تشكل مخططات زمنية برمجية ، أو يجب أن يكون أداؤها الحد الأقصى ، مما يعني أنه لا ينبغي أن يتباطأ بسبب أن بعض الوظائف الأخرى تفريغ باستمرار رمز المقابلة من ذاكرة التخزين المؤقت.

نلقي نظرة فاحصة على الإطارات.


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



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

ذات مرة ، في إحدى الجامعات في دورة تدريبية حول معالجات الإشارات ، علمنا فن استخدام حافلتين بالتوازي مع فريق واحد. بقدر ما أعرف ، في وحدات التحكم ARM الحديثة ، المعرفة التفصيلية لمصفوفة الناقل يسمح أيضا التحسين. ولكن كل هذا أمر جيد عندما يكون هناك مطور يعمل مع نفس النظام لسنوات. إذا كان عليك ركوب قطع مختلفة تمامًا من الأجهزة من مشروع إلى آخر ، فلا يمكنك حفظ كل شيء. في حالة FPGAs ، نحن لا ندرس ميزات البيئة ، نحن أحرار في تخصيص البيئة لأنفسنا.
فيما يتعلق بالنهج "لا نقضي وقتًا طويلاً في التنمية" ، يبدو الأمر كما يلي:
لا نحتاج إلى بذل جهود لتحسين استخدام الإطارات القياسية الجاهزة ، يمكننا وضعها بسرعة بالطريقة المثلى لحل المهمة ، والانتهاء بسرعة من هذا التطوير المساعد وضمان سرعة تصحيح الأخطاء أو اختبار المشروع الرئيسي بسرعة.
دعنا نلقي نظرة على مثال على تضمين كتلة DMA من دليل مستخدم Embedded Peripherals IP لدمج المواد.



نرى ثلاثة اتصالات مستقلة. إدخال البيانات (في هذا الشكل ، يتم عرض ناقل على الذاكرة) ، وإخراج البيانات (في هذا الشكل هو نوع مختلف تمامًا من ناقل - واجهة دفق) والتواصل مع معالج التحكم. لا أحد يزعج أن يربط كل شيء بحافلات مختلفة ، ثم سيتم العمل بالتوازي. سيتم إدخال بيانات الإدخال (على سبيل المثال ، من SDRAM) في دفق واحد ، لا يتعارض معها أحد ؛ سيتم نقل الإخراج في دفق مختلف ، على سبيل المثال ، إلى قناة FT245-FIFO ، التي نظرنا فيها بالفعل ؛ ولن يأكل المعالج المركزي بعيدًا عن حافلات الساعة هذه ، نظرًا لأن الحافلة الرئيسية معزولة. على الرغم من أنه في هذه الحالة ، بطبيعة الحال ، فإن الذاكرة في SDRAM ، كونها في حافلة منفصلة ، ستكون غير متوفرة برمجياً. لكن لا أحد سيمنعها من قراءتها بواسطة DMA. إذا كان الهدف هو تحقيق أداء عالٍ باستخدام المخزن المؤقت ، فيجب تحقيقه بأي ثمن. ما لم يكن البرنامج بأكمله مضمنًا في الذاكرة المدمجة في FPGA ، نظرًا لعدم وجود وحدات تخزين أخرى في أجهزة Redd.

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

  • العبد هو دائما واحد على متن الحافلة ؛
  • لا يستخدم العبد آلية تأخير الحافلة ؛
  • كتابة الكمون هو دائمًا صفر ؛ قراءة زمن الوصول دائمًا واحد.

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

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

استنتاج


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

لإنهاء الموضوع ، لا يزال يتعين علينا معرفة كيفية رفع تردد ساعة النظام (الآن يقتصر على نبضات ساعة توليد المكون لرقاقة SDRAM). ولكن بما أن المقالات تتبع مبدأ "شيء واحد - مقال واحد" ، فسنفعل ذلك في المرة القادمة.

Source: https://habr.com/ru/post/ar468027/


All Articles