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

أساسيات testbench في Verilog: كيف تتحقق من module

كيف تكتب testbench في Verilog - توليد clock، تسلسل reset، تحفيز، مراقبة، والهيكل القياسي الذي يقود كل محاكاة ستشغّلها.

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

ما هو testbench فعلًا

testbench هو فقط module Verilog. الفرق عن module قابل للتركيب هو ما بداخله:

  • ليس له منافذ - إنه قمة هرمية المحاكاة.
  • يأخذ نسخة من design under test.
  • يقود مدخلات DUT من كتل initial وalways.
  • يراقب مخارج DUT بـ $display أو $monitor أو بتفريغ VCD.
  • ينهي المحاكاة بـ $finish.

هذه الوظيفة بأكملها. لا توجد كلمة مفتاحية خاصة "testbench" - الطريقة التي تخبر بها أن شيئًا testbench هي بقراءة ما يفعله.

الهيكل القياسي

هذا النمط بأكمله. خمسة أقسام، سهلة للنسخ واللصق، سهلة للتكييف.

لـ DUTs توافقية (مثل adder)، لا تحتاج clock. لـ DUTs تسلسلية، تحتاج - وهذا هو المستوى التالي.

توليد Clock لـ DUTs التسلسلية

عندما يكون لـ DUT دخل clk، على testbench إنتاج واحد. الـ one-liner القياسي:

reg clk = 0;
always #5 clk = ~clk;

يقلب clk كل 5 وحدات زمن. كل قلب نصف فترة clock، فالفترة الكاملة 10 وحدات (دورة واحدة = مرتفع 5 + منخفض 5). إن كان timescale لديك 1ns / 1ps (افتراضي في معظم المحاكيات)، فهذا clock 100 MHz.

اختر نصف الفترة بناءً على ما تريد نمذجته. لـ clock 10 MHz في نفس timescale، استخدم #50. لـ clock 200 MHz، #2.5 (أو غيّر timescale).

تسلسل Reset

التصاميم المتزامنة تحتاج reset يُحفَظ مرتفعًا لبضع دورات بعد الزمن 0، ثم يُحرَّر. الشكل المعتاد:

reg reset = 1;     // start asserted

initial begin
    // Hold reset for a few clocks.
    #20 reset = 0;
end

يبقي reset مرتفعًا حتى الزمن 20، ثم يخفضه. بحلول وقت سقوط reset، تكون الساعة قد قامت بدورتين، وكل flip-flop التقط قيمة reset، والتصميم في حالة معروفة.

لـ reset active-low (reset_n بدلًا من reset)، اعكس القيمة الأولية:

reg reset_n = 0;   // active-low: 0 means asserted

initial begin
    #20 reset_n = 1;
end

testbench تسلسلي كامل

دمج clock وreset وتحفيز:

هذا testbench كامل لعدّاد 4 بت. اضغط Run وسترى العدّاد يخرج من reset، يعدّ صعودًا بينما يكون enable مفعّلًا، يتوقف عند سقوط enable، يستأنف عند ارتفاعه، ثم يتوقف.

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

  1. ثلاث بؤر نشاط متميزة: مولّد clock (always واحدة)، التحفيز (initial واحدة)، والمراقب (always أخرى). كل واحدة لها وظيفة واحدة.
  2. # delays في كتلة التحفيز تُقاس بوحدات الزمن - لا بدورات clock. إن كانت فترة clock لديك 10 وحدات، فإن #10 هي دورة واحدة بالضبط. بعض testbenches تستخدم @(posedge clk) بدلًا من ذلك، الذي يتقدم دورة واحدة بصرف النظر عن الفترة.
  3. المراقب يستخدم $display من كتلة always @(posedge clk). هذا يطبع كل دورة. لخرج أكثر تطورًا، انتقل إلى $monitor (المغطّى تاليًا).

تحفيز قائم على الدورات

أحيانًا "انتظر N دورة" يُقرأ أفضل من "انتظر N وحدة زمن":

