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

جملة Case في Verilog: فك ترميز متعدد الطرق بالطريقة الصحيحة

كيف تعمل case لفك ترميز متعدد الطرق نظيف، الـ default الذي يجب ألا تتخطاه أبدًا، والفروق بين case وcasex وcasez.

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

التفرع متعدد الطرق

case هو بناء الإرسال المسطح في Verilog. تعطيه تعبيرًا؛ فيختار الفرع المُطابق:

case (expression)
    pattern_1: statement_1;
    pattern_2: statement_2;
    pattern_3: begin
        statement_3a;
        statement_3b;
    end
    default: default_statement;
endcase

بنيويًا يشبه switch في C، لكن:

  • لا break - كل فرع ينتهي ضمنيًا بالفرع التالي.
  • الأنماط يمكن أن تكون vectors، لا فقط أعداد صحيحة.
  • تحوّل أداة التركيب الكل إلى mux مسطح (أو فاكّ ترميز one-hot عندما تجعل الأنماط ذلك ممكنًا).

مثال عملي: mux 4-إلى-1

جسم case له أربعة أنماط صريحة إضافة إلى default. ترى أداة التركيب دخل 2 بت يُعيَّن على إحدى أربع قيم وتُصدر multiplexer 4-إلى-1. نظيف، مسطح، سريع.

لماذا تحتاج default

لـ case التوافقي، حذف default نفس فخ if بلا else: أي قيمة دخل غير مُطابَقة تترك out غير مُسند، فتستنتج أداة التركيب latch.

لـ sel بـ 2 بت، الأنماط أعلاه تغطّي القيم الأربع الممكنة - لذا نظريًا default زائد. عمليًا:

  1. أداة التركيب لا تُثبت دائمًا أن الحالات شاملة.
  2. المُحدّد قد يكون x أو z في المحاكاة، وهو لا يطابق أي حالة صريحة.
  3. إضافة حالة جديدة لاحقًا قد تترك السلوك الافتراضي غير محدد.

اكتب دائمًا default. لآلات الحالة ومنطق mux حيث تعرف أن الافتراضي غير قابل للوصول، إسناد 'x:

default: out = 8'bx;

…يخبر أداة التركيب "هذا don't-care، حسّن بحرية" ويُظهر x أحمر براق في المحاكاة إن وُصلت الحالة غير القابلة للوصول. هذا أفضل ما في العالمين.

آلة حالة في case

الاستخدام الكلاسيكي لـ case هو منطق انتقال الحالة لآلة حالة منتهية:

كتلة case (state) هي منطق انتقال الحالة. كل فرع يقرر ما الحالة التالية وكم يبقى فيها. default غير قابل للوصول هنا (نغطّي RED/GREEN/YELLOW بشكل شامل في فضاء 2 بت)، لكنه موجود كشبكة أمان - إن أصبحت state بطريقة ما 2'd3، تُعيد FSM ضبط نفسها بنظافة إلى RED بدلًا من التعليق.

آلات الحالة المنتهية تتعمق في هذا النمط.

عدة أنماط لكل فرع

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

case (opcode)
    4'h0, 4'h1, 4'h2: result = a + b;
    4'h3, 4'h4:       result = a - b;
    4'h8:             result = a & b;
    default:          result = 8'd0;
endcase

هذان opcodes كلاهما يعني "اطرح"، وثلاثة تعني "اجمع". تجمع أداة التركيب الأنماط بـ OR للمقارن.

casez وcasex: مطابقة Don't-Care

أحيانًا تريد مطابقة نمط ببعض البتات غير محددة - "أي opcode يبدأ بـ 010":

casez (opcode)
    8'b010?_????: instruction = ALU_OP;
    8'b110?_????: instruction = LOAD_OP;
    8'b1110_????: instruction = JUMP_OP;
    default:      instruction = UNKNOWN;
endcasez

casez يعامل ?z) في النمط كـ don't-cares. كل ? يطابق 0 أو 1. مفيد لفك ترميز تنسيقات التعليمات حيث لا تُستخدم بعض مواضع البتات لفئات opcode معينة.

