תחביר מוכר, מודל מנטלי שונה
if/else נראה בדיוק כמו ב-C:
if (condition) begin
// ... statements ...
end else begin
// ... statements ...
end
אבל הכללים שונים, כי Verilog היא לא תוכנה. שני דברים שכדאי לזכור:
if/elseחי רק בתוך בלוק פרוצדורלי. אי אפשר לכתובifעצמאי ברמת המודול.- מה ש-
if/elseהופך אליו בסינתזה תלוי בסוג הבלוק. בבלוק צירופי הוא הופך ל-multiplexer או ל-priority encoder. בבלוק מסונכרן לשעון הוא הופך ל-flip-flop עם לוגיקת עדכון מותנית.
בתוך בלוק צירופי
הבלוק הצירופי always @(*) רץ מחדש בכל פעם ש-a, b או c משתנים. שרשרת ה-if/else בוחרת ענף אחד, מבצעת השמה ל-max, והבלוק מסתיים. מכיוון ש-max מקבל השמה תמיד (בכל מסלול יש השמה), הסינתיסייזר מייצר לוגיקה צירופית טהורה, בלי latch.
שימו לב ש-max מוצהר כ-reg למרות שאין flip-flop בחומרה. אותו כלל כמו תמיד: כל מה שמקבל השמה בתוך always חייב להיות reg.
מלכודת ה-latch
זה הבאג הנפוץ ביותר בקוד צירופי של מתחילים:
// WRONG - infers a latch
always @(*) begin
if (enable)
out = data;
// no else! when `enable` is low, what does `out` do?
end
הסינתיסייזר קורא "כש-enable נמוך, out לא מקבל השמה", ומחליט ש-out חייב לזכור את הערך הקודם שלו. כדי לזכור ערך צריך תא אחסון, ולכן הכלי מכניס latch. Latches בתכנונים סינכרוניים גורמים לבעיות תזמון, קשה לאפס אותם, וכמעט אף פעם הם לא מה שהתכוונתם.
שתי דרכים לתקן:
שתיהן מייצרות את אותה חומרה צירופית: mux של 2 ל-1. תבנית "ברירת מחדל בראש הבלוק" מתאימה יותר כשיש הרבה השמות מותנות לאותו אות.
בתוך בלוק מסונכרן לשעון
שימו לב מה שונה מהמקרה הצירופי:
- הבלוק הוא
always @(posedge clk): הטריטוריה של flip-flops. - ההשמה משתמשת ב-
<=(לא חוסמת). - אין
elseלמקרה של "לא reset ולא enable". זה בסדר. בבלוק מסונכרן לשעון, כששום ענף לא מופעל, ה-flip-flop פשוט מחזיק את הערך הקודם שלו, וזה בדיוק מה ש-flip-flop עושה פיזית. לא מוסק latch, כי האות כבר הוא אוגר.
זה המקום היחיד שבו השמטת else בטוחה. מחוץ לבלוקים מסונכרנים לשעון, תמיד טפלו בכל מסלול.
שרשרת else if: priority encoder
לשרשרת של הוראות else if יש עדיפות מובלעת: תנאים מוקדמים גוברים על מאוחרים:
ל-requests[0] יש העדיפות הגבוהה ביותר: אם הוא דולק, ה-grant הוא 0 בלי קשר למה שהביטים הגבוהים יותר עושים. הסינתיסייזר הופך את השרשרת ל-mux מדורג: בודק קודם את ביט 0, אחר כך את ביט 1, ביט 2 וביט 3. כל רמה מוסיפה קצת השהיה.
אם התנאים מוציאים זה את זה, למשל בפענוח כניסת one-hot, משפט case (המסמך הבא) מייצר חומרה שטוחה ומהירה יותר משרשרת else if. השתמשו בצורת ה-case כשאין דרישת עדיפות אמיתית.
if בלי else בקוד מסונכרן לשעון
בלוק מסונכרן לשעון לא צריך else, כי "להחזיק את הערך הקודם" היא ברירת המחדל. כך בונים enables:
always @(posedge clk) begin
if (load) target <= incoming;
// no else: when load is low, target keeps its value
end
זה אוגר עם load enable. רוב אוגרי ה-pipeline, המונים ואוגרי התצורה משתמשים בתבנית הזו.
begin/end והוראות בודדות
כמו ב-C, אפשר להשמיט את begin/end בהוראה בודדת:
if (a) out = 1;
else out = 0;
לכל מה שיותר מהוראה אחת, השתמשו בבלוק:
if (a) begin
out = 1;
flag = 1;
end else begin
out = 0;
flag = 0;
end
שתי התבניות מתערבבות בחופשיות. מדריכי סגנון בדרך כלל ממליצים להשתמש תמיד ב-begin/end, כדי שהוספת הוראה שנייה לא תכאב.
מה הלאה
המסמך הבא, משפט case, עוסק ב-case, הכלי הנכון לפענוח רב כיווני (מכונות מצבים, ניתוב opcodes, טבלאות ROM). אחריו מגיעות לולאות for, ששונות בדקות מבנות הדודות שלהן בתוכנה, כי הן נפרשות בזמן ה-elaboration.
שאלות נפוצות
איך משפט if עובד ב-Verilog?
if (cond) statement; מריץ את statement כש-cond שונה מאפס. אפשר לעטוף כמה הוראות ב-begin ... end. מוסיפים else statement; לענף החלופי, או משרשרים עם else if (other_cond) .... if/else קיים רק בתוך בלוקים פרוצדורליים, initial או always, ולא ברמה העליונה של מודול.
מה זה latch מוסק ב-Verilog?
Latch שכלי הסינתזה יצר בלי שביקשתם, כי בלוק ה-always הצירופי שלכם לא ביצע השמה לאות בכל מסלול. הכלי רואה 'אם a אז out = 1' בלי else, מחליט שבמקרה שלא טופל צריך לזכור את הערך הקודם, ומייצר latch. Latches הם כמעט תמיד טעות; הפתרון הוא לתת לכל אות ערך ברירת מחדל בראש הבלוק או else מפורש.
איך נמנעים מ-latches מוסקים ב-Verilog?
בבלוק צירופי always @(*), ודאו שכל reg של פלט מקבל השמה בכל מסלול קוד. התבנית הנקייה ביותר היא לקבוע ערכי ברירת מחדל בראש הבלוק, ואז לדרוס אותם לפי תנאי. הקומפיילר בדרך כלל מתריע כשהוא מסיק latch: התייחסו לאזהרה כאל שגיאה.
מה נוצר בסינתזה משרשרת if-else ב-Verilog?
Priority encoder. ל-if הראשון יש העדיפות הגבוהה ביותר, ה-else if הבא נבדק רק אם הראשון שקרי, וכן הלאה. בחומרה זה הופך לשרשרת של muxes שסדר העדיפויות צרוב בה. אם התנאים מוציאים זה את זה, משפט case עם אותה לוגיקה עובר לעיתים קרובות סינתזה לחומרה שטוחה יותר ונקרא ברור יותר.