Menu
flag Ar iconالعربيةdown icon

كتلة Always في Verilog: المنطق التوافقي والتسلسلي

كيف تعمل كتل always، والفرق بين التوافقي always @(*) والمتزامن مع clock always @(posedge clk)، والقواعد التي تقرر ما العتاد الذي يُنتجه كل منهما.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

حصان عمل Verilog السلوكي

assign يصف منطقًا توافقيًا ذا معادلة واحدة. بمجرد أن تحتاج إلى if/else أو case أو ذاكرة، تلجأ إلى always. كتلة always هي قطعة من الشيفرة الإجرائية تعيد التشغيل كلما تغيّرت إشارات محددة. الإشارات التي تُفعّل إعادة التشغيل هي قائمة الحساسية.

شكلان من always ستراهما أكثر من غيرهما:

  1. always @(*) - يعيد التشغيل كلما تغيّرت أي إشارة تُقرأ في الكتلة. يبني منطقًا توافقيًا.
  2. always @(posedge clk) - يعيد التشغيل فقط عند الحافة الصاعدة لـ clk. يبني منطقًا تسلسليًا متزامنًا مع clock (flip-flops).

توجد أشكال أخرى (@(a or b) و@(negedge clk) و@(posedge clk or negedge reset_n)) لكن الشكلين أعلاه يمثلان تقريبًا كل كتلة قابلة للتركيب ستكتبها.

التوافقي always @(*)

ثلاث ملاحظات:

  • out هو reg. كل شيء يُسند داخل always يجب أن يكون reg. الكلمة المفتاحية لا تعني "هذا flip-flop"؛ هنا تعني فقط "أنا مكتوب من كتلة إجرائية".
  • always @(*). * تقول "استيقظ كلما تغيّر أي شيء أقرأه". المحاكي يستنتج قائمة الحساسية تلقائيًا. يمكنك كتابتها يدويًا - always @(sel) - لكن @(*) أكثر أمانًا لأن فقدان إشارة مصدر كلاسيكي للأخطاء.
  • لا ساعة. هذه الكتلة تصف منطقًا توافقيًا. تُنتج أداة التركيب قطعة من المنطق تحسب out من sel مباشرة - بلا flip-flops، بلا حاجة لمنفذ clock.

حالة default ليست اختيارية روحيًا حتى لو كانت اختيارية صياغيًا. احذفها وأي قيمة دخل غير محددة تترك out يحتفظ بقيمته السابقة - والتي تُركَّب إلى latch غير مقصود. ضع default دائمًا.

التسلسلي always @(posedge clk)

الفروق الرئيسية عن النسخة التوافقية:

  • always @(posedge clk). الكتلة تعيد التشغيل فقط عند الحافة الصاعدة لـ clk. لا يحدث شيء بين الحواف.
  • non-blocking assignment <=. داخل كتلة متزامنة مع clock، هذا هو العامل الصحيح. يقول "جدوِل count ليأخذ قيمته الجديدة في نهاية الخطوة الزمنية"، وهذا بالضبط كيف يتصرف flip-flop. السبب والبديل في Blocking مقابل Non-blocking.
  • لا default مطلوب. if يغطي كلا الفرعين (reset وlا reset). لا خطر latch.

أداة التركيب ترى هذا الشكل - حساسية متزامنة مع clock، non-blocking - وتُنتج register 4 بت (أربعة flip-flops) إضافة إلى المنطق التوافقي الذي يحسب count + 1 والـ mux الذي يختار بين reset والزيادة.

تمييز التركيب

نفس مصدر module يمكن أن يصف قطعتين مختلفتين تمامًا من العتاد بحسب شكل كتلة always:

الكتلةالعتاد
always @(*) y = expr;منطق توافقي بحت. لا ذاكرة.
always @(posedge clk) y <= expr;flip-flop. يلتقط expr مرة لكل دورة ساعة.
always @(*) if (en) y = expr;Latch - عادةً bug. حالة "else" تُبقي القيمة القديمة.
always @(posedge clk) if (en) y <= expr;flip-flop بـ enable. يلتقط فقط عندما يكون en مرتفعًا.

الحالة الثالثة هي فخ الـ latch. الـ latch هي خلية ذاكرة شفافة تحتفظ بخرجها عندما لا يكون الدخل مؤكَّدًا - مفيدة في تصاميم محددة، تقريبًا دائمًا bug عندما تكون عرضية. معظم أدوات التركيب تحذر بصوت عالٍ عند استنتاج latch لم تطلبها. عامل التحذير كخطأ.

تنوعات قائمة الحساسية

