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

أول Verilog module: شرح خطوة بخطوة

اكتب أول module Verilog كامل من الصفر - الإعلان، والمنافذ (ports)، وقطعة من المنطق التوافقي، وtestbench يقودها. قابل للتشغيل في المتصفح.

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

الـ module هو وحدة Verilog

كل شيء في Verilog يعيش داخل module. الـ module يلفّ قطعة من الدارة: يعلن عن الإشارات الداخلة، والإشارات الخارجة، وما يقع بينهما من أسلاك/registers/منطق. كل شريحة رأيتها يومًا هي شجرة من modules تأخذ نسخًا من modules أخرى، نزولًا إلى عناصر بوابات يقدمها المصنّع.

الشكل ثابت:

module name(port_list);
    // التصريحات: wire, reg, parameter
    // الجسم: assigns, instantiations, always blocks
endmodule

سنبني تدريجيًا حتى نصل إلى module كامل.

أصغر module مفيد: بوابة AND بدخلين

نحتاج إلى شيء بسيط ومكتفٍ بذاته. بوابة AND بدخلين مثالية: دخلان، خرج واحد، سطر منطق واحد.

اضغط Run. ينبغي أن ترى الصفوف الأربعة لجدول حقيقة AND. لنمرّ على كل جزء.

قراءة إعلان الـ module

module and_gate(
    input  wire a,
    input  wire b,
    output wire y
);
  • module and_gate يُعلن عن module بالاسم and_gate. الاسم هو ما ستستخدمه modules أخرى لأخذ نسخة منه.
  • القائمة بين القوسين هي قائمة المنافذ (port list) - الإشارات المرئية من الخارج.
  • input wire a - a منفذ دخل؛ وهو wire (يُقاد من الخارج).
  • output wire y - y منفذ خرج يُقاد بشيء داخل module.

إن أردت الإيجاز يمكنك كتابة input a بدلًا من input wire a - فالاتجاه وحده يجعل النوع الافتراضي wire. لكن الصراحة عادة جيدة تستحق التشجيع. منافذ Module تغطّي مجموعة أشكال المنافذ بالكامل.

الجسم

assign y = a & b;

هذه continuous assignment. تقول: "كلما تغيّر a أو b، أعِد حساب y بوصفها العملية AND على مستوى البتات بين الاثنين". لا ساعة، ولا توقيت - العلاقة صحيحة دائمًا. هذا منطق توافقي بحت.

endmodule يُغلق الكتلة. انتهى module.

الـ testbench

لا يمكنك تشغيل and_gate بمفرده. تحتاج module ثانٍ يقود مدخلاته ويراقب مخرجاته. هذا هو الـ testbench، وبالعرف نسمّيه test أو tb أو <design>_tb.

module test;
    reg  a, b;
    wire y;

    and_gate dut(.a(a), .b(b), .y(y));
    ...
endmodule

ثلاثة أشياء يجب ملاحظتها:

  • لا قائمة منافذ. الـ testbench هو قمة المحاكاة - لا شيء خارجه.
  • reg a, b وwire y. مدخلات التصميم تحت الاختبار (DUT) هي reg في testbench لأننا نقودها من كتلة إجرائية. الخرج هو wire لأن DUT يقوده.
  • and_gate dut(.a(a), .b(b), .y(y)). هذا هو instantiation. نأخذ نسخة من and_gate ونسميها dut (اسم شائع - "design under test"). الصياغة .a(a) تقول "صِل المنفذ المسمى a على النسخة بالإشارة المحلية المسماة a". Module Instantiation يتعمق أكثر.

التحفيز (Stimulus)

initial begin
    a = 0; b = 0; #1 $display("a=%b b=%b y=%b", a, b, y);
    a = 0; b = 1; #1 $display("a=%b b=%b y=%b", a, b, y);
    a = 1; b = 0; #1 $display("a=%b b=%b y=%b", a, b, y);
    a = 1; b = 1; #1 $display("a=%b b=%b y=%b", a, b, y);
    $finish;
end

