Menu

Verilog testbench למתחילים: איך בודקים מודול

איך כותבים testbench ב-Verilog: יצירת שעון, רצף reset, גירוי, תצפית, והשלד הסטנדרטי שמניע כל סימולציה שתריצו.

בדף הזה יש עורכים שאפשר להריץ. לערוך, להריץ ולראות את הפלט מיד.

מה testbench הוא בעצם

testbench הוא פשוט מודול Verilog. ההבדל ממודול בר סינתזה הוא במה שיש בתוכו:

  • אין לו פורטים: הוא הראש של היררכיית הסימולציה.
  • הוא יוצר מופע של התכנון הנבדק.
  • הוא מזין את הכניסות של ה-DUT מבלוקים של initial ו-always.
  • הוא צופה בפלטים של ה-DUT עם $display, $monitor, או על ידי שמירת VCD.
  • הוא מסיים את הסימולציה עם $finish.

זו כל העבודה. אין מילת מפתח מיוחדת של "testbench": מזהים testbench לפי מה שהוא עושה.

השלד הסטנדרטי

זו כל התבנית. חמישה חלקים, קל להעתיק, קל להתאים.

ל-DUTs צירופיים (כמו המחבר) לא צריך שעון. ל-DUTs סדרתיים כן, וזו השכבה הבאה.

יצירת שעון ל-DUTs סדרתיים

כשל-DUT יש כניסת clk, ה-testbench צריך לייצר שעון. השורה הסטנדרטית:

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

היא הופכת את clk כל 5 יחידות זמן. כל היפוך הוא חצי ממחזור השעון, ולכן המחזור המלא הוא 10 יחידות (מחזור אחד = גבוה ל-5 ונמוך ל-5). אם ה-timescale שלכם הוא 1ns / 1ps (ברירת המחדל ברוב הסימולטורים), זה שעון של 100 MHz.

בחרו את חצי המחזור לפי מה שרוצים למדל. לשעון של 10 MHz באותו timescale, השתמשו ב-#50. לשעון של 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 פעיל בנמוך (reset_n במקום reset), הופכים את הערך ההתחלתי:

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

initial begin
    #20 reset_n = 1;
end

testbench סדרתי מלא

שילוב של שעון, reset וגירוי:

זה testbench מלא למונה של 4 ביטים. לחצו Run ותראו את המונה יוצא מ-reset, סופר למעלה כשהוא מאופשר, נעצר כש-enable יורד, ממשיך כשהוא עולה, ואז נעצר.

שלושה דברים שכדאי לשים לב אליהם במבנה:

  1. שלושה מוקדי פעילות נפרדים: מחולל השעון (always אחד), הגירוי (initial אחד), והמעקב (always נוסף). לכל אחד תפקיד אחד.
  2. ההשהיות # בבלוק הגירוי נמדדות ביחידות זמן, לא במחזורי שעון. אם מחזור השעון הוא 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) חוסם עד הקצה העולה הבא של השעון. repeat (N) @(posedge clk); מחכה N מחזורים. הסגנון הזה לא תלוי באורך מחזור השעון: אם משנים את תדר השעון, הגירוי עדיין עושה את אותו הדבר במונחי מחזורים.

ב-testbenches ראשונים, השהיות # פשוטות יותר. ב-testbenches של ייצור שעשויים לרוץ בכמה מהירויות שעון, הסגנון לפי מחזורים בטוח יותר.

בדיקות שבודקות את עצמן

ה-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

השתמשו ב-!== (אופרטור ה-case inequality) ליתר ביטחון: הוא לא מחזיר x כשבאחד האופרנדים יש ביטים לא ידועים. התבנית: מזינים כניסות, מחכים שהדברים יתייצבו, משווים לציפייה, ומדווחים הצלחה או כישלון.

בדיקה עצמית היא בעלת ערך רב בחבילות רגרסיה: אלפי בדיקות יכולות לרוץ בלי השגחה, ורק כישלונות דורשים תשומת לב.

מה הלאה

עכשיו יש לכם צורת testbench שמספיקה לכל מודול. המסמכים הבאים עוסקים בכלים שנכנסים לתוך ה-testbench: Display ו-Monitor לפלט טקסט עשיר יותר, Dumpfile ו-VCD לדיבוג גרפי עם waveforms, ו-Timescale והשהיות לשליטה מדויקת על הקשר בין זמן הסימולציה לזמן אמיתי.

שאלות נפוצות

מה זה testbench ב-Verilog?

testbench הוא מודול Verilog, בדרך כלל בלי פורטים, שכל תפקידו להפעיל את התכנון הנבדק (DUT). הוא יוצר מופע של ה-DUT, מייצר שעון, מזין גירוי דרך בלוקים של initial ו-always, צופה בפלטים עם $display/$monitor, שומר waveform של VCD אם רוצים, ומסיים את הסימולציה עם $finish.

איך מייצרים שעון ב-testbench של Verilog?

התבנית הסטנדרטית היא שורה אחת: always #5 clk = ~clk; (עם reg clk = 0; שהוצהר קודם). היא הופכת את clk כל 5 יחידות זמן של סימולציה, ונותנת מחזור של 10 יחידות (שעון של 100 MHz אם ה-timescale בננו שניות). ה-#5 הוא חצי מחזור: חצי מהזמן clk גבוה, וחצי נמוך.

מה זה DUT ב-Verilog?

DUT הוא ראשי תיבות של Design Under Test, המודול שה-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

ללמוד תכנות עם Coddy

להתחיל