Menu

Verilog wire מול reg: מתי להשתמש בכל אחד (עם דוגמאות)

שני טיפוסי הנתונים העיקריים ב-Verilog, wire לחיבורים רציפים ו-reg לאחסון פרוצדורלי, והכלל לבחירה ביניהם בכל פעם.

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

שני שמות, תפקיד אחד

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

  • אותות wire מוזנים מחוץ לבלוקים פרוצדורליים: על ידי פקודות assign, על ידי יציאות של תת-מודולים, או על ידי פורטי כניסה של המודול.
  • אותות reg מוזנים בתוך בלוקים פרוצדורליים: initial או always.

זה כל הכלל. השמות של מילות המפתח מגושמים כי הם קדמו לדרך המודרנית לכתוב Verilog. reg לא תמיד הופך לרגיסטר בחומרה. המשיכו לקרוא כדי להבין למה.

wire: החיבור הרציף

wire הוא חוט חשמלי. הוא נושא את מה שהמקור שלו מפיק, באופן רציף. יש שתי דרכים להזין wire:

שלושה דברים לשים לב אליהם:

  • y ו-z מוצהרים כ-wire במודול, ושוב כ-wire ב-testbench.
  • הם מוזנים על ידי assign, וזו צורת ההשמה הרציפה. הצד הימני של ה-= מחושב מחדש בכל פעם שאות כלשהו בו משתנה.
  • אי אפשר לכתוב y = a & b בתוך בלוק always ולהשאיר את y כ-wire. הקומפיילר ידחה את זה.

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

reg: אחסון בתוך בלוק פרוצדורלי

reg הוא אות שמקבל ערך בתוך initial או always. השם הוא שריד מימיה הראשונים של השפה, כש-"reg" נשמע כמו "register". בשימוש המודרני reg הוא פשוט הטיפוס של כל אות שקוד פרוצדורלי כותב אליו.

count הוא reg בתוך המודול (כי בלוק ה-always כותב אליו) ו-wire ב-testbench (כי פורט הפלט של ה-DUT מזין אותו). אותו אות, תפקידים שונים, וטיפוסים שונים בכל תחום.

למה "reg" לא תמיד אומר "רגיסטר"

זו מלכודת המתחילים הנפוצה ביותר. המודול הזה מצהיר על y כ-reg, אבל החומרה שמתקבלת מהסינתזה לא מכילה flip-flop:

בלוק ה-always @(*) רגיש ל_כל_ שינוי בכניסה. הוא צירופי. כלי סינתזה מזהה את התבנית הזו ומפיק שער AND: בלי flip-flop, בלי שעון, רק לוגיקה. מילת המפתח reg היא דרישה תחבירית בלבד, כי y מקבל ערך בתוך always.

כדי לקבל flip-flop אמיתי, בלוק ה-always חייב להיות רגיש לקצה שעון:

always @(posedge clk) begin
    q <= d;
end

זו התבנית שאומרת לכלי הסינתזה "בבקשה תייצר flip-flop": רשימת רגישות מתוזמנת בשעון והשמה non-blocking. אותה מילת מפתח reg, חומרה שונה לגמרי. אנחנו מכסים את ההבחנה בבלוק always ובBlocking מול Non-blocking.

ההחלטה בפועל

בכל פעם שאתם מצהירים על אות, שאלו: איך אזין אותו?

  • מזינים עם assign? → wire.
  • מחברים לפורט פלט של תת-מודול? → wire.
  • פורט כניסה של המודול? → wire. (כניסות הן תמיד wire.)
  • מזינים מתוך initial או always? → reg.
  • פורט פלט של המודול שמוזן על ידי קוד פרוצדורלי? → output reg.
  • פורט פלט של המודול שמוזן על ידי assign? → output wire. (או פשוט output: כיוון לבדו הוא wire כברירת מחדל.)

עץ ההחלטות הזה מכסה כל מצב שתפגשו ב-Verilog רגילה.

התנגשויות בין מקורות הזנה

לאותות wire יכולים להיות בתיאוריה כמה מקורות הזנה. כך עובדים bus-ים של tri-state: כמה מודולים יכולים להזין את אותו wire, והלא פעילים עוברים לעכבה גבוהה (z). בלוגיקה רגילה, שתי פקודות assign שכותבות לאותו wire יוצרות התנהגות לא מוגדרת:

