Menu

Verilog case: פענוח רב כיווני כמו שצריך

איך case עובד לפענוח רב כיווני נקי, ה-default שאסור לדלג עליו, וההבדלים בין case, casex ו-casez.

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

ההסתעפות הרב כיוונית

case הוא מבנה הניתוב השטוח של Verilog. נותנים לו ביטוי, והוא בוחר את הענף המתאים:

case (expression)
    pattern_1: statement_1;
    pattern_2: statement_2;
    pattern_3: begin
        statement_3a;
        statement_3b;
    end
    default: default_statement;
endcase

המבנה דומה ל-switch של C, אבל:

  • אין break: כל ענף מסתיים באופן מובלע כשמתחיל הענף הבא.
  • תבניות יכולות להיות וקטורים, לא רק מספרים שלמים.
  • הסינתיסייזר הופך את כל המבנה ל-mux שטוח (או למפענח one-hot כשהתבניות מאפשרות זאת).

דוגמה מלאה: mux של 4 ל-1

בגוף ה-case יש ארבע תבניות מפורשות ועוד default. הסינתיסייזר רואה כניסה של 2 ביטים שממופה לאחד מארבעה ערכים, ומפיק multiplexer של 4 ל-1. נקי, שטוח ומהיר.

למה צריך default

ב-case צירופי, השמטת default היא אותה מלכודת כמו if בלי else: כל ערך כניסה שלא הותאם משאיר את out בלי השמה, והסינתיסייזר מסיק latch.

ב-sel של 2 ביטים, התבניות שלמעלה מכסות את כל ארבעת הערכים האפשריים, כך שבתיאוריה default מיותר. בפועל:

  1. הסינתיסייזר לא תמיד מוכיח שהמקרים ממצים.
  2. הבורר עשוי להיות x או z בסימולציה, ואז הוא לא מתאים ל_אף_ מקרה מפורש.
  3. הוספת מקרה חדש בהמשך עלולה להשאיר את התנהגות ברירת המחדל לא מוגדרת.

תמיד כתבו default. במכונות מצבים ובלוגיקת mux שבהן ידוע שברירת המחדל לא ניתנת להשגה, השמה של 'x:

default: out = 8'bx;

…אומרת לסינתיסייזר "זה don't-care, בצע אופטימיזציה בחופשיות", ומציגה x אדום בוהק בסימולציה אם המקרה הבלתי אפשרי בכל זאת מתרחש. כך מקבלים את הטוב משני העולמות.

מכונת מצבים בתוך case

השימוש הקלאסי ב-case הוא לוגיקת המעברים בין מצבים של מכונת מצבים סופית:

הבלוק case (state) הוא לוגיקת המעברים. כל ענף מחליט מה המצב הבא וכמה זמן להישאר בו. כאן default לא ניתן להשגה (אנחנו מכסים את RED/GREEN/YELLOW באופן ממצה במרחב של 2 ביטים), אבל הוא שם כרשת ביטחון: אם state הופך איכשהו ל-2'd3, מכונת המצבים חוזרת בצורה נקייה ל-RED במקום ליצור latch.

מכונות מצבים סופיות מעמיק בתבנית הזו.

כמה תבניות לענף אחד

אפשר לרשום כמה תבניות שחולקות הוראה אחת, מופרדות בפסיקים:

case (opcode)
    4'h0, 4'h1, 4'h2: result = a + b;
    4'h3, 4'h4:       result = a - b;
    4'h8:             result = a & b;
    default:          result = 8'd0;
endcase

כאן שני opcodes שמשמעותם "חיסור", ושלושה שמשמעותם "חיבור". הסינתיסייזר מחבר את התבניות ב-OR עבור המשווה.

casez ו-casex: התאמת don't-care

לפעמים רוצים להתאים תבנית שחלק מהביטים שלה לא מוגדרים, למשל "כל opcode שמתחיל ב-010":

casez (opcode)
    8'b010?_????: instruction = ALU_OP;
    8'b110?_????: instruction = LOAD_OP;
    8'b1110_????: instruction = JUMP_OP;
    default:      instruction = UNKNOWN;
endcasez

casez מתייחס ל-? (ול-z) בתבנית כאל don't-care. כל ? מתאים ל-0 או ל-1. שימושי לפענוח פורמטים של הוראות שבהם חלק ממיקומי הביטים לא בשימוש במחלקות opcode מסוימות.

