הדומה לתוכנה
לולאת ה-for של Verilog היא העתק של זו של C:
for (i = 0; i < 8; i = i + 1) begin
// body
end
אותם שלושה חלקים: אתחול, תנאי, קידום. הגוף רץ שוב כל עוד התנאי מתקיים.
ב-testbench זה מתנהג בדיוק כמו שהייתם מצפים מתוכנה. הסימולטור עובר על כל איטרציה בתורה:
ארבע איטרציות, ארבע שורות פלט. בלי הפתעות.
ההפתעה מגיעה כששמים לולאת for בתוך קוד בר סינתזה.
הפרישה
לולאת for בבלוק always בר סינתזה לא הופכת ללולאה בזמן ריצה בחומרה. הסינתיסייזר פורש אותה בזמן ה-elaboration: הוא מרחיב את הלולאה ל-N עותקים של הגוף, כש-N הוא מספר האיטרציות:
זה נראה כמו לולאה. בסימולציה, הסימולטור באמת עובר בלולאה על שמונה איטרציות. בסינתזה, הלולאה נפרשת לשמונה בדיקות מקביליות של data[0] עד data[7], שכולן קורות באותו זמן. הסינתיסייזר רואה:
count = 0;
if (data[0]) count = count + 1;
if (data[1]) count = count + 1;
if (data[2]) count = count + 1;
...
if (data[7]) count = count + 1;
…ואז הופך את הרצף לעץ מחברים. ההתנהגות בזמן ריצה היא "הסתכל על כל 8 הביטים בבת אחת וספור כמה מהם 1", במעבר צירופי אחד.
המשמעות: לולאת for ב-Verilog בר סינתזה אינה בחינם. לולאה של 64 איטרציות הופכת ל-64 עותקים של הגוף בחומרה. אם הגוף מורכב, בניתם זה עתה בלוק צירופי גדול. השתמשו בלולאות כש-N קטן (בין קומץ לכמה עשרות). במספרים גדולים יותר, בדרך כלל רוצים מונה מסונכרן לשעון ומכונת מצבים.
חובה גבולות קבועים
הסינתיסייזר יכול לפרוש את הלולאה רק אם הוא יודע את N בזמן ה-elaboration. כלומר, גבולות הלולאה חייבים להיות קבועים:
// Works - bound is constant
for (i = 0; i < 8; i = i + 1) ...
// Works - bound is a parameter
for (i = 0; i < WIDTH; i = i + 1) ...
// Doesn't synthesize - bound depends on a runtime signal
for (i = 0; i < dynamic_count; i = i + 1) ...
הצורה האחרונה עשויה לעבוד בסימולציה, אבל הסינתיסייזר ידחה אותה. אם באמת צריך לולאה שמספר האיטרציות שלה נקבע בזמן ריצה, בונים אותה עם מכונת מצבים מסונכרנת לשעון ואוגר מונה: לחומרה אין לולאות עם מספר איטרציות משתנה כמו שיש לתוכנה.
generate for מול for פרוצדורלי
מבנה נפרד אבל קשור הוא generate for, שמשתמש ב-genvar ונמצא מחוץ לבלוקים של always:
genvar i;
generate
for (i = 0; i < 8; i = i + 1) begin : g
bit_inverter inv(.x(in[i]), .y(out[i]));
end
endgenerate
זה משכפל 8 מופעים של bit_inverter (מוסבר ב-יצירת מופעים של מודולים). זה מבני, אתם אומרים "צור 8 עותקים של תת המודול הזה", ולא התנהגותי.
הבחנה מהירה:
forפרוצדורלי (בתוךalways): פורש הוראות בתוך בלוק התנהגותי יחיד.forשל generate (מחוץ ל-always): משכפל מבנים שלמים: מופעים, הוראותassign, בלוקים עם שם.
השתמשו במה שמתאים למה שאתם משכפלים.
איפה for מצטיין: פעולות על וקטורים
לולאות במיטבן כשמבצעים את אותה פעולה על כל ביט של וקטור. ספירת ביטים דולקים, parity, היפוך סדר, יצירת טבלאות חיפוש:
32 איטרציות, כל אחת מבצעת השמה של ביט אחד: הרבה יותר קריא מכתיבה ידנית של 32 השמות. הסינתיסייזר פורש את זה בצורה נקייה.
while, repeat, forever
מעבר ל-for, יש ב-Verilog עוד שלושה מבני לולאה, בעיקר ל-testbenches:
// Run until a condition fails
while (~done) begin
@(posedge clk);
cycles = cycles + 1;
end
// Run N times - simpler than for when you don't need a counter
repeat (8) @(posedge clk);
// Run forever - clock generators, monitoring loops
always #5 clk = ~clk;
forever begin
@(posedge clk);
$display("count=%0d", count);
end
while, repeat ו-forever ניתנים לסינתזה רק במקרים צרים (בעיקר repeat עם מספר קבוע וגוף מסונכרן לשעון). ב-testbenches הם כלים שימושיים; ב-RTL בר סינתזה העדיפו for עם מונה ומכונת מצבים מפורשת.
for פרוצדורלי ב-testbenches
ב-testbench, לולאות for מתנהגות כמו בתוכנה. השתמשו בהן בחופשיות:
לולאות מקוננות עוברות על כל צירוף של שתי כניסות של 2 ביטים. הסימולטור מריץ את האיטרציות לפי הסדר. אין דאגות של פרישה: testbenches לא עוברים סינתזה.
טעויות נפוצות
שימוש בלולאת for בקוד בר סינתזה עם גבול לא קבוע. הסינתיסייזר ידחה את זה. אם הגבול נקבע בזמן ריצה, בנו מונה ומכונת מצבים.
לשכוח שגוף הלולאה הופך לחומרה מקבילית. לולאה של 64 איטרציות עם מכפל בגוף היא 64 מכפלים מקביליים, כנראה לא מה שרציתם. ב-datapaths רחבים, בנו מכפל יחיד והזינו אותו באופן סדרתי.
ערבוב integer i ו-reg בשם i. אלה תחומים שונים; ה-integer גובר בתוך הלולאה. בחרו שמות ברורים כדי להימנע מהבלבול.
מה הלאה
עכשיו יש לכם את כל המבנים הפרוצדורליים ש-Verilog מציעה. הפרק הבא מחבר הכול לתבניות שמתכנני חומרה באמת משתמשים בהן: לוגיקה מסונכרנת לשעון, כלומר flip-flops, אוגרים ו-pipelines, ו-מכונות מצבים סופיות, הניב הסטנדרטי לכל בקר עם כמה מצבי פעולה.
שאלות נפוצות
איך לולאות for עובדות ב-Verilog?
מבחינת תחביר הן נראות כמו ב-C: for (i = 0; i < N; i = i + 1) statement;. אבל בקוד בר סינתזה הלולאה נפרשת בזמן ה-elaboration: הסינתיסייזר מרחיב אותה ל-N עותקים של הגוף. אין מונה לולאה בזמן ריצה ואין לולאה בחומרה. ב-testbenches, לולאות for מתנהגות כמו בתוכנה, כי הסימולטור יכול לעבור עליהן לפי הסדר.
האם לולאת for ניתנת לסינתזה ב-Verilog?
כן, אבל רק כשגבולות הלולאה הם קבועים שידועים בזמן ה-elaboration. הסינתיסייזר פורש את הלולאה ל-N עותקים מקביליים של הגוף. אם הגבולות תלויים באות בזמן ריצה, הלולאה לא ניתנת לסינתזה, וצריך להמיר אותה לתכנון סדרתי מסונכרן לשעון.
מה ההבדל בין for לבין generate for ב-Verilog?
לולאת for בתוך בלוק always היא מבנה פרוצדורלי שעובר סינתזה על ידי פרישה. לולאת generate for (עם genvar) היא מבנה מפורש של זמן elaboration שמשכפל חומרה מבנית: כמה מופעים של מודול, כמה wires, כמה הוראות assign. השתמשו ב-for בתוך בלוקים פרוצדורליים, וב-generate for מחוץ להם כדי לשכפל מבנה.
יש ב-Verilog לולאת while?
כן: while (condition) statement;. היא ניתנת לסינתזה רק כשהסינתיסייזר יכול להוכיח שהלולאה מסתיימת אחרי מספר חסום של איטרציות. בפועל זה נדיר, ולכן while מופיעה בעיקר ב-testbenches ובקוד לסימולציה בלבד. לאיטרציה ברת סינתזה, השתמשו במקום זאת בלולאת for עם מונה.