باستخدام أداة تكوين Datapath



علينا أن نتخذ الخطوة قبل الأخيرة في التطوير العملي للعمل مع UDB. اليوم لن نطور باستخدام محرر UDB الآلي ، ولكن في الوضع شبه اليدوي باستخدام Datapath Config Tool. هناك مساعدة جيدة للغاية في إتقان هذه الأداة وهي AN82156 - PSoC 3 و PSoC 4 و PSoC 5LP - تصميم مكونات منشئ PSoC باستخدام قواعد بيانات UDB. في الواقع ، لقد درست ذلك بنفسي.

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

  • تنظيم المدخلات والمخرجات المتوازية للبيانات ؛
  • تنظيم إدارة FIFO الديناميكية ؛
  • تنفيذ معكوس إشارة الساعة FIFO ؛
  • تنفيذ وظيفة اتفاقية حقوق الطفل ؛
  • تنفيذ وظيفة استراتيجية الحد من الفقر ؛
  • تنفيذ اختيار النقل الوارد ؛
  • تنفيذ الترحيل الداخلي الديناميكي.

من نفسي ، سأضيف أنني لم أجد كيفية تنفيذ التقليب من يقضم في UDB Editor.

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

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

ضع في اعتبارك نظام تبديل العرض المأخوذ من reprap.org (لم يكن هذا سهلاً ، يحظر مزودي هذا الموقع ، بالإضافة إلى عدد من المواقع التقنية الأخرى ، مما يحفزه على أنه يعيش على نفس عنوان IP الذي يحظره شخص ما).



تخطيط كبير! أولاً ، لست مضطرًا للتفكير في القراءة: لا يمكن كتابة البيانات الموجودة على شاشة LCD (خط R / W مؤرض وغير متاح على الموصل). ثانياً ، تأتي البيانات بتنسيق 4 بت ، مما يعني أنه لا يمكننا حل الخرج الموازي فحسب ، بل أيضًا التحقق من تشغيل وظيفة التقليب nibble.

إنشاء المشروع


لذلك ، قم بتشغيل PSoC Creator وحدد File-> New-> Project :



بعد ذلك ، اخترت اللوح الخاص بي:



التالي هو المخطط الفارغ:



سأتصل بمشروع LCDTest2 :



الآن ، كما كان من قبل ، انتقل إلى علامة التبويب المكونات :



وبعد تحديد المشروع ، اضغط على زر الماوس الأيمن ، ثم حدد " إضافة عنصر مكون" .



وهنا عليك اختيار معالج الرموز . إعطاء اسم ... حسنا ، دعنا نقول LCD4bit .



قمت بتعيين المنافذ التالية للرمز:



clk هو مدخلات الساعة. المنافذ التي تحتوي على بادئة LCD هي منافذ LCD قياسية. جائع - مخرجات تخبر وحدة DMA أن هناك مساحة حرة في FIFO ، تمت مناقشة الفكرة في مقال حول التحكم في RGB LEDs . انقر فوق "موافق" للحصول على الحرف.



الآن ، بناءً على هذا الرمز ، يجب إنشاء قالب Verilog. انقر فوق زر الماوس الأيمن في جوار الرمز وحدد إنشاء Verilog في قائمة السياق.



حصلنا على القالب الموضح في الشكل أدناه (في النموذج النصي ، ليس له أي معنى حتى الآن):



لقد أنشأنا وحدة وبعض الأقسام. لكنهم لم يخلقوا Datapath حتى الآن. لإضافته ، انتقل إلى شجرة المشروع ، وحدد ملف LCD4bit.v ، واضغط على زر الماوس الأيمن وحدد أداة تكوين Datapath في قائمة السياق التي تظهر:



يتم فتح نافذة أمامنا ، والتي سأعرضها الآن جزئيًا فقط:



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



أي خيار للاختيار في مربع الحوار الذي يظهر؟



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



انقر فوق موافق وأغلق أداة تكوين Datapath الآن ، والموافقة على حفظ النتيجة. سوف نعود هنا في وقت لاحق.

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



