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

If-Else في Verilog: المنطق الشرطي في الكتل الإجرائية

كيف تعمل if/else داخل كتلة always، فخ latch الذي يلتقط المبتدئين، وعتاد priority encoder الذي تُنتجه سلاسل else if.

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

صياغة مألوفة، نموذج ذهني مختلف

if/else تبدو تمامًا كـ C:

if (condition) begin
    // ... statements ...
end else begin
    // ... statements ...
end

لكن القواعد مختلفة لأن Verilog ليس برمجيات. شيئان يجب تذكرهما:

  1. if/else يعيش فقط داخل كتلة إجرائية. لا يمكنك كتابة if مستقلة على مستوى module.
  2. ما يُركَّب منه if/else يعتمد على نوع الكتلة. في كتلة توافقية، يصبح multiplexer أو priority encoder. في كتلة متزامنة مع clock، يصبح flip-flop بمنطق تحديث شرطي.

داخل كتلة توافقية

كتلة always @(*) التوافقية تعيد التشغيل كلما تغيّر a أو b أو c. سلسلة if/else تختار فرعًا، تُسند max، وتنتهي الكتلة. بما أن max يُسند دائمًا (كل مسار له إسناد)، تُنتج أداة التركيب منطقًا توافقيًا بحتًا - بلا latch.

لاحظ أن max مُعلن reg رغم أنه لا يوجد flip-flop في العتاد. نفس القاعدة كما دائمًا: أي شيء يُسند داخل always يجب أن يكون reg.

فخ Latch

هذا أكثر خطأ شيوعًا في شيفرة التوافقي للمبتدئين:

// WRONG - infers a latch
always @(*) begin
    if (enable)
        out = data;
    // no else! when `enable` is low, what does `out` do?
end

تقرأ أداة التركيب "عندما يكون enable منخفضًا، out لا يُسند" فتقرر أن out يجب أن يتذكر قيمته السابقة. تذكر قيمة يتطلب خلية تخزين، فتُدخل الأداة latch. Latches في التصاميم المتزامنة تُسبّب مشاكل توقيت، وصعبة في الـ reset، وتقريبًا أبدًا ما عنيته.

طريقتان للإصلاح:

كلاهما ينتج نفس العتاد التوافقي - mux 2-إلى-1. نمط "افتراضي في الأعلى" يتدرج أفضل عندما يكون لديك كثير من الإسنادات الشرطية لنفس الإشارة.

داخل كتلة متزامنة مع clock

لاحظ ما يختلف عن الحالة التوافقية:

  • الكتلة always @(posedge clk) - منطقة flip-flop.
  • الإسناد يستخدم <= (non-blocking).
  • لا else لحالة "لا reset ولا enable". هذا لا بأس به. في كتلة متزامنة مع clock، عندما لا يُطلق أي فرع، يحتفظ flip-flop بقيمته السابقة - وهذا بالضبط ما يفعله flip-flop فيزيائيًا. لا تُستنتج latch لأن الإشارة هي بالفعل register.

هذا هو المكان الوحيد الذي يكون فيه حذف else آمنًا. خارج الكتل المتزامنة مع clock، تعامل دائمًا مع كل مسار.

سلسلة else if: priority encoder

سلسلة من جمل else if لها أولوية ضمنية - الشروط الأبكر تتفوق على اللاحقة:

requests[0] هي أعلى أولوية - إن كانت مضبوطة، فالمنحة هي 0 بصرف النظر عما تفعله البتات الأعلى رقمًا. تحوّل أداة التركيب السلسلة إلى mux متتابع: افحص البت 0 أولًا، ثم البت 1، ثم البت 2، ثم البت 3. كل مستوى يضيف قدرًا صغيرًا من التأخير.

إن كانت الشروط متبادلة الإقصاء - لنقل، فكّ ترميز دخل one-hot - فإن جملة case (المستند التالي) تُنتج عتادًا أكثر تسطيحًا وأسرع من سلسلة else if. استخدم شكل case عندما لا يكون هناك متطلب أولوية حقيقي.

if بلا else في الشيفرة المتزامنة مع clock

الكتلة المتزامنة مع clock لا تحتاج else لأن "احتفظ بالقيمة السابقة" هو الافتراضي. هكذا تبني enables:

always @(posedge clk) begin
    if (load) target <= incoming;
    // no else: when load is low, target keeps its value
end

هذا register بـ load-enable. معظم registers الـ pipeline والعدّادات وregisters الإعداد تستخدم هذا النمط.

begin/end والجمل المفردة

مثل C، يمكنك حذف begin/end لجملة واحدة:

if (a) out = 1;
else   out = 0;

لأي شيء يتجاوز جملة واحدة، استخدم الكتلة:

if (a) begin
    out = 1;
    flag = 1;
end else begin
    out = 0;
    flag = 0;
end

النمطان يمتزجان بحرية. تنصح دلائل الأسلوب عمومًا باستخدام begin/end دائمًا لجعل إضافة جملة ثانية مؤلمة أقل.

ماذا بعد

المستند التالي - جملة Case - يغطّي case، وهي الأداة الصحيحة لفك الترميز متعدد الطرق (آلات الحالة، تشفير opcode، جداول ROM). بعد ذلك، حلقات for، التي تختلف بدقة عن نظيراتها في البرمجيات لأنها تُنشر في وقت elaboration.

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

كيف تعمل جملة if في Verilog؟

if (cond) statement; يشغّل statement عندما يكون cond غير صفر. يمكنك تغليف جمل متعددة في begin ... end. أضف else statement; للفرع البديل، أو سلسل بـ else if (other_cond) .... if/else يوجد فقط داخل الكتل الإجرائية - initial أو always - لا على المستوى العلوي لـ module.

ما هي latch مُستنتجة في Verilog؟

latch أنشأتها أداة التركيب دون أن تطلب، لأن كتلة always التوافقية لم تُسند إشارة في كل مسار. ترى الأداة 'إن a ثم out = 1' بلا else، فتقرر أن الحالة غير المُسندة يجب أن تتذكر القيمة السابقة، فتُنتج latch. Latches تقريبًا دائمًا خاطئة؛ الحل هو إعطاء كل إشارة قيمة افتراضية في أعلى الكتلة أو else صريح.

كيف تتجنب latches المُستنتجة في Verilog؟

في كتلة always @(*) توافقية، تأكد أن كل reg خرج يُسند في كل مسار شيفرة. أنظف نمط هو ضبط قيم افتراضية في أعلى الكتلة ثم التجاوز شرطيًا. المترجم عادةً يحذر عند استنتاج latch - عامل التحذير كخطأ.

ماذا يُركَّب من سلسلة if-else في Verilog؟

priority encoder. أول if له أعلى أولوية، التالي else if يُفحص فقط إن كان الأول خاطئًا، وهكذا. في العتاد يصبح هذا سلسلة من muxes بترتيب الأولوية مُحفور. إن كانت الشروط متبادلة الإقصاء، فإن جملة case بنفس المنطق غالبًا تُركَّب إلى عتاد أكثر تسطيحًا وأوضح للقراءة.

Coddy programming languages illustration

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

ابدأ الآن