الـ 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 - شغّل الاختبار ثم انتهِ.