casex מרחיב את זה ומתייחס גם ל-x כאל don't-care. casex מסוכן, כי אותות לא מאותחלים (שהם x בסימולציה) מתאימים ל_כל_ מקרה ויוצרים התנהגות מפתיעה. רוב מדריכי הסגנון המודרניים ממליצים על casez ואוסרים את casex.

ב-SystemVerilog יש גם case inside, שהיא הגרסה הנקייה מכולן: היא מקבלת טווחים ורשימות. אבל Verilog רגיל נעצר ב-casez.

case מול שרשרת if/else if

שני המבנים יכולים לבטא החלטות רב כיווניות, אבל הם עוברים סינתזה לחומרה שונה:

  • case הוא ניתוב שטוח. הסינתיסייזר יכול לייצר מפענח one-hot, עץ mux מאוזן או מבנים שטוחים אחרים. זמן חישוב קבוע.
  • if/else if הוא שרשרת עדיפויות. הסינתיסייזר מייצר מפל שבו כל רמה מוסיפה השהיה. איטי יותר באופן לוגריתמי.

מבחינה פונקציונלית הם חופפים. מבחינת סגנון: השתמשו ב-case בכל פעם שהתנאים עוסקים בערך של ביטוי יחיד. השתמשו ב-if/else if כשיש עדיפות אמיתית או כשהתנאים עוסקים באותות שונים.

// Better as case:
if      (sel == 2'd0) out = a;
else if (sel == 2'd1) out = b;
else if (sel == 2'd2) out = c;
else                  out = d;

// Better as if/else if:
if      (urgent_event)  next_state = HANDLE_URGENT;
else if (timer_expired) next_state = TIMEOUT;
else if (data_ready)    next_state = PROCESS;
else                    next_state = state;

שלושת התנאים הראשונים שואלים כולם "מה הערך של sel?", ולכן case נקרא טבעי יותר ועובר סינתזה שטוחה יותר. שלושת הבאים הם אירועים בלתי תלויים עם עדיפות ברורה, ולכן if/else if מתאים יותר.

מה הלאה

המסמך האחרון בפרק הזה, לולאות for, עוסק ב-for של Verilog ובדבר המפתיע שקורה כשמשתמשים בו בקוד בר סינתזה. אחר כך נעבור ברצינות ללוגיקה סדרתית ולמכונות מצבים.

שאלות נפוצות

מה זה משפט case ב-Verilog?

case (expr) ... endcase הוא מבנה ההסתעפות הרב כיווני של Verilog. הוא מחשב את הביטוי פעם אחת ומעביר את הביצוע לענף שמתאים. זו הבחירה המקובלת למכונות מצבים, מפענחי opcode, בוררי mux וכל דבר אחר שבוחר בין כמה אפשרויות שמוציאות זו את זו.

מה ההבדל בין case, casex ו-casez ב-Verilog?

case מתאים ביט אחר ביט במדויק, כולל ערכי x ו-z. casez מתייחס ל-z (ול-?) בפריטי ה-case כאל don't-care. casex מתייחס גם ל-x וגם ל-z כאל don't-care. התאמת don't-care שימושית לתבניות opcode שבהן חלק ממיקומי הביטים לא רלוונטיים, אבל casex מסוכן בסימולציה, כי אותות לא מאותחלים (x) עלולים להתאים בטעות לכל case.

למה צריך default במשפט case ב-Verilog?

בלי default, כלי הסינתזה רואה אפשרות שאף case לא התאים, מחליט שאות הפלט חייב לשמור את הערך הקודם שלו, ומסיק latch לא רצוי. הענף default מטפל בכל ערך שלא הותאם, בדרך כלל על ידי קביעת הפלט לערך בטוח או סימון המקרה כבלתי ניתן להשגה עם השמת x. תמיד כללו אותו.

מתי להשתמש ב-case ומתי ב-if-else ב-Verilog?

השתמשו ב-case כשהתנאים מוציאים זה את זה ותלויים בערך של ביטוי יחיד: מכונות מצבים, מפענחי opcode, בוררי mux. השתמשו ב-if/else כשיש סדר עדיפויות אמיתי או כשהתנאים עוסקים באותות שונים. case עובר סינתזה לחומרה שטוחה ומהירה יותר משרשרת ארוכה של else if.

איור של שפות התכנות ב-Coddy

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

להתחיל