وسوف نحكم في نهاية وصف Datapath. موقعنا يشبه هذا
(من هذه النقطة ، من المنطقي إحضار كل شيء في شكل نص).
)) LCD_DP( /* input */ .reset(1'b0), /* input */ .clk(1'b0), /* input [02:00] */ .cs_addr(3'b0), /* input */ .route_si(1'b0), /* input */ .route_ci(1'b0), /* input */ .f0_load(1'b0), /* input */ .f1_load(1'b0), /* input */ .d0_load(1'b0), /* input */ .d1_load(1'b0), /* output */ .ce0(), /* output */ .cl0(), /* output */ .z0(), /* output */ .ff0(), /* output */ .ce1(), /* output */ .cl1(), /* output */ .z1(), /* output */ .ff1(), /* output */ .ov_msb(), /* output */ .co_msb(), /* output */ .cmsb(), /* output */ .so(), /* output */ .f0_bus_stat(), /* output */ .f0_blk_stat(), /* output */ .f1_bus_stat(), /* output */ .f1_blk_stat(), /* input */ .ci(1'b0), // Carry in from previous stage /* output */ .co(), // Carry out to next stage /* input */ .sir(1'b0), // Shift in from right side /* output */ .sor(), // Shift out to right side /* input */ .sil(1'b0), // Shift in from left side /* output */ .sol(), // Shift out to left side /* input */ .msbi(1'b0), // MSB chain in /* output */ .msbo(), // MSB chain out /* input [01:00] */ .cei(2'b0), // Compare equal in from prev stage /* output [01:00] */ .ceo(), // Compare equal out to next stage /* input [01:00] */ .cli(2'b0), // Compare less than in from prv stage /* output [01:00] */ .clo(), // Compare less than out to next stage /* input [01:00] */ .zi(2'b0), // Zero detect in from previous stage /* output [01:00] */ .zo(), // Zero detect out to next stage /* input [01:00] */ .fi(2'b0), // 0xFF detect in from previous stage /* output [01:00] */ .fo(), // 0xFF detect out to next stage /* input [01:00] */ .capi(2'b0), // Software capture from previous stage /* output [01:00] */ .capo(), // Software capture to next stage /* input */ .cfbi(1'b0), // CRC Feedback in from previous stage /* output */ .cfbo(), // CRC Feedback out to next stage /* input [07:00] */ .pi(8'b0), // Parallel data port /* output [07:00] */ .po() // Parallel data port ); 


مخيف الآن سنكتشف ما هو - سوف يتوقف عن أن يكون مخيفًا. في الواقع ، هناك ثلاث مجموعات متميزة في هذا النص. دعونا نتذكر ترجمة الوثائق. كيف شكل datapath في الصورة؟ سأشير على الفور في الشكل إلى الأماكن التي تنتمي إليها المجموعات "1" و "2" و "3".



في الواقع ، فإن أول مجموعة من المنافذ في كود فيريلوج هي المدخلات. قارن بين الأسماء عند إخراج معدد الإدخال ("1" في الشكل) وأسماء الإشارات في الكود.

الآن جميع المدخلات هي صفر. سيتعين علينا توصيل مدخلات الساعة ويمكننا إعادة توجيه ما يصل إلى ستة خطوط إدخال ، كما حدث في محرر UDB. هذه المدخلات هي:

  /* input */ .reset(1'b0), /* input */ .clk(1'b0), /* input [02:00] */ .cs_addr(3'b0), /* input */ .route_si(1'b0), /* input */ .route_ci(1'b0), /* input */ .f0_load(1'b0), /* input */ .f1_load(1'b0), /* input */ .d0_load(1'b0), /* input */ .d1_load(1'b0), 

المجموعة الثانية هي المخارج. تتزامن الأسماء في الكود أيضًا مع أسماء مدخلات معدد الإخراج "2":

  /* output */ .ce0(), /* output */ .cl0(), /* output */ .z0(), /* output */ .ff0(), /* output */ .ce1(), /* output */ .cl1(), /* output */ .z1(), /* output */ .ff1(), /* output */ .ov_msb(), /* output */ .co_msb(), /* output */ .cmsb(), /* output */ .so(), /* output */ .f0_bus_stat(), /* output */ .f0_blk_stat(), /* output */ .f1_bus_stat(), /* output */ .f1_blk_stat(), 

فقط أنواع Datapath المعينة لديها المجموعة الثالثة (الأنواع الأخرى ليس لديها أي ، لذلك ، لا توجد بيانات موازية). هذه هي إشارات Datapath الداخلية التي يمكنك من خلالها السلسلة بشكل مستقل أو تنفيذ إجراءات مفيدة أخرى. تتزامن الأسماء في الكود أيضًا مع أسماء الإشارات الداخلية المنتشرة في الشكل. سنقوم من خلال واحد منهم (الأخير في القائمة ، اسمه po ) بإخراج بيانات موازية مباشرة إلى أرجل الشريحة.

  /* input */ .ci(1'b0), // Carry in from previous stage /* output */ .co(), // Carry out to next stage /* input */ .sir(1'b0), // Shift in from right side /* output */ .sor(), // Shift out to right side /* input */ .sil(1'b0), // Shift in from left side /* output */ .sol(), // Shift out to left side /* input */ .msbi(1'b0), // MSB chain in /* output */ .msbo(), // MSB chain out /* input [01:00] */ .cei(2'b0), // Compare equal in from prev stage /* output [01:00] */ .ceo(), // Compare equal out to next stage /* input [01:00] */ .cli(2'b0), // Compare less than in from prv stage /* output [01:00] */ .clo(), // Compare less than out to next stage /* input [01:00] */ .zi(2'b0), // Zero detect in from previous stage /* output [01:00] */ .zo(), // Zero detect out to next stage /* input [01:00] */ .fi(2'b0), // 0xFF detect in from previous stage /* output [01:00] */ .fo(), // 0xFF detect out to next stage /* input [01:00] */ .capi(2'b0), // Software capture from previous stage /* output [01:00] */ .capo(), // Software capture to next stage /* input */ .cfbi(1'b0), // CRC Feedback in from previous stage /* output */ .cfbo(), // CRC Feedback out to next stage /* input [07:00] */ .pi(8'b0), // Parallel data port /* output [07:00] */ .po() // Parallel data port ); 

هكذا. أثناء عملنا ، سيتعين علينا توصيل بعض هذه المدخلات والمخرجات بكياناتنا الخاصة ، والباقي - فقط اتركها بالشكل الذي أنشأناها به.

باستخدام محرر UDB كمرجع


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

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

لذلك ، نبدأ في صنع عنصر مساعد. مع حركة اليد المعتادة ، أضف عنصرًا جديدًا للمشروع:



ستكون هذه وثيقة UDB ، دعنا نسميها UDBhelper :



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





هكذا. تحتاج أولاً إلى ضبط إشارة RS (حيث يتم لحام R / W إلى صفر في الأجهزة). بعد ذلك ، انتظر tAS ، ثم ارفع الإشارة E وقم بتعيين البيانات (إعداد البيانات فيما يتعلق بالحافة الموجبة E غير محدود). يجب أن تكون البيانات على الحافلة لا تقل عن tDSW ، وبعد ذلك يجب إسقاط الإشارة E. يجب أن تبقى البيانات على الحافلة لمدة tDHW على الأقل ، و RS لمدة tAH على الأقل.

RS هو الأمر أو علامة البيانات. إذا كانت RS صفراً ، فسيتم كتابة أمر ، إذا كان أحد ، تتم كتابة البيانات.

أقترح إرسال أوامر من خلال FIFO0 ، والبيانات من خلال FIFO1 . في إطار المهمة الحالية ، هذا لا يتعارض مع أي شيء. عندئذٍ سيكون لدى آلة الحالة المحدودة التي اقترحها لي الشكل التالي:



في حالة الخمول ، لا يزال الجهاز في حالة عدم وجود بيانات FIFO. إذا ظهرت البيانات في FIFO0 ، فستذهب إلى LoadF0 ، حيث ستتلقى في المستقبل البيانات من FIFO0 إلى A0.

أثناء إرسال الأوامر ، لا ينبغي إرسال البيانات. لذلك ، سيكون شرط تلقي البيانات أقل أولوية من شرط تلقي الأوامر.



يتم تلقي البيانات في A1 في الحالة LoadF1 (من FIFO1 ، يمكنهم فقط الانتقال إلى A1 ولا يمكنهم الانتقال إلى التسجيل A0) ، ثم يتم نسخها من A1 إلى A0 في الحالة A1toA0 .

مهما كانت الطريقة التي نذهب بها إلى نقطة التقاء الأسهم ، لدينا بيانات في A0. هم بالفعل الإخراج إلى المنفذ المتوازي. نحن صياح الديك E (في الحالة E_UP1 ) ، إسقاط E (في الولاية E_DOWN1 ). بعد ذلك ، سيكون لدينا حالة للتبادل nibbles ( SWAP ) ، وبعدها يرتفع E مرة أخرى ( E_UP2 ). على هذا ، لقد استنفدت ثماني ولايات يمكن ترميزها في ثلاث بتات. ونتذكر أن ذاكرة الوصول العشوائي للتكوين الديناميكي Datapath لا تحتوي إلا على ثلاثة إدخالات عنوان. يمكن تطبيق بعض الحيل ، لكن المقالة كبيرة بالفعل. لذلك ، في المرة الثانية فقط سوف نسقط E في حالة الخمول . ثم ثماني ولايات كافية تماما بالنسبة لنا.

نضع Datapath أيضًا على الورقة ونعين المدخلات والمخرجات بطريقة مألوفة في المقالات السابقة. وهنا المدخلات:



وهنا المخرجات:



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

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



لتلبية بدقة الإطار الزمني ، اخترت تردد ساعة يساوي ميغاهيرتز واحد. قد يكون من الممكن أخذ تردد وأعلى ، ولكن يتم نقل البيانات عبر أسلاك طويلة في ظروف التداخل العالي ، لذلك من الأفضل أن تأخذ الوقت الكافي لضبط البيانات قبل وبعد البوابة بهامش. إذا كرر شخص ما تجاربي على نفس اللوح - لا تستخدم المنفذ P3.2: مكثف ملحوم لهذه الساق على السبورة. لقد قتلت لمدة نصف ساعة ، حتى اكتشفت السبب في أنني لم أشكِّل حافزًا E ، قمت بالاتصال به لأول مرة هناك. رميت به إلى P3.1 - كل شيء يعمل على الفور. يذهب ناقل البيانات الخاص بي إلى P3.7-P3.4 ، RS يذهب إلى P3.3 ، لذلك انتقل E في الأصل إلى P3.2 ...

حسنا هنا. الآن ، إذا حاولت ترجمة المشروع ، فسنحصل على أخطاء يمكن التنبؤ بها تمامًا



لذا فإن النظام يحاول جمع شيء ما. لكنها لا تزال لديها شيء لجمع. ننتقل لنسخ الرمز. للقيام بذلك ، في محرر UDB ، قم بالتبديل إلى علامة التبويب Verilog (توجد علامة التبويب هذه أسفل النافذة مع ورقة محرر UDB):



ما هو مألوف هناك؟ في نهاية النص ، يوجد النص الأساسي لـ autaton. لنبدأ الترحيل منه.

ضعه أيضًا تحت Datapath:
 /* ==================== State Machine: SM ==================== */ always @ (posedge clock) begin : Idle_state_logic case(SM) Idle : begin if (( !F0empty ) == 1'b1) begin SM <= LoadF0 ; end else if (( !F1empty ) == 1'b1) begin SM <= LoadF1 ; end end LoadF0 : begin if (( 1'b1 ) == 1'b1) begin SM <= E_Up1 ; end end E_Up1 : begin if (( 1'b1 ) == 1'b1) begin SM <= E_Down1 ; end end E_Down1 : begin if (( 1'b1 ) == 1'b1) begin SM <= SWAP ; end end SWAP : begin if (( 1'b1 ) == 1'b1) begin SM <= E_UP2 ; end end E_UP2 : begin if (( 1'b1 ) == 1'b1) begin SM <= Idle ; end end LoadF1 : begin if (( 1'b1 ) == 1'b1) begin SM <= A1toA0 ; end end A1toA0 : begin if (( 1'b1 ) == 1'b1) begin SM <= E_Up1 ; end end default : begin SM <= Idle; end endcase end 


توجد تصريحات في الجزء العلوي من هذا الرمز (أسماء الولايات وسلاسل Datapath ، وهو سجل يشتمل على حالة التشغيل التلقائي). نحن نقلها إلى المناسب
قسم من الكود لدينا:
 /* ==================== Wire and Register Declarations ==================== */ localparam [2:0] Idle = 3'b000; localparam [2:0] LoadF0 = 3'b001; localparam [2:0] LoadF1 = 3'b010; localparam [2:0] E_Up1 = 3'b100; localparam [2:0] A1toA0 = 3'b011; localparam [2:0] E_Down1 = 3'b101; localparam [2:0] SWAP = 3'b110; localparam [2:0] E_UP2 = 3'b111; wire hungry0; wire F0empty; wire hungry1; wire F1empty; wire Datapath_1_d0_load; wire Datapath_1_d1_load; wire Datapath_1_f0_load; wire Datapath_1_f1_load; wire Datapath_1_route_si; wire Datapath_1_route_ci; wire [2:0] Datapath_1_select; reg [2:0] SM; 


حسنا ، و

موقع ربط الإشارة قابل للتحويل:
 /* ==================== Assignment of Combinatorial Variables ==================== */ assign Datapath_1_d0_load = (1'b0); assign Datapath_1_d1_load = (1'b0); assign Datapath_1_f0_load = (1'b0); assign Datapath_1_f1_load = (1'b0); assign Datapath_1_route_si = (1'b0); assign Datapath_1_route_ci = (1'b0); assign Datapath_1_select[0] = (SM[0]); assign Datapath_1_select[1] = (SM[1]); assign Datapath_1_select[2] = (SM[2]); 


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



نفس النص:
 )) LCD_DP( /* input */ .reset(1'b0), /* input */ .clk(clk), /* input [02:00] */ .cs_addr(SM), /* input */ .route_si(1'b0), /* input */ .route_ci(1'b0), /* input */ .f0_load(1'b0), /* input */ .f1_load(1'b0), /* input */ .d0_load(1'b0), /* input */ .d1_load(1'b0), /* output */ .ce0(), /* output */ .cl0(), /* output */ .z0(), /* output */ .ff0(), /* output */ .ce1(), /* output */ .cl1(), /* output */ .z1(), /* output */ .ff1(), /* output */ .ov_msb(), /* output */ .co_msb(), /* output */ .cmsb(), /* output */ .so(), /* output */ .f0_bus_stat(hungry0), /* output */ .f0_blk_stat(F0empty), /* output */ .f1_bus_stat(hungry1), /* output */ .f1_blk_stat(F1empty), 


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

 wire [7:0] tempBus; assign LCD_D = tempBus[7:4]; 

وتوصيله مثل هذا:



نفس النص:
  /* input [07:00] */ .pi(8'b0), // Parallel data port /* output [07:00] */ .po( tempBus) // Parallel data port ); 


نحن نحاول تجميع (Shift + F6 أو من خلال عنصر القائمة Build-> Generate Application ). حصلنا على الخطأ:



لدينا منافذ hungry0 و hungry1 (ظهرت عند إنشاء المكون) ، وكذلك سلاسل بنفس الاسم (ظهرت عند السحب من العينة). فقط قم بإزالة هذه السلاسل (مغادرة المنافذ). وفي مكان ما تسربت إشارة الساعة ، ولدينا هذه الدائرة تسمى clk .

بعد إزالة جميع الدوائر غير الضرورية (تلك التي أرسلت في البداية ثوابت صفرية إلى مدخلات Datapath ، وكذلك hungry0 و hungry1 ) ، نحصل على الكود التالي لبداية ملفنا :

 // Your code goes here /* ==================== Wire and Register Declarations ==================== */ localparam [2:0] Idle = 3'b000; localparam [2:0] LoadF0 = 3'b001; localparam [2:0] LoadF1 = 3'b010; localparam [2:0] E_Up1 = 3'b100; localparam [2:0] A1toA0 = 3'b011; localparam [2:0] E_Down1 = 3'b101; localparam [2:0] SWAP = 3'b110; localparam [2:0] E_UP2 = 3'b111; wire F0empty; wire F1empty; reg [2:0] SM; /* ==================== Assignment of Combinatorial Variables ==================== */ wire [7:0] tempBus; assign LCD_D = tempBus[7:4]; 

وعند استبدال الساعة بـ clk في جسم الماكينة ، سأقوم في الوقت نفسه برمي جميع الخطوط المفيدة للتوليد التلقائي ، لكن مع التحرير اليدوي فقط يخلق البلبلة (جميع المقارنات التي تعطي نتيجة غير مشروطة TRUE وما إلى ذلك). على وجه الخصوص ، في المثال أدناه ، يمكنك شطب نصف الخطوط تقريبًا (وبعضها يبدأ / ينتهي اختياريًا ، وستكون هناك حاجة في بعض الأحيان ، لأننا سنضيف إجراءات ، أبرزتها):



بعد التمشيط وفقًا للمبدأ أعلاه (واستبدال الساعة بـ clk ) ، يبقى مثل هذا الجسم

(لقد أصبح أقصر ، مما يعني أنه من الأسهل قراءته):
 always @ (posedge clk) begin : Idle_state_logic case(SM) Idle : begin if (( !F0empty ) == 1'b1) begin SM <= LoadF0 ; end else if (( !F1empty ) == 1'b1) begin SM <= LoadF1 ; end end LoadF0 : begin SM <= E_Up1 ; end E_Up1 : begin SM <= E_Down1 ; end E_Down1 : begin SM <= SWAP ; end SWAP : begin SM <= E_UP2 ; end E_UP2 : begin SM <= Idle ; end LoadF1 : begin SM <= A1toA0 ; end A1toA0 : begin SM <= E_Up1 ; end default : begin SM <= Idle; end endcase end 


الآن ، أثناء التحويل البرمجي ، يتم إخبارنا بأن دارات LCD_E و LCD_RS غير متصلة.

في الواقع ، هذا صحيح:



لقد حان الوقت لإضافة العمل إلى آلة الدولة. سنستبدل تصريحات المنافذ المطابقة للسلاسل غير المتصلة بـ reg ، نظرًا لأننا سنكتبها في نص الجهاز (هذا هو بناء جملة لغة Verilog ، إذا نكتب ، يجب أن تنقر البيانات ، لذلك نحن بحاجة إلى مشغل ، ويتم تقديمه بواسطة كلمة مفتاحية reg ):


نفس النص:
 module LCD4bit ( output hungry0, output hungry1, output [3:0] LCD_D, output reg LCD_E, output reg LCD_RS, input clk ); 


وملء الجهاز مع الإجراءات. لقد قلت بالفعل المنطق أعلاه عندما كنت أفكر في الرسم البياني لمرحلة انتقال الأوتوماتون ، لذلك سأعرض النتيجة فقط:


نفس النص:
 always @ (posedge clk) begin : Idle_state_logic case(SM) Idle : begin LCD_E <= 0; if (( !F0empty ) == 1'b1) begin SM <= LoadF0 ; LCD_RS <= 0; end else if (( !F1empty ) == 1'b1) begin SM <= LoadF1 ; LCD_RS <= 1; end end LoadF0 : begin SM <= E_Up1 ; end E_Up1 : begin SM <= E_Down1 ; LCD_E <= 1'b1; end E_Down1 : begin SM <= SWAP ; LCD_E <= 1'b0; end SWAP : begin SM <= E_UP2 ; end E_UP2 : begin SM <= Idle ; LCD_E <= 1; end LoadF1 : begin SM <= A1toA0 ; end A1toA0 : begin SM <= E_Up1 ; end default : begin SM <= Idle; end endcase end 


من هذه اللحظة ، يبدأ المشروع في التجمع. لكنه لن يعمل بعد. حتى الآن ، قلت الشهيرة: "في هذه الحالة ، سنقوم بتحميل السجل من FIFO" ، "A1 سيتم نسخه إلى A0 في هذا" ، "سيتم استبدال Nibbles بهذا". بشكل عام ، لقد تحدثت كثيرًا ، لكن حتى الآن لم تحدث أية أعمال. لقد حان الوقت لتحقيقها. ننظر إلى كيفية تشفير الولايات:

 localparam [2:0] Idle = 3'b000; localparam [2:0] LoadF0 = 3'b001; localparam [2:0] LoadF1 = 3'b010; localparam [2:0] E_Up1 = 3'b100; localparam [2:0] A1toA0 = 3'b011; localparam [2:0] E_Down1 = 3'b101; localparam [2:0] SWAP = 3'b110; localparam [2:0] E_UP2 = 3'b111; 

أعد فتح أداة تكوين Datapath :



وابدأ في تحرير خطوط CFGRAM . عند التحرير ، يجب أن تضع في الاعتبار مخطط Datapath ، وهو:



الإطارات الحمراء في الشكل أدناه (والأسهم في الشكل أعلاه) أبرزت المناطق المصححة (ومسار البيانات) للحالة LoadF0 (الرمز 001 ، أي Reg1 ). أنا أيضا دخلت التعليقات يدويا. يجب أن تدخل محتويات F0 إلى A0.



باستخدام الإطارات والسهام الخضراء ، قمت بتمييز الإعدادات والمسار الخاص بالحالة LoadF1 (الرمز 010 - Reg2 ).

باستخدام الإطارات والسهام الزرقاء ، قمت بتمييز الإعدادات والمسار الخاص بالحالة A1toA0 (الرمز 011 - Reg3 ).

الإطارات والأسهم الأرجوانية قمت بتمييز الإعدادات والمسار لحالة SWAP (الكود 110 - Reg6 ).

أخيرًا ، تُظهر الأسهم البرتقالية مسار البيانات الموازي. ولا يتم اتخاذ أي إجراء بالنسبة لهم. انهم دائما يخرجون من SRCA . لدينا دائمًا ما يتم اختيار A0 كـ SRCA : البيانات تأتي من A0. لذلك ، لإعادة توجيه بيانات الإدخال ، سيتعين علينا تنفيذ الكثير من الإجراءات المساعدة ، لكننا لا نقبل أي بيانات ، لذلك نحن لسنا بحاجة إلى هذه الإجراءات ، وسيجد الجميع قائمتهم في AN82156 . لا نحتاج أيضًا إلى تحرير أي إعدادات Datapath ثابتة ، لذا أغلق أداة تكوين Datapath .

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



يبدو التهيئة العادية لشاشات الكريستال السائل ومخرج الأحرف "ABC" بهذا الشكل (أذكرك أن الأوامر تذهب إلى FIFO0 ، وأن الوثائق تحتاج إلى إدراج فترات توقف مؤقتة بين الفرق ، والبيانات تذهب إلى FIFO1 ، ولم أجد أي شيء عن الإيقاف المؤقت بين البيانات):

  volatile uint8_t* pFIFO0 = (uint8_t*) LCD4bit_1_LCD_DP__F0_REG; volatile uint8_t* pFIFO1 = (uint8_t*) LCD4bit_1_LCD_DP__F1_REG; pFIFO0[0] = 0x33; CyDelay (5); pFIFO0[0] = 0x33; CyDelay (100); pFIFO0[0] = 0x33; CyDelay (5); pFIFO0[0] = 0x20; CyDelay (5); pFIFO0[0] = 0x0C; //   CyDelay (50); pFIFO0[0] = 0x01; //   CyDelay (50); pFIFO1[0] = 'A'; pFIFO1[0] = 'B'; pFIFO1[0] = 'C'; 

ما هو لماذا لا يوجد سوى الشخصية الأولى على الشاشة؟



وإذا قمت بإضافة التأخير بين إخراج البيانات - كل شيء على ما يرام:



لا يحتوي الذبذبات على قنوات كافية لمثل هذا العمل. نتحقق من العمل على محلل منطقي. عملية تسجيل البيانات هي على النحو التالي.



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

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



ستعمل هذه العداد دائمًا ( يتم تمكين تمكين على 1). ولكن سيتم تحميله عندما يكون الجهاز في حالة E_UP2 (بعد ذلك سنقع على الفور في حالة الخمول ). سيتم رفع السطر Count7_1_tc إلى 1 عند حساب العداد على الصفر ، مما يجعلنا شرطًا إضافيًا للخروج من حالة الخمول . يحتوي الشكل أيضًا على قيمة الفترة ، لكننا لن نجدها في رمز Verilog. يجب إدخالها في الكود C. لكن أولاً ، ننقل رمز Verilog الذي تم إنشاؤه تلقائيًا عن طريق التبديل إلى علامة التبويب Verilog. بادئ ذي بدء ، يجب أن يكون العداد متصلاً (نرى هذا الرمز في بداية الملف ونقله إلى البداية أيضًا):

 `define CY_BLK_DIR "$CYPRESS_DIR\..\psoc\content\CyComponentLibrary\CyComponentLibrary.cylib\Count7_v1_0" `include "$CYPRESS_DIR\..\psoc\content\CyComponentLibrary\CyComponentLibrary.cylib\Count7_v1_0\Count7_v1_0.v" 

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

 wire Count7_1_load; wire Count7_1_tc; assign Count7_1_load = (SM==E_UP2); 

وهنا العداد نفسه ، وضعت في نهاية الملف. يتم تعيين جميع الثوابت إلى المنافذ مباشرة في هذا الإعلان:

  Count7_v1_0 Count7_1 ( .en(1'b1), .load(Count7_1_load), .clock(clk), .reset(1'b0), .cnt(), .tc(Count7_1_tc)); defparam Count7_1.EnableSignal = 1; defparam Count7_1.LoadSignal = 1; 

لتمكين هذا العداد من العمل ، نضيف تلقائيًا شرطًا إضافيًا للخروج من حالة الخمول :


نفس النص:
  case(SM) Idle : begin LCD_E <= 0; if (( !F0empty ) == 1'b1) begin SM <= LoadF0 ; LCD_RS <= 0; end else if (( !F1empty &Count7_1_tc ) == 1'b1) begin SM <= LoadF1 ; LCD_RS <= 1; end end 


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

  *((uint8_t*)LCD4bit_1_Count7_1_Counter7__PERIOD_REG) = 0x20; *((uint8_t*)LCD4bit_1_Count7_1_Counter7__CONTROL_AUX_CTL_REG) |= 0x20; // Start 

يوضح المحلل أن التأخير واضح في الحالة المعدلة:



يحتوي LCD أيضًا على الأحرف الثلاثة.

لكن إخراج الطابع البرنامجي في الحياة الحقيقية أمر غير مقبول. مجرد إضافتها إلى FIFO سيتجاوز. انتظر FIFO لإفراغ - وهذا يعني خلق تأخير كبير لب المعالج.يعمل المعالج على تردد 72 ميجاهرتز ، ويتم إخراج البيانات لمدة 7-8 دورات على مدار الساعة بتردد 1 ميجاهيرتز. لذلك ، في الواقع ، يجب عرض النص باستخدام DMA. هذا هو المكان الذي يأتي فيه مبدأ "الإطلاق والنسيان" في متناول يدي. سيتم إنشاء جميع التأخيرات في مخطط التوقيت بواسطة UDB ، وستحدد وحدة التحكم DMA مدى استعداد FIFO لاستلام البيانات إلينا. يحتاج المعالج الأساسي فقط إلى تكوين خط في الذاكرة وتكوين DMA ، وبعد ذلك يمكنه القيام بمهام أخرى دون الحاجة إلى القلق بشأن الإخراج إلى شاشة LCD.

أضف الكود التالي:
  static const char line[] = "This is a line"; /* Defines for DMA_D */ #define DMA_D_BYTES_PER_BURST 1 #define DMA_D_REQUEST_PER_BURST 1 /* Variable declarations for DMA_D */ /* Move these variable declarations to the top of the function */ uint8 DMA_D_Chan; uint8 DMA_D_TD[1]; /* DMA Configuration for DMA_D */ DMA_D_Chan = DMA_D_DmaInitialize(DMA_D_BYTES_PER_BURST, DMA_D_REQUEST_PER_BURST, HI16(line), HI16(LCD4bit_1_LCD_DP__F1_REG)); DMA_D_TD[0] = CyDmaTdAllocate(); CyDmaTdSetConfiguration(DMA_D_TD[0], sizeof(line)-1, CY_DMA_DISABLE_TD, CY_DMA_TD_INC_SRC_ADR); CyDmaTdSetAddress(DMA_D_TD[0], LO16((uint32)line), LO16((uint32)LCD4bit_1_LCD_DP__F1_REG)); CyDmaChSetInitialTd(DMA_D_Chan, DMA_D_TD[0]); CyDmaChEnable(DMA_D_Chan, 1); 


على الشاشة لدينا:



الخاتمة


في مثال شبه اصطناعي ، ولكن قريب من المهام الحقيقية ، على سبيل المثال ، لقد أتقننا آلية تطوير كود UDB باستخدام آلية بديلة - أداة تكوين Datapath. تتيح هذه الآلية ، على عكس UDB Editor ، الوصول إلى جميع إمكانيات إدارة UDB تمامًا ، لكن العمل بها أكثر تعقيدًا من محرر UDB. ومع ذلك ، فإن الطريقة التي اقترحها مؤلف المقال لا تسمح بكتابة التعليمات البرمجية من نقطة الصفر ، ولكن ببساطة إنشائها ، بالاعتماد على التعليمات البرمجية المساعدة التي أنشأها محرر UDB نفسه.

يمكن إجراء اختبار المشروع الذي تم الحصول عليه عند كتابة المقالة هنا .

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


All Articles