initial begin
    @(posedge clk);     // wait for next clock edge
    reset = 0;

    repeat (5) @(posedge clk);   // wait 5 more cycles
    enable = 1;

    repeat (8) @(posedge clk);
    enable = 0;

    repeat (10) @(posedge clk);
    $finish;
end

@(posedge clk) يحجب حتى الحافة الصاعدة التالية لـ clock. repeat (N) @(posedge clk); ينتظر N دورة. هذا الأسلوب مستقل عن فترة clock - إن غيّرت تردد clock، فالتحفيز ما زال يفعل نفس الشيء من حيث الدورات.

لـ testbenches المبكرة # delays أبسط. لـ testbenches الإنتاج التي قد تعمل بسرعات clock متعددة، القائم على الدورات أسلوب أكثر أمانًا.

اختبارات ذاتية الفحص

testbench حتى الآن يطبع ما حدث، تاركًا لك قراءة الخرج. testbench ذاتي الفحص بدلًا من ذلك يفحص الخرج ويُبلّغ عن النجاح/الفشل:

initial begin
    #1;
    a = 10; b = 20;
    #1;
    if (sum !== 30) begin
        $display("FAIL: 10 + 20 = %0d (expected 30)", sum);
        $finish;
    end
    a = 250; b = 5;
    #1;
    if (sum !== 255) begin
        $display("FAIL: 250 + 5 = %0d (expected 255)", sum);
        $finish;
    end

    $display("PASS");
    $finish;
end

استخدم !== (عامل عدم المساواة الصارم) للأمان - لا يُرجع x عندما يكون لمعامل بتات مجهولة. النمط: قُد المدخلات، انتظر استقرار الأشياء، قارن مع المتوقع، أبلغ عن النجاح أو الفشل.

ذاتي الفحص لا يقدّر بثمن في مجموعات regression: آلاف الاختبارات يمكن أن تعمل دون إشراف، وفقط الإخفاقات تحتاج اهتمامًا.

ماذا بعد

لديك الآن شكل testbench كافٍ لأي module. المستندات التالية تغطّي الأدوات داخل testbench: Display وMonitor للخرج النصي الأغنى، Dumpfile وVCD لتنقيح الموجة الرسومي، وTimescale وDelays للتحكم بدقة في كيفية ارتباط زمن المحاكاة بزمن الحائط.

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

ما هو testbench في Verilog؟

testbench هو module Verilog - عادةً بلا منافذ - غرضه الوحيد هو تمرين design under test (DUT). يأخذ نسخة من DUT، ويُولّد clock، ويقود التحفيز عبر كتل initial وalways، ويراقب المخارج بـ $display/$monitor، ويُفرّغ اختياريًا waveform VCD، وينهي المحاكاة بـ $finish.

كيف أُولّد clock في testbench Verilog؟

النمط القياسي هو سطر واحد: always #5 clk = ~clk; (مع reg clk = 0; مُعلن من قبل). يقلب clk كل 5 وحدات زمن محاكاة، فيعطي فترة 10 وحدات (clock 100 MHz إن كان timescale لديك بالنانوثانية). #5 هو نصف الفترة - نصف الوقت clk مرتفع، ونصفه منخفض.

ما هو DUT في Verilog؟

DUT اختصار لـ Design Under Test - module الذي يُمرّنه testbench. العرف هو تسمية نسخة DUT بـ dut أو u_dut في testbench: my_module dut(.clk(clk), .reset(reset), .in(in), .out(out));. الاسم مجرد لافتة؛ ما يهم أنه يُؤخذ منه نسخة، ومنافذه مُتصلة بإشارات testbench، وtestbench يقود تلك الإشارات.

كم يجب أن تستمر محاكاة Verilog؟

طويلة بما يكفي لتمرين كل ما تريد التحقق منه، ثم $finish. معظم testbenches تضع حدًا زمنيًا صريحًا (#1000 $finish) كي لا تتعلق المحاكاة بانتظار حدث لا يأتي. داخل تلك النافذة، قُد تحفيزك، دع DUT يستقر، ومثاليًا ضمّن بعض جمل if للفحص الذاتي تطبع FAIL إن لم يطابق الخرج التوقعات.

Coddy programming languages illustration

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

ابدأ الآن