casex يمتد ذلك ليعامل x كـ don't-care أيضًا. casex خطر لأن الإشارات غير المُهيّأة (التي هي x في المحاكاة) تطابق كل حالة، فتُنتج سلوكًا مفاجئًا. معظم دلائل الأسلوب الحديثة توصي بـ casez وتحظر casex.

في SystemVerilog تحصل أيضًا على case inside، وهو أنظف نسخة على الإطلاق - يقبل النطاقات والقوائم - لكن Verilog العادي يتوقف عند casez.

case مقابل سلسلة if/else if

البناءان يمكن أن يعبّرا عن قرارات متعددة الطرق، لكنهما يُركَّبان إلى عتاد مختلف:

  • case هو إرسال مسطح. يمكن لأداة التركيب إنتاج فاكّ ترميز one-hot، أو شجرة mux متوازنة، أو هياكل مسطحة أخرى. وقت ثابت للتقييم.
  • if/else if هو سلسلة أولوية. تُنتج أداة التركيب تتابعًا حيث كل مستوى يضيف تأخيرًا. أبطأ لوغاريتميًا.

وظيفيًا متداخلان. أسلوبيًا: استخدم case عندما تتعلق الشروط بقيمة تعبير واحد. استخدم if/else if عند وجود أولوية حقيقية أو عندما تتضمن الشروط إشارات مختلفة.

// Better as case:
if      (sel == 2'd0) out = a;
else if (sel == 2'd1) out = b;
else if (sel == 2'd2) out = c;
else                  out = d;

// Better as if/else if:
if      (urgent_event)  next_state = HANDLE_URGENT;
else if (timer_expired) next_state = TIMEOUT;
else if (data_ready)    next_state = PROCESS;
else                    next_state = state;

الشروط الثلاثة الأولى كلها "ما هي sel؟" - case تُقرأ أكثر طبيعية وتُركَّب أكثر تسطيحًا. الشروط الثلاثة الثانية أحداث مستقلة بأولوية واضحة - if/else if خيار أفضل.

ماذا بعد

المستند الأخير في هذا الفصل - حلقات For - يغطّي for في Verilog والشيء المفاجئ الذي يحدث عند استخدامها في شيفرة قابلة للتركيب. ثم ننتقل إلى المنطق التسلسلي وFSMs بجدية.

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

ما هي جملة case في Verilog؟

case (expr) ... endcase هو بناء التفرع متعدد الطرق في Verilog. يُقيِّم التعبير مرة ويُرسل إلى الفرع المُطابق. إنه الخيار المعتاد لآلات الحالة وفاكات ترميز opcode ومُحدّدات mux وأي شيء آخر يختار بين عدة خيارات متبادلة الإقصاء.

ما الفرق بين case وcasex وcasez في Verilog؟

case يطابق بالبت بالضبط، بما في ذلك قيم x وz. casez يعامل z?) في عناصر case كـ don't-cares. casex يعامل كلًا من x وz كـ don't-cares. مطابقة don't-care مفيدة لأنماط opcode حيث بعض مواضع البتات غير ذات صلة، لكن casex خطر في المحاكاة لأن الإشارات غير المُهيّأة (x) قد تطابق كل حالة بالخطأ.

لماذا أحتاج default في جملة case في Verilog؟

بدون default، ترى أداة التركيب إمكانية عدم تطابق أي حالة، فتقرر أن إشارة الخرج يجب أن تحتفظ بقيمتها السابقة، فتستنتج latch غير مرغوبة. فرع default يتعامل مع كل قيمة غير مُطابَقة - عادةً بضبط الخرج على قيمة آمنة أو بتعليم الحالة كغير قابلة للوصول بإسناد x. ضعه دائمًا.

متى أستخدم case مقابل if-else في Verilog؟

استخدم case عندما تكون الشروط متبادلة الإقصاء وتُرسل بناءً على قيمة تعبير واحد - آلات الحالة، فاكات ترميز opcode، مُحدّدات mux. استخدم if/else عند وجود ترتيب أولوية حقيقي أو عندما تتضمن الشروط إشارات مختلفة. case يُركَّب إلى عتاد أكثر تسطيحًا وأسرع من سلسلة else if طويلة.

Coddy programming languages illustration

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

ابدأ الآن