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

آلات الحالة المنتهية في Verilog: نمط FSM القياسي

كيف تكتب FSM في Verilog بطريقة المحترفين - register حالة متزامن مع clock، كتلة توافقية لـ next-state، وفصل نظيف سهل القراءة والتركيب.

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

ما هي FSM

آلة الحالة المنتهية متحكم:

  • يحفظ إحدى مجموعة ثابتة من الحالات المُسماة في أي وقت معطى.
  • ينتقل بين الحالات بناءً على المدخلات (وربما الحالة الحالية).
  • يُنتج مخارج تعتمد على الحالة الحالية (وربما المدخلات الحالية).

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

ما يجعل FSMs تبدو مختلفة عن منطق datapath هو أنها حالات، لا قيم. عدّاد يخطو عبر 1, 2, 3, 4. FSM يخطو عبر IDLE, FETCHING, BUSY, DONE - نفس الشكل، لكن الحالات لها أسماء والانتقالات لها معنى.

نمط العمليتين

FSM Verilog القياسية تستخدم كتلتي always:

  1. كتلة متزامنة مع clock تلتقط register الحالة عند كل حافة clock. تستخدم non-blocking assignment. صغيرة - عادةً ثلاثة أسطر.
  2. كتلة توافقية تستخدم case على الحالة الحالية لحساب الحالة التالية والمخارج. تستخدم blocking assignment. الشيفرة "المثيرة للاهتمام" تعيش هنا.

لهذا الفصل ثلاث فوائد: يجبرك على التفكير في الحالة صراحةً، يُنتج عتادًا يُعيَّن بنظافة إلى "register واحد إضافة إلى كتلة توافقية واحدة"، وهو المعيار - الجميع ممن يقرأ Verilog لديك سيتعرّف عليه فورًا.

مثال عملي: كاشف تسلسل

FSM تعليمية كلاسيكية: اكتشف تسلسل البتات 1011 على دخل تسلسلي. أصدر نبضة دورة واحدة عند اكتمال التسلسل.

هيكل الكتلتين هو النقطة أعلاه. تفاصيل النظافة تستحق الإشارة:

  • افتراضيات في أعلى الكتلة التوافقية. next_state = state (ابق حيث أنت) وdetected = 1'b0 (لا نبضة) هي إسنادات "لا تفعل شيئًا". كل فرع case بعد ذلك يضبط فقط الأشياء التي تختلف. هذا يجعل من المستحيل استنتاج latch.
  • localparam لأسماء الحالات. أي قارئ لـ module يفكر بـ S0 وS1 وS2 وS3، لا بـ 3'd0 و3'd1. تقوم أداة التركيب بالاستبدال.
  • لا مخارج من الكتلة المتزامنة مع clock. كل منطق "ماذا تفعل هذه الحالة" يعيش في الكتلة التوافقية. الكتلة المتزامنة مسؤولة عن لا شيء سوى حفظ الحالة الحالية.

Moore مقابل Mealy

Moore: الخرج يعتمد فقط على الحالة الحالية. Mealy: الخرج يعتمد على الحالة الحالية والمدخلات الحالية.

في المثال أعلاه، يُضبط detected داخل فرع S3 فقط عندما يطابق in أحد أنماط إكمال التسلسل المتوقعة. هذا خرج Mealy - يعتمد على in إضافة إلى state. نسخة Moore سيكون لها حالة منفصلة لـ "اكتُشف للتو" وتضبط detected = 1 كلما كانت تلك الحالة حالية؛ سينبض الخرج بعد دورة لكنه لن يكون أبدًا حساسًا لـ glitch على in.

كلا الأسلوبين شرعيان. Moore هو الافتراضي في الكتب لأن المخارج مضمونة ألا تُسبّب glitch عندما تتغير المدخلات في منتصف الدورة. Mealy أسرع (لا تأخير register للمخارج المعتمدة على المدخلات) ويُنتج عتادًا أصغر في كثير من الحالات. اختر بناءً على البروتوكول الذي تنفذه.

ترميز الحالات: ثنائي، One-Hot، Gray

نمط البتات الذي تُسنده لكل حالة يهم للمساحة والسرعة:

  • ثنائي (S0 = 3'd0، S1 = 3'd1، ...): أصغر register حالة - log2N\lceil \log_2 N \rceil بت لـ NN حالة. أقصى منطق فك ترميز.
  • One-hot (S0 = 4'b0001، S1 = 4'b0010، ...): N بت لـ N حالة. منطق فك الترميز تافه (كل حالة wire واحدة)؛ الانتقالات سريعة. FPGAs غالبًا تستخدمه افتراضيًا.
  • Gray code: الحالات المتتالية تختلف ببت واحدة. مفيد عندما تعبر بتات الحالة نطاقات clock.

معظم أدوات التركيب الحديثة تختار الترميز لك (Vivado، Quartus، Design Compiler كلها لها وضع تلقائي يجرب كل واحد ويختار الأفضل). نادرًا ما تحتاج إلى التحديد. حدّد عندما تفعل: معظم الأدوات تقبل تعليق attribute أو pragma (* fsm_encoding = "one_hot" *).

نوع ثلاثي الكتل