كتلة initial تعمل مرة واحدة عند بدء المحاكاة. داخلها:

  • a = 0; b = 0; تقود المدخلات. هذه blocking assignments - الترتيب مهم، وكل منها يحدث قبل التالية.
  • #1 يُقدم زمن المحاكاة بوحدة واحدة. نحتاجها كي يستقرّ y بعد تغيير المدخل. بدون #1، سيطبع $display القيمة القديمة لـ y.
  • $display(...) تطبع إلى console المحاكاة. سلسلة التنسيق تعمل مثل printf في C: %b ثنائي، %d عشري، %h ست عشري، %t زمن المحاكاة.
  • $finish تُنهي المحاكاة. بدونه يستمر المحاكي في تقديم الزمن إلى الأبد بانتظار حدث لا يأتي.

جرّب كسرها

عدّل module ليصبح بوابة OR (غيّر & إلى |) وأعد التشغيل. يتقلّب جدول الحقيقة. الآن جرّب XOR:

نفس الهيكل العظمي. عامل مختلف. هذه هي اللعبة كلها بالنسبة للمنطق التوافقي - أعلن عن المنافذ، اكتب assigns، اسحب المدخلات، راقب المخرجات.

module بخرجين

يمكن أن يكون لـ modules أكثر من خرج. إليك half-adder - مدخلان، sum وcarry-out:

جملتا assign تعيشان جنبًا إلى جنب لكنهما تحدثان بالتوازي - لا "أحسب sum أولًا ثم carry". كلاهما صحيح دائمًا. هذا هو التزامن الذي تحدثنا عنه في العتاد مقابل البرمجيات، مُجسَّدًا.

ماذا تعرف الآن

شاهدت الهيكل العظمي الكامل لملف Verilog: module تصميم بمنافذ مُعلن عنها وجسم، وtestbench يأخذ نسخة منه، ويقود مدخلاته في كتلة initial، ويُبلّغ عن النتائج. كل ملف Verilog ستقرؤه تقريبًا يتوافق مع هذا القالب. بقية اللغة هي مجرد ملء بمنطق أغنى، وإشارات متعددة البت، وسلوك متزامن مع clock، وtestbenches أكبر.

التالي: التعليقات وأسلوب الكتابة، حتى تبقى modules لديك قابلة للقراءة مع نموّها.

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

ما أبسط Verilog module؟

أصغر module شرعي في Verilog هو فقط module name; endmodule - بلا منافذ ولا جسم. أصغر module مفيد هو module بخرج واحد: module and_gate(input wire a, input wire b, output wire y); assign y = a & b; endmodule. هذه قطعة حقيقية من المنطق التوافقي يمكن إسقاطها في أي تصميم أكبر.

كيف أُشغّل Verilog module؟

لا يمكنك تشغيل module بمفرده - فهو وصف لدارة لا برنامج. تكتب module من نوع testbench يأخذ نسخة (instantiates) من تصميمك ويقود مدخلاته، ثم تترجم الاثنين بـ iverilog -o sim design.v test.v وتُشغّل vvp sim. محرر المتصفح في هذه الصفحة يقوم بالخطوتين عند الضغط على Run.

ما هو testbench في Verilog؟

testbench هو module ثانٍ - عادةً بلا منافذ - مهمته إخضاع تصميمك للاختبار. يأخذ نسخة من التصميم، ويُحرّك مدخلاته عبر كتلة initial، ويراقب الخرج بـ $display أو $monitor، ويستدعي $finish عند الانتهاء. testbench غير قابل للتركيب؛ فهو موجود فقط للتحقق من السلوك.

لماذا تحتاج شيفرة Verilog إلى $finish؟

لأن العتاد لا يتوقف أبدًا. المحاكي يتظاهر بأن الزمن يمضي، ومن دون $finish صريح يستمرّ في تقديم الزمن إلى الأبد بانتظار أحداث جديدة. يخبر $finish المحاكي بـ 'انتهينا، اخرج بنظافة'. في testbench يكون السطر الأخير في كتلة initial - شغّل الاختبار ثم انتهِ.

Coddy programming languages illustration

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

ابدأ الآن