سترى بعض قوائم الحساسية الأقل شيوعًا:

  • always @(a or b or c) - قائمة صريحة. Verilog-2001 أضافت فاصل ,: always @(a, b, c). كلاهما يعمل.
  • always @(posedge clk or negedge reset_n) - reset غير متزامن. الكتلة تعمل عند حافة ساعة صاعدة أو حافة reset هابطة. يُستخدم عندما يجب أن يأخذ reset مفعوله فورًا، لا انتظار الساعة التالية.
  • always @(negedge clk) - متزامن مع الحافة الهابطة. نادر؛ بعض التصاميم تستخدمه لـ flip-flops "negative-edge-triggered" تلتقط على الحافة الهابطة بدلًا من الصاعدة.

للتصاميم الجديدة، فضّل always @(*) للتوافقي وalways @(posedge clk) للتسلسلي. لا تلجأ إلى reset غير متزامن إلا عندما يحتاج التصميم إليه فعلًا.

كتلتان قطعتا عتاد

كتل always متعددة في نفس module مستقلة - كل واحدة تصبح قطعتها الخاصة من العتاد:

الكتلة المتزامنة مع clock تُنتج register flip-flop. الكتلة التوافقية تُنتج بوابة XOR. يعيشان جنبًا إلى جنب؛ لا تعرف إحداهما عن الأخرى. الخرجان يتغيران بجداول مختلفة تمامًا.

ما لا تستطيع كتل always فعله

بعض الأشياء التي تبدو مغرية لكنها غير مسموحة:

  • الإسناد إلى wire: الهدف يجب أن يكون reg. المترجم يفرض.
  • الإسناد إلى نفس reg من كتلتي always مختلفتين: يُنتج سلوكًا غير معرَّف في المحاكاة ولن يُركَّب. قائد واحد لكل إشارة.
  • قراءة وكتابة نفس الإشارة في نفس الكتلة التوافقية بطريقة تُنشئ حلقة تغذية مرتدة: always @(*) x = x + 1; هي حلقة بصفر تأخير لا يستطيع المحاكي حلّها.

الأوّلَين يلتقطهما المترجم. الثالث أحيانًا يظهر فقط في وقت المحاكاة كتعليق.

ماذا بعد

المستند التالي - كتلة Initial - يغطّي شقيق always: كتلة تعمل مرة واحدة بالضبط في بداية المحاكاة. إنه حصان عمل testbenches. بعد ذلك، قواعد blocking مقابل non-blocking التي تقرر ما إذا كانت كتلتك المتزامنة مع clock تفعل ما تعنيه.

الأسئلة الشائعة

ما هي كتلة always في Verilog؟

always تُقدّم كتلة إجرائية تعيد التشغيل كلما تغيّرت الإشارات في قائمة الحساسية الخاصة بها. هناك نكهتان: always @(*) يبني منطقًا توافقيًا (يعيد التشغيل كلما تغيّر أي دخل)، وalways @(posedge clk) يبني منطقًا تسلسليًا (يعيد التشغيل عند كل حافة صاعدة لـ clk). جسم كتلة always يمكن أن يحوي if وcase وfor وإسنادات إجرائية.

ما الفرق بين always @(*) وalways @(posedge clk)؟

always @(*) حساس لـ أي إشارة تُقرأ في الكتلة؛ ينتج منطقًا توافقيًا بلا ذاكرة. always @(posedge clk) حساس فقط للحافة الصاعدة لـ clk؛ ينتج flip-flops تلتقط الحالة مرة لكل دورة ساعة. الأول بلا ساعة وبلا register؛ الثاني له كلاهما.

ما هي قائمة الحساسية في Verilog؟

قائمة الإشارات بعد @ التي تحدد متى تعيد كتلة always التشغيل. @(*) اختصار لـ 'كل إشارة تُقرأ في الكتلة'. @(posedge clk) يعمل فقط عند الحافة الصاعدة لـ clk. @(posedge clk or negedge reset_n) يعمل عند أي من الحدثين - يُستخدم لـ resets غير متزامنة. الخطأ في قائمة الحساسية من أكثر مصادر عدم التطابق بين المحاكاة والتركيب شيوعًا.

هل يمكن الإسناد إلى wire داخل كتلة always؟

لا. كتل always يمكنها الإسناد فقط إلى reg (أو logic في SystemVerilog). المترجم يفرض هذا. إن أردت wire أن يكون خرج منطق إجرائي، أعلن reg وسيطًا، وقده داخل always، وأسند wire من reg خارجه - أو غيّر wire إلى reg.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن