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