ستراه أحيانًا FSM مقسومة إلى ثلاثة أقسام: كتلة متزامنة مع clock للحالة، كتلة توافقية لـ next-state، كتلة توافقية للمخارج. هذا فقط نمط الكتلتين مع رفع حساب الخرج إلى كتلته الخاصة:

// State register
always @(posedge clk) ...

// Next-state logic
always @(*) ...

// Output logic
always @(*) begin
    case (state)
        ...
    endcase
end

أسلوب الخرج المنفصل مفيد عندما تكون المخارج كبيرة ومنطق next-state سيكون مزدحمًا لو كانوا في الكتلة نفسها. لـ FSMs صغيرة، مبالغة.

ماذا يفعل default في FSM

كل جملة case في FSM يجب أن يكون لها فرع default. سببان:

  1. الأمان: إن أخذت register الحالة قيمة غير شرعية بطريقة ما (تلف، bug، reset جزئي)، فإن default يعيدها إلى حالة معروفة.
  2. تلميح للتركيب: عندما تكون الحالات الصريحة شاملة (حالة 2-بت بكل القيم الأربع معالجة، مثلًا)، default: next_state = 'x; يخبر أداة التركيب "أعدك أن default غير قابل للوصول، حسّن بحرية". إن أُصِيب المسار غير القابل للوصول فعلًا في المحاكاة، تنتشر x الناتجة وتُظهر الـ bug فورًا.
default: begin
    next_state = S0;   // safe recovery
    // or
    next_state = 'x;   // unreachable, optimize freely
end

اختر بناءً على ما إذا كنت قد أثبتت أن default غير قابل للوصول حقًا.

أشياء يجب الانتباه لها

نسيان الافتراضيات في أعلى الكتلة التوافقية. بدون next_state = state وافتراضيات الخرج، فرع لا يُسند كل شيء يُسرّب latch.

وضع المخارج في الكتلة المتزامنة مع clock. إن عاش detected <= 1 في كتلة always @(posedge clk)، فالخرج مُسجَّل - يظهر متأخرًا دورة. هذا قد يكون مقصودًا (خرج Mealy "مُسجَّل")، لكنه خطأ تصميم عرضي شائع عندما تتطلب المواصفات نبضة فورية.

خلط blocking وnon-blocking. الكتلة المتزامنة مع clock: <=. الكتلة التوافقية: =. خلط الاثنين داخل كتلة واحدة race condition.

كتلة always توافقية تشير إلى next_state وتُسند state. هذا يبني حلقة تغذية مرتدة لا يستطيع المحاكي حلّها. الكتلة المتزامنة مع clock تملك state؛ الكتلة التوافقية تملك next_state؛ لا تدع أيًا منهما تلمس متغير الأخرى.

ماذا بعد

تستطيع الآن بناء أي متحكم تستطيع وصفه. الفصل التالي يخطو خطوة للوراء من التصميم القابل للتركيب ويغطّي testbenches التي تُمرّنه - كيف تقود التحفيز، تراقب المخارج، تفرّغ الموجات، وتتحقق من أن modules فعلًا تفعل ما تظن.

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

ما هي آلة الحالة المنتهية في Verilog؟

FSM متحكم يحفظ إحدى مجموعة صغيرة من الحالات المُسماة وينتقل بينها بناءً على المدخلات. في Verilog، التنفيذ القياسي له كتلتان: always متزامنة مع clock تُحدّث register الحالة عند كل حافة clock، وalways توافقية تحسب الحالة التالية والمخارج بناءً على الحالة الحالية والمدخلات.

ما هو نمط FSM القياسي في Verilog؟

FSM ذو عمليتين: كتلة always @(posedge clk) متزامنة مع clock تحفظ register الحالة وتستخدم non-blocking assignment، وكتلة always @(*) توافقية تستخدم case على الحالة الحالية لحساب الحالة التالية والمخارج. هذا الفصل يجعل الشيفرة سهلة القراءة وlint والتركيب بنظافة.

ما الفرق بين Mealy وMoore FSM؟

في Moore FSM، تعتمد المخارج فقط على الحالة الحالية. في Mealy FSM، تعتمد المخارج على الحالة الحالية والمدخلات الحالية. آلات Mealy تتفاعل بدورة أسرع (لا تأخير register للمخارج المعتمدة على المدخلات) لكن يمكن أن تُنتج glitches إن تغيّرت المدخلات في منتصف الدورة. آلات Moore أبطأ بدورة لكنها أكثر قابلية للتنبؤ - هي الخيار الافتراضي ما لم تحتج السرعة.

كيف تُرمّز الحالات في Verilog؟

استخدم ثوابت localparam داخل module: localparam IDLE = 3'd0; إلخ. ثلاثة ترميزات شائعة: ثنائي (الحالات 0، 1، 2، ... - أصغر register حالة)، one-hot (بت لكل حالة، مستويات منطق أقل لكل انتقال)، وgray code (الحالات المتتالية تختلف ببت واحدة - تقلّل glitches). أدوات التركيب عادةً تختار الترميز لك؛ تثبيته نادرًا ضروري.

Coddy programming languages illustration

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

ابدأ الآن