assign y = a;
assign y = b;   // BAD - y has two drivers

הסימולטור עשוי לבחור אחד, עשוי להפוך את האות ל-X, תלוי בכלי. כך או כך זה באג. מקור הזנה אחד לכל wire, אלא אם אתם בונים bus באופן מפורש.

אותות reg יכולים להיות מוזנים רק מבלוק פרוצדורלי אחד. שני בלוקי always שכותבים לאותו reg הם שגיאת סינתזה, ובסימולציה ההתנהגות מוזרה. אותו כלל: מקור הזנה אחד.

העדכון של SystemVerilog: logic

SystemVerilog (ההרחבה ש-Verilog התפתחה אליה) מוסיפה מילת מפתח אחת שמחליפה את שתיהן: logic. אות logic יכול להיות מוזן על ידי assign או על ידי בלוק פרוצדורלי, אבל לא בשניהם, והקומפיילר עוצר אתכם מלייצר בטעות באג של כמה מקורות הזנה.

module modern(input logic a, input logic b, output logic y);
    assign y = a ^ b;
endmodule

אם אתם מתחילים פרויקט מאפס והכלי שלכם תומך ב-SystemVerilog (העורך בדף הזה תומך, עם -g2012), שימוש ב-logic בכל מקום מפשט את הכללים. קבצי .v רגילים עדיין צריכים את ההפרדה בין wire/reg, ותמשיכו לראות את שני הסגנונות בשטח עוד הרבה זמן.

מה הלאה

הדף הבא, וקטורים ומערכים, לוקח את אותם שני טיפוסים ומראה איך להפוך אותם למרובי ביטים. רוחבי bus, טווחים, ממדי packed, וההבדל בין וקטור של ביטים למערך של וקטורים. כל מה שצריך לפני שבונים משהו גדול יותר ממחבר של ביט אחד.

שאלות נפוצות

מה ההבדל בין wire ל-reg ב-Verilog?

גם wire וגם reg מחזיקים ערך של אות בכל רגע בסימולציה, אבל בוחרים ביניהם לפי מי מזין את האות. wire מוזן מחוץ לבלוק פרוצדורלי: על ידי assign או על ידי פלט של תת-מודול. reg מוזן מתוך בלוק initial או always. השמות היסטוריים ומטעים: reg לא תמיד אומר 'רגיסטר'.

האם reg ב-Verilog הוא flip-flop?

לא בהכרח. reg עובר סינתזה ל-flip-flop רק כשבלוק ה-always שלו רגיש לקצה שעון ומשתמש בהשמה non-blocking. reg שמקבל ערך בתוך בלוק צירופי always @(*) עובר סינתזה ללוגיקה צירופית רגילה. מילת המפתח בוחרת את טיפוס הנתונים ב-Verilog, לא את החומרה.

מתי להשתמש ב-wire ומתי ב-reg?

כלל אצבע: אם תזינו את האות עם assign או תחברו אותו לפורט פלט של תת-מודול, השתמשו ב-wire. אם תשימו בו ערך בתוך בלוק always או initial, השתמשו ב-reg. כניסות של מודול הן תמיד wire. יציאות הן wire אם הן מוזנות על ידי assign, ו-reg אם הן מוזנות על ידי בלוק פרוצדורלי.

האם אפשר לבצע השמה ל-wire בתוך בלוק always?

לא, זו שגיאת תחביר. אותות wire יכולים להיות מוזנים רק על ידי השמות רציפות (assign) או על ידי חיבור כפלט של מופע תת-מודול. כל מה שבתוך initial או always חייב לכוון ל-reg (או ב-SystemVerilog, ל-logic). הקומפיילר יתפוס את זה ויתלונן על 'left-hand side type mismatch'.

מה זה logic ב-SystemVerilog?

logic הוא האיחוד של wire ו-reg ב-SystemVerilog. אפשר להזין אותו בהשמה רציפה או בבלוק פרוצדורלי (אבל לא בשניהם בו זמנית). קוד מודרני משתמש יותר ויותר ב-logic בכל מקום ושוכח מההבחנה בין wire/reg. קבצי Verilog רגילים עדיין צריכים לבחור.

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

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

להתחיל