الموجة التي تريدها فعلًا
$display تطبع نصًا. هذا جيد لفحص سريع، لكن لحظة تنقيح عدّاد بمشكلة توقيت أو آلة حالة عالقة، تريد صورة: إشارات مرسومة كجهد عبر الزمن، قابلة للتمرير والتكبير، بمؤشر يمكنك إسقاطه على أي انتقال.
تلك الصورة هي ملف VCD - "value change dump"، تنسيق نصي وضع IEEE معياره في مواصفات Verilog الأصلية. كل محاكٍ حديث يستطيع كتابة واحد؛ كل عارض موجات حديث يستطيع قراءة واحد. تكلفة إنتاج واحد سطران في testbench لديك.
السطران
داخل كتلة initial - عادةً نفسها التي تحفظ تحفيزك - أضف:
$dumpfile("dump.vcd");
$dumpvars(0, test);
$dumpfileيُسمّي ملف الخرج. مرّر أي مسار؛ إن لم يكن له دليل يقع في دليل العمل للمحاكي.$dumpvars(0, test)يقول "سجّل كل إشارة في نطاقtestوكل sub-scope، تكراريًا، بعمق غير محدود".
هذا الإعداد بأكمله. شغّل المحاكاة وسيكون لديك dump.vcd يمكنك فتحه في GTKWave أو تبويب Waveform لمحرر المتصفح.
مثال كامل
شغّله. يُظهر تبويب Waveform الآن ثلاث إشارات - clk وreset وcount - مرسومة عبر زمن المحاكاة الكامل. يمكنك إسقاط مؤشر على أي نقطة وقراءة قيم الإشارة عند ذلك الزمن. يمكنك السحب-والتكبير لفحص دورة clock واحدة.
ماذا تفعل الحجة الأولى لـ $dumpvars
$dumpvars(depth, scope) يمشي scope وكل sub-instance يحويها، يُسجّل الإشارات حتى depth مستوى. قيم العمق:
0- غير محدود. كل إشارة فيscopeوكل sub-module متداخل تُسجَّل.1- فقط الإشارات المُعلنة مباشرة فيscope. sub-modules لا تُسجَّل.2-scopeإضافة إلى مستوى واحد من sub-module.N-scopeإضافة إلى N-1 مستوى من sub-modules.
عمليًا، $dumpvars(0, test) هو ما يستخدمه كل testbench تقريبًا. التقاط كل شيء رخيص (VCDs تخزن فقط الانتقالات، لا الحالات المستقرة) ولا تريد اكتشاف في منتصف التنقيح أن الإشارة التي تحتاجها لم تُفرَّغ.
إن كان لديك تصميم كبير حقًا وVCD كبير جدًا، يمكنك التفريغ انتقائيًا:
$dumpvars(0, dut.inner_module); // just the interesting sub-module
$dumpvars(0, dut.regs); // just the register file
يمكنك استدعاء $dumpvars عدة مرات لتراكم النطاقات.
تفريغ إشارات محددة
$dumpvars يمكن أن تأخذ أيضًا قائمة بإشارات محددة بدلًا من نطاق كامل:
$dumpvars(0, test.clk, test.reset, dut.count);
هذا يُسجّل فقط تلك الإشارات الثلاث. مفيد عندما يكون التصميم ضخمًا وتهتم فقط بحفنة من الإشارات. للتصاميم الصغيرة، تفريغ كل شيء أبسط.
التحكم في التفريغ أثناء المحاكاة
مهمتان أخريان تقترنان مع $dumpvars:
$dumpoff- أوقف التفريغ مؤقتًا. لا تغييرات أخرى تذهب إلى VCD.$dumpon- استأنف التفريغ.
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, test);
// ... interesting region ...
#1000 $dumpoff;
// ... long boring region we don't want in the VCD ...
#5000 $dumpon;
// ... another interesting region ...
end
هكذا تتخطى الجزء المملّ "يعمل لمليون دورة" من اختبار طويل دون إنتاج ملف VCD بحجم 5GB.
عرض النتيجة
طريقتان رئيسيتان:
في محرر المتصفح
المحرر في هذه الصفحة يعرض VCDs مباشرة. شغّل المحاكاة؛ بدّل إلى تبويب Waveform؛ تظهر الإشارات بهرميتها على جانب وبمؤشر قابل للسحب. انقر في أي مكان على trace لإسقاط المؤشر؛ يُظهر قيم الإشارة عند ذلك الزمن كحبيبات صغيرة بجوار كل اسم إشارة.
في GTKWave
إن شغّلت المحاكاة محليًا (iverilog -o sim test.v ثم vvp sim)، افتح VCD الناتج بـ:
gtkwave dump.vcd
يُحمّل GTKWave الملف، يعرض شجرة النطاقات على جانب، وينتظرك لتسحب الإشارات إلى منطقة الموجة. انقر بزر الفأرة الأيمن على إشارة متعددة البت لتغيير تنسيق العرض (ثنائي، ست عشري، عشري، تماثلي). استخدم مربع البحث في الأسفل للقفز إلى أزمنة محددة أو انتقالات إشارات.
أنماط شائعة
مساعد testbench صغير
معظم ملفات testbench تبدأ بهذه الكتلة بالضبط. يمكنك إبقاؤها قصيرة بدمج كل شيء في initial واحدة:
initial begin
$dumpfile("dump.vcd");
$dumpvars(0, test);
// ... stimulus ...
$finish;
end
تفريغ شرطي
في بيئة اختبار حيث تريد خرج VCD فقط للاختبارات الفاشلة:
initial begin
if (DUMP_VCD) begin
$dumpfile("dump.vcd");
$dumpvars(0, test);
end
// ... stimulus ...
end
DUMP_VCD سيكون parameter تضبطه من سطر الأوامر أو تُعرّفه بحسب وضع الاختبار. يحفظ مساحة قرص في مجموعات regression.
تفريغ الذاكرة
الذواكر (arrays غير محزومة) لا تُفرّغ بواسطة $dumpvars افتراضيًا - يمكن أن تكون ضخمة. استخدم $dumpvars(0, dut.memory) صراحةً إن أردتها، أو $dumpmem في بعض المحاكيات.
أخطاء شائعة
ملف VCD فارغ. نسيت $dumpfile أو $dumpvars، أو استدعت المحاكاة $finish قبل أن تتغيّر أي إشارة. شغّل لـ بضع وحدات زمن على الأقل بعد الإعداد.
إشارات مفقودة من الموجة. النطاق الذي مررته لم يتضمنها. $dumpvars(0, dut) يُسجّل فقط ما داخل dut؛ إن كان testbench لديك يقود إشارات على مستوى test، فلن تظهر. فرّغ دائمًا من نطاق testbench: $dumpvars(0, test).
ملف VCD ضخم. محاكاة طويلة بإشارات واسعة سريعة التغيير تُنتج كثيرًا من أسطر VCD. ثلاثة إصلاحات: فرّغ نطاقًا أضيق، استخدم $dumpoff/$dumpon حول الأجزاء المملة، أو انتقل إلى تنسيق أكثر تراصًا مثل FST (الذي يدعمه iverilog وGTKWave مع راية -fst).
ماذا بعد
المستند الأخير في هذا الفصل - Timescale وDelays - يشرح توجيه \timescaleوكيف يُعيَّن#delay` فعلًا على زمن الحائط. بعد ذلك تكون قد أكملت المستندات من البداية إلى النهاية.
الأسئلة الشائعة
ما هو ملف VCD في Verilog؟
VCD اختصار لـ Value Change Dump - تنسيق نصي لتسجيل كل انتقال إشارة أثناء المحاكاة. يكتب المحاكي القيمة الأولية لكل إشارة عند الزمن 0، ثم كل تغيير مع طابعه الزمني. عارضات الموجات مثل GTKWave تقرأ الملف وتعرضه كرسم توقيت رسومي يمكنك التمرير فيه وتكبيره.
كيف أُولّد ملف VCD في Verilog؟
أضف مهمتي نظام داخل كتلة initial في testbench لديك: $dumpfile("dump.vcd"); يُسمّي ملف الخرج، و$dumpvars(0, top_module); يُسجّل كل إشارة في top_module وما تحته. بعد انتهاء المحاكاة، سيكون لديك ملف dump.vcd يمكنك فتحه في أي عارض موجات.
ماذا يعني $dumpvars(0, ...) في Verilog؟
$dumpvars(depth, instance) يُسجّل الإشارات بدءًا من instance ويتعمق depth مستوى. $dumpvars(0, test) يعني 'كل الإشارات في نطاق test وكل sub-scope، تكراريًا' - عمق 0 خاص ويعني غير محدود. $dumpvars(1, test) سيُسجّل فقط الإشارات مباشرة في test، لا في أي sub-modules مأخوذ منها نسخة.
لماذا ملف VCD لدي فارغ في Verilog؟
ثلاثة أسباب محتملة: لم تستدعِ $dumpfile/$dumpvars على الإطلاق؛ أو وصلت المحاكاة إلى $finish قبل أن تتغيّر أي إشارة (شغّل لـ بضع وحدات زمن على الأقل بعد استدعاء التفريغ)؛ أو النطاق الذي مررته لـ $dumpvars لا يطابق الهرمية الفعلية. تسلسل عمل أدنى هو $dumpfile("dump.vcd"); $dumpvars(0, test); #10; $finish;.